/* NDS client for ncpfs Copyright (C) 1997 Arne de Bruijn Copyright (C) 1999, 2000 Petr Vandrovec 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. Revision history: 0.00 1997 Arne de Bruijn Initial release. 1.00 1999, November 20 Petr Vandrovec Moved file to new API. Added NWIsDSServer, key storing functions, NWDSAuthenticateConn. 1.01 2000, April 15 Petr Vandrovec Added new functions needed for NWDSGenerateObjectKeyPair. 1.02 2000, April 23 Petr Vandrovec Moved nds_login to ds/setkeys.c, added __NWDSGetPrivateKey. 1.03 2000, July 6 Petr Vandrovec Added DCV_DEREF_ALIASES into authentication functions. 1.04 2000, July 10 Gerhard Lausser Fixed strcpy_wc call in nds_login_auth. 1.05 2001, March 9 Petr Vandrovec Added NWIsDSServerW. For now ISO-8859-1 only. 1.06 2001, May 31 Petr Vandrovec Use correct parameter (nuint32) to GetLE32(). */ #define RANDBUF /* if defined: read random data once from /dev/urandom */ /*#define ERR_MSG*/ /* if defined: show error messages in nds_login_auth */ /*#define DEBUG_PRINT*/ /*#define FIND_ISR */ /* if defined: show reasons for -330 invalid response */ #define NCP_OBSOLETE #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "ncplib_i.h" #include "ndscrypt.h" #include "nwnet_i.h" #include "ndslib_i.h" #define USUALS typedef u_int32_t word32; typedef u_int16_t word16; typedef unsigned char boolean; #include "mpilib.h" #ifdef ERR_MSG #include #include "private/libintl.h" #define _(X) dgettext(PACKAGE, (X)) #define N_(X) (X) #endif #ifdef FIND_ISR #include #define ISRPrint(X...) do { fprintf(stderr, __FILE__ ":%d: ", __LINE__); fprintf(stderr, X); } while (0) #else #define ISRPrint(X...) do {} while (0) #endif NWDSCCODE NWIsDSServer(NWCONN_HANDLE conn, char* treename) { char buf[128]; size_t size; NWDSCCODE err; size_t namelen; err = ncp_send_nds(conn, 1, "\0\0\0", 3, buf, sizeof(buf), &size); if (err) return 0; if (size < 8) return 0; namelen = DVAL_LH(buf, 4); if (namelen > size - 8) return 0; if (namelen > MAX_TREE_NAME_CHARS + 1) return 0; if (buf[8 + namelen - 1]) return 0; if (treename) memcpy(treename, buf + 8, namelen); return 1; } NWDSCCODE NWIsDSServerW(NWCONN_HANDLE conn, wchar_t* treename) { char treechar[MAX_TREE_NAME_CHARS + 1]; NWDSCCODE err; err = NWIsDSServer(conn, treechar); if (err && treename) { char* treesrc = treechar; do { } while ((*treename++ = *treesrc++ & 0xFF) != 0); } return err; } static NWDSCCODE nds_read_pk(NWDSContextHandle ctx, const wchar_t* objname, Octet_String_T** post) { char attrname[256]; char reply_b[DEFAULT_MESSAGE_LEN]; Buf_T bufattrname; nuint32 iterhandle = NO_MORE_ITERATIONS; Buf_T replybuf; NWObjectCount cnt; wchar_t name[MAX_DN_CHARS+1]; NWObjectCount valcnt; enum SYNTAX synt; size_t size; Octet_String_T* ost; NWDSCCODE err; NWDSSetupBuf(&bufattrname, attrname, sizeof(attrname)); NWDSInitBuf(ctx, DSV_READ, &bufattrname); NWDSSetupBuf(&replybuf, reply_b, sizeof(reply_b)); NWDSPutAttrName(ctx, &bufattrname, (const NWDSChar*)L"Public Key"); err = NWDSRead(ctx, (const NWDSChar*)objname, DS_ATTRIBUTE_VALUES, 0, &bufattrname, &iterhandle, &replybuf); if (err) return err; err = NWDSGetAttrCount(ctx, &replybuf, &cnt); if (err) return err; if (cnt != 1) { ISRPrint("Attr count != 1 (%d)\n", cnt); return ERR_INVALID_SERVER_RESPONSE; } err = NWDSGetAttrName(ctx, &replybuf, (NWDSChar*)name, &valcnt, &synt); if (err) return err; if ((synt != SYN_OCTET_STRING) || (wcscmp(name, L"Public Key")) || (valcnt < 1)) { ISRPrint("Attr type wrong (synt=%d, valcnt=%d)\n", synt, valcnt); return ERR_INVALID_SERVER_RESPONSE; } err = NWDSComputeAttrValSize(ctx, &replybuf, SYN_OCTET_STRING, &size); if (err) return err; ost = (Octet_String_T*)malloc(size); if (!ost) return ENOMEM; err = NWDSGetAttrVal(ctx, &replybuf, SYN_OCTET_STRING, ost); if (err) { free(ost); return err; } *post = ost; return 0; } #ifdef RANDBUF #define RANDBUFSIZE 1236 /* total size of all fillrandom's for login+auth */ static char global_randbuf[RANDBUFSIZE]; static char *g_rndp = global_randbuf + RANDBUFSIZE; static ncpt_mutex_t randbuflock = NCPT_MUTEX_INITIALIZER; static void fillrandom(nuint8 *buf, size_t buflen) { ncpt_mutex_lock(&randbuflock); do { size_t i; if (g_rndp == global_randbuf + RANDBUFSIZE) { int fh; 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; } i = RANDBUFSIZE - (g_rndp - global_randbuf); if (i > buflen) { i = buflen; } memcpy(buf, g_rndp, i); buf += i; g_rndp += i; buflen -= i; } while (buflen); ncpt_mutex_unlock(&randbuflock); } #else static void fillrandom(nuint8 *buf, size_t 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 static int countbits_l(const unsigned char *buf, int bufsize) { unsigned char b; while ((--bufsize) && (!buf[bufsize])); b = buf[bufsize]; bufsize <<= 3; while (b) { b >>= 2; bufsize++; } return bufsize; } static 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); } static char keyprefix[] = {1, 0, 0, 0, 3, 0, 1, 0}; static int initkey(const unsigned char *key, const unsigned char **keyptr, size_t *keylen) { /* 1=ok, 0=err */ if (!memcmp(key, keyprefix, 8)) { if (keylen) *keylen = WVAL_LH(key, 8); if (keyptr) *keyptr = key + 10; return 1; } else return 0; } static void clearkey(unsigned char *key) { const unsigned char *keyptr; size_t keylen; if (initkey(key, &keyptr, &keylen)) memset(key, 0, keylen + 10); } static int findchunk(const unsigned char *keyptr, size_t keylen, const char *chunk, const unsigned char **chunkptr) { const unsigned char *p; if ((p = keyptr)) { while (p < keyptr + keylen) { if ((p[0] != chunk[0]) || (p[1] != chunk[1])) { p += 4 + p[2] + p[3]; } else { if (chunkptr) *chunkptr = p + 4; return p[2] + p[3]; } } } if (chunkptr) *chunkptr = NULL; return 0; } #ifdef DEBUG_PRINT static void printnumber(const char* m, const unit* p) { int i; printf("%s: ", m); for (i = global_precision; i--; ) { printf("%02X", p[i]); if ((i & 1) == 0) printf(" "); } printf("\n"); } static void dumpkey(const nuint8 *key, size_t keylen) { while (keylen > 8 + 4) { size_t kl = WVAL_LH(key, 2); const nuint8 *k2; nuint16 type = WVAL_HL(key, 0); size_t kl2 = kl; printf("El %c%c(%u): ", key[0], key[1], kl); key += 4; keylen -= kl + 4; k2 = key + kl; while (kl--) { printf("%02X", key[kl]); } printf("\n"); if (type == 0x4E4E) { unit yy[2000]; unit xx[2000]; memset(xx, 0, sizeof(xx)); memcpy(xx, key, kl2); set_precision(bytes2units(kl2)); mp_move_and_shift_left_bits(yy, xx, 8); mp_recip(xx, yy); printnumber("MyNI", xx); } key = k2; } } #endif static int checkkey(const unsigned char *key) { /* 0 - wrong key, != 0 - key ok */ unsigned char temp[8]; const unsigned char *keyptr, *p; size_t keylen; if ((initkey(key, &keyptr, &keylen)) && (findchunk(keyptr, keylen, "MA", &p))) { #ifdef DEBUG_PRINT mpkey(keyptr, keylen); #endif nwhash1init(temp, 8); nwhash1(temp, 8, key + 10, WVAL_LH(key, 8) - 20); return (!memcmp(p, temp, 8)); } return 0; } static ncpt_mutex_t mpilock = NCPT_MUTEX_INITIALIZER; static long modexpkey(const unsigned char *s_key, unsigned char *buf, unsigned char *outbuf, int bufsize) { const unsigned char *s_keyptr; size_t s_keylen; int i, nbits, nblocksize; int err = -1; unitptr nmod, nexp, nin, nout; const unsigned char *p; nmod = nexp = nin = nout = NULL; if (!initkey(s_key, &s_keyptr, &s_keylen)) { ISRPrint("Initkey failed\n"); return ERR_INVALID_SERVER_RESPONSE; } i = findchunk(s_keyptr, s_keylen, "NN", &p); if (!p) { ISRPrint("NN chunk not found\n"); return ERR_INVALID_SERVER_RESPONSE; } nbits = countbits_l(p, i); nblocksize = ((nbits + 31) & (~31)) >> 3; if (!(nmod = malloc(nblocksize * 4))) return ENOMEM; copyfill(nmod, nblocksize, p, i); i = findchunk(s_keyptr, s_keylen, "EN", &p); err = ERR_INVALID_SERVER_RESPONSE; if (!p) { ISRPrint("EN chunk not found\n"); goto end1; } nexp = (unitptr)(((char*)nmod) + nblocksize); nin = (unitptr)(((char*)nexp) + nblocksize); nout = (unitptr)(((char*)nin) + nblocksize); copyfill(nexp, nblocksize, p, i); copyfill(nin, nblocksize, buf, bufsize); ncpt_mutex_lock(&mpilock); set_precision(bytes2units(nblocksize)); i = mp_modexp(nout, nin, nexp, nmod); ncpt_mutex_unlock(&mpilock); if (i) { ISRPrint("mp_modexp failed\n"); err = ERR_INVALID_SERVER_RESPONSE; } else { copyfill(outbuf, bufsize, nout, nblocksize); err = 0; } end1: free(nmod); return err; } /* ctx must be in WCHAR_T mode, without DCV_CANONICALIZE_NAMES */ static NWDSCCODE get_public_key(NWDSContextHandle ctx, const wchar_t* objname, nuint8 **key) { const unsigned char *keybuf, *kptr; NWDSCCODE err; size_t keylen, ofs, klen; Octet_String_T* ost; err = nds_read_pk(ctx, objname, &ost); if (err) return err; keybuf = ost->data; keylen = ost->length; ofs = WVAL_LH(keybuf, 10) + 0x1a; if ((ofs > keylen) || (!initkey(keybuf + ofs, &kptr, &klen)) || (klen + ofs > keylen) || (!checkkey(keybuf + ofs))) { err = ERR_INVALID_SERVER_RESPONSE; ISRPrint("Wrong key\n"); goto err_exit; } if (key) { unsigned char *kbuf; if (!(kbuf = malloc(klen + 10))) { err = ENOMEM; goto err_exit; } memcpy(kbuf, keybuf + ofs, klen + 10); *key = kbuf; } err = 0; err_exit: free(ost); return err; } static NWDSCCODE __AlignAndEncryptBlockWithSK( const nuint8* hash, const nuint8* idata, size_t ilen, nuint8* odata, size_t* olen ) { size_t filler; memcpy(odata, idata, ilen); filler = 8 - (ilen + 5) % 8; memset(odata + ilen, filler, filler); ilen += filler; nwhash1init(odata + ilen, 5); nwhash1(odata + ilen, 5, odata, ilen); ilen += 5; nwencryptblock(hash, odata, ilen, odata); *olen = ilen; return 0; } NWDSCCODE __NWEncryptWithSK( const void* hashsrc, size_t hashsrclen, const void* idata, size_t ilen, void* odata, size_t* olen ) { int err; size_t len; nuint8 hash[8]; int i; if (!idata || !ilen || !odata || !olen) return ERR_NULL_POINTER; if (!hashsrc) hashsrclen = 0; nwhash1init(hash, 8); for (i = 0; i < 10; i++) { nwhash1(hash, 8, hashsrc, hashsrclen); } err = __AlignAndEncryptBlockWithSK(hash, idata, ilen, (nuint8*)odata + 12, &len); if (err) return err; *olen = len + 12; DSET_LH(odata, 0, 1); WSET_LH(odata, 4, 1); WSET_LH(odata, 6, 6); WSET_LH(odata, 8, len); WSET_LH(odata, 10, ilen); return 0; } static const char buf2str1[8] = { 1,0,0,0, 9,0, 2,0}; static const char buf2str2[16] = {65,0,0,0, 1,0,0,0, 1,0, 9,0, 53,0, 28,0}; NWDSCCODE rsa_crypt2( const nuint8* s_key, Buf_T* input, Buf_T* output) { char hashrand[8], temp[8]; unsigned short cryptbuf[128]; nuint8 buf2[56]; NWDSCCODE err; void* data; size_t datalen; nuint8* outp; nuint8 b2[28]; nuint8* ln1; nuint8* sp1; size_t outlen; fillrandom(b2, 28); buf2[0] = 11; memcpy(buf2 + 1, b2, 28); memset(buf2 + 29, 11, 11); memset(buf2 + 40, 0, 16); nwhash1(buf2 + 40, 5, buf2 + 1, 39); nwhash1(buf2 + 45, 2, buf2, 45); fillrandom(buf2 + 47, 5); err = modexpkey(s_key, buf2, buf2, 56); if (err) return err; err = NWDSBufPut(output, buf2str1, sizeof(buf2str1)); if (err) return err; sp1 = NWDSBufPutPtr(output, 4); if (!sp1) return ERR_BUFFER_FULL; err = NWDSBufPut(output, buf2str2, sizeof(buf2str2)); if (err) return err; err = NWDSBufPut(output, buf2, 56); if (err) return err; memset(buf2, 0, sizeof(buf2)); ln1 = NWDSBufPutPtr(output, 4); if (!ln1) return ERR_BUFFER_FULL; outp = NWDSBufPeekPtr(output); if (!outp) return ERR_BUFFER_FULL; data = NWDSBufRetrieve(input, &datalen); err = __NWEncryptWithSK(b2, 28, data, datalen, outp, &outlen); if (err) return err; DSET_LH(ln1, 0, outlen); NWDSBufPutSkip(output, outlen); DSET_LH(sp1, 0, outlen + 76); memset(hashrand, 0, sizeof(hashrand)); memset(temp, 0, sizeof(temp)); memset(cryptbuf, 0, sizeof(cryptbuf)); return 0; } NWDSCCODE rsa_crypt( NWDSContextHandle ctx, const wchar_t* servername, Buf_T* input, Buf_T* output) { NWDSCCODE err; nuint8 *s_key; if ((err = get_public_key(ctx, servername, &s_key))) return err; err = rsa_crypt2(s_key, input, output); free(s_key); return err; } static NWDSCCODE NWDXSetKeysOnConns( NWDS_HANDLE dxh, const void* authinfo, size_t authinfo_len) { struct list_head* stop = &dxh->conns; struct list_head* current; for (current = stop->next; current != stop; current = current->next) { NWCONN_HANDLE conn = list_entry(current, struct ncp_conn, nds_ring); ncp_set_private_key(conn, authinfo, authinfo_len); } return 0; } static NWDSCCODE NWDXSetKeys( NWDS_HANDLE dxh, const nuint8 logindata[8], const wchar_t* username, const nuint8* pkey, size_t pkey_len) { union __NWDSAuthInfo* ndai; size_t slen; size_t tlen; size_t cpos; if (!dxh || !logindata || !username || !pkey) return ERR_NULL_POINTER; slen = (wcslen(username) + 1) * sizeof(wchar_t); cpos = ROUNDBUFF(sizeof(*ndai)); tlen = cpos + ROUNDBUFF(slen) + ROUNDBUFF(pkey_len); ndai = (union __NWDSAuthInfo*)malloc(tlen); if (!ndai) return ENOMEM; ndai->header.total = tlen; ndai->header.version = 0; ndai->header.hdrlen = sizeof(*ndai); memcpy(ndai->header.logindata, logindata, 8); ndai->header.name_len = slen; memcpy(ndai->data + cpos, username, slen); ndai->header.privkey_len = pkey_len; cpos += ROUNDBUFF(slen); memcpy(ndai->data + cpos, pkey, pkey_len); /* ignore errors... there is nothing we can do around */ mlock(ndai, tlen); NWDXSetKeysOnConns(dxh, ndai, tlen); if (dxh->authinfo) { tlen = dxh->authinfo->header.total; memset(dxh->authinfo, 0, tlen); munlock(dxh->authinfo, tlen); free(dxh->authinfo); } dxh->authinfo = ndai; return 0; } NWDSCCODE NWDSSetKeys( NWDSContextHandle ctx, const nuint8 logindata[8], const wchar_t* username, const nuint8* pkey, size_t pkey_len) { NWDSCCODE err; err = NWDSIsContextValid(ctx); if (err) return err; return NWDXSetKeys(ctx->ds_connection, logindata, username, pkey, pkey_len); } NWDSCCODE NWDSGetKeys( NWDSContextHandle ctx, union __NWDSAuthInfo** pndai, size_t* pndailen) { NWDS_HANDLE dxh; union __NWDSAuthInfo* ndai; if (!ctx || !pndai) return ERR_NULL_POINTER; dxh = ctx->ds_connection; if (!dxh) return ERR_NOT_LOGGED_IN; ndai = dxh->authinfo; if (!ndai) { /* OK, we do not have authentication info */ /* but maybe some permanent/shared connection */ /* does have authentication information */ struct list_head* stop = &dxh->conns; struct list_head* current; size_t authinfo_len = 0; for (current = stop->next; current != stop; current = current->next) { NWDSCCODE err; NWCONN_HANDLE conn = list_entry(current, struct ncp_conn, nds_ring); err = ncp_get_private_key(conn, NULL, &authinfo_len); if (err) continue; /* kernel without private key support */ if (!authinfo_len) continue; /* no private key on this connection */ ndai = (union __NWDSAuthInfo*)malloc(authinfo_len); if (!ndai) continue; /* not enough memory */ err = ncp_get_private_key(conn, ndai, &authinfo_len); if (!err) break; /* we have it */ free(ndai); ndai = NULL; } if (!ndai) return ERR_NOT_LOGGED_IN; mlock(ndai, authinfo_len); NWDXSetKeysOnConns(dxh, ndai, authinfo_len); if (dxh->authinfo) { size_t tlen = dxh->authinfo->header.total; memset(dxh->authinfo, 0, tlen); munlock(dxh->authinfo, tlen); free(dxh->authinfo); } dxh->authinfo = ndai; } *pndai = ndai; *pndailen = ndai->header.total; return 0; } static NWDSCCODE NWDXUpdateKey( NWDS_HANDLE dxh, NWCONN_HANDLE conn ) { size_t authinfo_len; union __NWDSAuthInfo* ndai; NWDSCCODE err; if (conn->nds_conn != dxh) { return NWE_PARAM_INVALID; } if (dxh->authinfo) { return EBUSY; } err = ncp_get_private_key(conn, NULL, &authinfo_len); if (err) return err; /* kernel without private key support */ if (!authinfo_len) return ERR_NOT_LOGGED_IN; /* no private key on this connection */ ndai = (union __NWDSAuthInfo*)malloc(authinfo_len); if (!ndai) return ENOMEM; /* not enough memory */ err = ncp_get_private_key(conn, ndai, &authinfo_len); if (err) { free(ndai); return err; } mlock(ndai, authinfo_len); NWDXSetKeysOnConns(dxh, ndai, authinfo_len); if (dxh->authinfo) { size_t tlen = dxh->authinfo->header.total; memset(dxh->authinfo, 0, tlen); munlock(dxh->authinfo, tlen); free(dxh->authinfo); } dxh->authinfo = ndai; return 0; } /* FIXME: Internal only! */ static NWDSCCODE NWDXAddConnection(NWDS_HANDLE dsh, NWCONN_HANDLE conn) { ncpt_mutex_lock(&nds_ring_lock); list_del(&conn->nds_ring); conn->state++; list_add(&conn->nds_ring, &dsh->conns); conn->nds_conn = dsh; ncpt_mutex_unlock(&nds_ring_lock); if (!dsh->authinfo) { NWDXUpdateKey(dsh, conn); } return 0; } NWDSCCODE NWDSAddConnection(NWDSContextHandle ctx, NWCONN_HANDLE conn) { return NWDXAddConnection(ctx->ds_connection, conn); } #define xprintf(X...) NWDSCCODE __NWDSGetPrivateKey( NWCONN_HANDLE conn, const nuint8* connPublicKey, const nuint8 rndseed[4], NWObjectID objectID, const nuint8 pwd[16], nuint8 logindata[8], nuint8** privateKey, size_t* privateKeyLen ) { nuint8 crypt1strc[40]; Buf_T cbuf; Buf_T rpb; Buf_T rqb; nuint8 tempbuf[1200]; nuint8 rqb_b[1200]; nuint8 rpb_b[1200]; nuint8 logindata2[8]; nuint8* p; size_t bl; NWDSCCODE err; NWDSCCODE grace_err; nuint32 len; size_t n2; size_t n3; size_t i; nuint8 temp[8]; nuint8 hashshuf[8]; bl = sizeof(crypt1strc); err = __NWEncryptWithSK(pwd, 16, rndseed, 4, crypt1strc, &bl); if (err) goto err_exit; NWDSSetupBuf(&cbuf, tempbuf, sizeof(tempbuf)); p = NWDSBufPutPtr(&cbuf, 4); fillrandom(p, 4); NWDSBufPutLE32(&cbuf, 1024); p = NWDSBufPutPtr(&cbuf, 1024); fillrandom(p, 1024); NWDSBufPutBuffer(&cbuf, crypt1strc, bl); NWDSSetupBuf(&rqb, rqb_b, sizeof(rqb_b)); NWDSInitBuf(NULL, DSV_FINISH_LOGIN, &rqb); err = rsa_crypt2(connPublicKey, &cbuf, &rqb); if (err) goto err_exit; NWDSSetupBuf(&rpb, rpb_b, sizeof(rpb_b)); if (logindata) err = __NWDSFinishLoginV2(conn, 0, objectID, &rqb, logindata, &rpb); else err = __NWDSFinishLoginV2(conn, 1, objectID, &rqb, logindata2, &rpb); if ((err != 0) && (err != NWE_PASSWORD_EXPIRED)) goto err_exit; grace_err = err; err = NWDSBufGetLE32(&rpb, &len); if (err) goto err_exit; xprintf("Want len %d\n", len); p = NWDSBufGetPtr(&rpb, len); if (!p) { err = ERR_BUFFER_EMPTY; goto err_exit; } err = ERR_INVALID_SERVER_RESPONSE; if (len < 12) { ISRPrint("Key shorter than 12 bytes (%d)\n", len); goto err_exit; } xprintf("%04X %04X %02X\n", DVAL_LH(p, 0), DVAL_LH(p, 4), WVAL_LH(p, 8)); if ((DVAL_LH(p, 0) != 0x00000001) || (DVAL_LH(p, 4) != 0x00060001)) { ISRPrint("Invalid key start (%08X %08X %04X)\n", DVAL_LH(p, 0), DVAL_LH(p, 4), WVAL_LH(p, 8)); goto err_exit; } n3 = WVAL_LH(p, 8); if (len < n3 + 12) { ISRPrint("Invalid key len (%d < %d)\n", len, n3 + 12); goto err_exit; } p += 12; 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); xprintf("G1\n"); if (memcmp(temp, p + n3 - 5, 5)) { ISRPrint("Wrong hash\n"); goto err_exit; } xprintf("G2\n"); if (memcmp(p, tempbuf, 4)) { ISRPrint("Wrong hash #2\n"); goto err_exit; } xprintf("G3\n"); n2 = DVAL_LH(p, 4); p += 8; for (i = 0; i < n2; i++) p[i] ^= tempbuf[i+8]; xprintf("G4: %08X %08X\n", DVAL_LH(p, 0), DVAL_LH(p, 4)); if ((DVAL_LH(p, 0) != 0x00000001) || (DVAL_LH(p, 4) != 0x00060001)) { ISRPrint("Invalid start #2: %08X %08X\n", DVAL_LH(p, 0), DVAL_LH(p, 4)); goto err_exit; } n3 = WVAL_LH(p, 8); p += 12; nwhash1init(hashshuf, 8); for (i = 10; i; i--) nwhash1(hashshuf, 8, pwd, 16); nwdecryptblock(hashshuf, p, n3, p); xprintf("G5: %08X %08X\n", DVAL_LH(p, 0), DVAL_LH(p, 4)); if ((DVAL_LH(p, 0) != 1) || (WVAL_LH(p, 4) != 2)) { ISRPrint("Invalid start #3: %08X %08X\n", DVAL_LH(p, 0), DVAL_LH(p, 4)); goto err_exit; } n3 = WVAL_LH(p, 6); p += 8; { unsigned char* tb; if (!(tb = malloc(n3 + 10))) { err = ENOMEM; goto err_exit; } DSET_LH(tb, 0, 1); WSET_LH(tb, 4, 3); WSET_LH(tb, 6, 1); WSET_LH(tb, 8, n3); memcpy(tb + 10, p, n3); xprintf("G6\n"); if (!checkkey(tb)) { free(tb); ISRPrint("Post check failed\n"); goto err_exit; } xprintf("G7\n"); if (privateKeyLen) *privateKeyLen = n3 + 10; if (privateKey) { *privateKey = tb; } else { memset(tb, 0, n3 + 10); free(tb); } } err = grace_err; err_exit: memset(logindata2, 0, sizeof(logindata2)); memset(tempbuf, 0, sizeof(tempbuf)); memset(rpb_b, 0, sizeof(rpb_b)); memset(hashshuf, 0, sizeof(hashshuf)); memset(crypt1strc, 0, sizeof(crypt1strc)); memset(temp, 0, sizeof(temp)); memset(rqb_b, 0, sizeof(rqb_b)); memset(temp, 0, sizeof(temp)); return err; } static NWDSCCODE nds_beginauth2( NWCONN_HANDLE conn, NWObjectID user_id, nuint8 authid[4], const nuint8* s_key) { nuint8 *p, *pend, *n_temp, temp[8], *k1end; nuint8 randno[4]; NWDSCCODE err; int n1, n3; u_int16_t n3a; char rpb_b[DEFAULT_MESSAGE_LEN] __attribute__ ((aligned (4))); Buf_T rpb; size_t k1tl; n_temp = NULL; fillrandom(randno, 4); NWDSSetupBuf(&rpb, rpb_b, sizeof(rpb_b)); err = __NWDSBeginAuthenticationV0(conn, user_id, randno, authid, &rpb); if (err) goto err_exit; err = ERR_INVALID_SERVER_RESPONSE; p = rpb.curPos; pend = rpb.dataend; if (pend - p < 12) { ISRPrint("Invalid len: %u < 12\n", pend - p); goto err_exit; } if ((DVAL_LH(p, 0) != 0x00000001) || (DVAL_LH(p, 4) != 0x00020009)) { ISRPrint("Invalid start: %08X %08X\n", DVAL_LH(p, 0), DVAL_LH(p, 4)); goto err_exit; } n3 = DVAL_LH(p, 8); if (n3 < 16) { ISRPrint("Invalid len: %u < 16\n", n3); goto err_exit; } p += 12; if (pend - p < n3) { ISRPrint("Invalid len: %u < %u\n", pend - p, n3); goto err_exit; } pend = p + n3; k1tl = DVAL_LH(p, 0); if (k1tl < 12) { ISRPrint("K1Tl %u < 12\n", k1tl); goto err_exit; } p += 4; k1end = p + k1tl; if ((DVAL_LH(p, 0) != 0x00000001) || (DVAL_LH(p, 4) != 0x000A0001)) { ISRPrint("Invalid start: %08X %08X\n", DVAL_LH(p, 0), DVAL_LH(p, 4)); goto err_exit; } n3a = WVAL_LH(p, 8); p += 12; if (k1end - p < n3a) { ISRPrint("Invalid len: %u < %u\n", k1end - p, n3a); goto err_exit; } n1 = ((countbits_l(p, n3a) + 31) & (~31)) >> 3; if (n1 < 52) { ISRPrint("Too short key: %u < 52\n", n1); goto err_exit; } if (!(n_temp = malloc(n1))) { err = ENOMEM; goto err_exit; } copyfill(n_temp, n1, p, n3a); /* align p to the first 4 byte boundary beyond k1end */ p = k1end + ((p - k1end) & 3); err = modexpkey(s_key, n_temp, n_temp, n1); if (err) { ISRPrint("modexpkey failed\n"); goto err_exit; } err = ERR_INVALID_SERVER_RESPONSE; nwhash1init(temp, 7); nwhash1(temp + 5, 2, n_temp, 45); nwhash1(temp, 5, n_temp + 1, 39); if (memcmp(temp, n_temp + 40, 7)) { ISRPrint("Invalid hash\n"); 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 (pend - p < 16) { ISRPrint("Invalid len: %u < 16\n", pend - p); goto err_exit; } if ((DVAL_LH(p, 0) != 28) || (DVAL_LH(p, 4) != 0x00000001) || (DVAL_LH(p, 8) != 0x00060001) || (DVAL_LH(p, 12) != 0x00040010)) { ISRPrint("Invalid start: %u %08X %08X %08X\n", DVAL_LH(p, 0), DVAL_LH(p, 4), DVAL_LH(p, 8), DVAL_LH(p, 12)); goto err_exit; } p += 16; if (pend - p < 16) { ISRPrint("Invalid len: %u < 16\n", pend - p); 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; else ISRPrint("Invalid hash\n"); err_exit: if (n_temp) free(n_temp); return err; } static unsigned char *allocfillchunk(const unsigned char *keyptr, int keylen, const char *chunk, int destsize) { const unsigned char *p; unsigned char *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; } static NWDSCCODE nds_beginauth( NWCONN_HANDLE conn, NWObjectID user_id, NWDSContextHandle ctx, const wchar_t* servername, nuint8 authid[4]) { NWDSCCODE err; nuint8* s_key; if ((err = get_public_key(ctx, servername, &s_key))) return err; err = nds_beginauth2(conn, user_id, authid, s_key); free(s_key); return err; } static NWDSCCODE gen_auth_data( Buf_T* outbuf, const unsigned char *u_key, const unsigned char *u_priv_key, const nuint8* authid, nuint8 *loginstrc, int loginstrc_len) { const unsigned char *keyptr; size_t keylen; int i, j; int nbits, nblocksize, nbytes, nblocksize_nw; 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; unitptr randbuf; const unsigned char *pkey; nuint8 *p; nuint8 *tempbuf; nuint8 hashbuf[0x42]; NWDSCCODE err; n_temp = n_mod = n_exp = n_pn = n_qn = n_dp = n_dq = n_cr = n_key = NULL; if (!initkey(u_key, &keyptr, &keylen)) { ISRPrint("Initkey failed\n"); return ERR_INVALID_SERVER_RESPONSE; } i = findchunk(keyptr, keylen, "NN", &pkey); if (!pkey) { ISRPrint("NN chunk not found\n"); return ERR_INVALID_SERVER_RESPONSE; } nbits = countbits_l(pkey, i); nbytes = (nbits + 7) >> 3; nmask = (unsigned char)(255 >> (8 - (nbits & 7))); /* we really want (x + 31) & ~15... I'm sorry, but Novell thinks that way */ nblocksize_nw = ((nbits + 31) & (~15)) >> 3; ncpt_mutex_lock(&mpilock); set_precision(bytes2units(nblocksize_nw)); nblocksize = units2bytes(global_precision); n_mod = (unitptr)allocfillchunk(keyptr, keylen, "NN", nblocksize); n_exp = (unitptr)allocfillchunk(keyptr, keylen, "EN", nblocksize); if (!initkey(u_priv_key, &keyptr, &keylen)) { err = ERR_INVALID_SERVER_RESPONSE; ISRPrint("initkey failed\n"); 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; } DSET_LH(tempbuf, 0, 0); DSET_LH(tempbuf, 4, 0x3c); memcpy(tempbuf + 8, authid, 4); DSET_LH(tempbuf, 12, loginstrc_len); memcpy(tempbuf + 16, loginstrc, loginstrc_len); nwhash2init(hashbuf); nwhash2block(hashbuf, tempbuf, loginstrc_len + 16); free(tempbuf); n_temp = malloc(nblocksize * 6); n_key_dp = (unitptr)(((char*)n_temp) + nblocksize); n_key_dq = (unitptr)(((char*)n_key_dp) + 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 = (unitptr)(((char*)n_key_dq) + nblocksize); memset(randbuf, 0, nblocksize * 3); p = NWDSBufPutPtr(outbuf, 12 + nblocksize_nw * 6); DSET_LH(p, 0, 0x00000001); DSET_LH(p, 4, 0x00100008); WSET_LH(p, 8, 3); WSET_LH(p, 10, nblocksize_nw * 3); memset(p + 12, 0, nblocksize_nw * 6); up = randbuf; up2 = (unitptr)(p + 12); for (i = 3; i; i--) { fillrandom((nuint8 *)up, nbytes); ((nuint8 *)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); up = (unitptr)((char*)up + nblocksize); up2 = (unitptr)((char*)up2 + nblocksize_nw); } nwhash2block(hashbuf, p+12, nblocksize_nw * 3); nwhash2end(hashbuf); up = randbuf; for (i = 0; i < 3; i++) { mp_init(n_temp, WVAL_LH(hashbuf, i<<1)); mp_modexp(up2, n_key, n_temp, n_mod); stage_modulus(n_mod); mp_modmult(up2, up2, up); up = (unitptr)((char*)up + nblocksize); up2 = (unitptr)((char*)up2 + nblocksize_nw); } if (n_temp) { mp_init0(n_temp); mp_init0(n_key_dp); mp_init0(n_key_dq); free(n_temp); } err = 0; err_exit: memset(hashbuf, 0, sizeof(hashbuf)); 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); } ncpt_mutex_unlock(&mpilock); free(n_mod); free(n_exp); return err; } NWDSCCODE NWDSAuthenticateConn( NWDSContextHandle inctx, NWCONN_HANDLE conn) { /* it can be per NWDS_HANDLE lock... */ static ncpt_mutex_t auth_lock = NCPT_MUTEX_INITIALIZER; nuint8 authid[4]; NWDSCCODE err; size_t user_name_len; nuint8 *loginstrc; size_t loginstrc_len; nuint8 *u_key; Buf_T signbuf; char signbuf_b[DEFAULT_MESSAGE_LEN]; Buf_T keybuf; char keybuf_b[DEFAULT_MESSAGE_LEN]; wchar_t server_name[MAX_DN_CHARS+1]; NWObjectID user_id; unsigned char signkey[8]; wchar_t* w_user_name; nuint8* u_priv_key; nuint8* logindata; union __NWDSAuthInfo* ndai; size_t ndailen; NWDSContextHandle ctx; if (!conn) return ERR_NULL_POINTER; ncpt_mutex_lock(&auth_lock); if (conn->connState & CONNECTION_AUTHENTICATED) { err = 0; goto err_exit3; } err = NWDSGetKeys(inctx, &ndai, &ndailen); if (err) goto err_exit3; w_user_name = (wchar_t*)(ndai->data + ndai->header.hdrlen); logindata = ndai->header.logindata; u_priv_key = ndai->data + ndai->header.hdrlen + ndai->header.name_len; err = __NWDSGetServerDN(conn, server_name, sizeof(server_name)); if (err) goto err_exit3; err = NWDSDuplicateContextHandle(inctx, &ctx); if (err) goto err_exit3; ctx->dck.flags = DCV_XLATE_STRINGS | DCV_TYPELESS_NAMES | DCV_DEREF_ALIASES; ctx->priv_flags |= DCV_PRIV_AUTHENTICATING; err = NWDSSetContext(ctx, DCK_LOCAL_CHARSET, "WCHAR_T//"); if (err) goto err_exit2; /* FIXME! deref aliases? */ err = NWDSMapNameToID(ctx, conn, (const NWDSChar*)w_user_name, &user_id); if (err) goto err_exit2; if ((err = nds_beginauth(conn, user_id, ctx, server_name, authid))) goto err_exit2; u_key = NULL; loginstrc = malloc(DEFAULT_MESSAGE_LEN); if (!loginstrc) { err = ENOMEM; goto err_exit2; } user_name_len = DEFAULT_MESSAGE_LEN - 22; err = __NWDSGetObjectDNUnicode(conn, user_id, (unicode*)(loginstrc + 22), &user_name_len); if (err) goto err_exit; loginstrc_len = user_name_len + 22; DSET_LH(loginstrc, 0, 1); WSET_LH(loginstrc, 4, 6); memcpy(loginstrc + 6, logindata, 8); fillrandom(loginstrc + 14, 4); WSET_LH(loginstrc, 18, 0); WSET_LH(loginstrc, 20, user_name_len); if ((err = get_public_key(ctx, w_user_name, &u_key))) goto err_exit; NWDSSetupBuf(&signbuf, signbuf_b, sizeof(signbuf_b)); #ifdef SIGNATURES if (conn->sign_wanted) { Buf_T tmp; NWDSSetupBuf(&tmp, signkey, 8); NWDSBufPutSkip(&tmp, 8); fillrandom(signkey, 8); err = rsa_crypt(ctx, server_name, &tmp, &signbuf); if (err) goto err_exit; } #endif NWDSSetupBuf(&keybuf,keybuf_b, sizeof(keybuf_b)); if ((err = gen_auth_data(&keybuf, u_key, u_priv_key, authid, loginstrc, loginstrc_len))) goto err_exit; err = __NWDSFinishAuthenticationV0(conn, &signbuf, loginstrc, loginstrc_len, &keybuf); memset(keybuf_b, 0, sizeof(keybuf_b)); if (err) goto err_exit; ncp_set_private_key(conn, ndai, ndailen); conn->user_id = user_id; conn->user_id_valid = 1; conn->connState |= CONNECTION_AUTHENTICATED; #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 (u_key) free(u_key); err_exit2: NWDSFreeContext(ctx); err_exit3: ncpt_mutex_unlock(&auth_lock); return err; } long nds_login_auth(struct ncp_conn *conn, const char *user, const char *pwd) { long err; wchar_t user_w[MAX_DN_CHARS+1]; nuint8 *u_priv_key = NULL; wchar_t server_name[MAX_DN_CHARS+1]; NWCONN_HANDLE userconn = NULL; int i; struct timeval tv; int grace_period = 0; NWDSContextHandle ctx = NULL; #ifdef ERR_MSG char buf[256]; /* to print username */ #endif gettimeofday(&tv, NULL); srand(tv.tv_usec); if (strlen(user) > MAX_DN_CHARS) return ENAMETOOLONG; err = NWDSCreateContextHandle(&ctx); if (err) return err; ctx->dck.flags = DCV_XLATE_STRINGS | DCV_TYPELESS_NAMES | DCV_DEREF_ALIASES | DCV_CANONICALIZE_NAMES; ctx->priv_flags |= DCV_PRIV_AUTHENTICATING; err = NWDSXlateFromCtx(ctx, user_w, sizeof(user_w), user); if (err) goto err_exit; err = NWDSSetContext(ctx, DCK_LOCAL_CHARSET, "WCHAR_T//"); if (err) goto err_exit; NWDSAddConnection(ctx, conn); err = nds_login(ctx, (const NWDSChar*)user_w, pwd); if ((err == ERR_NO_SUCH_ENTRY) && (user_w[0] != '.') && (user_w[wcslen(user_w)-1] != '.')) { #ifdef ERR_MSG printf(_("User %s not found in current context.\n" "Trying server context...\n"), user); #endif err = __NWDSGetServerDN(conn, server_name, sizeof(server_name)); if (err) goto err_exit; i = 0; while (server_name[i] && (server_name[i] != '.')) i++; if (wcslen(user_w)+wcslen(server_name+i)+1 > MAX_DN_CHARS) { err = ENAMETOOLONG; goto err_exit; } memcpy(user_w + wcslen(user_w), server_name + i, (wcslen(server_name + i) + 1) * sizeof(wchar_t)); ctx->dck.flags &= ~DCV_CANONICALIZE_NAMES; err = nds_login(ctx, (const NWDSChar*)user_w, pwd); } if (err) { if (err != NWE_PASSWORD_EXPIRED) { #ifdef ERR_MSG fprintf(stderr, _("error %d logging in\n"), err); #endif goto err_exit; } grace_period = 1; } if ((err = NWDSAuthenticateConn(ctx, conn))) { #ifdef ERR_MSG fprintf(stderr, _("error %d authenticating\n"), err); #endif goto err_exit; } if (grace_period && (!err)) { err = NWE_PASSWORD_EXPIRED; } err_exit: if (userconn) NWCCCloseConn(userconn); if (ctx) NWDSFreeContext(ctx); if (u_priv_key) { clearkey(u_priv_key); free(u_priv_key); } #ifdef RANDBUF ncpt_mutex_lock(&randbuflock); memset(global_randbuf, 0, RANDBUFSIZE); g_rndp = global_randbuf + RANDBUFSIZE; ncpt_mutex_unlock(&randbuflock); #endif return err; } #ifdef DEBUG_PRINT static void dgcdv(const unit* a, const unit* b, const unit* c, const unit* d, const unit* e) { unit a1[4096]; unit a2[4096]; unit a3[4096]; printnumber("Z", a); printnumber("U", b); printnumber("V", c); printnumber("X", d); printnumber("Y", e); mp_mult(a1, b, d); mp_mult(a2, c, e); printnumber("UX", a1); printnumber("VY", a2); mp_add(a1, a2); printnumber("UX+VY", a1); mp_mod(a3, a1, d); printnumber("UX+VY%U", a3); mp_mod(a3, a1, e); printnumber("UX+VY%V", a3); } #endif /* ctx must be in wchar_t mode */ NWDSCCODE __NWDSGetPublicKeyFromConnection(NWDSContextHandle ctx, NWCONN_HANDLE conn, nuint8 **skey) { wchar_t serverName[MAX_DN_CHARS+1]; NWDSCCODE err; *skey = NULL; err = __NWDSGetServerDN(conn, serverName, sizeof(serverName)); if (err) return err; err = get_public_key(ctx, serverName, skey); if (err) return err; return 0; } struct keyparam { unit n_exp[bytes2units(106)]; unit n_mod[bytes2units(106)]; unit n_recip[bytes2units(106)]; unit n_pn[bytes2units(106 /*50*/)]; unit n_qn[bytes2units(106 /*50*/)]; unit n_dp[bytes2units(106 /*50*/)]; unit n_dq[bytes2units(106 /*50*/)]; unit n_cr[bytes2units(106)]; int n_recip_present; size_t bytes; nuint8 BA; size_t BL; }; static int CreatePublicKey(const struct keyparam* kp, nuint8 *pubkey, size_t *pubkey_len) { nuint8* ptr; nuint8 hash[8]; size_t n_exp_len; size_t len; n_exp_len = (countbits(kp->n_exp) + 7) / 8 + 2; len = (4 + 4) /* BV */ + (4 + 1) * 2 /* BC, BA */ + (4 + 2) /* BL */ + (4 + kp->bytes * 2) /* NN */ + (4 + n_exp_len) /* EN */ + (4 + 8) /* MA */ + (2 + 6); /* PURSAF */ if (kp->n_recip_present) len += 4 + kp->bytes * 2 + 6; /* NI */ if (len + 10 > *pubkey_len) return NWE_BUFFER_OVERFLOW; *pubkey_len = len + 10; DSET_LH(pubkey, 0, 1); WSET_LH(pubkey, 4, 3); WSET_LH(pubkey, 6, 1); WSET_LH(pubkey, 8, len); pubkey += 10; ptr = pubkey; #define PUTHDR(ptr, type, len) \ WSET_HL(ptr, 0, (type)); \ WSET_LH(ptr, 2, len); \ ptr += 4; #define PUTMEM(ptr, type, len, data) \ PUTHDR(ptr, (type), len); \ memcpy(ptr, data, len); \ ptr += len; #define PUTNUM(ptr, type, len, data) \ PUTHDR(ptr, (type), len); \ memcpy(ptr, data, len); \ ptr += len; #define PUTBYTE(ptr, type, data) \ PUTHDR(ptr, (type), 1); \ BSET(ptr, 0, data); \ ptr += 1; #define PUTWORD(ptr, type, data) \ PUTHDR(ptr, (type), 2); \ WSET_LH(ptr, 0, data); \ ptr += 2; PUTMEM(ptr, 0x4256, 4, "1.7b"); PUTBYTE(ptr, 0x4243, 3); PUTBYTE(ptr, 0x4241, kp->BA); PUTWORD(ptr, 0x424C, kp->BL); PUTNUM(ptr, 0x4E4E, kp->bytes * 2, kp->n_mod); if (kp->n_recip_present) { PUTNUM(ptr, 0x4E49, kp->bytes * 2 + 6, kp->n_recip); } PUTNUM(ptr, 0x454E, n_exp_len, kp->n_exp); nwhash1init(hash, sizeof(hash)); nwhash1(hash, sizeof(hash), pubkey, ptr - pubkey); PUTMEM(ptr, 0x4D41, sizeof(hash), hash); WSET_LH(ptr, 0, ptr - pubkey); memcpy(ptr + 2, "PURSAF", 6); return 0; } static int CreatePrivateKey(const struct keyparam* kp, nuint8* privkey, size_t* privkey_len) { nuint8* ptr; nuint8 hash[8]; size_t n_exp_len; size_t len; n_exp_len = (countbits(kp->n_exp) + 7 ) / 8 + 2; len = (4 + 4) /* BV */ + (4 + 1) * 2 /* BC, BA */ + (4 + 2) /* BL */ + (4 + kp->bytes) * 5 /* PN, QN, DP, DQ, CR */ + (4 + n_exp_len) /* EN */ + (4 + kp->bytes * 2) /* NN */ + (4 + 8) /* MA */ + (2 + 6); /* PRRSAF */ if (len + 8 > *privkey_len) return NWE_BUFFER_OVERFLOW; *privkey_len = len + 8; DSET_LH(privkey, 0, 1); WSET_LH(privkey, 4, 2); WSET_LH(privkey, 6, len); privkey += 8; ptr = privkey; PUTMEM(ptr, 0x4256, 4, "1.7b"); PUTBYTE(ptr, 0x4243, 4); PUTBYTE(ptr, 0x4241, kp->BA); PUTWORD(ptr, 0x424C, kp->BL); PUTNUM(ptr, 0x4E4E, kp->bytes * 2, kp->n_mod); PUTNUM(ptr, 0x454E, n_exp_len, kp->n_exp); PUTNUM(ptr, 0x504E, kp->bytes, kp->n_pn); PUTNUM(ptr, 0x514E, kp->bytes, kp->n_qn); PUTNUM(ptr, 0x4450, kp->bytes, kp->n_dp); PUTNUM(ptr, 0x4451, kp->bytes, kp->n_dq); PUTNUM(ptr, 0x4352, kp->bytes, kp->n_cr); nwhash1init(hash, sizeof(hash)); nwhash1(hash, sizeof(hash), privkey, ptr - privkey); PUTMEM(ptr, 0x4D41, sizeof(hash), hash); WSET_LH(ptr, 0, ptr - privkey); memcpy(ptr + 2, "PRRSAF", 6); return 0; } #undef PUTWORD #undef PUTBYTE #undef PUTNUM #undef PUTMEM #undef PUTHDR static int primes[] = { 3, 5, 7, 0, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 0x89, 0x8B, 0x95, 0x97, 0x9D, 0xA3, 0xA7, 0xAD, 0xB3, 0xB5, 0xBF, 0xC1, 0xC5, 0xC7, 0xD3, 0xDF, 0xE3, 0xE5, 0xE9, 0xEF, 0xF1, 0xFB, 0x101, 0x107, 0x10D, 0x10F, 0x115, 0x119, 0x11B, 0x125, 0x133, 0x137, 0x139, 0x13D, 0x14B, 0x151, 0x15B, 0x15D, 0x161, 0x167, 0x16F, 0x175, 0x17B, 0x17F, 0x185, 0x18D, 0x191, 0x199, 0x1A3, 0x1A5, 0x1AF, 0x1B1, 0x1B7, 0x1BB, 0x1C1, 0x1C9, 0x1CD, 0x1CF, 0x1D3, 0x1DF, 0x1E7, 0x1EB, 0x1F3, 0 }; static int IsPrime(const unit* n) { unitptr tmp; unitptr result; unitptr oneless; size_t len; size_t i; len = units2bytes(global_precision); tmp = alloca(len); result = alloca(len); oneless = alloca(len); /* n is odd, so n-1 is even... */ mp_move(oneless, n); mp_clrbit(oneless, 0); for (i = 0; i < 100; i++) { int err; do { fillrandom((void*)tmp, len); if (mp_mod(result, tmp, n)) { continue; /* return error? */ } } while (testle(result, 1)); err = mp_modexp(tmp, result, oneless, n); if (err) { continue; /* return error? */ } if (testne(tmp, 1)) return 0; } mp_burn(oneless); mp_burn(result); mp_burn(tmp); return 1; } static int FindPrime(unitptr p, size_t bits) { size_t len; size_t old_precision; old_precision = global_precision; set_precision(bits2units(bits + SLOP_BITS)); len = units2bytes(global_precision); while (1) { size_t i; int divd; nuint8 skip[1000]; fillrandom((void*)p, len); mp_setbit(p, bits - 2); mp_setbit(p, bits - 1); for (i = units2bits(global_precision); i-- > bits; ) mp_clrbit(p, i); mp_clrbit(p, 0); memset(skip, 0, sizeof(skip)); for (divd = 3; divd < 9000; divd += 2) { int modd; int *prime; for (prime = primes; *prime && (*prime < divd); prime++) { if (divd % *prime == 0) goto next; } modd = mp_shortmod(p, divd); if (!modd) modd = divd; for (i = divd - modd; i < sizeof(skip); i += divd) { skip[i] = 1; } next:; } for (mp_inc(p), i = 1; i < sizeof(skip); mp_inc(p), mp_inc(p), i += 2) { if (skip[i]) continue; if (IsPrime(p)) { /* extend precision */ for (i = global_precision; i < old_precision; i++) { p[i] = 0; } set_precision(old_precision); return 0; } } } } /* move this to mpilib? */ static void gcdv(unit *z, unit *u, unit *v, const unit *x, const unit *y) { unit *u2; unit *v2; unit *z2; unit *quot; unit *tmp; unit *tmp2; size_t len = units2bytes(global_precision) * 6; u2 = alloca(len); v2 = u2 + global_precision; z2 = v2 + global_precision; quot = z2 + global_precision; tmp = quot + global_precision; tmp2 = tmp + global_precision; mp_init(u, 1); mp_init(v, 0); mp_move(z, x); mp_init(u2, 0); mp_init(v2, 1); mp_move(z2, y); while (testne(z2, 0)) { mp_div(tmp, quot, z, z2); #define OSTEP(r,prev,mult) \ mp_mult(tmp, prev, mult); \ mp_move(tmp2, r); \ mp_sub(tmp2, tmp); \ mp_move(r, prev); \ mp_move(prev, tmp2); OSTEP(u, u2, quot); OSTEP(v, v2, quot); OSTEP(z, z2, quot); #undef OSTEP } if (mp_tstminus(u)) { mp_add(u, y); } if (mp_tstminus(v)) { mp_add(v, x); } memset(u2, 0, len); } static int ComputeKey(struct keyparam *kp) { unitptr tmp; unitptr tmp2; unitptr tmp3; unitptr n_pn1; unitptr n_qn1; unitptr n_dn; int err; size_t len = units2bytes(global_precision) * 6; tmp = alloca(len); tmp2 = tmp + global_precision; tmp3 = tmp2 + global_precision; n_pn1 = tmp3 + global_precision; n_qn1 = n_pn1 + global_precision; n_dn = n_qn1 + global_precision; mp_mult(kp->n_mod, kp->n_pn, kp->n_qn); /* Hey, why shift by 8? */ mp_move_and_shift_left_bits(tmp, kp->n_mod, 8); mp_recip(kp->n_recip, tmp); kp->n_recip_present = 1; mp_move(n_qn1, kp->n_qn); mp_dec(n_qn1); mp_move(n_pn1, kp->n_pn); mp_dec(n_pn1); mp_mult(tmp, n_pn1, n_qn1); gcdv(tmp2, n_dn, tmp3, kp->n_exp, tmp); #ifdef DEBUG_PRINT dgcdv(tmp2, n_dn, tmp3, kp->n_exp, tmp); #endif if (testne(tmp2, 1)) { err = ERR_SYSTEM_ERROR; } else { mp_div(kp->n_dp, tmp, n_dn, n_pn1); mp_div(kp->n_dq, tmp, n_dn, n_qn1); gcdv(tmp, tmp2, kp->n_cr, kp->n_pn, kp->n_qn); err = 0; } memset(tmp, 0, len); return err; } NWDSCCODE __NWGenerateKeyPair( size_t key_len, const void* n_exp, size_t n_exp_len, void* pubkey, size_t* pubkey_len, void* privkey, size_t* privkey_len ) { static const nuint8 def_exp[] = { 1, 0, 1}; struct keyparam kp; NWDSCCODE err; size_t bits; size_t words; /* netware native units */ if (!pubkey || !pubkey_len || !privkey || !privkey_len) return -2; if (!key_len || !n_exp || !n_exp_len) { n_exp = def_exp; n_exp_len = sizeof(def_exp); key_len = 420; /* well, at least 620 does work too */ } else { if ((key_len > 760) || (key_len < 256) || (key_len & 1) || (n_exp_len > 16)) { return NWE_PARAM_INVALID; } } memset(&kp, 0, sizeof(kp)); kp.BA = 48; kp.BL = key_len; memcpy(kp.n_exp, n_exp, n_exp_len); ncpt_mutex_lock(&mpilock); set_precision(bits2units(kp.BL) + SLOP_BITS); if ((countbits(kp.n_exp) > kp.BL) || !(kp.n_exp[0] & 1)) { err = NWE_PARAM_INVALID; goto quit; } bits = kp.BL >> 1; words = (bits / 16) + 1; kp.bytes = words * 2; err = FindPrime(kp.n_pn, bits); if (err) goto quit; err = FindPrime(kp.n_qn, bits); if (err) goto quit; if (mp_compare(kp.n_pn, kp.n_qn) != 1) { unitptr tmp = alloca(units2bytes(global_precision)); mp_move(tmp, kp.n_pn); mp_move(kp.n_pn, kp.n_qn); mp_move(kp.n_qn, tmp); mp_init0(tmp); } /* +6: NI has 6 additonal bytes... ?! */ set_precision(bytes2units(kp.bytes * 2 + 6 + (SLOP_BITS + 7) / 8)); err = ComputeKey(&kp); if (err) goto quit; err = CreatePublicKey(&kp, pubkey, pubkey_len); if (err) goto quit; err = CreatePrivateKey(&kp, privkey, privkey_len); quit:; ncpt_mutex_unlock(&mpilock); memset(&kp, 0, sizeof(kp)); return err; }