1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2014, 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 http://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_memory.h"
37 #include "curl_multibyte.h"
39 #define _MPRINTF_REPLACE /* use our functions only */
40 #include <curl/mprintf.h>
42 /* The last #include file should be: */
45 /* returning zero (0) means success, everything else is treated as "failure"
46 with no care exactly what the failure was */
47 int Curl_input_negotiate(struct connectdata *conn, bool proxy,
50 BYTE *input_token = NULL;
51 SecBufferDesc out_buff_desc;
52 SecBuffer out_sec_buff;
53 SecBufferDesc in_buff_desc;
54 SecBuffer in_sec_buff;
55 unsigned long context_attributes;
58 size_t len = 0, input_token_len = 0;
61 /* Point to the username and password */
65 /* Point to the correct struct with this */
66 struct negotiatedata *neg_ctx;
69 userp = conn->proxyuser;
70 passwdp = conn->proxypasswd;
71 neg_ctx = &conn->data->state.proxyneg;
75 passwdp = conn->passwd;
76 neg_ctx = &conn->data->state.negotiate;
79 /* Not set means empty */
86 if(neg_ctx->context && neg_ctx->status == SEC_E_OK) {
87 /* We finished successfully our part of authentication, but server
88 * rejected it (since we're again here). Exit with an error since we
89 * can't invent anything better */
90 Curl_cleanup_negotiate(conn->data);
94 if(!neg_ctx->server_name) {
95 /* Check proxy auth requested but no given proxy name */
96 if(proxy && !conn->proxy.name)
99 /* Generate our SPN */
100 neg_ctx->server_name = Curl_sasl_build_spn("HTTP",
101 proxy ? conn->proxy.name :
103 if(!neg_ctx->server_name)
107 if(!neg_ctx->output_token) {
108 PSecPkgInfo SecurityPackage;
109 ret = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NEGOTIATE),
114 /* Allocate input and output buffers according to the max token size
115 as indicated by the security package */
116 neg_ctx->token_max = SecurityPackage->cbMaxToken;
117 neg_ctx->output_token = malloc(neg_ctx->token_max);
118 s_pSecFn->FreeContextBuffer(SecurityPackage);
121 /* Obtain the input token, if any */
122 header += strlen("Negotiate");
123 while(*header && ISSPACE(*header))
126 len = strlen(header);
128 /* Is this the first call in a new negotiation? */
129 if(neg_ctx->context) {
130 /* The server rejected our authentication and hasn't suppled any more
131 negotiation mechanisms */
135 /* We have to acquire credentials and allocate memory for the context */
136 neg_ctx->credentials = malloc(sizeof(CredHandle));
137 neg_ctx->context = malloc(sizeof(CtxtHandle));
139 if(!neg_ctx->credentials || !neg_ctx->context)
142 if(userp && *userp) {
143 /* Populate our identity structure */
144 error = Curl_create_sspi_identity(userp, passwdp, &neg_ctx->identity);
148 /* Allow proper cleanup of the identity structure */
149 neg_ctx->p_identity = &neg_ctx->identity;
152 /* Use the current Windows user */
153 neg_ctx->p_identity = NULL;
155 /* Acquire our credientials handle */
157 s_pSecFn->AcquireCredentialsHandle(NULL,
158 (TCHAR *) TEXT(SP_NAME_NEGOTIATE),
159 SECPKG_CRED_OUTBOUND, NULL,
160 neg_ctx->p_identity, NULL, NULL,
161 neg_ctx->credentials, &expiry);
162 if(neg_ctx->status != SEC_E_OK)
166 error = Curl_base64_decode(header,
167 (unsigned char **)&input_token,
169 if(error || !input_token_len)
173 /* Setup the "output" security buffer */
174 out_buff_desc.ulVersion = SECBUFFER_VERSION;
175 out_buff_desc.cBuffers = 1;
176 out_buff_desc.pBuffers = &out_sec_buff;
177 out_sec_buff.BufferType = SECBUFFER_TOKEN;
178 out_sec_buff.pvBuffer = neg_ctx->output_token;
179 out_sec_buff.cbBuffer = curlx_uztoul(neg_ctx->token_max);
181 /* Setup the "input" security buffer if present */
183 in_buff_desc.ulVersion = SECBUFFER_VERSION;
184 in_buff_desc.cBuffers = 1;
185 in_buff_desc.pBuffers = &in_sec_buff;
186 in_sec_buff.BufferType = SECBUFFER_TOKEN;
187 in_sec_buff.pvBuffer = input_token;
188 in_sec_buff.cbBuffer = curlx_uztoul(input_token_len);
191 /* Generate our message */
192 neg_ctx->status = s_pSecFn->InitializeSecurityContext(
193 neg_ctx->credentials,
194 input_token ? neg_ctx->context : NULL,
195 neg_ctx->server_name,
196 ISC_REQ_CONFIDENTIALITY,
198 SECURITY_NATIVE_DREP,
199 input_token ? &in_buff_desc : NULL,
206 Curl_safefree(input_token);
208 if(GSS_ERROR(neg_ctx->status))
211 if(neg_ctx->status == SEC_I_COMPLETE_NEEDED ||
212 neg_ctx->status == SEC_I_COMPLETE_AND_CONTINUE) {
213 neg_ctx->status = s_pSecFn->CompleteAuthToken(neg_ctx->context,
215 if(GSS_ERROR(neg_ctx->status))
219 neg_ctx->output_token_length = out_sec_buff.cbBuffer;
225 CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
227 struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
228 &conn->data->state.negotiate;
229 char *encoded = NULL;
234 error = Curl_base64_encode(conn->data,
235 (const char*)neg_ctx->output_token,
236 neg_ctx->output_token_length,
242 return CURLE_REMOTE_ACCESS_DENIED;
244 userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "",
248 Curl_safefree(conn->allocptr.proxyuserpwd);
249 conn->allocptr.proxyuserpwd = userp;
252 Curl_safefree(conn->allocptr.userpwd);
253 conn->allocptr.userpwd = userp;
256 return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
259 static void cleanup(struct negotiatedata *neg_ctx)
261 /* Free our security context */
262 if(neg_ctx->context) {
263 s_pSecFn->DeleteSecurityContext(neg_ctx->context);
264 free(neg_ctx->context);
265 neg_ctx->context = NULL;
268 /* Free our credentials handle */
269 if(neg_ctx->credentials) {
270 s_pSecFn->FreeCredentialsHandle(neg_ctx->credentials);
271 free(neg_ctx->credentials);
272 neg_ctx->credentials = NULL;
275 /* Free our identity */
276 Curl_sspi_free_identity(neg_ctx->p_identity);
277 neg_ctx->p_identity = NULL;
279 /* Free the SPN and output token */
280 Curl_safefree(neg_ctx->server_name);
281 Curl_safefree(neg_ctx->output_token);
283 /* Reset any variables */
284 neg_ctx->token_max = 0;
287 void Curl_cleanup_negotiate(struct SessionHandle *data)
289 cleanup(&data->state.negotiate);
290 cleanup(&data->state.proxyneg);
293 #endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */
295 #endif /* USE_WINDOWS_SSPI */