curl_sasl.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) 2012, 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. * RFC2195 CRAM-MD5 authentication
  22. * RFC2831 DIGEST-MD5 authentication
  23. * RFC4616 PLAIN authentication
  24. *
  25. ***************************************************************************/
  26. #include "setup.h"
  27. #include <curl/curl.h>
  28. #include "urldata.h"
  29. #include "curl_base64.h"
  30. #include "curl_md5.h"
  31. #include "curl_rand.h"
  32. #include "curl_hmac.h"
  33. #include "curl_ntlm_msgs.h"
  34. #include "curl_sasl.h"
  35. #include "warnless.h"
  36. #include "curl_memory.h"
  37. #define _MPRINTF_REPLACE /* use our functions only */
  38. #include <curl/mprintf.h>
  39. /* The last #include file should be: */
  40. #include "memdebug.h"
  41. #ifndef CURL_DISABLE_CRYPTO_AUTH
  42. /* Retrieves the value for a corresponding key from the challenge string
  43. * returns TRUE if the key could be found, FALSE if it does not exists
  44. */
  45. static bool sasl_digest_get_key_value(const unsigned char *chlg,
  46. const char *key,
  47. char *value,
  48. size_t max_val_len,
  49. char end_char)
  50. {
  51. char *find_pos;
  52. size_t i;
  53. find_pos = strstr((const char *) chlg, key);
  54. if(!find_pos)
  55. return FALSE;
  56. find_pos += strlen(key);
  57. for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
  58. value[i] = *find_pos++;
  59. value[i] = '\0';
  60. return TRUE;
  61. }
  62. #endif
  63. /*
  64. * Curl_sasl_create_plain_message()
  65. *
  66. * This is used to generate an already encoded PLAIN message ready
  67. * for sending to the recipient.
  68. *
  69. * Parameters:
  70. *
  71. * data [in] - The session handle.
  72. * userp [in] - The user name.
  73. * passdwp [in] - The user's password.
  74. * outptr [in/out] - The address where a pointer to newly allocated memory
  75. * holding the result will be stored upon completion.
  76. * outlen [out] - The length of the output message.
  77. *
  78. * Returns CURLE_OK on success.
  79. */
  80. CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data,
  81. const char* userp,
  82. const char* passwdp,
  83. char **outptr, size_t *outlen)
  84. {
  85. char plainauth[2 * MAX_CURL_USER_LENGTH + MAX_CURL_PASSWORD_LENGTH];
  86. size_t ulen;
  87. size_t plen;
  88. ulen = strlen(userp);
  89. plen = strlen(passwdp);
  90. if(2 * ulen + plen + 2 > sizeof(plainauth)) {
  91. *outlen = 0;
  92. *outptr = NULL;
  93. /* Plainauth too small */
  94. return CURLE_OUT_OF_MEMORY;
  95. }
  96. /* Calculate the reply */
  97. memcpy(plainauth, userp, ulen);
  98. plainauth[ulen] = '\0';
  99. memcpy(plainauth + ulen + 1, userp, ulen);
  100. plainauth[2 * ulen + 1] = '\0';
  101. memcpy(plainauth + 2 * ulen + 2, passwdp, plen);
  102. /* Base64 encode the reply */
  103. return Curl_base64_encode(data, plainauth, 2 * ulen + plen + 2, outptr,
  104. outlen);
  105. }
  106. /*
  107. * Curl_sasl_create_login_message()
  108. *
  109. * This is used to generate an already encoded LOGIN message containing the
  110. * user name or password ready for sending to the recipient.
  111. *
  112. * Parameters:
  113. *
  114. * data [in] - The session handle.
  115. * valuep [in] - The user name or user's password.
  116. * outptr [in/out] - The address where a pointer to newly allocated memory
  117. * holding the result will be stored upon completion.
  118. * outlen [out] - The length of the output message.
  119. *
  120. * Returns CURLE_OK on success.
  121. */
  122. CURLcode Curl_sasl_create_login_message(struct SessionHandle *data,
  123. const char* valuep, char **outptr,
  124. size_t *outlen)
  125. {
  126. size_t vlen = strlen(valuep);
  127. if(!vlen) {
  128. /* Calculate an empty reply */
  129. *outptr = strdup("=");
  130. if(*outptr) {
  131. *outlen = (size_t) 1;
  132. return CURLE_OK;
  133. }
  134. *outlen = 0;
  135. return CURLE_OUT_OF_MEMORY;
  136. }
  137. /* Base64 encode the value */
  138. return Curl_base64_encode(data, valuep, vlen, outptr, outlen);
  139. }
  140. #ifndef CURL_DISABLE_CRYPTO_AUTH
  141. /*
  142. * Curl_sasl_create_cram_md5_message()
  143. *
  144. * This is used to generate an already encoded CRAM-MD5 response message ready
  145. * for sending to the recipient.
  146. *
  147. * Parameters:
  148. *
  149. * data [in] - The session handle.
  150. * chlg64 [in] - Pointer to the base64 encoded challenge buffer.
  151. * userp [in] - The user name.
  152. * passdwp [in] - The user's password.
  153. * outptr [in/out] - The address where a pointer to newly allocated memory
  154. * holding the result will be stored upon completion.
  155. * outlen [out] - The length of the output message.
  156. *
  157. * Returns CURLE_OK on success.
  158. */
  159. CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data,
  160. const char* chlg64,
  161. const char* userp,
  162. const char* passwdp,
  163. char **outptr, size_t *outlen)
  164. {
  165. CURLcode result = CURLE_OK;
  166. size_t chlg64len = strlen(chlg64);
  167. unsigned char *chlg = (unsigned char *) NULL;
  168. size_t chlglen = 0;
  169. HMAC_context *ctxt;
  170. unsigned char digest[MD5_DIGEST_LEN];
  171. char response[MAX_CURL_USER_LENGTH + 2 * MD5_DIGEST_LEN + 1];
  172. /* Decode the challenge if necessary */
  173. if(chlg64len && *chlg64 != '=') {
  174. result = Curl_base64_decode(chlg64, &chlg, &chlglen);
  175. if(result)
  176. return result;
  177. }
  178. /* Compute the digest using the password as the key */
  179. ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
  180. (const unsigned char *) passwdp,
  181. curlx_uztoui(strlen(passwdp)));
  182. if(!ctxt) {
  183. Curl_safefree(chlg);
  184. return CURLE_OUT_OF_MEMORY;
  185. }
  186. /* Update the digest with the given challenge */
  187. if(chlglen > 0)
  188. Curl_HMAC_update(ctxt, chlg, curlx_uztoui(chlglen));
  189. Curl_safefree(chlg);
  190. /* Finalise the digest */
  191. Curl_HMAC_final(ctxt, digest);
  192. /* Prepare the response */
  193. snprintf(response, sizeof(response),
  194. "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
  195. userp, digest[0], digest[1], digest[2], digest[3], digest[4],
  196. digest[5], digest[6], digest[7], digest[8], digest[9], digest[10],
  197. digest[11], digest[12], digest[13], digest[14], digest[15]);
  198. /* Base64 encode the reply */
  199. return Curl_base64_encode(data, response, 0, outptr, outlen);
  200. }
  201. /*
  202. * Curl_sasl_create_digest_md5_message()
  203. *
  204. * This is used to generate an already encoded DIGEST-MD5 response message
  205. * ready for sending to the recipient.
  206. *
  207. * Parameters:
  208. *
  209. * data [in] - The session handle.
  210. * chlg64 [in] - Pointer to the base64 encoded challenge buffer.
  211. * userp [in] - The user name.
  212. * passdwp [in] - The user's password.
  213. * service [in] - The service type such as www, smtp or pop
  214. * outptr [in/out] - The address where a pointer to newly allocated memory
  215. * holding the result will be stored upon completion.
  216. * outlen [out] - The length of the output message.
  217. *
  218. * Returns CURLE_OK on success.
  219. */
  220. CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
  221. const char* chlg64,
  222. const char* userp,
  223. const char* passwdp,
  224. const char* service,
  225. char **outptr, size_t *outlen)
  226. {
  227. static const char table16[] = "0123456789abcdef";
  228. CURLcode result = CURLE_OK;
  229. unsigned char *chlg = (unsigned char *) NULL;
  230. size_t chlglen = 0;
  231. size_t i;
  232. MD5_context *ctxt;
  233. unsigned char digest[MD5_DIGEST_LEN];
  234. char HA1_hex[2 * MD5_DIGEST_LEN + 1];
  235. char HA2_hex[2 * MD5_DIGEST_LEN + 1];
  236. char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
  237. char nonce[64];
  238. char realm[128];
  239. char alg[64];
  240. char nonceCount[] = "00000001";
  241. char cnonce[] = "12345678"; /* will be changed */
  242. char method[] = "AUTHENTICATE";
  243. char qop[] = "auth";
  244. char uri[128];
  245. char response[512];
  246. result = Curl_base64_decode(chlg64, &chlg, &chlglen);
  247. if(result)
  248. return result;
  249. /* Retrieve nonce string from the challenge */
  250. if(!sasl_digest_get_key_value(chlg, "nonce=\"", nonce,
  251. sizeof(nonce), '\"')) {
  252. Curl_safefree(chlg);
  253. return CURLE_LOGIN_DENIED;
  254. }
  255. /* Retrieve realm string from the challenge */
  256. if(!sasl_digest_get_key_value(chlg, "realm=\"", realm,
  257. sizeof(realm), '\"')) {
  258. /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
  259. strcpy(realm, "");
  260. }
  261. /* Retrieve algorithm string from the challenge */
  262. if(!sasl_digest_get_key_value(chlg, "algorithm=", alg, sizeof(alg), ',')) {
  263. Curl_safefree(chlg);
  264. return CURLE_LOGIN_DENIED;
  265. }
  266. Curl_safefree(chlg);
  267. /* We do not support other algorithms */
  268. if(strcmp(alg, "md5-sess") != 0)
  269. return CURLE_LOGIN_DENIED;
  270. /* Generate 64 bits of random data */
  271. for(i = 0; i < 8; i++)
  272. cnonce[i] = table16[Curl_rand()%16];
  273. /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
  274. ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
  275. if(!ctxt)
  276. return CURLE_OUT_OF_MEMORY;
  277. Curl_MD5_update(ctxt, (const unsigned char *) userp,
  278. curlx_uztoui(strlen(userp)));
  279. Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
  280. Curl_MD5_update(ctxt, (const unsigned char *) realm,
  281. curlx_uztoui(strlen(realm)));
  282. Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
  283. Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
  284. curlx_uztoui(strlen(passwdp)));
  285. Curl_MD5_final(ctxt, digest);
  286. ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
  287. if(!ctxt)
  288. return CURLE_OUT_OF_MEMORY;
  289. Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
  290. Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
  291. Curl_MD5_update(ctxt, (const unsigned char *) nonce,
  292. curlx_uztoui(strlen(nonce)));
  293. Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
  294. Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
  295. curlx_uztoui(strlen(cnonce)));
  296. Curl_MD5_final(ctxt, digest);
  297. /* Convert calculated 16 octet hex into 32 bytes string */
  298. for(i = 0; i < MD5_DIGEST_LEN; i++)
  299. snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
  300. /* Prepare the URL string */
  301. strcpy(uri, service);
  302. strcat(uri, "/");
  303. strcat(uri, realm);
  304. /* Calculate H(A2) */
  305. ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
  306. if(!ctxt)
  307. return CURLE_OUT_OF_MEMORY;
  308. Curl_MD5_update(ctxt, (const unsigned char *) method,
  309. curlx_uztoui(strlen(method)));
  310. Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
  311. Curl_MD5_update(ctxt, (const unsigned char *) uri,
  312. curlx_uztoui(strlen(uri)));
  313. Curl_MD5_final(ctxt, digest);
  314. for(i = 0; i < MD5_DIGEST_LEN; i++)
  315. snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
  316. /* Now calculate the response hash */
  317. ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
  318. if(!ctxt)
  319. return CURLE_OUT_OF_MEMORY;
  320. Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
  321. Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
  322. Curl_MD5_update(ctxt, (const unsigned char *) nonce,
  323. curlx_uztoui(strlen(nonce)));
  324. Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
  325. Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
  326. curlx_uztoui(strlen(nonceCount)));
  327. Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
  328. Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
  329. curlx_uztoui(strlen(cnonce)));
  330. Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
  331. Curl_MD5_update(ctxt, (const unsigned char *) qop,
  332. curlx_uztoui(strlen(qop)));
  333. Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
  334. Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
  335. Curl_MD5_final(ctxt, digest);
  336. for(i = 0; i < MD5_DIGEST_LEN; i++)
  337. snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
  338. strcpy(response, "username=\"");
  339. strcat(response, userp);
  340. strcat(response, "\",realm=\"");
  341. strcat(response, realm);
  342. strcat(response, "\",nonce=\"");
  343. strcat(response, nonce);
  344. strcat(response, "\",cnonce=\"");
  345. strcat(response, cnonce);
  346. strcat(response, "\",nc=");
  347. strcat(response, nonceCount);
  348. strcat(response, ",digest-uri=\"");
  349. strcat(response, uri);
  350. strcat(response, "\",response=");
  351. strcat(response, resp_hash_hex);
  352. /* Base64 encode the reply */
  353. return Curl_base64_encode(data, response, 0, outptr, outlen);
  354. }
  355. #endif
  356. #ifdef USE_NTLM
  357. /*
  358. * Curl_sasl_create_ntlm_type1_message()
  359. *
  360. * This is used to generate an already encoded NTLM type-1 message ready for
  361. * sending to the recipient.
  362. *
  363. * Note: This is a simple wrapper of the NTLM function which means that any
  364. * SASL based protocols don't have to include the NTLM functions directly.
  365. *
  366. * Parameters:
  367. *
  368. * userp [in] - The user name in the format User or Domain\User.
  369. * passdwp [in] - The user's password.
  370. * ntlm [in/out] - The ntlm data struct being used and modified.
  371. * outptr [in/out] - The address where a pointer to newly allocated memory
  372. * holding the result will be stored upon completion.
  373. * outlen [out] - The length of the output message.
  374. *
  375. * Returns CURLE_OK on success.
  376. */
  377. CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp,
  378. const char *passwdp,
  379. struct ntlmdata *ntlm,
  380. char **outptr, size_t *outlen)
  381. {
  382. return Curl_ntlm_create_type1_message(userp, passwdp, ntlm, outptr,
  383. outlen);
  384. }
  385. /*
  386. * Curl_sasl_create_ntlm_type3_message()
  387. *
  388. * This is used to generate an already encoded NTLM type-3 message ready for
  389. * sending to the recipient.
  390. *
  391. * Parameters:
  392. *
  393. * data [in] - Pointer to session handle.
  394. * header [in] - Pointer to the base64 encoded type-2 message buffer.
  395. * userp [in] - The user name in the format User or Domain\User.
  396. * passdwp [in] - The user's password.
  397. * ntlm [in/out] - The ntlm data struct being used and modified.
  398. * outptr [in/out] - The address where a pointer to newly allocated memory
  399. * holding the result will be stored upon completion.
  400. * outlen [out] - The length of the output message.
  401. *
  402. * Returns CURLE_OK on success.
  403. */
  404. CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data,
  405. const char *header,
  406. const char *userp,
  407. const char *passwdp,
  408. struct ntlmdata *ntlm,
  409. char **outptr, size_t *outlen)
  410. {
  411. CURLcode result = Curl_ntlm_decode_type2_message(data, header, ntlm);
  412. if(!result)
  413. result = Curl_ntlm_create_type3_message(data, userp, passwdp, ntlm,
  414. outptr, outlen);
  415. return result;
  416. }
  417. #endif /* USE_NTLM */
  418. /*
  419. * Curl_sasl_cleanup()
  420. *
  421. * This is used to cleanup any libraries or curl modules used by the sasl
  422. * functions.
  423. *
  424. * Parameters:
  425. *
  426. * conn [in] - Pointer to the connection data.
  427. * authused [in] - The authentication mechanism used.
  428. */
  429. void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
  430. {
  431. #ifdef USE_NTLM
  432. /* Cleanup the ntlm structure */
  433. if(authused == SASL_MECH_NTLM) {
  434. Curl_ntlm_sspi_cleanup(&conn->ntlm);
  435. }
  436. (void)conn;
  437. #else
  438. /* Reserved for future use */
  439. (void)conn;
  440. (void)authused;
  441. #endif
  442. }