http_digest.c 14 KB

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