Blob


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 */
18 #include "std.h"
19 #include "dat.h"
21 extern Proto p9sk1, p9sk2;
22 static int gettickets(Ticketreq*, char*, Key*);
24 #define max(a, b) ((a) > (b) ? (a) : (b))
25 enum
26 {
27 MAXAUTH = max(TICKREQLEN, TICKETLEN+max(TICKETLEN, AUTHENTLEN))
28 };
30 static int
31 p9skclient(Conv *c)
32 {
33 char *user;
34 char cchal[CHALLEN];
35 uchar secret[8];
36 char buf[MAXAUTH];
37 int speakfor, ret;
38 Attr *a;
39 Authenticator au;
40 Key *k;
41 Ticket t;
42 Ticketreq tr;
44 ret = -1;
45 a = nil;
46 k = nil;
48 /* p9sk1: send client challenge */
49 if(c->proto == &p9sk1){
50 c->state = "write challenge";
51 memrandom(cchal, CHALLEN);
52 if(convwrite(c, cchal, CHALLEN) < 0)
53 goto out;
54 }
56 /* read ticket request */
57 c->state = "read tickreq";
58 if(convread(c, buf, TICKREQLEN) < 0)
59 goto out;
60 convM2TR(buf, &tr);
62 /* p9sk2: use server challenge as client challenge */
63 if(c->proto == &p9sk2)
64 memmove(cchal, tr.chal, CHALLEN);
66 /*
67 * find a key.
68 *
69 * if the user is the factotum owner, any key will do.
70 * if not, then if we have a speakfor key,
71 * we will only vouch for the user's local identity.
72 *
73 * this logic is duplicated in p9any.c
74 */
75 user = strfindattr(c->attr, "user");
76 a = delattr(copyattr(c->attr), "role");
77 a = addattr(a, "proto=p9sk1");
79 if(strcmp(c->sysuser, owner) == 0){
80 speakfor = 0;
81 a = addattr(a, "proto=p9sk1 user? dom=%q", tr.authdom);
82 }else if(user==nil || strcmp(c->sysuser, user)==0){
83 speakfor = 1;
84 a = delattr(a, "user");
85 a = addattr(a, "proto=p9sk1 user? dom=%q role=speakfor", tr.authdom);
86 }else{
87 werrstr("will not authenticate for %q as %q", c->sysuser, user);
88 goto out;
89 }
91 for(;;){
92 c->state = "find key";
93 k = keyfetch(c, "%A", a);
94 if(k == nil)
95 goto out;
97 /* relay ticket request to auth server, get tickets */
98 strcpy(tr.hostid, strfindattr(k->attr, "user"));
99 if(speakfor)
100 strcpy(tr.uid, c->sysuser);
101 else
102 strcpy(tr.uid, tr.hostid);
104 c->state = "get tickets";
105 if(gettickets(&tr, buf, k) < 0)
106 goto out;
108 convM2T(buf, &t, k->priv);
109 if(t.num == AuthTc)
110 break;
112 /* we don't agree with the auth server about the key; try again */
113 c->state = "replace key";
114 if((k = keyreplace(c, k, "key mismatch with auth server")) == nil){
115 werrstr("key mismatch with auth server");
116 goto out;
120 /* send second ticket and authenticator to server */
121 c->state = "write ticket+auth";
122 memmove(buf, buf+TICKETLEN, TICKETLEN);
123 au.num = AuthAc;
124 memmove(au.chal, tr.chal, CHALLEN);
125 au.id = 0;
126 convA2M(&au, buf+TICKETLEN, t.key);
127 if(convwrite(c, buf, TICKETLEN+AUTHENTLEN) < 0)
128 goto out;
130 /* read authenticator from server */
131 c->state = "read auth";
132 if(convread(c, buf, AUTHENTLEN) < 0)
133 goto out;
134 convM2A(buf, &au, t.key);
135 if(au.num != AuthAs || memcmp(au.chal, cchal, CHALLEN) != 0 || au.id != 0){
136 werrstr("server lies through his teeth");
137 goto out;
140 /* success */
141 c->attr = addcap(c->attr, c->sysuser, &t);
142 flog("p9skclient success %A", c->attr); /* before adding secret! */
143 des56to64((uchar*)t.key, secret);
144 c->attr = addattr(c->attr, "secret=%.8H", secret);
145 ret = 0;
147 out:
148 if(ret < 0)
149 flog("p9skclient: %r");
150 freeattr(a);
151 keyclose(k);
152 return ret;
155 static int
156 p9skserver(Conv *c)
158 char cchal[CHALLEN], buf[MAXAUTH];
159 uchar secret[8];
160 int ret;
161 Attr *a;
162 Authenticator au;
163 Key *k;
164 Ticketreq tr;
165 Ticket t;
167 ret = -1;
169 a = addattr(copyattr(c->attr), "user? dom?");
170 a = addattr(a, "user? dom? proto=p9sk1");
171 if((k = keyfetch(c, "%A", a)) == nil)
172 goto out;
174 /* p9sk1: read client challenge */
175 if(c->proto == &p9sk1){
176 if(convread(c, cchal, CHALLEN) < 0)
177 goto out;
180 /* send ticket request */
181 memset(&tr, 0, sizeof tr);
182 tr.type = AuthTreq;
183 strcpy(tr.authid, strfindattr(k->attr, "user"));
184 strcpy(tr.authdom, strfindattr(k->attr, "dom"));
185 memrandom(tr.chal, sizeof tr.chal);
186 convTR2M(&tr, buf);
187 if(convwrite(c, buf, TICKREQLEN) < 0)
188 goto out;
190 /* p9sk2: use server challenge as client challenge */
191 if(c->proto == &p9sk2)
192 memmove(cchal, tr.chal, sizeof tr.chal);
194 /* read ticket+authenticator */
195 if(convread(c, buf, TICKETLEN+AUTHENTLEN) < 0)
196 goto out;
198 convM2T(buf, &t, k->priv);
199 if(t.num != AuthTs || memcmp(t.chal, tr.chal, CHALLEN) != 0){
200 /* BUG badkey */
201 werrstr("key mismatch with auth server");
202 goto out;
205 convM2A(buf+TICKETLEN, &au, t.key);
206 if(au.num != AuthAc || memcmp(au.chal, tr.chal, CHALLEN) != 0 || au.id != 0){
207 werrstr("client lies through his teeth");
208 goto out;
211 /* send authenticator */
212 au.num = AuthAs;
213 memmove(au.chal, cchal, CHALLEN);
214 convA2M(&au, buf, t.key);
215 if(convwrite(c, buf, AUTHENTLEN) < 0)
216 goto out;
218 /* success */
219 c->attr = addcap(c->attr, c->sysuser, &t);
220 flog("p9skserver success %A", c->attr); /* before adding secret! */
221 des56to64((uchar*)t.key, secret);
222 c->attr = addattr(c->attr, "secret=%.8H", secret);
223 ret = 0;
225 out:
226 if(ret < 0)
227 flog("p9skserver: %r");
228 freeattr(a);
229 keyclose(k);
230 return ret;
233 int
234 _asgetticket(int fd, char *trbuf, char *tbuf)
236 if(write(fd, trbuf, TICKREQLEN) < 0){
237 close(fd);
238 return -1;
240 return _asrdresp(fd, tbuf, 2*TICKETLEN);
242 static int
243 getastickets(Ticketreq *tr, char *buf)
245 int asfd;
246 int ret;
248 if((asfd = xioauthdial(nil, tr->authdom)) < 0)
249 return -1;
250 convTR2M(tr, buf);
251 ret = xioasgetticket(asfd, buf, buf);
252 xioclose(asfd);
253 return ret;
256 static int
257 mktickets(Ticketreq *tr, char *buf, Key *k)
259 Ticket t;
261 if(strcmp(tr->authid, tr->hostid) != 0)
262 return -1;
264 memset(&t, 0, sizeof t);
265 memmove(t.chal, tr->chal, CHALLEN);
266 strcpy(t.cuid, tr->uid);
267 strcpy(t.suid, tr->uid);
268 memrandom(t.key, DESKEYLEN);
269 t.num = AuthTc;
270 convT2M(&t, buf, k->priv);
271 t.num = AuthTs;
272 convT2M(&t, buf+TICKETLEN, k->priv);
273 return 0;
276 static int
277 gettickets(Ticketreq *tr, char *buf, Key *k)
279 if(getastickets(tr, buf) == 0)
280 return 0;
281 if(mktickets(tr, buf, k) == 0)
282 return 0;
283 werrstr("gettickets: %r");
284 return -1;
287 static int
288 p9sk1check(Key *k)
290 char *user, *dom, *pass;
291 Ticketreq tr;
293 user = strfindattr(k->attr, "user");
294 dom = strfindattr(k->attr, "dom");
295 if(user==nil || dom==nil){
296 werrstr("need user and dom attributes");
297 return -1;
299 if(strlen(user) >= sizeof tr.authid){
300 werrstr("user name too long");
301 return -1;
303 if(strlen(dom) >= sizeof tr.authdom){
304 werrstr("auth dom name too long");
305 return -1;
308 k->priv = emalloc(DESKEYLEN);
309 if(pass = strfindattr(k->privattr, "!password"))
310 passtokey(k->priv, pass);
311 else if(pass = strfindattr(k->privattr, "!hex")){
312 if(hexparse(pass, k->priv, 7) < 0){
313 werrstr("malformed !hex key data");
314 return -1;
316 }else{
317 werrstr("need !password or !hex attribute");
318 return -1;
321 return 0;
324 static void
325 p9sk1close(Key *k)
327 free(k->priv);
328 k->priv = nil;
331 static Role
332 p9sk1roles[] =
334 "client", p9skclient,
335 "server", p9skserver,
337 };
339 static Role
340 p9sk2roles[] =
342 "client", p9skclient,
343 "server", p9skserver,
345 };
347 Proto p9sk1 = {
348 "p9sk1",
349 p9sk1roles,
350 "user? dom? !password?",
351 p9sk1check,
352 p9sk1close
353 };
355 Proto p9sk2 = {
356 "p9sk2",
357 p9sk2roles
358 };