Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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
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
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
409
p9skclosekey(Key *k)
410
{
411
	free(k->priv);
412
}
413
 
414
static int
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
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
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