Imported Upstream version 7.48.0
[platform/upstream/curl.git] / lib / http_negotiate_sspi.c
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 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  ***************************************************************************/
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
43 CURLcode 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
230 CURLcode 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 result;
238
239   result = Curl_base64_encode(conn->data,
240                               (const char*)neg_ctx->output_token,
241                               neg_ctx->output_token_length,
242                               &encoded, &len);
243   if(result)
244     return result;
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
261   free(encoded);
262
263   return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
264 }
265
266 static void cleanup(struct negotiatedata *neg_ctx)
267 {
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;
273   }
274
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;
280   }
281
282   /* Free our identity */
283   Curl_sspi_free_identity(neg_ctx->p_identity);
284   neg_ctx->p_identity = NULL;
285
286   /* Free the SPN and output token */
287   Curl_safefree(neg_ctx->server_name);
288   Curl_safefree(neg_ctx->output_token);
289
290   /* Reset any variables */
291   neg_ctx->token_max = 0;
292 }
293
294 void Curl_cleanup_negotiate(struct SessionHandle *data)
295 {
296   cleanup(&data->state.negotiate);
297   cleanup(&data->state.proxyneg);
298 }
299
300 #endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */
301
302 #endif /* USE_WINDOWS_SSPI */