Imported Upstream version 7.50.2
[platform/upstream/curl.git] / lib / vauth / ntlm_sspi.c
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  ***************************************************************************/
22
23 #include "curl_setup.h"
24
25 #if defined(USE_WINDOWS_SSPI) && defined(USE_NTLM)
26
27 #include <curl/curl.h>
28
29 #include "vauth/vauth.h"
30 #include "urldata.h"
31 #include "curl_base64.h"
32 #include "warnless.h"
33 #include "curl_multibyte.h"
34 #include "sendf.h"
35
36 /* The last #include files should be: */
37 #include "curl_memory.h"
38 #include "memdebug.h"
39
40 /*
41  * Curl_auth_is_ntlm_supported()
42  *
43  * This is used to evaluate if NTLM is supported.
44  *
45  * Parameters: None
46  *
47  * Returns TRUE if NTLM is supported by Windows SSPI.
48  */
49 bool Curl_auth_is_ntlm_supported(void)
50 {
51   PSecPkgInfo SecurityPackage;
52   SECURITY_STATUS status;
53
54   /* Query the security package for NTLM */
55   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
56                                               &SecurityPackage);
57
58   return (status == SEC_E_OK ? TRUE : FALSE);
59 }
60
61 /*
62  * Curl_auth_create_ntlm_type1_message()
63  *
64  * This is used to generate an already encoded NTLM type-1 message ready for
65  * sending to the recipient.
66  *
67  * Parameters:
68  *
69  * userp   [in]     - The user name in the format User or Domain\User.
70  * passdwp [in]     - The user's password.
71  * ntlm    [in/out] - The NTLM data struct being used and modified.
72  * outptr  [in/out] - The address where a pointer to newly allocated memory
73  *                    holding the result will be stored upon completion.
74  * outlen  [out]    - The length of the output message.
75  *
76  * Returns CURLE_OK on success.
77  */
78 CURLcode Curl_auth_create_ntlm_type1_message(const char *userp,
79                                              const char *passwdp,
80                                              struct ntlmdata *ntlm,
81                                              char **outptr, size_t *outlen)
82 {
83   PSecPkgInfo SecurityPackage;
84   SecBuffer type_1_buf;
85   SecBufferDesc type_1_desc;
86   SECURITY_STATUS status;
87   unsigned long attrs;
88   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
89
90   /* Clean up any former leftovers and initialise to defaults */
91   Curl_auth_ntlm_cleanup(ntlm);
92
93   /* Query the security package for NTLM */
94   status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
95                                               &SecurityPackage);
96   if(status != SEC_E_OK)
97     return CURLE_NOT_BUILT_IN;
98
99   ntlm->token_max = SecurityPackage->cbMaxToken;
100
101   /* Release the package buffer as it is not required anymore */
102   s_pSecFn->FreeContextBuffer(SecurityPackage);
103
104   /* Allocate our output buffer */
105   ntlm->output_token = malloc(ntlm->token_max);
106   if(!ntlm->output_token)
107     return CURLE_OUT_OF_MEMORY;
108
109   if(userp && *userp) {
110     CURLcode result;
111
112     /* Populate our identity structure */
113     result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity);
114     if(result)
115       return result;
116
117     /* Allow proper cleanup of the identity structure */
118     ntlm->p_identity = &ntlm->identity;
119   }
120   else
121     /* Use the current Windows user */
122     ntlm->p_identity = NULL;
123
124   /* Allocate our credentials handle */
125   ntlm->credentials = malloc(sizeof(CredHandle));
126   if(!ntlm->credentials)
127     return CURLE_OUT_OF_MEMORY;
128
129   memset(ntlm->credentials, 0, sizeof(CredHandle));
130
131   /* Acquire our credentials handle */
132   status = s_pSecFn->AcquireCredentialsHandle(NULL,
133                                               (TCHAR *) TEXT(SP_NAME_NTLM),
134                                               SECPKG_CRED_OUTBOUND, NULL,
135                                               ntlm->p_identity, NULL, NULL,
136                                               ntlm->credentials, &expiry);
137   if(status != SEC_E_OK)
138     return CURLE_LOGIN_DENIED;
139
140   /* Allocate our new context handle */
141   ntlm->context = malloc(sizeof(CtxtHandle));
142   if(!ntlm->context)
143     return CURLE_OUT_OF_MEMORY;
144
145   memset(ntlm->context, 0, sizeof(CtxtHandle));
146
147   /* Setup the type-1 "output" security buffer */
148   type_1_desc.ulVersion = SECBUFFER_VERSION;
149   type_1_desc.cBuffers  = 1;
150   type_1_desc.pBuffers  = &type_1_buf;
151   type_1_buf.BufferType = SECBUFFER_TOKEN;
152   type_1_buf.pvBuffer   = ntlm->output_token;
153   type_1_buf.cbBuffer   = curlx_uztoul(ntlm->token_max);
154
155   /* Generate our type-1 message */
156   status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
157                                                (TCHAR *) TEXT(""),
158                                                0, 0, SECURITY_NETWORK_DREP,
159                                                NULL, 0,
160                                                ntlm->context, &type_1_desc,
161                                                &attrs, &expiry);
162   if(status == SEC_I_COMPLETE_NEEDED ||
163     status == SEC_I_COMPLETE_AND_CONTINUE)
164     s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc);
165   else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
166     return CURLE_RECV_ERROR;
167
168   /* Base64 encode the response */
169   return Curl_base64_encode(NULL, (char *) ntlm->output_token,
170                             type_1_buf.cbBuffer, outptr, outlen);
171 }
172
173 /*
174  * Curl_auth_decode_ntlm_type2_message()
175  *
176  * This is used to decode an already encoded NTLM type-2 message.
177  *
178  * Parameters:
179  *
180  * data     [in]     - The session handle.
181  * type2msg [in]     - The base64 encoded type-2 message.
182  * ntlm     [in/out] - The NTLM data struct being used and modified.
183  *
184  * Returns CURLE_OK on success.
185  */
186 CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
187                                              const char *type2msg,
188                                              struct ntlmdata *ntlm)
189 {
190   CURLcode result = CURLE_OK;
191   unsigned char *type2 = NULL;
192   size_t type2_len = 0;
193
194 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
195   (void) data;
196 #endif
197
198   /* Decode the base-64 encoded type-2 message */
199   if(strlen(type2msg) && *type2msg != '=') {
200     result = Curl_base64_decode(type2msg, &type2, &type2_len);
201     if(result)
202       return result;
203   }
204
205   /* Ensure we have a valid type-2 message */
206   if(!type2) {
207     infof(data, "NTLM handshake failure (empty type-2 message)\n");
208
209     return CURLE_BAD_CONTENT_ENCODING;
210   }
211
212   /* Simply store the challenge for use later */
213   ntlm->input_token = type2;
214   ntlm->input_token_len = type2_len;
215
216   return result;
217 }
218
219 /*
220 * Curl_auth_create_ntlm_type3_message()
221  * Curl_auth_create_ntlm_type3_message()
222  *
223  * This is used to generate an already encoded NTLM type-3 message ready for
224  * sending to the recipient.
225  *
226  * Parameters:
227  *
228  * data    [in]     - The session handle.
229  * userp   [in]     - The user name in the format User or Domain\User.
230  * passdwp [in]     - The user's password.
231  * ntlm    [in/out] - The NTLM data struct being used and modified.
232  * outptr  [in/out] - The address where a pointer to newly allocated memory
233  *                    holding the result will be stored upon completion.
234  * outlen  [out]    - The length of the output message.
235  *
236  * Returns CURLE_OK on success.
237  */
238 CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
239                                              const char *userp,
240                                              const char *passwdp,
241                                              struct ntlmdata *ntlm,
242                                              char **outptr, size_t *outlen)
243 {
244   CURLcode result = CURLE_OK;
245   SecBuffer type_2_buf;
246   SecBuffer type_3_buf;
247   SecBufferDesc type_2_desc;
248   SecBufferDesc type_3_desc;
249   SECURITY_STATUS status;
250   unsigned long attrs;
251   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
252
253   (void) passwdp;
254   (void) userp;
255
256   /* Setup the type-2 "input" security buffer */
257   type_2_desc.ulVersion = SECBUFFER_VERSION;
258   type_2_desc.cBuffers  = 1;
259   type_2_desc.pBuffers  = &type_2_buf;
260   type_2_buf.BufferType = SECBUFFER_TOKEN;
261   type_2_buf.pvBuffer   = ntlm->input_token;
262   type_2_buf.cbBuffer   = curlx_uztoul(ntlm->input_token_len);
263
264   /* Setup the type-3 "output" security buffer */
265   type_3_desc.ulVersion = SECBUFFER_VERSION;
266   type_3_desc.cBuffers  = 1;
267   type_3_desc.pBuffers  = &type_3_buf;
268   type_3_buf.BufferType = SECBUFFER_TOKEN;
269   type_3_buf.pvBuffer   = ntlm->output_token;
270   type_3_buf.cbBuffer   = curlx_uztoul(ntlm->token_max);
271
272   /* Generate our type-3 message */
273   status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
274                                                ntlm->context,
275                                                (TCHAR *) TEXT(""),
276                                                0, 0, SECURITY_NETWORK_DREP,
277                                                &type_2_desc,
278                                                0, ntlm->context,
279                                                &type_3_desc,
280                                                &attrs, &expiry);
281   if(status != SEC_E_OK) {
282     infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
283           status);
284
285     return CURLE_RECV_ERROR;
286   }
287
288   /* Base64 encode the response */
289   result = Curl_base64_encode(data, (char *) ntlm->output_token,
290                               type_3_buf.cbBuffer, outptr, outlen);
291
292   Curl_auth_ntlm_cleanup(ntlm);
293
294   return result;
295 }
296
297 /*
298  * Curl_auth_ntlm_cleanup()
299  *
300  * This is used to clean up the NTLM specific data.
301  *
302  * Parameters:
303  *
304  * ntlm    [in/out] - The NTLM data struct being cleaned up.
305  *
306  */
307 void Curl_auth_ntlm_cleanup(struct ntlmdata *ntlm)
308 {
309   /* Free our security context */
310   if(ntlm->context) {
311     s_pSecFn->DeleteSecurityContext(ntlm->context);
312     free(ntlm->context);
313     ntlm->context = NULL;
314   }
315
316   /* Free our credentials handle */
317   if(ntlm->credentials) {
318     s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
319     free(ntlm->credentials);
320     ntlm->credentials = NULL;
321   }
322
323   /* Free our identity */
324   Curl_sspi_free_identity(ntlm->p_identity);
325   ntlm->p_identity = NULL;
326
327   /* Free the input and output tokens */
328   Curl_safefree(ntlm->input_token);
329   Curl_safefree(ntlm->output_token);
330
331   /* Reset any variables */
332   ntlm->token_max = 0;
333 }
334
335 #endif /* USE_WINDOWS_SSPI && USE_NTLM */