✓ProveIt
Examples / Report #4040551 Saved analysis

CVE-2026-94265: Authorization: Negotiate sent to foreign origin

curl · 2026-09-1818.6s analysis · $0.041 in model calls

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

Needs human review

The available evidence does not settle the report. A maintainer should investigate.

Judgment withdrawn. The answer failed the evidence check.
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.

1 · Grounding
partially grounded
no model · 42 references checked out, 4 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 6329 tokens · $0.0005
3 · Evidence
16 code snippets
real curl source at d1823ebc17e5 · 1 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
33742 tokens · 10.7s · $0.0400
5 · Receipts
6 of 7 citations verified
every citation must point at code the judge was shown
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

C1flaw supported

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.

C2location supported

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.

C3trigger supported

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.

C4impact supported

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.

C5reproduction supported

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.

reproduce.sh:0

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)

Full report on HackerOne ↗

Hi all, I've discovered that curl can be made to send an "Authorization: Negotiate" header, carrying a freshly minted Kerberos ticket for the victim's ambient identity, to a host chosen by an HTTP redirect, without --location-trusted. All the attacker has to do is put arbitrary userinfo in the Location header they redirect to. This contradicts the guarantee documented for CURLOPT_UNRESTRICTED_AUTH, which says 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". I have an end-to-end PoC against a real KDC on pristine upstream d1823ebc17e5778c30f8a6b2221a057ec607a9b7 (8.23.0-DEV), a control showing the protection holds when the userinfo is removed, and a small fix that I have verified closes it without regressing either normal Negotiate or --location-trusted. Details below. How it works ------------ The gate asks "do I have any credentials?" when it should ask "am I still on the original host?" ``` https://github.com/curl/curl/blob/d1823ebc17e5778c30f8a6b2221a057ec607a9b7/lib/http.c#L872-L878 /* Either we have credentials for the origin we talk to or performing authentication is allowed here */ if(data->state.creds || Curl_auth_allowed_to_host(data)) result = output_auth_headers(data, conn, authhost, request, path_and_query, FALSE); else authhost->done = TRUE; ``` That "||" is the whole vulnerability. Either condition is enough. The flow: 1. You run `curl --negotiate -L http://intranet/` - Kerberos SSO, no password typed. 2. `intranet` redirects you to `http://bob:alice@attacker.example/`. 3. curl correctly drops *your* credentials, because it is a different host. But it then parses `bob:alice` out of the attacker's own URL and stores it in state.creds. That path has no origin check. 4. Gate: credentials exist -> proceed. The origin check is never consulted. 5. The Negotiate branch runs. It throws `bob:alice` away, goes to the KDC, and gets a fresh Kerberos ticket for HTTP/attacker.example. Sends it. The attacker now holds a Kerberos AP-REQ for their own service, proving your identity. What makes it odd is that a credential which is never used unlocks an authentication that does not use credentials. `bob:alice` is neither forwarded nor chec …

Source code

lib/http.clines 847–903 · lines 872-878 cited by the report
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
891out:
892 curlx_free(tmp_str);
893 return result;
894}
895
896#else /* !CURL_DISABLE_HTTP_AUTH */
897/* when disabled */
898CURLcode 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,
lib/url.clines 1330–1416 · lines 1355-1360 cited by the report; lines 1383-1391 cited by the report
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
1344out:
1345 Curl_creds_unlink(&ncreds_out);
1346 return result;
1347}
1348#endif /* CURL_DISABLE_NETRC */
1349
1350static 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);
lib/http.clines 646–796 · lines 671-704 cited by the report; definition of output_auth_headers
646static 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}
lib/http_negotiate.clines 52–152 · lines 77-77 cited by the report; lines 126-127 cited by the report
52CURLcode 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;
lib/vauth/spnego_gssapi.clines 67–232 · lines 92-92 cited by the report; definition of Curl_auth_decode_spnego_message (truncated)
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 */
72CURLcode 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
lib/vauth/vauth.clines 115–174 · lines 140-149 cited by the report
Show 60 lines (not cited by the judge)
115bool 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 */
140bool Curl_auth_allowed_to_host(struct Curl_easy *data)
141{
142 return Curl_auth_allowed_to_origin(data, data->state.origin);
143}
144
145bool 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
153static 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
163struct 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
lib/http.clines 1853–1908 · lines 1878-1883 cited by the report
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
1901CURLcode 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;
lib/http.clines 150–166 · definition of Curl_checkProxyheaders
Show 17 lines (not cited by the judge)
150char *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}
lib/transfer.clines 84–99 · definition of Curl_checkheaders
Show 16 lines (not cited by the judge)
84char *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}
lib/creds.hlines 75–75 · definition of Curl_creds_has_user
Show 1 lines (not cited by the judge)
75#define Curl_creds_has_user(c) ((c) && (c)->user[0])
lib/http_httpsig.clines 529–687 · definition of Curl_output_httpsig
Show 159 lines (not cited by the judge)
529CURLcode 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));
667done:
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
673fail:
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}
lib/http_negotiate.clines 155–264 · definition of Curl_output_negotiate
Show 110 lines (not cited by the judge)
155CURLcode 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}
lib/peer.clines 321–327 · definition of Curl_peer_equal
Show 7 lines (not cited by the judge)
321bool 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}
lib/curl_setup.hlines 1249–1249 · definition of STRCONST
Show 1 lines (not cited by the judge)
1249#define STRCONST(x) x, CURL_CSTRLEN(x)
lib/urlapi.clines 1676–1775 · definition of curl_url_get
Show 100 lines (not cited by the judge)
1676CURLUcode 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}
lib/CMakeLists.txtlines 59–219 · definition of endif (truncated)
Show 161 lines (not cited by the judge)
59 add_custom_target(curlu-unitprotos DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/unitprotos.h")
60endif()
61
62## Library definition
63
64if(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()
73endif()
74if(NOT DEFINED STATIC_LIB_SUFFIX)
75 set(STATIC_LIB_SUFFIX "")
76endif()
77
78# Detect implib static lib filename collision
79if(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.")
86endif()
87
88# Whether to do a single compilation pass for libcurl sources and reuse these
89# objects to generate both static and shared target.
90if(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()
100endif()
101
102if(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}>)
145else()
146 set(LIB_SOURCE ${HHEADERS} ${CSOURCES})
147endif()
148
149# We want it to be called libcurl on all platforms
150if(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>")
196endif()
197
198if(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()