19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier * APOP, CRAM - MD5 challenge/response authentication
39a747e4fSDavid du Colombier *
49a747e4fSDavid du Colombier * The client does not authenticate the server, hence no CAI
59a747e4fSDavid du Colombier *
69a747e4fSDavid du Colombier * Client protocol:
79a747e4fSDavid du Colombier * write challenge: randomstring@domain
89a747e4fSDavid du Colombier * read response: 2*MD5dlen hex digits
99a747e4fSDavid du Colombier *
109a747e4fSDavid du Colombier * Server protocol:
119a747e4fSDavid du Colombier * read challenge: randomstring@domain
129a747e4fSDavid du Colombier * write user: user
139a747e4fSDavid du Colombier * write response: 2*MD5dlen hex digits
149a747e4fSDavid du Colombier */
159a747e4fSDavid du Colombier
169a747e4fSDavid du Colombier #include "dat.h"
179a747e4fSDavid du Colombier
189a747e4fSDavid du Colombier struct State
199a747e4fSDavid du Colombier {
209a747e4fSDavid du Colombier int asfd;
219a747e4fSDavid du Colombier int astype;
229a747e4fSDavid du Colombier Key *key;
239a747e4fSDavid du Colombier Ticket t;
249a747e4fSDavid du Colombier Ticketreq tr;
259a747e4fSDavid du Colombier char chal[128];
269a747e4fSDavid du Colombier char resp[64];
279a747e4fSDavid du Colombier char *user;
289a747e4fSDavid du Colombier };
299a747e4fSDavid du Colombier
309a747e4fSDavid du Colombier enum
319a747e4fSDavid du Colombier {
329a747e4fSDavid du Colombier CNeedChal,
339a747e4fSDavid du Colombier CHaveResp,
349a747e4fSDavid du Colombier
359a747e4fSDavid du Colombier SHaveChal,
369a747e4fSDavid du Colombier SNeedUser,
379a747e4fSDavid du Colombier SNeedResp,
389a747e4fSDavid du Colombier
399a747e4fSDavid du Colombier Maxphase,
409a747e4fSDavid du Colombier };
419a747e4fSDavid du Colombier
429a747e4fSDavid du Colombier static char *phasenames[Maxphase] = {
439a747e4fSDavid du Colombier [CNeedChal] "CNeedChal",
449a747e4fSDavid du Colombier [CHaveResp] "CHaveResp",
459a747e4fSDavid du Colombier
469a747e4fSDavid du Colombier [SHaveChal] "SHaveChal",
479a747e4fSDavid du Colombier [SNeedUser] "SNeedUser",
489a747e4fSDavid du Colombier [SNeedResp] "SNeedResp",
499a747e4fSDavid du Colombier };
509a747e4fSDavid du Colombier
519a747e4fSDavid du Colombier static int dochal(State*);
529a747e4fSDavid du Colombier static int doreply(State*, char*, char*);
539a747e4fSDavid du Colombier
549a747e4fSDavid du Colombier static int
apopinit(Proto * p,Fsstate * fss)559a747e4fSDavid du Colombier apopinit(Proto *p, Fsstate *fss)
569a747e4fSDavid du Colombier {
579a747e4fSDavid du Colombier int iscli, ret;
589a747e4fSDavid du Colombier State *s;
599a747e4fSDavid du Colombier
602ebbfa15SDavid du Colombier if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
619a747e4fSDavid du Colombier return failure(fss, nil);
629a747e4fSDavid du Colombier
639a747e4fSDavid du Colombier s = emalloc(sizeof *s);
649a747e4fSDavid du Colombier fss->phasename = phasenames;
659a747e4fSDavid du Colombier fss->maxphase = Maxphase;
669a747e4fSDavid du Colombier s->asfd = -1;
679a747e4fSDavid du Colombier if(p == &apop)
689a747e4fSDavid du Colombier s->astype = AuthApop;
699a747e4fSDavid du Colombier else if(p == &cram)
709a747e4fSDavid du Colombier s->astype = AuthCram;
719a747e4fSDavid du Colombier else
729a747e4fSDavid du Colombier abort();
739a747e4fSDavid du Colombier
749a747e4fSDavid du Colombier if(iscli)
759a747e4fSDavid du Colombier fss->phase = CNeedChal;
769a747e4fSDavid du Colombier else{
779a747e4fSDavid du Colombier if((ret = findp9authkey(&s->key, fss)) != RpcOk){
789a747e4fSDavid du Colombier free(s);
799a747e4fSDavid du Colombier return ret;
809a747e4fSDavid du Colombier }
819a747e4fSDavid du Colombier if(dochal(s) < 0){
829a747e4fSDavid du Colombier free(s);
839a747e4fSDavid du Colombier return failure(fss, nil);
849a747e4fSDavid du Colombier }
859a747e4fSDavid du Colombier fss->phase = SHaveChal;
869a747e4fSDavid du Colombier }
879a747e4fSDavid du Colombier fss->ps = s;
889a747e4fSDavid du Colombier return RpcOk;
899a747e4fSDavid du Colombier }
909a747e4fSDavid du Colombier
919a747e4fSDavid du Colombier static int
apopwrite(Fsstate * fss,void * va,uint n)929a747e4fSDavid du Colombier apopwrite(Fsstate *fss, void *va, uint n)
939a747e4fSDavid du Colombier {
949a747e4fSDavid du Colombier char *a, *v;
9559c21d95SDavid du Colombier int i, ret;
969a747e4fSDavid du Colombier uchar digest[MD5dlen];
979a747e4fSDavid du Colombier DigestState *ds;
989a747e4fSDavid du Colombier Key *k;
999a747e4fSDavid du Colombier State *s;
100260f7b65SDavid du Colombier Keyinfo ki;
1019a747e4fSDavid du Colombier
1029a747e4fSDavid du Colombier s = fss->ps;
1039a747e4fSDavid du Colombier a = va;
1049a747e4fSDavid du Colombier switch(fss->phase){
1059a747e4fSDavid du Colombier default:
1069a747e4fSDavid du Colombier return phaseerror(fss, "write");
1079a747e4fSDavid du Colombier
1089a747e4fSDavid du Colombier case CNeedChal:
109260f7b65SDavid du Colombier ret = findkey(&k, mkkeyinfo(&ki, fss, nil), "%s", fss->proto->keyprompt);
1109a747e4fSDavid du Colombier if(ret != RpcOk)
1119a747e4fSDavid du Colombier return ret;
1122ebbfa15SDavid du Colombier v = _strfindattr(k->privattr, "!password");
1139a747e4fSDavid du Colombier if(v == nil)
1149a747e4fSDavid du Colombier return failure(fss, "key has no password");
1159a747e4fSDavid du Colombier setattrs(fss->attr, k->attr);
1169a747e4fSDavid du Colombier switch(s->astype){
1179a747e4fSDavid du Colombier default:
1189a747e4fSDavid du Colombier abort();
1199a747e4fSDavid du Colombier case AuthCram:
1209a747e4fSDavid du Colombier hmac_md5((uchar*)a, n, (uchar*)v, strlen(v),
1219a747e4fSDavid du Colombier digest, nil);
122*f54edc78SDavid du Colombier snprint(s->resp, sizeof s->resp, "%.*H", MD5dlen, digest);
1239a747e4fSDavid du Colombier break;
1249a747e4fSDavid du Colombier case AuthApop:
1259a747e4fSDavid du Colombier ds = md5((uchar*)a, n, nil, nil);
1269a747e4fSDavid du Colombier md5((uchar*)v, strlen(v), digest, ds);
12759c21d95SDavid du Colombier for(i=0; i<MD5dlen; i++)
12859c21d95SDavid du Colombier sprint(&s->resp[2*i], "%2.2x", digest[i]);
1299a747e4fSDavid du Colombier break;
1309a747e4fSDavid du Colombier }
1319a747e4fSDavid du Colombier closekey(k);
1329a747e4fSDavid du Colombier fss->phase = CHaveResp;
1339a747e4fSDavid du Colombier return RpcOk;
1349a747e4fSDavid du Colombier
1359a747e4fSDavid du Colombier case SNeedUser:
1362ebbfa15SDavid du Colombier if((v = _strfindattr(fss->attr, "user")) && strcmp(v, a) != 0)
1379a747e4fSDavid du Colombier return failure(fss, "bad user");
1389a747e4fSDavid du Colombier fss->attr = setattr(fss->attr, "user=%q", a);
1399a747e4fSDavid du Colombier s->user = estrdup(a);
1409a747e4fSDavid du Colombier fss->phase = SNeedResp;
1419a747e4fSDavid du Colombier return RpcOk;
1429a747e4fSDavid du Colombier
1439a747e4fSDavid du Colombier case SNeedResp:
1449a747e4fSDavid du Colombier if(n != 2*MD5dlen)
1459a747e4fSDavid du Colombier return failure(fss, "response not MD5 digest");
1469a747e4fSDavid du Colombier if(doreply(s, s->user, a) < 0){
1479a747e4fSDavid du Colombier fss->phase = SNeedUser;
1489a747e4fSDavid du Colombier return failure(fss, nil);
1499a747e4fSDavid du Colombier }
1509a747e4fSDavid du Colombier fss->haveai = 1;
1519a747e4fSDavid du Colombier fss->ai.cuid = s->t.cuid;
1529a747e4fSDavid du Colombier fss->ai.suid = s->t.suid;
1539a747e4fSDavid du Colombier fss->ai.nsecret = 0;
1549a747e4fSDavid du Colombier fss->ai.secret = nil;
1559a747e4fSDavid du Colombier fss->phase = Established;
1569a747e4fSDavid du Colombier return RpcOk;
1579a747e4fSDavid du Colombier }
1589a747e4fSDavid du Colombier }
1599a747e4fSDavid du Colombier
1609a747e4fSDavid du Colombier static int
apopread(Fsstate * fss,void * va,uint * n)1619a747e4fSDavid du Colombier apopread(Fsstate *fss, void *va, uint *n)
1629a747e4fSDavid du Colombier {
1639a747e4fSDavid du Colombier State *s;
1649a747e4fSDavid du Colombier
1659a747e4fSDavid du Colombier s = fss->ps;
1669a747e4fSDavid du Colombier switch(fss->phase){
1679a747e4fSDavid du Colombier default:
1689a747e4fSDavid du Colombier return phaseerror(fss, "read");
1699a747e4fSDavid du Colombier
1709a747e4fSDavid du Colombier case CHaveResp:
1719a747e4fSDavid du Colombier if(*n > strlen(s->resp))
1729a747e4fSDavid du Colombier *n = strlen(s->resp);
1739a747e4fSDavid du Colombier memmove(va, s->resp, *n);
1749a747e4fSDavid du Colombier fss->phase = Established;
1759a747e4fSDavid du Colombier fss->haveai = 0;
1769a747e4fSDavid du Colombier return RpcOk;
1779a747e4fSDavid du Colombier
1789a747e4fSDavid du Colombier case SHaveChal:
1799a747e4fSDavid du Colombier if(*n > strlen(s->chal))
1809a747e4fSDavid du Colombier *n = strlen(s->chal);
1819a747e4fSDavid du Colombier memmove(va, s->chal, *n);
1829a747e4fSDavid du Colombier fss->phase = SNeedUser;
1839a747e4fSDavid du Colombier return RpcOk;
1849a747e4fSDavid du Colombier }
1859a747e4fSDavid du Colombier }
1869a747e4fSDavid du Colombier
1879a747e4fSDavid du Colombier static void
apopclose(Fsstate * fss)1889a747e4fSDavid du Colombier apopclose(Fsstate *fss)
1899a747e4fSDavid du Colombier {
1909a747e4fSDavid du Colombier State *s;
1919a747e4fSDavid du Colombier
1929a747e4fSDavid du Colombier s = fss->ps;
1939a747e4fSDavid du Colombier if(s->asfd >= 0){
1949a747e4fSDavid du Colombier close(s->asfd);
1959a747e4fSDavid du Colombier s->asfd = -1;
1969a747e4fSDavid du Colombier }
1979a747e4fSDavid du Colombier if(s->key != nil){
1989a747e4fSDavid du Colombier closekey(s->key);
1999a747e4fSDavid du Colombier s->key = nil;
2009a747e4fSDavid du Colombier }
2019a747e4fSDavid du Colombier if(s->user != nil){
2029a747e4fSDavid du Colombier free(s->user);
2039a747e4fSDavid du Colombier s->user = nil;
2049a747e4fSDavid du Colombier }
2059a747e4fSDavid du Colombier free(s);
2069a747e4fSDavid du Colombier }
2079a747e4fSDavid du Colombier
2089a747e4fSDavid du Colombier static int
dochal(State * s)2099a747e4fSDavid du Colombier dochal(State *s)
2109a747e4fSDavid du Colombier {
2119a747e4fSDavid du Colombier char *dom, *user, trbuf[TICKREQLEN];
2129a747e4fSDavid du Colombier
2139a747e4fSDavid du Colombier s->asfd = -1;
2149a747e4fSDavid du Colombier
2159a747e4fSDavid du Colombier /* send request to authentication server and get challenge */
2169a747e4fSDavid du Colombier /* send request to authentication server and get challenge */
2172ebbfa15SDavid du Colombier if((dom = _strfindattr(s->key->attr, "dom")) == nil
2182ebbfa15SDavid du Colombier || (user = _strfindattr(s->key->attr, "user")) == nil){
2199a747e4fSDavid du Colombier werrstr("apop/dochal cannot happen");
2209a747e4fSDavid du Colombier goto err;
2219a747e4fSDavid du Colombier }
2229a747e4fSDavid du Colombier
2239a747e4fSDavid du Colombier s->asfd = _authdial(nil, dom);
2249a747e4fSDavid du Colombier
2259a747e4fSDavid du Colombier /* could generate our own challenge on error here */
2269a747e4fSDavid du Colombier if(s->asfd < 0)
2279a747e4fSDavid du Colombier goto err;
2289a747e4fSDavid du Colombier
2299a747e4fSDavid du Colombier memset(&s->tr, 0, sizeof(s->tr));
2309a747e4fSDavid du Colombier s->tr.type = s->astype;
2319a747e4fSDavid du Colombier safecpy(s->tr.authdom, dom, sizeof s->tr.authdom);
2329a747e4fSDavid du Colombier safecpy(s->tr.hostid, user, sizeof(s->tr.hostid));
2339a747e4fSDavid du Colombier convTR2M(&s->tr, trbuf);
2349a747e4fSDavid du Colombier
2359a747e4fSDavid du Colombier if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
2369a747e4fSDavid du Colombier goto err;
2379a747e4fSDavid du Colombier if(_asrdresp(s->asfd, s->chal, sizeof s->chal) <= 5)
2389a747e4fSDavid du Colombier goto err;
2399a747e4fSDavid du Colombier return 0;
2409a747e4fSDavid du Colombier
2419a747e4fSDavid du Colombier err:
2429a747e4fSDavid du Colombier if(s->asfd >= 0)
2439a747e4fSDavid du Colombier close(s->asfd);
2449a747e4fSDavid du Colombier s->asfd = -1;
2459a747e4fSDavid du Colombier return -1;
2469a747e4fSDavid du Colombier }
2479a747e4fSDavid du Colombier
2489a747e4fSDavid du Colombier static int
doreply(State * s,char * user,char * response)2499a747e4fSDavid du Colombier doreply(State *s, char *user, char *response)
2509a747e4fSDavid du Colombier {
2519a747e4fSDavid du Colombier char ticket[TICKETLEN+AUTHENTLEN];
2529a747e4fSDavid du Colombier char trbuf[TICKREQLEN];
2539a747e4fSDavid du Colombier int n;
2549a747e4fSDavid du Colombier Authenticator a;
2559a747e4fSDavid du Colombier
2569a747e4fSDavid du Colombier memrandom(s->tr.chal, CHALLEN);
2579a747e4fSDavid du Colombier safecpy(s->tr.uid, user, sizeof(s->tr.uid));
2589a747e4fSDavid du Colombier convTR2M(&s->tr, trbuf);
2599a747e4fSDavid du Colombier if((n=write(s->asfd, trbuf, TICKREQLEN)) != TICKREQLEN){
2609a747e4fSDavid du Colombier if(n >= 0)
2619a747e4fSDavid du Colombier werrstr("short write to auth server");
2629a747e4fSDavid du Colombier goto err;
2639a747e4fSDavid du Colombier }
2649a747e4fSDavid du Colombier /* send response to auth server */
2659a747e4fSDavid du Colombier if(strlen(response) != MD5dlen*2){
2669a747e4fSDavid du Colombier werrstr("response not MD5 digest");
2679a747e4fSDavid du Colombier goto err;
2689a747e4fSDavid du Colombier }
2699a747e4fSDavid du Colombier if((n=write(s->asfd, response, MD5dlen*2)) != MD5dlen*2){
2709a747e4fSDavid du Colombier if(n >= 0)
2719a747e4fSDavid du Colombier werrstr("short write to auth server");
2729a747e4fSDavid du Colombier goto err;
2739a747e4fSDavid du Colombier }
2749a747e4fSDavid du Colombier if(_asrdresp(s->asfd, ticket, TICKETLEN+AUTHENTLEN) < 0){
2759a747e4fSDavid du Colombier /* leave connection open so we can try again */
2769a747e4fSDavid du Colombier return -1;
2779a747e4fSDavid du Colombier }
2789a747e4fSDavid du Colombier close(s->asfd);
2799a747e4fSDavid du Colombier s->asfd = -1;
2809a747e4fSDavid du Colombier
2819a747e4fSDavid du Colombier convM2T(ticket, &s->t, (char*)s->key->priv);
2829a747e4fSDavid du Colombier if(s->t.num != AuthTs
2839a747e4fSDavid du Colombier || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
2846822557bSDavid du Colombier if(s->key->successes == 0)
285260f7b65SDavid du Colombier disablekey(s->key);
2869a747e4fSDavid du Colombier werrstr(Easproto);
2879a747e4fSDavid du Colombier goto err;
2889a747e4fSDavid du Colombier }
2896822557bSDavid du Colombier s->key->successes++;
2909a747e4fSDavid du Colombier convM2A(ticket+TICKETLEN, &a, s->t.key);
2919a747e4fSDavid du Colombier if(a.num != AuthAc
2929a747e4fSDavid du Colombier || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
2939a747e4fSDavid du Colombier || a.id != 0){
2949a747e4fSDavid du Colombier werrstr(Easproto);
2959a747e4fSDavid du Colombier goto err;
2969a747e4fSDavid du Colombier }
2979a747e4fSDavid du Colombier
2989a747e4fSDavid du Colombier return 0;
2999a747e4fSDavid du Colombier err:
3009a747e4fSDavid du Colombier if(s->asfd >= 0)
3019a747e4fSDavid du Colombier close(s->asfd);
3029a747e4fSDavid du Colombier s->asfd = -1;
3039a747e4fSDavid du Colombier return -1;
3049a747e4fSDavid du Colombier }
3059a747e4fSDavid du Colombier
3069a747e4fSDavid du Colombier Proto apop = {
3079a747e4fSDavid du Colombier .name= "apop",
3089a747e4fSDavid du Colombier .init= apopinit,
3099a747e4fSDavid du Colombier .write= apopwrite,
3109a747e4fSDavid du Colombier .read= apopread,
3119a747e4fSDavid du Colombier .close= apopclose,
3129a747e4fSDavid du Colombier .addkey= replacekey,
3139a747e4fSDavid du Colombier .keyprompt= "!password?"
3149a747e4fSDavid du Colombier };
3159a747e4fSDavid du Colombier
3169a747e4fSDavid du Colombier Proto cram = {
3179a747e4fSDavid du Colombier .name= "cram",
3189a747e4fSDavid du Colombier .init= apopinit,
3199a747e4fSDavid du Colombier .write= apopwrite,
3209a747e4fSDavid du Colombier .read= apopread,
3219a747e4fSDavid du Colombier .close= apopclose,
3229a747e4fSDavid du Colombier .addkey= replacekey,
3239a747e4fSDavid du Colombier .keyprompt= "!password?"
3249a747e4fSDavid du Colombier };
325