1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2015, 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 http://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 ***************************************************************************/
22
23#include "curl_setup.h"
24
25#ifdef USE_WINDOWS_SSPI
26
27#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO)
28
29#include "urldata.h"
30#include "sendf.h"
31#include "rawstr.h"
32#include "warnless.h"
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"
38
39/* The last #include files should be: */
40#include "curl_memory.h"
41#include "memdebug.h"
42
43CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,
44                              const char *header)
45{
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;
53  unsigned long     attrs;
54  TimeStamp         expiry; /* For Windows 9x compatibility of SSPI calls */
55  size_t len = 0, input_token_len = 0;
56  CURLcode result;
57
58  /* Point to the username and password */
59  const char *userp;
60  const char *passwdp;
61
62  /* Point to the correct struct with this */
63  struct negotiatedata *neg_ctx;
64
65  if(proxy) {
66    userp = conn->proxyuser;
67    passwdp = conn->proxypasswd;
68    neg_ctx = &data->state.proxyneg;
69  }
70  else {
71    userp = conn->user;
72    passwdp = conn->passwd;
73    neg_ctx = &data->state.negotiate;
74  }
75
76  /* Not set means empty */
77  if(!userp)
78    userp = "";
79
80  if(!passwdp)
81    passwdp = "";
82
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;
89  }
90
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;
95
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;
103  }
104
105  if(!neg_ctx->output_token) {
106    PSecPkgInfo SecurityPackage;
107    status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
108                                                TEXT(SP_NAME_NEGOTIATE),
109                                                &SecurityPackage);
110    if(status != SEC_E_OK)
111      return CURLE_NOT_BUILT_IN;
112
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);
118  }
119
120  /* Obtain the input token, if any */
121  header += strlen("Negotiate");
122  while(*header && ISSPACE(*header))
123    header++;
124
125  len = strlen(header);
126  if(!len) {
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;
132    }
133
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));
137
138    if(!neg_ctx->credentials || !neg_ctx->context)
139      return CURLE_OUT_OF_MEMORY;
140
141    if(userp && *userp) {
142      /* Populate our identity structure */
143      result = Curl_create_sspi_identity(userp, passwdp, &neg_ctx->identity);
144      if(result)
145        return result;
146
147      /* Allow proper cleanup of the identity structure */
148      neg_ctx->p_identity = &neg_ctx->identity;
149    }
150    else
151      /* Use the current Windows user */
152      neg_ctx->p_identity = NULL;
153
154    /* Acquire our credientials handle */
155    neg_ctx->status =
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;
163  }
164  else {
165    result = Curl_base64_decode(header,
166                                (unsigned char **)&input_token,
167                                &input_token_len);
168    if(result)
169      return result;
170
171    if(!input_token_len) {
172      infof(data,
173            "Negotiate handshake failure (empty challenge message)\n");
174
175      return CURLE_BAD_CONTENT_ENCODING;
176    }
177  }
178
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);
186
187  /* Setup the "input" security buffer if present */
188  if(input_token) {
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);
195  }
196
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,
203    0,
204    SECURITY_NATIVE_DREP,
205    input_token ? &in_buff_desc : NULL,
206    0,
207    neg_ctx->context,
208    &out_buff_desc,
209    &attrs,
210    &expiry);
211
212  free(input_token);
213
214  if(GSS_ERROR(neg_ctx->status))
215    return CURLE_OUT_OF_MEMORY;
216
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,
220                                                  &out_buff_desc);
221    if(GSS_ERROR(neg_ctx->status))
222      return CURLE_RECV_ERROR;
223  }
224
225  neg_ctx->output_token_length = out_sec_buff.cbBuffer;
226
227  return CURLE_OK;
228}
229
230CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
231{
232  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
233    &conn->data->state.negotiate;
234  char *encoded = NULL;
235  size_t len = 0;
236  char *userp;
237  CURLcode error;
238
239  error = Curl_base64_encode(conn->data,
240                             (const char*)neg_ctx->output_token,
241                             neg_ctx->output_token_length,
242                             &encoded, &len);
243  if(error)
244    return error;
245
246  if(!len)
247    return CURLE_REMOTE_ACCESS_DENIED;
248
249  userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "",
250                  encoded);
251
252  if(proxy) {
253    Curl_safefree(conn->allocptr.proxyuserpwd);
254    conn->allocptr.proxyuserpwd = userp;
255  }
256  else {
257    Curl_safefree(conn->allocptr.userpwd);
258    conn->allocptr.userpwd = userp;
259  }
260  free(encoded);
261  return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
262}
263
264static void cleanup(struct negotiatedata *neg_ctx)
265{
266  /* Free our security context */
267  if(neg_ctx->context) {
268    s_pSecFn->DeleteSecurityContext(neg_ctx->context);
269    free(neg_ctx->context);
270    neg_ctx->context = NULL;
271  }
272
273  /* Free our credentials handle */
274  if(neg_ctx->credentials) {
275    s_pSecFn->FreeCredentialsHandle(neg_ctx->credentials);
276    free(neg_ctx->credentials);
277    neg_ctx->credentials = NULL;
278  }
279
280  /* Free our identity */
281  Curl_sspi_free_identity(neg_ctx->p_identity);
282  neg_ctx->p_identity = NULL;
283
284  /* Free the SPN and output token */
285  Curl_safefree(neg_ctx->server_name);
286  Curl_safefree(neg_ctx->output_token);
287
288  /* Reset any variables */
289  neg_ctx->token_max = 0;
290}
291
292void Curl_cleanup_negotiate(struct SessionHandle *data)
293{
294  cleanup(&data->state.negotiate);
295  cleanup(&data->state.proxyneg);
296}
297
298#endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */
299
300#endif /* USE_WINDOWS_SSPI */
301