Annotation of embedaddon/curl/lib/vauth/krb5_sspi.c, revision 1.1.1.1
1.1 misho 1: /***************************************************************************
2: * _ _ ____ _
3: * Project ___| | | | _ \| |
4: * / __| | | | |_) | |
5: * | (__| |_| | _ <| |___
6: * \___|\___/|_| \_\_____|
7: *
8: * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>.
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 https://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: * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
22: *
23: ***************************************************************************/
24:
25: #include "curl_setup.h"
26:
27: #if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5)
28:
29: #include <curl/curl.h>
30:
31: #include "vauth/vauth.h"
32: #include "urldata.h"
33: #include "curl_base64.h"
34: #include "warnless.h"
35: #include "curl_multibyte.h"
36: #include "sendf.h"
37:
38: /* The last #include files should be: */
39: #include "curl_memory.h"
40: #include "memdebug.h"
41:
42: /*
43: * Curl_auth_is_gssapi_supported()
44: *
45: * This is used to evaluate if GSSAPI (Kerberos V5) is supported.
46: *
47: * Parameters: None
48: *
49: * Returns TRUE if Kerberos V5 is supported by Windows SSPI.
50: */
51: bool Curl_auth_is_gssapi_supported(void)
52: {
53: PSecPkgInfo SecurityPackage;
54: SECURITY_STATUS status;
55:
56: /* Query the security package for Kerberos */
57: status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
58: TEXT(SP_NAME_KERBEROS),
59: &SecurityPackage);
60:
61: /* Release the package buffer as it is not required anymore */
62: if(status == SEC_E_OK) {
63: s_pSecFn->FreeContextBuffer(SecurityPackage);
64: }
65:
66: return (status == SEC_E_OK ? TRUE : FALSE);
67: }
68:
69: /*
70: * Curl_auth_create_gssapi_user_message()
71: *
72: * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
73: * message ready for sending to the recipient.
74: *
75: * Parameters:
76: *
77: * data [in] - The session handle.
78: * userp [in] - The user name in the format User or Domain\User.
79: * passwdp [in] - The user's password.
80: * service [in] - The service type such as http, smtp, pop or imap.
81: * host [in] - The host name.
82: * mutual_auth [in] - Flag specifying whether or not mutual authentication
83: * is enabled.
84: * chlg64 [in] - The optional base64 encoded challenge message.
85: * krb5 [in/out] - The Kerberos 5 data struct being used and modified.
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_auth_create_gssapi_user_message(struct Curl_easy *data,
93: const char *userp,
94: const char *passwdp,
95: const char *service,
96: const char *host,
97: const bool mutual_auth,
98: const char *chlg64,
99: struct kerberos5data *krb5,
100: char **outptr, size_t *outlen)
101: {
102: CURLcode result = CURLE_OK;
103: size_t chlglen = 0;
104: unsigned char *chlg = NULL;
105: CtxtHandle context;
106: PSecPkgInfo SecurityPackage;
107: SecBuffer chlg_buf;
108: SecBuffer resp_buf;
109: SecBufferDesc chlg_desc;
110: SecBufferDesc resp_desc;
111: SECURITY_STATUS status;
112: unsigned long attrs;
113: TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
114:
115: if(!krb5->spn) {
116: /* Generate our SPN */
117: krb5->spn = Curl_auth_build_spn(service, host, NULL);
118: if(!krb5->spn)
119: return CURLE_OUT_OF_MEMORY;
120: }
121:
122: if(!krb5->output_token) {
123: /* Query the security package for Kerberos */
124: status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
125: TEXT(SP_NAME_KERBEROS),
126: &SecurityPackage);
127: if(status != SEC_E_OK) {
128: return CURLE_NOT_BUILT_IN;
129: }
130:
131: krb5->token_max = SecurityPackage->cbMaxToken;
132:
133: /* Release the package buffer as it is not required anymore */
134: s_pSecFn->FreeContextBuffer(SecurityPackage);
135:
136: /* Allocate our response buffer */
137: krb5->output_token = malloc(krb5->token_max);
138: if(!krb5->output_token)
139: return CURLE_OUT_OF_MEMORY;
140: }
141:
142: if(!krb5->credentials) {
143: /* Do we have credentials to use or are we using single sign-on? */
144: if(userp && *userp) {
145: /* Populate our identity structure */
146: result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
147: if(result)
148: return result;
149:
150: /* Allow proper cleanup of the identity structure */
151: krb5->p_identity = &krb5->identity;
152: }
153: else
154: /* Use the current Windows user */
155: krb5->p_identity = NULL;
156:
157: /* Allocate our credentials handle */
158: krb5->credentials = calloc(1, sizeof(CredHandle));
159: if(!krb5->credentials)
160: return CURLE_OUT_OF_MEMORY;
161:
162: /* Acquire our credentials handle */
163: status = s_pSecFn->AcquireCredentialsHandle(NULL,
164: (TCHAR *)
165: TEXT(SP_NAME_KERBEROS),
166: SECPKG_CRED_OUTBOUND, NULL,
167: krb5->p_identity, NULL, NULL,
168: krb5->credentials, &expiry);
169: if(status != SEC_E_OK)
170: return CURLE_LOGIN_DENIED;
171:
172: /* Allocate our new context handle */
173: krb5->context = calloc(1, sizeof(CtxtHandle));
174: if(!krb5->context)
175: return CURLE_OUT_OF_MEMORY;
176: }
177:
178: if(chlg64 && *chlg64) {
179: /* Decode the base-64 encoded challenge message */
180: if(*chlg64 != '=') {
181: result = Curl_base64_decode(chlg64, &chlg, &chlglen);
182: if(result)
183: return result;
184: }
185:
186: /* Ensure we have a valid challenge message */
187: if(!chlg) {
188: infof(data, "GSSAPI handshake failure (empty challenge message)\n");
189:
190: return CURLE_BAD_CONTENT_ENCODING;
191: }
192:
193: /* Setup the challenge "input" security buffer */
194: chlg_desc.ulVersion = SECBUFFER_VERSION;
195: chlg_desc.cBuffers = 1;
196: chlg_desc.pBuffers = &chlg_buf;
197: chlg_buf.BufferType = SECBUFFER_TOKEN;
198: chlg_buf.pvBuffer = chlg;
199: chlg_buf.cbBuffer = curlx_uztoul(chlglen);
200: }
201:
202: /* Setup the response "output" security buffer */
203: resp_desc.ulVersion = SECBUFFER_VERSION;
204: resp_desc.cBuffers = 1;
205: resp_desc.pBuffers = &resp_buf;
206: resp_buf.BufferType = SECBUFFER_TOKEN;
207: resp_buf.pvBuffer = krb5->output_token;
208: resp_buf.cbBuffer = curlx_uztoul(krb5->token_max);
209:
210: /* Generate our challenge-response message */
211: status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
212: chlg ? krb5->context : NULL,
213: krb5->spn,
214: (mutual_auth ?
215: ISC_REQ_MUTUAL_AUTH : 0),
216: 0, SECURITY_NATIVE_DREP,
217: chlg ? &chlg_desc : NULL, 0,
218: &context,
219: &resp_desc, &attrs,
220: &expiry);
221:
222: /* Free the decoded challenge as it is not required anymore */
223: free(chlg);
224:
225: if(status == SEC_E_INSUFFICIENT_MEMORY) {
226: return CURLE_OUT_OF_MEMORY;
227: }
228:
229: if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
230: return CURLE_AUTH_ERROR;
231: }
232:
233: if(memcmp(&context, krb5->context, sizeof(context))) {
234: s_pSecFn->DeleteSecurityContext(krb5->context);
235:
236: memcpy(krb5->context, &context, sizeof(context));
237: }
238:
239: if(resp_buf.cbBuffer) {
240: /* Base64 encode the response */
241: result = Curl_base64_encode(data, (char *) resp_buf.pvBuffer,
242: resp_buf.cbBuffer, outptr, outlen);
243: }
244: else if(mutual_auth) {
245: *outptr = strdup("");
246: if(!*outptr)
247: result = CURLE_OUT_OF_MEMORY;
248: }
249:
250: return result;
251: }
252:
253: /*
254: * Curl_auth_create_gssapi_security_message()
255: *
256: * This is used to generate an already encoded GSSAPI (Kerberos V5) security
257: * token message ready for sending to the recipient.
258: *
259: * Parameters:
260: *
261: * data [in] - The session handle.
262: * chlg64 [in] - The optional base64 encoded challenge message.
263: * krb5 [in/out] - The Kerberos 5 data struct being used and modified.
264: * outptr [in/out] - The address where a pointer to newly allocated memory
265: * holding the result will be stored upon completion.
266: * outlen [out] - The length of the output message.
267: *
268: * Returns CURLE_OK on success.
269: */
270: CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
271: const char *chlg64,
272: struct kerberos5data *krb5,
273: char **outptr,
274: size_t *outlen)
275: {
276: CURLcode result = CURLE_OK;
277: size_t offset = 0;
278: size_t chlglen = 0;
279: size_t messagelen = 0;
280: size_t appdatalen = 0;
281: unsigned char *chlg = NULL;
282: unsigned char *trailer = NULL;
283: unsigned char *message = NULL;
284: unsigned char *padding = NULL;
285: unsigned char *appdata = NULL;
286: SecBuffer input_buf[2];
287: SecBuffer wrap_buf[3];
288: SecBufferDesc input_desc;
289: SecBufferDesc wrap_desc;
290: unsigned long indata = 0;
291: unsigned long outdata = 0;
292: unsigned long qop = 0;
293: unsigned long sec_layer = 0;
294: unsigned long max_size = 0;
295: SecPkgContext_Sizes sizes;
296: SecPkgCredentials_Names names;
297: SECURITY_STATUS status;
298: char *user_name;
299:
300: /* Decode the base-64 encoded input message */
301: if(strlen(chlg64) && *chlg64 != '=') {
302: result = Curl_base64_decode(chlg64, &chlg, &chlglen);
303: if(result)
304: return result;
305: }
306:
307: /* Ensure we have a valid challenge message */
308: if(!chlg) {
309: infof(data, "GSSAPI handshake failure (empty security message)\n");
310:
311: return CURLE_BAD_CONTENT_ENCODING;
312: }
313:
314: /* Get our response size information */
315: status = s_pSecFn->QueryContextAttributes(krb5->context,
316: SECPKG_ATTR_SIZES,
317: &sizes);
318: if(status != SEC_E_OK) {
319: free(chlg);
320:
321: if(status == SEC_E_INSUFFICIENT_MEMORY)
322: return CURLE_OUT_OF_MEMORY;
323:
324: return CURLE_AUTH_ERROR;
325: }
326:
327: /* Get the fully qualified username back from the context */
328: status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
329: SECPKG_CRED_ATTR_NAMES,
330: &names);
331: if(status != SEC_E_OK) {
332: free(chlg);
333:
334: if(status == SEC_E_INSUFFICIENT_MEMORY)
335: return CURLE_OUT_OF_MEMORY;
336:
337: return CURLE_AUTH_ERROR;
338: }
339:
340: /* Setup the "input" security buffer */
341: input_desc.ulVersion = SECBUFFER_VERSION;
342: input_desc.cBuffers = 2;
343: input_desc.pBuffers = input_buf;
344: input_buf[0].BufferType = SECBUFFER_STREAM;
345: input_buf[0].pvBuffer = chlg;
346: input_buf[0].cbBuffer = curlx_uztoul(chlglen);
347: input_buf[1].BufferType = SECBUFFER_DATA;
348: input_buf[1].pvBuffer = NULL;
349: input_buf[1].cbBuffer = 0;
350:
351: /* Decrypt the inbound challenge and obtain the qop */
352: status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
353: if(status != SEC_E_OK) {
354: infof(data, "GSSAPI handshake failure (empty security message)\n");
355:
356: free(chlg);
357:
358: return CURLE_BAD_CONTENT_ENCODING;
359: }
360:
361: /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
362: if(input_buf[1].cbBuffer != 4) {
363: infof(data, "GSSAPI handshake failure (invalid security data)\n");
364:
365: free(chlg);
366:
367: return CURLE_BAD_CONTENT_ENCODING;
368: }
369:
370: /* Copy the data out and free the challenge as it is not required anymore */
371: memcpy(&indata, input_buf[1].pvBuffer, 4);
372: s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
373: free(chlg);
374:
375: /* Extract the security layer */
376: sec_layer = indata & 0x000000FF;
377: if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
378: infof(data, "GSSAPI handshake failure (invalid security layer)\n");
379:
380: return CURLE_BAD_CONTENT_ENCODING;
381: }
382:
383: /* Extract the maximum message size the server can receive */
384: max_size = ntohl(indata & 0xFFFFFF00);
385: if(max_size > 0) {
386: /* The server has told us it supports a maximum receive buffer, however, as
387: we don't require one unless we are encrypting data, we tell the server
388: our receive buffer is zero. */
389: max_size = 0;
390: }
391:
392: /* Allocate the trailer */
393: trailer = malloc(sizes.cbSecurityTrailer);
394: if(!trailer)
395: return CURLE_OUT_OF_MEMORY;
396:
397: /* Convert the user name to UTF8 when operating with Unicode */
398: user_name = Curl_convert_tchar_to_UTF8(names.sUserName);
399: if(!user_name) {
400: free(trailer);
401:
402: return CURLE_OUT_OF_MEMORY;
403: }
404:
405: /* Allocate our message */
406: messagelen = sizeof(outdata) + strlen(user_name) + 1;
407: message = malloc(messagelen);
408: if(!message) {
409: free(trailer);
410: Curl_unicodefree(user_name);
411:
412: return CURLE_OUT_OF_MEMORY;
413: }
414:
415: /* Populate the message with the security layer, client supported receive
416: message size and authorization identity including the 0x00 based
417: terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization
418: identity is not terminated with the zero-valued (%x00) octet." it seems
419: necessary to include it. */
420: outdata = htonl(max_size) | sec_layer;
421: memcpy(message, &outdata, sizeof(outdata));
422: strcpy((char *) message + sizeof(outdata), user_name);
423: Curl_unicodefree(user_name);
424:
425: /* Allocate the padding */
426: padding = malloc(sizes.cbBlockSize);
427: if(!padding) {
428: free(message);
429: free(trailer);
430:
431: return CURLE_OUT_OF_MEMORY;
432: }
433:
434: /* Setup the "authentication data" security buffer */
435: wrap_desc.ulVersion = SECBUFFER_VERSION;
436: wrap_desc.cBuffers = 3;
437: wrap_desc.pBuffers = wrap_buf;
438: wrap_buf[0].BufferType = SECBUFFER_TOKEN;
439: wrap_buf[0].pvBuffer = trailer;
440: wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer;
441: wrap_buf[1].BufferType = SECBUFFER_DATA;
442: wrap_buf[1].pvBuffer = message;
443: wrap_buf[1].cbBuffer = curlx_uztoul(messagelen);
444: wrap_buf[2].BufferType = SECBUFFER_PADDING;
445: wrap_buf[2].pvBuffer = padding;
446: wrap_buf[2].cbBuffer = sizes.cbBlockSize;
447:
448: /* Encrypt the data */
449: status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
450: &wrap_desc, 0);
451: if(status != SEC_E_OK) {
452: free(padding);
453: free(message);
454: free(trailer);
455:
456: if(status == SEC_E_INSUFFICIENT_MEMORY)
457: return CURLE_OUT_OF_MEMORY;
458:
459: return CURLE_AUTH_ERROR;
460: }
461:
462: /* Allocate the encryption (wrap) buffer */
463: appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer +
464: wrap_buf[2].cbBuffer;
465: appdata = malloc(appdatalen);
466: if(!appdata) {
467: free(padding);
468: free(message);
469: free(trailer);
470:
471: return CURLE_OUT_OF_MEMORY;
472: }
473:
474: /* Populate the encryption buffer */
475: memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer);
476: offset += wrap_buf[0].cbBuffer;
477: memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer);
478: offset += wrap_buf[1].cbBuffer;
479: memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer);
480:
481: /* Base64 encode the response */
482: result = Curl_base64_encode(data, (char *) appdata, appdatalen, outptr,
483: outlen);
484:
485: /* Free all of our local buffers */
486: free(appdata);
487: free(padding);
488: free(message);
489: free(trailer);
490:
491: return result;
492: }
493:
494: /*
495: * Curl_auth_cleanup_gssapi()
496: *
497: * This is used to clean up the GSSAPI (Kerberos V5) specific data.
498: *
499: * Parameters:
500: *
501: * krb5 [in/out] - The Kerberos 5 data struct being cleaned up.
502: *
503: */
504: void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
505: {
506: /* Free our security context */
507: if(krb5->context) {
508: s_pSecFn->DeleteSecurityContext(krb5->context);
509: free(krb5->context);
510: krb5->context = NULL;
511: }
512:
513: /* Free our credentials handle */
514: if(krb5->credentials) {
515: s_pSecFn->FreeCredentialsHandle(krb5->credentials);
516: free(krb5->credentials);
517: krb5->credentials = NULL;
518: }
519:
520: /* Free our identity */
521: Curl_sspi_free_identity(krb5->p_identity);
522: krb5->p_identity = NULL;
523:
524: /* Free the SPN and output token */
525: Curl_safefree(krb5->spn);
526: Curl_safefree(krb5->output_token);
527:
528: /* Reset any variables */
529: krb5->token_max = 0;
530: }
531:
532: #endif /* USE_WINDOWS_SSPI && USE_KERBEROS5*/
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>