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