/* NDS client for ncpfs Copyright (C) 1997 Arne de Bruijn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define RANDBUF /* if defined: read random data once from /dev/urandom */ /*#define ERR_MSG*/ /* if defined: show error messages in nds_login_auth */ #include #include #include #ifdef ERR_MSG #include #endif #include #include #ifdef RANDBUF #include #endif #include "ncplib.h" #include "ncplib_err.h" #include "ndslib.h" #include "ndscrypt.h" #define USUALS typedef __u32 word32; typedef __u16 word16; typedef unsigned char boolean; #include "mpilib.h" #include #include #include "kernel/ipx.h" #include #include "ndslib.h" static int buf_get_dword_lh(char **buf, char *bufend, unsigned int *v) { if ((*buf) + 4 <= bufend) { if (v) *v = (unsigned char)(**buf) | (unsigned char)(*(*buf + 1)) << 8 | (unsigned char)(*(*buf + 2)) << 16 | (unsigned char)(*(*buf + 3)) << 24; (*buf) += 4; return 0; } else return -1; } static int buf_get_lbuf(char **buf, char *bufend, char *out, int outmax, int *outlen) { int i, j; if ((!buf_get_dword_lh(buf, bufend, &i)) && (*buf + i <= bufend)) { j = i; if (out) { if (j > outmax) j = outmax; memcpy(out, *buf, j); } if (outlen) *outlen = j; *buf += (i + 3) & (~3); return 0; } else return -1; } static int buf_put_word_lh2(char **buf, char *bufend, unsigned int v) { if ((*buf) + 2 <= bufend) { *((*buf)++) = v & 255; *((*buf)++) = v >> 8; return 0; } else return -1; } static int buf_put_dword_lh(char **buf, char *bufend, unsigned long v) { if ((buf) && ((*buf) + 4 <= bufend)) { *((*buf)++) = v & 255; *((*buf)++) = (v >> 8) & 255; *((*buf)++) = (v >> 16) & 255; *((*buf)++) = v >> 24; return 0; } else return -1; } static int buf_put_dword_hl(char **buf, char *bufend, unsigned long v) { if ((*buf) + 4 <= bufend) { *((*buf)++) = v >> 24; *((*buf)++) = (v >> 16) & 255; *((*buf)++) = (v >> 8) & 255; *((*buf)++) = v & 255; return 0; } else return -1; } static int buf_put_lbuf(char **buf, char *bufend, const char *databuf, int buflen) { if ((databuf) && (!buf_put_dword_lh(buf, bufend, buflen)) && (*buf + buflen <= bufend)) { memcpy(*buf, databuf, buflen); (*buf) += buflen; while (buflen++ & 3) *(*buf)++ = 0; return 0; } else return -1; } static int buf_put_buf(char **buf, char *bufend, const char *databuf, int buflen) { if ((databuf) && (*buf + buflen <= bufend)) { memcpy(*buf, databuf, buflen); (*buf) += buflen; while (buflen++ & 3) *(*buf)++ = 0; return 0; } else return -1; } static int buf_put_unistr(char **buf, char *bufend, const uni_char *str) { int i = (strlen_u(str) + 1) * 2; if ((str) && (!buf_put_dword_lh(buf, bufend, i)) && (*buf + i <= bufend)) { memcpy(*buf, (char *)str, i); (*buf) += i; while (i++ & 3) *(*buf)++ = 0; return 0; } else return -1; } static int buf_get_dword_hl(char **buf, char *bufend, unsigned int *v) { if ((*buf) + 4 <= bufend) { if (v) { *v = (unsigned char)(**buf) << 24 | (unsigned char)(*(*buf + 1)) << 16 | (unsigned char)(*(*buf + 2)) << 8 | (unsigned char)(*(*buf + 3)); } (*buf) += 4; return 0; } else return -1; } static int buf_get_word_lh(char **buf, char *bufend, unsigned int *v) { if ((v) && ((*buf) + 2 <= bufend)) { *v = (unsigned char)(**buf) | (unsigned char)(*(*buf + 1)) << 8; (*buf) += 4; return 0; } else return -1; } static int buf_get_word_lh2(char **buf, char *bufend, unsigned int *v) { if ((v) && ((*buf) + 2 <= bufend)) { *v = (unsigned char)(**buf) | (unsigned char)(*(*buf + 1)) << 8; (*buf) += 2; return 0; } else return -1; } static int buf_get_lbuf_alloc(char **buf, char *bufend, char **outbuf, int *bufsize) { int i, err = 0; if ((!buf_get_dword_lh(buf, bufend, &i)) && (*buf + i <= bufend)) { if (outbuf) { if (((*outbuf) = malloc(i))) memcpy(*outbuf, *buf, i); else err = ENOMEM; } (*buf) += (i + 3) & (~3); if (bufsize) *bufsize = i; return err; } else { if (outbuf) *outbuf = NULL; if (bufsize) *bufsize = 0; return -1; } } static int buf_get_buf(char **buf, char *bufend, char *outbuf, int bufsize) { if (*buf + bufsize <= bufend) { if (outbuf) memcpy(outbuf, *buf, bufsize); *buf += (bufsize + 3) & (~3); return 0; } else return -1; } int strlen_u(const uni_char *s) { int i = 0; while (*s++) i++; return i; } void strcpy_uc(char *d, const uni_char *s) { while ((*d++ = *s++)); } void strcpy_cu(uni_char *d, const char *s) { while ((*d++ = *s++)); } long nds_get_server_name(struct ncp_conn *conn, uni_char **server_name) { long err; int outlen; char *p, *pend, *outbuf; if (!(outbuf = malloc(4096))) return ENOMEM; if (server_name) *server_name = NULL; if ((err = ncp_send_nds_frag(conn, 53, NULL, 0, outbuf, 4096, &outlen)) == 0) { pend = (p = outbuf) + outlen; if (buf_get_dword_lh(&p, pend, &outlen)) err = NCPL_ET_REPLY_FORMAT; else { if (!((*server_name) = malloc(outlen))) err = ENOMEM; else memcpy(*server_name, p, outlen); } } free(outbuf); return err; } long nds_get_tree_name(struct ncp_conn *conn, char *name, int name_buf_len) { char buf[128]; int size; long err; char *p, *pend; if (!(err = ncp_send_nds(conn, 1, "\0\0\0", 3, buf, sizeof(buf), &size))) { p = buf + 4; pend = buf + size; if (buf_get_lbuf(&p, pend, name, name_buf_len, &size)) return NCPL_ET_REPLY_FORMAT; if (name) { p = name + size - 1; while ((p >= name) && (!*p)) p--; while ((p >= name) && (*p == '_')) p--; *(p + 1) = 0; } } return err; } /* for login */ long nds_resolve_name(struct ncp_conn *conn, int flags, uni_char *entry_name, int *entry_id, int *remote, struct sockaddr *serv_addr, int *addr_len) { char *buf, *p, *pend, addr_buf[12]; long err; int i; if (!(buf = malloc(4096))) return ENOMEM; pend = (p = buf) + 2048; buf_put_dword_lh(&p, pend, 0); buf_put_dword_lh(&p, pend, flags); buf_put_dword_lh(&p, pend, 0); buf_put_unistr(&p, pend, entry_name); buf_put_dword_lh(&p, pend, 1); buf_put_dword_lh(&p, pend, 0); buf_put_dword_lh(&p, pend, 1); buf_put_dword_lh(&p, pend, 0); if ((err = ncp_send_nds_frag(conn, 1, buf, p - buf, buf + 2048, 2048, &i)) == 0) { pend = (p = buf + 2048) + i; if (buf_get_dword_lh(&p, pend, &i) || (i < 0) || (i > 2)) err = NCPL_ET_REPLY_FORMAT; else if (i == 1) { if (remote) *remote = 0; if (buf_get_dword_hl(&p, pend, entry_id)) err = NCPL_ET_REPLY_FORMAT; } else { if (remote) *remote = 1; if ((!serv_addr) || (!addr_len)) { free(buf); return 0; } if (buf_get_dword_hl(&p, pend, entry_id) || buf_get_dword_lh(&p, pend, &i) || (i != 0) || buf_get_dword_lh(&p, pend, &i) || (i == 0) || buf_get_dword_lh(&p, pend, &i)) err = NCPL_ET_REPLY_FORMAT; else if (i != 0) /* no ipx? */ err = NCPL_ET_TRANSPORT_UNKNOWN; else if (buf_get_dword_lh(&p, pend, &i) || (i != 12) || buf_get_buf(&p, pend, addr_buf, 12)) err = NCPL_ET_REPLY_FORMAT; else if (*addr_len < sizeof(struct sockaddr_ipx)) err = EINVAL; else { ((struct sockaddr_ipx *)serv_addr)->sipx_family = AF_IPX; ((struct sockaddr_ipx *)serv_addr)->sipx_type = NCP_PTYPE; /* buf and addr both in network order */ memcpy(&((struct sockaddr_ipx *)serv_addr)->sipx_network, addr_buf, 4); memcpy(((struct sockaddr_ipx *)serv_addr)->sipx_node, addr_buf + 4, 6); memcpy(&((struct sockaddr_ipx *)serv_addr)->sipx_port, addr_buf + 10, 2); *addr_len = sizeof(struct sockaddr_ipx); } } } free(buf); return err; } long nds_readentryname(struct ncp_conn *conn, int obj_id, uni_char **name, int *namelen) { char reqbuf[16], *p, *pend, *buf; uni_char *p2; long err; int outlen; if (name) *name = NULL; if (namelen) *namelen = 0; pend = (p = reqbuf) + 16; buf_put_dword_lh(&p, pend, 2); buf_put_dword_lh(&p, pend, 0); buf_put_dword_lh(&p, pend, 0x281d); buf_put_dword_hl(&p, pend, obj_id); if (!(buf = malloc(4096))) return ENOMEM; if ((err = ncp_send_nds_frag(conn, 2, reqbuf, 16, buf, 4096, &outlen))) { free(buf); return err; } pend = (p = buf) + outlen; p += 16; buf_get_lbuf(&p, pend, NULL, 0, NULL); if ((buf_get_dword_lh(&p, pend, &outlen)) || (outlen > pend - p)) { free(buf); return NCPL_ET_REPLY_FORMAT; } if (name) { if (!(p2 = malloc(outlen))) { free(buf); return ENOMEM; } memcpy(p2, p, outlen); *name = p2; } if (namelen) *namelen = outlen; free(buf); return 0; } long nds_read(struct ncp_conn *conn, int obj_id, uni_char *propname, char **outbuf, int *outlen) { long err; char *buf, *p, *pend; int n1, n2, n3, n4, n5; if (outbuf) *outbuf = NULL; if (outlen) *outlen = 0; if (!(buf = malloc(4096))) return ENOMEM; pend = (p = buf) + 2048; buf_put_dword_lh(&p, pend, 0); buf_put_dword_lh(&p, pend, -1L); buf_put_dword_hl(&p, pend, obj_id); buf_put_dword_lh(&p, pend, 1); buf_put_dword_lh(&p, pend, 0); buf_put_dword_lh(&p, pend, 1); buf_put_unistr(&p, pend, propname); if (!(err = ncp_send_nds_frag(conn, 3, buf, p - buf, buf + 2048, 2048, &n1))) { pend = (p = (buf + 2048)) + n1; if (!(err = buf_get_dword_lh(&p, pend, &n1)) && !(err = buf_get_dword_lh(&p, pend, &n2)) && !(err = buf_get_dword_lh(&p, pend, &n3)) && !(err = buf_get_dword_lh(&p, pend, &n4)) && !(err = buf_get_lbuf(&p, pend, NULL, 0, NULL)) && !(err = buf_get_dword_lh(&p, pend, &n5))) { if ((n1 != -1) || (n2 != 1) || (n3 != 1) || (n4 != 9) || (n5 != 1)) err = -1; else err = buf_get_lbuf_alloc(&p, pend, outbuf, outlen); } } free(buf); return err; } #ifdef RANDBUF #define RANDBUFSIZE 1236 /* total size of all fillrandom's for login+auth */ char global_randbuf[RANDBUFSIZE]; char *g_rndp = global_randbuf + RANDBUFSIZE; void fillrandom(char *buf, int buflen) { int fh,i; do { if (g_rndp == global_randbuf + RANDBUFSIZE) { if ((fh = open("/dev/urandom", O_RDONLY)) >=0) { read(fh, global_randbuf, RANDBUFSIZE); close(fh); } else { g_rndp = global_randbuf; while (g_rndp - global_randbuf < RANDBUFSIZE) *(g_rndp++) = rand() / ((((unsigned)RAND_MAX)+255) / 256); } g_rndp = global_randbuf; } if ((i = RANDBUFSIZE - (g_rndp - global_randbuf)) > buflen) i = buflen; memcpy(buf, g_rndp, i); buf += i; g_rndp += i; buflen -= i; } while (buflen); } #else void fillrandom(char *buf, int buflen) { int fh; char *p; if (((fh = open("/dev/urandom", O_RDONLY)) >= 0) { read(fh, buf, buflen); close(fh); } else { p = buf; while (p - buf < buflen) *(p++) = rand() / ((((unsigned)RAND_MAX)+255) / 256); } } #endif int countbits_l(char *buf, int bufsize) { unsigned char b; while ((--bufsize) && (!buf[bufsize])); b = (unsigned char)buf[bufsize]; bufsize <<= 3; while (b) { b >>= 2; bufsize++; } return bufsize; } void copyfill(void *outbuf, int outsize, const void *inbuf, int insize) { if (outsize < insize) insize = outsize; memcpy(outbuf, inbuf, insize); memset((char *)outbuf + insize, 0, outsize - insize); } uni_char c_public_key[] = {'P','u','b','l','i','c',' ','K','e','y',0}; char keyprefix[] = {1, 0, 0, 0, 3, 0, 1, 0}; int initkey(const char *key, char **keyptr, int *keylen) { /* 1=ok, 0=err */ if (!memcmp(key, keyprefix, 8)) { if (keylen) *keylen = (unsigned char)key[8] | \ (unsigned char)key[9] << 8; if (keyptr) (const char *)(*keyptr) = key + 10; return 1; } else return 0; } void clearkey(char *key) { char *keyptr; int keylen; if (initkey(key, &keyptr, &keylen)) memset(key, 0, keylen + 10); } int findchunk(const char *keyptr, int keylen, const char *chunk, char **chunkptr) { const char *p; if ((p = keyptr)) { while (p - keyptr < keylen) { if ((p[0] != chunk[0]) || (p[1] != chunk[1])) p += 4 + (unsigned char)p[2] + (unsigned char)p[3]; else { if (chunkptr) (const char *)(*chunkptr) = p + 4; return (unsigned char)p[2] + (unsigned char)p[3]; } } } if (chunkptr) *chunkptr = NULL; return 0; } int checkkey(const char *key) { /* 0 - wrong key, != 0 - key ok */ char temp[8]; char *keyptr, *p; int keylen; if ((initkey(key, &keyptr, &keylen)) && (findchunk(keyptr, keylen, "MA", &p))) { nwhash1init(temp, 8); nwhash1(temp, 8, key + 10, (unsigned char)key[8] + ((unsigned char)key[9] << 8) - 20); return (!memcmp(p, temp, 8)); } else return 0; } long modexpkey(const char *s_key, char *buf, char *outbuf, int bufsize) { char *s_keyptr; int s_keylen, i, nbits, nblocksize; int err = -1; unitptr nmod, nexp, nin, nout; char *p; nmod = nexp = nin = nout = NULL; if (!initkey(s_key, &s_keyptr, &s_keylen)) return NCPL_ET_REPLY_FORMAT; i = findchunk(s_keyptr, s_keylen, "NN", &p); if (!p) return NCPL_ET_REPLY_FORMAT; nbits = countbits_l(p, i); nblocksize = ((nbits + 31) & (~31)) >> 3; if (!(nmod = malloc(nblocksize))) return ENOMEM; copyfill(nmod, nblocksize, p, i); i = findchunk(s_keyptr, s_keylen, "EN", &p); err = NCPL_ET_REPLY_FORMAT; if (!p) goto end; err = ENOMEM; if (!(nexp = malloc(nblocksize))) goto end; copyfill(nexp, nblocksize, p, i); if (!(nin = malloc(nblocksize))) goto end; copyfill(nin, nblocksize, buf, bufsize); if (!(nout = malloc(nblocksize))) goto end; set_precision(bytes2units(nblocksize)); if (mp_modexp((unitptr) nout, (unitptr) nin, (unitptr) nexp, (unitptr) nmod)) err = NCPL_ET_REPLY_FORMAT; else { copyfill(outbuf, bufsize, nout, nblocksize); err = 0; } end: if (nout) { mp_init0(nout); free(nout); } if (nin) { mp_init0(nin); free(nin); } if (nexp) free(nexp); if (nmod) free(nmod); return err; } long get_public_key(struct ncp_conn *conn, long obj_id, char **key) { char *keybuf, *kptr; long err; int keylen, ofs, klen; if ((err = nds_read(conn, obj_id, c_public_key, &keybuf, &keylen))) { return err; } ofs = (unsigned char)keybuf[10] + ((unsigned char)keybuf[11] << 8) + 0x1a; if ((ofs > keylen) || (!initkey(keybuf + ofs, &kptr, &klen)) || (klen + ofs > keylen) || (!checkkey(keybuf + ofs))) { err = NCPL_ET_REPLY_FORMAT; goto err_exit; } if (key) { if (!(kptr = malloc(klen + 10))) { err = ENOMEM; goto err_exit; } memcpy(kptr, keybuf + ofs, klen + 10); *key = kptr; } err = 0; err_exit: free(keybuf); return err; } char buf2str1[8] = {1,0,0,0,9,0,2,0}; char buf2str2[16] = {65,0,0,0,1,0,0,0,1,0,9,0,53,0,28,0}; char buf2str3[8] = {1,0,0,0,1,0,6,0}; long rsa_crypt(struct ncp_conn *conn, char *data, int datalen, long serv_id, char **outp, char *pend) { char rand[28]; char hashrand[8], temp[8]; unsigned short cryptbuf[128]; char buf2[56]; int i; long err; char *s_key; char *p; if ((*outp + datalen + 108) > pend) return -1; if ((err = get_public_key(conn, serv_id, &s_key))) return err; fillrandom(rand, 28); nwhash1init(hashrand, 8); for (i = 10; i; i--) nwhash1(hashrand, 8, rand, 28); memset(buf2 + 40, 0, 16); buf2[0] = 11; memcpy(buf2 + 1, rand, 28); memset(buf2 + 29, 11, 11); nwhash1(buf2 + 40, 5, buf2 + 1, 39); nwhash1(buf2 + 45, 2, buf2, 45); fillrandom(buf2 + 47, 5); err = modexpkey(s_key, buf2, buf2, 56); free(s_key); if (err) return err; buf_put_dword_lh(outp, pend, datalen + 108); buf_put_buf(outp, pend, buf2str1, sizeof(buf2str1)); buf_put_dword_lh(outp, pend, datalen + 96); buf_put_buf(outp, pend, buf2str2, sizeof(buf2str2)); buf_put_buf(outp, pend, buf2, 56); buf_put_dword_lh(outp, pend, datalen + 20); buf_put_buf(outp, pend, buf2str3, sizeof(buf2str3)); buf_put_dword_lh(outp, pend, (datalen + 8) | (datalen << 16)); memset(temp, 3, 3); nwhash1init(temp + 3, 5); nwhash1(temp + 3, 5, data, datalen); nwhash1(temp + 3, 5, temp, 3); nwencryptblock(hashrand, data, datalen, *outp); *outp += datalen; for (i = 0, p = *outp - 8; i < 8; i++, p++) temp[i] ^= *p; nwcryptinit(cryptbuf, hashrand); nwencrypt(cryptbuf, temp, *outp); *outp += 8; memzero(rand); memzero(hashrand); memzero(temp); memzero(cryptbuf); memzero(buf2); return 0; } char bufstr[16]={28, 0, 0, 0, 1, 0, 0, 0, 1, 0, 6, 0, 16, 0, 4, 0}; long nds_login(struct ncp_conn *conn, long user_id, const char *pwd, long serv_id, char *logindata, char **u_priv_key) { char *buf, *p, *pend; char temp[16]; char hashshuf[8]; char loginid[4]; char crypt1strc[28]; char randno[4]; char randbuf[1024]; char *tempbuf; int i, outlen; int n1, n2, n3; long err; int grace_period = 0; if (u_priv_key) *u_priv_key = NULL; if (!(buf = malloc(4096))) return ENOMEM; pend = (p = buf) + 2048; buf_put_dword_lh(&p, pend, 0); buf_put_dword_hl(&p, pend, user_id); if ((err = ncp_send_nds_frag(conn, 57, buf, p - buf, buf + 2048, 2048, &outlen))) { free(buf); return err; } pend = (p = buf + 2048) + outlen; if ((buf_get_buf(&p, pend, temp, 4)) || (buf_get_buf(&p, pend, loginid, 4))) { free(buf); return NCPL_ET_REPLY_FORMAT; } free(buf); if (strlen(pwd) > 127) return NCPL_ET_PWD_TOO_LONG; if (!(tempbuf = malloc(1064))) return ENOMEM; if (!(buf = malloc(4096))) { free(tempbuf); return ENOMEM; } strcpy(randbuf, pwd); for (p = randbuf; *p; p++) *p = toupper(*p); #if 0 shuffle(temp, randbuf, temp); #else shuffle(temp, randbuf, strlen(randbuf), temp); #endif nwhash1init(hashshuf, 8); for (i = 10; i; i--) nwhash1(hashshuf, 8, temp, 16); memcpy(temp, loginid, 4); memset(temp + 4, 7, 7); nwhash1init(temp + 11, 5); nwhash1(temp + 11, 5, temp, 11); memcpy(crypt1strc, bufstr + 4, 12); nwencryptblock(hashshuf, temp, 16, crypt1strc + 12); fillrandom(randno, 4); fillrandom(randbuf, 1024); pend = (p = tempbuf) + 1064; buf_put_buf(&p, pend, randno, 4); buf_put_dword_lh(&p, pend, 1024); buf_put_buf(&p, pend, randbuf, 1024); buf_put_buf(&p, pend, bufstr, sizeof(bufstr)); buf_put_buf(&p, pend, crypt1strc + 12, 16); pend = (p = buf) + 2048; buf_put_dword_lh(&p, pend, 2); buf_put_dword_lh(&p, pend, 0); buf_put_dword_hl(&p, pend, user_id); rsa_crypt(conn, tempbuf, 1064, serv_id, &p, pend); memset(tempbuf, 0, 1064); free(tempbuf); if ((err = ncp_send_nds_frag(conn, 58, buf, p - buf, buf + 2048, 2048, &outlen))) { if ((err != NCPL_ET_REQUEST_ERROR) || (conn->completion != NDS_GRACE_PERIOD)) goto err_exit; grace_period = 1; } err = NCPL_ET_REPLY_FORMAT; pend = (p = buf + 2048) + outlen; if ((buf_get_buf(&p, pend, logindata, 8)) || (buf_get_dword_lh(&p, pend, &n1)) || (n1 > pend - p)) goto err_exit; pend = p + n1; if ((buf_get_dword_lh(&p, pend, &n1)) || (buf_get_dword_lh(&p, pend, &n2)) || (buf_get_word_lh(&p, pend, &n3)) || (n1 != 1) || (n2 != 0x060001) || (n3 > pend - p)) goto err_exit; nwhash1init(temp, 8); for (i = 10; i; i--) nwhash1(temp, 8, crypt1strc, 28); nwdecryptblock(temp, p, n3, p); nwhash1init(temp, 5); nwhash1(temp, 5, p, n3 - 5); if (memcmp(temp, p + n3 - 5, 5)) goto err_exit; pend = p + n3 - 12; if ((buf_get_buf(&p, pend, loginid, 4)) || (buf_get_dword_lh(&p, pend, &n2)) || (memcmp(loginid, randno, 4)) || (n2 > pend - p)) goto err_exit; pend = p + n2; for (i = 0; i < n2; i++) p[i] ^= randbuf[i]; if ((buf_get_dword_lh(&p, pend, &n1)) || (buf_get_dword_lh(&p, pend, &n2)) || (buf_get_word_lh(&p, pend, &n3)) || (n1 != 1) || (n2 != 0x060001) || (n3 > pend - p)) goto err_exit; pend = p + n3; nwdecryptblock(hashshuf, p, n3, p); if ((buf_get_dword_lh(&p, pend, &n1)) || (buf_get_word_lh2(&p, pend, &n2)) || (buf_get_word_lh2(&p, pend, &n3)) || (n1 != 1) || (n2 != 2) || (n3 > pend - p)) goto err_exit; if (u_priv_key) { if (!(tempbuf = malloc(n3 + 10))) { err = ENOMEM; goto err_exit; } memset(tempbuf, 0, 8); tempbuf[0] = 1; tempbuf[4] = 3; tempbuf[6] = 1; tempbuf[8] = n3 & 255; tempbuf[9] = n3 >> 8; memcpy(tempbuf + 10, p, n3); if (!checkkey(tempbuf)) { free(tempbuf); goto err_exit; } *u_priv_key = tempbuf; } err = 0; if (grace_period) { conn->completion = NDS_GRACE_PERIOD; err = NCPL_ET_REQUEST_ERROR; } err_exit: memzero(hashshuf); memzero(randbuf); memzero(crypt1strc); memzero(randno); memzero(temp); if (buf) free(buf); return err; } long nds_beginauth(struct ncp_conn *conn, long user_id, long serv_id, char *authid) { char *buf, *p, *pend, *n_temp, temp[8]; char *s_key; char randno[4]; long err; int outlen, n1, n2, n3, n4; if (!(buf = malloc(2048))) return ENOMEM; n_temp = NULL; fillrandom(randno, 4); pend = (p = buf) + 512; buf_put_dword_lh(&p, pend, 0); buf_put_dword_hl(&p, pend, user_id); buf_put_buf(&p, pend, randno, 4); if ((err = ncp_send_nds_frag(conn, 59, buf, p - buf, buf + 1024, 1024, &outlen))) goto err_exit; err = NCPL_ET_REPLY_FORMAT; pend = (p = buf + 1024) + outlen; if ((buf_get_buf(&p, pend, authid, 4)) || (buf_get_dword_lh(&p, pend, &outlen)) || (outlen > pend - p)) goto err_exit; pend = p + outlen; if ((buf_get_dword_lh(&p, pend, &n1)) || (buf_get_dword_lh(&p, pend, &n2)) || (buf_get_dword_lh(&p, pend, &n3)) || (n1 != 1) || (n2 != 0x020009) || (n3 > pend - p)) goto err_exit; pend = p + n3; if ((buf_get_dword_lh(&p, pend, &n1)) || (buf_get_dword_lh(&p, pend, &n1)) || (buf_get_dword_lh(&p, pend, &n2)) || (buf_get_word_lh(&p, pend, &n3)) || (n1 != 1) || (n2 != 0x0a0001) || (n3 > pend - p)) goto err_exit; n1 = ((countbits_l(p, n3) + 31) & (~31)) >> 3; if (n1 < 52) goto err_exit; if (!(n_temp = malloc(n1))) { err = ENOMEM; goto err_exit; } copyfill(n_temp, n1, p, n3); p += (n3 + 3) & (~3); if ((err = get_public_key(conn, serv_id, &s_key))) goto err_exit; err = modexpkey(s_key, n_temp, n_temp, n1); free(s_key); if (err) goto err_exit; err = NCPL_ET_REPLY_FORMAT; nwhash1init(temp, 7); nwhash1(temp + 5, 2, n_temp, 45); nwhash1(temp, 5, n_temp + 1, 39); if (memcmp(temp, n_temp + 40, 7)) goto err_exit; nwhash1init(temp, 8); for (n1 = 10; n1; n1--) nwhash1(temp, 8, n_temp + 1, 28); free(n_temp); n_temp = NULL; if ((buf_get_dword_lh(&p, pend, &n1)) || (buf_get_dword_lh(&p, pend, &n2)) || (buf_get_dword_lh(&p, pend, &n3)) || (buf_get_dword_lh(&p, pend, &n4)) || (n1 != 28) || (n2 != 1) || (n3 != 0x060001) || (n4 != 0x040010) || (pend - p < 16)) goto err_exit; nwdecryptblock(temp, p, 16, p); nwhash1init(temp, 5); nwhash1(temp, 5, p, 11); if ((!memcmp(temp, p + 11, 5)) || (!memcmp(p, randno, 4))) err = 0; err_exit: if (n_temp) free(n_temp); if (buf) free(buf); return err; } char *allocfillchunk(const char *keyptr, int keylen, const char *chunk, int destsize) { char *p, *p2; int i; i = findchunk(keyptr, keylen, chunk, &p); if (!p) return NULL; if (!(p2 = malloc(destsize))) return NULL; copyfill(p2, destsize, p, i); return p2; } long gen_auth_data(char **outp, char *outend, const char *u_key, const char *u_priv_key, const char *authid, char *loginstrc, int loginstrc_len) { char *keyptr; int keylen, i, j; int nbits, nblocksize, nbytes; unsigned char nmask; unitptr n_mod, n_exp, n_pn, n_qn, n_dp, n_dq, n_cr, n_key, n_temp; unitptr n_key_dp, n_key_dq; unitptr up, up2; char *p, *tempbuf; char *randbuf = NULL; char hashbuf[0x42]; long err; n_temp = n_mod = n_exp = n_pn = n_qn = n_dp = n_dq = n_cr = n_key = n_key_dp = n_key_dq = NULL; if (!initkey(u_key, &keyptr, &keylen)) return NCPL_ET_REPLY_FORMAT; i = findchunk(keyptr, keylen, "NN", &p); if (!p) return NCPL_ET_REPLY_FORMAT; nbits = countbits_l(p, i); nbytes = (nbits + 7) >> 3; nmask = (unsigned char)(255 >> (8 - (nbits & 7))); nblocksize = ((nbits + 31) & (~31)) >> 3; set_precision(bytes2units(nblocksize)); n_mod = (unitptr)allocfillchunk(keyptr, keylen, "NN", nblocksize); n_exp = (unitptr)allocfillchunk(keyptr, keylen, "EN", nblocksize); if (!initkey(u_priv_key, &keyptr, &keylen)) { err = NCPL_ET_REPLY_FORMAT; goto err_exit; } n_pn = (unitptr)allocfillchunk(keyptr, keylen, "PN", nblocksize); n_qn = (unitptr)allocfillchunk(keyptr, keylen, "QN", nblocksize); n_dp = (unitptr)allocfillchunk(keyptr, keylen, "DP", nblocksize); n_dq = (unitptr)allocfillchunk(keyptr, keylen, "DQ", nblocksize); n_cr = (unitptr)allocfillchunk(keyptr, keylen, "CR", nblocksize); n_key = malloc(nblocksize); nwhash2init(hashbuf); nwhash2block(hashbuf, loginstrc, loginstrc_len); nwhash2end(hashbuf); copyfill(n_key, nblocksize, hashbuf, 16); if (!(tempbuf = malloc(loginstrc_len + 16))) { err = ENOMEM; goto err_exit; } memset(tempbuf, 0, 16); tempbuf[4] = 0x3c; memcpy(tempbuf + 8, authid, 4); p = tempbuf + 12; buf_put_dword_lh(&p, tempbuf + 16, loginstrc_len); memcpy(p, loginstrc, loginstrc_len); nwhash2init(hashbuf); nwhash2block(hashbuf, tempbuf, loginstrc_len + 16); free(tempbuf); n_temp = malloc(nblocksize); n_key_dp = malloc(nblocksize); n_key_dq = malloc(nblocksize); mp_mult(n_temp, n_pn, n_qn); mp_modexp(n_key_dp, n_key, n_dp, n_pn); mp_modexp(n_key_dq, n_key, n_dq, n_qn); mp_move(n_temp, n_key_dp); mp_add(n_temp, n_pn); mp_sub(n_temp, n_key_dq); stage_modulus(n_pn); mp_modmult(n_temp, n_temp, n_cr); mp_mult(n_key, n_temp, n_qn); mp_add(n_key, n_key_dq); randbuf = malloc(nblocksize * 3); memset(randbuf, 0, nblocksize * 3); buf_put_dword_lh(outp, outend, 12 + nblocksize * 6); buf_put_dword_lh(outp, outend, 1); buf_put_dword_lh(outp, outend, 0x100008); buf_put_word_lh2(outp, outend, 3); buf_put_word_lh2(outp, outend, nblocksize * 3); memset(*outp, 0, nblocksize * 6); up = (unitptr)randbuf; up2 = (unitptr)*outp; for (i = 3; i; i--) { fillrandom((char *)up, nbytes); ((char *)up)[nbytes - 1] &= nmask; if (!(j = mp_compare(up, n_mod))) mp_dec(up); else if (j > 0) { mp_sub(up, n_mod); mp_neg(up); mp_add(up, n_mod); } mp_modexp(up2, up, n_exp, n_mod); ((char *)up) += nblocksize; ((char *)up2) += nblocksize; } nwhash2block(hashbuf, *outp, nblocksize * 3); nwhash2end(hashbuf); up = (unitptr)randbuf; for (i = 0; i < 3; i++) { mp_init(n_temp, (unsigned char)hashbuf[i << 1] | ((unsigned char)hashbuf[(i << 1) + 1] << 8)); mp_modexp(up2, n_key, n_temp, n_mod); stage_modulus(n_mod); mp_modmult(up2, up2, up); ((char *)up) += nblocksize; ((char *)up2) += nblocksize; } *outp = (char *)up2; err = 0; err_exit: memzero(hashbuf); free(randbuf); if (n_temp) { mp_init0(n_temp); free(n_temp); } if (n_key_dp) { mp_init0(n_key_dp); free(n_key_dp); } if (n_key_dq) { mp_init0(n_key_dq); free(n_key_dq); } if (n_pn) { mp_init0(n_pn); free(n_pn); } if (n_qn) { mp_init0(n_qn); free(n_qn); } if (n_dp) { mp_init0(n_dp); free(n_dp); } if (n_dq) { mp_init0(n_dq); free(n_dq); } if (n_cr) { mp_init0(n_cr); free(n_cr); } free(n_mod); free(n_exp); return err; } long nds_authenticate(struct ncp_conn *conn, long user_id, long serv_id, const char *logindata, const char *u_priv_key) { char authid[4]; long err; int user_name_len; uni_char *user_name = NULL; char *loginstrc; int loginstrc_len; char *buf, *p, *pend; char *u_key; #ifdef SIGNATURES char signkey[8]; #endif u_key = loginstrc = buf = NULL; if ((err = nds_beginauth(conn, user_id, serv_id, authid))) return err; if ((err = nds_readentryname(conn, user_id, &user_name, &user_name_len))) return err; loginstrc_len = user_name_len + 22; if (!(loginstrc = malloc(loginstrc_len))) { err = ENOMEM; goto err_exit; } memset(loginstrc, 0, 22); loginstrc[0] = 1; loginstrc[4] = 6; memcpy(loginstrc + 6, logindata, 8); fillrandom(loginstrc + 14, 4); loginstrc[20] = user_name_len & 255; loginstrc[21] = user_name_len >> 8; memcpy(loginstrc + 22, user_name, user_name_len); free(user_name); user_name = NULL; if ((err = get_public_key(conn, user_id, &u_key))) goto err_exit; if (!(buf = malloc(2048))) { err = ENOMEM; goto err_exit; } pend = (p = buf) + 2048; buf_put_dword_lh(&p, pend, 0); #ifdef SIGNATURES if (conn->sign_wanted) { fillrandom(signkey, 8); rsa_crypt(conn, signkey, 8, serv_id, &p, pend); } else #endif buf_put_dword_lh(&p, pend, 0); buf_put_lbuf(&p, pend, loginstrc, loginstrc_len); if ((err = gen_auth_data(&p, pend, u_key, u_priv_key, authid, loginstrc, loginstrc_len))) goto err_exit; if ((err = ncp_send_nds_frag(conn, 60, buf, p - buf, NULL, 0, NULL))) goto err_exit; #ifdef SIGNATURES if ((err = ncp_sign_start(conn, signkey))) goto err_exit; #endif err = ncp_change_conn_state(conn, 1); err_exit: if (loginstrc) free(loginstrc); if (buf) free(buf); if (u_key) free(u_key); if (user_name) free(user_name); return err; } long nds_login_auth(struct ncp_conn *conn, const char *user, const char *pwd) { long err; uni_char user_u[200]; char *u_priv_key = NULL; char logindata[8]; uni_char *server_name = NULL; __u32 serv_id, user_id; struct sockaddr_ipx wserv_addr; struct ncp_conn *login_conn, *wserv_conn = NULL; int not_wserv; /* =1: current server doesn't have a writable replica */ int i; struct timeval tv; int grace_period = 0; #ifdef ERR_MSG char buf[200]; /* to print username */ #endif gettimeofday(&tv, NULL); srand(tv.tv_usec); if (strlen(user) >= 200) return NCPL_ET_NAMETOOLONG; strcpy_cu(user_u, user); i = sizeof(wserv_addr); err = nds_resolve_name(conn, 0x64, user_u, &user_id, ¬_wserv, (struct sockaddr *)&wserv_addr, &i); if ((err == NCPL_ET_REQUEST_ERROR) && (conn->completion == -601) && (user_u[strlen_u(user_u)-1] != '.')) { #ifdef ERR_MSG strcpy_uc(buf, user_u); printf("User %s not found in current context.\n" "Trying server context...\n", buf); #endif if ((err = nds_get_server_name(conn, &server_name)) != 0) goto err_exit; i = 0; while ((server_name[i]) && (server_name[i] != '.')) i++; memcpy(user_u + strlen_u(user_u), server_name + i, (strlen_u(server_name) - i + 1) * 2); free(server_name); server_name = NULL; i = sizeof(wserv_addr); err = nds_resolve_name(conn, 0x64, user_u, &user_id, ¬_wserv, (struct sockaddr *)&wserv_addr, &i); } if (err) { #ifdef ERR_MSG if (err == NCPL_ET_REQUEST_ERROR) fprintf(stderr, "error %d finding user\n", conn->completion); #endif goto err_exit; } if (not_wserv) { if (!(login_conn = wserv_conn = ncp_open_addr(&wserv_addr, &err))) goto err_exit; } else login_conn = conn; if ((err = nds_get_server_name(login_conn, &server_name)) != 0) goto err_exit2; if ((err = nds_resolve_name(login_conn, 0x62, server_name, &serv_id, NULL, NULL, NULL)) != 0) goto err_exit2; if ((err = nds_login(login_conn, user_id, pwd, serv_id, logindata, &u_priv_key))) { if ((err != NCPL_ET_REQUEST_ERROR) || (login_conn->completion != NDS_GRACE_PERIOD)) { #ifdef ERR_MSG if (err == NCPL_ET_REQUEST_ERROR) fprintf(stderr, "error %d logging in\n", login_conn->completion); #endif err_exit2:; conn->completion = login_conn->completion; goto err_exit; } grace_period = 1; } if (not_wserv) { free(server_name); if ((err = nds_get_server_name(conn, &server_name)) != 0) goto err_exit; if ((err = nds_resolve_name(conn, 0x62, server_name, &serv_id, NULL, NULL, NULL)) != 0) goto err_exit; if ((err = nds_resolve_name(conn, 0x51, user_u, &user_id, NULL, NULL, NULL)) !=0) goto err_exit; } if ((err = nds_authenticate(conn, user_id, serv_id, logindata, u_priv_key))) { #ifdef ERR_MSG if (err == NCPL_ET_REQUEST_ERROR) fprintf(stderr, "error %d authenticating\n", conn->completion); #endif goto err_exit; } if (grace_period && (!err)) { conn->completion = NDS_GRACE_PERIOD; err = NCPL_ET_REQUEST_ERROR; } err_exit: if (wserv_conn) ncp_close(wserv_conn); if (u_priv_key) { clearkey(u_priv_key); free(u_priv_key); } free(server_name); #ifdef RANDBUF memset(global_randbuf, 0, RANDBUFSIZE); g_rndp = global_randbuf + RANDBUFSIZE; #endif return err; }