Coverage Report

Created: 2025-12-03 07:13

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