Annotation of embedaddon/curl/lib/vauth/spnego_sspi.c, revision 1.1.1.1
1.1 misho 1: /***************************************************************************
2: * _ _ ____ _
3: * Project ___| | | | _ \| |
4: * / __| | | | |_) | |
5: * | (__| |_| | _ <| |___
6: * \___|\___/|_| \_\_____|
7: *
8: * Copyright (C) 1998 - 2019, 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 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: * RFC4178 Simple and Protected GSS-API Negotiation Mechanism
22: *
23: ***************************************************************************/
24:
25: #include "curl_setup.h"
26:
27: #if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO)
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: #include "strerror.h"
38:
39: /* The last #include files should be: */
40: #include "curl_memory.h"
41: #include "memdebug.h"
42:
43: /*
44: * Curl_auth_is_spnego_supported()
45: *
46: * This is used to evaluate if SPNEGO (Negotiate) is supported.
47: *
48: * Parameters: None
49: *
50: * Returns TRUE if Negotiate is supported by Windows SSPI.
51: */
52: bool Curl_auth_is_spnego_supported(void)
53: {
54: PSecPkgInfo SecurityPackage;
55: SECURITY_STATUS status;
56:
57: /* Query the security package for Negotiate */
58: status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
59: TEXT(SP_NAME_NEGOTIATE),
60: &SecurityPackage);
61:
62: /* Release the package buffer as it is not required anymore */
63: if(status == SEC_E_OK) {
64: s_pSecFn->FreeContextBuffer(SecurityPackage);
65: }
66:
67:
68: return (status == SEC_E_OK ? TRUE : FALSE);
69: }
70:
71: /*
72: * Curl_auth_decode_spnego_message()
73: *
74: * This is used to decode an already encoded SPNEGO (Negotiate) challenge
75: * message.
76: *
77: * Parameters:
78: *
79: * data [in] - The session handle.
80: * user [in] - The user name in the format User or Domain\User.
81: * password [in] - The user's password.
82: * service [in] - The service type such as http, smtp, pop or imap.
83: * host [in] - The host name.
84: * chlg64 [in] - The optional base64 encoded challenge message.
85: * nego [in/out] - The Negotiate data struct being used and modified.
86: *
87: * Returns CURLE_OK on success.
88: */
89: CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
90: const char *user,
91: const char *password,
92: const char *service,
93: const char *host,
94: const char *chlg64,
95: struct negotiatedata *nego)
96: {
97: CURLcode result = CURLE_OK;
98: size_t chlglen = 0;
99: unsigned char *chlg = NULL;
100: PSecPkgInfo SecurityPackage;
101: SecBuffer chlg_buf[2];
102: SecBuffer resp_buf;
103: SecBufferDesc chlg_desc;
104: SecBufferDesc resp_desc;
105: unsigned long attrs;
106: TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
107:
108: #if defined(CURL_DISABLE_VERBOSE_STRINGS)
109: (void) data;
110: #endif
111:
112: if(nego->context && nego->status == SEC_E_OK) {
113: /* We finished successfully our part of authentication, but server
114: * rejected it (since we're again here). Exit with an error since we
115: * can't invent anything better */
116: Curl_auth_cleanup_spnego(nego);
117: return CURLE_LOGIN_DENIED;
118: }
119:
120: if(!nego->spn) {
121: /* Generate our SPN */
122: nego->spn = Curl_auth_build_spn(service, host, NULL);
123: if(!nego->spn)
124: return CURLE_OUT_OF_MEMORY;
125: }
126:
127: if(!nego->output_token) {
128: /* Query the security package for Negotiate */
129: nego->status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
130: TEXT(SP_NAME_NEGOTIATE),
131: &SecurityPackage);
132: if(nego->status != SEC_E_OK)
133: return CURLE_NOT_BUILT_IN;
134:
135: nego->token_max = SecurityPackage->cbMaxToken;
136:
137: /* Release the package buffer as it is not required anymore */
138: s_pSecFn->FreeContextBuffer(SecurityPackage);
139:
140: /* Allocate our output buffer */
141: nego->output_token = malloc(nego->token_max);
142: if(!nego->output_token)
143: return CURLE_OUT_OF_MEMORY;
144: }
145:
146: if(!nego->credentials) {
147: /* Do we have credentials to use or are we using single sign-on? */
148: if(user && *user) {
149: /* Populate our identity structure */
150: result = Curl_create_sspi_identity(user, password, &nego->identity);
151: if(result)
152: return result;
153:
154: /* Allow proper cleanup of the identity structure */
155: nego->p_identity = &nego->identity;
156: }
157: else
158: /* Use the current Windows user */
159: nego->p_identity = NULL;
160:
161: /* Allocate our credentials handle */
162: nego->credentials = calloc(1, sizeof(CredHandle));
163: if(!nego->credentials)
164: return CURLE_OUT_OF_MEMORY;
165:
166: /* Acquire our credentials handle */
167: nego->status =
168: s_pSecFn->AcquireCredentialsHandle(NULL,
169: (TCHAR *)TEXT(SP_NAME_NEGOTIATE),
170: SECPKG_CRED_OUTBOUND, NULL,
171: nego->p_identity, NULL, NULL,
172: nego->credentials, &expiry);
173: if(nego->status != SEC_E_OK)
174: return CURLE_AUTH_ERROR;
175:
176: /* Allocate our new context handle */
177: nego->context = calloc(1, sizeof(CtxtHandle));
178: if(!nego->context)
179: return CURLE_OUT_OF_MEMORY;
180: }
181:
182: if(chlg64 && *chlg64) {
183: /* Decode the base-64 encoded challenge message */
184: if(*chlg64 != '=') {
185: result = Curl_base64_decode(chlg64, &chlg, &chlglen);
186: if(result)
187: return result;
188: }
189:
190: /* Ensure we have a valid challenge message */
191: if(!chlg) {
192: infof(data, "SPNEGO handshake failure (empty challenge message)\n");
193:
194: return CURLE_BAD_CONTENT_ENCODING;
195: }
196:
197: /* Setup the challenge "input" security buffer */
198: chlg_desc.ulVersion = SECBUFFER_VERSION;
199: chlg_desc.cBuffers = 1;
200: chlg_desc.pBuffers = &chlg_buf[0];
201: chlg_buf[0].BufferType = SECBUFFER_TOKEN;
202: chlg_buf[0].pvBuffer = chlg;
203: chlg_buf[0].cbBuffer = curlx_uztoul(chlglen);
204:
205: #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
206: /* ssl context comes from Schannel.
207: * When extended protection is used in IIS server,
208: * we have to pass a second SecBuffer to the SecBufferDesc
209: * otherwise IIS will not pass the authentication (401 response).
210: * Minimum supported version is Windows 7.
211: * https://docs.microsoft.com/en-us/security-updates
212: * /SecurityAdvisories/2009/973811
213: */
214: if(nego->sslContext) {
215: SEC_CHANNEL_BINDINGS channelBindings;
216: SecPkgContext_Bindings pkgBindings;
217: pkgBindings.Bindings = &channelBindings;
218: nego->status = s_pSecFn->QueryContextAttributes(
219: nego->sslContext,
220: SECPKG_ATTR_ENDPOINT_BINDINGS,
221: &pkgBindings
222: );
223: if(nego->status == SEC_E_OK) {
224: chlg_desc.cBuffers++;
225: chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS;
226: chlg_buf[1].cbBuffer = pkgBindings.BindingsLength;
227: chlg_buf[1].pvBuffer = pkgBindings.Bindings;
228: }
229: }
230: #endif
231: }
232:
233: /* Setup the response "output" security buffer */
234: resp_desc.ulVersion = SECBUFFER_VERSION;
235: resp_desc.cBuffers = 1;
236: resp_desc.pBuffers = &resp_buf;
237: resp_buf.BufferType = SECBUFFER_TOKEN;
238: resp_buf.pvBuffer = nego->output_token;
239: resp_buf.cbBuffer = curlx_uztoul(nego->token_max);
240:
241: /* Generate our challenge-response message */
242: nego->status = s_pSecFn->InitializeSecurityContext(nego->credentials,
243: chlg ? nego->context :
244: NULL,
245: nego->spn,
246: ISC_REQ_CONFIDENTIALITY,
247: 0, SECURITY_NATIVE_DREP,
248: chlg ? &chlg_desc : NULL,
249: 0, nego->context,
250: &resp_desc, &attrs,
251: &expiry);
252:
253: /* Free the decoded challenge as it is not required anymore */
254: free(chlg);
255:
256: if(GSS_ERROR(nego->status)) {
257: char buffer[STRERROR_LEN];
258: failf(data, "InitializeSecurityContext failed: %s",
259: Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
260:
261: if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
262: return CURLE_OUT_OF_MEMORY;
263:
264: return CURLE_AUTH_ERROR;
265: }
266:
267: if(nego->status == SEC_I_COMPLETE_NEEDED ||
268: nego->status == SEC_I_COMPLETE_AND_CONTINUE) {
269: nego->status = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc);
270: if(GSS_ERROR(nego->status)) {
271: char buffer[STRERROR_LEN];
272: failf(data, "CompleteAuthToken failed: %s",
273: Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
274:
275: if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
276: return CURLE_OUT_OF_MEMORY;
277:
278: return CURLE_AUTH_ERROR;
279: }
280: }
281:
282: nego->output_token_length = resp_buf.cbBuffer;
283:
284: return result;
285: }
286:
287: /*
288: * Curl_auth_create_spnego_message()
289: *
290: * This is used to generate an already encoded SPNEGO (Negotiate) response
291: * message ready for sending to the recipient.
292: *
293: * Parameters:
294: *
295: * data [in] - The session handle.
296: * nego [in/out] - The Negotiate data struct being used and modified.
297: * outptr [in/out] - The address where a pointer to newly allocated memory
298: * holding the result will be stored upon completion.
299: * outlen [out] - The length of the output message.
300: *
301: * Returns CURLE_OK on success.
302: */
303: CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
304: struct negotiatedata *nego,
305: char **outptr, size_t *outlen)
306: {
307: CURLcode result;
308:
309: /* Base64 encode the already generated response */
310: result = Curl_base64_encode(data,
311: (const char *) nego->output_token,
312: nego->output_token_length,
313: outptr, outlen);
314:
315: if(result)
316: return result;
317:
318: if(!*outptr || !*outlen) {
319: free(*outptr);
320: return CURLE_REMOTE_ACCESS_DENIED;
321: }
322:
323: return CURLE_OK;
324: }
325:
326: /*
327: * Curl_auth_cleanup_spnego()
328: *
329: * This is used to clean up the SPNEGO (Negotiate) specific data.
330: *
331: * Parameters:
332: *
333: * nego [in/out] - The Negotiate data struct being cleaned up.
334: *
335: */
336: void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
337: {
338: /* Free our security context */
339: if(nego->context) {
340: s_pSecFn->DeleteSecurityContext(nego->context);
341: free(nego->context);
342: nego->context = NULL;
343: }
344:
345: /* Free our credentials handle */
346: if(nego->credentials) {
347: s_pSecFn->FreeCredentialsHandle(nego->credentials);
348: free(nego->credentials);
349: nego->credentials = NULL;
350: }
351:
352: /* Free our identity */
353: Curl_sspi_free_identity(nego->p_identity);
354: nego->p_identity = NULL;
355:
356: /* Free the SPN and output token */
357: Curl_safefree(nego->spn);
358: Curl_safefree(nego->output_token);
359:
360: /* Reset any variables */
361: nego->status = 0;
362: nego->token_max = 0;
363: nego->noauthpersist = FALSE;
364: nego->havenoauthpersist = FALSE;
365: nego->havenegdata = FALSE;
366: nego->havemultiplerequests = FALSE;
367: }
368:
369: #endif /* USE_WINDOWS_SSPI && USE_SPNEGO */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>