sasl: Added initial stub functions for SSPI DIGEST-MD support
[platform/upstream/curl.git] / lib / curl_sasl.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2012 - 2014, 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  * RFC6749 OAuth 2.0 Authorization Framework
26  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
27  *
28  ***************************************************************************/
29
30 #include "curl_setup.h"
31
32 #include <curl/curl.h>
33 #include "urldata.h"
34
35 #include "curl_base64.h"
36 #include "curl_md5.h"
37 #include "vtls/vtls.h"
38 #include "curl_hmac.h"
39 #include "curl_ntlm_msgs.h"
40 #include "curl_sasl.h"
41 #include "warnless.h"
42 #include "curl_memory.h"
43
44 #ifdef USE_NSS
45 #include "vtls/nssg.h" /* for Curl_nss_force_init() */
46 #endif
47
48 #define _MPRINTF_REPLACE /* use our functions only */
49 #include <curl/mprintf.h>
50
51 /* The last #include file should be: */
52 #include "memdebug.h"
53
54 #ifndef CURL_DISABLE_CRYPTO_AUTH
55 /* Retrieves the value for a corresponding key from the challenge string
56  * returns TRUE if the key could be found, FALSE if it does not exists
57  */
58 static bool sasl_digest_get_key_value(const char *chlg,
59                                       const char *key,
60                                       char *value,
61                                       size_t max_val_len,
62                                       char end_char)
63 {
64   char *find_pos;
65   size_t i;
66
67   find_pos = strstr(chlg, key);
68   if(!find_pos)
69     return FALSE;
70
71   find_pos += strlen(key);
72
73   for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
74     value[i] = *find_pos++;
75   value[i] = '\0';
76
77   return TRUE;
78 }
79 #endif
80
81 /*
82  * Curl_sasl_create_plain_message()
83  *
84  * This is used to generate an already encoded PLAIN message ready
85  * for sending to the recipient.
86  *
87  * Parameters:
88  *
89  * data    [in]     - The session handle.
90  * userp   [in]     - The user name.
91  * passdwp [in]     - The user's password.
92  * outptr  [in/out] - The address where a pointer to newly allocated memory
93  *                    holding the result will be stored upon completion.
94  * outlen  [out]    - The length of the output message.
95  *
96  * Returns CURLE_OK on success.
97  */
98 CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data,
99                                         const char *userp,
100                                         const char *passwdp,
101                                         char **outptr, size_t *outlen)
102 {
103   CURLcode result;
104   char *plainauth;
105   size_t ulen;
106   size_t plen;
107
108   ulen = strlen(userp);
109   plen = strlen(passwdp);
110
111   plainauth = malloc(2 * ulen + plen + 2);
112   if(!plainauth) {
113     *outlen = 0;
114     *outptr = NULL;
115     return CURLE_OUT_OF_MEMORY;
116   }
117
118   /* Calculate the reply */
119   memcpy(plainauth, userp, ulen);
120   plainauth[ulen] = '\0';
121   memcpy(plainauth + ulen + 1, userp, ulen);
122   plainauth[2 * ulen + 1] = '\0';
123   memcpy(plainauth + 2 * ulen + 2, passwdp, plen);
124
125   /* Base64 encode the reply */
126   result = Curl_base64_encode(data, plainauth, 2 * ulen + plen + 2, outptr,
127                               outlen);
128   Curl_safefree(plainauth);
129   return result;
130 }
131
132 /*
133  * Curl_sasl_create_login_message()
134  *
135  * This is used to generate an already encoded LOGIN message containing the
136  * user name or password ready for sending to the recipient.
137  *
138  * Parameters:
139  *
140  * data    [in]     - The session handle.
141  * valuep  [in]     - The user name or user's password.
142  * outptr  [in/out] - The address where a pointer to newly allocated memory
143  *                    holding the result will be stored upon completion.
144  * outlen  [out]    - The length of the output message.
145  *
146  * Returns CURLE_OK on success.
147  */
148 CURLcode Curl_sasl_create_login_message(struct SessionHandle *data,
149                                         const char *valuep, char **outptr,
150                                         size_t *outlen)
151 {
152   size_t vlen = strlen(valuep);
153
154   if(!vlen) {
155     /* Calculate an empty reply */
156     *outptr = strdup("=");
157     if(*outptr) {
158       *outlen = (size_t) 1;
159       return CURLE_OK;
160     }
161
162     *outlen = 0;
163     return CURLE_OUT_OF_MEMORY;
164   }
165
166   /* Base64 encode the value */
167   return Curl_base64_encode(data, valuep, vlen, outptr, outlen);
168 }
169
170 #ifndef CURL_DISABLE_CRYPTO_AUTH
171  /*
172  * Curl_sasl_decode_cram_md5_message()
173  *
174  * This is used to decode an already encoded CRAM-MD5 challenge message.
175  *
176  * Parameters:
177  *
178  * chlg64  [in]     - Pointer to the base64 encoded challenge message.
179  * outptr  [in/out] - The address where a pointer to newly allocated memory
180  *                    holding the result will be stored upon completion.
181  * outlen  [out]    - The length of the output message.
182  *
183  * Returns CURLE_OK on success.
184  */
185 CURLcode Curl_sasl_decode_cram_md5_message(const char *chlg64, char **outptr,
186                                            size_t *outlen)
187 {
188   CURLcode result = CURLE_OK;
189   size_t chlg64len = strlen(chlg64);
190
191   *outptr = NULL;
192   *outlen = 0;
193
194   /* Decode the challenge if necessary */
195   if(chlg64len && *chlg64 != '=')
196     result = Curl_base64_decode(chlg64, (unsigned char **) outptr, outlen);
197
198     return result;
199  }
200
201  /*
202  * Curl_sasl_create_cram_md5_message()
203  *
204  * This is used to generate an already encoded CRAM-MD5 response message ready
205  * for sending to the recipient.
206  *
207  * Parameters:
208  *
209  * data    [in]     - The session handle.
210  * chlg    [in]     - The challenge.
211  * userp   [in]     - The user name.
212  * passdwp [in]     - The user's password.
213  * outptr  [in/out] - The address where a pointer to newly allocated memory
214  *                    holding the result will be stored upon completion.
215  * outlen  [out]    - The length of the output message.
216  *
217  * Returns CURLE_OK on success.
218  */
219 CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data,
220                                            const char *chlg,
221                                            const char *userp,
222                                            const char *passwdp,
223                                            char **outptr, size_t *outlen)
224 {
225   CURLcode result = CURLE_OK;
226   size_t chlglen = 0;
227   HMAC_context *ctxt;
228   unsigned char digest[MD5_DIGEST_LEN];
229   char *response;
230
231   if(chlg)
232     chlglen = strlen(chlg);
233
234   /* Compute the digest using the password as the key */
235   ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
236                         (const unsigned char *) passwdp,
237                         curlx_uztoui(strlen(passwdp)));
238   if(!ctxt)
239     return CURLE_OUT_OF_MEMORY;
240
241   /* Update the digest with the given challenge */
242   if(chlglen > 0)
243     Curl_HMAC_update(ctxt, (const unsigned char *) chlg,
244                      curlx_uztoui(chlglen));
245
246   /* Finalise the digest */
247   Curl_HMAC_final(ctxt, digest);
248
249   /* Generate the response */
250   response = aprintf(
251       "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
252            userp, digest[0], digest[1], digest[2], digest[3], digest[4],
253            digest[5], digest[6], digest[7], digest[8], digest[9], digest[10],
254            digest[11], digest[12], digest[13], digest[14], digest[15]);
255   if(!response)
256     return CURLE_OUT_OF_MEMORY;
257
258   /* Base64 encode the response */
259   result = Curl_base64_encode(data, response, 0, outptr, outlen);
260
261   Curl_safefree(response);
262
263   return result;
264 }
265
266 #ifndef USE_WINDOWS_SSPI
267 /*
268  * sasl_decode_digest_md5_message()
269  *
270  * This is used internally to decode an already encoded DIGEST-MD5 challenge
271  * message into the seperate attributes.
272  *
273  * Parameters:
274  *
275  * chlg64  [in]     - Pointer to the base64 encoded challenge message.
276  * nonce   [in/out] - The buffer where the nonce will be stored.
277  * nlen    [in]     - The length of the nonce buffer.
278  * realm   [in/out] - The buffer where the realm will be stored.
279  * rlen    [in]     - The length of the realm buffer.
280  * alg     [in/out] - The buffer where the algorithm will be stored.
281  * alen    [in]     - The length of the algorithm buffer.
282  *
283  * Returns CURLE_OK on success.
284  */
285 static CURLcode sasl_decode_digest_md5_message(const char *chlg64,
286                                                char *nonce, size_t nlen,
287                                                char *realm, size_t rlen,
288                                                char *alg, size_t alen)
289 {
290   CURLcode result = CURLE_OK;
291   unsigned char *chlg = NULL;
292   size_t chlglen = 0;
293   size_t chlg64len = strlen(chlg64);
294
295   if(chlg64len && *chlg64 != '=') {
296     result = Curl_base64_decode(chlg64, &chlg, &chlglen);
297     if(result)
298       return result;
299   }
300
301   /* Ensure we have a valid challenge message */
302   if(!chlg)
303     return CURLE_BAD_CONTENT_ENCODING;
304
305   /* Retrieve nonce string from the challenge */
306   if(!sasl_digest_get_key_value((char *)chlg, "nonce=\"", nonce, nlen, '\"')) {
307     Curl_safefree(chlg);
308     return CURLE_BAD_CONTENT_ENCODING;
309   }
310
311   /* Retrieve realm string from the challenge */
312   if(!sasl_digest_get_key_value((char *)chlg, "realm=\"", realm, rlen, '\"')) {
313     /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
314     strcpy(realm, "");
315   }
316
317   /* Retrieve algorithm string from the challenge */
318   if(!sasl_digest_get_key_value((char *)chlg, "algorithm=", alg, alen, ',')) {
319     Curl_safefree(chlg);
320     return CURLE_BAD_CONTENT_ENCODING;
321   }
322
323   Curl_safefree(chlg);
324
325   return CURLE_OK;
326 }
327
328 /*
329  * Curl_sasl_create_digest_md5_message()
330  *
331  * This is used to generate an already encoded DIGEST-MD5 response message
332  * ready for sending to the recipient.
333  *
334  * Parameters:
335  *
336  * data    [in]     - The session handle.
337  * chlg64  [in]     - Pointer to the base64 encoded challenge message.
338  * userp   [in]     - The user name.
339  * passdwp [in]     - The user's password.
340  * service [in]     - The service type such as www, smtp, pop or imap.
341  * outptr  [in/out] - The address where a pointer to newly allocated memory
342  *                    holding the result will be stored upon completion.
343  * outlen  [out]    - The length of the output message.
344  *
345  * Returns CURLE_OK on success.
346  */
347 CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
348                                              const char *chlg64,
349                                              const char *userp,
350                                              const char *passwdp,
351                                              const char *service,
352                                              char **outptr, size_t *outlen)
353 {
354 #ifndef DEBUGBUILD
355   static const char table16[] = "0123456789abcdef";
356 #endif
357   CURLcode result = CURLE_OK;
358   size_t i;
359   MD5_context *ctxt;
360   char *response = NULL;
361   unsigned char digest[MD5_DIGEST_LEN];
362   char HA1_hex[2 * MD5_DIGEST_LEN + 1];
363   char HA2_hex[2 * MD5_DIGEST_LEN + 1];
364   char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
365
366   char nonce[64];
367   char realm[128];
368   char algorithm[64];
369   char nonceCount[] = "00000001";
370   char cnonce[]     = "12345678"; /* will be changed */
371   char method[]     = "AUTHENTICATE";
372   char qop[]        = "auth";
373   char uri[128];
374
375   /* Decode the challange message */
376   result = sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
377                                           realm, sizeof(realm),
378                                           algorithm, sizeof(algorithm));
379   if(result)
380     return result;
381
382   /* We only support md5 sessions */
383   if(strcmp(algorithm, "md5-sess") != 0)
384      return CURLE_BAD_CONTENT_ENCODING;
385
386 #ifndef DEBUGBUILD
387   /* Generate 64 bits of random data */
388   for(i = 0; i < 8; i++)
389     cnonce[i] = table16[Curl_rand(data)%16];
390 #endif
391
392   /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
393   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
394   if(!ctxt)
395     return CURLE_OUT_OF_MEMORY;
396
397   Curl_MD5_update(ctxt, (const unsigned char *) userp,
398                   curlx_uztoui(strlen(userp)));
399   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
400   Curl_MD5_update(ctxt, (const unsigned char *) realm,
401                   curlx_uztoui(strlen(realm)));
402   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
403   Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
404                   curlx_uztoui(strlen(passwdp)));
405   Curl_MD5_final(ctxt, digest);
406
407   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
408   if(!ctxt)
409     return CURLE_OUT_OF_MEMORY;
410
411   Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
412   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
413   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
414                   curlx_uztoui(strlen(nonce)));
415   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
416   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
417                   curlx_uztoui(strlen(cnonce)));
418   Curl_MD5_final(ctxt, digest);
419
420   /* Convert calculated 16 octet hex into 32 bytes string */
421   for(i = 0; i < MD5_DIGEST_LEN; i++)
422     snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
423
424   /* Prepare the URL string */
425   snprintf(uri, sizeof(uri), "%s/%s", service, realm);
426
427   /* Calculate H(A2) */
428   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
429   if(!ctxt)
430     return CURLE_OUT_OF_MEMORY;
431
432   Curl_MD5_update(ctxt, (const unsigned char *) method,
433                   curlx_uztoui(strlen(method)));
434   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
435   Curl_MD5_update(ctxt, (const unsigned char *) uri,
436                   curlx_uztoui(strlen(uri)));
437   Curl_MD5_final(ctxt, digest);
438
439   for(i = 0; i < MD5_DIGEST_LEN; i++)
440     snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
441
442   /* Now calculate the response hash */
443   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
444   if(!ctxt)
445     return CURLE_OUT_OF_MEMORY;
446
447   Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
448   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
449   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
450                   curlx_uztoui(strlen(nonce)));
451   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
452
453   Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
454                   curlx_uztoui(strlen(nonceCount)));
455   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
456   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
457                   curlx_uztoui(strlen(cnonce)));
458   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
459   Curl_MD5_update(ctxt, (const unsigned char *) qop,
460                   curlx_uztoui(strlen(qop)));
461   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
462
463   Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
464   Curl_MD5_final(ctxt, digest);
465
466   for(i = 0; i < MD5_DIGEST_LEN; i++)
467     snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
468
469   /* Generate the response */
470   response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
471                      "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s",
472                      userp, realm, nonce,
473                      cnonce, nonceCount, uri, resp_hash_hex);
474   if(!response)
475     return CURLE_OUT_OF_MEMORY;
476
477   /* Base64 encode the response */
478   result = Curl_base64_encode(data, response, 0, outptr, outlen);
479
480   Curl_safefree(response);
481
482   return result;
483 }
484 #endif  /* USE_WINDOWS_SSPI */
485
486 #endif  /* CURL_DISABLE_CRYPTO_AUTH */
487
488 #ifdef USE_NTLM
489 /*
490  * Curl_sasl_create_ntlm_type1_message()
491  *
492  * This is used to generate an already encoded NTLM type-1 message ready for
493  * sending to the recipient.
494  *
495  * Note: This is a simple wrapper of the NTLM function which means that any
496  * SASL based protocols don't have to include the NTLM functions directly.
497  *
498  * Parameters:
499  *
500  * userp   [in]     - The user name in the format User or Domain\User.
501  * passdwp [in]     - The user's password.
502  * ntlm    [in/out] - The ntlm data struct being used and modified.
503  * outptr  [in/out] - The address where a pointer to newly allocated memory
504  *                    holding the result will be stored upon completion.
505  * outlen  [out]    - The length of the output message.
506  *
507  * Returns CURLE_OK on success.
508  */
509 CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp,
510                                              const char *passwdp,
511                                              struct ntlmdata *ntlm,
512                                              char **outptr, size_t *outlen)
513 {
514   return Curl_ntlm_create_type1_message(userp, passwdp, ntlm, outptr, outlen);
515 }
516
517 /*
518  * Curl_sasl_decode_ntlm_type2_message()
519  *
520  * This is used to decode an already encoded NTLM type-2 message.
521  *
522  * Parameters:
523  *
524  * data     [in]     - Pointer to session handle.
525  * type2msg [in]     - Pointer to the base64 encoded type-2 message.
526  * ntlm     [in/out] - The ntlm data struct being used and modified.
527  *
528  * Returns CURLE_OK on success.
529  */
530 CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data,
531                                              const char *type2msg,
532                                              struct ntlmdata *ntlm)
533 {
534 #ifdef USE_NSS
535   CURLcode result;
536
537   /* make sure the crypto backend is initialized */
538   result = Curl_nss_force_init(data);
539   if(result)
540     return result;
541 #endif
542
543   return Curl_ntlm_decode_type2_message(data, type2msg, ntlm);
544 }
545
546 /*
547  * Curl_sasl_create_ntlm_type3_message()
548  *
549  * This is used to generate an already encoded NTLM type-3 message ready for
550  * sending to the recipient.
551  *
552  * Parameters:
553  *
554  * data    [in]     - Pointer to session handle.
555  * userp   [in]     - The user name in the format User or Domain\User.
556  * passdwp [in]     - The user's password.
557  * ntlm    [in/out] - The ntlm data struct being used and modified.
558  * outptr  [in/out] - The address where a pointer to newly allocated memory
559  *                    holding the result will be stored upon completion.
560  * outlen  [out]    - The length of the output message.
561  *
562  * Returns CURLE_OK on success.
563  */
564 CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data,
565                                              const char *userp,
566                                              const char *passwdp,
567                                              struct ntlmdata *ntlm,
568                                              char **outptr, size_t *outlen)
569 {
570   return Curl_ntlm_create_type3_message(data, userp, passwdp, ntlm, outptr,
571                                         outlen);
572 }
573 #endif /* USE_NTLM */
574
575 /*
576  * Curl_sasl_create_xoauth2_message()
577  *
578  * This is used to generate an already encoded OAuth 2.0 message ready for
579  * sending to the recipient.
580  *
581  * Parameters:
582  *
583  * data    [in]     - The session handle.
584  * user    [in]     - The user name.
585  * bearer  [in]     - The bearer token.
586  * outptr  [in/out] - The address where a pointer to newly allocated memory
587  *                    holding the result will be stored upon completion.
588  * outlen  [out]    - The length of the output message.
589  *
590  * Returns CURLE_OK on success.
591  */
592 CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data,
593                                           const char *user,
594                                           const char *bearer,
595                                           char **outptr, size_t *outlen)
596 {
597   CURLcode result = CURLE_OK;
598   char *xoauth = NULL;
599
600   /* Generate the message */
601   xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer);
602   if(!xoauth)
603     return CURLE_OUT_OF_MEMORY;
604
605   /* Base64 encode the reply */
606   result = Curl_base64_encode(data, xoauth, strlen(xoauth), outptr, outlen);
607
608   Curl_safefree(xoauth);
609
610   return result;
611 }
612
613 /*
614  * Curl_sasl_cleanup()
615  *
616  * This is used to cleanup any libraries or curl modules used by the sasl
617  * functions.
618  *
619  * Parameters:
620  *
621  * conn     [in]     - Pointer to the connection data.
622  * authused [in]     - The authentication mechanism used.
623  */
624 void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
625 {
626 #ifdef USE_NTLM
627   /* Cleanup the ntlm structure */
628   if(authused == SASL_MECH_NTLM) {
629     Curl_ntlm_sspi_cleanup(&conn->ntlm);
630   }
631   (void)conn;
632 #else
633   /* Reserved for future use */
634   (void)conn;
635   (void)authused;
636 #endif
637 }