Revert "Imported Upstream version 7.44.0"
[platform/upstream/curl.git] / lib / http_negotiate_sspi.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2014, 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_memory.h"
37 #include "curl_multibyte.h"
38
39 #define _MPRINTF_REPLACE /* use our functions only */
40 #include <curl/mprintf.h>
41
42 /* The last #include file should be: */
43 #include "memdebug.h"
44
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,
48                          const char *header)
49 {
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;
56   TimeStamp         expiry;
57   int ret;
58   size_t len = 0, input_token_len = 0;
59   CURLcode error;
60
61   /* Point to the username and password */
62   const char *userp;
63   const char *passwdp;
64
65   /* Point to the correct struct with this */
66   struct negotiatedata *neg_ctx;
67
68   if(proxy) {
69     userp = conn->proxyuser;
70     passwdp = conn->proxypasswd;
71     neg_ctx = &conn->data->state.proxyneg;
72   }
73   else {
74     userp = conn->user;
75     passwdp = conn->passwd;
76     neg_ctx = &conn->data->state.negotiate;
77   }
78
79   /* Not set means empty */
80   if(!userp)
81     userp = "";
82
83   if(!passwdp)
84     passwdp = "";
85
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);
91     return -1;
92   }
93
94   if(!neg_ctx->server_name) {
95     /* Check proxy auth requested but no given proxy name */
96     if(proxy && !conn->proxy.name)
97       return -1;
98
99     /* Generate our SPN */
100     neg_ctx->server_name = Curl_sasl_build_spn("HTTP",
101                                                 proxy ? conn->proxy.name :
102                                                         conn->host.name);
103     if(!neg_ctx->server_name)
104       return -1;
105   }
106
107   if(!neg_ctx->output_token) {
108     PSecPkgInfo SecurityPackage;
109     ret = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NEGOTIATE),
110                                              &SecurityPackage);
111     if(ret != SEC_E_OK)
112       return -1;
113
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);
119   }
120
121   /* Obtain the input token, if any */
122   header += strlen("Negotiate");
123   while(*header && ISSPACE(*header))
124     header++;
125
126   len = strlen(header);
127   if(!len) {
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 */
132       return -1;
133     }
134
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));
138
139     if(!neg_ctx->credentials || !neg_ctx->context)
140       return -1;
141
142     if(userp && *userp) {
143       /* Populate our identity structure */
144       error = Curl_create_sspi_identity(userp, passwdp, &neg_ctx->identity);
145       if(error)
146         return -1;
147
148       /* Allow proper cleanup of the identity structure */
149       neg_ctx->p_identity = &neg_ctx->identity;
150     }
151     else
152       /* Use the current Windows user */
153       neg_ctx->p_identity = NULL;
154
155     /* Acquire our credientials handle */
156     neg_ctx->status =
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)
163       return -1;
164   }
165   else {
166     error = Curl_base64_decode(header,
167                                (unsigned char **)&input_token,
168                                &input_token_len);
169     if(error || !input_token_len)
170       return -1;
171   }
172
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);
180
181   /* Setup the "input" security buffer if present */
182   if(input_token) {
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);
189   }
190
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,
197     0,
198     SECURITY_NATIVE_DREP,
199     input_token ? &in_buff_desc : NULL,
200     0,
201     neg_ctx->context,
202     &out_buff_desc,
203     &context_attributes,
204     &expiry);
205
206   Curl_safefree(input_token);
207
208   if(GSS_ERROR(neg_ctx->status))
209     return -1;
210
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,
214                                                   &out_buff_desc);
215     if(GSS_ERROR(neg_ctx->status))
216       return -1;
217   }
218
219   neg_ctx->output_token_length = out_sec_buff.cbBuffer;
220
221   return 0;
222 }
223
224
225 CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
226 {
227   struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
228     &conn->data->state.negotiate;
229   char *encoded = NULL;
230   size_t len = 0;
231   char *userp;
232   CURLcode error;
233
234   error = Curl_base64_encode(conn->data,
235                              (const char*)neg_ctx->output_token,
236                              neg_ctx->output_token_length,
237                              &encoded, &len);
238   if(error)
239     return error;
240
241   if(!len)
242     return CURLE_REMOTE_ACCESS_DENIED;
243
244   userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "",
245                   encoded);
246
247   if(proxy) {
248     Curl_safefree(conn->allocptr.proxyuserpwd);
249     conn->allocptr.proxyuserpwd = userp;
250   }
251   else {
252     Curl_safefree(conn->allocptr.userpwd);
253     conn->allocptr.userpwd = userp;
254   }
255   free(encoded);
256   return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
257 }
258
259 static void cleanup(struct negotiatedata *neg_ctx)
260 {
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;
266   }
267
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;
273   }
274
275   /* Free our identity */
276   Curl_sspi_free_identity(neg_ctx->p_identity);
277   neg_ctx->p_identity = NULL;
278
279   /* Free the SPN and output token */
280   Curl_safefree(neg_ctx->server_name);
281   Curl_safefree(neg_ctx->output_token);
282
283   /* Reset any variables */
284   neg_ctx->token_max = 0;
285 }
286
287 void Curl_cleanup_negotiate(struct SessionHandle *data)
288 {
289   cleanup(&data->state.negotiate);
290   cleanup(&data->state.proxyneg);
291 }
292
293 #endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */
294
295 #endif /* USE_WINDOWS_SSPI */