curl_sasl.c 16 KB

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