xref: /plan9-contrib/sys/src/cmd/auth/factotum/p9sk1.c (revision decede3daff5663ad9461ecbdee99636df315e35)
1 /*
2  * p9sk1, p9sk2 - Plan 9 secret (private) key authentication.
3  * p9sk2 is an incomplete flawed variant of p9sk1.
4  *
5  * Client protocol:
6  *	write challenge[challen]	(p9sk1 only)
7  *	read tickreq[tickreqlen]
8  *	write ticket[ticketlen]
9  *	read authenticator[authentlen]
10  *
11  * Server protocol:
12  * 	read challenge[challen]	(p9sk1 only)
13  *	write tickreq[tickreqlen]
14  *	read ticket[ticketlen]
15  *	write authenticator[authentlen]
16  */
17 
18 #include "dat.h"
19 
20 struct State
21 {
22 	int vers;
23 	Key *key;
24 	Ticket t;
25 	Ticketreq tr;
26 	char cchal[CHALLEN];
27 	char tbuf[TICKETLEN+AUTHENTLEN];
28 	char authkey[DESKEYLEN];
29 	uchar *secret;
30 	int speakfor;
31 };
32 
33 enum
34 {
35 	/* client phases */
36 	CHaveChal,
37 	CNeedTreq,
38 	CHaveTicket,
39 	CNeedAuth,
40 
41 	/* server phases */
42 	SNeedChal,
43 	SHaveTreq,
44 	SNeedTicket,
45 	SHaveAuth,
46 
47 	Maxphase,
48 };
49 
50 static char *phasenames[Maxphase] =
51 {
52 [CHaveChal]		"CHaveChal",
53 [CNeedTreq]		"CNeedTreq",
54 [CHaveTicket]		"CHaveTicket",
55 [CNeedAuth]		"CNeedAuth",
56 
57 [SNeedChal]		"SNeedChal",
58 [SHaveTreq]		"SHaveTreq",
59 [SNeedTicket]		"SNeedTicket",
60 [SHaveAuth]		"SHaveAuth",
61 };
62 
63 static int gettickets(State*, char*, char*);
64 
65 static int
p9skinit(Proto * p,Fsstate * fss)66 p9skinit(Proto *p, Fsstate *fss)
67 {
68 	State *s;
69 	int iscli, ret;
70 	Key *k;
71 	Keyinfo ki;
72 	Attr *attr;
73 
74 	if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
75 		return failure(fss, nil);
76 
77 	s = emalloc(sizeof *s);
78 	fss = fss;
79 	fss->phasename = phasenames;
80 	fss->maxphase = Maxphase;
81 	if(p == &p9sk1)
82 		s->vers = 1;
83 	else if(p == &p9sk2)
84 		s->vers = 2;
85 	else
86 		abort();
87 	if(iscli){
88 		switch(s->vers){
89 		case 1:
90 			fss->phase = CHaveChal;
91 			memrandom(s->cchal, CHALLEN);
92 			break;
93 		case 2:
94 			fss->phase = CNeedTreq;
95 			break;
96 		}
97 	}else{
98 		s->tr.type = AuthTreq;
99 		attr = setattr(_copyattr(fss->attr), "proto=p9sk1");
100 		mkkeyinfo(&ki, fss, attr);
101 		ki.user = nil;
102 		ret = findkey(&k, &ki, "user? dom?");
103 		_freeattr(attr);
104 		if(ret != RpcOk){
105 			free(s);
106 			return ret;
107 		}
108 		safecpy(s->tr.authid, _strfindattr(k->attr, "user"), sizeof(s->tr.authid));
109 		safecpy(s->tr.authdom, _strfindattr(k->attr, "dom"), sizeof(s->tr.authdom));
110 		s->key = k;
111 		memrandom(s->tr.chal, sizeof s->tr.chal);
112 		switch(s->vers){
113 		case 1:
114 			fss->phase = SNeedChal;
115 			break;
116 		case 2:
117 			fss->phase = SHaveTreq;
118 			memmove(s->cchal, s->tr.chal, CHALLEN);
119 			break;
120 		}
121 	}
122 	fss->ps = s;
123 	return RpcOk;
124 }
125 
126 static int
p9skread(Fsstate * fss,void * a,uint * n)127 p9skread(Fsstate *fss, void *a, uint *n)
128 {
129 	int m;
130 	State *s;
131 
132 	s = fss->ps;
133 	switch(fss->phase){
134 	default:
135 		return phaseerror(fss, "read");
136 
137 	case CHaveChal:
138 		m = CHALLEN;
139 		if(*n < m)
140 			return toosmall(fss, m);
141 		*n = m;
142 		memmove(a, s->cchal, m);
143 		fss->phase = CNeedTreq;
144 		return RpcOk;
145 
146 	case SHaveTreq:
147 		m = TICKREQLEN;
148 		if(*n < m)
149 			return toosmall(fss, m);
150 		*n = m;
151 		convTR2M(&s->tr, a);
152 		fss->phase = SNeedTicket;
153 		return RpcOk;
154 
155 	case CHaveTicket:
156 		m = TICKETLEN+AUTHENTLEN;
157 		if(*n < m)
158 			return toosmall(fss, m);
159 		*n = m;
160 		memmove(a, s->tbuf, m);
161 		fss->phase = CNeedAuth;
162 		return RpcOk;
163 
164 	case SHaveAuth:
165 		m = AUTHENTLEN;
166 		if(*n < m)
167 			return toosmall(fss, m);
168 		*n = m;
169 		memmove(a, s->tbuf+TICKETLEN, m);
170 		fss->ai.cuid = s->t.cuid;
171 		fss->ai.suid = s->t.suid;
172 		s->secret = emalloc(8);
173 		des56to64((uchar*)s->t.key, s->secret);
174 		fss->ai.secret = s->secret;
175 		fss->ai.nsecret = 8;
176 		fss->haveai = 1;
177 		fss->phase = Established;
178 		return RpcOk;
179 	}
180 }
181 
182 static int
p9skwrite(Fsstate * fss,void * a,uint n)183 p9skwrite(Fsstate *fss, void *a, uint n)
184 {
185 	int m, ret, sret;
186 	char tbuf[2*TICKETLEN], trbuf[TICKREQLEN], *user;
187 	Attr *attr;
188 	Authenticator auth;
189 	State *s;
190 	Key *srvkey;
191 	Keyinfo ki;
192 
193 	s = fss->ps;
194 	switch(fss->phase){
195 	default:
196 		return phaseerror(fss, "write");
197 
198 	case SNeedChal:
199 		m = CHALLEN;
200 		if(n < m)
201 			return toosmall(fss, m);
202 		memmove(s->cchal, a, m);
203 		fss->phase = SHaveTreq;
204 		return RpcOk;
205 
206 	case CNeedTreq:
207 		m = TICKREQLEN;
208 		if(n < m)
209 			return toosmall(fss, m);
210 
211 		/* remember server's chal */
212 		convM2TR(a, &s->tr);
213 		if(s->vers == 2)
214 			memmove(s->cchal, s->tr.chal, CHALLEN);
215 
216 		if(s->key != nil)
217 			closekey(s->key);
218 
219 		attr = _delattr(_delattr(_copyattr(fss->attr), "role"), "user");
220 		attr = setattr(attr, "proto=p9sk1");
221 		user = _strfindattr(fss->attr, "user");
222 		/*
223 		 * If our client is the user who started factotum (client==owner), then
224 		 * he can use whatever keys we have to speak as whoever he pleases.
225 		 * If, on the other hand, we're speaking on behalf of someone else,
226 		 * we will only vouch for their name on the local system.
227 		 *
228 		 * We do the sysuser findkey second so that if we return RpcNeedkey,
229 		 * the correct key information gets asked for.
230 		 */
231 		srvkey = nil;
232 		s->speakfor = 0;
233 		sret = RpcFailure;
234 		if(user==nil || strcmp(user, fss->sysuser) == 0){
235 			mkkeyinfo(&ki, fss, attr);
236 			ki.user = nil;
237 			sret = findkey(&srvkey, &ki,
238 				"role=speakfor dom=%q user?", s->tr.authdom);
239 		}
240 		if(user != nil)
241 			attr = setattr(attr, "user=%q", user);
242 		mkkeyinfo(&ki, fss, attr);
243 		ret = findkey(&s->key, &ki,
244 			"role=client dom=%q %s", s->tr.authdom, p9sk1.keyprompt);
245 		if(ret == RpcOk)
246 			closekey(srvkey);
247 		else if(sret == RpcOk){
248 			s->key = srvkey;
249 			s->speakfor = 1;
250 		}else if(ret == RpcConfirm || sret == RpcConfirm){
251 			_freeattr(attr);
252 			return RpcConfirm;
253 		}else{
254 			_freeattr(attr);
255 			return ret;
256 		}
257 
258 		/* fill in the rest of the request */
259 		s->tr.type = AuthTreq;
260 		safecpy(s->tr.hostid, _strfindattr(s->key->attr, "user"), sizeof s->tr.hostid);
261 		if(s->speakfor)
262 			safecpy(s->tr.uid, fss->sysuser, sizeof s->tr.uid);
263 		else
264 			safecpy(s->tr.uid, s->tr.hostid, sizeof s->tr.uid);
265 
266 		convTR2M(&s->tr, trbuf);
267 
268 		/* get tickets, from auth server or invent if we can */
269 		if(gettickets(s, trbuf, tbuf) < 0){
270 			_freeattr(attr);
271 			return failure(fss, nil);
272 		}
273 
274 		convM2T(tbuf, &s->t, (char*)s->key->priv);
275 		if(s->t.num != AuthTc){
276 			if(s->key->successes == 0 && !s->speakfor)
277 				disablekey(s->key);
278 			if(askforkeys && !s->speakfor){
279 				snprint(fss->keyinfo, sizeof fss->keyinfo,
280 					"%A %s", attr, p9sk1.keyprompt);
281 				_freeattr(attr);
282 				return RpcNeedkey;
283 			}else{
284 				_freeattr(attr);
285 				return failure(fss, Ebadkey);
286 			}
287 		}
288 		s->key->successes++;
289 		_freeattr(attr);
290 		memmove(s->tbuf, tbuf+TICKETLEN, TICKETLEN);
291 
292 		auth.num = AuthAc;
293 		memmove(auth.chal, s->tr.chal, CHALLEN);
294 		auth.id = 0;
295 		convA2M(&auth, s->tbuf+TICKETLEN, s->t.key);
296 		fss->phase = CHaveTicket;
297 		return RpcOk;
298 
299 	case SNeedTicket:
300 		m = TICKETLEN+AUTHENTLEN;
301 		if(n < m)
302 			return toosmall(fss, m);
303 		convM2T(a, &s->t, (char*)s->key->priv);
304 		if(s->t.num != AuthTs
305 		|| memcmp(s->t.chal, s->tr.chal, CHALLEN) != 0)
306 			return failure(fss, Easproto);
307 		convM2A((char*)a+TICKETLEN, &auth, s->t.key);
308 		if(auth.num != AuthAc
309 		|| memcmp(auth.chal, s->tr.chal, CHALLEN) != 0
310 		|| auth.id != 0)
311 			return failure(fss, Easproto);
312 		auth.num = AuthAs;
313 		memmove(auth.chal, s->cchal, CHALLEN);
314 		auth.id = 0;
315 		convA2M(&auth, s->tbuf+TICKETLEN, s->t.key);
316 		fss->phase = SHaveAuth;
317 		return RpcOk;
318 
319 	case CNeedAuth:
320 		m = AUTHENTLEN;
321 		if(n < m)
322 			return toosmall(fss, m);
323 		convM2A(a, &auth, s->t.key);
324 		if(auth.num != AuthAs
325 		|| memcmp(auth.chal, s->cchal, CHALLEN) != 0
326 		|| auth.id != 0)
327 			return failure(fss, Easproto);
328 		fss->ai.cuid = s->t.cuid;
329 		fss->ai.suid = s->t.suid;
330 		s->secret = emalloc(8);
331 		des56to64((uchar*)s->t.key, s->secret);
332 		fss->ai.secret = s->secret;
333 		fss->ai.nsecret = 8;
334 		fss->haveai = 1;
335 		fss->phase = Established;
336 		return RpcOk;
337 	}
338 }
339 
340 static void
p9skclose(Fsstate * fss)341 p9skclose(Fsstate *fss)
342 {
343 	State *s;
344 
345 	s = fss->ps;
346 	if(s->secret != nil){
347 		free(s->secret);
348 		s->secret = nil;
349 	}
350 	if(s->key != nil){
351 		closekey(s->key);
352 		s->key = nil;
353 	}
354 	free(s);
355 }
356 
357 static int
unhex(char c)358 unhex(char c)
359 {
360 	if('0' <= c && c <= '9')
361 		return c-'0';
362 	if('a' <= c && c <= 'f')
363 		return c-'a'+10;
364 	if('A' <= c && c <= 'F')
365 		return c-'A'+10;
366 	abort();
367 	return -1;
368 }
369 
370 static int
hexparse(char * hex,uchar * dat,int ndat)371 hexparse(char *hex, uchar *dat, int ndat)
372 {
373 	int i;
374 
375 	if(strlen(hex) != 2*ndat)
376 		return -1;
377 	if(hex[strspn(hex, "0123456789abcdefABCDEF")] != '\0')
378 		return -1;
379 	for(i=0; i<ndat; i++)
380 		dat[i] = (unhex(hex[2*i])<<4)|unhex(hex[2*i+1]);
381 	return 0;
382 }
383 
384 static int
p9skaddkey(Key * k,int before)385 p9skaddkey(Key *k, int before)
386 {
387 	char *s;
388 
389 	k->priv = emalloc(DESKEYLEN);
390 	if(s = _strfindattr(k->privattr, "!hex")){
391 		if(hexparse(s, k->priv, 7) < 0){
392 			free(k->priv);
393 			k->priv = nil;
394 			werrstr("malformed key data");
395 			return -1;
396 		}
397 	}else if(s = _strfindattr(k->privattr, "!password")){
398 		passtokey((char*)k->priv, s);
399 	}else{
400 		werrstr("no key data");
401 		free(k->priv);
402 		k->priv = nil;
403 		return -1;
404 	}
405 	return replacekey(k, before);
406 }
407 
408 static void
p9skclosekey(Key * k)409 p9skclosekey(Key *k)
410 {
411 	free(k->priv);
412 }
413 
414 static int
getastickets(State * s,char * trbuf,char * tbuf)415 getastickets(State *s, char *trbuf, char *tbuf)
416 {
417 	int asfd, rv;
418 	char *dom;
419 
420 	if((dom = _strfindattr(s->key->attr, "dom")) == nil){
421 		werrstr("auth key has no domain");
422 		return -1;
423 	}
424 	asfd = _authdial(nil, dom);
425 	if(asfd < 0)
426 		return -1;
427 	rv = _asgetticket(asfd, trbuf, tbuf);
428 	close(asfd);
429 	return rv;
430 }
431 
432 static int
mkserverticket(State * s,char * tbuf)433 mkserverticket(State *s, char *tbuf)
434 {
435 	Ticketreq *tr = &s->tr;
436 	Ticket t;
437 
438 	if(strcmp(tr->authid, tr->hostid) != 0)
439 		return -1;
440 /* this keeps creating accounts on martha from working.  -- presotto
441 	if(strcmp(tr->uid, "none") == 0)
442 		return -1;
443 */
444 	memset(&t, 0, sizeof(t));
445 	memmove(t.chal, tr->chal, CHALLEN);
446 	strcpy(t.cuid, tr->uid);
447 	strcpy(t.suid, tr->uid);
448 	memrandom(t.key, DESKEYLEN);
449 	t.num = AuthTc;
450 	convT2M(&t, tbuf, s->key->priv);
451 	t.num = AuthTs;
452 	convT2M(&t, tbuf+TICKETLEN, s->key->priv);
453 	return 0;
454 }
455 
456 static int
gettickets(State * s,char * trbuf,char * tbuf)457 gettickets(State *s, char *trbuf, char *tbuf)
458 {
459 /*
460 	if(mktickets(s, trbuf, tbuf) >= 0)
461 		return 0;
462 */
463 	if(getastickets(s, trbuf, tbuf) >= 0)
464 		return 0;
465 	return mkserverticket(s, tbuf);
466 }
467 
468 Proto p9sk1 = {
469 .name=	"p9sk1",
470 .init=		p9skinit,
471 .write=	p9skwrite,
472 .read=	p9skread,
473 .close=	p9skclose,
474 .addkey=	p9skaddkey,
475 .closekey=	p9skclosekey,
476 .keyprompt=	"user? !password?"
477 };
478 
479 Proto p9sk2 = {
480 .name=	"p9sk2",
481 .init=		p9skinit,
482 .write=	p9skwrite,
483 .read=	p9skread,
484 .close=	p9skclose,
485 };
486 
487