1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
25 #ifdef USE_WINDOWS_SSPI
27 #if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO)
33 #include "curl_base64.h"
34 #include "curl_sasl.h"
35 #include "http_negotiate.h"
36 #include "curl_multibyte.h"
37 #include "curl_printf.h"
39 /* The last #include files should be: */
40 #include "curl_memory.h"
43 CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,
46 struct SessionHandle *data = conn->data;
47 BYTE *input_token = NULL;
48 SecBufferDesc out_buff_desc;
49 SecBuffer out_sec_buff;
50 SecBufferDesc in_buff_desc;
51 SecBuffer in_sec_buff;
52 SECURITY_STATUS status;
54 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
55 size_t len = 0, input_token_len = 0;
58 /* Point to the username and password */
62 /* Point to the correct struct with this */
63 struct negotiatedata *neg_ctx;
66 userp = conn->proxyuser;
67 passwdp = conn->proxypasswd;
68 neg_ctx = &data->state.proxyneg;
72 passwdp = conn->passwd;
73 neg_ctx = &data->state.negotiate;
76 /* Not set means empty */
83 if(neg_ctx->context && neg_ctx->status == SEC_E_OK) {
84 /* We finished successfully our part of authentication, but server
85 * rejected it (since we're again here). Exit with an error since we
86 * can't invent anything better */
87 Curl_cleanup_negotiate(data);
88 return CURLE_LOGIN_DENIED;
91 if(!neg_ctx->server_name) {
92 /* Check proxy auth requested but no given proxy name */
93 if(proxy && !conn->proxy.name)
94 return CURLE_BAD_FUNCTION_ARGUMENT;
96 /* Generate our SPN */
97 neg_ctx->server_name = Curl_sasl_build_spn(
98 proxy ? data->set.str[STRING_PROXY_SERVICE_NAME] :
99 data->set.str[STRING_SERVICE_NAME],
100 proxy ? conn->proxy.name : conn->host.name);
101 if(!neg_ctx->server_name)
102 return CURLE_OUT_OF_MEMORY;
105 if(!neg_ctx->output_token) {
106 PSecPkgInfo SecurityPackage;
107 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
108 TEXT(SP_NAME_NEGOTIATE),
110 if(status != SEC_E_OK)
111 return CURLE_NOT_BUILT_IN;
113 /* Allocate input and output buffers according to the max token size
114 as indicated by the security package */
115 neg_ctx->token_max = SecurityPackage->cbMaxToken;
116 neg_ctx->output_token = malloc(neg_ctx->token_max);
117 s_pSecFn->FreeContextBuffer(SecurityPackage);
120 /* Obtain the input token, if any */
121 header += strlen("Negotiate");
122 while(*header && ISSPACE(*header))
125 len = strlen(header);
127 /* Is this the first call in a new negotiation? */
128 if(neg_ctx->context) {
129 /* The server rejected our authentication and hasn't suppled any more
130 negotiation mechanisms */
131 return CURLE_LOGIN_DENIED;
134 /* We have to acquire credentials and allocate memory for the context */
135 neg_ctx->credentials = malloc(sizeof(CredHandle));
136 neg_ctx->context = malloc(sizeof(CtxtHandle));
138 if(!neg_ctx->credentials || !neg_ctx->context)
139 return CURLE_OUT_OF_MEMORY;
141 if(userp && *userp) {
142 /* Populate our identity structure */
143 result = Curl_create_sspi_identity(userp, passwdp, &neg_ctx->identity);
147 /* Allow proper cleanup of the identity structure */
148 neg_ctx->p_identity = &neg_ctx->identity;
151 /* Use the current Windows user */
152 neg_ctx->p_identity = NULL;
154 /* Acquire our credientials handle */
156 s_pSecFn->AcquireCredentialsHandle(NULL,
157 (TCHAR *) TEXT(SP_NAME_NEGOTIATE),
158 SECPKG_CRED_OUTBOUND, NULL,
159 neg_ctx->p_identity, NULL, NULL,
160 neg_ctx->credentials, &expiry);
161 if(neg_ctx->status != SEC_E_OK)
162 return CURLE_LOGIN_DENIED;
165 result = Curl_base64_decode(header,
166 (unsigned char **)&input_token,
171 if(!input_token_len) {
173 "Negotiate handshake failure (empty challenge message)\n");
175 return CURLE_BAD_CONTENT_ENCODING;
179 /* Setup the "output" security buffer */
180 out_buff_desc.ulVersion = SECBUFFER_VERSION;
181 out_buff_desc.cBuffers = 1;
182 out_buff_desc.pBuffers = &out_sec_buff;
183 out_sec_buff.BufferType = SECBUFFER_TOKEN;
184 out_sec_buff.pvBuffer = neg_ctx->output_token;
185 out_sec_buff.cbBuffer = curlx_uztoul(neg_ctx->token_max);
187 /* Setup the "input" security buffer if present */
189 in_buff_desc.ulVersion = SECBUFFER_VERSION;
190 in_buff_desc.cBuffers = 1;
191 in_buff_desc.pBuffers = &in_sec_buff;
192 in_sec_buff.BufferType = SECBUFFER_TOKEN;
193 in_sec_buff.pvBuffer = input_token;
194 in_sec_buff.cbBuffer = curlx_uztoul(input_token_len);
197 /* Generate our message */
198 neg_ctx->status = s_pSecFn->InitializeSecurityContext(
199 neg_ctx->credentials,
200 input_token ? neg_ctx->context : NULL,
201 neg_ctx->server_name,
202 ISC_REQ_CONFIDENTIALITY,
204 SECURITY_NATIVE_DREP,
205 input_token ? &in_buff_desc : NULL,
214 if(GSS_ERROR(neg_ctx->status))
215 return CURLE_OUT_OF_MEMORY;
217 if(neg_ctx->status == SEC_I_COMPLETE_NEEDED ||
218 neg_ctx->status == SEC_I_COMPLETE_AND_CONTINUE) {
219 neg_ctx->status = s_pSecFn->CompleteAuthToken(neg_ctx->context,
221 if(GSS_ERROR(neg_ctx->status))
222 return CURLE_RECV_ERROR;
225 neg_ctx->output_token_length = out_sec_buff.cbBuffer;
230 CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
232 struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
233 &conn->data->state.negotiate;
234 char *encoded = NULL;
239 result = Curl_base64_encode(conn->data,
240 (const char*)neg_ctx->output_token,
241 neg_ctx->output_token_length,
247 return CURLE_REMOTE_ACCESS_DENIED;
249 userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "",
253 Curl_safefree(conn->allocptr.proxyuserpwd);
254 conn->allocptr.proxyuserpwd = userp;
257 Curl_safefree(conn->allocptr.userpwd);
258 conn->allocptr.userpwd = userp;
263 return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
266 static void cleanup(struct negotiatedata *neg_ctx)
268 /* Free our security context */
269 if(neg_ctx->context) {
270 s_pSecFn->DeleteSecurityContext(neg_ctx->context);
271 free(neg_ctx->context);
272 neg_ctx->context = NULL;
275 /* Free our credentials handle */
276 if(neg_ctx->credentials) {
277 s_pSecFn->FreeCredentialsHandle(neg_ctx->credentials);
278 free(neg_ctx->credentials);
279 neg_ctx->credentials = NULL;
282 /* Free our identity */
283 Curl_sspi_free_identity(neg_ctx->p_identity);
284 neg_ctx->p_identity = NULL;
286 /* Free the SPN and output token */
287 Curl_safefree(neg_ctx->server_name);
288 Curl_safefree(neg_ctx->output_token);
290 /* Reset any variables */
291 neg_ctx->token_max = 0;
294 void Curl_cleanup_negotiate(struct SessionHandle *data)
296 cleanup(&data->state.negotiate);
297 cleanup(&data->state.proxyneg);
300 #endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */
302 #endif /* USE_WINDOWS_SSPI */