1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2016, 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
38 /* The last #include files should be: */
39 #include "curl_memory.h"
40 #include "memdebug.h"
41
42 /*
43 * Curl_auth_decode_spnego_message()
44 *
45 * This is used to decode an already encoded SPNEGO (Negotiate) challenge
46 * message.
47 *
48 * Parameters:
49 *
50 * data [in] - The session handle.
51 * userp [in] - The user name in the format User or Domain\User.
52 * passdwp [in] - The user's password.
53 * service [in] - The service type such as http, smtp, pop or imap.
54 * host [in] - The host name.
55 * chlg64 [in] - The optional base64 encoded challenge message.
56 * nego [in/out] - The Negotiate data struct being used and modified.
57 *
58 * Returns CURLE_OK on success.
59 */
Curl_auth_decode_spnego_message(struct Curl_easy * data,const char * user,const char * password,const char * service,const char * host,const char * chlg64,struct negotiatedata * nego)60 CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
61 const char *user,
62 const char *password,
63 const char *service,
64 const char *host,
65 const char *chlg64,
66 struct negotiatedata *nego)
67 {
68 CURLcode result = CURLE_OK;
69 size_t chlglen = 0;
70 unsigned char *chlg = NULL;
71 PSecPkgInfo SecurityPackage;
72 SecBuffer chlg_buf;
73 SecBuffer resp_buf;
74 SecBufferDesc chlg_desc;
75 SecBufferDesc resp_desc;
76 unsigned long attrs;
77 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
78
79 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
80 (void) data;
81 #endif
82
83 if(nego->context && nego->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_auth_spnego_cleanup(nego);
88 return CURLE_LOGIN_DENIED;
89 }
90
91 if(!nego->spn) {
92 /* Generate our SPN */
93 nego->spn = Curl_auth_build_spn(service, host, NULL);
94 if(!nego->spn)
95 return CURLE_OUT_OF_MEMORY;
96 }
97
98 if(!nego->output_token) {
99 /* Query the security package for Negotiate */
100 nego->status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
101 TEXT(SP_NAME_NEGOTIATE),
102 &SecurityPackage);
103 if(nego->status != SEC_E_OK)
104 return CURLE_NOT_BUILT_IN;
105
106 nego->token_max = SecurityPackage->cbMaxToken;
107
108 /* Release the package buffer as it is not required anymore */
109 s_pSecFn->FreeContextBuffer(SecurityPackage);
110
111 /* Allocate our output buffer */
112 nego->output_token = malloc(nego->token_max);
113 if(!nego->output_token)
114 return CURLE_OUT_OF_MEMORY;
115 }
116
117 if(!nego->credentials) {
118 /* Do we have credientials to use or are we using single sign-on? */
119 if(user && *user) {
120 /* Populate our identity structure */
121 result = Curl_create_sspi_identity(user, password, &nego->identity);
122 if(result)
123 return result;
124
125 /* Allow proper cleanup of the identity structure */
126 nego->p_identity = &nego->identity;
127 }
128 else
129 /* Use the current Windows user */
130 nego->p_identity = NULL;
131
132 /* Allocate our credentials handle */
133 nego->credentials = malloc(sizeof(CredHandle));
134 if(!nego->credentials)
135 return CURLE_OUT_OF_MEMORY;
136
137 memset(nego->credentials, 0, sizeof(CredHandle));
138
139 /* Acquire our credentials handle */
140 nego->status =
141 s_pSecFn->AcquireCredentialsHandle(NULL,
142 (TCHAR *)TEXT(SP_NAME_NEGOTIATE),
143 SECPKG_CRED_OUTBOUND, NULL,
144 nego->p_identity, NULL, NULL,
145 nego->credentials, &expiry);
146 if(nego->status != SEC_E_OK)
147 return CURLE_LOGIN_DENIED;
148
149 /* Allocate our new context handle */
150 nego->context = malloc(sizeof(CtxtHandle));
151 if(!nego->context)
152 return CURLE_OUT_OF_MEMORY;
153
154 memset(nego->context, 0, sizeof(CtxtHandle));
155 }
156
157 if(chlg64 && *chlg64) {
158 /* Decode the base-64 encoded challenge message */
159 if(*chlg64 != '=') {
160 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
161 if(result)
162 return result;
163 }
164
165 /* Ensure we have a valid challenge message */
166 if(!chlg) {
167 infof(data, "SPNEGO handshake failure (empty challenge message)\n");
168
169 return CURLE_BAD_CONTENT_ENCODING;
170 }
171
172 /* Setup the challenge "input" security buffer */
173 chlg_desc.ulVersion = SECBUFFER_VERSION;
174 chlg_desc.cBuffers = 1;
175 chlg_desc.pBuffers = &chlg_buf;
176 chlg_buf.BufferType = SECBUFFER_TOKEN;
177 chlg_buf.pvBuffer = chlg;
178 chlg_buf.cbBuffer = curlx_uztoul(chlglen);
179 }
180
181 /* Setup the response "output" security buffer */
182 resp_desc.ulVersion = SECBUFFER_VERSION;
183 resp_desc.cBuffers = 1;
184 resp_desc.pBuffers = &resp_buf;
185 resp_buf.BufferType = SECBUFFER_TOKEN;
186 resp_buf.pvBuffer = nego->output_token;
187 resp_buf.cbBuffer = curlx_uztoul(nego->token_max);
188
189 /* Generate our challenge-response message */
190 nego->status = s_pSecFn->InitializeSecurityContext(nego->credentials,
191 chlg ? nego->context :
192 NULL,
193 nego->spn,
194 ISC_REQ_CONFIDENTIALITY,
195 0, SECURITY_NATIVE_DREP,
196 chlg ? &chlg_desc : NULL,
197 0, nego->context,
198 &resp_desc, &attrs,
199 &expiry);
200
201 /* Free the decoded challenge as it is not required anymore */
202 free(chlg);
203
204 if(GSS_ERROR(nego->status)) {
205 return CURLE_OUT_OF_MEMORY;
206 }
207
208 if(nego->status == SEC_I_COMPLETE_NEEDED ||
209 nego->status == SEC_I_COMPLETE_AND_CONTINUE) {
210 nego->status = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc);
211 if(GSS_ERROR(nego->status)) {
212 return CURLE_RECV_ERROR;
213 }
214 }
215
216 nego->output_token_length = resp_buf.cbBuffer;
217
218 return result;
219 }
220
221 /*
222 * Curl_auth_create_spnego_message()
223 *
224 * This is used to generate an already encoded SPNEGO (Negotiate) response
225 * message ready for sending to the recipient.
226 *
227 * Parameters:
228 *
229 * data [in] - The session handle.
230 * nego [in/out] - The Negotiate data struct being used and modified.
231 * outptr [in/out] - The address where a pointer to newly allocated memory
232 * holding the result will be stored upon completion.
233 * outlen [out] - The length of the output message.
234 *
235 * Returns CURLE_OK on success.
236 */
Curl_auth_create_spnego_message(struct Curl_easy * data,struct negotiatedata * nego,char ** outptr,size_t * outlen)237 CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
238 struct negotiatedata *nego,
239 char **outptr, size_t *outlen)
240 {
241 CURLcode result;
242
243 /* Base64 encode the already generated response */
244 result = Curl_base64_encode(data,
245 (const char*) nego->output_token,
246 nego->output_token_length,
247 outptr, outlen);
248
249 if(result)
250 return result;
251
252 if(!*outptr || !*outlen)
253 return CURLE_REMOTE_ACCESS_DENIED;
254
255 return CURLE_OK;
256 }
257
258 /*
259 * Curl_auth_spnego_cleanup()
260 *
261 * This is used to clean up the SPNEGO (Negotiate) specific data.
262 *
263 * Parameters:
264 *
265 * nego [in/out] - The Negotiate data struct being cleaned up.
266 *
267 */
Curl_auth_spnego_cleanup(struct negotiatedata * nego)268 void Curl_auth_spnego_cleanup(struct negotiatedata *nego)
269 {
270 /* Free our security context */
271 if(nego->context) {
272 s_pSecFn->DeleteSecurityContext(nego->context);
273 free(nego->context);
274 nego->context = NULL;
275 }
276
277 /* Free our credentials handle */
278 if(nego->credentials) {
279 s_pSecFn->FreeCredentialsHandle(nego->credentials);
280 free(nego->credentials);
281 nego->credentials = NULL;
282 }
283
284 /* Free our identity */
285 Curl_sspi_free_identity(nego->p_identity);
286 nego->p_identity = NULL;
287
288 /* Free the SPN and output token */
289 Curl_safefree(nego->spn);
290 Curl_safefree(nego->output_token);
291
292 /* Reset any variables */
293 nego->status = 0;
294 nego->token_max = 0;
295 }
296
297 #endif /* USE_WINDOWS_SSPI && USE_SPNEGO */
298