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