Annotation of embedaddon/curl/lib/vauth/krb5_gssapi.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: * 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: * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
23: *
24: ***************************************************************************/
25:
26: #include "curl_setup.h"
27:
28: #if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5)
29:
30: #include <curl/curl.h>
31:
32: #include "vauth/vauth.h"
33: #include "curl_sasl.h"
34: #include "urldata.h"
35: #include "curl_base64.h"
36: #include "curl_gssapi.h"
37: #include "sendf.h"
38: #include "curl_printf.h"
39:
40: /* The last #include files should be: */
41: #include "curl_memory.h"
42: #include "memdebug.h"
43:
44: /*
45: * Curl_auth_is_gssapi_supported()
46: *
47: * This is used to evaluate if GSSAPI (Kerberos V5) is supported.
48: *
49: * Parameters: None
50: *
51: * Returns TRUE if Kerberos V5 is supported by the GSS-API library.
52: */
53: bool Curl_auth_is_gssapi_supported(void)
54: {
55: return TRUE;
56: }
57:
58: /*
59: * Curl_auth_create_gssapi_user_message()
60: *
61: * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
62: * message ready for sending to the recipient.
63: *
64: * Parameters:
65: *
66: * data [in] - The session handle.
67: * userp [in] - The user name.
68: * passwdp [in] - The user's password.
69: * service [in] - The service type such as http, smtp, pop or imap.
70: * host [in[ - The host name.
71: * mutual_auth [in] - Flag specifying whether or not mutual authentication
72: * is enabled.
73: * chlg64 [in] - Pointer to the optional base64 encoded challenge
74: * message.
75: * krb5 [in/out] - The Kerberos 5 data struct being used and modified.
76: * outptr [in/out] - The address where a pointer to newly allocated memory
77: * holding the result will be stored upon completion.
78: * outlen [out] - The length of the output message.
79: *
80: * Returns CURLE_OK on success.
81: */
82: CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
83: const char *userp,
84: const char *passwdp,
85: const char *service,
86: const char *host,
87: const bool mutual_auth,
88: const char *chlg64,
89: struct kerberos5data *krb5,
90: char **outptr, size_t *outlen)
91: {
92: CURLcode result = CURLE_OK;
93: size_t chlglen = 0;
94: unsigned char *chlg = NULL;
95: OM_uint32 major_status;
96: OM_uint32 minor_status;
97: OM_uint32 unused_status;
98: gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
99: gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
100: gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
101:
102: (void) userp;
103: (void) passwdp;
104:
105: if(!krb5->spn) {
106: /* Generate our SPN */
107: char *spn = Curl_auth_build_spn(service, NULL, host);
108: if(!spn)
109: return CURLE_OUT_OF_MEMORY;
110:
111: /* Populate the SPN structure */
112: spn_token.value = spn;
113: spn_token.length = strlen(spn);
114:
115: /* Import the SPN */
116: major_status = gss_import_name(&minor_status, &spn_token,
117: GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn);
118: if(GSS_ERROR(major_status)) {
119: Curl_gss_log_error(data, "gss_import_name() failed: ",
120: major_status, minor_status);
121:
122: free(spn);
123:
124: return CURLE_AUTH_ERROR;
125: }
126:
127: free(spn);
128: }
129:
130: if(chlg64 && *chlg64) {
131: /* Decode the base-64 encoded challenge message */
132: if(*chlg64 != '=') {
133: result = Curl_base64_decode(chlg64, &chlg, &chlglen);
134: if(result)
135: return result;
136: }
137:
138: /* Ensure we have a valid challenge message */
139: if(!chlg) {
140: infof(data, "GSSAPI handshake failure (empty challenge message)\n");
141:
142: return CURLE_BAD_CONTENT_ENCODING;
143: }
144:
145: /* Setup the challenge "input" security buffer */
146: input_token.value = chlg;
147: input_token.length = chlglen;
148: }
149:
150: major_status = Curl_gss_init_sec_context(data,
151: &minor_status,
152: &krb5->context,
153: krb5->spn,
154: &Curl_krb5_mech_oid,
155: GSS_C_NO_CHANNEL_BINDINGS,
156: &input_token,
157: &output_token,
158: mutual_auth,
159: NULL);
160:
161: /* Free the decoded challenge as it is not required anymore */
162: free(input_token.value);
163:
164: if(GSS_ERROR(major_status)) {
165: if(output_token.value)
166: gss_release_buffer(&unused_status, &output_token);
167:
168: Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
169: major_status, minor_status);
170:
171: return CURLE_AUTH_ERROR;
172: }
173:
174: if(output_token.value && output_token.length) {
175: /* Base64 encode the response */
176: result = Curl_base64_encode(data, (char *) output_token.value,
177: output_token.length, outptr, outlen);
178:
179: gss_release_buffer(&unused_status, &output_token);
180: }
181: else if(mutual_auth) {
182: *outptr = strdup("");
183: if(!*outptr)
184: result = CURLE_OUT_OF_MEMORY;
185: }
186:
187: return result;
188: }
189:
190: /*
191: * Curl_auth_create_gssapi_security_message()
192: *
193: * This is used to generate an already encoded GSSAPI (Kerberos V5) security
194: * token message ready for sending to the recipient.
195: *
196: * Parameters:
197: *
198: * data [in] - The session handle.
199: * chlg64 [in] - Pointer to the optional base64 encoded challenge message.
200: * krb5 [in/out] - The Kerberos 5 data struct being used and modified.
201: * outptr [in/out] - The address where a pointer to newly allocated memory
202: * holding the result will be stored upon completion.
203: * outlen [out] - The length of the output message.
204: *
205: * Returns CURLE_OK on success.
206: */
207: CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
208: const char *chlg64,
209: struct kerberos5data *krb5,
210: char **outptr,
211: size_t *outlen)
212: {
213: CURLcode result = CURLE_OK;
214: size_t chlglen = 0;
215: size_t messagelen = 0;
216: unsigned char *chlg = NULL;
217: unsigned char *message = NULL;
218: OM_uint32 major_status;
219: OM_uint32 minor_status;
220: OM_uint32 unused_status;
221: gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
222: gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
223: unsigned int indata = 0;
224: unsigned int outdata = 0;
225: gss_qop_t qop = GSS_C_QOP_DEFAULT;
226: unsigned int sec_layer = 0;
227: unsigned int max_size = 0;
228: gss_name_t username = GSS_C_NO_NAME;
229: gss_buffer_desc username_token;
230:
231: /* Decode the base-64 encoded input message */
232: if(strlen(chlg64) && *chlg64 != '=') {
233: result = Curl_base64_decode(chlg64, &chlg, &chlglen);
234: if(result)
235: return result;
236: }
237:
238: /* Ensure we have a valid challenge message */
239: if(!chlg) {
240: infof(data, "GSSAPI handshake failure (empty security message)\n");
241:
242: return CURLE_BAD_CONTENT_ENCODING;
243: }
244:
245: /* Get the fully qualified username back from the context */
246: major_status = gss_inquire_context(&minor_status, krb5->context,
247: &username, NULL, NULL, NULL, NULL,
248: NULL, NULL);
249: if(GSS_ERROR(major_status)) {
250: Curl_gss_log_error(data, "gss_inquire_context() failed: ",
251: major_status, minor_status);
252:
253: free(chlg);
254:
255: return CURLE_AUTH_ERROR;
256: }
257:
258: /* Convert the username from internal format to a displayable token */
259: major_status = gss_display_name(&minor_status, username,
260: &username_token, NULL);
261: if(GSS_ERROR(major_status)) {
262: Curl_gss_log_error(data, "gss_display_name() failed: ",
263: major_status, minor_status);
264:
265: free(chlg);
266:
267: return CURLE_AUTH_ERROR;
268: }
269:
270: /* Setup the challenge "input" security buffer */
271: input_token.value = chlg;
272: input_token.length = chlglen;
273:
274: /* Decrypt the inbound challenge and obtain the qop */
275: major_status = gss_unwrap(&minor_status, krb5->context, &input_token,
276: &output_token, NULL, &qop);
277: if(GSS_ERROR(major_status)) {
278: Curl_gss_log_error(data, "gss_unwrap() failed: ",
279: major_status, minor_status);
280:
281: gss_release_buffer(&unused_status, &username_token);
282: free(chlg);
283:
284: return CURLE_BAD_CONTENT_ENCODING;
285: }
286:
287: /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
288: if(output_token.length != 4) {
289: infof(data, "GSSAPI handshake failure (invalid security data)\n");
290:
291: gss_release_buffer(&unused_status, &username_token);
292: free(chlg);
293:
294: return CURLE_BAD_CONTENT_ENCODING;
295: }
296:
297: /* Copy the data out and free the challenge as it is not required anymore */
298: memcpy(&indata, output_token.value, 4);
299: gss_release_buffer(&unused_status, &output_token);
300: free(chlg);
301:
302: /* Extract the security layer */
303: sec_layer = indata & 0x000000FF;
304: if(!(sec_layer & GSSAUTH_P_NONE)) {
305: infof(data, "GSSAPI handshake failure (invalid security layer)\n");
306:
307: gss_release_buffer(&unused_status, &username_token);
308:
309: return CURLE_BAD_CONTENT_ENCODING;
310: }
311:
312: /* Extract the maximum message size the server can receive */
313: max_size = ntohl(indata & 0xFFFFFF00);
314: if(max_size > 0) {
315: /* The server has told us it supports a maximum receive buffer, however, as
316: we don't require one unless we are encrypting data, we tell the server
317: our receive buffer is zero. */
318: max_size = 0;
319: }
320:
321: /* Allocate our message */
322: messagelen = sizeof(outdata) + username_token.length + 1;
323: message = malloc(messagelen);
324: if(!message) {
325: gss_release_buffer(&unused_status, &username_token);
326:
327: return CURLE_OUT_OF_MEMORY;
328: }
329:
330: /* Populate the message with the security layer, client supported receive
331: message size and authorization identity including the 0x00 based
332: terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization
333: identity is not terminated with the zero-valued (%x00) octet." it seems
334: necessary to include it. */
335: outdata = htonl(max_size) | sec_layer;
336: memcpy(message, &outdata, sizeof(outdata));
337: memcpy(message + sizeof(outdata), username_token.value,
338: username_token.length);
339: message[messagelen - 1] = '\0';
340:
341: /* Free the username token as it is not required anymore */
342: gss_release_buffer(&unused_status, &username_token);
343:
344: /* Setup the "authentication data" security buffer */
345: input_token.value = message;
346: input_token.length = messagelen;
347:
348: /* Encrypt the data */
349: major_status = gss_wrap(&minor_status, krb5->context, 0,
350: GSS_C_QOP_DEFAULT, &input_token, NULL,
351: &output_token);
352: if(GSS_ERROR(major_status)) {
353: Curl_gss_log_error(data, "gss_wrap() failed: ",
354: major_status, minor_status);
355:
356: free(message);
357:
358: return CURLE_AUTH_ERROR;
359: }
360:
361: /* Base64 encode the response */
362: result = Curl_base64_encode(data, (char *) output_token.value,
363: output_token.length, outptr, outlen);
364:
365: /* Free the output buffer */
366: gss_release_buffer(&unused_status, &output_token);
367:
368: /* Free the message buffer */
369: free(message);
370:
371: return result;
372: }
373:
374: /*
375: * Curl_auth_cleanup_gssapi()
376: *
377: * This is used to clean up the GSSAPI (Kerberos V5) specific data.
378: *
379: * Parameters:
380: *
381: * krb5 [in/out] - The Kerberos 5 data struct being cleaned up.
382: *
383: */
384: void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
385: {
386: OM_uint32 minor_status;
387:
388: /* Free our security context */
389: if(krb5->context != GSS_C_NO_CONTEXT) {
390: gss_delete_sec_context(&minor_status, &krb5->context, GSS_C_NO_BUFFER);
391: krb5->context = GSS_C_NO_CONTEXT;
392: }
393:
394: /* Free the SPN */
395: if(krb5->spn != GSS_C_NO_NAME) {
396: gss_release_name(&minor_status, &krb5->spn);
397: krb5->spn = GSS_C_NO_NAME;
398: }
399: }
400:
401: #endif /* HAVE_GSSAPI && USE_KERBEROS5 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>