• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>.
9  * Copyright (C) 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
10  *
11  * This software is licensed as described in the file COPYING, which
12  * you should have received as part of this distribution. The terms
13  * are also available at https://curl.haxx.se/docs/copyright.html.
14  *
15  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16  * copies of the Software, and permit persons to whom the Software is
17  * furnished to do so, under the terms of the COPYING file.
18  *
19  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20  * KIND, either express or implied.
21  *
22  * RFC2831 DIGEST-MD5 authentication
23  *
24  ***************************************************************************/
25 
26 #include "curl_setup.h"
27 
28 #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_CRYPTO_AUTH)
29 
30 #include <curl/curl.h>
31 
32 #include "vauth/vauth.h"
33 #include "vauth/digest.h"
34 #include "urldata.h"
35 #include "curl_base64.h"
36 #include "warnless.h"
37 #include "curl_multibyte.h"
38 #include "sendf.h"
39 #include "strdup.h"
40 #include "rawstr.h"
41 
42 /* The last #include files should be: */
43 #include "curl_memory.h"
44 #include "memdebug.h"
45 
46 /*
47  * Curl_auth_create_digest_md5_message()
48  *
49  * This is used to generate an already encoded DIGEST-MD5 response message
50  * ready for sending to the recipient.
51  *
52  * Parameters:
53  *
54  * data    [in]     - The session handle.
55  * chlg64  [in]     - The base64 encoded challenge message.
56  * userp   [in]     - The user name in the format User or Domain\User.
57  * passdwp [in]     - The user's password.
58  * service [in]     - The service type such as http, smtp, pop or imap.
59  * outptr  [in/out] - The address where a pointer to newly allocated memory
60  *                    holding the result will be stored upon completion.
61  * outlen  [out]    - The length of the output message.
62  *
63  * Returns CURLE_OK on success.
64  */
Curl_auth_create_digest_md5_message(struct Curl_easy * data,const char * chlg64,const char * userp,const char * passwdp,const char * service,char ** outptr,size_t * outlen)65 CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
66                                              const char *chlg64,
67                                              const char *userp,
68                                              const char *passwdp,
69                                              const char *service,
70                                              char **outptr, size_t *outlen)
71 {
72   CURLcode result = CURLE_OK;
73   TCHAR *spn = NULL;
74   size_t chlglen = 0;
75   size_t token_max = 0;
76   unsigned char *input_token = NULL;
77   unsigned char *output_token = NULL;
78   CredHandle credentials;
79   CtxtHandle context;
80   PSecPkgInfo SecurityPackage;
81   SEC_WINNT_AUTH_IDENTITY identity;
82   SEC_WINNT_AUTH_IDENTITY *p_identity;
83   SecBuffer chlg_buf;
84   SecBuffer resp_buf;
85   SecBufferDesc chlg_desc;
86   SecBufferDesc resp_desc;
87   SECURITY_STATUS status;
88   unsigned long attrs;
89   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
90 
91   /* Decode the base-64 encoded challenge message */
92   if(strlen(chlg64) && *chlg64 != '=') {
93     result = Curl_base64_decode(chlg64, &input_token, &chlglen);
94     if(result)
95       return result;
96   }
97 
98   /* Ensure we have a valid challenge message */
99   if(!input_token) {
100     infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n");
101 
102     return CURLE_BAD_CONTENT_ENCODING;
103   }
104 
105   /* Query the security package for DigestSSP */
106   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
107                                               &SecurityPackage);
108   if(status != SEC_E_OK) {
109     free(input_token);
110 
111     return CURLE_NOT_BUILT_IN;
112   }
113 
114   token_max = SecurityPackage->cbMaxToken;
115 
116   /* Release the package buffer as it is not required anymore */
117   s_pSecFn->FreeContextBuffer(SecurityPackage);
118 
119   /* Allocate our response buffer */
120   output_token = malloc(token_max);
121   if(!output_token) {
122     free(input_token);
123 
124     return CURLE_OUT_OF_MEMORY;
125   }
126 
127   /* Generate our SPN */
128   spn = Curl_auth_build_spn(service, data->easy_conn->host.name, NULL);
129   if(!spn) {
130     free(output_token);
131     free(input_token);
132 
133     return CURLE_OUT_OF_MEMORY;
134   }
135 
136   if(userp && *userp) {
137     /* Populate our identity structure */
138     result = Curl_create_sspi_identity(userp, passwdp, &identity);
139     if(result) {
140       free(spn);
141       free(output_token);
142       free(input_token);
143 
144       return result;
145     }
146 
147     /* Allow proper cleanup of the identity structure */
148     p_identity = &identity;
149   }
150   else
151     /* Use the current Windows user */
152     p_identity = NULL;
153 
154   /* Acquire our credentials handle */
155   status = s_pSecFn->AcquireCredentialsHandle(NULL,
156                                               (TCHAR *) TEXT(SP_NAME_DIGEST),
157                                               SECPKG_CRED_OUTBOUND, NULL,
158                                               p_identity, NULL, NULL,
159                                               &credentials, &expiry);
160 
161   if(status != SEC_E_OK) {
162     Curl_sspi_free_identity(p_identity);
163     free(spn);
164     free(output_token);
165     free(input_token);
166 
167     return CURLE_LOGIN_DENIED;
168   }
169 
170   /* Setup the challenge "input" security buffer */
171   chlg_desc.ulVersion = SECBUFFER_VERSION;
172   chlg_desc.cBuffers  = 1;
173   chlg_desc.pBuffers  = &chlg_buf;
174   chlg_buf.BufferType = SECBUFFER_TOKEN;
175   chlg_buf.pvBuffer   = input_token;
176   chlg_buf.cbBuffer   = curlx_uztoul(chlglen);
177 
178   /* Setup the response "output" security buffer */
179   resp_desc.ulVersion = SECBUFFER_VERSION;
180   resp_desc.cBuffers  = 1;
181   resp_desc.pBuffers  = &resp_buf;
182   resp_buf.BufferType = SECBUFFER_TOKEN;
183   resp_buf.pvBuffer   = output_token;
184   resp_buf.cbBuffer   = curlx_uztoul(token_max);
185 
186   /* Generate our response message */
187   status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn,
188                                                0, 0, 0, &chlg_desc, 0,
189                                                &context, &resp_desc, &attrs,
190                                                &expiry);
191 
192   if(status == SEC_I_COMPLETE_NEEDED ||
193      status == SEC_I_COMPLETE_AND_CONTINUE)
194     s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
195   else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
196     s_pSecFn->FreeCredentialsHandle(&credentials);
197     Curl_sspi_free_identity(p_identity);
198     free(spn);
199     free(output_token);
200     free(input_token);
201 
202     return CURLE_RECV_ERROR;
203   }
204 
205   /* Base64 encode the response */
206   result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer,
207                               outptr, outlen);
208 
209   /* Free our handles */
210   s_pSecFn->DeleteSecurityContext(&context);
211   s_pSecFn->FreeCredentialsHandle(&credentials);
212 
213   /* Free the identity structure */
214   Curl_sspi_free_identity(p_identity);
215 
216   /* Free the SPN */
217   free(spn);
218 
219   /* Free the response buffer */
220   free(output_token);
221 
222   /* Free the decoded challenge message */
223   free(input_token);
224 
225   return result;
226 }
227 
228 /*
229  * Curl_override_sspi_http_realm()
230  *
231  * This is used to populate the domain in a SSPI identity structure
232  * The realm is extracted from the challenge message and used as the
233  * domain if it is not already explicitly set.
234  *
235  * Parameters:
236  *
237  * chlg     [in]     - The challenge message.
238  * identity [in/out] - The identity structure.
239  *
240  * Returns CURLE_OK on success.
241  */
Curl_override_sspi_http_realm(const char * chlg,SEC_WINNT_AUTH_IDENTITY * identity)242 CURLcode Curl_override_sspi_http_realm(const char *chlg,
243                                        SEC_WINNT_AUTH_IDENTITY *identity)
244 {
245   xcharp_u domain, dup_domain;
246 
247   /* If domain is blank or unset, check challenge message for realm */
248   if(!identity->Domain || !identity->DomainLength) {
249     for(;;) {
250       char value[DIGEST_MAX_VALUE_LENGTH];
251       char content[DIGEST_MAX_CONTENT_LENGTH];
252 
253       /* Pass all additional spaces here */
254       while(*chlg && ISSPACE(*chlg))
255         chlg++;
256 
257       /* Extract a value=content pair */
258       if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
259         if(Curl_raw_equal(value, "realm")) {
260 
261           /* Setup identity's domain and length */
262           domain.tchar_ptr = Curl_convert_UTF8_to_tchar((char *) content);
263           if(!domain.tchar_ptr)
264             return CURLE_OUT_OF_MEMORY;
265 
266           dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr);
267           if(!dup_domain.tchar_ptr) {
268             Curl_unicodefree(domain.tchar_ptr);
269             return CURLE_OUT_OF_MEMORY;
270           }
271 
272           free(identity->Domain);
273           identity->Domain = dup_domain.tbyte_ptr;
274           identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr));
275           dup_domain.tchar_ptr = NULL;
276 
277           Curl_unicodefree(domain.tchar_ptr);
278         }
279         else {
280           /* Unknown specifier, ignore it! */
281         }
282       }
283       else
284         break; /* We're done here */
285 
286       /* Pass all additional spaces here */
287       while(*chlg && ISSPACE(*chlg))
288         chlg++;
289 
290       /* Allow the list to be comma-separated */
291       if(',' == *chlg)
292         chlg++;
293     }
294   }
295 
296   return CURLE_OK;
297 }
298 
299 /*
300  * Curl_auth_decode_digest_http_message()
301  *
302  * This is used to decode a HTTP DIGEST challenge message into the seperate
303  * attributes.
304  *
305  * Parameters:
306  *
307  * chlg    [in]     - The challenge message.
308  * digest  [in/out] - The digest data struct being used and modified.
309  *
310  * Returns CURLE_OK on success.
311  */
Curl_auth_decode_digest_http_message(const char * chlg,struct digestdata * digest)312 CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
313                                               struct digestdata *digest)
314 {
315   size_t chlglen = strlen(chlg);
316 
317   /* We had an input token before and we got another one now. This means we
318      provided bad credentials in the previous request. */
319   if(digest->input_token)
320     return CURLE_BAD_CONTENT_ENCODING;
321 
322   /* Simply store the challenge for use later */
323   digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen);
324   if(!digest->input_token)
325     return CURLE_OUT_OF_MEMORY;
326 
327   digest->input_token_len = chlglen;
328 
329   return CURLE_OK;
330 }
331 
332 /*
333  * Curl_auth_create_digest_http_message()
334  *
335  * This is used to generate a HTTP DIGEST response message ready for sending
336  * to the recipient.
337  *
338  * Parameters:
339  *
340  * data    [in]     - The session handle.
341  * userp   [in]     - The user name in the format User or Domain\User.
342  * passdwp [in]     - The user's password.
343  * request [in]     - The HTTP request.
344  * uripath [in]     - The path of the HTTP uri.
345  * digest  [in/out] - The digest data struct being used and modified.
346  * outptr  [in/out] - The address where a pointer to newly allocated memory
347  *                    holding the result will be stored upon completion.
348  * outlen  [out]    - The length of the output message.
349  *
350  * Returns CURLE_OK on success.
351  */
Curl_auth_create_digest_http_message(struct Curl_easy * data,const char * userp,const char * passwdp,const unsigned char * request,const unsigned char * uripath,struct digestdata * digest,char ** outptr,size_t * outlen)352 CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
353                                               const char *userp,
354                                               const char *passwdp,
355                                               const unsigned char *request,
356                                               const unsigned char *uripath,
357                                               struct digestdata *digest,
358                                               char **outptr, size_t *outlen)
359 {
360   size_t token_max;
361   CredHandle credentials;
362   CtxtHandle context;
363   char *resp;
364   BYTE *output_token;
365   PSecPkgInfo SecurityPackage;
366   SEC_WINNT_AUTH_IDENTITY identity;
367   SEC_WINNT_AUTH_IDENTITY *p_identity;
368   SecBuffer chlg_buf[3];
369   SecBuffer resp_buf;
370   SecBufferDesc chlg_desc;
371   SecBufferDesc resp_desc;
372   SECURITY_STATUS status;
373   unsigned long attrs;
374   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
375   TCHAR *spn;
376 
377   (void) data;
378 
379   /* Query the security package for DigestSSP */
380   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
381                                               &SecurityPackage);
382   if(status != SEC_E_OK)
383     return CURLE_NOT_BUILT_IN;
384 
385   token_max = SecurityPackage->cbMaxToken;
386 
387   /* Release the package buffer as it is not required anymore */
388   s_pSecFn->FreeContextBuffer(SecurityPackage);
389 
390   if(userp && *userp) {
391     /* Populate our identity structure */
392     if(Curl_create_sspi_identity(userp, passwdp, &identity))
393       return CURLE_OUT_OF_MEMORY;
394 
395     /* Populate our identity domain */
396     if(Curl_override_sspi_http_realm((const char*) digest->input_token,
397                                      &identity))
398       return CURLE_OUT_OF_MEMORY;
399 
400     /* Allow proper cleanup of the identity structure */
401     p_identity = &identity;
402   }
403   else
404     /* Use the current Windows user */
405     p_identity = NULL;
406 
407   /* Acquire our credentials handle */
408   status = s_pSecFn->AcquireCredentialsHandle(NULL,
409                                               (TCHAR *) TEXT(SP_NAME_DIGEST),
410                                               SECPKG_CRED_OUTBOUND, NULL,
411                                               p_identity, NULL, NULL,
412                                               &credentials, &expiry);
413   if(status != SEC_E_OK) {
414     Curl_sspi_free_identity(p_identity);
415 
416     return CURLE_LOGIN_DENIED;
417   }
418 
419   /* Allocate the output buffer according to the max token size as indicated
420      by the security package */
421   output_token = malloc(token_max);
422   if(!output_token) {
423     s_pSecFn->FreeCredentialsHandle(&credentials);
424 
425     Curl_sspi_free_identity(p_identity);
426 
427     return CURLE_OUT_OF_MEMORY;
428   }
429 
430   /* Setup the challenge "input" security buffer if present */
431   chlg_desc.ulVersion    = SECBUFFER_VERSION;
432   chlg_desc.cBuffers     = 3;
433   chlg_desc.pBuffers     = chlg_buf;
434   chlg_buf[0].BufferType = SECBUFFER_TOKEN;
435   chlg_buf[0].pvBuffer   = digest->input_token;
436   chlg_buf[0].cbBuffer   = curlx_uztoul(digest->input_token_len);
437   chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
438   chlg_buf[1].pvBuffer   = (void *) request;
439   chlg_buf[1].cbBuffer   = curlx_uztoul(strlen((const char *) request));
440   chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
441   chlg_buf[2].pvBuffer   = NULL;
442   chlg_buf[2].cbBuffer   = 0;
443 
444   /* Setup the response "output" security buffer */
445   resp_desc.ulVersion = SECBUFFER_VERSION;
446   resp_desc.cBuffers  = 1;
447   resp_desc.pBuffers  = &resp_buf;
448   resp_buf.BufferType = SECBUFFER_TOKEN;
449   resp_buf.pvBuffer   = output_token;
450   resp_buf.cbBuffer   = curlx_uztoul(token_max);
451 
452   spn = Curl_convert_UTF8_to_tchar((char *) uripath);
453   if(!spn) {
454     s_pSecFn->FreeCredentialsHandle(&credentials);
455 
456     Curl_sspi_free_identity(p_identity);
457     free(output_token);
458 
459     return CURLE_OUT_OF_MEMORY;
460   }
461 
462   /* Generate our reponse message */
463   status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
464                                                spn,
465                                                ISC_REQ_USE_HTTP_STYLE, 0, 0,
466                                                &chlg_desc, 0, &context,
467                                                &resp_desc, &attrs, &expiry);
468   Curl_unicodefree(spn);
469 
470   if(status == SEC_I_COMPLETE_NEEDED ||
471      status == SEC_I_COMPLETE_AND_CONTINUE)
472     s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
473   else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
474     s_pSecFn->FreeCredentialsHandle(&credentials);
475 
476     Curl_sspi_free_identity(p_identity);
477     free(output_token);
478 
479     return CURLE_OUT_OF_MEMORY;
480   }
481 
482   resp = malloc(resp_buf.cbBuffer + 1);
483   if(!resp) {
484     s_pSecFn->DeleteSecurityContext(&context);
485     s_pSecFn->FreeCredentialsHandle(&credentials);
486 
487     Curl_sspi_free_identity(p_identity);
488     free(output_token);
489 
490     return CURLE_OUT_OF_MEMORY;
491   }
492 
493   /* Copy the generated reponse */
494   memcpy(resp, resp_buf.pvBuffer, resp_buf.cbBuffer);
495   resp[resp_buf.cbBuffer] = 0x00;
496 
497   /* Return the response */
498   *outptr = resp;
499   *outlen = resp_buf.cbBuffer;
500 
501   /* Free our handles */
502   s_pSecFn->DeleteSecurityContext(&context);
503   s_pSecFn->FreeCredentialsHandle(&credentials);
504 
505   /* Free the identity structure */
506   Curl_sspi_free_identity(p_identity);
507 
508   /* Free the response buffer */
509   free(output_token);
510 
511   return CURLE_OK;
512 }
513 
514 /*
515  * Curl_auth_digest_cleanup()
516  *
517  * This is used to clean up the digest specific data.
518  *
519  * Parameters:
520  *
521  * digest    [in/out] - The digest data struct being cleaned up.
522  *
523  */
Curl_auth_digest_cleanup(struct digestdata * digest)524 void Curl_auth_digest_cleanup(struct digestdata *digest)
525 {
526   /* Free the input token */
527   Curl_safefree(digest->input_token);
528 
529   /* Reset any variables */
530   digest->input_token_len = 0;
531 }
532 
533 #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_CRYPTO_AUTH */
534