xref: /plan9-contrib/sys/src/cmd/auth/factotum/chap.c (revision 6822557b53c0fb0bf9a8ec3fb47e57255de0479a)
19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier  * CHAP, MSCHAP
39a747e4fSDavid du Colombier  *
49a747e4fSDavid du Colombier  * The client does not authenticate the server, hence no CAI
59a747e4fSDavid du Colombier  *
69a747e4fSDavid du Colombier  * Client protocol:
749eeb88aSDavid du Colombier  *	write Chapchal
849eeb88aSDavid du Colombier  *	read response Chapreply or MSchaprely structure
99a747e4fSDavid du Colombier  *
109a747e4fSDavid du Colombier  * Server protocol:
119a747e4fSDavid du Colombier  *	read challenge: 8 bytes binary
129a747e4fSDavid du Colombier  *	write user: utf8
139a747e4fSDavid du Colombier  *	write response: Chapreply or MSchapreply structure
149a747e4fSDavid du Colombier  */
159a747e4fSDavid du Colombier 
1649eeb88aSDavid du Colombier #include <ctype.h>
179a747e4fSDavid du Colombier #include "dat.h"
189a747e4fSDavid du Colombier 
199a747e4fSDavid du Colombier enum {
209a747e4fSDavid du Colombier 	ChapChallen = 8,
2149eeb88aSDavid du Colombier 	ChapResplen = 16,
2249eeb88aSDavid du Colombier 	MSchapResplen = 24,
239a747e4fSDavid du Colombier };
249a747e4fSDavid du Colombier 
259a747e4fSDavid du Colombier static int dochal(State*);
269a747e4fSDavid du Colombier static int doreply(State*, void*, int);
2749eeb88aSDavid du Colombier static void doLMchap(char *, uchar [ChapChallen], uchar [MSchapResplen]);
2849eeb88aSDavid du Colombier static void doNTchap(char *, uchar [ChapChallen], uchar [MSchapResplen]);
2949eeb88aSDavid du Colombier static void dochap(char *, int, char [ChapChallen], uchar [ChapResplen]);
3049eeb88aSDavid du Colombier 
319a747e4fSDavid du Colombier 
329a747e4fSDavid du Colombier struct State
339a747e4fSDavid du Colombier {
349a747e4fSDavid du Colombier 	char *protoname;
359a747e4fSDavid du Colombier 	int astype;
369a747e4fSDavid du Colombier 	int asfd;
379a747e4fSDavid du Colombier 	Key *key;
389a747e4fSDavid du Colombier 	Ticket	t;
399a747e4fSDavid du Colombier 	Ticketreq	tr;
409a747e4fSDavid du Colombier 	char chal[ChapChallen];
4149eeb88aSDavid du Colombier 	MSchapreply mcr;
4249eeb88aSDavid du Colombier 	char cr[ChapResplen];
439a747e4fSDavid du Colombier 	char err[ERRMAX];
449a747e4fSDavid du Colombier 	char user[64];
459a747e4fSDavid du Colombier 	uchar secret[16];	/* for mschap */
469a747e4fSDavid du Colombier 	int nsecret;
479a747e4fSDavid du Colombier };
489a747e4fSDavid du Colombier 
499a747e4fSDavid du Colombier enum
509a747e4fSDavid du Colombier {
5149eeb88aSDavid du Colombier 	CNeedChal,
5249eeb88aSDavid du Colombier 	CHaveResp,
5349eeb88aSDavid du Colombier 
549a747e4fSDavid du Colombier 	SHaveChal,
559a747e4fSDavid du Colombier 	SNeedUser,
569a747e4fSDavid du Colombier 	SNeedResp,
579a747e4fSDavid du Colombier 	SHaveZero,
589a747e4fSDavid du Colombier 	SHaveCAI,
599a747e4fSDavid du Colombier 
609a747e4fSDavid du Colombier 	Maxphase
619a747e4fSDavid du Colombier };
629a747e4fSDavid du Colombier 
639a747e4fSDavid du Colombier static char *phasenames[Maxphase] =
649a747e4fSDavid du Colombier {
6549eeb88aSDavid du Colombier [CNeedChal]	"CNeedChal",
6649eeb88aSDavid du Colombier [CHaveResp]	"CHaveResp",
6749eeb88aSDavid du Colombier 
689a747e4fSDavid du Colombier [SHaveChal]	"SHaveChal",
699a747e4fSDavid du Colombier [SNeedUser]	"SNeedUser",
709a747e4fSDavid du Colombier [SNeedResp]	"SNeedResp",
719a747e4fSDavid du Colombier [SHaveZero]	"SHaveZero",
729a747e4fSDavid du Colombier [SHaveCAI]	"SHaveCAI",
739a747e4fSDavid du Colombier };
749a747e4fSDavid du Colombier 
759a747e4fSDavid du Colombier static int
chapinit(Proto * p,Fsstate * fss)769a747e4fSDavid du Colombier chapinit(Proto *p, Fsstate *fss)
779a747e4fSDavid du Colombier {
789a747e4fSDavid du Colombier 	int iscli, ret;
799a747e4fSDavid du Colombier 	State *s;
809a747e4fSDavid du Colombier 
812ebbfa15SDavid du Colombier 	if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
829a747e4fSDavid du Colombier 		return failure(fss, nil);
839a747e4fSDavid du Colombier 
849a747e4fSDavid du Colombier 	s = emalloc(sizeof *s);
859a747e4fSDavid du Colombier 	fss->phasename = phasenames;
869a747e4fSDavid du Colombier 	fss->maxphase = Maxphase;
879a747e4fSDavid du Colombier 	s->asfd = -1;
889a747e4fSDavid du Colombier 	if(p == &chap){
899a747e4fSDavid du Colombier 		s->astype = AuthChap;
909a747e4fSDavid du Colombier 		s->protoname = "chap";
919a747e4fSDavid du Colombier 	}else{
929a747e4fSDavid du Colombier 		s->astype = AuthMSchap;
939a747e4fSDavid du Colombier 		s->protoname = "mschap";
949a747e4fSDavid du Colombier 	}
9549eeb88aSDavid du Colombier 
9649eeb88aSDavid du Colombier 	if(iscli)
9749eeb88aSDavid du Colombier 		fss->phase = CNeedChal;
9849eeb88aSDavid du Colombier 	else{
999a747e4fSDavid du Colombier 		if((ret = findp9authkey(&s->key, fss)) != RpcOk){
1009a747e4fSDavid du Colombier 			free(s);
1019a747e4fSDavid du Colombier 			return ret;
1029a747e4fSDavid du Colombier 		}
1039a747e4fSDavid du Colombier 		if(dochal(s) < 0){
1049a747e4fSDavid du Colombier 			free(s);
1059a747e4fSDavid du Colombier 			return failure(fss, nil);
1069a747e4fSDavid du Colombier 		}
1079a747e4fSDavid du Colombier 		fss->phase = SHaveChal;
10849eeb88aSDavid du Colombier 	}
10949eeb88aSDavid du Colombier 
1109a747e4fSDavid du Colombier 	fss->ps = s;
1119a747e4fSDavid du Colombier 	return RpcOk;
1129a747e4fSDavid du Colombier }
1139a747e4fSDavid du Colombier 
1149a747e4fSDavid du Colombier static void
chapclose(Fsstate * fss)1159a747e4fSDavid du Colombier chapclose(Fsstate *fss)
1169a747e4fSDavid du Colombier {
1179a747e4fSDavid du Colombier 	State *s;
1189a747e4fSDavid du Colombier 
1199a747e4fSDavid du Colombier 	s = fss->ps;
1209a747e4fSDavid du Colombier 	if(s->asfd >= 0){
1219a747e4fSDavid du Colombier 		close(s->asfd);
1229a747e4fSDavid du Colombier 		s->asfd = -1;
1239a747e4fSDavid du Colombier 	}
1249a747e4fSDavid du Colombier 	free(s);
1259a747e4fSDavid du Colombier }
1269a747e4fSDavid du Colombier 
12749eeb88aSDavid du Colombier 
1289a747e4fSDavid du Colombier static int
chapwrite(Fsstate * fss,void * va,uint n)1299a747e4fSDavid du Colombier chapwrite(Fsstate *fss, void *va, uint n)
1309a747e4fSDavid du Colombier {
13149eeb88aSDavid du Colombier 	int ret, nreply;
13249eeb88aSDavid du Colombier 	char *a, *v;
1339a747e4fSDavid du Colombier 	void *reply;
13449eeb88aSDavid du Colombier 	Key *k;
135260f7b65SDavid du Colombier 	Keyinfo ki;
1369a747e4fSDavid du Colombier 	State *s;
1379a747e4fSDavid du Colombier 	Chapreply cr;
1389a747e4fSDavid du Colombier 	MSchapreply mcr;
1399a747e4fSDavid du Colombier 	OChapreply ocr;
1409a747e4fSDavid du Colombier 	OMSchapreply omcr;
1419a747e4fSDavid du Colombier 
1429a747e4fSDavid du Colombier 	s = fss->ps;
14349eeb88aSDavid du Colombier 	a = va;
1449a747e4fSDavid du Colombier 	switch(fss->phase){
1459a747e4fSDavid du Colombier 	default:
1469a747e4fSDavid du Colombier 		return phaseerror(fss, "write");
1479a747e4fSDavid du Colombier 
14849eeb88aSDavid du Colombier 	case CNeedChal:
149260f7b65SDavid du Colombier 		ret = findkey(&k, mkkeyinfo(&ki, fss, nil), "%s", fss->proto->keyprompt);
15049eeb88aSDavid du Colombier 		if(ret != RpcOk)
15149eeb88aSDavid du Colombier 			return ret;
15249eeb88aSDavid du Colombier 		v = _strfindattr(k->privattr, "!password");
15349eeb88aSDavid du Colombier 		if(v == nil)
15449eeb88aSDavid du Colombier 			return failure(fss, "key has no password");
15549eeb88aSDavid du Colombier 		setattrs(fss->attr, k->attr);
15649eeb88aSDavid du Colombier 		switch(s->astype){
15749eeb88aSDavid du Colombier 		default:
15849eeb88aSDavid du Colombier 			abort();
15949eeb88aSDavid du Colombier 		case AuthMSchap:
16049eeb88aSDavid du Colombier 			doLMchap(v, (uchar *)a, (uchar *)s->mcr.LMresp);
16149eeb88aSDavid du Colombier 			doNTchap(v, (uchar *)a, (uchar *)s->mcr.NTresp);
16249eeb88aSDavid du Colombier 			break;
16349eeb88aSDavid du Colombier 		case AuthChap:
16449eeb88aSDavid du Colombier 			dochap(v, *a, a+1, (uchar *)s->cr);
16549eeb88aSDavid du Colombier 			break;
16649eeb88aSDavid du Colombier 		}
16749eeb88aSDavid du Colombier 		closekey(k);
16849eeb88aSDavid du Colombier 		fss->phase = CHaveResp;
16949eeb88aSDavid du Colombier 		return RpcOk;
17049eeb88aSDavid du Colombier 
1719a747e4fSDavid du Colombier 	case SNeedUser:
1729a747e4fSDavid du Colombier 		if(n >= sizeof s->user)
1739a747e4fSDavid du Colombier 			return failure(fss, "user name too long");
1749a747e4fSDavid du Colombier 		memmove(s->user, va, n);
1759a747e4fSDavid du Colombier 		s->user[n] = '\0';
1769a747e4fSDavid du Colombier 		fss->phase = SNeedResp;
1779a747e4fSDavid du Colombier 		return RpcOk;
1789a747e4fSDavid du Colombier 
1799a747e4fSDavid du Colombier 	case SNeedResp:
1809a747e4fSDavid du Colombier 		switch(s->astype){
1819a747e4fSDavid du Colombier 		default:
1829a747e4fSDavid du Colombier 			return failure(fss, "chap internal botch");
1839a747e4fSDavid du Colombier 		case AuthChap:
1849a747e4fSDavid du Colombier 			if(n != sizeof(Chapreply))
1859a747e4fSDavid du Colombier 				return failure(fss, "did not get Chapreply");
1869a747e4fSDavid du Colombier 			memmove(&cr, va, sizeof cr);
1879a747e4fSDavid du Colombier 			ocr.id = cr.id;
1889a747e4fSDavid du Colombier 			memmove(ocr.resp, cr.resp, sizeof ocr.resp);
189f4eafd00SDavid du Colombier 			memset(omcr.uid, 0, sizeof(omcr.uid));
1909a747e4fSDavid du Colombier 			strecpy(ocr.uid, ocr.uid+sizeof ocr.uid, s->user);
1919a747e4fSDavid du Colombier 			reply = &ocr;
1929a747e4fSDavid du Colombier 			nreply = sizeof ocr;
1939a747e4fSDavid du Colombier 			break;
1949a747e4fSDavid du Colombier 		case AuthMSchap:
1959a747e4fSDavid du Colombier 			if(n != sizeof(MSchapreply))
1969a747e4fSDavid du Colombier 				return failure(fss, "did not get MSchapreply");
1979a747e4fSDavid du Colombier 			memmove(&mcr, va, sizeof mcr);
1989a747e4fSDavid du Colombier 			memmove(omcr.LMresp, mcr.LMresp, sizeof omcr.LMresp);
1999a747e4fSDavid du Colombier 			memmove(omcr.NTresp, mcr.NTresp, sizeof omcr.NTresp);
200f4eafd00SDavid du Colombier 			memset(omcr.uid, 0, sizeof(omcr.uid));
2019a747e4fSDavid du Colombier 			strecpy(omcr.uid, omcr.uid+sizeof omcr.uid, s->user);
2029a747e4fSDavid du Colombier 			reply = &omcr;
2039a747e4fSDavid du Colombier 			nreply = sizeof omcr;
2049a747e4fSDavid du Colombier 			break;
2059a747e4fSDavid du Colombier 		}
2069a747e4fSDavid du Colombier 		if(doreply(s, reply, nreply) < 0)
2079a747e4fSDavid du Colombier 			return failure(fss, nil);
2089a747e4fSDavid du Colombier 		fss->phase = Established;
2099a747e4fSDavid du Colombier 		fss->ai.cuid = s->t.cuid;
2109a747e4fSDavid du Colombier 		fss->ai.suid = s->t.suid;
2119a747e4fSDavid du Colombier 		fss->ai.secret = s->secret;
2129a747e4fSDavid du Colombier 		fss->ai.nsecret = s->nsecret;
2139a747e4fSDavid du Colombier 		fss->haveai = 1;
2149a747e4fSDavid du Colombier 		return RpcOk;
2159a747e4fSDavid du Colombier 	}
2169a747e4fSDavid du Colombier }
2179a747e4fSDavid du Colombier 
2189a747e4fSDavid du Colombier static int
chapread(Fsstate * fss,void * va,uint * n)2199a747e4fSDavid du Colombier chapread(Fsstate *fss, void *va, uint *n)
2209a747e4fSDavid du Colombier {
2219a747e4fSDavid du Colombier 	State *s;
2229a747e4fSDavid du Colombier 
2239a747e4fSDavid du Colombier 	s = fss->ps;
2249a747e4fSDavid du Colombier 	switch(fss->phase){
2259a747e4fSDavid du Colombier 	default:
2269a747e4fSDavid du Colombier 		return phaseerror(fss, "read");
2279a747e4fSDavid du Colombier 
22849eeb88aSDavid du Colombier 	case CHaveResp:
22949eeb88aSDavid du Colombier 		switch(s->astype){
23049eeb88aSDavid du Colombier 		default:
231f4eafd00SDavid du Colombier 			phaseerror(fss, "write");
232f4eafd00SDavid du Colombier 			break;
23349eeb88aSDavid du Colombier 		case AuthMSchap:
23449eeb88aSDavid du Colombier 			if(*n > sizeof(MSchapreply))
23549eeb88aSDavid du Colombier 				*n = sizeof(MSchapreply);
23649eeb88aSDavid du Colombier 			memmove(va, &s->mcr, *n);
23749eeb88aSDavid du Colombier 			break;
23849eeb88aSDavid du Colombier 		case AuthChap:
23949eeb88aSDavid du Colombier 			if(*n > ChapResplen)
24049eeb88aSDavid du Colombier 				*n = ChapResplen;
24149eeb88aSDavid du Colombier 			memmove(va, s->cr, ChapResplen);
24249eeb88aSDavid du Colombier 			break;
24349eeb88aSDavid du Colombier 		}
24449eeb88aSDavid du Colombier 		fss->phase = Established;
24549eeb88aSDavid du Colombier 		fss->haveai = 0;
24649eeb88aSDavid du Colombier 		return RpcOk;
24749eeb88aSDavid du Colombier 
2489a747e4fSDavid du Colombier 	case SHaveChal:
2499a747e4fSDavid du Colombier 		if(*n > sizeof s->chal)
2509a747e4fSDavid du Colombier 			*n = sizeof s->chal;
2519a747e4fSDavid du Colombier 		memmove(va, s->chal, *n);
2529a747e4fSDavid du Colombier 		fss->phase = SNeedUser;
2539a747e4fSDavid du Colombier 		return RpcOk;
2549a747e4fSDavid du Colombier 	}
2559a747e4fSDavid du Colombier }
2569a747e4fSDavid du Colombier 
2579a747e4fSDavid du Colombier static int
dochal(State * s)2589a747e4fSDavid du Colombier dochal(State *s)
2599a747e4fSDavid du Colombier {
2609a747e4fSDavid du Colombier 	char *dom, *user;
2619a747e4fSDavid du Colombier 	char trbuf[TICKREQLEN];
2629a747e4fSDavid du Colombier 
2639a747e4fSDavid du Colombier 	s->asfd = -1;
2649a747e4fSDavid du Colombier 
2659a747e4fSDavid du Colombier 	/* send request to authentication server and get challenge */
2662ebbfa15SDavid du Colombier 	if((dom = _strfindattr(s->key->attr, "dom")) == nil
2672ebbfa15SDavid du Colombier 	|| (user = _strfindattr(s->key->attr, "user")) == nil){
2689a747e4fSDavid du Colombier 		werrstr("chap/dochal cannot happen");
2699a747e4fSDavid du Colombier 		goto err;
2709a747e4fSDavid du Colombier 	}
2719a747e4fSDavid du Colombier 	s->asfd = _authdial(nil, dom);
2729a747e4fSDavid du Colombier 	if(s->asfd < 0)
2739a747e4fSDavid du Colombier 		goto err;
2749a747e4fSDavid du Colombier 
2759a747e4fSDavid du Colombier 	memset(&s->tr, 0, sizeof(s->tr));
2769a747e4fSDavid du Colombier 	s->tr.type = s->astype;
2779a747e4fSDavid du Colombier 	safecpy(s->tr.authdom, dom, sizeof s->tr.authdom);
2789a747e4fSDavid du Colombier 	safecpy(s->tr.hostid, user, sizeof(s->tr.hostid));
2799a747e4fSDavid du Colombier 	convTR2M(&s->tr, trbuf);
2809a747e4fSDavid du Colombier 
2819a747e4fSDavid du Colombier 	if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
2829a747e4fSDavid du Colombier 		goto err;
2839a747e4fSDavid du Colombier 
2849a747e4fSDavid du Colombier 	/* readn, not _asrdresp.  needs to match auth.srv.c. */
2859a747e4fSDavid du Colombier 	if(readn(s->asfd, s->chal, sizeof s->chal) != sizeof s->chal)
2869a747e4fSDavid du Colombier 		goto err;
2879a747e4fSDavid du Colombier 	return 0;
2889a747e4fSDavid du Colombier 
2899a747e4fSDavid du Colombier err:
2909a747e4fSDavid du Colombier 	if(s->asfd >= 0)
2919a747e4fSDavid du Colombier 		close(s->asfd);
2929a747e4fSDavid du Colombier 	s->asfd = -1;
2939a747e4fSDavid du Colombier 	return -1;
2949a747e4fSDavid du Colombier }
2959a747e4fSDavid du Colombier 
2969a747e4fSDavid du Colombier static int
doreply(State * s,void * reply,int nreply)2979a747e4fSDavid du Colombier doreply(State *s, void *reply, int nreply)
2989a747e4fSDavid du Colombier {
2999a747e4fSDavid du Colombier 	char ticket[TICKETLEN+AUTHENTLEN];
3009a747e4fSDavid du Colombier 	int n;
3019a747e4fSDavid du Colombier 	Authenticator a;
3029a747e4fSDavid du Colombier 
3039a747e4fSDavid du Colombier 	if((n=write(s->asfd, reply, nreply)) != nreply){
3049a747e4fSDavid du Colombier 		if(n >= 0)
3059a747e4fSDavid du Colombier 			werrstr("short write to auth server");
3069a747e4fSDavid du Colombier 		goto err;
3079a747e4fSDavid du Colombier 	}
3089a747e4fSDavid du Colombier 
3099a747e4fSDavid du Colombier 	if(_asrdresp(s->asfd, ticket, TICKETLEN+AUTHENTLEN) < 0){
3109a747e4fSDavid du Colombier 		/* leave connection open so we can try again */
3119a747e4fSDavid du Colombier 		return -1;
3129a747e4fSDavid du Colombier 	}
3139a747e4fSDavid du Colombier 	s->nsecret = readn(s->asfd, s->secret, sizeof s->secret);
3149a747e4fSDavid du Colombier 	if(s->nsecret < 0)
3159a747e4fSDavid du Colombier 		s->nsecret = 0;
3169a747e4fSDavid du Colombier 	close(s->asfd);
3179a747e4fSDavid du Colombier 	s->asfd = -1;
3189a747e4fSDavid du Colombier 	convM2T(ticket, &s->t, s->key->priv);
3199a747e4fSDavid du Colombier 	if(s->t.num != AuthTs
3209a747e4fSDavid du Colombier 	|| memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
321*6822557bSDavid du Colombier 		if(s->key->successes == 0)
322260f7b65SDavid du Colombier 			disablekey(s->key);
3239a747e4fSDavid du Colombier 		werrstr(Easproto);
3249a747e4fSDavid du Colombier 		return -1;
3259a747e4fSDavid du Colombier 	}
326*6822557bSDavid du Colombier 	s->key->successes++;
3279a747e4fSDavid du Colombier 	convM2A(ticket+TICKETLEN, &a, s->t.key);
3289a747e4fSDavid du Colombier 	if(a.num != AuthAc
3299a747e4fSDavid du Colombier 	|| memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
3309a747e4fSDavid du Colombier 	|| a.id != 0){
3319a747e4fSDavid du Colombier 		werrstr(Easproto);
3329a747e4fSDavid du Colombier 		return -1;
3339a747e4fSDavid du Colombier 	}
3349a747e4fSDavid du Colombier 
3359a747e4fSDavid du Colombier 	return 0;
3369a747e4fSDavid du Colombier err:
3379a747e4fSDavid du Colombier 	if(s->asfd >= 0)
3389a747e4fSDavid du Colombier 		close(s->asfd);
3399a747e4fSDavid du Colombier 	s->asfd = -1;
3409a747e4fSDavid du Colombier 	return -1;
3419a747e4fSDavid du Colombier }
3429a747e4fSDavid du Colombier 
3439a747e4fSDavid du Colombier Proto chap = {
3449a747e4fSDavid du Colombier .name=	"chap",
3459a747e4fSDavid du Colombier .init=	chapinit,
3469a747e4fSDavid du Colombier .write=	chapwrite,
3479a747e4fSDavid du Colombier .read=	chapread,
3489a747e4fSDavid du Colombier .close=	chapclose,
34949eeb88aSDavid du Colombier .addkey= replacekey,
35049eeb88aSDavid du Colombier .keyprompt= "!password?"
3519a747e4fSDavid du Colombier };
3529a747e4fSDavid du Colombier 
3539a747e4fSDavid du Colombier Proto mschap = {
3549a747e4fSDavid du Colombier .name=	"mschap",
3559a747e4fSDavid du Colombier .init=	chapinit,
3569a747e4fSDavid du Colombier .write=	chapwrite,
3579a747e4fSDavid du Colombier .read=	chapread,
3589a747e4fSDavid du Colombier .close=	chapclose,
35949eeb88aSDavid du Colombier .addkey= replacekey,
36049eeb88aSDavid du Colombier .keyprompt= "!password?"
3619a747e4fSDavid du Colombier };
3629a747e4fSDavid du Colombier 
36349eeb88aSDavid du Colombier static void
hash(uchar pass[16],uchar c8[ChapChallen],uchar p24[MSchapResplen])36449eeb88aSDavid du Colombier hash(uchar pass[16], uchar c8[ChapChallen], uchar p24[MSchapResplen])
36549eeb88aSDavid du Colombier {
36649eeb88aSDavid du Colombier 	int i;
36749eeb88aSDavid du Colombier 	uchar p21[21];
36849eeb88aSDavid du Colombier 	ulong schedule[32];
36949eeb88aSDavid du Colombier 
37049eeb88aSDavid du Colombier 	memset(p21, 0, sizeof p21 );
37149eeb88aSDavid du Colombier 	memmove(p21, pass, 16);
37249eeb88aSDavid du Colombier 
37349eeb88aSDavid du Colombier 	for(i=0; i<3; i++) {
37449eeb88aSDavid du Colombier 		key_setup(p21+i*7, schedule);
37549eeb88aSDavid du Colombier 		memmove(p24+i*8, c8, 8);
37649eeb88aSDavid du Colombier 		block_cipher(schedule, p24+i*8, 0);
37749eeb88aSDavid du Colombier 	}
37849eeb88aSDavid du Colombier }
37949eeb88aSDavid du Colombier 
38049eeb88aSDavid du Colombier static void
doNTchap(char * pass,uchar chal[ChapChallen],uchar reply[MSchapResplen])38149eeb88aSDavid du Colombier doNTchap(char *pass, uchar chal[ChapChallen], uchar reply[MSchapResplen])
38249eeb88aSDavid du Colombier {
38349eeb88aSDavid du Colombier 	Rune r;
384f4eafd00SDavid du Colombier 	int i, n;
385f4eafd00SDavid du Colombier 	uchar digest[MD4dlen];
386f4eafd00SDavid du Colombier 	uchar *w, unipass[256];
38749eeb88aSDavid du Colombier 
38849eeb88aSDavid du Colombier 	// Standard says unlimited length, experience says 128 max
38949eeb88aSDavid du Colombier 	if ((n = strlen(pass)) > 128)
39049eeb88aSDavid du Colombier 		n = 128;
39149eeb88aSDavid du Colombier 
39249eeb88aSDavid du Colombier 	for(i=0, w=unipass; i < n; i++) {
39349eeb88aSDavid du Colombier 		pass += chartorune(&r, pass);
39449eeb88aSDavid du Colombier 		*w++ = r & 0xff;
39549eeb88aSDavid du Colombier 		*w++ = r >> 8;
39649eeb88aSDavid du Colombier 	}
39749eeb88aSDavid du Colombier 
39849eeb88aSDavid du Colombier 	memset(digest, 0, sizeof digest);
39949eeb88aSDavid du Colombier 	md4(unipass, w-unipass, digest, nil);
400f4eafd00SDavid du Colombier 	memset(unipass, 0, sizeof unipass);
40149eeb88aSDavid du Colombier 	hash(digest, chal, reply);
40249eeb88aSDavid du Colombier }
40349eeb88aSDavid du Colombier 
40449eeb88aSDavid du Colombier static void
doLMchap(char * pass,uchar chal[ChapChallen],uchar reply[MSchapResplen])40549eeb88aSDavid du Colombier doLMchap(char *pass, uchar chal[ChapChallen], uchar reply[MSchapResplen])
40649eeb88aSDavid du Colombier {
40749eeb88aSDavid du Colombier 	int i;
40849eeb88aSDavid du Colombier 	ulong schedule[32];
40949eeb88aSDavid du Colombier 	uchar p14[15], p16[16];
41049eeb88aSDavid du Colombier 	uchar s8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
41157ea2923SDavid du Colombier 	int n = strlen(pass);
41257ea2923SDavid du Colombier 
41357ea2923SDavid du Colombier 	if(n > 14){
41457ea2923SDavid du Colombier 		// let prudent people avoid the LM vulnerability
41557ea2923SDavid du Colombier 		//   and protect the loop below from buffer overflow
41657ea2923SDavid du Colombier 		memset(reply, 0, MSchapResplen);
41757ea2923SDavid du Colombier 		return;
41857ea2923SDavid du Colombier 	}
41949eeb88aSDavid du Colombier 
42049eeb88aSDavid du Colombier 	// Spec says space padded, experience says otherwise
42149eeb88aSDavid du Colombier 	memset(p14, 0, sizeof p14 -1);
42249eeb88aSDavid du Colombier 	p14[sizeof p14 - 1] = '\0';
42349eeb88aSDavid du Colombier 
42449eeb88aSDavid du Colombier 	// NT4 requires uppercase, Win XP doesn't care
42549eeb88aSDavid du Colombier 	for (i = 0; pass[i]; i++)
42649eeb88aSDavid du Colombier 		p14[i] = islower(pass[i])? toupper(pass[i]): pass[i];
42749eeb88aSDavid du Colombier 
42849eeb88aSDavid du Colombier 	for(i=0; i<2; i++) {
42949eeb88aSDavid du Colombier 		key_setup(p14+i*7, schedule);
43049eeb88aSDavid du Colombier 		memmove(p16+i*8, s8, 8);
43149eeb88aSDavid du Colombier 		block_cipher(schedule, p16+i*8, 0);
43249eeb88aSDavid du Colombier 	}
43349eeb88aSDavid du Colombier 
434f4eafd00SDavid du Colombier 	memset(p14, 0, sizeof p14);
43549eeb88aSDavid du Colombier 	hash(p16, chal, reply);
43649eeb88aSDavid du Colombier }
43749eeb88aSDavid du Colombier 
43849eeb88aSDavid du Colombier static void
dochap(char * pass,int id,char chal[ChapChallen],uchar resp[ChapResplen])43949eeb88aSDavid du Colombier dochap(char *pass, int id, char chal[ChapChallen], uchar resp[ChapResplen])
44049eeb88aSDavid du Colombier {
44149eeb88aSDavid du Colombier 	char buf[1+ChapChallen+MAXNAMELEN+1];
44249eeb88aSDavid du Colombier 	int n = strlen(pass);
44349eeb88aSDavid du Colombier 
44449eeb88aSDavid du Colombier 	*buf = id;
445f4eafd00SDavid du Colombier 	if (n > MAXNAMELEN)
446f4eafd00SDavid du Colombier 		n = MAXNAMELEN-1;
447f4eafd00SDavid du Colombier 	memset(buf, 0, sizeof buf);
448f4eafd00SDavid du Colombier 	strncpy(buf+1, pass, n);
44949eeb88aSDavid du Colombier 	memmove(buf+1+n, chal, ChapChallen);
45049eeb88aSDavid du Colombier 	md5((uchar*)buf, 1+n+ChapChallen, resp, nil);
45149eeb88aSDavid du Colombier }
45249eeb88aSDavid du Colombier 
453