1 /* 2 * HTTPDIGEST - MD5 challenge/response authentication (RFC 2617) 3 * 4 * Client protocol: 5 * write challenge: nonce method uri 6 * read response: 2*MD5dlen hex digits 7 * 8 * Server protocol: 9 * unimplemented 10 */ 11 #include "dat.h" 12 13 enum 14 { 15 CNeedChal, 16 CHaveResp, 17 18 Maxphase, 19 }; 20 21 static char *phasenames[Maxphase] = { 22 [CNeedChal] "CNeedChal", 23 [CHaveResp] "CHaveResp", 24 }; 25 26 struct State 27 { 28 char resp[MD5dlen*2+1]; 29 }; 30 31 static int 32 hdinit(Proto *p, Fsstate *fss) 33 { 34 int iscli; 35 State *s; 36 37 if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0) 38 return failure(fss, nil); 39 if(!iscli) 40 return failure(fss, "%s server not supported", p->name); 41 42 s = emalloc(sizeof *s); 43 fss->phasename = phasenames; 44 fss->maxphase = Maxphase; 45 fss->phase = CNeedChal; 46 fss->ps = s; 47 return RpcOk; 48 } 49 50 static void 51 strtolower(char *s) 52 { 53 while(*s){ 54 *s = tolower(*s); 55 s++; 56 } 57 } 58 59 static void 60 digest(char *user, char *realm, char *passwd, 61 char *nonce, char *method, char *uri, 62 char *dig) 63 { 64 uchar b[MD5dlen]; 65 char ha1[MD5dlen*2+1]; 66 char ha2[MD5dlen*2+1]; 67 DigestState *s; 68 69 /* 70 * H(A1) = MD5(uid + ":" + realm ":" + passwd) 71 */ 72 s = md5((uchar*)user, strlen(user), nil, nil); 73 md5((uchar*)":", 1, nil, s); 74 md5((uchar*)realm, strlen(realm), nil, s); 75 md5((uchar*)":", 1, nil, s); 76 md5((uchar*)passwd, strlen(passwd), b, s); 77 enc16(ha1, sizeof(ha1), b, MD5dlen); 78 strtolower(ha1); 79 80 /* 81 * H(A2) = MD5(method + ":" + uri) 82 */ 83 s = md5((uchar*)method, strlen(method), nil, nil); 84 md5((uchar*)":", 1, nil, s); 85 md5((uchar*)uri, strlen(uri), b, s); 86 enc16(ha2, sizeof(ha2), b, MD5dlen); 87 strtolower(ha2); 88 89 /* 90 * digest = MD5(H(A1) + ":" + nonce + ":" + H(A2)) 91 */ 92 s = md5((uchar*)ha1, MD5dlen*2, nil, nil); 93 md5((uchar*)":", 1, nil, s); 94 md5((uchar*)nonce, strlen(nonce), nil, s); 95 md5((uchar*)":", 1, nil, s); 96 md5((uchar*)ha2, MD5dlen*2, b, s); 97 enc16(dig, MD5dlen*2+1, b, MD5dlen); 98 strtolower(dig); 99 } 100 101 static int 102 hdwrite(Fsstate *fss, void *va, uint n) 103 { 104 State *s; 105 int ret; 106 char *a, *p, *r, *u, *t; 107 char *tok[4]; 108 Key *k; 109 Keyinfo ki; 110 Attr *attr; 111 112 s = fss->ps; 113 a = va; 114 115 if(fss->phase != CNeedChal) 116 return phaseerror(fss, "write"); 117 118 attr = _delattr(_copyattr(fss->attr), "role"); 119 mkkeyinfo(&ki, fss, attr); 120 ret = findkey(&k, &ki, "%s", fss->proto->keyprompt); 121 _freeattr(attr); 122 if(ret != RpcOk) 123 return ret; 124 p = _strfindattr(k->privattr, "!password"); 125 if(p == nil) 126 return failure(fss, "key has no password"); 127 r = _strfindattr(k->attr, "realm"); 128 if(r == nil) 129 return failure(fss, "key has no realm"); 130 u = _strfindattr(k->attr, "user"); 131 if(u == nil) 132 return failure(fss, "key has no user"); 133 setattrs(fss->attr, k->attr); 134 135 /* copy in case a is not null-terminated */ 136 t = emalloc(n+1); 137 memcpy(t, a, n); 138 t[n] = 0; 139 140 /* get nonce, method, uri */ 141 if(tokenize(t, tok, 4) != 3) 142 return failure(fss, "bad challenge"); 143 144 digest(u, r, p, tok[0], tok[1], tok[2], s->resp); 145 146 free(t); 147 closekey(k); 148 fss->phase = CHaveResp; 149 return RpcOk; 150 } 151 152 static int 153 hdread(Fsstate *fss, void *va, uint *n) 154 { 155 State *s; 156 157 s = fss->ps; 158 if(fss->phase != CHaveResp) 159 return phaseerror(fss, "read"); 160 if(*n > strlen(s->resp)) 161 *n = strlen(s->resp); 162 memmove(va, s->resp, *n); 163 fss->phase = Established; 164 fss->haveai = 0; 165 return RpcOk; 166 } 167 168 static void 169 hdclose(Fsstate *fss) 170 { 171 State *s; 172 s = fss->ps; 173 free(s); 174 } 175 176 Proto httpdigest = { 177 .name= "httpdigest", 178 .init= hdinit, 179 .write= hdwrite, 180 .read= hdread, 181 .close= hdclose, 182 .addkey= replacekey, 183 .keyprompt= "user? realm? !password?" 184 }; 185