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 | |
37 | CURLcode 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 | |
115 | CURLcode 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 | |
211 | void 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 | |