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