xref: /third_party/curl/lib/http_negotiate.c (revision 13498266)
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_SPNEGO)
28
29#include "urldata.h"
30#include "sendf.h"
31#include "http_negotiate.h"
32#include "vauth/vauth.h"
33
34/* The last 3 #include files should be in this order */
35#include "curl_printf.h"
36#include "curl_memory.h"
37#include "memdebug.h"
38
39CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
40                              bool proxy, const char *header)
41{
42  CURLcode result;
43  size_t len;
44
45  /* Point to the username, password, service and host */
46  const char *userp;
47  const char *passwdp;
48  const char *service;
49  const char *host;
50
51  /* Point to the correct struct with this */
52  struct negotiatedata *neg_ctx;
53  curlnegotiate state;
54
55  if(proxy) {
56#ifndef CURL_DISABLE_PROXY
57    userp = conn->http_proxy.user;
58    passwdp = conn->http_proxy.passwd;
59    service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
60              data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";
61    host = conn->http_proxy.host.name;
62    neg_ctx = &conn->proxyneg;
63    state = conn->proxy_negotiate_state;
64#else
65    return CURLE_NOT_BUILT_IN;
66#endif
67  }
68  else {
69    userp = conn->user;
70    passwdp = conn->passwd;
71    service = data->set.str[STRING_SERVICE_NAME] ?
72              data->set.str[STRING_SERVICE_NAME] : "HTTP";
73    host = conn->host.name;
74    neg_ctx = &conn->negotiate;
75    state = conn->http_negotiate_state;
76  }
77
78  /* Not set means empty */
79  if(!userp)
80    userp = "";
81
82  if(!passwdp)
83    passwdp = "";
84
85  /* Obtain the input token, if any */
86  header += strlen("Negotiate");
87  while(*header && ISBLANK(*header))
88    header++;
89
90  len = strlen(header);
91  neg_ctx->havenegdata = len != 0;
92  if(!len) {
93    if(state == GSS_AUTHSUCC) {
94      infof(data, "Negotiate auth restarted");
95      Curl_http_auth_cleanup_negotiate(conn);
96    }
97    else if(state != GSS_AUTHNONE) {
98      /* The server rejected our authentication and hasn't supplied any more
99      negotiation mechanisms */
100      Curl_http_auth_cleanup_negotiate(conn);
101      return CURLE_LOGIN_DENIED;
102    }
103  }
104
105  /* Supports SSL channel binding for Windows ISS extended protection */
106#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS)
107  neg_ctx->sslContext = conn->sslContext;
108#endif
109
110  /* Initialize the security context and decode our challenge */
111  result = Curl_auth_decode_spnego_message(data, userp, passwdp, service,
112                                           host, header, neg_ctx);
113
114  if(result)
115    Curl_http_auth_cleanup_negotiate(conn);
116
117  return result;
118}
119
120CURLcode Curl_output_negotiate(struct Curl_easy *data,
121                               struct connectdata *conn, bool proxy)
122{
123  struct negotiatedata *neg_ctx = proxy ? &conn->proxyneg :
124    &conn->negotiate;
125  struct auth *authp = proxy ? &data->state.authproxy : &data->state.authhost;
126  curlnegotiate *state = proxy ? &conn->proxy_negotiate_state :
127    &conn->http_negotiate_state;
128  char *base64 = NULL;
129  size_t len = 0;
130  char *userp;
131  CURLcode result;
132
133  authp->done = FALSE;
134
135  if(*state == GSS_AUTHRECV) {
136    if(neg_ctx->havenegdata) {
137      neg_ctx->havemultiplerequests = TRUE;
138    }
139  }
140  else if(*state == GSS_AUTHSUCC) {
141    if(!neg_ctx->havenoauthpersist) {
142      neg_ctx->noauthpersist = !neg_ctx->havemultiplerequests;
143    }
144  }
145
146  if(neg_ctx->noauthpersist ||
147     (*state != GSS_AUTHDONE && *state != GSS_AUTHSUCC)) {
148
149    if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) {
150      infof(data, "Curl_output_negotiate, "
151            "no persistent authentication: cleanup existing context");
152      Curl_http_auth_cleanup_negotiate(conn);
153    }
154    if(!neg_ctx->context) {
155      result = Curl_input_negotiate(data, conn, proxy, "Negotiate");
156      if(result == CURLE_AUTH_ERROR) {
157        /* negotiate auth failed, let's continue unauthenticated to stay
158         * compatible with the behavior before curl-7_64_0-158-g6c6035532 */
159        authp->done = TRUE;
160        return CURLE_OK;
161      }
162      else if(result)
163        return result;
164    }
165
166    result = Curl_auth_create_spnego_message(neg_ctx, &base64, &len);
167    if(result)
168      return result;
169
170    userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "",
171                    base64);
172
173    if(proxy) {
174      Curl_safefree(data->state.aptr.proxyuserpwd);
175      data->state.aptr.proxyuserpwd = userp;
176    }
177    else {
178      Curl_safefree(data->state.aptr.userpwd);
179      data->state.aptr.userpwd = userp;
180    }
181
182    free(base64);
183
184    if(!userp) {
185      return CURLE_OUT_OF_MEMORY;
186    }
187
188    *state = GSS_AUTHSENT;
189  #ifdef HAVE_GSSAPI
190    if(neg_ctx->status == GSS_S_COMPLETE ||
191       neg_ctx->status == GSS_S_CONTINUE_NEEDED) {
192      *state = GSS_AUTHDONE;
193    }
194  #else
195  #ifdef USE_WINDOWS_SSPI
196    if(neg_ctx->status == SEC_E_OK ||
197       neg_ctx->status == SEC_I_CONTINUE_NEEDED) {
198      *state = GSS_AUTHDONE;
199    }
200  #endif
201  #endif
202  }
203
204  if(*state == GSS_AUTHDONE || *state == GSS_AUTHSUCC) {
205    /* connection is already authenticated,
206     * don't send a header in future requests */
207    authp->done = TRUE;
208  }
209
210  neg_ctx->havenegdata = FALSE;
211
212  return CURLE_OK;
213}
214
215void Curl_http_auth_cleanup_negotiate(struct connectdata *conn)
216{
217  conn->http_negotiate_state = GSS_AUTHNONE;
218  conn->proxy_negotiate_state = GSS_AUTHNONE;
219
220  Curl_auth_cleanup_spnego(&conn->negotiate);
221  Curl_auth_cleanup_spnego(&conn->proxyneg);
222}
223
224#endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */
225