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 | |
39 | CURLcode 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 | |
120 | CURLcode 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 | |
215 | void 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 | |