Annotation of embedaddon/curl/lib/vauth/digest_sspi.c, revision 1.1.1.1
1.1 misho 1: /***************************************************************************
2: * _ _ ____ _
3: * Project ___| | | | _ \| |
4: * / __| | | | |_) | |
5: * | (__| |_| | _ <| |___
6: * \___|\___/|_| \_\_____|
7: *
8: * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>.
9: * Copyright (C) 2015 - 2019, 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 "strcase.h"
41:
42: /* The last #include files should be: */
43: #include "curl_memory.h"
44: #include "memdebug.h"
45:
46: /*
47: * Curl_auth_is_digest_supported()
48: *
49: * This is used to evaluate if DIGEST is supported.
50: *
51: * Parameters: None
52: *
53: * Returns TRUE if DIGEST is supported by Windows SSPI.
54: */
55: bool Curl_auth_is_digest_supported(void)
56: {
57: PSecPkgInfo SecurityPackage;
58: SECURITY_STATUS status;
59:
60: /* Query the security package for Digest */
61: status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
62: &SecurityPackage);
63:
64: /* Release the package buffer as it is not required anymore */
65: if(status == SEC_E_OK) {
66: s_pSecFn->FreeContextBuffer(SecurityPackage);
67: }
68:
69: return (status == SEC_E_OK ? TRUE : FALSE);
70: }
71:
72: /*
73: * Curl_auth_create_digest_md5_message()
74: *
75: * This is used to generate an already encoded DIGEST-MD5 response message
76: * ready for sending to the recipient.
77: *
78: * Parameters:
79: *
80: * data [in] - The session handle.
81: * chlg64 [in] - The base64 encoded challenge message.
82: * userp [in] - The user name in the format User or Domain\User.
83: * passwdp [in] - The user's password.
84: * service [in] - The service type such as http, smtp, pop or imap.
85: * outptr [in/out] - The address where a pointer to newly allocated memory
86: * holding the result will be stored upon completion.
87: * outlen [out] - The length of the output message.
88: *
89: * Returns CURLE_OK on success.
90: */
91: CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
92: const char *chlg64,
93: const char *userp,
94: const char *passwdp,
95: const char *service,
96: char **outptr, size_t *outlen)
97: {
98: CURLcode result = CURLE_OK;
99: TCHAR *spn = NULL;
100: size_t chlglen = 0;
101: size_t token_max = 0;
102: unsigned char *input_token = NULL;
103: unsigned char *output_token = NULL;
104: CredHandle credentials;
105: CtxtHandle context;
106: PSecPkgInfo SecurityPackage;
107: SEC_WINNT_AUTH_IDENTITY identity;
108: SEC_WINNT_AUTH_IDENTITY *p_identity;
109: SecBuffer chlg_buf;
110: SecBuffer resp_buf;
111: SecBufferDesc chlg_desc;
112: SecBufferDesc resp_desc;
113: SECURITY_STATUS status;
114: unsigned long attrs;
115: TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
116:
117: /* Decode the base-64 encoded challenge message */
118: if(strlen(chlg64) && *chlg64 != '=') {
119: result = Curl_base64_decode(chlg64, &input_token, &chlglen);
120: if(result)
121: return result;
122: }
123:
124: /* Ensure we have a valid challenge message */
125: if(!input_token) {
126: infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n");
127:
128: return CURLE_BAD_CONTENT_ENCODING;
129: }
130:
131: /* Query the security package for DigestSSP */
132: status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
133: &SecurityPackage);
134: if(status != SEC_E_OK) {
135: free(input_token);
136:
137: return CURLE_NOT_BUILT_IN;
138: }
139:
140: token_max = SecurityPackage->cbMaxToken;
141:
142: /* Release the package buffer as it is not required anymore */
143: s_pSecFn->FreeContextBuffer(SecurityPackage);
144:
145: /* Allocate our response buffer */
146: output_token = malloc(token_max);
147: if(!output_token) {
148: free(input_token);
149:
150: return CURLE_OUT_OF_MEMORY;
151: }
152:
153: /* Generate our SPN */
154: spn = Curl_auth_build_spn(service, data->conn->host.name, NULL);
155: if(!spn) {
156: free(output_token);
157: free(input_token);
158:
159: return CURLE_OUT_OF_MEMORY;
160: }
161:
162: if(userp && *userp) {
163: /* Populate our identity structure */
164: result = Curl_create_sspi_identity(userp, passwdp, &identity);
165: if(result) {
166: free(spn);
167: free(output_token);
168: free(input_token);
169:
170: return result;
171: }
172:
173: /* Allow proper cleanup of the identity structure */
174: p_identity = &identity;
175: }
176: else
177: /* Use the current Windows user */
178: p_identity = NULL;
179:
180: /* Acquire our credentials handle */
181: status = s_pSecFn->AcquireCredentialsHandle(NULL,
182: (TCHAR *) TEXT(SP_NAME_DIGEST),
183: SECPKG_CRED_OUTBOUND, NULL,
184: p_identity, NULL, NULL,
185: &credentials, &expiry);
186:
187: if(status != SEC_E_OK) {
188: Curl_sspi_free_identity(p_identity);
189: free(spn);
190: free(output_token);
191: free(input_token);
192:
193: return CURLE_LOGIN_DENIED;
194: }
195:
196: /* Setup the challenge "input" security buffer */
197: chlg_desc.ulVersion = SECBUFFER_VERSION;
198: chlg_desc.cBuffers = 1;
199: chlg_desc.pBuffers = &chlg_buf;
200: chlg_buf.BufferType = SECBUFFER_TOKEN;
201: chlg_buf.pvBuffer = input_token;
202: chlg_buf.cbBuffer = curlx_uztoul(chlglen);
203:
204: /* Setup the response "output" security buffer */
205: resp_desc.ulVersion = SECBUFFER_VERSION;
206: resp_desc.cBuffers = 1;
207: resp_desc.pBuffers = &resp_buf;
208: resp_buf.BufferType = SECBUFFER_TOKEN;
209: resp_buf.pvBuffer = output_token;
210: resp_buf.cbBuffer = curlx_uztoul(token_max);
211:
212: /* Generate our response message */
213: status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn,
214: 0, 0, 0, &chlg_desc, 0,
215: &context, &resp_desc, &attrs,
216: &expiry);
217:
218: if(status == SEC_I_COMPLETE_NEEDED ||
219: status == SEC_I_COMPLETE_AND_CONTINUE)
220: s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
221: else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
222: s_pSecFn->FreeCredentialsHandle(&credentials);
223: Curl_sspi_free_identity(p_identity);
224: free(spn);
225: free(output_token);
226: free(input_token);
227:
228: if(status == SEC_E_INSUFFICIENT_MEMORY)
229: return CURLE_OUT_OF_MEMORY;
230:
231: return CURLE_AUTH_ERROR;
232: }
233:
234: /* Base64 encode the response */
235: result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer,
236: outptr, outlen);
237:
238: /* Free our handles */
239: s_pSecFn->DeleteSecurityContext(&context);
240: s_pSecFn->FreeCredentialsHandle(&credentials);
241:
242: /* Free the identity structure */
243: Curl_sspi_free_identity(p_identity);
244:
245: /* Free the SPN */
246: free(spn);
247:
248: /* Free the response buffer */
249: free(output_token);
250:
251: /* Free the decoded challenge message */
252: free(input_token);
253:
254: return result;
255: }
256:
257: /*
258: * Curl_override_sspi_http_realm()
259: *
260: * This is used to populate the domain in a SSPI identity structure
261: * The realm is extracted from the challenge message and used as the
262: * domain if it is not already explicitly set.
263: *
264: * Parameters:
265: *
266: * chlg [in] - The challenge message.
267: * identity [in/out] - The identity structure.
268: *
269: * Returns CURLE_OK on success.
270: */
271: CURLcode Curl_override_sspi_http_realm(const char *chlg,
272: SEC_WINNT_AUTH_IDENTITY *identity)
273: {
274: xcharp_u domain, dup_domain;
275:
276: /* If domain is blank or unset, check challenge message for realm */
277: if(!identity->Domain || !identity->DomainLength) {
278: for(;;) {
279: char value[DIGEST_MAX_VALUE_LENGTH];
280: char content[DIGEST_MAX_CONTENT_LENGTH];
281:
282: /* Pass all additional spaces here */
283: while(*chlg && ISSPACE(*chlg))
284: chlg++;
285:
286: /* Extract a value=content pair */
287: if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
288: if(strcasecompare(value, "realm")) {
289:
290: /* Setup identity's domain and length */
291: domain.tchar_ptr = Curl_convert_UTF8_to_tchar((char *) content);
292: if(!domain.tchar_ptr)
293: return CURLE_OUT_OF_MEMORY;
294:
295: dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr);
296: if(!dup_domain.tchar_ptr) {
297: Curl_unicodefree(domain.tchar_ptr);
298: return CURLE_OUT_OF_MEMORY;
299: }
300:
301: free(identity->Domain);
302: identity->Domain = dup_domain.tbyte_ptr;
303: identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr));
304: dup_domain.tchar_ptr = NULL;
305:
306: Curl_unicodefree(domain.tchar_ptr);
307: }
308: else {
309: /* Unknown specifier, ignore it! */
310: }
311: }
312: else
313: break; /* We're done here */
314:
315: /* Pass all additional spaces here */
316: while(*chlg && ISSPACE(*chlg))
317: chlg++;
318:
319: /* Allow the list to be comma-separated */
320: if(',' == *chlg)
321: chlg++;
322: }
323: }
324:
325: return CURLE_OK;
326: }
327:
328: /*
329: * Curl_auth_decode_digest_http_message()
330: *
331: * This is used to decode a HTTP DIGEST challenge message into the separate
332: * attributes.
333: *
334: * Parameters:
335: *
336: * chlg [in] - The challenge message.
337: * digest [in/out] - The digest data struct being used and modified.
338: *
339: * Returns CURLE_OK on success.
340: */
341: CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
342: struct digestdata *digest)
343: {
344: size_t chlglen = strlen(chlg);
345:
346: /* We had an input token before so if there's another one now that means we
347: provided bad credentials in the previous request or it's stale. */
348: if(digest->input_token) {
349: bool stale = false;
350: const char *p = chlg;
351:
352: /* Check for the 'stale' directive */
353: for(;;) {
354: char value[DIGEST_MAX_VALUE_LENGTH];
355: char content[DIGEST_MAX_CONTENT_LENGTH];
356:
357: while(*p && ISSPACE(*p))
358: p++;
359:
360: if(!Curl_auth_digest_get_pair(p, value, content, &p))
361: break;
362:
363: if(strcasecompare(value, "stale") &&
364: strcasecompare(content, "true")) {
365: stale = true;
366: break;
367: }
368:
369: while(*p && ISSPACE(*p))
370: p++;
371:
372: if(',' == *p)
373: p++;
374: }
375:
376: if(stale)
377: Curl_auth_digest_cleanup(digest);
378: else
379: return CURLE_LOGIN_DENIED;
380: }
381:
382: /* Store the challenge for use later */
383: digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen + 1);
384: if(!digest->input_token)
385: return CURLE_OUT_OF_MEMORY;
386:
387: digest->input_token_len = chlglen;
388:
389: return CURLE_OK;
390: }
391:
392: /*
393: * Curl_auth_create_digest_http_message()
394: *
395: * This is used to generate a HTTP DIGEST response message ready for sending
396: * to the recipient.
397: *
398: * Parameters:
399: *
400: * data [in] - The session handle.
401: * userp [in] - The user name in the format User or Domain\User.
402: * passwdp [in] - The user's password.
403: * request [in] - The HTTP request.
404: * uripath [in] - The path of the HTTP uri.
405: * digest [in/out] - The digest data struct being used and modified.
406: * outptr [in/out] - The address where a pointer to newly allocated memory
407: * holding the result will be stored upon completion.
408: * outlen [out] - The length of the output message.
409: *
410: * Returns CURLE_OK on success.
411: */
412: CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
413: const char *userp,
414: const char *passwdp,
415: const unsigned char *request,
416: const unsigned char *uripath,
417: struct digestdata *digest,
418: char **outptr, size_t *outlen)
419: {
420: size_t token_max;
421: char *resp;
422: BYTE *output_token;
423: size_t output_token_len = 0;
424: PSecPkgInfo SecurityPackage;
425: SecBuffer chlg_buf[5];
426: SecBufferDesc chlg_desc;
427: SECURITY_STATUS status;
428:
429: (void) data;
430:
431: /* Query the security package for DigestSSP */
432: status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
433: &SecurityPackage);
434: if(status != SEC_E_OK)
435: return CURLE_NOT_BUILT_IN;
436:
437: token_max = SecurityPackage->cbMaxToken;
438:
439: /* Release the package buffer as it is not required anymore */
440: s_pSecFn->FreeContextBuffer(SecurityPackage);
441:
442: /* Allocate the output buffer according to the max token size as indicated
443: by the security package */
444: output_token = malloc(token_max);
445: if(!output_token) {
446: return CURLE_OUT_OF_MEMORY;
447: }
448:
449: /* If the user/passwd that was used to make the identity for http_context
450: has changed then delete that context. */
451: if((userp && !digest->user) || (!userp && digest->user) ||
452: (passwdp && !digest->passwd) || (!passwdp && digest->passwd) ||
453: (userp && digest->user && strcmp(userp, digest->user)) ||
454: (passwdp && digest->passwd && strcmp(passwdp, digest->passwd))) {
455: if(digest->http_context) {
456: s_pSecFn->DeleteSecurityContext(digest->http_context);
457: Curl_safefree(digest->http_context);
458: }
459: Curl_safefree(digest->user);
460: Curl_safefree(digest->passwd);
461: }
462:
463: if(digest->http_context) {
464: chlg_desc.ulVersion = SECBUFFER_VERSION;
465: chlg_desc.cBuffers = 5;
466: chlg_desc.pBuffers = chlg_buf;
467: chlg_buf[0].BufferType = SECBUFFER_TOKEN;
468: chlg_buf[0].pvBuffer = NULL;
469: chlg_buf[0].cbBuffer = 0;
470: chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
471: chlg_buf[1].pvBuffer = (void *) request;
472: chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
473: chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
474: chlg_buf[2].pvBuffer = (void *) uripath;
475: chlg_buf[2].cbBuffer = curlx_uztoul(strlen((const char *) uripath));
476: chlg_buf[3].BufferType = SECBUFFER_PKG_PARAMS;
477: chlg_buf[3].pvBuffer = NULL;
478: chlg_buf[3].cbBuffer = 0;
479: chlg_buf[4].BufferType = SECBUFFER_PADDING;
480: chlg_buf[4].pvBuffer = output_token;
481: chlg_buf[4].cbBuffer = curlx_uztoul(token_max);
482:
483: status = s_pSecFn->MakeSignature(digest->http_context, 0, &chlg_desc, 0);
484: if(status == SEC_E_OK)
485: output_token_len = chlg_buf[4].cbBuffer;
486: else { /* delete the context so a new one can be made */
487: infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx\n",
488: (long)status);
489: s_pSecFn->DeleteSecurityContext(digest->http_context);
490: Curl_safefree(digest->http_context);
491: }
492: }
493:
494: if(!digest->http_context) {
495: CredHandle credentials;
496: SEC_WINNT_AUTH_IDENTITY identity;
497: SEC_WINNT_AUTH_IDENTITY *p_identity;
498: SecBuffer resp_buf;
499: SecBufferDesc resp_desc;
500: unsigned long attrs;
501: TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
502: TCHAR *spn;
503:
504: /* free the copy of user/passwd used to make the previous identity */
505: Curl_safefree(digest->user);
506: Curl_safefree(digest->passwd);
507:
508: if(userp && *userp) {
509: /* Populate our identity structure */
510: if(Curl_create_sspi_identity(userp, passwdp, &identity)) {
511: free(output_token);
512: return CURLE_OUT_OF_MEMORY;
513: }
514:
515: /* Populate our identity domain */
516: if(Curl_override_sspi_http_realm((const char *) digest->input_token,
517: &identity)) {
518: free(output_token);
519: return CURLE_OUT_OF_MEMORY;
520: }
521:
522: /* Allow proper cleanup of the identity structure */
523: p_identity = &identity;
524: }
525: else
526: /* Use the current Windows user */
527: p_identity = NULL;
528:
529: if(userp) {
530: digest->user = strdup(userp);
531:
532: if(!digest->user) {
533: free(output_token);
534: return CURLE_OUT_OF_MEMORY;
535: }
536: }
537:
538: if(passwdp) {
539: digest->passwd = strdup(passwdp);
540:
541: if(!digest->passwd) {
542: free(output_token);
543: Curl_safefree(digest->user);
544: return CURLE_OUT_OF_MEMORY;
545: }
546: }
547:
548: /* Acquire our credentials handle */
549: status = s_pSecFn->AcquireCredentialsHandle(NULL,
550: (TCHAR *) TEXT(SP_NAME_DIGEST),
551: SECPKG_CRED_OUTBOUND, NULL,
552: p_identity, NULL, NULL,
553: &credentials, &expiry);
554: if(status != SEC_E_OK) {
555: Curl_sspi_free_identity(p_identity);
556: free(output_token);
557:
558: return CURLE_LOGIN_DENIED;
559: }
560:
561: /* Setup the challenge "input" security buffer if present */
562: chlg_desc.ulVersion = SECBUFFER_VERSION;
563: chlg_desc.cBuffers = 3;
564: chlg_desc.pBuffers = chlg_buf;
565: chlg_buf[0].BufferType = SECBUFFER_TOKEN;
566: chlg_buf[0].pvBuffer = digest->input_token;
567: chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len);
568: chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
569: chlg_buf[1].pvBuffer = (void *) request;
570: chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
571: chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
572: chlg_buf[2].pvBuffer = NULL;
573: chlg_buf[2].cbBuffer = 0;
574:
575: /* Setup the response "output" security buffer */
576: resp_desc.ulVersion = SECBUFFER_VERSION;
577: resp_desc.cBuffers = 1;
578: resp_desc.pBuffers = &resp_buf;
579: resp_buf.BufferType = SECBUFFER_TOKEN;
580: resp_buf.pvBuffer = output_token;
581: resp_buf.cbBuffer = curlx_uztoul(token_max);
582:
583: spn = Curl_convert_UTF8_to_tchar((char *) uripath);
584: if(!spn) {
585: s_pSecFn->FreeCredentialsHandle(&credentials);
586:
587: Curl_sspi_free_identity(p_identity);
588: free(output_token);
589:
590: return CURLE_OUT_OF_MEMORY;
591: }
592:
593: /* Allocate our new context handle */
594: digest->http_context = calloc(1, sizeof(CtxtHandle));
595: if(!digest->http_context)
596: return CURLE_OUT_OF_MEMORY;
597:
598: /* Generate our response message */
599: status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
600: spn,
601: ISC_REQ_USE_HTTP_STYLE, 0, 0,
602: &chlg_desc, 0,
603: digest->http_context,
604: &resp_desc, &attrs, &expiry);
605: Curl_unicodefree(spn);
606:
607: if(status == SEC_I_COMPLETE_NEEDED ||
608: status == SEC_I_COMPLETE_AND_CONTINUE)
609: s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
610: else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
611: s_pSecFn->FreeCredentialsHandle(&credentials);
612:
613: Curl_sspi_free_identity(p_identity);
614: free(output_token);
615:
616: Curl_safefree(digest->http_context);
617:
618: if(status == SEC_E_INSUFFICIENT_MEMORY)
619: return CURLE_OUT_OF_MEMORY;
620:
621: return CURLE_AUTH_ERROR;
622: }
623:
624: output_token_len = resp_buf.cbBuffer;
625:
626: s_pSecFn->FreeCredentialsHandle(&credentials);
627: Curl_sspi_free_identity(p_identity);
628: }
629:
630: resp = malloc(output_token_len + 1);
631: if(!resp) {
632: free(output_token);
633:
634: return CURLE_OUT_OF_MEMORY;
635: }
636:
637: /* Copy the generated response */
638: memcpy(resp, output_token, output_token_len);
639: resp[output_token_len] = 0;
640:
641: /* Return the response */
642: *outptr = resp;
643: *outlen = output_token_len;
644:
645: /* Free the response buffer */
646: free(output_token);
647:
648: return CURLE_OK;
649: }
650:
651: /*
652: * Curl_auth_digest_cleanup()
653: *
654: * This is used to clean up the digest specific data.
655: *
656: * Parameters:
657: *
658: * digest [in/out] - The digest data struct being cleaned up.
659: *
660: */
661: void Curl_auth_digest_cleanup(struct digestdata *digest)
662: {
663: /* Free the input token */
664: Curl_safefree(digest->input_token);
665:
666: /* Reset any variables */
667: digest->input_token_len = 0;
668:
669: /* Delete security context */
670: if(digest->http_context) {
671: s_pSecFn->DeleteSecurityContext(digest->http_context);
672: Curl_safefree(digest->http_context);
673: }
674:
675: /* Free the copy of user/passwd used to make the identity for http_context */
676: Curl_safefree(digest->user);
677: Curl_safefree(digest->passwd);
678: }
679:
680: #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_CRYPTO_AUTH */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>