✓ProveIt
Examples / Report #3642555 Saved analysis

CVE-2026-5545: wrong reuse of HTTP Negotiate connection

curl · 2026-04-0114.2s analysis · $0.031 in model calls

What the report claims

An attacker sharing a libcurl multi-handle connection pool can hijack another user's Negotiate/Kerberos-authenticated connection, causing User B to be authenticated as User A on servers with persistent Negotiate auth.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2026-5545 wrong reuse of HTTP Negotiate connection

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Code supports the claim

The code supports the reported flaw. A maintainer should review its impact and decide what to do next.

Why this finding?
  • The code at lib/url.c:1085-1093, 1239-1247, and 1263-1271 exactly matches the reported flaw mechanism: NTLM tentative match sets m->found on a Negotiate-authenticated connection with mismatched credentials, returns FALSE to skip the Negotiate check, and url_match_result() ignores the FALSE result and attaches based solely on m->found.
  • The PoC and test server demonstrate a realistic attack: multi-handle shared pool, CURLAUTH_ANY enabling both NTLM and Negotiate, and a server with persistent Negotiate auth (IIS/Kerberos default).
  • All required symbols (USE_NTLM, USE_SPNEGO, CURLAUTH_ANY, CURL_GSS_STUB, url_match_auth_ntlm, url_match_auth_nego, url_match_result, Curl_attach_connection) are confirmed present at the target revision 2b3dfb4.
  • This is not covered by any 'not security issues' category in the project policy — it's an authentication bypass in a supported configuration.
How this analysis ran 6 claims · 10 citations verified

Checked against curl master on 2026-04-01 (2b3dfb4ad47e). Dev 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 · 34 references checked out, 1 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
6 claims · 6970 tokens · $0.0005
3 · Evidence
9 code snippets
real curl source at 2b3dfb4ad47e · 6 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
24801 tokens · 9.7s · $0.0303
5 · Receipts
10 of 14 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

Thank you for the detailed report and reproducible PoC. The code confirms the flaw: url_match_auth_ntlm() sets a tentative match on a Negotiate-authenticated connection (http_ntlm_state == NTLMSTATE_NONE) with mismatched credentials, returns FALSE to skip url_match_auth_nego(), and url_match_result() ignores the FALSE result and attaches based on m->found alone. This allows User B to hijack User A's authenticated connection on servers with persistent Negotiate auth. We'll prioritize a fix — Option A (adding a check for http_negotiate_state == GSS_AUTHNONE before setting the tentative match) is the targeted approach we'll take.

Inspect all claims and source code 6 claims · 9 code excerpts

Report claims

C1flaw supported

url_match_auth_ntlm() sets m->found for a connection with mismatched credentials and returns FALSE, which exits url_match_conn() before url_match_auth_nego() can reject the connection

Read the reasoning

The code at lines 1085-1093 exactly matches the claim: when want_ntlm_http is true and credentials mismatch but http_ntlm_state is NTLMSTATE_NONE, it sets m->found and returns FALSE.

C2location supported

lib/url.c:1085-1093

Read the reasoning

At lines 1239-1247, url_match_conn() returns FALSE immediately when url_match_auth_ntlm() returns FALSE, so url_match_auth_nego() is never reached.

C3trigger supported

User B requests the same host with CURLAUTH_ANY and different credentials

Read the reasoning

url_match_result() at lines 1263-1271 ignores the result parameter and only uses match->found to decide attachment.

C4impact supported

User B's request is attached to User A's TCP connection and authenticated as User A on servers with persistent Negotiate auth

Read the reasoning

The code path allows a Negotiate-authenticated connection (http_ntlm_state == NTLMSTATE_NONE, http_negotiate_state == GSS_AUTHSUCC) to be tentatively matched by NTLM logic, skipping the Negotiate check that would reject it. On servers with persistent auth (simulated by nego_server.py), the reused TCP connection carries the original identity.

C5reproduction supported

PoC shows User B authenticated as User A after User A's connection is returned to the pool

Read the reasoning

The provided PoC code and its described output demonstrate the vulnerability: User B gets User A's authenticated identity.

poc.c:1 poc.c:200
C6reproduction supported

Server log confirms conn=127.0.0.1:38020 identity=KRB5_userA for User B's request

Read the reasoning

The test server confirms the attack model: it authenticates by TCP connection, so when User B reuses User A's TCP connection, the server sees User A's identity.

nego_server.py:1 nego_server.py:80

Deterministic checks that failed

  • quote only 0/4 quoted lines appear anywhere in curl at 2b3dfb4ad47e
Read the original report (excerpt)

Full report on HackerOne ↗

## Summary: An attacker sharing a libcurl multi-handle connection pool can hijack another user's Negotiate/Kerberos-authenticated connection. When User A authenticates via Negotiate (SPNEGO) and the connection returns to the pool, User B using `CURLAUTH_ANY` with different credentials gets that connection attached to their transfer. On servers with persistent Negotiate auth (Windows IIS with Kerberos — the default), User B's requests execute as User A. The root cause is in `url_match_auth_ntlm()` (`lib/url.c:1092`): a tentative match sets `m->found` for a connection with mismatched credentials, returns FALSE, which exits the matching chain before `url_match_auth_nego()` can reject the connection. `url_match_result()` then attaches the connection based solely on `m->found` being non-NULL, ignoring the FALSE result. ## Affected version - curl 8.20.0-DEV (master, commit 2b3dfb4, 2026-03-31) - Built with `--enable-debug --with-openssl --with-gssapi` - Both `USE_NTLM` and `USE_SPNEGO` compiled in (verified via `curl_setup.h`) - Test uses curl's built-in `CURL_GSS_STUB` (debug-build fake GSSAPI) ## Steps To Reproduce: 1. Start the test server (simulates IIS persistent Negotiate auth): ```bash python3 nego_server.py 8999 & ``` 2. Compile and run the PoC: ```bash gcc -o poc poc.c -I../include -L../lib/.libs -lcurl \ $(pkg-config --libs openssl libnghttp2 zlib libbrotlidec libzstd libpsl \ krb5-gssapi krb5 libidn2) -lldap -llber -Wl,-rpath,../lib/.libs CURL_STUB_GSS_CREDS="KRB5_userA" ./poc ``` **PoC output (abbreviated):** ``` [STEP 1] User A: Negotiate auth to http://127.0.0.1:8999/api/secret [USER_A] Server auth using Negotiate with user 'userA' [USER_A] Authorization: Negotiate S1JCNV91c2VyQTpIVFRQ... [RESULT A] HTTP 200 [RESULT A] Body: {"identity": "KRB5_userA", "mechanism": "negotiate_complete"} [STEP 2] User A's connection is now in the pool [STEP 3] User B: CURLAUTH_ANY with DIFFERENT creds [USER_B] [POC-TRACE] url_match_result: attaching conn #1 (result param=0, IGNORED). negotiate_state=0 [USER_B] Reusing existing http: connection with host 127.0.0.1 [USER_B] Server auth using Negotiate with user 'userB' [USER_B] Authorization: Negotiate S1JCNV91c2VyQTpIVFRQ... ^^^^^^^^^^^^^^^^ Still User A's GSSAPI context! [RESULT B] HTTP 200 [RESULT B] B …

Source code

lib/url.clines 1052–1311 · lines 1092-1092 cited by the report; lines 1085-1093 cited by the report; lines 1077-1139 cited by the report; lines 1145-1196 cited by the report; lines 1201-1261 cited by the report; lines 1263-1286 cited by the report
1052 return FALSE;
1053
1054 /* hostname and port must match */
1055 if(!curl_strequal(m->needle->host.name, conn->host.name) ||
1056 m->needle->remote_port != conn->remote_port)
1057 return FALSE;
1058 }
1059 return TRUE;
1060}
1061
1062static bool url_match_ssl_config(struct connectdata *conn,
1063 struct url_conn_match *m)
1064{
1065 /* If talking TLS, conn needs to use the same SSL options. */
1066 if((m->needle->scheme->flags & PROTOPT_SSL) &&
1067 !Curl_ssl_conn_config_match(m->data, conn, FALSE)) {
1068 DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T
1069 " has different SSL parameters, cannot reuse",
1070 conn->connection_id));
1071 return FALSE;
1072 }
1073 return TRUE;
1074}
1075
1076#ifdef USE_NTLM
1077static bool url_match_auth_ntlm(struct connectdata *conn,
1078 struct url_conn_match *m)
1079{
1080 /* If we are looking for an HTTP+NTLM connection, check if this is
1081 already authenticating with the right credentials. If not, keep
1082 looking so that we can reuse NTLM connections if
1083 possible. (Especially we must not reuse the same connection if
1084 partway through a handshake!) */
1085 if(m->want_ntlm_http) {
C1: url_match_auth_ntlm() checks want_ntlm_http and compares credentials via Curl_timestrcmp
1086 if(Curl_timestrcmp(m->needle->user, conn->user) ||
1087 Curl_timestrcmp(m->needle->passwd, conn->passwd)) {
C1: When credentials don't match but http_ntlm_state == NTLMSTATE_NONE, sets m->found = conn
1088
1089 /* we prefer a credential match, but this is at least a connection
1090 that can be reused and "upgraded" to NTLM */
1091 if(conn->http_ntlm_state == NTLMSTATE_NONE)
C4: Tentative match set when http_ntlm_state == NTLMSTATE_NONE (true for Negotiate-authenticated connections)
1092 m->found = conn;
1093 return FALSE;
C1: Returns FALSE, which causes url_match_conn() to exit early
1094 }
1095 }
1096 else if(conn->http_ntlm_state != NTLMSTATE_NONE) {
1097 /* Connection is using NTLM auth but we do not want NTLM */
1098 return FALSE;
1099 }
1100
1101#ifndef CURL_DISABLE_PROXY
1102 /* Same for Proxy NTLM authentication */
1103 if(m->want_proxy_ntlm_http) {
1104 /* Both conn->http_proxy.user and conn->http_proxy.passwd can be
1105 * NULL */
1106 if(!conn->http_proxy.user || !conn->http_proxy.passwd)
1107 return FALSE;
1108
1109 if(Curl_timestrcmp(m->needle->http_proxy.user,
1110 conn->http_proxy.user) ||
1111 Curl_timestrcmp(m->needle->http_proxy.passwd,
1112 conn->http_proxy.passwd))
1113 return FALSE;
1114 }
1115 else if(conn->proxy_ntlm_state != NTLMSTATE_NONE) {
1116 /* Proxy connection is using NTLM auth but we do not want NTLM */
1117 return FALSE;
1118 }
1119#endif
1120 if(m->want_ntlm_http || m->want_proxy_ntlm_http) {
1121 /* Credentials are already checked, we may use this connection.
1122 * With NTLM being weird as it is, we MUST use a
1123 * connection where it has already been fully negotiated.
1124 * If it has not, we keep on looking for a better one. */
1125 m->found = conn;
1126
1127 if((m->want_ntlm_http &&
1128 (conn->http_ntlm_state != NTLMSTATE_NONE)) ||
1129 (m->want_proxy_ntlm_http &&
1130 (conn->proxy_ntlm_state != NTLMSTATE_NONE))) {
1131 /* We must use this connection, no other */
1132 m->force_reuse = TRUE;
1133 return TRUE;
1134 }
1135 /* Continue look up for a better connection */
1136 return FALSE;
1137 }
1138 return TRUE;
1139}
1140#else
1141#define url_match_auth_ntlm(c, m) ((void)(c), (void)(m), TRUE)
1142#endif
1143
1144#ifdef USE_SPNEGO
1145static bool url_match_auth_nego(struct connectdata *conn,
1146 struct url_conn_match *m)
1147{
1148 /* If we are looking for an HTTP+Negotiate connection, check if this is
1149 already authenticating with the right credentials. If not, keep looking
1150 so that we can reuse Negotiate connections if possible. */
1151 if(m->want_nego_http) {
1152 if(Curl_timestrcmp(m->needle->user, conn->user) ||
1153 Curl_timestrcmp(m->needle->passwd, conn->passwd))
1154 return FALSE;
1155 }
1156 else if(conn->http_negotiate_state != GSS_AUTHNONE) {
C4: url_match_auth_nego() would reject connections with http_negotiate_state != GSS_AUTHNONE, but is never called
1157 /* Connection is using Negotiate auth but we do not want Negotiate */
1158 return FALSE;
1159 }
1160
1161#ifndef CURL_DISABLE_PROXY
1162 /* Same for Proxy Negotiate authentication */
1163 if(m->want_proxy_nego_http) {
1164 /* Both conn->http_proxy.user and conn->http_proxy.passwd can be
1165 * NULL */
1166 if(!conn->http_proxy.user || !conn->http_proxy.passwd)
1167 return FALSE;
1168
1169 if(Curl_timestrcmp(m->needle->http_proxy.user,
1170 conn->http_proxy.user) ||
1171 Curl_timestrcmp(m->needle->http_proxy.passwd,
1172 conn->http_proxy.passwd))
1173 return FALSE;
1174 }
1175 else if(conn->proxy_negotiate_state != GSS_AUTHNONE) {
1176 /* Proxy connection is using Negotiate auth but we do not want Negotiate */
1177 return FALSE;
1178 }
1179#endif
1180 if(m->want_nego_http || m->want_proxy_nego_http) {
1181 /* Credentials are already checked, we may use this connection. We MUST
1182 * use a connection where it has already been fully negotiated. If it has
1183 * not, we keep on looking for a better one. */
1184 m->found = conn;
1185 if((m->want_nego_http &&
1186 (conn->http_negotiate_state != GSS_AUTHNONE)) ||
1187 (m->want_proxy_nego_http &&
1188 (conn->proxy_negotiate_state != GSS_AUTHNONE))) {
1189 /* We must use this connection, no other */
1190 m->force_reuse = TRUE;
1191 return TRUE;
1192 }
1193 return FALSE; /* get another */
1194 }
1195 return TRUE;
1196}
1197#else
1198#define url_match_auth_nego(c, m) ((void)(c), (void)(m), TRUE)
1199#endif
1200
1201static bool url_match_conn(struct connectdata *conn, void *userdata)
1202{
1203 struct url_conn_match *m = userdata;
1204 /* Check if `conn` can be used for transfer `m->data` */
1205
1206 /* general connect config setting match? */
1207 if(!url_match_connect_config(conn, m))
1208 return FALSE;
1209
1210 /* match for destination and protocol? */
1211 if(!url_match_destination(conn, m))
1212 return FALSE;
1213
1214 if(!url_match_fully_connected(conn, m))
1215 return FALSE;
1216
1217 if(!url_match_multiplex_needs(conn, m))
1218 return FALSE;
1219
1220 if(!url_match_ssl_use(conn, m))
1221 return FALSE;
1222 if(!url_match_proxy_use(conn, m))
1223 return FALSE;
1224 if(!url_match_ssl_config(conn, m))
1225 return FALSE;
1226
1227 if(!url_match_http_multiplex(conn, m))
1228 return FALSE;
1229 else if(m->wait_pipe)
1230 /* we decided to wait on PIPELINING */
1231 return TRUE;
1232
1233 if(!url_match_auth(conn, m))
1234 return FALSE;
1235
1236 if(!url_match_proto_config(conn, m))
1237 return FALSE;
1238
1239 if(!url_match_auth_ntlm(conn, m))
C2: url_match_conn() calls url_match_auth_ntlm() and returns FALSE immediately if it returns FALSE
1240 return FALSE;
1241 else if(m->force_reuse)
1242 return TRUE;
1243
1244 if(!url_match_auth_nego(conn, m))
C2: url_match_auth_nego() is only called if url_match_auth_ntlm() returns TRUE
1245 return FALSE;
1246 else if(m->force_reuse)
1247 return TRUE;
1248
1249 if(!url_match_multiplex_limits(conn, m))
1250 return FALSE;
1251
1252 if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data)) {
1253 /* remove and disconnect. */
1254 Curl_conn_terminate(m->data, conn, FALSE);
1255 return FALSE;
1256 }
1257
1258 /* conn matches our needs. */
1259 m->found = conn;
1260 return TRUE;
1261}
1262
1263static bool url_match_result(bool result, void *userdata)
1264{
1265 struct url_conn_match *match = userdata;
1266 (void)result;
C3: (void)result; explicitly ignores the result parameter
1267 if(match->found) {
C3: Only checks if(match->found) to decide whether to attach the connection
1268 /* Attach it now while still under lock, so the connection does
1269 * no longer appear idle and can be reaped. */
1270 Curl_attach_connection(match->data, match->found);
1271 return TRUE;
1272 }
1273 else if(match->seen_single_use_conn && !match->seen_multiplex_conn) {
1274 /* We have seen a single-use, existing connection to the destination and
1275 * no multiplexed one. It seems safe to assume that the server does
1276 * not support multiplexing. */
1277 match->wait_pipe = FALSE;
1278 }
1279 else if(match->seen_pending_conn && match->data->set.pipewait) {
1280 infof(match->data,
1281 "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set");
1282 match->wait_pipe = TRUE;
1283 }
1284 match->force_reuse = FALSE;
1285 return FALSE;
1286}
1287
1288/*
1289 * Given a transfer and a prototype connection (needle),
1290 * find and attach an existing connection that matches.
1291 *
1292 * Return TRUE if an existing connection was attached.
1293 * `waitpipe` is TRUE if no existing connection matched, but there
1294 * might be suitable one in the near future (common cause: multiplexing
1295 * capability has not been determined yet, e.g. ALPN handshake).
1296 */
1297static bool url_attach_existing(struct Curl_easy *data,
1298 struct connectdata *needle,
1299 bool *waitpipe)
1300{
1301 struct url_conn_match match;
1302 bool result;
1303
1304 DEBUGASSERT(!data->conn);
1305 memset(&match, 0, sizeof(match));
1306 match.data = data;
1307 match.needle = needle;
1308 match.may_multiplex = xfer_may_multiplex(data, needle);
1309
1310#ifdef USE_NTLM
1311 match.want_ntlm_http =
lib/url.clines 1214–1296 · lines 1239-1247 cited by the report; lines 1263-1271 cited by the report
1214 if(!url_match_fully_connected(conn, m))
1215 return FALSE;
1216
1217 if(!url_match_multiplex_needs(conn, m))
1218 return FALSE;
1219
1220 if(!url_match_ssl_use(conn, m))
1221 return FALSE;
1222 if(!url_match_proxy_use(conn, m))
1223 return FALSE;
1224 if(!url_match_ssl_config(conn, m))
1225 return FALSE;
1226
1227 if(!url_match_http_multiplex(conn, m))
1228 return FALSE;
1229 else if(m->wait_pipe)
1230 /* we decided to wait on PIPELINING */
1231 return TRUE;
1232
1233 if(!url_match_auth(conn, m))
1234 return FALSE;
1235
1236 if(!url_match_proto_config(conn, m))
1237 return FALSE;
1238
1239 if(!url_match_auth_ntlm(conn, m))
C2: url_match_conn() calls url_match_auth_ntlm() and returns FALSE immediately if it returns FALSE
1240 return FALSE;
1241 else if(m->force_reuse)
1242 return TRUE;
1243
1244 if(!url_match_auth_nego(conn, m))
C2: url_match_auth_nego() is only called if url_match_auth_ntlm() returns TRUE
1245 return FALSE;
1246 else if(m->force_reuse)
1247 return TRUE;
1248
1249 if(!url_match_multiplex_limits(conn, m))
1250 return FALSE;
1251
1252 if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data)) {
1253 /* remove and disconnect. */
1254 Curl_conn_terminate(m->data, conn, FALSE);
1255 return FALSE;
1256 }
1257
1258 /* conn matches our needs. */
1259 m->found = conn;
1260 return TRUE;
1261}
1262
1263static bool url_match_result(bool result, void *userdata)
1264{
1265 struct url_conn_match *match = userdata;
1266 (void)result;
C3: (void)result; explicitly ignores the result parameter
1267 if(match->found) {
C3: Only checks if(match->found) to decide whether to attach the connection
1268 /* Attach it now while still under lock, so the connection does
1269 * no longer appear idle and can be reaped. */
1270 Curl_attach_connection(match->data, match->found);
1271 return TRUE;
1272 }
1273 else if(match->seen_single_use_conn && !match->seen_multiplex_conn) {
1274 /* We have seen a single-use, existing connection to the destination and
1275 * no multiplexed one. It seems safe to assume that the server does
1276 * not support multiplexing. */
1277 match->wait_pipe = FALSE;
1278 }
1279 else if(match->seen_pending_conn && match->data->set.pipewait) {
1280 infof(match->data,
1281 "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set");
1282 match->wait_pipe = TRUE;
1283 }
1284 match->force_reuse = FALSE;
1285 return FALSE;
1286}
1287
1288/*
1289 * Given a transfer and a prototype connection (needle),
1290 * find and attach an existing connection that matches.
1291 *
1292 * Return TRUE if an existing connection was attached.
1293 * `waitpipe` is TRUE if no existing connection matched, but there
1294 * might be suitable one in the near future (common cause: multiplexing
1295 * capability has not been determined yet, e.g. ALPN handshake).
1296 */
lib/curl_gssapi.clines 63–334 · lines 88-309 cited by the report
63gss_OID_desc Curl_spnego_mech_oid CURL_ALIGN8 = {
64 6, CURL_UNCONST("\x2b\x06\x01\x05\x05\x02")
65};
66gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = {
67 9, CURL_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")
68};
69
70#ifdef CURL_GSS_STUB
71enum min_err_code {
72 STUB_GSS_OK = 0,
73 STUB_GSS_NO_MEMORY,
74 STUB_GSS_INVALID_ARGS,
75 STUB_GSS_INVALID_CREDS,
76 STUB_GSS_INVALID_CTX,
77 STUB_GSS_SERVER_ERR,
78 STUB_GSS_NO_MECH,
79 STUB_GSS_LAST
80};
81
82/* libcurl is also passing this struct to these functions, which are not yet
83 * stubbed:
84 * gss_inquire_context()
85 * gss_unwrap()
86 * gss_wrap()
87 */
88struct stub_gss_ctx_id_t_desc {
89 enum { STUB_GSS_NONE, STUB_GSS_KRB5, STUB_GSS_NTLM1, STUB_GSS_NTLM3 } sent;
90 int have_krb5;
91 int have_ntlm;
92 OM_uint32 flags;
93 char creds[250];
94};
95
96static OM_uint32
97stub_gss_init_sec_context(OM_uint32 *min,
98 gss_cred_id_t initiator_cred_handle,
99 struct stub_gss_ctx_id_t_desc **context,
100 gss_name_t target_name,
101 const gss_OID mech_type,
102 OM_uint32 req_flags,
103 OM_uint32 time_req,
104 const gss_channel_bindings_t input_chan_bindings,
105 gss_buffer_desc *input_token,
106 gss_OID *actual_mech_type,
107 gss_buffer_desc *output_token,
108 OM_uint32 *ret_flags,
109 OM_uint32 *time_rec)
110{
111 struct stub_gss_ctx_id_t_desc *ctx = NULL;
112
113 /* The token will be encoded in base64 */
114 size_t length = sizeof(ctx->creds) * 3 / 4;
115 size_t used = 0;
116 char *token = NULL;
117 const char *creds = NULL;
118
119 (void)initiator_cred_handle;
120 (void)mech_type;
121 (void)time_req;
122 (void)input_chan_bindings;
123 (void)actual_mech_type;
124
125 if(!min)
126 return GSS_S_FAILURE;
127
128 *min = 0;
129
130 if(!context || !target_name || !output_token) {
131 *min = STUB_GSS_INVALID_ARGS;
132 return GSS_S_FAILURE;
133 }
134
135 creds = getenv("CURL_STUB_GSS_CREDS");
136 if(!creds || strlen(creds) >= sizeof(ctx->creds)) {
137 *min = STUB_GSS_INVALID_CREDS;
138 return GSS_S_FAILURE;
139 }
140
141 ctx = *context;
142 if(ctx && strcmp(ctx->creds, creds)) {
143 *min = STUB_GSS_INVALID_CREDS;
144 return GSS_S_FAILURE;
145 }
146
147 output_token->length = 0;
148 output_token->value = NULL;
149
150 if(input_token && input_token->length) {
151 if(!ctx) {
152 *min = STUB_GSS_INVALID_CTX;
153 return GSS_S_FAILURE;
154 }
155
156 /* Server response, either D (RA==) or C (Qw==) */
157 if(((char *)input_token->value)[0] == 'D') {
158 /* Done */
159 switch(ctx->sent) {
160 case STUB_GSS_KRB5:
C4: Stub GSS returns GSS_S_COMPLETE for KRB5, setting http_negotiate_state = GSS_AUTHSUCC
161 case STUB_GSS_NTLM3:
162 if(ret_flags)
163 *ret_flags = ctx->flags;
164 if(time_rec)
165 *time_rec = GSS_C_INDEFINITE;
166 return GSS_S_COMPLETE;
167 default:
168 *min = STUB_GSS_SERVER_ERR;
169 return GSS_S_FAILURE;
170 }
171 }
172
173 if(((char *)input_token->value)[0] != 'C') {
174 /* We only support Done or Continue */
175 *min = STUB_GSS_SERVER_ERR;
176 return GSS_S_FAILURE;
177 }
178
179 /* Continue */
180 switch(ctx->sent) {
181 case STUB_GSS_KRB5:
182 /* We sent KRB5 and it failed, let's try NTLM */
183 if(ctx->have_ntlm) {
184 ctx->sent = STUB_GSS_NTLM1;
185 break;
186 }
187 else {
188 *min = STUB_GSS_SERVER_ERR;
189 return GSS_S_FAILURE;
190 }
191 case STUB_GSS_NTLM1:
192 ctx->sent = STUB_GSS_NTLM3;
193 break;
194 default:
195 *min = STUB_GSS_SERVER_ERR;
196 return GSS_S_FAILURE;
197 }
198 }
199 else {
200 if(ctx) {
201 *min = STUB_GSS_INVALID_CTX;
202 return GSS_S_FAILURE;
203 }
204
205 ctx = curlx_calloc(1, sizeof(*ctx));
206 if(!ctx) {
207 *min = STUB_GSS_NO_MEMORY;
208 return GSS_S_FAILURE;
209 }
210
211 if(strstr(creds, "KRB5"))
212 ctx->have_krb5 = 1;
213
214 if(strstr(creds, "NTLM"))
215 ctx->have_ntlm = 1;
216
217 if(ctx->have_krb5)
218 ctx->sent = STUB_GSS_KRB5;
219 else if(ctx->have_ntlm)
220 ctx->sent = STUB_GSS_NTLM1;
221 else {
222 curlx_free(ctx);
223 *min = STUB_GSS_NO_MECH;
224 return GSS_S_FAILURE;
225 }
226
227 curlx_strcopy(ctx->creds, sizeof(ctx->creds), creds, strlen(creds));
228 ctx->flags = req_flags;
229 }
230
231 token = Curl_gss_alloc(length);
232 if(!token) {
233 curlx_free(ctx);
234 *min = STUB_GSS_NO_MEMORY;
235 return GSS_S_FAILURE;
236 }
237
238 {
239 gss_buffer_desc target_desc;
240 gss_OID name_type = GSS_C_NO_OID;
241 OM_uint32 minor_status;
242 OM_uint32 major_status;
243 major_status = gss_display_name(&minor_status, target_name,
244 &target_desc, &name_type);
245 if(GSS_ERROR(major_status)) {
246 Curl_gss_free(token);
247 curlx_free(ctx);
248 *min = STUB_GSS_NO_MEMORY;
249 return GSS_S_FAILURE;
250 }
251
252 if(strlen(creds) + target_desc.length + 5 >= sizeof(ctx->creds)) {
253 Curl_gss_free(token);
254 curlx_free(ctx);
255 *min = STUB_GSS_NO_MEMORY;
256 return GSS_S_FAILURE;
257 }
258
259 /* Token format: creds:target:type:padding */
260 used = curl_msnprintf(token, length, "%s:%.*s:%d:", creds,
261 (int)target_desc.length,
262 (const char *)target_desc.value,
263 ctx->sent);
264
265 gss_release_buffer(&minor_status, &target_desc);
266 }
267
268 if(used >= length) {
269 Curl_gss_free(token);
270 curlx_free(ctx);
271 *min = STUB_GSS_NO_MEMORY;
272 return GSS_S_FAILURE;
273 }
274
275 /* Overwrite null-terminator */
276 memset(token + used, 'A', length - used);
277
278 *context = ctx;
279
280 output_token->value = token;
281 output_token->length = length;
282
283 return GSS_S_CONTINUE_NEEDED;
284}
285
286static OM_uint32
287stub_gss_delete_sec_context(OM_uint32 *min,
288 struct stub_gss_ctx_id_t_desc **context,
289 gss_buffer_t output_token)
290{
291 (void)output_token;
292
293 if(!min)
294 return GSS_S_FAILURE;
295
296 if(!context) {
297 *min = STUB_GSS_INVALID_CTX;
298 return GSS_S_FAILURE;
299 }
300 if(!*context) {
301 *min = STUB_GSS_INVALID_CTX;
302 return GSS_S_FAILURE;
303 }
304
305 curlx_free(*context);
306 *context = NULL;
307 *min = 0;
308
309 return GSS_S_COMPLETE;
310}
311#endif /* CURL_GSS_STUB */
312
313OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data,
314 OM_uint32 *minor_status,
315 gss_ctx_id_t *context,
316 gss_name_t target_name,
317 gss_OID mech_type,
318 gss_channel_bindings_t input_chan_bindings,
319 gss_buffer_t input_token,
320 gss_buffer_t output_token,
321 const bool mutual_auth,
322 OM_uint32 *ret_flags)
323{
324 OM_uint32 req_flags = GSS_C_REPLAY_FLAG;
325
326 if(mutual_auth)
327 req_flags |= GSS_C_MUTUAL_FLAG;
328
329 if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_POLICY_FLAG) {
330#ifdef GSS_C_DELEG_POLICY_FLAG /* MIT Kerberos 1.8+, missing from GNU GSS */
331 req_flags |= GSS_C_DELEG_POLICY_FLAG;
332#else
333 infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not "
334 "compiled in");
include/curl/curl.hlines 845–846 · definition of CURLAUTH_ANY
Show 2 lines (not cited by the judge)
845#define CURLAUTH_ANY ((~CURLAUTH_DIGEST_IE) & \
846 ((unsigned long)0xffffffff))
lib/curl_gssapi.clines 36–36 · definition of CURL_GSS_STUB
Show 1 lines (not cited by the judge)
36#define CURL_GSS_STUB
lib/multi.clines 961–977 · definition of Curl_attach_connection
Show 17 lines (not cited by the judge)
961void Curl_attach_connection(struct Curl_easy *data,
962 struct connectdata *conn)
963{
964 DEBUGASSERT(data);
965 DEBUGASSERT(!data->conn);
966 DEBUGASSERT(conn);
967 DEBUGASSERT(conn->attached_xfers < UINT32_MAX);
968 data->conn = conn;
969 conn->attached_xfers++;
970 /* all attached transfers must be from the same multi */
971 if(!conn->attached_multi)
972 conn->attached_multi = data->multi;
973 DEBUGASSERT(conn->attached_multi == data->multi);
974
975 if(conn->scheme && conn->scheme->run->attach)
976 conn->scheme->run->attach(data, conn);
977}
lib/strcase.clines 130–146 · definition of Curl_timestrcmp
Show 17 lines (not cited by the judge)
130int Curl_timestrcmp(const char *a, const char *b)
131{
132 int match = 0;
133 int i = 0;
134
135 if(a && b) {
136 while(1) {
137 match |= a[i] ^ b[i];
138 if(!a[i] || !b[i])
139 break;
140 i++;
141 }
142 }
143 else
144 return a || b;
145 return match;
146}
lib/curl_setup.hlines 774–774 · definition of USE_NTLM
Show 1 lines (not cited by the judge)
774# define USE_NTLM
lib/curl_setup.hlines 755–755 · definition of USE_SPNEGO
Show 1 lines (not cited by the judge)
755#define USE_SPNEGO