xref: /plan9/sys/src/cmd/unix/u9fs/authp9any.c (revision 031ba9d16a307751d9a4418aa220a672cebb5430)
1 /*
2  * 4th Edition p9any/p9sk1 authentication based on auth9p1.c
3  * Nigel Roles (nigel@9fs.org) 2003
4  */
5 
6 #include <plan9.h>
7 #include <fcall.h>
8 #include <u9fs.h>
9 #include <stdlib.h>	/* for random stuff */
10 
11 typedef struct	Ticket		Ticket;
12 typedef struct	Ticketreq	Ticketreq;
13 typedef struct	Authenticator	Authenticator;
14 
15 enum
16 {
17 	DOMLEN=		48,		/* length of an authentication domain name */
18 	CHALLEN=	8		/* length of a challenge */
19 };
20 
21 enum {
22 	HaveProtos,
23 	NeedProto,
24 	NeedChal,
25 	HaveTreq,
26 	NeedTicket,
27 	HaveAuth,
28 	Established,
29 };
30 
31 /* encryption numberings (anti-replay) */
32 enum
33 {
34 	AuthTreq=1,	/* ticket request */
35 	AuthChal=2,	/* challenge box request */
36 	AuthPass=3,	/* change password */
37 	AuthOK=4,	/* fixed length reply follows */
38 	AuthErr=5,	/* error follows */
39 	AuthMod=6,	/* modify user */
40 	AuthApop=7,	/* apop authentication for pop3 */
41 	AuthOKvar=9,	/* variable length reply follows */
42 	AuthChap=10,	/* chap authentication for ppp */
43 	AuthMSchap=11,	/* MS chap authentication for ppp */
44 	AuthCram=12,	/* CRAM verification for IMAP (RFC2195 & rfc2104) */
45 	AuthHttp=13,	/* http domain login */
46 	AuthVNC=14,	/* http domain login */
47 
48 
49 	AuthTs=64,	/* ticket encrypted with server's key */
50 	AuthTc,		/* ticket encrypted with client's key */
51 	AuthAs,		/* server generated authenticator */
52 	AuthAc,		/* client generated authenticator */
53 	AuthTp,		/* ticket encrypted with client's key for password change */
54 	AuthHr		/* http reply */
55 };
56 
57 struct Ticketreq
58 {
59 	char	type;
60 	char	authid[NAMELEN];	/* server's encryption id */
61 	char	authdom[DOMLEN];	/* server's authentication domain */
62 	char	chal[CHALLEN];		/* challenge from server */
63 	char	hostid[NAMELEN];	/* host's encryption id */
64 	char	uid[NAMELEN];		/* uid of requesting user on host */
65 };
66 #define	TICKREQLEN	(3*NAMELEN+CHALLEN+DOMLEN+1)
67 
68 struct Ticket
69 {
70 	char	num;			/* replay protection */
71 	char	chal[CHALLEN];		/* server challenge */
72 	char	cuid[NAMELEN];		/* uid on client */
73 	char	suid[NAMELEN];		/* uid on server */
74 	char	key[DESKEYLEN];		/* nonce DES key */
75 };
76 #define	TICKETLEN	(CHALLEN+2*NAMELEN+DESKEYLEN+1)
77 
78 struct Authenticator
79 {
80 	char	num;			/* replay protection */
81 	char	chal[CHALLEN];
82 	ulong	id;			/* authenticator id, ++'d with each auth */
83 };
84 #define	AUTHENTLEN	(CHALLEN+4+1)
85 
86 extern int chatty9p;
87 
88 static	int	convT2M(Ticket*, char*, char*);
89 static	void	convM2T(char*, Ticket*, char*);
90 static	void	convM2Tnoenc(char*, Ticket*);
91 static	int	convA2M(Authenticator*, char*, char*);
92 static	void	convM2A(char*, Authenticator*, char*);
93 static	int	convTR2M(Ticketreq*, char*);
94 static	void	convM2TR(char*, Ticketreq*);
95 static	int	passtokey(char*, char*);
96 
97 /*
98  * destructively encrypt the buffer, which
99  * must be at least 8 characters long.
100  */
101 static int
encrypt9p(void * key,void * vbuf,int n)102 encrypt9p(void *key, void *vbuf, int n)
103 {
104 	char ekey[128], *buf;
105 	int i, r;
106 
107 	if(n < 8)
108 		return 0;
109 	key_setup(key, ekey);
110 	buf = vbuf;
111 	n--;
112 	r = n % 7;
113 	n /= 7;
114 	for(i = 0; i < n; i++){
115 		block_cipher(ekey, buf, 0);
116 		buf += 7;
117 	}
118 	if(r)
119 		block_cipher(ekey, buf - 7 + r, 0);
120 	return 1;
121 }
122 
123 /*
124  * destructively decrypt the buffer, which
125  * must be at least 8 characters long.
126  */
127 static int
decrypt9p(void * key,void * vbuf,int n)128 decrypt9p(void *key, void *vbuf, int n)
129 {
130 	char ekey[128], *buf;
131 	int i, r;
132 
133 	if(n < 8)
134 		return 0;
135 	key_setup(key, ekey);
136 	buf = vbuf;
137 	n--;
138 	r = n % 7;
139 	n /= 7;
140 	buf += n * 7;
141 	if(r)
142 		block_cipher(ekey, buf - 7 + r, 1);
143 	for(i = 0; i < n; i++){
144 		buf -= 7;
145 		block_cipher(ekey, buf, 1);
146 	}
147 	return 1;
148 }
149 
150 #define	CHAR(x)		*p++ = f->x
151 #define	SHORT(x)	p[0] = f->x; p[1] = f->x>>8; p += 2
152 #define	VLONG(q)	p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
153 #define	LONG(x)		VLONG(f->x)
154 #define	STRING(x,n)	memmove(p, f->x, n); p += n
155 
156 static int
convTR2M(Ticketreq * f,char * ap)157 convTR2M(Ticketreq *f, char *ap)
158 {
159 	int n;
160 	uchar *p;
161 
162 	p = (uchar*)ap;
163 	CHAR(type);
164 	STRING(authid, NAMELEN);
165 	STRING(authdom, DOMLEN);
166 	STRING(chal, CHALLEN);
167 	STRING(hostid, NAMELEN);
168 	STRING(uid, NAMELEN);
169 	n = p - (uchar*)ap;
170 	return n;
171 }
172 
173 static int
convT2M(Ticket * f,char * ap,char * key)174 convT2M(Ticket *f, char *ap, char *key)
175 {
176 	int n;
177 	uchar *p;
178 
179 	p = (uchar*)ap;
180 	CHAR(num);
181 	STRING(chal, CHALLEN);
182 	STRING(cuid, NAMELEN);
183 	STRING(suid, NAMELEN);
184 	STRING(key, DESKEYLEN);
185 	n = p - (uchar*)ap;
186 	if(key)
187 		encrypt9p(key, ap, n);
188 	return n;
189 }
190 
191 int
convA2M(Authenticator * f,char * ap,char * key)192 convA2M(Authenticator *f, char *ap, char *key)
193 {
194 	int n;
195 	uchar *p;
196 
197 	p = (uchar*)ap;
198 	CHAR(num);
199 	STRING(chal, CHALLEN);
200 	LONG(id);
201 	n = p - (uchar*)ap;
202 	if(key)
203 		encrypt9p(key, ap, n);
204 	return n;
205 }
206 
207 #undef CHAR
208 #undef SHORT
209 #undef VLONG
210 #undef LONG
211 #undef STRING
212 
213 #define	CHAR(x)		f->x = *p++
214 #define	SHORT(x)	f->x = (p[0] | (p[1]<<8)); p += 2
215 #define	VLONG(q)	q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
216 #define	LONG(x)		VLONG(f->x)
217 #define	STRING(x,n)	memmove(f->x, p, n); p += n
218 
219 void
convM2A(char * ap,Authenticator * f,char * key)220 convM2A(char *ap, Authenticator *f, char *key)
221 {
222 	uchar *p;
223 
224 	if(key)
225 		decrypt9p(key, ap, AUTHENTLEN);
226 	p = (uchar*)ap;
227 	CHAR(num);
228 	STRING(chal, CHALLEN);
229 	LONG(id);
230 	USED(p);
231 }
232 
233 void
convM2T(char * ap,Ticket * f,char * key)234 convM2T(char *ap, Ticket *f, char *key)
235 {
236 	uchar *p;
237 
238 	if(key)
239 		decrypt9p(key, ap, TICKETLEN);
240 	p = (uchar*)ap;
241 	CHAR(num);
242 	STRING(chal, CHALLEN);
243 	STRING(cuid, NAMELEN);
244 	f->cuid[NAMELEN-1] = 0;
245 	STRING(suid, NAMELEN);
246 	f->suid[NAMELEN-1] = 0;
247 	STRING(key, DESKEYLEN);
248 	USED(p);
249 }
250 
251 #undef CHAR
252 #undef SHORT
253 #undef LONG
254 #undef VLONG
255 #undef STRING
256 
257 static int
passtokey(char * key,char * p)258 passtokey(char *key, char *p)
259 {
260 	uchar buf[NAMELEN], *t;
261 	int i, n;
262 
263 	n = strlen(p);
264 	if(n >= NAMELEN)
265 		n = NAMELEN-1;
266 	memset(buf, ' ', 8);
267 	t = buf;
268 	strncpy((char*)t, p, n);
269 	t[n] = 0;
270 	memset(key, 0, DESKEYLEN);
271 	for(;;){
272 		for(i = 0; i < DESKEYLEN; i++)
273 			key[i] = (t[i] >> i) + (t[i+1] << (8 - (i+1)));
274 		if(n <= 8)
275 			return 1;
276 		n -= 8;
277 		t += 8;
278 		if(n < 8){
279 			t -= 8 - n;
280 			n = 8;
281 		}
282 		encrypt9p(key, t, 8);
283 	}
284 	return 1;	/* not reached */
285 }
286 
287 static char authkey[DESKEYLEN];
288 static char *authid;
289 static char *authdom;
290 static char *haveprotosmsg;
291 static char *needprotomsg;
292 
293 static void
p9anyinit(void)294 p9anyinit(void)
295 {
296 	int n, fd;
297 	char abuf[200];
298 	char *af, *f[4];
299 
300 	af = autharg;
301 	if(af == nil)
302 		af = "/etc/u9fs.key";
303 
304 	if((fd = open(af, OREAD)) < 0)
305 		sysfatal("can't open key file '%s'", af);
306 
307 	if((n = readn(fd, abuf, sizeof(abuf)-1)) < 0)
308 		sysfatal("can't read key file '%s'", af);
309 	if (n > 0 && abuf[n - 1] == '\n')
310 		n--;
311 	abuf[n] = '\0';
312 
313 	if(getfields(abuf, f, nelem(f), 0, "\n") != 3)
314 		sysfatal("key file '%s' not exactly 3 lines", af);
315 
316 	passtokey(authkey, f[0]);
317 	authid = strdup(f[1]);
318 	authdom = strdup(f[2]);
319 	haveprotosmsg = malloc(strlen("p9sk1") + 1 + strlen(authdom) + 1);
320 	sprint(haveprotosmsg, "p9sk1@%s", authdom);
321 	needprotomsg = malloc(strlen("p9sk1") + 1 + strlen(authdom) + 1);
322 	sprint(needprotomsg, "p9sk1 %s", authdom);
323 }
324 
325 typedef struct AuthSession {
326 	int state;
327 	char *uname;
328 	char *aname;
329 	char cchal[CHALLEN];
330 	Ticketreq tr;
331 	Ticket t;
332 } AuthSession;
333 
334 static char*
p9anyauth(Fcall * rx,Fcall * tx)335 p9anyauth(Fcall *rx, Fcall *tx)
336 {
337 	AuthSession *sp;
338 	Fid *f;
339 	char *ep;
340 
341 	sp = malloc(sizeof(AuthSession));
342 	f = newauthfid(rx->afid, sp, &ep);
343 	if (f == nil) {
344 		free(sp);
345 		return ep;
346 	}
347 	if (chatty9p)
348 		fprint(2, "p9anyauth: afid %d\n", rx->afid);
349 	sp->state = HaveProtos;
350 	sp->uname = strdup(rx->uname);
351 	sp->aname = strdup(rx->aname);
352 	tx->aqid.type = QTAUTH;
353 	tx->aqid.path = 1;
354 	tx->aqid.vers = 0;
355 	return nil;
356 }
357 
358 static char *
p9anyattach(Fcall * rx,Fcall * tx)359 p9anyattach(Fcall *rx, Fcall *tx)
360 {
361 	AuthSession *sp;
362 	Fid *f;
363 	char *ep;
364 
365 	f = oldauthfid(rx->afid, (void **)&sp, &ep);
366 	if (f == nil)
367 		return ep;
368 	if (chatty9p)
369 		fprint(2, "p9anyattach: afid %d state %d\n", rx->afid, sp->state);
370 	if (sp->state == Established && strcmp(rx->uname, sp->uname) == 0
371 		&& strcmp(rx->aname, sp->aname) == 0)
372 		return nil;
373 	return "authentication failed";
374 }
375 
376 static int
readstr(Fcall * rx,Fcall * tx,char * s,int len)377 readstr(Fcall *rx, Fcall *tx, char *s, int len)
378 {
379 	if (rx->offset >= len)
380 		return 0;
381 	tx->count = len - rx->offset;
382 	if (tx->count > rx->count)
383 		tx->count = rx->count;
384 	memcpy(tx->data, s + rx->offset, tx->count);
385 	return tx->count;
386 }
387 
388 static char *
p9anyread(Fcall * rx,Fcall * tx)389 p9anyread(Fcall *rx, Fcall *tx)
390 {
391 	AuthSession *sp;
392 	char *ep;
393 
394 	Fid *f;
395 	f = oldauthfid(rx->afid, (void **)&sp, &ep);
396 	if (f == nil)
397 		return ep;
398 	if (chatty9p)
399 		fprint(2, "p9anyread: afid %d state %d\n", rx->fid, sp->state);
400 	switch (sp->state) {
401 	case HaveProtos:
402 		readstr(rx, tx, haveprotosmsg, strlen(haveprotosmsg) + 1);
403 		if (rx->offset + tx->count == strlen(haveprotosmsg) + 1)
404 			sp->state = NeedProto;
405 		return nil;
406 	case HaveTreq:
407 		if (rx->count != TICKREQLEN)
408 			goto botch;
409 		convTR2M(&sp->tr, tx->data);
410 		tx->count = TICKREQLEN;
411 		sp->state = NeedTicket;
412 		return nil;
413 	case HaveAuth: {
414 		Authenticator a;
415 		if (rx->count != AUTHENTLEN)
416 			goto botch;
417 		a.num = AuthAs;
418 		memmove(a.chal, sp->cchal, CHALLEN);
419 		a.id = 0;
420 		convA2M(&a, (char*)tx->data, sp->t.key);
421 		memset(sp->t.key, 0, sizeof(sp->t.key));
422 		tx->count = rx->count;
423 		sp->state = Established;
424 		return nil;
425 	}
426 	default:
427 	botch:
428 		return "protocol botch";
429 	}
430 }
431 
432 static char *
p9anywrite(Fcall * rx,Fcall * tx)433 p9anywrite(Fcall *rx, Fcall *tx)
434 {
435 	AuthSession *sp;
436 	char *ep;
437 
438 	Fid *f;
439 
440 	f = oldauthfid(rx->afid, (void **)&sp, &ep);
441 	if (f == nil)
442 		return ep;
443 	if (chatty9p)
444 		fprint(2, "p9anywrite: afid %d state %d\n", rx->fid, sp->state);
445 	switch (sp->state) {
446 	case NeedProto:
447 		if (rx->count != strlen(needprotomsg) + 1)
448 			return "protocol response wrong length";
449 		if (memcmp(rx->data, needprotomsg, rx->count) != 0)
450 			return "unacceptable protocol";
451 		sp->state = NeedChal;
452 		tx->count = rx->count;
453 		return nil;
454 	case NeedChal:
455 		if (rx->count != CHALLEN)
456 			goto botch;
457 		memmove(sp->cchal, rx->data, CHALLEN);
458 		sp->tr.type = AuthTreq;
459 		safecpy(sp->tr.authid, authid, sizeof(sp->tr.authid));
460 		safecpy(sp->tr.authdom, authdom, sizeof(sp->tr.authdom));
461 		randombytes((uchar *)sp->tr.chal, CHALLEN);
462 		safecpy(sp->tr.hostid, "", sizeof(sp->tr.hostid));
463 		safecpy(sp->tr.uid, "", sizeof(sp->tr.uid));
464 		tx->count = rx->count;
465 		sp->state = HaveTreq;
466 		return nil;
467 	case NeedTicket: {
468 		Authenticator a;
469 
470 		if (rx->count != TICKETLEN + AUTHENTLEN) {
471 			fprint(2, "bad length in attach");
472 			goto botch;
473 		}
474 		convM2T((char*)rx->data, &sp->t, authkey);
475 		if (sp->t.num != AuthTs) {
476 			fprint(2, "bad AuthTs in attach\n");
477 			goto botch;
478 		}
479 		if (memcmp(sp->t.chal, sp->tr.chal, CHALLEN) != 0) {
480 			fprint(2, "bad challenge in attach\n");
481 			goto botch;
482 		}
483 		convM2A((char*)rx->data + TICKETLEN, &a, sp->t.key);
484 		if (a.num != AuthAc) {
485 			fprint(2, "bad AuthAs in attach\n");
486 			goto botch;
487 		}
488 		if(memcmp(a.chal, sp->tr.chal, CHALLEN) != 0) {
489 			fprint(2, "bad challenge in attach 2\n");
490 			goto botch;
491 		}
492 		sp->state = HaveAuth;
493 		tx->count = rx->count;
494 		return nil;
495 	}
496 	default:
497 	botch:
498 		return "protocol botch";
499 	}
500 }
501 
502 static void
safefree(char * p)503 safefree(char *p)
504 {
505 	if (p) {
506 		memset(p, 0, strlen(p));
507 		free(p);
508 	}
509 }
510 
511 static char *
p9anyclunk(Fcall * rx,Fcall * tx)512 p9anyclunk(Fcall *rx, Fcall *tx)
513 {
514 	Fid *f;
515 	AuthSession *sp;
516 	char *ep;
517 
518 	f = oldauthfid(rx->afid, (void **)&sp, &ep);
519 	if (f == nil)
520 		return ep;
521 	if (chatty9p)
522 		fprint(2, "p9anyclunk: afid %d\n", rx->fid);
523 	safefree(sp->uname);
524 	safefree(sp->aname);
525 	memset(sp, 0, sizeof(sp));
526 	free(sp);
527 	return nil;
528 }
529 
530 Auth authp9any = {
531 	"p9any",
532 	p9anyauth,
533 	p9anyattach,
534 	p9anyinit,
535 	p9anyread,
536 	p9anywrite,
537 	p9anyclunk,
538 };
539