1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 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.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 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25#include "curl_setup.h"
26
27#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM)
28
29/*
30 * NTLM details:
31 *
32 * https://davenport.sourceforge.net/ntlm.html
33 * https://www.innovation.ch/java/ntlm.html
34 */
35
36#define DEBUG_ME 0
37
38#include "urldata.h"
39#include "sendf.h"
40#include "strcase.h"
41#include "http_ntlm.h"
42#include "curl_ntlm_core.h"
43#include "curl_ntlm_wb.h"
44#include "curl_base64.h"
45#include "vauth/vauth.h"
46#include "url.h"
47
48/* SSL backend-specific #if branches in this file must be kept in the order
49 documented in curl_ntlm_core. */
50#if defined(USE_WINDOWS_SSPI)
51#include "curl_sspi.h"
52#endif
53
54/* The last 3 #include files should be in this order */
55#include "curl_printf.h"
56#include "curl_memory.h"
57#include "memdebug.h"
58
59#if DEBUG_ME
60# define DEBUG_OUT(x) x
61#else
62# define DEBUG_OUT(x) Curl_nop_stmt
63#endif
64
65CURLcode Curl_input_ntlm(struct Curl_easy *data,
66 bool proxy, /* if proxy or not */
67 const char *header) /* rest of the www-authenticate:
68 header */
69{
70 /* point to the correct struct with this */
71 struct ntlmdata *ntlm;
72 curlntlm *state;
73 CURLcode result = CURLE_OK;
74 struct connectdata *conn = data->conn;
75
76 ntlm = proxy ? &conn->proxyntlm : &conn->ntlm;
77 state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state;
78
79 if(checkprefix("NTLM", header)) {
80 header += strlen(s: "NTLM");
81
82 while(*header && ISSPACE(*header))
83 header++;
84
85 if(*header) {
86 unsigned char *hdr;
87 size_t hdrlen;
88
89 result = Curl_base64_decode(src: header, outptr: &hdr, outlen: &hdrlen);
90 if(!result) {
91 struct bufref hdrbuf;
92
93 Curl_bufref_init(br: &hdrbuf);
94 Curl_bufref_set(br: &hdrbuf, ptr: hdr, len: hdrlen, dtor: curl_free);
95 result = Curl_auth_decode_ntlm_type2_message(data, type2: &hdrbuf, ntlm);
96 Curl_bufref_free(br: &hdrbuf);
97 }
98 if(result)
99 return result;
100
101 *state = NTLMSTATE_TYPE2; /* We got a type-2 message */
102 }
103 else {
104 if(*state == NTLMSTATE_LAST) {
105 infof(data, "NTLM auth restarted");
106 Curl_http_auth_cleanup_ntlm(conn);
107 }
108 else if(*state == NTLMSTATE_TYPE3) {
109 infof(data, "NTLM handshake rejected");
110 Curl_http_auth_cleanup_ntlm(conn);
111 *state = NTLMSTATE_NONE;
112 return CURLE_REMOTE_ACCESS_DENIED;
113 }
114 else if(*state >= NTLMSTATE_TYPE1) {
115 infof(data, "NTLM handshake failure (internal error)");
116 return CURLE_REMOTE_ACCESS_DENIED;
117 }
118
119 *state = NTLMSTATE_TYPE1; /* We should send away a type-1 */
120 }
121 }
122
123 return result;
124}
125
126/*
127 * This is for creating ntlm header output
128 */
129CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
130{
131 char *base64 = NULL;
132 size_t len = 0;
133 CURLcode result = CURLE_OK;
134 struct bufref ntlmmsg;
135
136 /* point to the address of the pointer that holds the string to send to the
137 server, which is for a plain host or for an HTTP proxy */
138 char **allocuserpwd;
139
140 /* point to the username, password, service and host */
141 const char *userp;
142 const char *passwdp;
143 const char *service = NULL;
144 const char *hostname = NULL;
145
146 /* point to the correct struct with this */
147 struct ntlmdata *ntlm;
148 curlntlm *state;
149 struct auth *authp;
150 struct connectdata *conn = data->conn;
151
152 DEBUGASSERT(conn);
153 DEBUGASSERT(data);
154
155 if(proxy) {
156#ifndef CURL_DISABLE_PROXY
157 allocuserpwd = &data->state.aptr.proxyuserpwd;
158 userp = data->state.aptr.proxyuser;
159 passwdp = data->state.aptr.proxypasswd;
160 service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
161 data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";
162 hostname = conn->http_proxy.host.name;
163 ntlm = &conn->proxyntlm;
164 state = &conn->proxy_ntlm_state;
165 authp = &data->state.authproxy;
166#else
167 return CURLE_NOT_BUILT_IN;
168#endif
169 }
170 else {
171 allocuserpwd = &data->state.aptr.userpwd;
172 userp = data->state.aptr.user;
173 passwdp = data->state.aptr.passwd;
174 service = data->set.str[STRING_SERVICE_NAME] ?
175 data->set.str[STRING_SERVICE_NAME] : "HTTP";
176 hostname = conn->host.name;
177 ntlm = &conn->ntlm;
178 state = &conn->http_ntlm_state;
179 authp = &data->state.authhost;
180 }
181 authp->done = FALSE;
182
183 /* not set means empty */
184 if(!userp)
185 userp = "";
186
187 if(!passwdp)
188 passwdp = "";
189
190#ifdef USE_WINDOWS_SSPI
191 if(!s_hSecDll) {
192 /* not thread safe and leaks - use curl_global_init() to avoid */
193 CURLcode err = Curl_sspi_global_init();
194 if(!s_hSecDll)
195 return err;
196 }
197#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
198 ntlm->sslContext = conn->sslContext;
199#endif
200#endif
201
202 Curl_bufref_init(br: &ntlmmsg);
203
204 /* connection is already authenticated, don't send a header in future
205 * requests so go directly to NTLMSTATE_LAST */
206 if(*state == NTLMSTATE_TYPE3)
207 *state = NTLMSTATE_LAST;
208
209 switch(*state) {
210 case NTLMSTATE_TYPE1:
211 default: /* for the weird cases we (re)start here */
212 /* Create a type-1 message */
213 result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp,
214 service, host: hostname,
215 ntlm, out: &ntlmmsg);
216 if(!result) {
217 DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0);
218 result = Curl_base64_encode(inputbuff: (const char *) Curl_bufref_ptr(br: &ntlmmsg),
219 insize: Curl_bufref_len(br: &ntlmmsg), outptr: &base64, outlen: &len);
220 if(!result) {
221 free(*allocuserpwd);
222 *allocuserpwd = aprintf(format: "%sAuthorization: NTLM %s\r\n",
223 proxy ? "Proxy-" : "",
224 base64);
225 free(base64);
226 if(!*allocuserpwd)
227 result = CURLE_OUT_OF_MEMORY;
228 }
229 }
230 break;
231
232 case NTLMSTATE_TYPE2:
233 /* We already received the type-2 message, create a type-3 message */
234 result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp,
235 ntlm, out: &ntlmmsg);
236 if(!result && Curl_bufref_len(br: &ntlmmsg)) {
237 result = Curl_base64_encode(inputbuff: (const char *) Curl_bufref_ptr(br: &ntlmmsg),
238 insize: Curl_bufref_len(br: &ntlmmsg), outptr: &base64, outlen: &len);
239 if(!result) {
240 free(*allocuserpwd);
241 *allocuserpwd = aprintf(format: "%sAuthorization: NTLM %s\r\n",
242 proxy ? "Proxy-" : "",
243 base64);
244 free(base64);
245 if(!*allocuserpwd)
246 result = CURLE_OUT_OF_MEMORY;
247 else {
248 *state = NTLMSTATE_TYPE3; /* we send a type-3 */
249 authp->done = TRUE;
250 }
251 }
252 }
253 break;
254
255 case NTLMSTATE_LAST:
256 Curl_safefree(*allocuserpwd);
257 authp->done = TRUE;
258 break;
259 }
260 Curl_bufref_free(br: &ntlmmsg);
261
262 return result;
263}
264
265void Curl_http_auth_cleanup_ntlm(struct connectdata *conn)
266{
267 Curl_auth_cleanup_ntlm(ntlm: &conn->ntlm);
268 Curl_auth_cleanup_ntlm(ntlm: &conn->proxyntlm);
269
270#if defined(NTLM_WB_ENABLED)
271 Curl_http_auth_cleanup_ntlm_wb(conn);
272#endif
273}
274
275#endif /* !CURL_DISABLE_HTTP && USE_NTLM */
276