http_digest.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
  9. *
  10. * This software is licensed as described in the file COPYING, which
  11. * you should have received as part of this distribution. The terms
  12. * are also available at http://curl.haxx.se/docs/copyright.html.
  13. *
  14. * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  15. * copies of the Software, and permit persons to whom the Software is
  16. * furnished to do so, under the terms of the COPYING file.
  17. *
  18. * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  19. * KIND, either express or implied.
  20. *
  21. * $Id$
  22. ***************************************************************************/
  23. #include "setup.h"
  24. #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
  25. /* -- WIN32 approved -- */
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include <stdarg.h>
  29. #include <stdlib.h>
  30. #include <ctype.h>
  31. #include "urldata.h"
  32. #include "sendf.h"
  33. #include "strequal.h"
  34. #include "base64.h"
  35. #include "md5.h"
  36. #include "http_digest.h"
  37. #include "strtok.h"
  38. #include "url.h" /* for Curl_safefree() */
  39. #include "memory.h"
  40. #define _MPRINTF_REPLACE /* use our functions only */
  41. #include <curl/mprintf.h>
  42. /* The last #include file should be: */
  43. #include "memdebug.h"
  44. /* Test example headers:
  45. WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
  46. Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598"
  47. */
  48. CURLdigest Curl_input_digest(struct connectdata *conn,
  49. bool proxy,
  50. char *header) /* rest of the *-authenticate:
  51. header */
  52. {
  53. bool more = TRUE;
  54. char *token = NULL;
  55. char *tmp = NULL;
  56. bool foundAuth = FALSE;
  57. bool foundAuthInt = FALSE;
  58. struct SessionHandle *data=conn->data;
  59. bool before = FALSE; /* got a nonce before */
  60. struct digestdata *d;
  61. if(proxy) {
  62. d = &data->state.proxydigest;
  63. }
  64. else {
  65. d = &data->state.digest;
  66. }
  67. /* skip initial whitespaces */
  68. while(*header && isspace((int)*header))
  69. header++;
  70. if(checkprefix("Digest", header)) {
  71. header += strlen("Digest");
  72. /* If we already have received a nonce, keep that in mind */
  73. if(d->nonce)
  74. before = TRUE;
  75. /* clear off any former leftovers and init to defaults */
  76. Curl_digest_cleanup_one(d);
  77. while(more) {
  78. char value[32];
  79. char content[128];
  80. size_t totlen=0;
  81. while(*header && isspace((int)*header))
  82. header++;
  83. /* how big can these strings be? */
  84. if((2 == sscanf(header, "%31[^=]=\"%127[^\"]\"",
  85. value, content)) ||
  86. /* try the same scan but without quotes around the content but don't
  87. include the possibly trailing comma */
  88. (2 == sscanf(header, "%31[^=]=%127[^,]",
  89. value, content)) ) {
  90. if(strequal(value, "nonce")) {
  91. d->nonce = strdup(content);
  92. if(!d->nonce)
  93. return CURLDIGEST_NOMEM;
  94. }
  95. else if(strequal(value, "stale")) {
  96. if(strequal(content, "true")) {
  97. d->stale = TRUE;
  98. d->nc = 1; /* we make a new nonce now */
  99. }
  100. }
  101. else if(strequal(value, "realm")) {
  102. d->realm = strdup(content);
  103. if(!d->realm)
  104. return CURLDIGEST_NOMEM;
  105. }
  106. else if(strequal(value, "opaque")) {
  107. d->opaque = strdup(content);
  108. if(!d->opaque)
  109. return CURLDIGEST_NOMEM;
  110. }
  111. else if(strequal(value, "qop")) {
  112. char *tok_buf;
  113. /* tokenize the list and choose auth if possible, use a temporary
  114. clone of the buffer since strtok_r() ruins it */
  115. tmp = strdup(content);
  116. if(!tmp)
  117. return CURLDIGEST_NOMEM;
  118. token = strtok_r(tmp, ",", &tok_buf);
  119. while (token != NULL) {
  120. if (strequal(token, "auth")) {
  121. foundAuth = TRUE;
  122. }
  123. else if (strequal(token, "auth-int")) {
  124. foundAuthInt = TRUE;
  125. }
  126. token = strtok_r(NULL, ",", &tok_buf);
  127. }
  128. free(tmp);
  129. /*select only auth o auth-int. Otherwise, ignore*/
  130. if (foundAuth) {
  131. d->qop = strdup("auth");
  132. if(!d->qop)
  133. return CURLDIGEST_NOMEM;
  134. }
  135. else if (foundAuthInt) {
  136. d->qop = strdup("auth-int");
  137. if(!d->qop)
  138. return CURLDIGEST_NOMEM;
  139. }
  140. }
  141. else if(strequal(value, "algorithm")) {
  142. d->algorithm = strdup(content);
  143. if(!d->algorithm)
  144. return CURLDIGEST_NOMEM;
  145. if(strequal(content, "MD5-sess"))
  146. d->algo = CURLDIGESTALGO_MD5SESS;
  147. else if(strequal(content, "MD5"))
  148. d->algo = CURLDIGESTALGO_MD5;
  149. else
  150. return CURLDIGEST_BADALGO;
  151. }
  152. else {
  153. /* unknown specifier, ignore it! */
  154. }
  155. totlen = strlen(value)+strlen(content)+1;
  156. if(header[strlen(value)+1] == '\"')
  157. /* the contents were within quotes, then add 2 for them to the
  158. length */
  159. totlen += 2;
  160. }
  161. else
  162. break; /* we're done here */
  163. header += totlen;
  164. if(',' == *header)
  165. /* allow the list to be comma-separated */
  166. header++;
  167. }
  168. /* We had a nonce since before, and we got another one now without
  169. 'stale=true'. This means we provided bad credentials in the previous
  170. request */
  171. if(before && !d->stale)
  172. return CURLDIGEST_BAD;
  173. /* We got this header without a nonce, that's a bad Digest line! */
  174. if(!d->nonce)
  175. return CURLDIGEST_BAD;
  176. }
  177. else
  178. /* else not a digest, get out */
  179. return CURLDIGEST_NONE;
  180. return CURLDIGEST_FINE;
  181. }
  182. /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
  183. static void md5_to_ascii(unsigned char *source, /* 16 bytes */
  184. unsigned char *dest) /* 33 bytes */
  185. {
  186. int i;
  187. for(i=0; i<16; i++)
  188. snprintf((char *)&dest[i*2], 3, "%02x", source[i]);
  189. }
  190. CURLcode Curl_output_digest(struct connectdata *conn,
  191. bool proxy,
  192. unsigned char *request,
  193. unsigned char *uripath)
  194. {
  195. /* We have a Digest setup for this, use it! Now, to get all the details for
  196. this sorted out, I must urge you dear friend to read up on the RFC2617
  197. section 3.2.2, */
  198. unsigned char md5buf[16]; /* 16 bytes/128 bits */
  199. unsigned char request_digest[33];
  200. unsigned char *md5this;
  201. unsigned char *ha1;
  202. unsigned char ha2[33];/* 32 digits and 1 zero byte */
  203. char cnoncebuf[7];
  204. char *cnonce;
  205. char *tmp = NULL;
  206. struct timeval now;
  207. char **allocuserpwd;
  208. char *userp;
  209. char *passwdp;
  210. struct auth *authp;
  211. struct SessionHandle *data = conn->data;
  212. struct digestdata *d;
  213. if(proxy) {
  214. d = &data->state.proxydigest;
  215. allocuserpwd = &conn->allocptr.proxyuserpwd;
  216. userp = conn->proxyuser;
  217. passwdp = conn->proxypasswd;
  218. authp = &data->state.authproxy;
  219. }
  220. else {
  221. d = &data->state.digest;
  222. allocuserpwd = &conn->allocptr.userpwd;
  223. userp = conn->user;
  224. passwdp = conn->passwd;
  225. authp = &data->state.authhost;
  226. }
  227. /* not set means empty */
  228. if(!userp)
  229. userp=(char *)"";
  230. if(!passwdp)
  231. passwdp=(char *)"";
  232. if(!d->nonce) {
  233. authp->done = FALSE;
  234. return CURLE_OK;
  235. }
  236. authp->done = TRUE;
  237. if(!d->nc)
  238. d->nc = 1;
  239. if(!d->cnonce) {
  240. /* Generate a cnonce */
  241. now = Curl_tvnow();
  242. snprintf(cnoncebuf, sizeof(cnoncebuf), "%06ld", now.tv_sec);
  243. if(Curl_base64_encode(cnoncebuf, strlen(cnoncebuf), &cnonce))
  244. d->cnonce = cnonce;
  245. else
  246. return CURLE_OUT_OF_MEMORY;
  247. }
  248. /*
  249. if the algorithm is "MD5" or unspecified (which then defaults to MD5):
  250. A1 = unq(username-value) ":" unq(realm-value) ":" passwd
  251. if the algorithm is "MD5-sess" then:
  252. A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
  253. ":" unq(nonce-value) ":" unq(cnonce-value)
  254. */
  255. md5this = (unsigned char *)
  256. aprintf("%s:%s:%s", userp, d->realm, passwdp);
  257. if(!md5this)
  258. return CURLE_OUT_OF_MEMORY;
  259. Curl_md5it(md5buf, md5this);
  260. free(md5this); /* free this again */
  261. ha1 = (unsigned char *)malloc(33); /* 32 digits and 1 zero byte */
  262. if(!ha1)
  263. return CURLE_OUT_OF_MEMORY;
  264. md5_to_ascii(md5buf, ha1);
  265. if(d->algo == CURLDIGESTALGO_MD5SESS) {
  266. /* nonce and cnonce are OUTSIDE the hash */
  267. tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce);
  268. free(ha1);
  269. if(!tmp)
  270. return CURLE_OUT_OF_MEMORY;
  271. ha1 = (unsigned char *)tmp;
  272. }
  273. /*
  274. If the "qop" directive's value is "auth" or is unspecified, then A2 is:
  275. A2 = Method ":" digest-uri-value
  276. If the "qop" value is "auth-int", then A2 is:
  277. A2 = Method ":" digest-uri-value ":" H(entity-body)
  278. (The "Method" value is the HTTP request method as specified in section
  279. 5.1.1 of RFC 2616)
  280. */
  281. md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
  282. if(!md5this) {
  283. free(ha1);
  284. return CURLE_OUT_OF_MEMORY;
  285. }
  286. if (d->qop && strequal(d->qop, "auth-int")) {
  287. /* We don't support auth-int at the moment. I can't see a easy way to get
  288. entity-body here */
  289. /* TODO: Append H(entity-body)*/
  290. }
  291. Curl_md5it(md5buf, md5this);
  292. free(md5this); /* free this again */
  293. md5_to_ascii(md5buf, ha2);
  294. if (d->qop) {
  295. md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
  296. ha1,
  297. d->nonce,
  298. d->nc,
  299. d->cnonce,
  300. d->qop,
  301. ha2);
  302. }
  303. else {
  304. md5this = (unsigned char *)aprintf("%s:%s:%s",
  305. ha1,
  306. d->nonce,
  307. ha2);
  308. }
  309. free(ha1);
  310. if(!md5this)
  311. return CURLE_OUT_OF_MEMORY;
  312. Curl_md5it(md5buf, md5this);
  313. free(md5this); /* free this again */
  314. md5_to_ascii(md5buf, request_digest);
  315. /* for test case 64 (snooped from a Mozilla 1.3a request)
  316. Authorization: Digest username="testuser", realm="testrealm", \
  317. nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
  318. */
  319. Curl_safefree(*allocuserpwd);
  320. if (d->qop) {
  321. *allocuserpwd =
  322. aprintf( "%sAuthorization: Digest "
  323. "username=\"%s\", "
  324. "realm=\"%s\", "
  325. "nonce=\"%s\", "
  326. "uri=\"%s\", "
  327. "cnonce=\"%s\", "
  328. "nc=%08x, "
  329. "qop=\"%s\", "
  330. "response=\"%s\"",
  331. proxy?"Proxy-":"",
  332. userp,
  333. d->realm,
  334. d->nonce,
  335. uripath, /* this is the PATH part of the URL */
  336. d->cnonce,
  337. d->nc,
  338. d->qop,
  339. request_digest);
  340. if(strequal(d->qop, "auth"))
  341. d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded
  342. which tells to the server how many times you are using the
  343. same nonce in the qop=auth mode. */
  344. }
  345. else {
  346. *allocuserpwd =
  347. aprintf( "%sAuthorization: Digest "
  348. "username=\"%s\", "
  349. "realm=\"%s\", "
  350. "nonce=\"%s\", "
  351. "uri=\"%s\", "
  352. "response=\"%s\"",
  353. proxy?"Proxy-":"",
  354. userp,
  355. d->realm,
  356. d->nonce,
  357. uripath, /* this is the PATH part of the URL */
  358. request_digest);
  359. }
  360. if(!*allocuserpwd)
  361. return CURLE_OUT_OF_MEMORY;
  362. /* Add optional fields */
  363. if(d->opaque) {
  364. /* append opaque */
  365. tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque);
  366. if(!tmp)
  367. return CURLE_OUT_OF_MEMORY;
  368. free(*allocuserpwd);
  369. *allocuserpwd = tmp;
  370. }
  371. if(d->algorithm) {
  372. /* append algorithm */
  373. tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm);
  374. if(!tmp)
  375. return CURLE_OUT_OF_MEMORY;
  376. free(*allocuserpwd);
  377. *allocuserpwd = tmp;
  378. }
  379. /* append CRLF to the userpwd header */
  380. tmp = (char*) realloc(*allocuserpwd, strlen(*allocuserpwd) + 3 + 1);
  381. if(!tmp)
  382. return CURLE_OUT_OF_MEMORY;
  383. strcat(tmp, "\r\n");
  384. *allocuserpwd = tmp;
  385. return CURLE_OK;
  386. }
  387. void Curl_digest_cleanup_one(struct digestdata *d)
  388. {
  389. if(d->nonce)
  390. free(d->nonce);
  391. d->nonce = NULL;
  392. if(d->cnonce)
  393. free(d->cnonce);
  394. d->cnonce = NULL;
  395. if(d->realm)
  396. free(d->realm);
  397. d->realm = NULL;
  398. if(d->opaque)
  399. free(d->opaque);
  400. d->opaque = NULL;
  401. if(d->qop)
  402. free(d->qop);
  403. d->qop = NULL;
  404. if(d->algorithm)
  405. free(d->algorithm);
  406. d->algorithm = NULL;
  407. d->nc = 0;
  408. d->algo = CURLDIGESTALGO_MD5; /* default algorithm */
  409. d->stale = FALSE; /* default means normal, not stale */
  410. }
  411. void Curl_digest_cleanup(struct SessionHandle *data)
  412. {
  413. Curl_digest_cleanup_one(&data->state.digest);
  414. Curl_digest_cleanup_one(&data->state.proxydigest);
  415. }
  416. #endif