http_ntlm.c (7951B)
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 #include "urldata.h" 37 #include "sendf.h" 38 #include "strcase.h" 39 #include "http_ntlm.h" 40 #include "curl_ntlm_core.h" 41 #include "curlx/base64.h" 42 #include "vauth/vauth.h" 43 #include "url.h" 44 #include "curlx/strparse.h" 45 46 /* SSL backend-specific #if branches in this file must be kept in the order 47 documented in curl_ntlm_core. */ 48 #if defined(USE_WINDOWS_SSPI) 49 #include "curl_sspi.h" 50 #endif 51 52 /* The last 3 #include files should be in this order */ 53 #include "curl_printf.h" 54 #include "curl_memory.h" 55 #include "memdebug.h" 56 57 CURLcode Curl_input_ntlm(struct Curl_easy *data, 58 bool proxy, /* if proxy or not */ 59 const char *header) /* rest of the www-authenticate: 60 header */ 61 { 62 /* point to the correct struct with this */ 63 curlntlm *state; 64 CURLcode result = CURLE_OK; 65 struct connectdata *conn = data->conn; 66 67 state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state; 68 69 if(checkprefix("NTLM", header)) { 70 struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, proxy); 71 if(!ntlm) 72 return CURLE_FAILED_INIT; 73 74 header += strlen("NTLM"); 75 curlx_str_passblanks(&header); 76 if(*header) { 77 unsigned char *hdr; 78 size_t hdrlen; 79 80 result = curlx_base64_decode(header, &hdr, &hdrlen); 81 if(!result) { 82 struct bufref hdrbuf; 83 84 Curl_bufref_init(&hdrbuf); 85 Curl_bufref_set(&hdrbuf, hdr, hdrlen, curl_free); 86 result = Curl_auth_decode_ntlm_type2_message(data, &hdrbuf, ntlm); 87 Curl_bufref_free(&hdrbuf); 88 } 89 if(result) 90 return result; 91 92 *state = NTLMSTATE_TYPE2; /* We got a type-2 message */ 93 } 94 else { 95 if(*state == NTLMSTATE_LAST) { 96 infof(data, "NTLM auth restarted"); 97 Curl_auth_ntlm_remove(conn, proxy); 98 } 99 else if(*state == NTLMSTATE_TYPE3) { 100 infof(data, "NTLM handshake rejected"); 101 Curl_auth_ntlm_remove(conn, proxy); 102 *state = NTLMSTATE_NONE; 103 return CURLE_REMOTE_ACCESS_DENIED; 104 } 105 else if(*state >= NTLMSTATE_TYPE1) { 106 infof(data, "NTLM handshake failure (internal error)"); 107 return CURLE_REMOTE_ACCESS_DENIED; 108 } 109 110 *state = NTLMSTATE_TYPE1; /* We should send away a type-1 */ 111 } 112 } 113 114 return result; 115 } 116 117 /* 118 * This is for creating NTLM header output 119 */ 120 CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) 121 { 122 char *base64 = NULL; 123 size_t len = 0; 124 CURLcode result = CURLE_OK; 125 struct bufref ntlmmsg; 126 127 /* point to the address of the pointer that holds the string to send to the 128 server, which is for a plain host or for an HTTP proxy */ 129 char **allocuserpwd; 130 131 /* point to the username, password, service and host */ 132 const char *userp; 133 const char *passwdp; 134 const char *service = NULL; 135 const char *hostname = NULL; 136 137 /* point to the correct struct with this */ 138 struct ntlmdata *ntlm; 139 curlntlm *state; 140 struct auth *authp; 141 struct connectdata *conn = data->conn; 142 143 DEBUGASSERT(conn); 144 DEBUGASSERT(data); 145 146 if(proxy) { 147 #ifndef CURL_DISABLE_PROXY 148 allocuserpwd = &data->state.aptr.proxyuserpwd; 149 userp = data->state.aptr.proxyuser; 150 passwdp = data->state.aptr.proxypasswd; 151 service = data->set.str[STRING_PROXY_SERVICE_NAME] ? 152 data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; 153 hostname = conn->http_proxy.host.name; 154 state = &conn->proxy_ntlm_state; 155 authp = &data->state.authproxy; 156 #else 157 return CURLE_NOT_BUILT_IN; 158 #endif 159 } 160 else { 161 allocuserpwd = &data->state.aptr.userpwd; 162 userp = data->state.aptr.user; 163 passwdp = data->state.aptr.passwd; 164 service = data->set.str[STRING_SERVICE_NAME] ? 165 data->set.str[STRING_SERVICE_NAME] : "HTTP"; 166 hostname = conn->host.name; 167 state = &conn->http_ntlm_state; 168 authp = &data->state.authhost; 169 } 170 ntlm = Curl_auth_ntlm_get(conn, proxy); 171 if(!ntlm) 172 return CURLE_OUT_OF_MEMORY; 173 authp->done = FALSE; 174 175 /* not set means empty */ 176 if(!userp) 177 userp = ""; 178 179 if(!passwdp) 180 passwdp = ""; 181 182 #ifdef USE_WINDOWS_SSPI 183 if(!Curl_pSecFn) { 184 /* not thread safe and leaks - use curl_global_init() to avoid */ 185 CURLcode err = Curl_sspi_global_init(); 186 if(!Curl_pSecFn) 187 return err; 188 } 189 #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS 190 ntlm->sslContext = conn->sslContext; 191 #endif 192 #endif 193 194 Curl_bufref_init(&ntlmmsg); 195 196 /* connection is already authenticated, do not send a header in future 197 * requests so go directly to NTLMSTATE_LAST */ 198 if(*state == NTLMSTATE_TYPE3) 199 *state = NTLMSTATE_LAST; 200 201 switch(*state) { 202 case NTLMSTATE_TYPE1: 203 default: /* for the weird cases we (re)start here */ 204 /* Create a type-1 message */ 205 result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp, service, 206 hostname, ntlm, &ntlmmsg); 207 if(!result) { 208 DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0); 209 result = curlx_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), 210 Curl_bufref_len(&ntlmmsg), &base64, &len); 211 if(!result) { 212 free(*allocuserpwd); 213 *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", 214 proxy ? "Proxy-" : "", 215 base64); 216 free(base64); 217 if(!*allocuserpwd) 218 result = CURLE_OUT_OF_MEMORY; 219 } 220 } 221 break; 222 223 case NTLMSTATE_TYPE2: 224 /* We already received the type-2 message, create a type-3 message */ 225 result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp, 226 ntlm, &ntlmmsg); 227 if(!result && Curl_bufref_len(&ntlmmsg)) { 228 result = curlx_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), 229 Curl_bufref_len(&ntlmmsg), &base64, &len); 230 if(!result) { 231 free(*allocuserpwd); 232 *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", 233 proxy ? "Proxy-" : "", 234 base64); 235 free(base64); 236 if(!*allocuserpwd) 237 result = CURLE_OUT_OF_MEMORY; 238 else { 239 *state = NTLMSTATE_TYPE3; /* we send a type-3 */ 240 authp->done = TRUE; 241 } 242 } 243 } 244 break; 245 246 case NTLMSTATE_LAST: 247 /* since this is a little artificial in that this is used without any 248 outgoing auth headers being set, we need to set the bit by force */ 249 if(proxy) 250 data->info.proxyauthpicked = CURLAUTH_NTLM; 251 else 252 data->info.httpauthpicked = CURLAUTH_NTLM; 253 Curl_safefree(*allocuserpwd); 254 authp->done = TRUE; 255 break; 256 } 257 Curl_bufref_free(&ntlmmsg); 258 259 return result; 260 } 261 262 #endif /* !CURL_DISABLE_HTTP && USE_NTLM */