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