Files
ncpfs/lib/ndslib.c
ncpfs archive import 0979ae6a41 Import ncpfs 2.2.0
2026-04-28 20:39:59 +02:00

1254 lines
36 KiB
C

/*
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 <string.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef ERR_MSG
#include <stdio.h>
#endif
#include <unistd.h>
#include <sys/time.h>
#ifdef RANDBUF
#include <fcntl.h>
#endif
#include "ncplib.h"
#include "ncplib_err.h"
#include "ndslib.h"
#include "ndscrypt.h"
#define USUALS
typedef u_int32_t word32;
typedef u_int16_t word16;
typedef unsigned char boolean;
#include "mpilib.h"
#include <unistd.h>
#include <fcntl.h>
#include "kernel/ipx.h"
#include <errno.h>
#include "ndslib.h"
int bindery_only = 0;
static int buf_get_dword_lh(char **buf, char *bufend, u_int32_t *v) {
if ((*buf) + 4 <= bufend) {
if (v)
*v = DVAL_LH(*buf, 0);
(*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, u_int16_t v) {
if ((*buf) + 2 <= bufend) {
WSET_LH(*buf, 0, v);
*buf += 2;
return 0;
} else
return -1;
}
static int buf_put_dword_lh(char **buf, char *bufend, u_int32_t v) {
if ((buf) && ((*buf) + 4 <= bufend)) {
DSET_LH(*buf, 0, v);
*buf += 4;
return 0;
} else
return -1;
}
static int buf_put_dword_hl(char **buf, char *bufend, u_int32_t v) {
if ((*buf) + 4 <= bufend) {
DSET_HL(*buf, 0, v);
*buf += 4;
return 0;
} else
return -1;
}
static int buf_put_lbuf(char **buf, char *bufend, const char *databuf,
size_t buflen) {
if ((!buf_put_dword_lh(buf, bufend, buflen)) &&
(*buf + buflen <= bufend)) {
if (!buflen) return 0; /* explicitly allow {NULL, 0} buffer */
if (!databuf) return -1;
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,
size_t 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, str, i);
(*buf) += i;
while (i++ & 3)
*(*buf)++ = 0;
return 0;
} else
return -1;
}
static int buf_get_dword_hl(char **buf, char *bufend, u_int32_t *v) {
if ((*buf) + 4 <= bufend) {
if (v) {
*v = DVAL_HL(*buf, 0);
}
(*buf) += 4;
return 0;
} else
return -1;
}
static int buf_get_word_lh(char **buf, char *bufend, u_int16_t *v) {
if (((*buf) + 2 <= bufend)) {
if (v) {
*v = WVAL_LH(*buf, 0);
}
(*buf) += 4;
return 0;
} else
return -1;
}
static int buf_get_word_lh2(char **buf, char *bufend, u_int16_t *v) {
if (((*buf) + 2 <= bufend)) {
if (v) {
*v = WVAL_LH(*buf, 0);
}
(*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, size_t 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 (bindery_only) return -1;
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, size_t *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
static 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;
}
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 uni_char c_public_key[] = {'P','u','b','l','i','c',' ','K','e','y',0};
static char keyprefix[] = {1, 0, 0, 0, 3, 0, 1, 0};
static int initkey(const char *key, char **keyptr, int *keylen) { /* 1=ok, 0=err */
if (!memcmp(key, keyprefix, 8)) {
if (keylen) *keylen = WVAL_LH(key, 8);
if (keyptr) (const char *)(*keyptr) = key + 10;
return 1;
} else
return 0;
}
static void clearkey(char *key) {
char *keyptr;
int keylen;
if (initkey(key, &keyptr, &keylen))
memset(key, 0, keylen + 10);
}
static 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;
}
static 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, WVAL_LH(key, 8) - 20);
return (!memcmp(p, temp, 8));
} else
return 0;
}
static 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 = WVAL_LH(keybuf, 10) + 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};
static 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;
}
static 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;
u_int16_t n2a, 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, &n2a)) ||
(buf_get_word_lh2(&p, pend, &n3)) ||
(n1 != 1) || (n2a != 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;
WSET_LH(tempbuf, 8, n3);
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, struct ncp_conn *readkey_conn,
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;
u_int16_t n3a;
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, &n3a)) ||
(n1 != 1) || (n2 != 0x0a0001) || (n3a > pend - p))
goto err_exit;
n1 = ((countbits_l(p, n3a) + 31) & (~31)) >> 3;
if (n1 < 52)
goto err_exit;
if (!(n_temp = malloc(n1))) {
err = ENOMEM;
goto err_exit;
}
copyfill(n_temp, n1, p, n3a);
p += (n3a + 3) & (~3);
if ((err = get_public_key(readkey_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;
}
static 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;
}
static 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, struct ncp_conn* readkey_conn,
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
if (!readkey_conn) readkey_conn = conn;
u_key = loginstrc = buf = NULL;
if ((err = nds_beginauth(conn, user_id, readkey_conn, 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);
WSET_LH(loginstrc, 20, user_name_len);
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(readkey_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, *readkey_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, &not_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, &not_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((struct sockaddr*)&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) {
struct sockaddr xaddr;
int remoteserver;
int i;
free(server_name);
if ((err = nds_get_server_name(conn, &server_name)) != 0)
goto err_exit;
i = sizeof(xaddr);
if ((err = nds_resolve_name(conn, 0x62, server_name, &serv_id,
&remoteserver, (struct sockaddr*)&xaddr, &i)) != 0)
goto err_exit;
if (remoteserver) {
if (!(readkey_conn = ncp_open_addr((struct sockaddr*)&xaddr, &err)))
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, readkey_conn, 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 (readkey_conn) ncp_close(readkey_conn);
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;
}
#ifdef NDS_PRIVATEKEY
long
nds_authenticate(struct ncp_conn* conn, uni_char* name, u_int8_t* code1, void* privateKey, size_t privateKeyLen) {
}
#endif /* NDS_PRIVATE_KEY */