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