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