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 static int gettickets(Ticketreq*, char*, Key*);
23 #define max(a, b) ((a) > (b) ? (a) : (b))
24 enum
25 {
26 MAXAUTH = max(TICKREQLEN, TICKETLEN+max(TICKETLEN, AUTHENTLEN))
27 };
29 static int
30 p9skclient(Conv *c)
31 {
32 char *user;
33 char cchal[CHALLEN];
34 uchar secret[8];
35 char buf[MAXAUTH];
36 int speakfor, ret;
37 Attr *a;
38 Authenticator au;
39 Key *k;
40 Ticket t;
41 Ticketreq tr;
43 ret = -1;
44 a = nil;
45 k = nil;
47 /* p9sk1: send client challenge */
48 if(c->proto == &p9sk1){
49 c->state = "write challenge";
50 memrandom(cchal, CHALLEN);
51 if(convwrite(c, cchal, CHALLEN) < 0)
52 goto out;
53 }
55 /* read ticket request */
56 c->state = "read tickreq";
57 if(convread(c, buf, TICKREQLEN) < 0)
58 goto out;
59 convM2TR(buf, &tr);
61 /* p9sk2: use server challenge as client challenge */
62 if(c->proto == &p9sk2)
63 memmove(cchal, tr.chal, CHALLEN);
65 /*
66 * find a key.
67 *
68 * if the user is the factotum owner, any key will do.
69 * if not, then if we have a speakfor key,
70 * we will only vouch for the user's local identity.
71 *
72 * this logic is duplicated in p9any.c
73 */
74 user = strfindattr(c->attr, "user");
75 a = delattr(copyattr(c->attr), "role");
76 a = addattr(a, "proto=p9sk1");
78 if(strcmp(c->sysuser, owner) == 0){
79 speakfor = 0;
80 a = addattr(a, "proto=p9sk1 user? dom=%q", tr.authdom);
81 }else if(user==nil || strcmp(c->sysuser, user)==0){
82 speakfor = 1;
83 a = delattr(a, "user");
84 a = addattr(a, "proto=p9sk1 user? dom=%q role=speakfor", tr.authdom);
85 }else{
86 werrstr("will not authenticate for %q as %q", c->sysuser, user);
87 goto out;
88 }
90 for(;;){
91 c->state = "find key";
92 k = keyfetch(c, "%A", a);
93 if(k == nil)
94 goto out;
96 /* relay ticket request to auth server, get tickets */
97 strcpy(tr.hostid, strfindattr(k->attr, "user"));
98 if(speakfor)
99 strcpy(tr.uid, c->sysuser);
100 else
101 strcpy(tr.uid, tr.hostid);
103 c->state = "get tickets";
104 if(gettickets(&tr, buf, k) < 0)
105 goto out;
107 convM2T(buf, &t, k->priv);
108 if(t.num == AuthTc)
109 break;
111 /* we don't agree with the auth server about the key; try again */
112 c->state = "replace key";
113 if((k = keyreplace(c, k, "key mismatch with auth server")) == nil){
114 werrstr("key mismatch with auth server");
115 goto out;
119 /* send second ticket and authenticator to server */
120 c->state = "write ticket+auth";
121 memmove(buf, buf+TICKETLEN, TICKETLEN);
122 au.num = AuthAc;
123 memmove(au.chal, tr.chal, CHALLEN);
124 au.id = 0;
125 convA2M(&au, buf+TICKETLEN, t.key);
126 if(convwrite(c, buf, TICKETLEN+AUTHENTLEN) < 0)
127 goto out;
129 /* read authenticator from server */
130 c->state = "read auth";
131 if(convread(c, buf, AUTHENTLEN) < 0)
132 goto out;
133 convM2A(buf, &au, t.key);
134 if(au.num != AuthAs || memcmp(au.chal, cchal, CHALLEN) != 0 || au.id != 0){
135 werrstr("server lies through his teeth");
136 goto out;
139 /* success */
140 c->attr = addcap(c->attr, c->sysuser, &t);
141 des56to64((uchar*)t.key, secret);
142 c->attr = addattr(c->attr, "secret=%.8H", secret);
143 ret = 0;
145 out:
146 freeattr(a);
147 keyclose(k);
148 return ret;
151 static int
152 p9skserver(Conv *c)
154 char cchal[CHALLEN], buf[MAXAUTH];
155 uchar secret[8];
156 int ret;
157 Attr *a;
158 Authenticator au;
159 Key *k;
160 Ticketreq tr;
161 Ticket t;
163 ret = -1;
165 a = addattr(copyattr(c->attr), "user? dom?");
166 a = addattr(a, "user? dom? proto=p9sk1");
167 if((k = keyfetch(c, "%A", a)) == nil)
168 goto out;
170 /* p9sk1: read client challenge */
171 if(c->proto == &p9sk1){
172 if(convread(c, cchal, CHALLEN) < 0)
173 goto out;
176 /* send ticket request */
177 memset(&tr, 0, sizeof tr);
178 tr.type = AuthTreq;
179 strcpy(tr.authid, strfindattr(k->attr, "user"));
180 strcpy(tr.authdom, strfindattr(k->attr, "dom"));
181 memrandom(tr.chal, sizeof tr.chal);
182 convTR2M(&tr, buf);
183 if(convwrite(c, buf, TICKREQLEN) < 0)
184 goto out;
186 /* p9sk2: use server challenge as client challenge */
187 if(c->proto == &p9sk2)
188 memmove(cchal, tr.chal, sizeof tr.chal);
190 /* read ticket+authenticator */
191 if(convread(c, buf, TICKETLEN+AUTHENTLEN) < 0)
192 goto out;
194 convM2T(buf, &t, k->priv);
195 if(t.num != AuthTs || memcmp(t.chal, tr.chal, CHALLEN) != 0){
196 /* BUG badkey */
197 werrstr("key mismatch with auth server");
198 goto out;
201 convM2A(buf+TICKETLEN, &au, t.key);
202 if(au.num != AuthAc || memcmp(au.chal, tr.chal, CHALLEN) != 0 || au.id != 0){
203 werrstr("client lies through his teeth");
204 goto out;
207 /* send authenticator */
208 au.num = AuthAs;
209 memmove(au.chal, cchal, CHALLEN);
210 convA2M(&au, buf, t.key);
211 if(convwrite(c, buf, AUTHENTLEN) < 0)
212 goto out;
214 /* success */
215 c->attr = addcap(c->attr, c->sysuser, &t);
216 des56to64((uchar*)t.key, secret);
217 c->attr = addattr(c->attr, "secret=%.8H", secret);
218 ret = 0;
220 out:
221 freeattr(a);
222 keyclose(k);
223 return ret;
226 int
227 _asgetticket(int fd, char *trbuf, char *tbuf)
229 if(write(fd, trbuf, TICKREQLEN) < 0){
230 close(fd);
231 return -1;
233 return _asrdresp(fd, tbuf, 2*TICKETLEN);
235 static int
236 getastickets(Ticketreq *tr, char *buf)
238 int asfd;
239 int ret;
241 if((asfd = xioauthdial(nil, tr->authdom)) < 0)
242 return -1;
243 convTR2M(tr, buf);
244 ret = xioasgetticket(asfd, buf, buf);
245 xioclose(asfd);
246 return ret;
249 static int
250 mktickets(Ticketreq *tr, char *buf, Key *k)
252 Ticket t;
254 if(strcmp(tr->authid, tr->hostid) != 0)
255 return -1;
257 memset(&t, 0, sizeof t);
258 memmove(t.chal, tr->chal, CHALLEN);
259 strcpy(t.cuid, tr->uid);
260 strcpy(t.suid, tr->uid);
261 memrandom(t.key, DESKEYLEN);
262 t.num = AuthTc;
263 convT2M(&t, buf, k->priv);
264 t.num = AuthTs;
265 convT2M(&t, buf+TICKETLEN, k->priv);
266 return 0;
269 static int
270 gettickets(Ticketreq *tr, char *buf, Key *k)
272 if(getastickets(tr, buf) == 0)
273 return 0;
274 if(mktickets(tr, buf, k) == 0)
275 return 0;
276 werrstr("gettickets: %r");
277 return -1;
280 static int
281 p9sk1check(Key *k)
283 char *user, *dom, *pass;
284 Ticketreq tr;
286 user = strfindattr(k->attr, "user");
287 dom = strfindattr(k->attr, "dom");
288 if(user==nil || dom==nil){
289 werrstr("need user and dom attributes");
290 return -1;
292 if(strlen(user) >= sizeof tr.authid){
293 werrstr("user name too long");
294 return -1;
296 if(strlen(dom) >= sizeof tr.authdom){
297 werrstr("auth dom name too long");
298 return -1;
301 k->priv = emalloc(DESKEYLEN);
302 if(pass = strfindattr(k->privattr, "!password"))
303 passtokey(k->priv, pass);
304 else if(pass = strfindattr(k->privattr, "!hex")){
305 if(hexparse(pass, k->priv, 7) < 0){
306 werrstr("malformed !hex key data");
307 return -1;
309 }else{
310 werrstr("need !password or !hex attribute");
311 return -1;
314 return 0;
317 static void
318 p9sk1close(Key *k)
320 free(k->priv);
321 k->priv = nil;
324 static Role
325 p9sk1roles[] =
327 "client", p9skclient,
328 "server", p9skserver,
330 };
332 static Role
333 p9sk2roles[] =
335 "client", p9skclient,
336 "server", p9skserver,
338 };
340 Proto p9sk1 = {
341 .name= "p9sk1",
342 .roles= p9sk1roles,
343 .checkkey= p9sk1check,
344 .closekey= p9sk1close,
345 .keyprompt= "user? dom? !password?",
346 };
348 Proto p9sk2 = {
349 .name= "p9sk2",
350 .roles= p9sk2roles,
351 };