xref: /plan9-contrib/sys/src/cmd/auth/factotum/p9cr.c (revision f54edc786b9c49b2c7ab1c0695cdc8c698b11f4d)
19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier  * p9cr, vnc - textual challenge/response authentication
39a747e4fSDavid du Colombier  *
49a747e4fSDavid du Colombier  * Client protocol:	[currently unimplemented]
59a747e4fSDavid du Colombier  *	write challenge
69a747e4fSDavid du Colombier  *	read response
79a747e4fSDavid du Colombier  *
89a747e4fSDavid du Colombier  * Server protocol:
99a747e4fSDavid du Colombier  *	write user
109a747e4fSDavid du Colombier  *	read challenge
119a747e4fSDavid du Colombier  * 	write response
129a747e4fSDavid du Colombier  */
139a747e4fSDavid du Colombier 
149a747e4fSDavid du Colombier #include "dat.h"
159a747e4fSDavid du Colombier 
169a747e4fSDavid du Colombier enum
179a747e4fSDavid du Colombier {
189a747e4fSDavid du Colombier 	Maxchal=	64,
199a747e4fSDavid du Colombier };
209a747e4fSDavid du Colombier 
219a747e4fSDavid du Colombier typedef struct State State;
229a747e4fSDavid du Colombier struct State
239a747e4fSDavid du Colombier {
249a747e4fSDavid du Colombier 	Key	*key;
259a747e4fSDavid du Colombier 	int	astype;
269a747e4fSDavid du Colombier 	int	asfd;
279a747e4fSDavid du Colombier 	Ticket	t;
289a747e4fSDavid du Colombier 	Ticketreq tr;
299a747e4fSDavid du Colombier 	char	chal[Maxchal];
309a747e4fSDavid du Colombier 	int	challen;
31b7b24591SDavid du Colombier 	char	resp[Maxchal];
32b7b24591SDavid du Colombier 	int	resplen;
339a747e4fSDavid du Colombier };
349a747e4fSDavid du Colombier 
359a747e4fSDavid du Colombier enum
369a747e4fSDavid du Colombier {
379a747e4fSDavid du Colombier 	CNeedChal,
389a747e4fSDavid du Colombier 	CHaveResp,
399a747e4fSDavid du Colombier 
409a747e4fSDavid du Colombier 	SHaveChal,
419a747e4fSDavid du Colombier 	SNeedResp,
429a747e4fSDavid du Colombier 
439a747e4fSDavid du Colombier 	Maxphase,
449a747e4fSDavid du Colombier };
459a747e4fSDavid du Colombier 
469a747e4fSDavid du Colombier static char *phasenames[Maxphase] =
479a747e4fSDavid du Colombier {
489a747e4fSDavid du Colombier [CNeedChal]	"CNeedChal",
499a747e4fSDavid du Colombier [CHaveResp]	"CHaveResp",
509a747e4fSDavid du Colombier 
519a747e4fSDavid du Colombier [SHaveChal]	"SHaveChal",
529a747e4fSDavid du Colombier [SNeedResp]	"SNeedResp",
539a747e4fSDavid du Colombier };
549a747e4fSDavid du Colombier 
559a747e4fSDavid du Colombier static void
p9crclose(Fsstate * fss)569a747e4fSDavid du Colombier p9crclose(Fsstate *fss)
579a747e4fSDavid du Colombier {
589a747e4fSDavid du Colombier 	State *s;
599a747e4fSDavid du Colombier 
609a747e4fSDavid du Colombier 	s = fss->ps;
619a747e4fSDavid du Colombier 	if(s->asfd >= 0){
629a747e4fSDavid du Colombier 		close(s->asfd);
639a747e4fSDavid du Colombier 		s->asfd = -1;
649a747e4fSDavid du Colombier 	}
659a747e4fSDavid du Colombier 	free(s);
669a747e4fSDavid du Colombier }
679a747e4fSDavid du Colombier 
689a747e4fSDavid du Colombier static int getchal(State*, Fsstate*);
699a747e4fSDavid du Colombier 
709a747e4fSDavid du Colombier static int
p9crinit(Proto * p,Fsstate * fss)719a747e4fSDavid du Colombier p9crinit(Proto *p, Fsstate *fss)
729a747e4fSDavid du Colombier {
739a747e4fSDavid du Colombier 	int iscli, ret;
749a747e4fSDavid du Colombier 	char *user;
759a747e4fSDavid du Colombier 	State *s;
76b7b24591SDavid du Colombier 	Attr *attr;
77260f7b65SDavid du Colombier 	Keyinfo ki;
789a747e4fSDavid du Colombier 
792ebbfa15SDavid du Colombier 	if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
809a747e4fSDavid du Colombier 		return failure(fss, nil);
819a747e4fSDavid du Colombier 
829a747e4fSDavid du Colombier 	s = emalloc(sizeof(*s));
839a747e4fSDavid du Colombier 	s->asfd = -1;
849a747e4fSDavid du Colombier 	if(p == &p9cr){
859a747e4fSDavid du Colombier 		s->astype = AuthChal;
869a747e4fSDavid du Colombier 		s->challen = NETCHLEN;
879a747e4fSDavid du Colombier 	}else if(p == &vnc){
889a747e4fSDavid du Colombier 		s->astype = AuthVNC;
899a747e4fSDavid du Colombier 		s->challen = Maxchal;
909a747e4fSDavid du Colombier 	}else
919a747e4fSDavid du Colombier 		abort();
929a747e4fSDavid du Colombier 
93b7b24591SDavid du Colombier 	if(iscli){
94b7b24591SDavid du Colombier 		fss->phase = CNeedChal;
95b7b24591SDavid du Colombier 		if(p == &p9cr)
96b7b24591SDavid du Colombier 			attr = setattr(_copyattr(fss->attr), "proto=p9sk1");
97b7b24591SDavid du Colombier 		else
98b7b24591SDavid du Colombier 			attr = nil;
99260f7b65SDavid du Colombier 		ret = findkey(&s->key, mkkeyinfo(&ki, fss, attr),
100b7b24591SDavid du Colombier 			"role=client %s", p->keyprompt);
101b7b24591SDavid du Colombier 		_freeattr(attr);
102b7b24591SDavid du Colombier 		if(ret != RpcOk){
103b7b24591SDavid du Colombier 			free(s);
104b7b24591SDavid du Colombier 			return ret;
105b7b24591SDavid du Colombier 		}
106b7b24591SDavid du Colombier 		fss->ps = s;
107b7b24591SDavid du Colombier 	}else{
1089a747e4fSDavid du Colombier 		if((ret = findp9authkey(&s->key, fss)) != RpcOk){
1099a747e4fSDavid du Colombier 			free(s);
1109a747e4fSDavid du Colombier 			return ret;
1119a747e4fSDavid du Colombier 		}
1122ebbfa15SDavid du Colombier 		if((user = _strfindattr(fss->attr, "user")) == nil){
1139a747e4fSDavid du Colombier 			free(s);
1149a747e4fSDavid du Colombier 			return failure(fss, "no user name specified in start msg");
1159a747e4fSDavid du Colombier 		}
1169a747e4fSDavid du Colombier 		if(strlen(user) >= sizeof s->tr.uid){
1179a747e4fSDavid du Colombier 			free(s);
1189a747e4fSDavid du Colombier 			return failure(fss, "user name too long");
1199a747e4fSDavid du Colombier 		}
1209a747e4fSDavid du Colombier 		fss->ps = s;
1219a747e4fSDavid du Colombier 		strcpy(s->tr.uid, user);
1229a747e4fSDavid du Colombier 		ret = getchal(s, fss);
1239a747e4fSDavid du Colombier 		if(ret != RpcOk){
1249a747e4fSDavid du Colombier 			p9crclose(fss);	/* frees s */
1259a747e4fSDavid du Colombier 			fss->ps = nil;
1269a747e4fSDavid du Colombier 		}
127b7b24591SDavid du Colombier 	}
128b7b24591SDavid du Colombier 	fss->phasename = phasenames;
129b7b24591SDavid du Colombier 	fss->maxphase = Maxphase;
1309a747e4fSDavid du Colombier 	return ret;
1319a747e4fSDavid du Colombier }
1329a747e4fSDavid du Colombier 
1339a747e4fSDavid du Colombier static int
p9crread(Fsstate * fss,void * va,uint * n)1349a747e4fSDavid du Colombier p9crread(Fsstate *fss, void *va, uint *n)
1359a747e4fSDavid du Colombier {
1369a747e4fSDavid du Colombier 	int m;
1379a747e4fSDavid du Colombier 	State *s;
1389a747e4fSDavid du Colombier 
1399a747e4fSDavid du Colombier 	s = fss->ps;
1409a747e4fSDavid du Colombier 	switch(fss->phase){
1419a747e4fSDavid du Colombier 	default:
1429a747e4fSDavid du Colombier 		return phaseerror(fss, "read");
1439a747e4fSDavid du Colombier 
144b7b24591SDavid du Colombier 	case CHaveResp:
145b7b24591SDavid du Colombier 		if(s->resplen < *n)
146b7b24591SDavid du Colombier 			*n = s->resplen;
147b7b24591SDavid du Colombier 		memmove(va, s->resp, *n);
148b7b24591SDavid du Colombier 		fss->phase = Established;
149b7b24591SDavid du Colombier 		return RpcOk;
150b7b24591SDavid du Colombier 
1519a747e4fSDavid du Colombier 	case SHaveChal:
1529a747e4fSDavid du Colombier 		if(s->astype == AuthChal)
1539a747e4fSDavid du Colombier 			m = strlen(s->chal);	/* ascii string */
1549a747e4fSDavid du Colombier 		else
1559a747e4fSDavid du Colombier 			m = s->challen;		/* fixed length binary */
1569a747e4fSDavid du Colombier 		if(m > *n)
1579a747e4fSDavid du Colombier 			return toosmall(fss, m);
1589a747e4fSDavid du Colombier 		*n = m;
1599a747e4fSDavid du Colombier 		memmove(va, s->chal, m);
1609a747e4fSDavid du Colombier 		fss->phase = SNeedResp;
1619a747e4fSDavid du Colombier 		return RpcOk;
1629a747e4fSDavid du Colombier 	}
1639a747e4fSDavid du Colombier }
1649a747e4fSDavid du Colombier 
1659a747e4fSDavid du Colombier static int
p9response(Fsstate * fss,State * s)166b7b24591SDavid du Colombier p9response(Fsstate *fss, State *s)
167b7b24591SDavid du Colombier {
168b7b24591SDavid du Colombier 	char key[DESKEYLEN];
169b7b24591SDavid du Colombier 	uchar buf[8];
170b7b24591SDavid du Colombier 	ulong chal;
171b7b24591SDavid du Colombier 	char *pw;
172b7b24591SDavid du Colombier 
1732ebbfa15SDavid du Colombier 	pw = _strfindattr(s->key->privattr, "!password");
174b7b24591SDavid du Colombier 	if(pw == nil)
175b7b24591SDavid du Colombier 		return failure(fss, "vncresponse cannot happen");
176b7b24591SDavid du Colombier 	passtokey(key, pw);
177b7b24591SDavid du Colombier 	memset(buf, 0, 8);
178*f54edc78SDavid du Colombier 	snprint((char*)buf, sizeof buf, "%d", atoi(s->chal));
179b7b24591SDavid du Colombier 	if(encrypt(key, buf, 8) < 0)
180b7b24591SDavid du Colombier 		return failure(fss, "can't encrypt response");
181b7b24591SDavid du Colombier 	chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
182b7b24591SDavid du Colombier 	s->resplen = snprint(s->resp, sizeof s->resp, "%.8lux", chal);
183b7b24591SDavid du Colombier 	return RpcOk;
184b7b24591SDavid du Colombier }
185b7b24591SDavid du Colombier 
186b7b24591SDavid du Colombier static uchar tab[256];
187b7b24591SDavid du Colombier 
188b7b24591SDavid du Colombier /* VNC reverses the bits of each byte before using as a des key */
189b7b24591SDavid du Colombier static void
mktab(void)190b7b24591SDavid du Colombier mktab(void)
191b7b24591SDavid du Colombier {
192b7b24591SDavid du Colombier 	int i, j, k;
193b7b24591SDavid du Colombier 	static int once;
194b7b24591SDavid du Colombier 
195b7b24591SDavid du Colombier 	if(once)
196b7b24591SDavid du Colombier 		return;
197b7b24591SDavid du Colombier 	once = 1;
198b7b24591SDavid du Colombier 
199b7b24591SDavid du Colombier 	for(i=0; i<256; i++) {
200b7b24591SDavid du Colombier 		j=i;
201b7b24591SDavid du Colombier 		tab[i] = 0;
202b7b24591SDavid du Colombier 		for(k=0; k<8; k++) {
203b7b24591SDavid du Colombier 			tab[i] = (tab[i]<<1) | (j&1);
204b7b24591SDavid du Colombier 			j >>= 1;
205b7b24591SDavid du Colombier 		}
206b7b24591SDavid du Colombier 	}
207b7b24591SDavid du Colombier }
208b7b24591SDavid du Colombier 
209b7b24591SDavid du Colombier static int
vncaddkey(Key * k,int before)21070b8e010SDavid du Colombier vncaddkey(Key *k, int before)
211b7b24591SDavid du Colombier {
212b7b24591SDavid du Colombier 	uchar *p;
213b7b24591SDavid du Colombier 	char *s;
214b7b24591SDavid du Colombier 
215b7b24591SDavid du Colombier 	k->priv = emalloc(8+1);
2162ebbfa15SDavid du Colombier 	if(s = _strfindattr(k->privattr, "!password")){
217b7b24591SDavid du Colombier 		mktab();
218b7b24591SDavid du Colombier 		memset(k->priv, 0, 8+1);
219b7b24591SDavid du Colombier 		strncpy((char*)k->priv, s, 8);
220b7b24591SDavid du Colombier 		for(p=k->priv; *p; p++)
221b7b24591SDavid du Colombier 			*p = tab[*p];
222b7b24591SDavid du Colombier 	}else{
223b7b24591SDavid du Colombier 		werrstr("no key data");
224b7b24591SDavid du Colombier 		return -1;
225b7b24591SDavid du Colombier 	}
22670b8e010SDavid du Colombier 	return replacekey(k, before);
227b7b24591SDavid du Colombier }
228b7b24591SDavid du Colombier 
229b7b24591SDavid du Colombier static void
vncclosekey(Key * k)230b7b24591SDavid du Colombier vncclosekey(Key *k)
231b7b24591SDavid du Colombier {
232b7b24591SDavid du Colombier 	free(k->priv);
233b7b24591SDavid du Colombier }
234b7b24591SDavid du Colombier 
235b7b24591SDavid du Colombier static int
vncresponse(Fsstate *,State * s)236b7b24591SDavid du Colombier vncresponse(Fsstate*, State *s)
237b7b24591SDavid du Colombier {
238b7b24591SDavid du Colombier 	DESstate des;
239b7b24591SDavid du Colombier 
240b7b24591SDavid du Colombier 	memmove(s->resp, s->chal, sizeof s->chal);
241b7b24591SDavid du Colombier 	setupDESstate(&des, s->key->priv, nil);
242b7b24591SDavid du Colombier 	desECBencrypt((uchar*)s->resp, s->challen, &des);
243b7b24591SDavid du Colombier 	s->resplen = s->challen;
244b7b24591SDavid du Colombier 	return RpcOk;
245b7b24591SDavid du Colombier }
246b7b24591SDavid du Colombier 
247b7b24591SDavid du Colombier static int
p9crwrite(Fsstate * fss,void * va,uint n)2489a747e4fSDavid du Colombier p9crwrite(Fsstate *fss, void *va, uint n)
2499a747e4fSDavid du Colombier {
2509a747e4fSDavid du Colombier 	char tbuf[TICKETLEN+AUTHENTLEN];
2519a747e4fSDavid du Colombier 	State *s;
2529a747e4fSDavid du Colombier 	char *data = va;
2539a747e4fSDavid du Colombier 	Authenticator a;
2549a747e4fSDavid du Colombier 	char resp[Maxchal];
255b7b24591SDavid du Colombier 	int ret;
2569a747e4fSDavid du Colombier 
2579a747e4fSDavid du Colombier 	s = fss->ps;
2589a747e4fSDavid du Colombier 	switch(fss->phase){
2599a747e4fSDavid du Colombier 	default:
2609a747e4fSDavid du Colombier 		return phaseerror(fss, "write");
2619a747e4fSDavid du Colombier 
262b7b24591SDavid du Colombier 	case CNeedChal:
263b7b24591SDavid du Colombier 		if(n >= sizeof(s->chal))
264b7b24591SDavid du Colombier 			return failure(fss, Ebadarg);
265b7b24591SDavid du Colombier 		memset(s->chal, 0, sizeof s->chal);
266b7b24591SDavid du Colombier 		memmove(s->chal, data, n);
267b7b24591SDavid du Colombier 		s->challen = n;
268b7b24591SDavid du Colombier 
269b7b24591SDavid du Colombier 		if(s->astype == AuthChal)
270b7b24591SDavid du Colombier 			ret = p9response(fss, s);
271b7b24591SDavid du Colombier 		else
272b7b24591SDavid du Colombier 			ret = vncresponse(fss, s);
273b7b24591SDavid du Colombier 		if(ret != RpcOk)
274b7b24591SDavid du Colombier 			return ret;
275b7b24591SDavid du Colombier 		fss->phase = CHaveResp;
276b7b24591SDavid du Colombier 		return RpcOk;
277b7b24591SDavid du Colombier 
2789a747e4fSDavid du Colombier 	case SNeedResp:
2799a747e4fSDavid du Colombier 		/* send response to auth server and get ticket */
2809a747e4fSDavid du Colombier 		if(n > sizeof(resp))
2819a747e4fSDavid du Colombier 			return failure(fss, Ebadarg);
2829a747e4fSDavid du Colombier 		memset(resp, 0, sizeof resp);
2839a747e4fSDavid du Colombier 		memmove(resp, data, n);
2849a747e4fSDavid du Colombier 		if(write(s->asfd, resp, s->challen) != s->challen)
2859a747e4fSDavid du Colombier 			return failure(fss, Easproto);
2869a747e4fSDavid du Colombier 
2879a747e4fSDavid du Colombier 		/* get ticket plus authenticator from auth server */
2889a747e4fSDavid du Colombier 		if(_asrdresp(s->asfd, tbuf, TICKETLEN+AUTHENTLEN) < 0)
2899a747e4fSDavid du Colombier 			return failure(fss, nil);
2909a747e4fSDavid du Colombier 
2919a747e4fSDavid du Colombier 		/* check ticket */
2929a747e4fSDavid du Colombier 		convM2T(tbuf, &s->t, s->key->priv);
2939a747e4fSDavid du Colombier 		if(s->t.num != AuthTs
294260f7b65SDavid du Colombier 		|| memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
2956822557bSDavid du Colombier 			if (s->key->successes == 0)
296260f7b65SDavid du Colombier 				disablekey(s->key);
2979a747e4fSDavid du Colombier 			return failure(fss, Easproto);
298260f7b65SDavid du Colombier 		}
2996822557bSDavid du Colombier 		s->key->successes++;
3009a747e4fSDavid du Colombier 		convM2A(tbuf+TICKETLEN, &a, s->t.key);
3019a747e4fSDavid du Colombier 		if(a.num != AuthAc
3029a747e4fSDavid du Colombier 		|| memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
3039a747e4fSDavid du Colombier 		|| a.id != 0)
3049a747e4fSDavid du Colombier 			return failure(fss, Easproto);
3059a747e4fSDavid du Colombier 
3069a747e4fSDavid du Colombier 		fss->haveai = 1;
3079a747e4fSDavid du Colombier 		fss->ai.cuid = s->t.cuid;
3089a747e4fSDavid du Colombier 		fss->ai.suid = s->t.suid;
3099a747e4fSDavid du Colombier 		fss->ai.nsecret = 0;
3109a747e4fSDavid du Colombier 		fss->ai.secret = nil;
3119a747e4fSDavid du Colombier 		fss->phase = Established;
3129a747e4fSDavid du Colombier 		return RpcOk;
3139a747e4fSDavid du Colombier 	}
3149a747e4fSDavid du Colombier }
3159a747e4fSDavid du Colombier 
3169a747e4fSDavid du Colombier static int
getchal(State * s,Fsstate * fss)3179a747e4fSDavid du Colombier getchal(State *s, Fsstate *fss)
3189a747e4fSDavid du Colombier {
3199a747e4fSDavid du Colombier 	char trbuf[TICKREQLEN];
3209a747e4fSDavid du Colombier 	int n;
321d9306527SDavid du Colombier 
3222ebbfa15SDavid du Colombier 	safecpy(s->tr.hostid, _strfindattr(s->key->attr, "user"), sizeof(s->tr.hostid));
3232ebbfa15SDavid du Colombier 	safecpy(s->tr.authdom, _strfindattr(s->key->attr, "dom"), sizeof(s->tr.authdom));
3249a747e4fSDavid du Colombier 	s->tr.type = s->astype;
3259a747e4fSDavid du Colombier 	convTR2M(&s->tr, trbuf);
3269a747e4fSDavid du Colombier 
3279a747e4fSDavid du Colombier 	/* get challenge from auth server */
3282ebbfa15SDavid du Colombier 	s->asfd = _authdial(nil, _strfindattr(s->key->attr, "dom"));
3299a747e4fSDavid du Colombier 	if(s->asfd < 0)
3309a747e4fSDavid du Colombier 		return failure(fss, Easproto);
3319a747e4fSDavid du Colombier 	if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
3329a747e4fSDavid du Colombier 		return failure(fss, Easproto);
3339a747e4fSDavid du Colombier 	n = _asrdresp(s->asfd, s->chal, s->challen);
3349a747e4fSDavid du Colombier 	if(n <= 0){
3359a747e4fSDavid du Colombier 		if(n == 0)
3369a747e4fSDavid du Colombier 			werrstr("_asrdresp short read");
3379a747e4fSDavid du Colombier 		return failure(fss, nil);
3389a747e4fSDavid du Colombier 	}
3399a747e4fSDavid du Colombier 	s->challen = n;
3409a747e4fSDavid du Colombier 	fss->phase = SHaveChal;
3419a747e4fSDavid du Colombier 	return RpcOk;
3429a747e4fSDavid du Colombier }
3439a747e4fSDavid du Colombier 
3449a747e4fSDavid du Colombier Proto p9cr =
3459a747e4fSDavid du Colombier {
3469a747e4fSDavid du Colombier .name=		"p9cr",
3479a747e4fSDavid du Colombier .init=		p9crinit,
3489a747e4fSDavid du Colombier .write=		p9crwrite,
3499a747e4fSDavid du Colombier .read=		p9crread,
3509a747e4fSDavid du Colombier .close=		p9crclose,
351b7b24591SDavid du Colombier .keyprompt=	"user? !password?",
3529a747e4fSDavid du Colombier };
3539a747e4fSDavid du Colombier 
3549a747e4fSDavid du Colombier Proto vnc =
3559a747e4fSDavid du Colombier {
3569a747e4fSDavid du Colombier .name=		"vnc",
3579a747e4fSDavid du Colombier .init=		p9crinit,
3589a747e4fSDavid du Colombier .write=		p9crwrite,
3599a747e4fSDavid du Colombier .read=		p9crread,
3609a747e4fSDavid du Colombier .close=		p9crclose,
361b7b24591SDavid du Colombier .keyprompt=	"!password?",
362b7b24591SDavid du Colombier .addkey=	vncaddkey,
3639a747e4fSDavid du Colombier };
364