Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/vauth/spnego_sspi.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*************************************************************************** | |
| 2 * _ _ ____ _ | |
| 3 * Project ___| | | | _ \| | | |
| 4 * / __| | | | |_) | | | |
| 5 * | (__| |_| | _ <| |___ | |
| 6 * \___|\___/|_| \_\_____| | |
| 7 * | |
| 8 * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. | |
| 9 * | |
| 10 * This software is licensed as described in the file COPYING, which | |
| 11 * you should have received as part of this distribution. The terms | |
| 12 * are also available at https://curl.haxx.se/docs/copyright.html. | |
| 13 * | |
| 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell | |
| 15 * copies of the Software, and permit persons to whom the Software is | |
| 16 * furnished to do so, under the terms of the COPYING file. | |
| 17 * | |
| 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | |
| 19 * KIND, either express or implied. | |
| 20 * | |
| 21 * RFC4178 Simple and Protected GSS-API Negotiation Mechanism | |
| 22 * | |
| 23 ***************************************************************************/ | |
| 24 | |
| 25 #include "curl_setup.h" | |
| 26 | |
| 27 #if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO) | |
| 28 | |
| 29 #include <curl/curl.h> | |
| 30 | |
| 31 #include "vauth/vauth.h" | |
| 32 #include "urldata.h" | |
| 33 #include "curl_base64.h" | |
| 34 #include "warnless.h" | |
| 35 #include "curl_multibyte.h" | |
| 36 #include "sendf.h" | |
| 37 #include "strerror.h" | |
| 38 | |
| 39 /* The last #include files should be: */ | |
| 40 #include "curl_memory.h" | |
| 41 #include "memdebug.h" | |
| 42 | |
| 43 /* | |
| 44 * Curl_auth_is_spnego_supported() | |
| 45 * | |
| 46 * This is used to evaluate if SPNEGO (Negotiate) is supported. | |
| 47 * | |
| 48 * Parameters: None | |
| 49 * | |
| 50 * Returns TRUE if Negotiate is supported by Windows SSPI. | |
| 51 */ | |
| 52 bool Curl_auth_is_spnego_supported(void) | |
| 53 { | |
| 54 PSecPkgInfo SecurityPackage; | |
| 55 SECURITY_STATUS status; | |
| 56 | |
| 57 /* Query the security package for Negotiate */ | |
| 58 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) | |
| 59 TEXT(SP_NAME_NEGOTIATE), | |
| 60 &SecurityPackage); | |
| 61 | |
| 62 /* Release the package buffer as it is not required anymore */ | |
| 63 if(status == SEC_E_OK) { | |
| 64 s_pSecFn->FreeContextBuffer(SecurityPackage); | |
| 65 } | |
| 66 | |
| 67 | |
| 68 return (status == SEC_E_OK ? TRUE : FALSE); | |
| 69 } | |
| 70 | |
| 71 /* | |
| 72 * Curl_auth_decode_spnego_message() | |
| 73 * | |
| 74 * This is used to decode an already encoded SPNEGO (Negotiate) challenge | |
| 75 * message. | |
| 76 * | |
| 77 * Parameters: | |
| 78 * | |
| 79 * data [in] - The session handle. | |
| 80 * user [in] - The user name in the format User or Domain\User. | |
| 81 * password [in] - The user's password. | |
| 82 * service [in] - The service type such as http, smtp, pop or imap. | |
| 83 * host [in] - The host name. | |
| 84 * chlg64 [in] - The optional base64 encoded challenge message. | |
| 85 * nego [in/out] - The Negotiate data struct being used and modified. | |
| 86 * | |
| 87 * Returns CURLE_OK on success. | |
| 88 */ | |
| 89 CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, | |
| 90 const char *user, | |
| 91 const char *password, | |
| 92 const char *service, | |
| 93 const char *host, | |
| 94 const char *chlg64, | |
| 95 struct negotiatedata *nego) | |
| 96 { | |
| 97 CURLcode result = CURLE_OK; | |
| 98 size_t chlglen = 0; | |
| 99 unsigned char *chlg = NULL; | |
| 100 PSecPkgInfo SecurityPackage; | |
| 101 SecBuffer chlg_buf[2]; | |
| 102 SecBuffer resp_buf; | |
| 103 SecBufferDesc chlg_desc; | |
| 104 SecBufferDesc resp_desc; | |
| 105 unsigned long attrs; | |
| 106 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ | |
| 107 | |
| 108 #if defined(CURL_DISABLE_VERBOSE_STRINGS) | |
| 109 (void) data; | |
| 110 #endif | |
| 111 | |
| 112 if(nego->context && nego->status == SEC_E_OK) { | |
| 113 /* We finished successfully our part of authentication, but server | |
| 114 * rejected it (since we're again here). Exit with an error since we | |
| 115 * can't invent anything better */ | |
| 116 Curl_auth_cleanup_spnego(nego); | |
| 117 return CURLE_LOGIN_DENIED; | |
| 118 } | |
| 119 | |
| 120 if(!nego->spn) { | |
| 121 /* Generate our SPN */ | |
| 122 nego->spn = Curl_auth_build_spn(service, host, NULL); | |
| 123 if(!nego->spn) | |
| 124 return CURLE_OUT_OF_MEMORY; | |
| 125 } | |
| 126 | |
| 127 if(!nego->output_token) { | |
| 128 /* Query the security package for Negotiate */ | |
| 129 nego->status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) | |
| 130 TEXT(SP_NAME_NEGOTIATE), | |
| 131 &SecurityPackage); | |
| 132 if(nego->status != SEC_E_OK) | |
| 133 return CURLE_NOT_BUILT_IN; | |
| 134 | |
| 135 nego->token_max = SecurityPackage->cbMaxToken; | |
| 136 | |
| 137 /* Release the package buffer as it is not required anymore */ | |
| 138 s_pSecFn->FreeContextBuffer(SecurityPackage); | |
| 139 | |
| 140 /* Allocate our output buffer */ | |
| 141 nego->output_token = malloc(nego->token_max); | |
| 142 if(!nego->output_token) | |
| 143 return CURLE_OUT_OF_MEMORY; | |
| 144 } | |
| 145 | |
| 146 if(!nego->credentials) { | |
| 147 /* Do we have credentials to use or are we using single sign-on? */ | |
| 148 if(user && *user) { | |
| 149 /* Populate our identity structure */ | |
| 150 result = Curl_create_sspi_identity(user, password, &nego->identity); | |
| 151 if(result) | |
| 152 return result; | |
| 153 | |
| 154 /* Allow proper cleanup of the identity structure */ | |
| 155 nego->p_identity = &nego->identity; | |
| 156 } | |
| 157 else | |
| 158 /* Use the current Windows user */ | |
| 159 nego->p_identity = NULL; | |
| 160 | |
| 161 /* Allocate our credentials handle */ | |
| 162 nego->credentials = calloc(1, sizeof(CredHandle)); | |
| 163 if(!nego->credentials) | |
| 164 return CURLE_OUT_OF_MEMORY; | |
| 165 | |
| 166 /* Acquire our credentials handle */ | |
| 167 nego->status = | |
| 168 s_pSecFn->AcquireCredentialsHandle(NULL, | |
| 169 (TCHAR *)TEXT(SP_NAME_NEGOTIATE), | |
| 170 SECPKG_CRED_OUTBOUND, NULL, | |
| 171 nego->p_identity, NULL, NULL, | |
| 172 nego->credentials, &expiry); | |
| 173 if(nego->status != SEC_E_OK) | |
| 174 return CURLE_AUTH_ERROR; | |
| 175 | |
| 176 /* Allocate our new context handle */ | |
| 177 nego->context = calloc(1, sizeof(CtxtHandle)); | |
| 178 if(!nego->context) | |
| 179 return CURLE_OUT_OF_MEMORY; | |
| 180 } | |
| 181 | |
| 182 if(chlg64 && *chlg64) { | |
| 183 /* Decode the base-64 encoded challenge message */ | |
| 184 if(*chlg64 != '=') { | |
| 185 result = Curl_base64_decode(chlg64, &chlg, &chlglen); | |
| 186 if(result) | |
| 187 return result; | |
| 188 } | |
| 189 | |
| 190 /* Ensure we have a valid challenge message */ | |
| 191 if(!chlg) { | |
| 192 infof(data, "SPNEGO handshake failure (empty challenge message)\n"); | |
| 193 | |
| 194 return CURLE_BAD_CONTENT_ENCODING; | |
| 195 } | |
| 196 | |
| 197 /* Setup the challenge "input" security buffer */ | |
| 198 chlg_desc.ulVersion = SECBUFFER_VERSION; | |
| 199 chlg_desc.cBuffers = 1; | |
| 200 chlg_desc.pBuffers = &chlg_buf[0]; | |
| 201 chlg_buf[0].BufferType = SECBUFFER_TOKEN; | |
| 202 chlg_buf[0].pvBuffer = chlg; | |
| 203 chlg_buf[0].cbBuffer = curlx_uztoul(chlglen); | |
| 204 | |
| 205 #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS | |
| 206 /* ssl context comes from Schannel. | |
| 207 * When extended protection is used in IIS server, | |
| 208 * we have to pass a second SecBuffer to the SecBufferDesc | |
| 209 * otherwise IIS will not pass the authentication (401 response). | |
| 210 * Minimum supported version is Windows 7. | |
| 211 * https://docs.microsoft.com/en-us/security-updates | |
| 212 * /SecurityAdvisories/2009/973811 | |
| 213 */ | |
| 214 if(nego->sslContext) { | |
| 215 SEC_CHANNEL_BINDINGS channelBindings; | |
| 216 SecPkgContext_Bindings pkgBindings; | |
| 217 pkgBindings.Bindings = &channelBindings; | |
| 218 nego->status = s_pSecFn->QueryContextAttributes( | |
| 219 nego->sslContext, | |
| 220 SECPKG_ATTR_ENDPOINT_BINDINGS, | |
| 221 &pkgBindings | |
| 222 ); | |
| 223 if(nego->status == SEC_E_OK) { | |
| 224 chlg_desc.cBuffers++; | |
| 225 chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS; | |
| 226 chlg_buf[1].cbBuffer = pkgBindings.BindingsLength; | |
| 227 chlg_buf[1].pvBuffer = pkgBindings.Bindings; | |
| 228 } | |
| 229 } | |
| 230 #endif | |
| 231 } | |
| 232 | |
| 233 /* Setup the response "output" security buffer */ | |
| 234 resp_desc.ulVersion = SECBUFFER_VERSION; | |
| 235 resp_desc.cBuffers = 1; | |
| 236 resp_desc.pBuffers = &resp_buf; | |
| 237 resp_buf.BufferType = SECBUFFER_TOKEN; | |
| 238 resp_buf.pvBuffer = nego->output_token; | |
| 239 resp_buf.cbBuffer = curlx_uztoul(nego->token_max); | |
| 240 | |
| 241 /* Generate our challenge-response message */ | |
| 242 nego->status = s_pSecFn->InitializeSecurityContext(nego->credentials, | |
| 243 chlg ? nego->context : | |
| 244 NULL, | |
| 245 nego->spn, | |
| 246 ISC_REQ_CONFIDENTIALITY, | |
| 247 0, SECURITY_NATIVE_DREP, | |
| 248 chlg ? &chlg_desc : NULL, | |
| 249 0, nego->context, | |
| 250 &resp_desc, &attrs, | |
| 251 &expiry); | |
| 252 | |
| 253 /* Free the decoded challenge as it is not required anymore */ | |
| 254 free(chlg); | |
| 255 | |
| 256 if(GSS_ERROR(nego->status)) { | |
| 257 char buffer[STRERROR_LEN]; | |
| 258 failf(data, "InitializeSecurityContext failed: %s", | |
| 259 Curl_sspi_strerror(nego->status, buffer, sizeof(buffer))); | |
| 260 | |
| 261 if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY) | |
| 262 return CURLE_OUT_OF_MEMORY; | |
| 263 | |
| 264 return CURLE_AUTH_ERROR; | |
| 265 } | |
| 266 | |
| 267 if(nego->status == SEC_I_COMPLETE_NEEDED || | |
| 268 nego->status == SEC_I_COMPLETE_AND_CONTINUE) { | |
| 269 nego->status = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc); | |
| 270 if(GSS_ERROR(nego->status)) { | |
| 271 char buffer[STRERROR_LEN]; | |
| 272 failf(data, "CompleteAuthToken failed: %s", | |
| 273 Curl_sspi_strerror(nego->status, buffer, sizeof(buffer))); | |
| 274 | |
| 275 if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY) | |
| 276 return CURLE_OUT_OF_MEMORY; | |
| 277 | |
| 278 return CURLE_AUTH_ERROR; | |
| 279 } | |
| 280 } | |
| 281 | |
| 282 nego->output_token_length = resp_buf.cbBuffer; | |
| 283 | |
| 284 return result; | |
| 285 } | |
| 286 | |
| 287 /* | |
| 288 * Curl_auth_create_spnego_message() | |
| 289 * | |
| 290 * This is used to generate an already encoded SPNEGO (Negotiate) response | |
| 291 * message ready for sending to the recipient. | |
| 292 * | |
| 293 * Parameters: | |
| 294 * | |
| 295 * data [in] - The session handle. | |
| 296 * nego [in/out] - The Negotiate data struct being used and modified. | |
| 297 * outptr [in/out] - The address where a pointer to newly allocated memory | |
| 298 * holding the result will be stored upon completion. | |
| 299 * outlen [out] - The length of the output message. | |
| 300 * | |
| 301 * Returns CURLE_OK on success. | |
| 302 */ | |
| 303 CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data, | |
| 304 struct negotiatedata *nego, | |
| 305 char **outptr, size_t *outlen) | |
| 306 { | |
| 307 CURLcode result; | |
| 308 | |
| 309 /* Base64 encode the already generated response */ | |
| 310 result = Curl_base64_encode(data, | |
| 311 (const char *) nego->output_token, | |
| 312 nego->output_token_length, | |
| 313 outptr, outlen); | |
| 314 | |
| 315 if(result) | |
| 316 return result; | |
| 317 | |
| 318 if(!*outptr || !*outlen) { | |
| 319 free(*outptr); | |
| 320 return CURLE_REMOTE_ACCESS_DENIED; | |
| 321 } | |
| 322 | |
| 323 return CURLE_OK; | |
| 324 } | |
| 325 | |
| 326 /* | |
| 327 * Curl_auth_cleanup_spnego() | |
| 328 * | |
| 329 * This is used to clean up the SPNEGO (Negotiate) specific data. | |
| 330 * | |
| 331 * Parameters: | |
| 332 * | |
| 333 * nego [in/out] - The Negotiate data struct being cleaned up. | |
| 334 * | |
| 335 */ | |
| 336 void Curl_auth_cleanup_spnego(struct negotiatedata *nego) | |
| 337 { | |
| 338 /* Free our security context */ | |
| 339 if(nego->context) { | |
| 340 s_pSecFn->DeleteSecurityContext(nego->context); | |
| 341 free(nego->context); | |
| 342 nego->context = NULL; | |
| 343 } | |
| 344 | |
| 345 /* Free our credentials handle */ | |
| 346 if(nego->credentials) { | |
| 347 s_pSecFn->FreeCredentialsHandle(nego->credentials); | |
| 348 free(nego->credentials); | |
| 349 nego->credentials = NULL; | |
| 350 } | |
| 351 | |
| 352 /* Free our identity */ | |
| 353 Curl_sspi_free_identity(nego->p_identity); | |
| 354 nego->p_identity = NULL; | |
| 355 | |
| 356 /* Free the SPN and output token */ | |
| 357 Curl_safefree(nego->spn); | |
| 358 Curl_safefree(nego->output_token); | |
| 359 | |
| 360 /* Reset any variables */ | |
| 361 nego->status = 0; | |
| 362 nego->token_max = 0; | |
| 363 nego->noauthpersist = FALSE; | |
| 364 nego->havenoauthpersist = FALSE; | |
| 365 nego->havenegdata = FALSE; | |
| 366 nego->havemultiplerequests = FALSE; | |
| 367 } | |
| 368 | |
| 369 #endif /* USE_WINDOWS_SSPI && USE_SPNEGO */ |
