2 * APOP, CRAM - MD5 challenge/response authentication
4 * The client does not authenticate the server, hence no CAI.
8 * S -> C: random@domain
12 * Note that this is the protocol between factotum and the local
13 * program, not between the two factotums. The information
14 * exchanged here is wrapped in the APOP protocol by the local
17 * If S sends "bad [msg]" instead of "ok", that is a hint that the key is bad.
18 * The protocol goes back to "C -> S: user".
27 if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
28 werrstr("need user and !password attributes");
37 char *chal, *pw, *res;
38 int astype, nchal, npw, ntry, ret;
52 else if(c->proto == &cram)
59 c->state = "find key";
60 k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
64 c->state = "read challenge";
65 if((nchal = convreadm(c, &chal)) < 0)
71 c->attr = addattrs(copyattr(attr), k->attr);
72 if((pw = strfindattr(k->privattr, "!password")) == nil){
73 werrstr("key has no password (cannot happen?)");
80 ds = md5((uchar*)chal, nchal, nil, nil);
81 md5((uchar*)pw, npw, resp, ds);
84 hmac_md5((uchar*)chal, nchal, (uchar*)pw, npw, resp, nil);
88 /* C->S: APOP user hex-response\n */
90 c->state = "write user";
92 sprint(c->statebuf, "write user (auth attempt #%d)", ntry);
93 c->state = c->statebuf;
95 if(convprint(c, "%s", strfindattr(k->attr, "user")) < 0)
98 c->state = "write response";
99 if(convprint(c, "%.*H", sizeof resp, resp) < 0)
102 c->state = "read result";
103 if(convreadm(c, &res) < 0)
106 if(strcmp(res, "ok") == 0)
109 if(strncmp(res, "bad ", 4) != 0){
110 werrstr("bad result: %s", res);
114 c->state = "replace key";
115 if((k = keyreplace(c, k, "%s", res+4)) == nil){
116 c->state = "auth failed";
117 werrstr("%s", res+4);
124 werrstr("succeeded");
135 /* shared with auth dialing routines */
136 typedef struct ServerState ServerState;
152 static int apopchal(ServerState*, int, char[APOPCHALLEN]);
153 static int apopresp(ServerState*, char*, char*);
158 char chal[APOPCHALLEN], *user, *resp;
166 memset(&s, 0, sizeof s);
169 if(c->proto == &apop)
171 else if(c->proto == &cram)
174 werrstr("bad proto");
178 c->state = "find key";
179 if((s.k = plan9authkey(c->attr)) == nil)
182 a = copyattr(s.k->attr);
183 a = delattr(a, "proto");
184 c->attr = addattrs(c->attr, a);
187 c->state = "authdial";
188 s.hostid = strfindattr(s.k->attr, "user");
189 s.dom = strfindattr(s.k->attr, "dom");
190 if((s.asfd = xioauthdial(nil, s.dom)) < 0){
191 werrstr("authdial %s: %r", s.dom);
195 c->state = "authchal";
196 if(apopchal(&s, astype, chal) < 0)
199 c->state = "write challenge";
200 if(convprint(c, "%s", chal) < 0)
204 c->state = "read user";
205 if(convreadm(c, &user) < 0)
208 c->state = "read response";
209 if(convreadm(c, &resp) < 0)
212 c->state = "authwrite";
213 switch(apopresp(&s, user, resp)){
217 c->state = "write status";
218 if(convprint(c, "bad authentication failed") < 0)
222 c->state = "write status";
223 if(convprint(c, "ok") < 0)
235 c->attr = addcap(c->attr, c->sysuser, &s.t);
246 apopchal(ServerState *s, int astype, char chal[APOPCHALLEN])
248 char trbuf[TICKREQLEN];
251 memset(&tr, 0, sizeof tr);
255 if(strlen(s->hostid) >= sizeof tr.hostid){
256 werrstr("hostid too long");
259 strcpy(tr.hostid, s->hostid);
261 if(strlen(s->dom) >= sizeof tr.authdom){
262 werrstr("domain too long");
265 strcpy(tr.authdom, s->dom);
267 convTR2M(&tr, trbuf);
268 if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
271 if(xioasrdresp(s->asfd, chal, APOPCHALLEN) <= 5)
279 apopresp(ServerState *s, char *user, char *resp)
281 char tabuf[TICKETLEN+AUTHENTLEN];
282 char trbuf[TICKREQLEN];
289 if(memrandom(tr.chal, CHALLEN) < 0)
292 if(strlen(user) >= sizeof tr.uid){
293 werrstr("uid too long");
296 strcpy(tr.uid, user);
298 convTR2M(&tr, trbuf);
299 if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
303 if(xiowrite(s->asfd, resp, len) != len)
306 if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
309 convM2T(tabuf, &t, s->k->priv);
311 || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
312 werrstr("key mismatch with auth server");
316 convM2A(tabuf+TICKETLEN, &a, t.key);
318 || memcmp(a.chal, tr.chal, sizeof a.chal) != 0
320 werrstr("key2 mismatch with auth server");
331 "client", apopclient,
332 "server", apopserver,
339 .checkkey= apopcheck,
340 .keyprompt= "user? !password?",
346 .checkkey= apopcheck,
347 .keyprompt= "user? !password?",