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("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(header, &hdr, &hdrlen); 90 if(!result) { 91 struct bufref hdrbuf; 92 93 Curl_bufref_init(&hdrbuf); 94 Curl_bufref_set(&hdrbuf, hdr, hdrlen, curl_free); 95 result = Curl_auth_decode_ntlm_type2_message(data, &hdrbuf, ntlm); 96 Curl_bufref_free(&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(&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, hostname, 215 ntlm, &ntlmmsg); 216 if(!result) { 217 DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0); 218 result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), 219 Curl_bufref_len(&ntlmmsg), &base64, &len); 220 if(!result) { 221 free(*allocuserpwd); 222 *allocuserpwd = aprintf("%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, &ntlmmsg); 236 if(!result && Curl_bufref_len(&ntlmmsg)) { 237 result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), 238 Curl_bufref_len(&ntlmmsg), &base64, &len); 239 if(!result) { 240 free(*allocuserpwd); 241 *allocuserpwd = aprintf("%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(&ntlmmsg); 261 262 return result; 263} 264 265void Curl_http_auth_cleanup_ntlm(struct connectdata *conn) 266{ 267 Curl_auth_cleanup_ntlm(&conn->ntlm); 268 Curl_auth_cleanup_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