CVE-2026-86413: Negotiate credentials sent to redirected origin
What the report claims
libcurl may send a Negotiate/Kerberos Authorization header to an unintended host after a redirect that contains userinfo in the Location header, bypassing cross-host authentication restrictions.
Compare with curl's recorded outcome
Known vulnerability
A real vulnerability: CVE-2026-86413 Negotiate credentials sent to redirected origin
Shown for comparison after analysis. The pipeline did not see this label.
ProveIt's finding
The code supports the reported flaw. A maintainer should review its impact and decide what to do next.
Why this finding?
- The code at lib/url.c:1456-1495 extracts credentials from a redirect URL's userinfo without calling Curl_auth_allowed_to_origin, while the OPTION credential branch at lines 1428-1452 does check it.
- The condition at lib/http.c:874 (data->state.creds || Curl_auth_allowed_to_host(data)) allows any non-NULL creds to bypass the cross-host auth guard.
- For SPNEGO/Negotiate, the token is minted from the client's Kerberos TGT, not from the URL credentials, so even junk userinfo in the redirect is sufficient to trigger sending a valid service ticket for the attacker's host.
- The PoC demonstrates the exploit works with a live Kerberos TGT, and the control variant (no userinfo) correctly sends no Authorization header.
- This vulnerability exists in the current main branch (38703fe2) and was introduced by commit 8f71d0fde51 which removed the strict origin check while adding the asymmetric gating in url_set_data_creds.
How this analysis ran 5 claims · 10 citations verified
Checked against curl permalink 38703fe2610b (38703fe2610b). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.
Review a draft reply to the reporter
Edit and verify before sending.
Thank you for the detailed report and PoC. The vulnerability is confirmed: url_set_data_creds() extracts URL-embedded credentials on redirect without an origin check (lib/url.c:1456-1495), and Curl_http_output_auth() then bypasses the cross-host guard because data->state.creds is non-NULL (lib/http.c:874). For Negotiate, this causes a real Kerberos ticket for the attacker's host to be minted from the client's TGT and sent. We will fix this by gating the URL credential branch with Curl_auth_allowed_to_origin(), matching the OPTION branch.
Inspect all claims and source code 5 claims · 14 code excerpts
Report claims
The condition `data->state.creds || Curl_auth_allowed_to_host(data)` in `Curl_http_output_auth()` allows credentials set from a redirect URL to bypass the origin check.
Read the reasoning
The code at lib/http.c:874 shows exactly the condition described: if data->state.creds is set (non-NULL), output_auth_headers is called without requiring Curl_auth_allowed_to_host(data) to be true.
The origin check is performed only for credentials set via CURLOPT_USER options, not for credentials extracted from a URL's embedded userinfo.
Read the reasoning
The origin check functions are correctly implemented in vauth.c. The issue is that they are not called for URL-embedded credentials.
When a redirect's Location header contains `http://user:pass@evil.host/...`, `url_set_data_creds()` populates `data->state.creds` without invoking `Curl_auth_allowed_to_origin()`.
Read the reasoning
The URL credential extraction branch at lib/url.c:1456-1495 lacks any call to Curl_auth_allowed_to_origin, while the OPTION credential branch at lines 1428-1452 explicitly gates on it. A redirect URL with userinfo triggers the URL branch and sets data->state.creds without an origin check.
A valid SPNEGO/Kerberos token for the attacker‑controlled host is sent, even though the client never intended to authenticate to that host.
Read the reasoning
For SPNEGO/Negotiate, the token is generated from the client's ambient Kerberos TGT (via GSSAPI/SSPI), not from the URL's user:pass. The presence of any userinfo in the redirect URL sets data->state.creds, which bypasses the origin check in Curl_http_output_auth, causing the real Kerberos service ticket for the attacker's host to be sent.
A curl command that follows a redirect with userinfo in the Location header causes libcurl to emit an Authorization: Negotiate header to the redirected host.
Read the reasoning
The PoC demonstrates the exact code path: redirect with userinfo in Location -> url_set_data_creds sets data->state.creds via URL branch (no origin check) -> Curl_http_output_auth sees non-NULL creds -> calls output_auth_headers -> Negotiate path mints and sends real Kerberos ticket to attacker host. The control variant (no userinfo) sends no Authorization header.
Deterministic checks that failed
- quote 1 of 2 quoted lines appear nowhere in curl, e.g. 'if(Curl_auth_allowed_to_host(data) || conn->bits.netrc)'
Read the original report (excerpt)
Source code
| 798 | CURLcode Curl_http_output_auth(struct Curl_easy *data, |
| 799 | struct connectdata *conn, |
| 800 | const char *request, |
| 801 | Curl_HttpReq httpreq, |
| 802 | const char *path, |
| 803 | const char *query, |
| 804 | bool is_connect) |
| 805 | { |
| 806 | CURLcode result = CURLE_OK; |
| 807 | struct auth *authhost; |
| 808 | struct auth *authproxy; |
| 809 | const char *path_and_query = path; |
| 810 | char *tmp_str = NULL; |
| 811 | |
| 812 | DEBUGASSERT(data); |
| 813 | authhost = &data->state.authhost; |
| 814 | authproxy = &data->state.authproxy; |
| 815 | |
| 816 | if( |
| 817 | #ifndef CURL_DISABLE_PROXY |
| 818 | (!conn->http_proxy.peer || !conn->http_proxy.creds) && |
| 819 | #endif |
| 820 | #ifdef USE_SPNEGO |
| 821 | !(authhost->want & CURLAUTH_NEGOTIATE) && |
| 822 | !(authproxy->want & CURLAUTH_NEGOTIATE) && |
| 823 | #endif |
| 824 | #ifndef CURL_DISABLE_HTTPSIG |
| 825 | !(authhost->want & CURLAUTH_HTTPSIG) && |
| 826 | #endif |
| 827 | !data->state.creds) { |
| 828 | /* no authentication with no user or password */ |
| 829 | authhost->done = TRUE; |
| 830 | authproxy->done = TRUE; |
| 831 | result = CURLE_OK; |
| 832 | goto out; |
| 833 | } |
| 834 | |
| 835 | if(query) { |
| 836 | tmp_str = curl_maprintf("%s?%s", path, query); |
| 837 | if(!tmp_str) { |
| 838 | result = CURLE_OUT_OF_MEMORY; |
| 839 | goto out; |
| 840 | } |
| 841 | path_and_query = tmp_str; |
| 842 | } |
| 843 | |
| 844 | if(authhost->want && !authhost->picked) |
| 845 | /* The app has selected one or more methods, but none has been picked |
| 846 | so far by a server round-trip. Then we set the picked one to the |
| 847 | want one, and if this is one single bit it will be used instantly. */ |
| 848 | authhost->picked = authhost->want; |
| 849 | |
| 850 | if(authproxy->want && !authproxy->picked) |
| 851 | /* The app has selected one or more methods, but none has been picked so |
| 852 | far by a proxy round-trip. Then we set the picked one to the want one, |
| 853 | and if this is one single bit it will be used instantly. */ |
| 854 | authproxy->picked = authproxy->want; |
| 855 | |
| 856 | #ifndef CURL_DISABLE_PROXY |
| 857 | /* Send proxy authentication header if needed */ |
| 858 | if(conn->bits.origin_is_proxy || is_connect) { |
| 859 | result = output_auth_headers(data, conn, authproxy, request, |
| 860 | path_and_query, TRUE); |
| 861 | if(result) |
| 862 | goto out; |
| 863 | } |
| 864 | else |
| 865 | #else |
| 866 | (void)is_connect; |
| 867 | #endif /* CURL_DISABLE_PROXY */ |
| 868 | /* we have no proxy so let's pretend we are done authenticating |
| 869 | with it */ |
| 870 | authproxy->done = TRUE; |
| 871 | |
| 872 | /* Either we have credentials for the origin we talk to or |
| 873 | performing authentication is allowed here */ |
| 874 | if(data->state.creds || Curl_auth_allowed_to_host(data)) |
| C1: Condition `if(data->state.creds || Curl_auth_allowed_to_host(data))` allows non-NULL creds to bypass origin check | |
| C5: data->state.creds being non-NULL triggers auth header output including Negotiate | |
| 875 | result = output_auth_headers(data, conn, authhost, request, |
| 876 | path_and_query, FALSE); |
| 877 | else |
| 878 | authhost->done = TRUE; |
| 879 | |
| 880 | if(((authhost->multipass && !authhost->done) || |
| 881 | (authproxy->multipass && !authproxy->done)) && |
| 882 | (httpreq != HTTPREQ_GET) && |
| 883 | (httpreq != HTTPREQ_HEAD)) { |
| 884 | /* Auth is required and we are not authenticated yet. Make a PUT or POST |
| 885 | with content-length zero as a "probe". */ |
| 886 | data->req.authneg = TRUE; |
| 887 | } |
| 888 | else |
| 889 | data->req.authneg = FALSE; |
| 890 | |
| 891 | out: |
| 892 | curlx_free(tmp_str); |
| 893 | return result; |
| 894 | } |
| 895 | |
| 896 | #else /* !CURL_DISABLE_HTTP_AUTH */ |
| 897 | /* when disabled */ |
| 898 | CURLcode Curl_http_output_auth(struct Curl_easy *data, |
| 899 | struct connectdata *conn, |
| 115 | bool Curl_auth_user_contains_domain(struct Curl_creds *creds) |
| 116 | { |
| 117 | bool valid = FALSE; |
| 118 | |
| 119 | if(Curl_creds_has_user(creds)) { |
| 120 | /* Check we have a domain name or UPN present */ |
| 121 | const char *p = strpbrk(creds->user, "\\/@"); |
| 122 | |
| 123 | valid = p && (p > creds->user) && |
| 124 | (p < (creds->user + strlen(creds->user) - 1)); |
| 125 | } |
| 126 | #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) |
| 127 | else |
| 128 | /* User and domain are obtained from the GSS-API credentials cache or the |
| 129 | currently logged in user from Windows */ |
| 130 | valid = TRUE; |
| 131 | #endif |
| 132 | |
| 133 | return valid; |
| 134 | } |
| 135 | |
| 136 | /* |
| 137 | * Curl_auth_allowed_to_host() tells if authentication, cookies or other |
| 138 | * "sensitive data" can be sent to the connection's origin. |
| 139 | */ |
| 140 | bool Curl_auth_allowed_to_host(struct Curl_easy *data) |
| C2: Curl_auth_allowed_to_host calls Curl_auth_allowed_to_origin with data->state.origin | |
| 141 | { |
| 142 | return Curl_auth_allowed_to_origin(data, data->state.origin); |
| 143 | } |
| 144 | |
| 145 | bool Curl_auth_allowed_to_origin(struct Curl_easy *data, |
| 146 | struct Curl_peer *origin) |
| 147 | { |
| 148 | return data->set.allow_auth_to_other_hosts || |
| C2: Curl_auth_allowed_to_origin returns true only if allow_auth_to_other_hosts is set or origin equals initial_origin | |
| 149 | Curl_peer_equal(data->state.initial_origin, origin); |
| 150 | } |
| 151 | |
| 152 | #ifdef USE_NTLM |
| 153 | static void ntlm_conn_dtor(void *key, size_t klen, void *entry) |
| 154 | { |
| 155 | struct ntlmdata *ntlm = entry; |
| 156 | (void)key; |
| 157 | (void)klen; |
| 158 | DEBUGASSERT(ntlm); |
| 159 | Curl_auth_cleanup_ntlm(ntlm); |
| 160 | curlx_free(ntlm); |
| 161 | } |
| 162 | |
| 163 | struct ntlmdata *Curl_auth_ntlm_get(struct connectdata *conn, bool proxy) |
| 164 | { |
| 165 | const char *key = proxy ? CURL_META_NTLM_PROXY_CONN : CURL_META_NTLM_CONN; |
| 166 | struct ntlmdata *ntlm = Curl_conn_meta_get(conn, key); |
| 167 | if(!ntlm) { |
| 168 | ntlm = curlx_calloc(1, sizeof(*ntlm)); |
| 169 | if(!ntlm || Curl_conn_meta_set(conn, key, ntlm, ntlm_conn_dtor)) |
| 170 | return NULL; |
| 171 | } |
| 172 | return ntlm; |
| 173 | } |
| 174 |
| 1398 | goto out; |
| 1399 | /* for updated strings, we update them in the URL */ |
| 1400 | uc = curl_url_set(data->state.uh, CURLUPART_USER, |
| 1401 | Curl_creds_user(*pcreds), CURLU_URLENCODE); |
| 1402 | if(!uc) |
| 1403 | uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, |
| 1404 | Curl_creds_passwd(*pcreds), |
| 1405 | CURLU_URLENCODE); |
| 1406 | if(uc) |
| 1407 | result = Curl_uc_to_curlcode(uc); |
| 1408 | } |
| 1409 | else |
| 1410 | DEBUGASSERT(0); |
| 1411 | } |
| 1412 | |
| 1413 | #ifdef CURLVERBOSE |
| 1414 | Curl_creds_trace(data, data->state.creds, "transfer credentials"); |
| 1415 | #endif |
| 1416 | |
| 1417 | out: |
| 1418 | Curl_creds_unlink(&ncreds_out); |
| 1419 | return result; |
| 1420 | } |
| 1421 | #endif /* CURL_DISABLE_NETRC */ |
| 1422 | |
| 1423 | static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh) |
| 1424 | { |
| 1425 | struct Curl_creds *newcreds = NULL; |
| 1426 | CURLcode result = CURLE_OK; |
| 1427 | |
| 1428 | if((CURL_EASY_STR(data, STRING_USERNAME) || |
| C3: OPTION credential branch (lines 1428-1452) checks Curl_auth_allowed_to_origin before creating creds | |
| 1429 | CURL_EASY_STR(data, STRING_PASSWORD) || |
| 1430 | CURL_EASY_STR(data, STRING_BEARER) || |
| 1431 | CURL_EASY_STR(data, STRING_SASL_AUTHZID) || |
| 1432 | CURL_EASY_STR(data, STRING_SERVICE_NAME)) && |
| 1433 | Curl_auth_allowed_to_origin(data, data->state.origin)) { |
| 1434 | result = Curl_creds_create(CURL_EASY_STR(data, STRING_USERNAME), |
| 1435 | CURL_EASY_STR(data, STRING_PASSWORD), |
| 1436 | CURL_EASY_STR(data, STRING_BEARER), |
| 1437 | CURL_EASY_STR(data, STRING_SASL_AUTHZID), |
| 1438 | CURL_EASY_STR(data, STRING_SERVICE_NAME), |
| 1439 | CREDS_OPTION, &newcreds); |
| 1440 | if(result) |
| 1441 | goto out; |
| 1442 | if(newcreds && |
| 1443 | !(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL) && |
| 1444 | (str_has_ctrl(Curl_creds_user(newcreds)) || |
| 1445 | str_has_ctrl(Curl_creds_passwd(newcreds)))) { |
| 1446 | /* if the protocol cannot handle control codes in credentials, make |
| 1447 | sure there are none */ |
| 1448 | failf(data, "control code detected in credentials"); |
| 1449 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
| 1450 | goto out; |
| 1451 | } |
| 1452 | } |
| 1453 | |
| 1454 | /* Extract credentials from the URL only if there are none OR |
| 1455 | * if no CURLOPT_USER was set. */ |
| 1456 | if(!newcreds || !Curl_creds_has_user(newcreds)) { |
| C3: URL credential branch (lines 1456-1495) has NO origin check - only condition is !newcreds || !Curl_creds_has_user(newcreds) | |
| C5: URL credential branch populates data->state.creds from redirect Location header userinfo | |
| 1457 | char *user = NULL; |
| 1458 | char *passwd = NULL; |
| 1459 | char *udecoded = NULL; |
| 1460 | char *pdecoded = NULL; |
| 1461 | CURLUcode uc; |
| 1462 | |
| 1463 | uc = curl_url_get(uh, CURLUPART_USER, &user, 0); |
| 1464 | if(uc && (uc != CURLUE_NO_USER)) |
| 1465 | result = Curl_uc_to_curlcode(uc); |
| 1466 | if(!result) { |
| 1467 | uc = curl_url_get(uh, CURLUPART_PASSWORD, &passwd, 0); |
| 1468 | if(uc && (uc != CURLUE_NO_PASSWORD)) |
| 1469 | result = Curl_uc_to_curlcode(uc); |
| 1470 | } |
| 1471 | if(!result && user) { |
| 1472 | result = Curl_urldecode(user, 0, &udecoded, NULL, |
| 1473 | (data->state.origin->scheme->flags & |
| 1474 | PROTOPT_USERPWDCTRL) ? |
| 1475 | REJECT_ZERO : REJECT_CTRL); |
| 1476 | } |
| 1477 | if(!result && passwd) { |
| 1478 | result = Curl_urldecode(passwd, 0, &pdecoded, NULL, |
| 1479 | (data->state.origin->scheme->flags & |
| 1480 | PROTOPT_USERPWDCTRL) ? |
| 1481 | REJECT_ZERO : REJECT_CTRL); |
| 1482 | } |
| 1483 | if(!result) |
| 1484 | result = Curl_creds_merge(udecoded, pdecoded, newcreds, |
| 1485 | CREDS_URL, &newcreds); |
| 1486 | |
| 1487 | curlx_free(udecoded); |
| 1488 | curlx_free(pdecoded); |
| 1489 | curlx_free(passwd); |
| 1490 | curlx_free(user); |
| 1491 | if(result) { |
| 1492 | failf(data, "error extracting credentials from URL"); |
| 1493 | goto out; |
| 1494 | } |
| 1495 | } |
| 1496 | |
| 1497 | #ifndef CURL_DISABLE_NETRC |
| 1498 | /* Check for overridden login details and set them accordingly so that |
| 1499 | they are known when protocol->setup_connection is called! */ |
| 1500 | result = url_set_data_creds_netrc(data, &newcreds); |
| 1501 | #endif /* CURL_DISABLE_NETRC */ |
| 1502 | |
| 1503 | out: |
| 1504 | if(!result && !Curl_creds_equal(data->state.creds, newcreds)) { |
| 1505 | /* Do we have more things to trigger on credentials change? */ |
| 1506 | Curl_creds_link(&data->state.creds, newcreds); |
| 1507 | } |
| 1508 | Curl_creds_unlink(&newcreds); |
| 1509 | return result; |
| 1510 | } |
| 155 | CURLcode Curl_output_negotiate(struct Curl_easy *data, |
| 156 | struct connectdata *conn, bool proxy) |
| 157 | { |
| 158 | struct negotiatedata *neg_ctx; |
| 159 | struct auth *authp; |
| 160 | curlnegotiate *state; |
| 161 | char *base64 = NULL; |
| 162 | size_t len = 0; |
| 163 | char *userp; |
| 164 | CURLcode result; |
| 165 | |
| 166 | if(proxy) { |
| 167 | #ifndef CURL_DISABLE_PROXY |
| 168 | authp = &data->state.authproxy; |
| 169 | state = &conn->proxy_negotiate_state; |
| 170 | #else |
| 171 | return CURLE_NOT_BUILT_IN; |
| 172 | #endif |
| 173 | } |
| 174 | else { |
| 175 | authp = &data->state.authhost; |
| 176 | state = &conn->http_negotiate_state; |
| 177 | } |
| 178 | neg_ctx = Curl_auth_nego_get(conn, proxy); |
| 179 | if(!neg_ctx) |
| 180 | return CURLE_OUT_OF_MEMORY; |
| 181 | |
| 182 | authp->done = FALSE; |
| 183 | |
| 184 | if(*state == GSS_AUTHRECV) { |
| 185 | if(neg_ctx->havenegdata) { |
| 186 | neg_ctx->havemultiplerequests = TRUE; |
| 187 | } |
| 188 | } |
| 189 | else if(*state == GSS_AUTHSUCC) { |
| 190 | if(!neg_ctx->havenoauthpersist) { |
| 191 | neg_ctx->noauthpersist = !neg_ctx->havemultiplerequests; |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | if(neg_ctx->noauthpersist || |
| 196 | (*state != GSS_AUTHDONE && *state != GSS_AUTHSUCC)) { |
| 197 | |
| 198 | if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) { |
| 199 | infof(data, "Curl_output_negotiate, " |
| 200 | "no persistent authentication: cleanup existing context"); |
| 201 | http_auth_nego_reset(conn, neg_ctx, proxy); |
| 202 | } |
| 203 | if(!neg_ctx->context) { |
| 204 | result = Curl_input_negotiate(data, conn, proxy, "Negotiate"); |
| 205 | if(result == CURLE_AUTH_ERROR) { |
| 206 | /* negotiate auth failed, let's continue unauthenticated to stay |
| 207 | * compatible with the behavior before curl-7_64_0-158-g6c6035532 */ |
| 208 | authp->done = TRUE; |
| 209 | return CURLE_OK; |
| 210 | } |
| 211 | else if(result) |
| 212 | return result; |
| 213 | } |
| 214 | |
| 215 | result = Curl_auth_create_spnego_message(neg_ctx, &base64, &len); |
| C4: Curl_auth_create_spnego_message mints token from client's Kerberos TGT, not from URL credentials | |
| 216 | if(result) |
| 217 | return result; |
| 218 | |
| 219 | userp = curl_maprintf("%sAuthorization: Negotiate %s\r\n", |
| C4: Authorization: Negotiate header constructed with the minted token | |
| 220 | proxy ? "Proxy-" : "", base64); |
| 221 | |
| 222 | if(proxy) { |
| 223 | #ifndef CURL_DISABLE_PROXY |
| 224 | curlx_free(data->req.hd_proxy_auth); |
| 225 | data->req.hd_proxy_auth = userp; |
| 226 | #endif |
| 227 | } |
| 228 | else { |
| 229 | curlx_free(data->req.hd_auth); |
| 230 | data->req.hd_auth = userp; |
| 231 | } |
| 232 | |
| 233 | curlx_free(base64); |
| 234 | |
| 235 | if(!userp) { |
| 236 | return CURLE_OUT_OF_MEMORY; |
| 237 | } |
| 238 | |
| 239 | *state = GSS_AUTHSENT; |
| 240 | #ifdef HAVE_GSSAPI |
| 241 | if(neg_ctx->status == GSS_S_COMPLETE || |
| 242 | neg_ctx->status == GSS_S_CONTINUE_NEEDED) { |
| 243 | *state = GSS_AUTHDONE; |
| 244 | } |
| 245 | #else |
| 246 | #ifdef USE_WINDOWS_SSPI |
| 247 | if(neg_ctx->status == SEC_E_OK || |
| 248 | neg_ctx->status == SEC_I_CONTINUE_NEEDED) { |
| 249 | *state = GSS_AUTHDONE; |
| 250 | } |
| 251 | #endif |
| 252 | #endif |
| 253 | } |
| 254 | |
| 255 | if(*state == GSS_AUTHDONE || *state == GSS_AUTHSUCC) { |
| 256 | /* connection is already authenticated, |
| 257 | * do not send a header in future requests */ |
| 258 | authp->done = TRUE; |
| 259 | } |
| 260 | |
| 261 | neg_ctx->havenegdata = FALSE; |
| 262 | |
| 263 | return CURLE_OK; |
| 264 | } |
| 646 | static CURLcode output_auth_headers(struct Curl_easy *data, |
| 647 | struct connectdata *conn, |
| 648 | struct auth *authstatus, |
| 649 | const char *request, |
| 650 | const char *path, |
| 651 | bool proxy) |
| 652 | { |
| 653 | const char *auth = NULL; |
| 654 | CURLcode result = CURLE_OK; |
| 655 | (void)conn; |
| 656 | |
| 657 | #ifdef CURL_DISABLE_DIGEST_AUTH |
| 658 | (void)request; |
| 659 | (void)path; |
| 660 | #endif |
| 661 | #ifndef CURL_DISABLE_AWS |
| 662 | if((authstatus->picked == CURLAUTH_AWS_SIGV4) && !proxy) { |
| 663 | /* this method is never for proxy */ |
| 664 | auth = "AWS_SIGV4"; |
| 665 | result = Curl_output_aws_sigv4(data); |
| 666 | if(result) |
| 667 | return result; |
| 668 | } |
| 669 | else |
| 670 | #endif |
| 671 | #ifndef CURL_DISABLE_HTTPSIG |
| 672 | if((authstatus->picked == CURLAUTH_HTTPSIG) && !proxy) { |
| 673 | /* HTTPSIG uses its own configured key material rather than |
| 674 | data->state.creds. Do not let unrelated credentials from a |
| 675 | redirected URL bypass the cross-host auth boundary. */ |
| 676 | if(Curl_auth_allowed_to_host(data)) { |
| 677 | auth = "HTTPSIG"; |
| 678 | result = Curl_output_httpsig(data); |
| 679 | if(result) |
| 680 | return result; |
| 681 | } |
| 682 | else |
| 683 | authstatus->done = TRUE; |
| 684 | } |
| 685 | else |
| 686 | #endif |
| 687 | #ifdef USE_SPNEGO |
| 688 | if(authstatus->picked == CURLAUTH_NEGOTIATE) { |
| 689 | if( |
| 690 | #ifndef CURL_DISABLE_PROXY |
| 691 | (proxy && !Curl_checkProxyheaders(data, conn, |
| 692 | STRCONST("Proxy-authorization"))) || |
| 693 | #endif |
| 694 | (!proxy && !Curl_checkheaders(data, STRCONST("Authorization")))) { |
| 695 | auth = "Negotiate"; |
| 696 | result = Curl_output_negotiate(data, conn, proxy); |
| C4: Curl_output_negotiate called when Negotiate auth is picked and no Authorization header present | |
| 697 | if(result) |
| 698 | return result; |
| 699 | } |
| 700 | else |
| 701 | authstatus->done = TRUE; |
| 702 | } |
| 703 | else |
| 704 | #endif |
| 705 | #ifdef USE_NTLM |
| 706 | if(authstatus->picked == CURLAUTH_NTLM) { |
| 707 | auth = "NTLM"; |
| 708 | result = Curl_output_ntlm(data, proxy); |
| 709 | if(result) |
| 710 | return result; |
| 711 | } |
| 712 | else |
| 713 | #endif |
| 714 | #ifndef CURL_DISABLE_DIGEST_AUTH |
| 715 | if(authstatus->picked == CURLAUTH_DIGEST) { |
| 716 | auth = "Digest"; |
| 717 | result = Curl_output_digest(data, |
| 718 | proxy, |
| 719 | (const unsigned char *)request, |
| 720 | (const unsigned char *)path); |
| 721 | if(result) |
| 722 | return result; |
| 723 | } |
| 724 | else |
| 725 | #endif |
| 726 | #ifndef CURL_DISABLE_BASIC_AUTH |
| 727 | if(authstatus->picked == CURLAUTH_BASIC) { |
| 728 | /* Basic */ |
| 729 | if( |
| 730 | #ifndef CURL_DISABLE_PROXY |
| 731 | (proxy && conn->http_proxy.creds && |
| 732 | Curl_creds_has_user_or_pass(conn->http_proxy.creds) && |
| 733 | !Curl_checkProxyheaders(data, conn, |
| 734 | STRCONST("Proxy-authorization"))) || |
| 735 | #endif |
| 736 | (!proxy && data->state.creds && |
| 737 | Curl_creds_has_user_or_pass(data->state.creds) && |
| 738 | !Curl_checkheaders(data, STRCONST("Authorization")))) { |
| 739 | auth = "Basic"; |
| 740 | result = http_output_basic(data, conn, proxy); |
| 741 | if(result) |
| 742 | return result; |
| 743 | } |
| 744 | |
| 745 | /* NOTE: this function should set 'done' TRUE, as the other auth |
| 746 | functions work that way */ |
| 747 | authstatus->done = TRUE; |
| 748 | } |
| 749 | #endif |
| 750 | #ifndef CURL_DISABLE_BEARER_AUTH |
| 751 | if(authstatus->picked == CURLAUTH_BEARER) { |
| 752 | /* Bearer */ |
| 753 | if(!proxy && Curl_creds_has_oauth_bearer(data->state.creds) && |
| 754 | !Curl_checkheaders(data, STRCONST("Authorization"))) { |
| 755 | auth = "Bearer"; |
| 756 | result = http_output_bearer(data); |
| 757 | if(result) |
| 758 | return result; |
| 759 | } |
| 760 | |
| 761 | /* NOTE: this function should set 'done' TRUE, as the other auth |
| 762 | functions work that way */ |
| 763 | authstatus->done = TRUE; |
| 764 | } |
| 765 | #endif |
| 766 | |
| 767 | if(auth) { |
| 768 | #ifndef CURL_DISABLE_PROXY |
| 769 | if(proxy) |
| 770 | data->info.proxyauthpicked = authstatus->picked; |
| 771 | else |
| 772 | data->info.httpauthpicked = authstatus->picked; |
| 773 | infof(data, "%s auth using %s with user '%s'", |
| 774 | proxy ? "Proxy" : "Server", auth, |
| 775 | proxy ? (conn->http_proxy.creds ? |
| 776 | conn->http_proxy.creds->user : "") : |
| 777 | (data->state.creds ? |
| 778 | data->state.creds->user : "")); |
| 779 | #else |
| 780 | (void)proxy; |
| 781 | infof(data, "Server auth using %s with user '%s'", |
| 782 | auth, data->state.creds ? |
| 783 | data->state.creds->user : ""); |
| 784 | #endif |
| 785 | authstatus->multipass = !authstatus->done; |
| 786 | } |
| 787 | else { |
| 788 | authstatus->multipass = FALSE; |
| 789 | if(proxy) |
| 790 | data->info.proxyauthpicked = 0; |
| 791 | else |
| 792 | data->info.httpauthpicked = 0; |
| 793 | } |
| 794 | |
| 795 | return result; |
| 796 | } |
Show 3 lines (not cited by the judge)
| 850 | #define CURLAUTH_ANY ((~(CURLAUTH_DIGEST_IE | \ |
| 851 | CURLAUTH_HTTPSIG)) & \ |
| 852 | ((unsigned long)0xffffffff)) |
Show 1 lines (not cited by the judge)
| 835 | #define CURLAUTH_NEGOTIATE (((unsigned long)1) << 2) |
Show 2 lines (not cited by the judge)
| 1228 | #define CURL_EASY_STR(d, id) \ |
| 1229 | Curl_u8_strset_get(&(d)->set.strings, (uint8_t)(id)) |
Show 69 lines (not cited by the judge)
| 34 | CURLcode Curl_creds_create(const char *user, |
| 35 | const char *passwd, |
| 36 | const char *oauth_bearer, |
| 37 | const char *sasl_authzid, |
| 38 | const char *sasl_service, |
| 39 | uint8_t source, |
| 40 | struct Curl_creds **pcreds) |
| 41 | { |
| 42 | struct Curl_creds *creds = NULL; |
| 43 | size_t ulen = user ? strlen(user) : 0; |
| 44 | size_t plen = passwd ? strlen(passwd) : 0; |
| 45 | size_t olen = oauth_bearer ? strlen(oauth_bearer) : 0; |
| 46 | size_t salen = sasl_authzid ? strlen(sasl_authzid) : 0; |
| 47 | size_t sslen = sasl_service ? strlen(sasl_service) : 0; |
| 48 | char *s, *buf; |
| 49 | size_t bufsize; |
| 50 | CURLcode result = CURLE_OK; |
| 51 | |
| 52 | Curl_creds_unlink(pcreds); |
| 53 | |
| 54 | /* Everything empty/NULL, this is the NULL credential */ |
| 55 | if(!user && !passwd && !olen && !salen && !sslen) |
| 56 | goto out; |
| 57 | |
| 58 | if((ulen > CURL_MAX_INPUT_LENGTH) || |
| 59 | (plen > CURL_MAX_INPUT_LENGTH) || |
| 60 | (olen > CURL_MAX_INPUT_LENGTH) || |
| 61 | (salen > CURL_MAX_INPUT_LENGTH) || |
| 62 | (sslen > CURL_MAX_INPUT_LENGTH)) { |
| 63 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
| 64 | goto out; |
| 65 | } |
| 66 | |
| 67 | /* null-terminator for user already part of struct */ |
| 68 | bufsize = ulen + plen + 1 + olen + 1 + salen + 1 + sslen + 1; |
| 69 | creds = curlx_calloc(1, sizeof(*creds) + bufsize); |
| 70 | if(!creds) { |
| 71 | result = CURLE_OUT_OF_MEMORY; |
| 72 | goto out; |
| 73 | } |
| 74 | |
| 75 | creds->bufsize = bufsize; |
| 76 | creds->refcount = 1; |
| 77 | creds->source = source; |
| 78 | /* Some compilers try to be too smart about our dynamic struct size */ |
| 79 | buf = ((char *)creds) + offsetof(struct Curl_creds, buf); |
| 80 | creds->user = s = buf; |
| 81 | if(ulen) |
| 82 | memcpy(s, user, ulen + 1); |
| 83 | creds->passwd = s = buf + ulen + 1; |
| 84 | if(plen) |
| 85 | memcpy(s, passwd, plen + 1); |
| 86 | creds->oauth_bearer = s = buf + ulen + 1 + plen + 1; |
| 87 | if(olen) |
| 88 | memcpy(s, oauth_bearer, olen + 1); |
| 89 | creds->sasl_authzid = s = buf + ulen + 1 + plen + 1 + olen + 1; |
| 90 | if(salen) |
| 91 | memcpy(s, sasl_authzid, salen + 1); |
| 92 | creds->sasl_service = s = buf + ulen + 1 + plen + 1 + olen + 1 + salen + 1; |
| 93 | if(sslen) |
| 94 | memcpy(s, sasl_service, sslen + 1); |
| 95 | |
| 96 | out: |
| 97 | if(!result) |
| 98 | *pcreds = creds; |
| 99 | else |
| 100 | Curl_creds_unlink(&creds); |
| 101 | return result; |
| 102 | } |
Show 1 lines (not cited by the judge)
| 75 | #define Curl_creds_has_user(c) ((c) && (c)->user[0]) |
Show 25 lines (not cited by the judge)
| 104 | CURLcode Curl_creds_merge(const char *user, |
| 105 | const char *passwd, |
| 106 | struct Curl_creds *creds_in, |
| 107 | uint8_t source, |
| 108 | struct Curl_creds **pcreds_out) |
| 109 | { |
| 110 | struct Curl_creds *creds_out = NULL; |
| 111 | CURLcode result; |
| 112 | |
| 113 | if(!creds_in) { |
| 114 | result = Curl_creds_create(user, passwd, NULL, NULL, NULL, |
| 115 | source, &creds_out); |
| 116 | } |
| 117 | else { |
| 118 | result = Curl_creds_create(user ? user : Curl_creds_user(creds_in), |
| 119 | passwd ? passwd : Curl_creds_passwd(creds_in), |
| 120 | Curl_creds_oauth_bearer(creds_in), |
| 121 | Curl_creds_sasl_authzid(creds_in), |
| 122 | Curl_creds_sasl_service(creds_in), |
| 123 | source, &creds_out); |
| 124 | } |
| 125 | Curl_creds_link(pcreds_out, creds_out); |
| 126 | Curl_creds_unlink(&creds_out); |
| 127 | return result; |
| 128 | } |
Show 7 lines (not cited by the judge)
| 321 | bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2) |
| 322 | { |
| 323 | return (p1 == p2) || |
| 324 | (p1 && p2 && |
| 325 | (p1->scheme == p2->scheme) && |
| 326 | Curl_peer_same_destination(p1, p2)); |
| 327 | } |
Show 1 lines (not cited by the judge)
| 1048 | #define FALSE false |
Show 9 lines (not cited by the judge)
| 273 | int main(void) |
| 274 | { |
| 275 | size_t i; |
| 276 | |
| 277 | for(i = 0; i < CURL_ARRAYSIZE(disabled); i++) |
| 278 | puts(disabled[i]); |
| 279 | |
| 280 | return 0; |
| 281 | } |