http_digest.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) 1998 - 2008, 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 "curl_base64.h"
  35. #include "curl_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. const 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[256];
  80. char content[1024];
  81. size_t totlen=0;
  82. while(*header && ISSPACE(*header))
  83. header++;
  84. /* how big can these strings be? */
  85. if((2 == sscanf(header, "%255[^=]=\"%1023[^\"]\"",
  86. value, content)) ||
  87. /* try the same scan but without quotes around the content but don't
  88. include the possibly trailing comma, newline or carriage return */
  89. (2 == sscanf(header, "%255[^=]=%1023[^\r\n,]",
  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. /* pass all additional spaces here */
  166. while(*header && ISSPACE(*header))
  167. header++;
  168. if(',' == *header)
  169. /* allow the list to be comma-separated */
  170. header++;
  171. }
  172. /* We had a nonce since before, and we got another one now without
  173. 'stale=true'. This means we provided bad credentials in the previous
  174. request */
  175. if(before && !d->stale)
  176. return CURLDIGEST_BAD;
  177. /* We got this header without a nonce, that's a bad Digest line! */
  178. if(!d->nonce)
  179. return CURLDIGEST_BAD;
  180. }
  181. else
  182. /* else not a digest, get out */
  183. return CURLDIGEST_NONE;
  184. return CURLDIGEST_FINE;
  185. }
  186. /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
  187. static void md5_to_ascii(unsigned char *source, /* 16 bytes */
  188. unsigned char *dest) /* 33 bytes */
  189. {
  190. int i;
  191. for(i=0; i<16; i++)
  192. snprintf((char *)&dest[i*2], 3, "%02x", source[i]);
  193. }
  194. CURLcode Curl_output_digest(struct connectdata *conn,
  195. bool proxy,
  196. const unsigned char *request,
  197. const unsigned char *uripath)
  198. {
  199. /* We have a Digest setup for this, use it! Now, to get all the details for
  200. this sorted out, I must urge you dear friend to read up on the RFC2617
  201. section 3.2.2, */
  202. unsigned char md5buf[16]; /* 16 bytes/128 bits */
  203. unsigned char request_digest[33];
  204. unsigned char *md5this;
  205. unsigned char *ha1;
  206. unsigned char ha2[33];/* 32 digits and 1 zero byte */
  207. char cnoncebuf[7];
  208. char *cnonce;
  209. char *tmp = NULL;
  210. struct timeval now;
  211. char **allocuserpwd;
  212. char *userp;
  213. char *passwdp;
  214. struct auth *authp;
  215. struct SessionHandle *data = conn->data;
  216. struct digestdata *d;
  217. #ifdef CURL_DOES_CONVERSIONS
  218. CURLcode rc;
  219. /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
  220. It converts digest text to ASCII so the MD5 will be correct for
  221. what ultimately goes over the network.
  222. */
  223. #define CURL_OUTPUT_DIGEST_CONV(a, b) \
  224. rc = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
  225. if(rc != CURLE_OK) { \
  226. free(b); \
  227. return rc; \
  228. }
  229. #else
  230. #define CURL_OUTPUT_DIGEST_CONV(a, b)
  231. #endif /* CURL_DOES_CONVERSIONS */
  232. if(proxy) {
  233. d = &data->state.proxydigest;
  234. allocuserpwd = &conn->allocptr.proxyuserpwd;
  235. userp = conn->proxyuser;
  236. passwdp = conn->proxypasswd;
  237. authp = &data->state.authproxy;
  238. }
  239. else {
  240. d = &data->state.digest;
  241. allocuserpwd = &conn->allocptr.userpwd;
  242. userp = conn->user;
  243. passwdp = conn->passwd;
  244. authp = &data->state.authhost;
  245. }
  246. if(*allocuserpwd) {
  247. Curl_safefree(*allocuserpwd);
  248. *allocuserpwd = NULL;
  249. }
  250. /* not set means empty */
  251. if(!userp)
  252. userp=(char *)"";
  253. if(!passwdp)
  254. passwdp=(char *)"";
  255. if(!d->nonce) {
  256. authp->done = FALSE;
  257. return CURLE_OK;
  258. }
  259. authp->done = TRUE;
  260. if(!d->nc)
  261. d->nc = 1;
  262. if(!d->cnonce) {
  263. /* Generate a cnonce */
  264. now = Curl_tvnow();
  265. snprintf(cnoncebuf, sizeof(cnoncebuf), "%06ld", now.tv_sec);
  266. if(Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), &cnonce))
  267. d->cnonce = cnonce;
  268. else
  269. return CURLE_OUT_OF_MEMORY;
  270. }
  271. /*
  272. if the algorithm is "MD5" or unspecified (which then defaults to MD5):
  273. A1 = unq(username-value) ":" unq(realm-value) ":" passwd
  274. if the algorithm is "MD5-sess" then:
  275. A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
  276. ":" unq(nonce-value) ":" unq(cnonce-value)
  277. */
  278. md5this = (unsigned char *)
  279. aprintf("%s:%s:%s", userp, d->realm, passwdp);
  280. if(!md5this)
  281. return CURLE_OUT_OF_MEMORY;
  282. CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
  283. Curl_md5it(md5buf, md5this);
  284. free(md5this); /* free this again */
  285. ha1 = (unsigned char *)malloc(33); /* 32 digits and 1 zero byte */
  286. if(!ha1)
  287. return CURLE_OUT_OF_MEMORY;
  288. md5_to_ascii(md5buf, ha1);
  289. if(d->algo == CURLDIGESTALGO_MD5SESS) {
  290. /* nonce and cnonce are OUTSIDE the hash */
  291. tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce);
  292. if(!tmp)
  293. return CURLE_OUT_OF_MEMORY;
  294. CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */
  295. Curl_md5it(md5buf, (unsigned char *)tmp);
  296. free(tmp); /* free this again */
  297. md5_to_ascii(md5buf, ha1);
  298. }
  299. /*
  300. If the "qop" directive's value is "auth" or is unspecified, then A2 is:
  301. A2 = Method ":" digest-uri-value
  302. If the "qop" value is "auth-int", then A2 is:
  303. A2 = Method ":" digest-uri-value ":" H(entity-body)
  304. (The "Method" value is the HTTP request method as specified in section
  305. 5.1.1 of RFC 2616)
  306. */
  307. md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
  308. if(!md5this) {
  309. free(ha1);
  310. return CURLE_OUT_OF_MEMORY;
  311. }
  312. if(d->qop && strequal(d->qop, "auth-int")) {
  313. /* We don't support auth-int at the moment. I can't see a easy way to get
  314. entity-body here */
  315. /* TODO: Append H(entity-body)*/
  316. }
  317. CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
  318. Curl_md5it(md5buf, md5this);
  319. free(md5this); /* free this again */
  320. md5_to_ascii(md5buf, ha2);
  321. if(d->qop) {
  322. md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
  323. ha1,
  324. d->nonce,
  325. d->nc,
  326. d->cnonce,
  327. d->qop,
  328. ha2);
  329. }
  330. else {
  331. md5this = (unsigned char *)aprintf("%s:%s:%s",
  332. ha1,
  333. d->nonce,
  334. ha2);
  335. }
  336. free(ha1);
  337. if(!md5this)
  338. return CURLE_OUT_OF_MEMORY;
  339. CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
  340. Curl_md5it(md5buf, md5this);
  341. free(md5this); /* free this again */
  342. md5_to_ascii(md5buf, request_digest);
  343. /* for test case 64 (snooped from a Mozilla 1.3a request)
  344. Authorization: Digest username="testuser", realm="testrealm", \
  345. nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
  346. */
  347. if(d->qop) {
  348. *allocuserpwd =
  349. aprintf( "%sAuthorization: Digest "
  350. "username=\"%s\", "
  351. "realm=\"%s\", "
  352. "nonce=\"%s\", "
  353. "uri=\"%s\", "
  354. "cnonce=\"%s\", "
  355. "nc=%08x, "
  356. "qop=\"%s\", "
  357. "response=\"%s\"",
  358. proxy?"Proxy-":"",
  359. userp,
  360. d->realm,
  361. d->nonce,
  362. uripath, /* this is the PATH part of the URL */
  363. d->cnonce,
  364. d->nc,
  365. d->qop,
  366. request_digest);
  367. if(strequal(d->qop, "auth"))
  368. d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded
  369. which tells to the server how many times you are using the
  370. same nonce in the qop=auth mode. */
  371. }
  372. else {
  373. *allocuserpwd =
  374. aprintf( "%sAuthorization: Digest "
  375. "username=\"%s\", "
  376. "realm=\"%s\", "
  377. "nonce=\"%s\", "
  378. "uri=\"%s\", "
  379. "response=\"%s\"",
  380. proxy?"Proxy-":"",
  381. userp,
  382. d->realm,
  383. d->nonce,
  384. uripath, /* this is the PATH part of the URL */
  385. request_digest);
  386. }
  387. if(!*allocuserpwd)
  388. return CURLE_OUT_OF_MEMORY;
  389. /* Add optional fields */
  390. if(d->opaque) {
  391. /* append opaque */
  392. tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque);
  393. if(!tmp)
  394. return CURLE_OUT_OF_MEMORY;
  395. free(*allocuserpwd);
  396. *allocuserpwd = tmp;
  397. }
  398. if(d->algorithm) {
  399. /* append algorithm */
  400. tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm);
  401. if(!tmp)
  402. return CURLE_OUT_OF_MEMORY;
  403. free(*allocuserpwd);
  404. *allocuserpwd = tmp;
  405. }
  406. /* append CRLF to the userpwd header */
  407. tmp = (char*) realloc(*allocuserpwd, strlen(*allocuserpwd) + 3 + 1);
  408. if(!tmp)
  409. return CURLE_OUT_OF_MEMORY;
  410. strcat(tmp, "\r\n");
  411. *allocuserpwd = tmp;
  412. return CURLE_OK;
  413. }
  414. void Curl_digest_cleanup_one(struct digestdata *d)
  415. {
  416. if(d->nonce)
  417. free(d->nonce);
  418. d->nonce = NULL;
  419. if(d->cnonce)
  420. free(d->cnonce);
  421. d->cnonce = NULL;
  422. if(d->realm)
  423. free(d->realm);
  424. d->realm = NULL;
  425. if(d->opaque)
  426. free(d->opaque);
  427. d->opaque = NULL;
  428. if(d->qop)
  429. free(d->qop);
  430. d->qop = NULL;
  431. if(d->algorithm)
  432. free(d->algorithm);
  433. d->algorithm = NULL;
  434. d->nc = 0;
  435. d->algo = CURLDIGESTALGO_MD5; /* default algorithm */
  436. d->stale = FALSE; /* default means normal, not stale */
  437. }
  438. void Curl_digest_cleanup(struct SessionHandle *data)
  439. {
  440. Curl_digest_cleanup_one(&data->state.digest);
  441. Curl_digest_cleanup_one(&data->state.proxydigest);
  442. }
  443. #endif