CVE-2026-94265: Authorization: Negotiate sent to foreign origin
What the report claims
curl sends an Authorization: Negotiate header with a fresh Kerberos ticket to a host chosen by an HTTP redirect without --location-trusted, bypassing the guarantee that such headers are not sent to other hosts.
Compare with curl's recorded outcome
Known vulnerability
A real vulnerability: CVE-2026-94265 Authorization: Negotiate sent to foreign origin
Shown for comparison after analysis. The pipeline did not see this label.
ProveIt's finding
The available evidence does not settle the report. A maintainer should investigate.
See the withdrawn model reasoning
The following reasoning failed the citation check and does not support the final finding.
- The gate at lib/http.c:874 uses `data->state.creds || Curl_auth_allowed_to_host(data)`, so any non-NULL credentials (including attacker-supplied userinfo from a redirect URL) bypass the origin check.
- URL-extracted credentials are stored into state.creds without an origin check (lib/url.c:1383-1391), while application-set credentials are correctly guarded (lib/url.c:1355-1360).
- The Negotiate branch in output_auth_headers (lib/http.c:688-701) lacks the Curl_auth_allowed_to_host(data) guard that the HTTPSIG branch has (lib/http.c:671-684).
- Curl_output_negotiate builds the SPN from data->state.origin->hostname (the attacker's host), causing a fresh Kerberos ticket to be minted for the attacker's service.
- The reporter's PoC demonstrates the flaw end-to-end against a real KDC, and the control (no userinfo in redirect) shows the protection works when the trigger is absent.
- The suggested fix adds the missing Curl_auth_allowed_to_host(data) check to the Negotiate branch, mirroring the HTTPSIG branch.
How this analysis ran 5 claims · 6 citations verified
Evidence check: judge quoted an external source that does not say that (https://curl.se/libcurl/c/CURLOPT_UNRESTRICTED_AUTH.html)
Checked against curl permalink d1823ebc17e5 (d1823ebc17e5). Test evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.
Inspect the withdrawn draft reply
This draft belongs to the withdrawn judgment. It may contain unsupported claims.
Thank you for the thorough report and reproduction. The code confirms the flaw: the gate at lib/http.c:874 short-circuits on any credentials, URL-extracted credentials lack an origin check (lib/url.c:1383-1391), and the Negotiate branch omits the cross-host guard that HTTPSIG has (lib/http.c:688-701 vs 671-684). Your fix adding Curl_auth_allowed_to_host(data) to the Negotiate condition is correct and mirrors the existing HTTPSIG pattern. We will apply this fix and credit you.
Inspect all claims and source code 5 claims · 16 code excerpts
This model judgment was withdrawn. The claim assessments below are preserved for inspection and may be wrong.
Report claims
The gate condition `if(data->state.creds || Curl_auth_allowed_to_host(data))` allows authentication to proceed if any credentials are present, without checking that the target host is the original origin.
Read the reasoning
The code at lib/http.c:874 shows the exact condition described. If data->state.creds is truthy (non-NULL), the second operand Curl_auth_allowed_to_host(data) is short-circuited and never evaluated, so the origin check is bypassed.
lib/http.c line 872-878 (gate condition) and lib/url.c lines 1355-1360 (origin-checked credentials) and 1383-1391 (URL‑extracted credentials).
Read the reasoning
The two code paths are present exactly as described: application credentials require Curl_auth_allowed_to_origin, while URL-extracted credentials do not.
A Location header containing userinfo (e.g., "bob:alice@attacker.example/") causes curl to populate state.creds for the foreign origin, satisfying the gate condition.
Read the reasoning
The URL parsing code at lib/url.c:1383-1391 runs after a redirect and stores attacker-supplied userinfo into state.creds without any origin validation, making the gate at lib/http.c:874 true.
The attacker receives an Authorization: Negotiate header carrying a freshly minted Kerberos ticket for their service, enabling impersonation.
Read the reasoning
The Negotiate branch lacks the cross-host guard that HTTPSIG has. Once the gate passes, output_auth_headers calls Curl_output_negotiate, which builds the SPN from the current (attacker) origin and obtains a fresh Kerberos ticket for it.
Run curl with --negotiate --location against a host that redirects to an attacker‑controlled URL containing userinfo; the attacker’s server logs the Authorization header.
Read the reasoning
The reproduction script is detailed and uses only documented curl options (--negotiate, --location, --resolve) without --location-trusted. The deterministic checks confirm the grounded symbols and files exist at the reported revision.
Deterministic checks that failed
- symbol L92() does not exist in curl at d1823ebc17e5 or in any release since 7.50
- quote 1 of 2 quoted lines appear nowhere in curl, e.g. 'https://github.com/curl/curl/blob/d1823ebc17e5778c30f8a6b222'
- quote 2 of 5 quoted lines appear nowhere in curl, e.g. 'https://github.com/curl/curl/blob/d1823ebc17e5778c30f8a6b222'
- commit no commit 557e43b237 in curl's history (may be a fork or PR branch)
What the report says about other projects
confirmed CURLOPT_UNRESTRICTED_AUTH documentation guarantees that with the option unset, libcurl 'does not send custom nor internally generated Authorization: or Cookie: headers on requests done to other hosts than the one used for the initial URL'.
“When this option is not set, libcurl does not send custom nor internally generated Authorization: or Cookie: headers on requests done to other hosts than the one used for the initial URL.” source
Read the original report (excerpt)
Source code
| 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: Gate condition `if(data->state.creds || Curl_auth_allowed_to_host(data))` allows auth to proceed if state.creds is non-NULL, without requiring the origin check to pass. | |
| 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, |
| 900 | const char *request, |
| 901 | Curl_HttpReq httpreq, |
| 902 | const char *path, |
| 903 | const char *query, |
| 1330 | uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, |
| 1331 | Curl_creds_passwd(*pcreds), |
| 1332 | CURLU_URLENCODE); |
| 1333 | if(uc) |
| 1334 | result = Curl_uc_to_curlcode(uc); |
| 1335 | } |
| 1336 | else |
| 1337 | DEBUGASSERT(0); |
| 1338 | } |
| 1339 | |
| 1340 | #ifdef CURLVERBOSE |
| 1341 | Curl_creds_trace(data, data->state.creds, "transfer credentials"); |
| 1342 | #endif |
| 1343 | |
| 1344 | out: |
| 1345 | Curl_creds_unlink(&ncreds_out); |
| 1346 | return result; |
| 1347 | } |
| 1348 | #endif /* CURL_DISABLE_NETRC */ |
| 1349 | |
| 1350 | static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh) |
| 1351 | { |
| 1352 | struct Curl_creds *newcreds = NULL; |
| 1353 | CURLcode result = CURLE_OK; |
| 1354 | |
| 1355 | if((CURL_EASY_STR(data, STRING_USERNAME) || |
| C2: Application-set credentials are guarded by `Curl_auth_allowed_to_origin(data, data->state.origin)` at lines 1355-1360. | |
| 1356 | CURL_EASY_STR(data, STRING_PASSWORD) || |
| 1357 | CURL_EASY_STR(data, STRING_BEARER) || |
| 1358 | CURL_EASY_STR(data, STRING_SASL_AUTHZID) || |
| 1359 | CURL_EASY_STR(data, STRING_SERVICE_NAME)) && |
| 1360 | Curl_auth_allowed_to_origin(data, data->state.origin)) { |
| 1361 | result = Curl_creds_create(CURL_EASY_STR(data, STRING_USERNAME), |
| 1362 | CURL_EASY_STR(data, STRING_PASSWORD), |
| 1363 | CURL_EASY_STR(data, STRING_BEARER), |
| 1364 | CURL_EASY_STR(data, STRING_SASL_AUTHZID), |
| 1365 | CURL_EASY_STR(data, STRING_SERVICE_NAME), |
| 1366 | CREDS_OPTION, &newcreds); |
| 1367 | if(result) |
| 1368 | goto out; |
| 1369 | if(newcreds && |
| 1370 | !(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL) && |
| 1371 | (str_has_ctrl(Curl_creds_user(newcreds)) || |
| 1372 | str_has_ctrl(Curl_creds_passwd(newcreds)))) { |
| 1373 | /* if the protocol cannot handle control codes in credentials, make |
| 1374 | sure there are none */ |
| 1375 | failf(data, "control code detected in credentials"); |
| 1376 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
| 1377 | goto out; |
| 1378 | } |
| 1379 | } |
| 1380 | |
| 1381 | /* Extract credentials from the URL only if there are none OR |
| 1382 | * if no CURLOPT_USER was set. */ |
| 1383 | if(!newcreds || !Curl_creds_has_user(newcreds)) { |
| C2: URL-extracted credentials (lines 1383-1391) have no origin check; they are parsed from the redirect target URL and stored into state.creds unconditionally. | |
| C3: When a redirect Location header contains userinfo (e.g., bob:alice@attacker.example), curl_url_get extracts it and the merge at line 1411 populates data->state.creds for the foreign origin. | |
| 1384 | char *user = NULL; |
| 1385 | char *passwd = NULL; |
| 1386 | char *udecoded = NULL; |
| 1387 | char *pdecoded = NULL; |
| 1388 | CURLUcode uc; |
| 1389 | |
| 1390 | uc = curl_url_get(uh, CURLUPART_USER, &user, 0); |
| 1391 | if(uc && (uc != CURLUE_NO_USER)) |
| 1392 | result = Curl_uc_to_curlcode(uc); |
| 1393 | if(!result) { |
| 1394 | uc = curl_url_get(uh, CURLUPART_PASSWORD, &passwd, 0); |
| 1395 | if(uc && (uc != CURLUE_NO_PASSWORD)) |
| 1396 | result = Curl_uc_to_curlcode(uc); |
| 1397 | } |
| 1398 | if(!result && user) { |
| 1399 | result = Curl_urldecode(user, 0, &udecoded, NULL, |
| 1400 | (data->state.origin->scheme->flags & |
| 1401 | PROTOPT_USERPWDCTRL) ? |
| 1402 | REJECT_ZERO : REJECT_CTRL); |
| 1403 | } |
| 1404 | if(!result && passwd) { |
| 1405 | result = Curl_urldecode(passwd, 0, &pdecoded, NULL, |
| 1406 | (data->state.origin->scheme->flags & |
| 1407 | PROTOPT_USERPWDCTRL) ? |
| 1408 | REJECT_ZERO : REJECT_CTRL); |
| 1409 | } |
| 1410 | if(!result) |
| 1411 | result = Curl_creds_merge(udecoded, pdecoded, newcreds, |
| 1412 | CREDS_URL, &newcreds); |
| 1413 | |
| 1414 | curlx_free(udecoded); |
| 1415 | curlx_free(pdecoded); |
| 1416 | curlx_free(passwd); |
| 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) { |
| C4: Negotiate branch (lines 688-701) only checks for absence of a custom Authorization header; it does NOT check Curl_auth_allowed_to_host(data), unlike the HTTPSIG branch above (lines 671-684). | |
| 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); |
| 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 | } |
| 52 | CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, |
| 53 | bool proxy, const char *header) |
| 54 | { |
| 55 | CURLcode result; |
| 56 | size_t len; |
| 57 | |
| 58 | /* Point to credentials and host */ |
| 59 | struct Curl_creds *creds = NULL; |
| 60 | const char *host; |
| 61 | |
| 62 | /* Point to the correct struct with this */ |
| 63 | struct negotiatedata *neg_ctx; |
| 64 | curlnegotiate state; |
| 65 | |
| 66 | if(proxy) { |
| 67 | #ifndef CURL_DISABLE_PROXY |
| 68 | creds = conn->http_proxy.creds; |
| 69 | host = conn->http_proxy.peer->hostname; |
| 70 | state = conn->proxy_negotiate_state; |
| 71 | #else |
| 72 | return CURLE_NOT_BUILT_IN; |
| 73 | #endif |
| 74 | } |
| 75 | else { |
| 76 | creds = data->state.creds; |
| 77 | host = data->state.origin->hostname; |
| C4: Curl_output_negotiate uses data->state.origin->hostname (the attacker's host) to build the SPN, so a fresh ticket is minted for the attacker's service. | |
| 78 | state = conn->http_negotiate_state; |
| 79 | } |
| 80 | |
| 81 | neg_ctx = Curl_auth_nego_get(conn, proxy); |
| 82 | if(!neg_ctx) |
| 83 | return CURLE_OUT_OF_MEMORY; |
| 84 | |
| 85 | /* Obtain the input token, if any */ |
| 86 | header += CURL_CSTRLEN("Negotiate"); |
| 87 | curlx_str_passblanks(&header); |
| 88 | |
| 89 | len = strlen(header); |
| 90 | neg_ctx->havenegdata = len != 0; |
| 91 | if(!len) { |
| 92 | if(state == GSS_AUTHSUCC) { |
| 93 | infof(data, "Negotiate auth restarted"); |
| 94 | http_auth_nego_reset(conn, neg_ctx, proxy); |
| 95 | } |
| 96 | else if(state != GSS_AUTHNONE) { |
| 97 | /* The server rejected our authentication and has not supplied any more |
| 98 | negotiation mechanisms */ |
| 99 | http_auth_nego_reset(conn, neg_ctx, proxy); |
| 100 | return CURLE_LOGIN_DENIED; |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | /* Supports SSL channel binding for Windows ISS extended protection */ |
| 105 | #ifdef USE_WINDOWS_SSPI |
| 106 | neg_ctx->sslContext = conn->sslContext; |
| 107 | #endif |
| 108 | /* Check if the connection is using SSL and get the channel binding data */ |
| 109 | #ifdef GSS_C_CHANNEL_BOUND_FLAG |
| 110 | #ifdef USE_SSL |
| 111 | curlx_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE + 1); |
| 112 | if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { |
| 113 | result = Curl_ssl_get_channel_binding(data, FIRSTSOCKET, |
| 114 | &neg_ctx->channel_binding_data); |
| 115 | if(result) { |
| 116 | http_auth_nego_reset(conn, neg_ctx, proxy); |
| 117 | return result; |
| 118 | } |
| 119 | } |
| 120 | #else |
| 121 | curlx_dyn_init(&neg_ctx->channel_binding_data, 1); |
| 122 | #endif /* USE_SSL */ |
| 123 | #endif /* GSS_C_CHANNEL_BOUND_FLAG */ |
| 124 | |
| 125 | /* Initialize the security context and decode our challenge */ |
| 126 | result = Curl_auth_decode_spnego_message(data, creds, "HTTP", |
| 127 | host, header, neg_ctx); |
| 128 | |
| 129 | #ifdef GSS_C_CHANNEL_BOUND_FLAG |
| 130 | curlx_dyn_free(&neg_ctx->channel_binding_data); |
| 131 | #endif |
| 132 | |
| 133 | if(result) |
| 134 | http_auth_nego_reset(conn, neg_ctx, proxy); |
| 135 | |
| 136 | if(!result && !proxy) { |
| 137 | /* Start it up. From this time onwards, the connection is tied |
| 138 | * tp the credentials used. */ |
| 139 | if(conn->creds_origin && |
| 140 | !Curl_peer_equal(conn->creds_origin, data->state.origin)) { |
| 141 | DEBUGASSERT(0); /* should not happen. */ |
| 142 | return CURLE_FAILED_INIT; |
| 143 | } |
| 144 | if(conn->creds && !Curl_creds_same(creds, conn->creds)) { |
| 145 | DEBUGASSERT(0); /* should not happen. */ |
| 146 | return CURLE_FAILED_INIT; |
| 147 | } |
| 148 | Curl_peer_link(&conn->creds_origin, data->state.origin); |
| 149 | Curl_creds_link(&conn->creds, creds); |
| 150 | } |
| 151 | |
| 152 | return result; |
Show 166 lines (not cited by the judge)
| 67 | * chlg64 [in] - The optional base64 encoded challenge message. |
| 68 | * nego [in/out] - The Negotiate data struct being used and modified. |
| 69 | * |
| 70 | * Returns CURLE_OK on success. |
| 71 | */ |
| 72 | CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, |
| 73 | struct Curl_creds *creds, |
| 74 | const char *default_service, |
| 75 | const char *host, |
| 76 | const char *chlg64, |
| 77 | struct negotiatedata *nego) |
| 78 | { |
| 79 | CURLcode result = CURLE_OK; |
| 80 | size_t chlglen = 0; |
| 81 | unsigned char *chlg = NULL; |
| 82 | OM_uint32 major_status; |
| 83 | OM_uint32 minor_status; |
| 84 | OM_uint32 unused_status; |
| 85 | gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; |
| 86 | gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; |
| 87 | gss_channel_bindings_t chan_bindings = GSS_C_NO_CHANNEL_BINDINGS; |
| 88 | #ifdef GSS_C_CHANNEL_BOUND_FLAG |
| 89 | struct gss_channel_bindings_struct chan; |
| 90 | #endif |
| 91 | |
| 92 | (void)creds; |
| 93 | |
| 94 | if(nego->context && nego->status == GSS_S_COMPLETE) { |
| 95 | /* We finished successfully our part of authentication, but server |
| 96 | * rejected it (since we are again here). Exit with an error since we |
| 97 | * cannot invent anything better */ |
| 98 | Curl_auth_cleanup_spnego(nego); |
| 99 | return CURLE_LOGIN_DENIED; |
| 100 | } |
| 101 | |
| 102 | if(!nego->spn) { |
| 103 | gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; |
| 104 | |
| 105 | /* Generate our SPN */ |
| 106 | const char *service = Curl_creds_has_sasl_service(creds) ? |
| 107 | Curl_creds_sasl_service(creds) : default_service; |
| 108 | char *spn = Curl_auth_build_spn(service, NULL, host); |
| 109 | if(!spn) |
| 110 | return CURLE_OUT_OF_MEMORY; |
| 111 | |
| 112 | /* Populate the SPN structure */ |
| 113 | spn_token.value = spn; |
| 114 | spn_token.length = strlen(spn); |
| 115 | |
| 116 | /* Import the SPN */ |
| 117 | major_status = gss_import_name(&minor_status, &spn_token, |
| 118 | GSS_C_NT_HOSTBASED_SERVICE, |
| 119 | &nego->spn); |
| 120 | if(GSS_ERROR(major_status)) { |
| 121 | Curl_gss_log_error(data, "gss_import_name() failed: ", |
| 122 | major_status, minor_status); |
| 123 | |
| 124 | curlx_free(spn); |
| 125 | |
| 126 | return CURLE_AUTH_ERROR; |
| 127 | } |
| 128 | |
| 129 | curlx_free(spn); |
| 130 | } |
| 131 | |
| 132 | if(chlg64 && *chlg64) { |
| 133 | /* Decode the base-64 encoded challenge message */ |
| 134 | if(*chlg64 != '=') { |
| 135 | result = curlx_base64_decode(chlg64, &chlg, &chlglen); |
| 136 | if(result) |
| 137 | return result; |
| 138 | } |
| 139 | |
| 140 | /* Ensure we have a valid challenge message */ |
| 141 | if(!chlg) { |
| 142 | infof(data, "SPNEGO handshake failure (empty challenge message)"); |
| 143 | return CURLE_BAD_CONTENT_ENCODING; |
| 144 | } |
| 145 | |
| 146 | /* Setup the challenge "input" security buffer */ |
| 147 | input_token.value = chlg; |
| 148 | input_token.length = chlglen; |
| 149 | } |
| 150 | |
| 151 | /* Set channel binding data if available */ |
| 152 | #ifdef GSS_C_CHANNEL_BOUND_FLAG |
| 153 | if(curlx_dyn_len(&nego->channel_binding_data)) { |
| 154 | memset(&chan, 0, sizeof(struct gss_channel_bindings_struct)); |
| 155 | chan.application_data.length = curlx_dyn_len(&nego->channel_binding_data); |
| 156 | chan.application_data.value = curlx_dyn_ptr(&nego->channel_binding_data); |
| 157 | chan_bindings = &chan; |
| 158 | } |
| 159 | #endif |
| 160 | |
| 161 | #ifdef HAVE_GSS_SET_NEG_MECHS |
| 162 | /* Acquire explicit credentials and restrict SPNEGO sub-mechanisms to |
| 163 | * exclude NTLM. We enumerate all available mechanisms and filter out |
| 164 | * the NTLMSSP OID, matching SSPI's "!ntlm". */ |
| 165 | if(nego->cred == GSS_C_NO_CREDENTIAL) { |
| 166 | /* OID 1.3.6.1.4.1.311.2.2.10 (NTLMSSP) */ |
| 167 | static const gss_OID_desc ntlmssp_oid = { |
| 168 | 10, CURL_UNCONST("\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a") |
| 169 | }; |
| 170 | gss_OID_set available_mechs = GSS_C_NO_OID_SET; |
| 171 | gss_OID_set filtered_mechs = GSS_C_NO_OID_SET; |
| 172 | |
| 173 | /* Acquire default credentials for SPNEGO */ |
| 174 | major_status = Curl_gss_acquire_cred(&minor_status, GSS_C_NO_NAME, |
| 175 | GSS_C_INDEFINITE, GSS_C_NO_OID_SET, |
| 176 | GSS_C_INITIATE, &nego->cred, NULL, NULL); |
| 177 | if(GSS_ERROR(major_status)) { |
| 178 | Curl_gss_log_error(data, "gss_acquire_cred() failed: ", |
| 179 | major_status, minor_status); |
| 180 | curlx_safefree(input_token.value); |
| 181 | return CURLE_AUTH_ERROR; |
| 182 | } |
| 183 | |
| 184 | /* Get all available mechanisms */ |
| 185 | major_status = Curl_gss_indicate_mechs(&minor_status, &available_mechs); |
| 186 | if(!GSS_ERROR(major_status)) { |
| 187 | /* Build a set excluding NTLMSSP */ |
| 188 | major_status = gss_create_empty_oid_set(&minor_status, &filtered_mechs); |
| 189 | if(!GSS_ERROR(major_status)) { |
| 190 | size_t i; |
| 191 | for(i = 0; i < available_mechs->count; i++) { |
| 192 | gss_OID oid = &available_mechs->elements[i]; |
| 193 | if(oid->length != ntlmssp_oid.length || |
| 194 | memcmp(oid->elements, ntlmssp_oid.elements, oid->length)) { |
| 195 | gss_add_oid_set_member(&minor_status, oid, &filtered_mechs); |
| 196 | } |
| 197 | } |
| 198 | /* Restrict SPNEGO to only use non-NTLM mechanisms */ |
| 199 | major_status = Curl_gss_set_neg_mechs(&minor_status, nego->cred, |
| 200 | filtered_mechs); |
| 201 | if(GSS_ERROR(major_status)) { |
| 202 | Curl_gss_log_error(data, "gss_set_neg_mechs() failed: ", |
| 203 | major_status, minor_status); |
| 204 | } |
| 205 | gss_release_oid_set(&minor_status, &filtered_mechs); |
| 206 | } |
| 207 | gss_release_oid_set(&minor_status, &available_mechs); |
| 208 | } |
| 209 | } |
| 210 | #endif /* HAVE_GSS_SET_NEG_MECHS */ |
| 211 | |
| 212 | /* Generate our challenge-response message */ |
| 213 | major_status = Curl_gss_init_sec_context(data, |
| 214 | &minor_status, |
| 215 | &nego->context, |
| 216 | nego->spn, |
| 217 | &Curl_spnego_mech_oid, |
| 218 | chan_bindings, |
| 219 | &input_token, |
| 220 | &output_token, |
| 221 | TRUE, |
| 222 | NULL, |
| 223 | nego->cred); |
| 224 | |
| 225 | /* Free the decoded challenge as it is not required anymore */ |
| 226 | curlx_safefree(input_token.value); |
| 227 | |
| 228 | nego->status = major_status; |
| 229 | if(GSS_ERROR(major_status)) { |
| 230 | if(output_token.value) |
| 231 | gss_release_buffer(&unused_status, &output_token); |
| 232 |
Show 60 lines (not cited by the judge)
| 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) |
| 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 || |
| 149 | Curl_peer_equal(data->state.initial_origin, origin); |
| 150 | } |
| 151 | |
| 152 | #ifdef USE_NTLM |
| 153 | static void ntlm_conn_dtor(const 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 |
Show 56 lines (not cited by the judge)
| 1853 | /* a Host: header was sent already, do not pass on any custom |
| 1854 | Host: header as that will produce *two* in the same |
| 1855 | request! */ |
| 1856 | curlx_str_casecompare(&name, "Host")) |
| 1857 | ; |
| 1858 | else if(data->state.httpreq == HTTPREQ_POST_FORM && |
| 1859 | /* this header (extended by formdata.c) is sent later */ |
| 1860 | curlx_str_casecompare(&name, "Content-Type")) |
| 1861 | ; |
| 1862 | else if(data->state.httpreq == HTTPREQ_POST_MIME && |
| 1863 | /* this header is sent later */ |
| 1864 | curlx_str_casecompare(&name, "Content-Type")) |
| 1865 | ; |
| 1866 | else if(data->req.authneg && |
| 1867 | /* while doing auth neg, do not allow the custom length since |
| 1868 | we will force length zero then */ |
| 1869 | curlx_str_casecompare(&name, "Content-Length")) |
| 1870 | ; |
| 1871 | else if(curlx_str_casecompare(&name, "Connection")) |
| 1872 | /* Connection headers are handled specially */ |
| 1873 | ; |
| 1874 | else if((httpversion >= 20) && |
| 1875 | curlx_str_casecompare(&name, "Transfer-Encoding")) |
| 1876 | /* HTTP/2 does not support chunked requests */ |
| 1877 | ; |
| 1878 | else if((curlx_str_casecompare(&name, "Authorization") || |
| 1879 | curlx_str_casecompare(&name, "Cookie")) && |
| 1880 | /* be careful of sending this potentially sensitive header to |
| 1881 | other hosts */ |
| 1882 | !Curl_auth_allowed_to_host(data)) |
| 1883 | ; |
| 1884 | else if(blankheader) { |
| 1885 | result = curlx_dyn_addn(req, curlx_str(&name), curlx_strlen(&name)); |
| 1886 | if(!result) |
| 1887 | result = curlx_dyn_addn(req, STRCONST(":\r\n")); |
| 1888 | } |
| 1889 | else |
| 1890 | result = curlx_dyn_addf(req, "%s\r\n", origp); |
| 1891 | |
| 1892 | if(result) |
| 1893 | return result; |
| 1894 | } |
| 1895 | } |
| 1896 | |
| 1897 | return CURLE_OK; |
| 1898 | } |
| 1899 | |
| 1900 | #ifndef CURL_DISABLE_PARSEDATE |
| 1901 | CURLcode Curl_add_timecondition(struct Curl_easy *data, |
| 1902 | struct dynbuf *req) |
| 1903 | { |
| 1904 | const struct tm *tm; |
| 1905 | struct tm keeptime; |
| 1906 | CURLcode result; |
| 1907 | char datestr[80]; |
| 1908 | const char *condp; |
Show 17 lines (not cited by the judge)
| 150 | char *Curl_checkProxyheaders(struct Curl_easy *data, |
| 151 | const struct connectdata *conn, |
| 152 | const char *thisheader, |
| 153 | const size_t thislen) |
| 154 | { |
| 155 | struct curl_slist *head; |
| 156 | |
| 157 | for(head = (conn->http_proxy.peer && data->set.sep_headers) ? |
| 158 | data->set.proxyheaders : data->set.headers; |
| 159 | head; head = head->next) { |
| 160 | if(curl_strnequal(head->data, thisheader, thislen) && |
| 161 | Curl_headersep(head->data[thislen])) |
| 162 | return head->data; |
| 163 | } |
| 164 | |
| 165 | return NULL; |
| 166 | } |
Show 16 lines (not cited by the judge)
| 84 | char *Curl_checkheaders(const struct Curl_easy *data, |
| 85 | const char *thisheader, |
| 86 | const size_t thislen) |
| 87 | { |
| 88 | struct curl_slist *head; |
| 89 | DEBUGASSERT(thislen); |
| 90 | DEBUGASSERT(thisheader[thislen - 1] != ':'); |
| 91 | |
| 92 | for(head = data->set.headers; head; head = head->next) { |
| 93 | if(curl_strnequal(head->data, thisheader, thislen) && |
| 94 | Curl_headersep(head->data[thislen])) |
| 95 | return head->data; |
| 96 | } |
| 97 | |
| 98 | return NULL; |
| 99 | } |
Show 1 lines (not cited by the judge)
| 75 | #define Curl_creds_has_user(c) ((c) && (c)->user[0]) |
Show 159 lines (not cited by the judge)
| 529 | CURLcode Curl_output_httpsig(struct Curl_easy *data) |
| 530 | { |
| 531 | CURLcode result = CURLE_OUT_OF_MEMORY; |
| 532 | struct connectdata *conn = data->conn; |
| 533 | const char *path; |
| 534 | const char *query; |
| 535 | Curl_HttpReq httpreq; |
| 536 | const char *method = NULL; |
| 537 | const char *hexkey = CURL_EASY_STR(data, STRING_HTTPSIG_KEY); |
| 538 | const char *keyid = CURL_EASY_STR(data, STRING_HTTPSIG_KEYID); |
| 539 | enum httpsig_alg alg; |
| 540 | time_t created; |
| 541 | struct dynbuf sig_params; |
| 542 | struct dynbuf sig_base; |
| 543 | struct dynbuf sig_hdr; |
| 544 | struct dynbuf input_hdr; |
| 545 | struct dynbuf authority_buf; |
| 546 | const char *authority; |
| 547 | const char *components[HTTPSIG_MAX_COMPONENTS]; |
| 548 | size_t ncomp = 0; |
| 549 | unsigned char *keybuf = NULL; |
| 550 | size_t keylen = 0; |
| 551 | unsigned char raw_sig[HTTPSIG_MAX_RAW_SIG]; |
| 552 | size_t raw_sig_len = 0; |
| 553 | char *auth_headers = NULL; |
| 554 | char *hdrs_copy = NULL; |
| 555 | struct dynbuf query_dyn; |
| 556 | |
| 557 | alg = id_to_alg(data->set.httpsig_algorithm); |
| 558 | if(alg == HTTPSIG_ALG_UNKNOWN) { |
| 559 | failf(data, "httpsig: CURLOPT_HTTPSIG_ALGORITHM is required"); |
| 560 | return CURLE_BAD_FUNCTION_ARGUMENT; |
| 561 | } |
| 562 | |
| 563 | if(!hexkey || !*hexkey) { |
| 564 | failf(data, "httpsig: CURLOPT_HTTPSIG_KEY is required"); |
| 565 | return CURLE_BAD_FUNCTION_ARGUMENT; |
| 566 | } |
| 567 | if(!keyid || !*keyid) { |
| 568 | failf(data, "httpsig: CURLOPT_HTTPSIG_KEYID is required"); |
| 569 | return CURLE_BAD_FUNCTION_ARGUMENT; |
| 570 | } |
| 571 | |
| 572 | curlx_dyn_init(&sig_params, CURL_MAX_HTTP_HEADER); |
| 573 | curlx_dyn_init(&sig_base, HTTPSIG_MAX_SIG_BASE); |
| 574 | curlx_dyn_init(&sig_hdr, CURL_MAX_HTTP_HEADER); |
| 575 | curlx_dyn_init(&input_hdr, CURL_MAX_HTTP_HEADER); |
| 576 | curlx_dyn_init(&authority_buf, CURL_MAX_HTTP_HEADER); |
| 577 | curlx_dyn_init(&query_dyn, CURL_MAX_HTTP_HEADER); |
| 578 | |
| 579 | if(Curl_checkheaders(data, STRCONST("Signature")) || |
| 580 | Curl_checkheaders(data, STRCONST("Signature-Input"))) { |
| 581 | /* user provides their own Signature / Signature-Input headers, consider |
| 582 | this done */ |
| 583 | goto done; |
| 584 | } |
| 585 | |
| 586 | result = decode_hex_key(data, hexkey, &keybuf, &keylen); |
| 587 | if(result) |
| 588 | goto fail; |
| 589 | |
| 590 | if(alg == HTTPSIG_ALG_ED25519 && keylen != 32) { |
| 591 | failf(data, "httpsig: ed25519 requires a 32-byte key (got %zu)", keylen); |
| 592 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
| 593 | goto fail; |
| 594 | } |
| 595 | |
| 596 | Curl_http_method(data, &method, &httpreq); |
| 597 | |
| 598 | path = data->state.up.path; |
| 599 | if(!path || !*path) |
| 600 | path = "/"; |
| 601 | |
| 602 | query = data->state.up.query; |
| 603 | |
| 604 | result = httpsig_authority(data, conn, &authority_buf); |
| 605 | if(result) |
| 606 | goto fail; |
| 607 | authority = curlx_dyn_ptr(&authority_buf); |
| 608 | |
| 609 | /* Build @query value: RFC 9421 Section 2.2.7 - always starts with "?" */ |
| 610 | if(query && *query) |
| 611 | result = curlx_dyn_addf(&query_dyn, "?%s", query); |
| 612 | else |
| 613 | result = curlx_dyn_add(&query_dyn, "?"); |
| 614 | if(result) |
| 615 | goto fail; |
| 616 | |
| 617 | result = parse_components(data, query, components, &ncomp, &hdrs_copy); |
| 618 | if(result) |
| 619 | goto fail; |
| 620 | |
| 621 | created = httpsig_get_created(); |
| 622 | |
| 623 | result = build_sig_params(&sig_params, components, ncomp, |
| 624 | created, keyid, alg); |
| 625 | if(result) |
| 626 | goto fail; |
| 627 | |
| 628 | infof(data, "httpsig: Signature-Input params: %s", |
| 629 | curlx_dyn_ptr(&sig_params)); |
| 630 | |
| 631 | result = build_sig_base(&sig_base, components, ncomp, |
| 632 | method, authority, path, |
| 633 | curlx_dyn_ptr(&query_dyn), |
| 634 | data, curlx_dyn_ptr(&sig_params)); |
| 635 | if(result) |
| 636 | goto fail; |
| 637 | |
| 638 | infof(data, "httpsig: Signature base: [%s]", |
| 639 | curlx_dyn_ptr(&sig_base)); |
| 640 | |
| 641 | result = httpsig_sign_base(data, alg, keybuf, keylen, &sig_base, |
| 642 | raw_sig, &raw_sig_len); |
| 643 | if(result) |
| 644 | goto fail; |
| 645 | |
| 646 | result = curlx_dyn_add(&sig_hdr, HTTPSIG_DEFAULT_LABEL "="); |
| 647 | if(result) |
| 648 | goto fail; |
| 649 | result = sf_encode_byte_seq(raw_sig, raw_sig_len, &sig_hdr); |
| 650 | if(result) |
| 651 | goto fail; |
| 652 | |
| 653 | result = curlx_dyn_addf(&input_hdr, "%s=%s", HTTPSIG_DEFAULT_LABEL, |
| 654 | curlx_dyn_ptr(&sig_params)); |
| 655 | if(result) |
| 656 | goto fail; |
| 657 | |
| 658 | auth_headers = curl_maprintf("Signature-Input: %s\r\n" |
| 659 | "Signature: %s\r\n", |
| 660 | curlx_dyn_ptr(&input_hdr), |
| 661 | curlx_dyn_ptr(&sig_hdr)); |
| 662 | if(!auth_headers) |
| 663 | goto fail; |
| 664 | |
| 665 | infof(data, "httpsig: Signature-Input: %s", curlx_dyn_ptr(&input_hdr)); |
| 666 | infof(data, "httpsig: Signature: %s", curlx_dyn_ptr(&sig_hdr)); |
| 667 | done: |
| 668 | curlx_free(data->req.hd_auth); |
| 669 | data->req.hd_auth = auth_headers; |
| 670 | data->state.authhost.done = TRUE; |
| 671 | result = CURLE_OK; |
| 672 | |
| 673 | fail: |
| 674 | if(keybuf) { |
| 675 | memset(keybuf, 0, keylen); |
| 676 | curlx_free(keybuf); |
| 677 | } |
| 678 | memset(raw_sig, 0, sizeof(raw_sig)); |
| 679 | curlx_free(hdrs_copy); |
| 680 | curlx_dyn_free(&sig_params); |
| 681 | curlx_dyn_free(&sig_base); |
| 682 | curlx_dyn_free(&sig_hdr); |
| 683 | curlx_dyn_free(&input_hdr); |
| 684 | curlx_dyn_free(&authority_buf); |
| 685 | curlx_dyn_free(&query_dyn); |
| 686 | return result; |
| 687 | } |
Show 110 lines (not cited by the judge)
| 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); |
| 216 | if(result) |
| 217 | return result; |
| 218 | |
| 219 | userp = curl_maprintf("%sAuthorization: Negotiate %s\r\n", |
| 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 | } |
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)
| 1249 | #define STRCONST(x) x, CURL_CSTRLEN(x) |
Show 100 lines (not cited by the judge)
| 1676 | CURLUcode curl_url_get(const CURLU *u, CURLUPart what, |
| 1677 | char **part, unsigned int flags) |
| 1678 | { |
| 1679 | const char *ptr; |
| 1680 | CURLUcode ifmissing = CURLUE_UNKNOWN_PART; |
| 1681 | char portbuf[7]; |
| 1682 | bool plusdecode = FALSE; |
| 1683 | if(!u) |
| 1684 | return CURLUE_BAD_HANDLE; |
| 1685 | if(!part) |
| 1686 | return CURLUE_BAD_PARTPOINTER; |
| 1687 | *part = NULL; |
| 1688 | |
| 1689 | switch(what) { |
| 1690 | case CURLUPART_SCHEME: |
| 1691 | ptr = u->scheme; |
| 1692 | ifmissing = CURLUE_NO_SCHEME; |
| 1693 | flags &= ~U_CURLU_URLDECODE; /* never for schemes */ |
| 1694 | if((flags & CURLU_NO_GUESS_SCHEME) && u->guessed_scheme) |
| 1695 | return CURLUE_NO_SCHEME; |
| 1696 | break; |
| 1697 | case CURLUPART_USER: |
| 1698 | ptr = u->user; |
| 1699 | ifmissing = CURLUE_NO_USER; |
| 1700 | break; |
| 1701 | case CURLUPART_PASSWORD: |
| 1702 | ptr = u->password; |
| 1703 | ifmissing = CURLUE_NO_PASSWORD; |
| 1704 | break; |
| 1705 | case CURLUPART_OPTIONS: |
| 1706 | ptr = u->options; |
| 1707 | ifmissing = CURLUE_NO_OPTIONS; |
| 1708 | break; |
| 1709 | case CURLUPART_HOST: |
| 1710 | ptr = u->host; |
| 1711 | ifmissing = CURLUE_NO_HOST; |
| 1712 | break; |
| 1713 | case CURLUPART_ZONEID: |
| 1714 | ptr = u->zoneid; |
| 1715 | ifmissing = CURLUE_NO_ZONEID; |
| 1716 | break; |
| 1717 | case CURLUPART_PORT: |
| 1718 | ptr = NULL; |
| 1719 | ifmissing = CURLUE_NO_PORT; |
| 1720 | flags &= ~U_CURLU_URLDECODE; /* never for port */ |
| 1721 | if(u->port_present) { |
| 1722 | const struct Curl_scheme *h = u->scheme ? |
| 1723 | Curl_get_scheme(u->scheme) : NULL; |
| 1724 | /* there is a stored port number, but ask to inhibit if |
| 1725 | it matches the default one for the scheme */ |
| 1726 | if(h && (h->defport == u->portnum) && |
| 1727 | (flags & CURLU_NO_DEFAULT_PORT)) { |
| 1728 | ptr = NULL; |
| 1729 | } |
| 1730 | else { |
| 1731 | curl_msnprintf(portbuf, sizeof(portbuf), "%u", u->portnum); |
| 1732 | ptr = portbuf; |
| 1733 | } |
| 1734 | } |
| 1735 | else if((flags & CURLU_DEFAULT_PORT) && u->scheme) { |
| 1736 | /* there is no stored port number, but asked to deliver |
| 1737 | a default one for the scheme */ |
| 1738 | const struct Curl_scheme *h = Curl_get_scheme(u->scheme); |
| 1739 | if(h) { |
| 1740 | curl_msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); |
| 1741 | ptr = portbuf; |
| 1742 | } |
| 1743 | } |
| 1744 | break; |
| 1745 | case CURLUPART_PATH: |
| 1746 | ptr = u->path; |
| 1747 | if(!ptr) |
| 1748 | ptr = "/"; |
| 1749 | break; |
| 1750 | case CURLUPART_QUERY: |
| 1751 | ptr = u->query; |
| 1752 | ifmissing = CURLUE_NO_QUERY; |
| 1753 | plusdecode = flags & CURLU_URLDECODE; |
| 1754 | if(ptr && !ptr[0] && !(flags & CURLU_GET_EMPTY)) |
| 1755 | /* there was a blank query and the user does not ask for it */ |
| 1756 | ptr = NULL; |
| 1757 | break; |
| 1758 | case CURLUPART_FRAGMENT: |
| 1759 | ptr = u->fragment; |
| 1760 | ifmissing = CURLUE_NO_FRAGMENT; |
| 1761 | if(!ptr && u->fragment_present && flags & CURLU_GET_EMPTY) |
| 1762 | /* there was a blank fragment and the user asks for it */ |
| 1763 | ptr = ""; |
| 1764 | break; |
| 1765 | case CURLUPART_URL: |
| 1766 | return urlget_url(u, part, flags); |
| 1767 | default: |
| 1768 | ptr = NULL; |
| 1769 | break; |
| 1770 | } |
| 1771 | if(ptr) |
| 1772 | return urlget_format(u, what, ptr, part, plusdecode, flags); |
| 1773 | |
| 1774 | return ifmissing; |
| 1775 | } |
Show 161 lines (not cited by the judge)
| 59 | add_custom_target(curlu-unitprotos DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/unitprotos.h") |
| 60 | endif() |
| 61 | |
| 62 | ## Library definition |
| 63 | |
| 64 | if(NOT DEFINED IMPORT_LIB_SUFFIX) |
| 65 | # Suffix implib name with "_imp" by default, to avoid conflicting with |
| 66 | # the generated static "libcurl.lib" (typically with MSVC). |
| 67 | if(WIN32 AND BUILD_SHARED_LIBS AND |
| 68 | CMAKE_IMPORT_LIBRARY_SUFFIX STREQUAL CMAKE_STATIC_LIBRARY_SUFFIX) |
| 69 | set(IMPORT_LIB_SUFFIX "_imp") |
| 70 | else() |
| 71 | set(IMPORT_LIB_SUFFIX "") |
| 72 | endif() |
| 73 | endif() |
| 74 | if(NOT DEFINED STATIC_LIB_SUFFIX) |
| 75 | set(STATIC_LIB_SUFFIX "") |
| 76 | endif() |
| 77 | |
| 78 | # Detect implib static lib filename collision |
| 79 | if(WIN32 AND BUILD_STATIC_LIBS AND BUILD_SHARED_LIBS AND |
| 80 | "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}" STREQUAL |
| 81 | "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}") |
| 82 | message(FATAL_ERROR "Library suffix is the same ('${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}') " |
| 83 | "for the import and static '${LIBCURL_OUTPUT_NAME}' library. " |
| 84 | "Set IMPORT_LIB_SUFFIX and/or STATIC_LIB_SUFFIX to different values, " |
| 85 | "or disable building either the shared or static library to avoid the filename collision.") |
| 86 | endif() |
| 87 | |
| 88 | # Whether to do a single compilation pass for libcurl sources and reuse these |
| 89 | # objects to generate both static and shared target. |
| 90 | if(NOT DEFINED SHARE_LIB_OBJECT) |
| 91 | # Enable it by default on platforms where PIC is the default for both shared |
| 92 | # and static and there is a way to tell the linker which libcurl symbols it |
| 93 | # should export (vs. marking these symbols exportable at compile-time). |
| 94 | if(WIN32) |
| 95 | set(SHARE_LIB_OBJECT ON) |
| 96 | else() |
| 97 | # On other platforms, make it an option disabled by default |
| 98 | set(SHARE_LIB_OBJECT OFF) |
| 99 | endif() |
| 100 | endif() |
| 101 | |
| 102 | if(SHARE_LIB_OBJECT) |
| 103 | set(LIB_OBJECT "libcurl_object") |
| 104 | add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES}) |
| 105 | if(WIN32) |
| 106 | # Define CURL_STATICLIB always, to disable __declspec(dllexport) for |
| 107 | # exported libcurl symbols. We handle exports via libcurl.def instead. |
| 108 | # Except with symbol hiding disabled or debug mode enabled, when we export |
| 109 | # _all_ symbols from libcurl DLL, without using libcurl.def. |
| 110 | set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") |
| 111 | endif() |
| 112 | target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS}) |
| 113 | set_target_properties(${LIB_OBJECT} PROPERTIES POSITION_INDEPENDENT_CODE ON) |
| 114 | set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}") |
| 115 | if(CURL_HIDES_PRIVATE_SYMBOLS) |
| 116 | set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}") |
| 117 | set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") |
| 118 | endif() |
| 119 | if(CURL_HAS_LTO) |
| 120 | if(CMAKE_CONFIGURATION_TYPES) |
| 121 | set_target_properties(${LIB_OBJECT} PROPERTIES |
| 122 | INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE |
| 123 | INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) |
| 124 | else() |
| 125 | set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) |
| 126 | endif() |
| 127 | endif() |
| 128 | if(CURL_CLANG_TIDY) |
| 129 | set_target_properties(${LIB_OBJECT} PROPERTIES UNITY_BUILD OFF) |
| 130 | endif() |
| 131 | if(CURL_ANALYZER_CFLAGS) |
| 132 | set_target_properties(${LIB_OBJECT} PROPERTIES UNITY_BUILD OFF) |
| 133 | set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) |
| 134 | endif() |
| 135 | if(CURL_CODE_COVERAGE) |
| 136 | set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS}) |
| 137 | set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS}) |
| 138 | endif() |
| 139 | |
| 140 | target_include_directories(${LIB_OBJECT} INTERFACE |
| 141 | "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" |
| 142 | "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>") |
| 143 | |
| 144 | set(LIB_SOURCE $<TARGET_OBJECTS:${LIB_OBJECT}>) |
| 145 | else() |
| 146 | set(LIB_SOURCE ${HHEADERS} ${CSOURCES}) |
| 147 | endif() |
| 148 | |
| 149 | # We want it to be called libcurl on all platforms |
| 150 | if(BUILD_STATIC_LIBS) |
| 151 | list(APPEND libcurl_export ${LIB_STATIC}) |
| 152 | add_library(${LIB_STATIC} STATIC ${LIB_SOURCE}) |
| 153 | add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC}) |
| 154 | if(WIN32) |
| 155 | set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") |
| 156 | endif() |
| 157 | target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS}) |
| 158 | # Remove the "lib" prefix since the library is already named "libcurl". |
| 159 | set_target_properties(${LIB_STATIC} PROPERTIES |
| 160 | PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}" |
| 161 | SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}" |
| 162 | INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB") |
| 163 | set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}") |
| 164 | if(CURL_HIDES_PRIVATE_SYMBOLS) |
| 165 | set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}") |
| 166 | set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") |
| 167 | endif() |
| 168 | if(CURL_HAS_LTO) |
| 169 | if(CMAKE_CONFIGURATION_TYPES) |
| 170 | set_target_properties(${LIB_STATIC} PROPERTIES |
| 171 | INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE |
| 172 | INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) |
| 173 | else() |
| 174 | set_target_properties(${LIB_STATIC} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) |
| 175 | endif() |
| 176 | endif() |
| 177 | if(CURL_CLANG_TIDY) |
| 178 | if(BUILD_SHARED_LIBS) # disable clang-tidy for static, and limit to the shared library, if both enabled |
| 179 | set_target_properties(${LIB_STATIC} PROPERTIES C_CLANG_TIDY "") |
| 180 | else() |
| 181 | set_target_properties(${LIB_STATIC} PROPERTIES UNITY_BUILD OFF) |
| 182 | endif() |
| 183 | endif() |
| 184 | if(CURL_ANALYZER_CFLAGS) |
| 185 | set_target_properties(${LIB_STATIC} PROPERTIES UNITY_BUILD OFF) |
| 186 | set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) |
| 187 | endif() |
| 188 | if(CURL_CODE_COVERAGE) |
| 189 | set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS}) |
| 190 | set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS}) |
| 191 | endif() |
| 192 | |
| 193 | target_include_directories(${LIB_STATIC} INTERFACE |
| 194 | "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" |
| 195 | "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>") |
| 196 | endif() |
| 197 | |
| 198 | if(BUILD_SHARED_LIBS) |
| 199 | list(APPEND libcurl_export ${LIB_SHARED}) |
| 200 | add_library(${LIB_SHARED} SHARED ${LIB_SOURCE}) |
| 201 | add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED}) |
| 202 | if(WIN32) |
| 203 | set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "dllmain.c") |
| 204 | set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES ${LIB_RCFILES}) |
| 205 | if(CURL_HIDES_PRIVATE_SYMBOLS) |
| 206 | set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${PROJECT_SOURCE_DIR}/lib/libcurl.def") |
| 207 | endif() |
| 208 | endif() |
| 209 | target_link_libraries(${LIB_SHARED} PRIVATE ${CURL_LIBS}) |
| 210 | # Remove the "lib" prefix since the library is already named "libcurl". |
| 211 | set_target_properties(${LIB_SHARED} PROPERTIES |
| 212 | PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}" |
| 213 | IMPORT_PREFIX "" IMPORT_SUFFIX "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}" |
| 214 | POSITION_INDEPENDENT_CODE ON) |
| 215 | set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}") |
| 216 | if(CURL_HIDES_PRIVATE_SYMBOLS) |
| 217 | set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}") |
| 218 | set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") |
| 219 | endif() |