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