httpdigest.c 3.8 KB

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