curl_sasl.c (30297B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 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.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 * SPDX-License-Identifier: curl 22 * 23 * RFC2195 CRAM-MD5 authentication 24 * RFC2617 Basic and Digest Access Authentication 25 * RFC2831 DIGEST-MD5 authentication 26 * RFC4422 Simple Authentication and Security Layer (SASL) 27 * RFC4616 PLAIN authentication 28 * RFC5802 SCRAM-SHA-1 authentication 29 * RFC7677 SCRAM-SHA-256 authentication 30 * RFC6749 OAuth 2.0 Authorization Framework 31 * RFC7628 A Set of SASL Mechanisms for OAuth 32 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> 33 * 34 ***************************************************************************/ 35 36 #include "curl_setup.h" 37 38 #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ 39 !defined(CURL_DISABLE_POP3) || \ 40 (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) 41 42 #include <curl/curl.h> 43 #include "urldata.h" 44 45 #include "curlx/base64.h" 46 #include "curl_md5.h" 47 #include "vauth/vauth.h" 48 #include "cfilters.h" 49 #include "vtls/vtls.h" 50 #include "curl_hmac.h" 51 #include "curl_sasl.h" 52 #include "curlx/warnless.h" 53 #include "sendf.h" 54 /* The last 3 #include files should be in this order */ 55 #include "curl_printf.h" 56 #include "curl_memory.h" 57 #include "memdebug.h" 58 59 /* Supported mechanisms */ 60 static const struct { 61 const char *name; /* Name */ 62 size_t len; /* Name length */ 63 unsigned short bit; /* Flag bit */ 64 } mechtable[] = { 65 { "LOGIN", 5, SASL_MECH_LOGIN }, 66 { "PLAIN", 5, SASL_MECH_PLAIN }, 67 { "CRAM-MD5", 8, SASL_MECH_CRAM_MD5 }, 68 { "DIGEST-MD5", 10, SASL_MECH_DIGEST_MD5 }, 69 { "GSSAPI", 6, SASL_MECH_GSSAPI }, 70 { "EXTERNAL", 8, SASL_MECH_EXTERNAL }, 71 { "NTLM", 4, SASL_MECH_NTLM }, 72 { "XOAUTH2", 7, SASL_MECH_XOAUTH2 }, 73 { "OAUTHBEARER", 11, SASL_MECH_OAUTHBEARER }, 74 { "SCRAM-SHA-1", 11, SASL_MECH_SCRAM_SHA_1 }, 75 { "SCRAM-SHA-256",13, SASL_MECH_SCRAM_SHA_256 }, 76 { ZERO_NULL, 0, 0 } 77 }; 78 79 /* 80 * Curl_sasl_decode_mech() 81 * 82 * Convert a SASL mechanism name into a token. 83 * 84 * Parameters: 85 * 86 * ptr [in] - The mechanism string. 87 * maxlen [in] - Maximum mechanism string length. 88 * len [out] - If not NULL, effective name length. 89 * 90 * Returns the SASL mechanism token or 0 if no match. 91 */ 92 unsigned short Curl_sasl_decode_mech(const char *ptr, size_t maxlen, 93 size_t *len) 94 { 95 unsigned int i; 96 char c; 97 98 for(i = 0; mechtable[i].name; i++) { 99 if(maxlen >= mechtable[i].len && 100 !memcmp(ptr, mechtable[i].name, mechtable[i].len)) { 101 if(len) 102 *len = mechtable[i].len; 103 104 if(maxlen == mechtable[i].len) 105 return mechtable[i].bit; 106 107 c = ptr[mechtable[i].len]; 108 if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_') 109 return mechtable[i].bit; 110 } 111 } 112 113 return 0; 114 } 115 116 /* 117 * Curl_sasl_parse_url_auth_option() 118 * 119 * Parse the URL login options. 120 */ 121 CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, 122 const char *value, size_t len) 123 { 124 CURLcode result = CURLE_OK; 125 size_t mechlen; 126 127 if(!len) 128 return CURLE_URL_MALFORMAT; 129 130 if(sasl->resetprefs) { 131 sasl->resetprefs = FALSE; 132 sasl->prefmech = SASL_AUTH_NONE; 133 } 134 135 if(!strncmp(value, "*", len)) 136 sasl->prefmech = SASL_AUTH_DEFAULT; 137 else { 138 unsigned short mechbit = Curl_sasl_decode_mech(value, len, &mechlen); 139 if(mechbit && mechlen == len) 140 sasl->prefmech |= mechbit; 141 else 142 result = CURLE_URL_MALFORMAT; 143 } 144 145 return result; 146 } 147 148 /* 149 * Curl_sasl_init() 150 * 151 * Initializes the SASL structure. 152 */ 153 void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data, 154 const struct SASLproto *params) 155 { 156 unsigned long auth = data->set.httpauth; 157 158 sasl->params = params; /* Set protocol dependent parameters */ 159 sasl->state = SASL_STOP; /* Not yet running */ 160 sasl->curmech = NULL; /* No mechanism yet. */ 161 sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */ 162 sasl->prefmech = params->defmechs; /* Default preferred mechanisms */ 163 sasl->authused = SASL_AUTH_NONE; /* The authentication mechanism used */ 164 sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */ 165 sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */ 166 sasl->force_ir = FALSE; /* Respect external option */ 167 168 if(auth != CURLAUTH_BASIC) { 169 unsigned short mechs = SASL_AUTH_NONE; 170 171 /* If some usable http authentication options have been set, determine 172 new defaults from them. */ 173 if(auth & CURLAUTH_BASIC) 174 mechs |= SASL_MECH_PLAIN | SASL_MECH_LOGIN; 175 if(auth & CURLAUTH_DIGEST) 176 mechs |= SASL_MECH_DIGEST_MD5; 177 if(auth & CURLAUTH_NTLM) 178 mechs |= SASL_MECH_NTLM; 179 if(auth & CURLAUTH_BEARER) 180 mechs |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2; 181 if(auth & CURLAUTH_GSSAPI) 182 mechs |= SASL_MECH_GSSAPI; 183 184 if(mechs != SASL_AUTH_NONE) 185 sasl->prefmech = mechs; 186 } 187 } 188 189 /* 190 * sasl_state() 191 * 192 * This is the ONLY way to change SASL state! 193 */ 194 static void sasl_state(struct SASL *sasl, struct Curl_easy *data, 195 saslstate newstate) 196 { 197 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 198 /* for debug purposes */ 199 static const char * const names[]={ 200 "STOP", 201 "PLAIN", 202 "LOGIN", 203 "LOGIN_PASSWD", 204 "EXTERNAL", 205 "CRAMMD5", 206 "DIGESTMD5", 207 "DIGESTMD5_RESP", 208 "NTLM", 209 "NTLM_TYPE2MSG", 210 "GSSAPI", 211 "GSSAPI_TOKEN", 212 "GSSAPI_NO_DATA", 213 "OAUTH2", 214 "OAUTH2_RESP", 215 "GSASL", 216 "CANCEL", 217 "FINAL", 218 /* LAST */ 219 }; 220 221 if(sasl->state != newstate) 222 infof(data, "SASL %p state change from %s to %s", 223 (void *)sasl, names[sasl->state], names[newstate]); 224 #else 225 (void) data; 226 #endif 227 228 sasl->state = newstate; 229 } 230 231 #if defined(USE_NTLM) || defined(USE_GSASL) || defined(USE_KERBEROS5) || \ 232 !defined(CURL_DISABLE_DIGEST_AUTH) 233 /* Get the SASL server message and convert it to binary. */ 234 static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data, 235 struct bufref *out) 236 { 237 CURLcode result = CURLE_OK; 238 239 result = sasl->params->getmessage(data, out); 240 if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) { 241 unsigned char *msg; 242 size_t msglen; 243 const char *serverdata = (const char *) Curl_bufref_ptr(out); 244 245 if(!*serverdata || *serverdata == '=') 246 Curl_bufref_set(out, NULL, 0, NULL); 247 else { 248 result = curlx_base64_decode(serverdata, &msg, &msglen); 249 if(!result) 250 Curl_bufref_set(out, msg, msglen, curl_free); 251 } 252 } 253 return result; 254 } 255 #endif 256 257 /* Encode the outgoing SASL message. */ 258 static CURLcode build_message(struct SASL *sasl, struct bufref *msg) 259 { 260 CURLcode result = CURLE_OK; 261 262 if(sasl->params->flags & SASL_FLAG_BASE64) { 263 if(!Curl_bufref_ptr(msg)) /* Empty message. */ 264 Curl_bufref_set(msg, "", 0, NULL); 265 else if(!Curl_bufref_len(msg)) /* Explicit empty response. */ 266 Curl_bufref_set(msg, "=", 1, NULL); 267 else { 268 char *base64; 269 size_t base64len; 270 271 result = curlx_base64_encode((const char *) Curl_bufref_ptr(msg), 272 Curl_bufref_len(msg), &base64, &base64len); 273 if(!result) 274 Curl_bufref_set(msg, base64, base64len, curl_free); 275 } 276 } 277 278 return result; 279 } 280 281 /* 282 * Curl_sasl_can_authenticate() 283 * 284 * Check if we have enough auth data and capabilities to authenticate. 285 */ 286 bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data) 287 { 288 /* Have credentials been provided? */ 289 if(data->state.aptr.user) 290 return TRUE; 291 292 /* EXTERNAL can authenticate without a username and/or password */ 293 if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL) 294 return TRUE; 295 296 return FALSE; 297 } 298 299 struct sasl_ctx { 300 struct SASL *sasl; 301 struct connectdata *conn; 302 const char *user; 303 unsigned short enabledmechs; 304 const char *mech; 305 saslstate state1; 306 saslstate state2; 307 struct bufref resp; 308 CURLcode result; 309 }; 310 311 static bool sasl_choose_external(struct Curl_easy *data, struct sasl_ctx *sctx) 312 { 313 if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && !sctx->conn->passwd[0]) { 314 sctx->mech = SASL_MECH_STRING_EXTERNAL; 315 sctx->state1 = SASL_EXTERNAL; 316 sctx->sasl->authused = SASL_MECH_EXTERNAL; 317 318 if(sctx->sasl->force_ir || data->set.sasl_ir) 319 Curl_auth_create_external_message(sctx->conn->user, &sctx->resp); 320 return TRUE; 321 } 322 return FALSE; 323 } 324 325 #ifdef USE_KERBEROS5 326 static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) 327 { 328 if(sctx->user && 329 (sctx->enabledmechs & SASL_MECH_GSSAPI) && 330 Curl_auth_is_gssapi_supported() && 331 Curl_auth_user_contains_domain(sctx->conn->user)) { 332 const char *service = data->set.str[STRING_SERVICE_NAME] ? 333 data->set.str[STRING_SERVICE_NAME] : 334 sctx->sasl->params->service; 335 336 sctx->sasl->mutual_auth = FALSE; 337 sctx->mech = SASL_MECH_STRING_GSSAPI; 338 sctx->state1 = SASL_GSSAPI; 339 sctx->state2 = SASL_GSSAPI_TOKEN; 340 sctx->sasl->authused = SASL_MECH_GSSAPI; 341 342 if(sctx->sasl->force_ir || data->set.sasl_ir) { 343 struct kerberos5data *krb5 = Curl_auth_krb5_get(sctx->conn); 344 sctx->result = !krb5 ? CURLE_OUT_OF_MEMORY : 345 Curl_auth_create_gssapi_user_message(data, sctx->conn->user, 346 sctx->conn->passwd, 347 service, sctx->conn->host.name, 348 sctx->sasl->mutual_auth, NULL, 349 krb5, &sctx->resp); 350 } 351 return TRUE; 352 } 353 return FALSE; 354 } 355 #endif /* USE_KERBEROS5 */ 356 357 #ifdef USE_GSASL 358 static bool sasl_choose_gsasl(struct Curl_easy *data, struct sasl_ctx *sctx) 359 { 360 struct gsasldata *gsasl; 361 struct bufref nullmsg; 362 363 if(sctx->user && 364 (sctx->enabledmechs & (SASL_MECH_SCRAM_SHA_256|SASL_MECH_SCRAM_SHA_1))) { 365 gsasl = Curl_auth_gsasl_get(sctx->conn); 366 if(!gsasl) { 367 sctx->result = CURLE_OUT_OF_MEMORY; 368 return TRUE; /* attempted, but failed */ 369 } 370 371 if((sctx->enabledmechs & SASL_MECH_SCRAM_SHA_256) && 372 Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256, 373 gsasl)) { 374 sctx->mech = SASL_MECH_STRING_SCRAM_SHA_256; 375 sctx->sasl->authused = SASL_MECH_SCRAM_SHA_256; 376 } 377 else if((sctx->enabledmechs & SASL_MECH_SCRAM_SHA_1) && 378 Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1, 379 gsasl)) { 380 sctx->mech = SASL_MECH_STRING_SCRAM_SHA_1; 381 sctx->sasl->authused = SASL_MECH_SCRAM_SHA_1; 382 } 383 else 384 return FALSE; 385 386 Curl_bufref_init(&nullmsg); 387 sctx->state1 = SASL_GSASL; 388 sctx->state2 = SASL_GSASL; 389 sctx->result = Curl_auth_gsasl_start(data, sctx->conn->user, 390 sctx->conn->passwd, gsasl); 391 if(!sctx->result && (sctx->sasl->force_ir || data->set.sasl_ir)) 392 sctx->result = Curl_auth_gsasl_token(data, &nullmsg, gsasl, &sctx->resp); 393 return TRUE; 394 } 395 return FALSE; 396 } 397 398 #endif /* USE_GSASL */ 399 400 #ifndef CURL_DISABLE_DIGEST_AUTH 401 static bool sasl_choose_digest(struct Curl_easy *data, struct sasl_ctx *sctx) 402 { 403 (void)data; 404 if(!sctx->user) 405 return FALSE; 406 else if((sctx->enabledmechs & SASL_MECH_DIGEST_MD5) && 407 Curl_auth_is_digest_supported()) { 408 sctx->mech = SASL_MECH_STRING_DIGEST_MD5; 409 sctx->state1 = SASL_DIGESTMD5; 410 sctx->sasl->authused = SASL_MECH_DIGEST_MD5; 411 return TRUE; 412 } 413 else if(sctx->enabledmechs & SASL_MECH_CRAM_MD5) { 414 sctx->mech = SASL_MECH_STRING_CRAM_MD5; 415 sctx->state1 = SASL_CRAMMD5; 416 sctx->sasl->authused = SASL_MECH_CRAM_MD5; 417 return TRUE; 418 } 419 return FALSE; 420 } 421 #endif /* !CURL_DISABLE_DIGEST_AUTH */ 422 423 #ifdef USE_NTLM 424 static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) 425 { 426 if(!sctx->user) 427 return FALSE; 428 else if((sctx->enabledmechs & SASL_MECH_NTLM) && 429 Curl_auth_is_ntlm_supported()) { 430 const char *service = data->set.str[STRING_SERVICE_NAME] ? 431 data->set.str[STRING_SERVICE_NAME] : 432 sctx->sasl->params->service; 433 const char *hostname; 434 int port; 435 436 Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); 437 438 sctx->mech = SASL_MECH_STRING_NTLM; 439 sctx->state1 = SASL_NTLM; 440 sctx->state2 = SASL_NTLM_TYPE2MSG; 441 sctx->sasl->authused = SASL_MECH_NTLM; 442 443 if(sctx->sasl->force_ir || data->set.sasl_ir) { 444 struct ntlmdata *ntlm = Curl_auth_ntlm_get(sctx->conn, FALSE); 445 sctx->result = !ntlm ? CURLE_OUT_OF_MEMORY : 446 Curl_auth_create_ntlm_type1_message(data, 447 sctx->conn->user, 448 sctx->conn->passwd, 449 service, hostname, 450 ntlm, &sctx->resp); 451 } 452 return TRUE; 453 } 454 return FALSE; 455 } 456 #endif /* USE_NTLM */ 457 458 static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) 459 { 460 const char *oauth_bearer = data->set.str[STRING_BEARER]; 461 462 if(sctx->user && oauth_bearer && 463 (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) { 464 const char *hostname; 465 int port; 466 Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); 467 468 sctx->mech = SASL_MECH_STRING_OAUTHBEARER; 469 sctx->state1 = SASL_OAUTH2; 470 sctx->state2 = SASL_OAUTH2_RESP; 471 sctx->sasl->authused = SASL_MECH_OAUTHBEARER; 472 473 if(sctx->sasl->force_ir || data->set.sasl_ir) 474 sctx->result = 475 Curl_auth_create_oauth_bearer_message(sctx->conn->user, 476 hostname, port, 477 oauth_bearer, &sctx->resp); 478 return TRUE; 479 } 480 return FALSE; 481 } 482 483 static bool sasl_choose_oauth2(struct Curl_easy *data, struct sasl_ctx *sctx) 484 { 485 const char *oauth_bearer = data->set.str[STRING_BEARER]; 486 487 if(sctx->user && oauth_bearer && 488 (sctx->enabledmechs & SASL_MECH_XOAUTH2)) { 489 sctx->mech = SASL_MECH_STRING_XOAUTH2; 490 sctx->state1 = SASL_OAUTH2; 491 sctx->sasl->authused = SASL_MECH_XOAUTH2; 492 493 if(sctx->sasl->force_ir || data->set.sasl_ir) 494 sctx->result = Curl_auth_create_xoauth_bearer_message(sctx->conn->user, 495 oauth_bearer, 496 &sctx->resp); 497 return TRUE; 498 } 499 return FALSE; 500 } 501 502 static bool sasl_choose_plain(struct Curl_easy *data, struct sasl_ctx *sctx) 503 { 504 if(sctx->user && (sctx->enabledmechs & SASL_MECH_PLAIN)) { 505 sctx->mech = SASL_MECH_STRING_PLAIN; 506 sctx->state1 = SASL_PLAIN; 507 sctx->sasl->authused = SASL_MECH_PLAIN; 508 509 if(sctx->sasl->force_ir || data->set.sasl_ir) 510 sctx->result = 511 Curl_auth_create_plain_message(sctx->conn->sasl_authzid, 512 sctx->conn->user, sctx->conn->passwd, 513 &sctx->resp); 514 return TRUE; 515 } 516 return FALSE; 517 } 518 519 static bool sasl_choose_login(struct Curl_easy *data, struct sasl_ctx *sctx) 520 { 521 if(sctx->user && (sctx->enabledmechs & SASL_MECH_LOGIN)) { 522 sctx->mech = SASL_MECH_STRING_LOGIN; 523 sctx->state1 = SASL_LOGIN; 524 sctx->state2 = SASL_LOGIN_PASSWD; 525 sctx->sasl->authused = SASL_MECH_LOGIN; 526 527 if(sctx->sasl->force_ir || data->set.sasl_ir) 528 Curl_auth_create_login_message(sctx->conn->user, &sctx->resp); 529 return TRUE; 530 } 531 return FALSE; 532 } 533 534 /* 535 * Curl_sasl_start() 536 * 537 * Calculate the required login details for SASL authentication. 538 */ 539 CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, 540 bool force_ir, saslprogress *progress) 541 { 542 struct sasl_ctx sctx; 543 544 sasl->force_ir = force_ir; /* Latch for future use */ 545 sasl->authused = 0; /* No mechanism used yet */ 546 *progress = SASL_IDLE; 547 548 memset(&sctx, 0, sizeof(sctx)); 549 sctx.sasl = sasl; 550 sctx.conn = data->conn; 551 sctx.user = data->state.aptr.user; 552 Curl_bufref_init(&sctx.resp); 553 sctx.enabledmechs = sasl->authmechs & sasl->prefmech; 554 sctx.state1 = SASL_STOP; 555 sctx.state2 = SASL_FINAL; 556 557 /* Calculate the supported authentication mechanism, by decreasing order of 558 security, as well as the initial response where appropriate */ 559 if(sasl_choose_external(data, &sctx) || 560 #if defined(USE_KERBEROS5) 561 sasl_choose_krb5(data, &sctx) || 562 #endif 563 #ifdef USE_GSASL 564 sasl_choose_gsasl(data, &sctx) || 565 #endif 566 #ifndef CURL_DISABLE_DIGEST_AUTH 567 sasl_choose_digest(data, &sctx) || 568 #endif 569 #ifdef USE_NTLM 570 sasl_choose_ntlm(data, &sctx) || 571 #endif 572 sasl_choose_oauth(data, &sctx) || 573 sasl_choose_oauth2(data, &sctx) || 574 sasl_choose_plain(data, &sctx) || 575 sasl_choose_login(data, &sctx)) { 576 /* selected, either we have a mechanism or a failure */ 577 DEBUGASSERT(sctx.mech || sctx.result); 578 } 579 580 if(!sctx.result && sctx.mech) { 581 sasl->curmech = sctx.mech; 582 if(Curl_bufref_ptr(&sctx.resp)) 583 sctx.result = build_message(sasl, &sctx.resp); 584 585 if(sasl->params->maxirlen && 586 strlen(sctx.mech) + Curl_bufref_len(&sctx.resp) > 587 sasl->params->maxirlen) 588 Curl_bufref_free(&sctx.resp); 589 590 if(!sctx.result) 591 sctx.result = sasl->params->sendauth(data, sctx.mech, &sctx.resp); 592 593 if(!sctx.result) { 594 *progress = SASL_INPROGRESS; 595 sasl_state(sasl, data, Curl_bufref_ptr(&sctx.resp) ? 596 sctx.state2 : sctx.state1); 597 } 598 } 599 600 Curl_bufref_free(&sctx.resp); 601 return sctx.result; 602 } 603 604 /* 605 * Curl_sasl_continue() 606 * 607 * Continue the authentication. 608 */ 609 CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, 610 int code, saslprogress *progress) 611 { 612 CURLcode result = CURLE_OK; 613 struct connectdata *conn = data->conn; 614 saslstate newstate = SASL_FINAL; 615 struct bufref resp; 616 const char *hostname; 617 int port; 618 #if defined(USE_KERBEROS5) || defined(USE_NTLM) \ 619 || !defined(CURL_DISABLE_DIGEST_AUTH) 620 const char *service = data->set.str[STRING_SERVICE_NAME] ? 621 data->set.str[STRING_SERVICE_NAME] : 622 sasl->params->service; 623 #endif 624 const char *oauth_bearer = data->set.str[STRING_BEARER]; 625 struct bufref serverdata; 626 627 Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); 628 Curl_bufref_init(&serverdata); 629 Curl_bufref_init(&resp); 630 *progress = SASL_INPROGRESS; 631 632 if(sasl->state == SASL_FINAL) { 633 if(code != sasl->params->finalcode) 634 result = CURLE_LOGIN_DENIED; 635 *progress = SASL_DONE; 636 sasl_state(sasl, data, SASL_STOP); 637 return result; 638 } 639 640 if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP && 641 code != sasl->params->contcode) { 642 *progress = SASL_DONE; 643 sasl_state(sasl, data, SASL_STOP); 644 return CURLE_LOGIN_DENIED; 645 } 646 647 switch(sasl->state) { 648 case SASL_STOP: 649 *progress = SASL_DONE; 650 return result; 651 case SASL_PLAIN: 652 result = Curl_auth_create_plain_message(conn->sasl_authzid, 653 conn->user, conn->passwd, &resp); 654 break; 655 case SASL_LOGIN: 656 Curl_auth_create_login_message(conn->user, &resp); 657 newstate = SASL_LOGIN_PASSWD; 658 break; 659 case SASL_LOGIN_PASSWD: 660 Curl_auth_create_login_message(conn->passwd, &resp); 661 break; 662 case SASL_EXTERNAL: 663 Curl_auth_create_external_message(conn->user, &resp); 664 break; 665 #ifdef USE_GSASL 666 case SASL_GSASL: 667 result = get_server_message(sasl, data, &serverdata); 668 if(!result) { 669 struct gsasldata *gsasl = Curl_auth_gsasl_get(conn); 670 result = !gsasl ? CURLE_OUT_OF_MEMORY : 671 Curl_auth_gsasl_token(data, &serverdata, gsasl, &resp); 672 } 673 if(!result && Curl_bufref_len(&resp) > 0) 674 newstate = SASL_GSASL; 675 break; 676 #endif 677 #ifndef CURL_DISABLE_DIGEST_AUTH 678 case SASL_CRAMMD5: 679 result = get_server_message(sasl, data, &serverdata); 680 if(!result) 681 result = Curl_auth_create_cram_md5_message(&serverdata, conn->user, 682 conn->passwd, &resp); 683 break; 684 case SASL_DIGESTMD5: 685 result = get_server_message(sasl, data, &serverdata); 686 if(!result) 687 result = Curl_auth_create_digest_md5_message(data, &serverdata, 688 conn->user, conn->passwd, 689 service, &resp); 690 if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) 691 newstate = SASL_DIGESTMD5_RESP; 692 break; 693 case SASL_DIGESTMD5_RESP: 694 /* Keep response NULL to output an empty line. */ 695 break; 696 #endif 697 698 #ifdef USE_NTLM 699 case SASL_NTLM: { 700 /* Create the type-1 message */ 701 struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE); 702 result = !ntlm ? CURLE_OUT_OF_MEMORY : 703 Curl_auth_create_ntlm_type1_message(data, 704 conn->user, conn->passwd, 705 service, hostname, 706 ntlm, &resp); 707 newstate = SASL_NTLM_TYPE2MSG; 708 break; 709 } 710 case SASL_NTLM_TYPE2MSG: { 711 /* Decode the type-2 message */ 712 struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE); 713 result = !ntlm ? CURLE_FAILED_INIT : 714 get_server_message(sasl, data, &serverdata); 715 if(!result) 716 result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, ntlm); 717 if(!result) 718 result = Curl_auth_create_ntlm_type3_message(data, conn->user, 719 conn->passwd, ntlm, 720 &resp); 721 break; 722 } 723 #endif 724 725 #if defined(USE_KERBEROS5) 726 case SASL_GSSAPI: { 727 struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); 728 result = !krb5 ? CURLE_OUT_OF_MEMORY : 729 Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd, 730 service, conn->host.name, 731 sasl->mutual_auth, NULL, 732 krb5, &resp); 733 newstate = SASL_GSSAPI_TOKEN; 734 break; 735 } 736 case SASL_GSSAPI_TOKEN: 737 result = get_server_message(sasl, data, &serverdata); 738 if(!result) { 739 struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); 740 if(!krb5) 741 result = CURLE_OUT_OF_MEMORY; 742 else if(sasl->mutual_auth) { 743 /* Decode the user token challenge and create the optional response 744 message */ 745 result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, 746 NULL, NULL, 747 sasl->mutual_auth, 748 &serverdata, 749 krb5, &resp); 750 newstate = SASL_GSSAPI_NO_DATA; 751 } 752 else 753 /* Decode the security challenge and create the response message */ 754 result = Curl_auth_create_gssapi_security_message(data, 755 conn->sasl_authzid, 756 &serverdata, 757 krb5, &resp); 758 } 759 break; 760 case SASL_GSSAPI_NO_DATA: 761 /* Decode the security challenge and create the response message */ 762 result = get_server_message(sasl, data, &serverdata); 763 if(!result) { 764 struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); 765 if(!krb5) 766 result = CURLE_OUT_OF_MEMORY; 767 else 768 result = Curl_auth_create_gssapi_security_message(data, 769 conn->sasl_authzid, 770 &serverdata, 771 krb5, &resp); 772 } 773 break; 774 #endif 775 776 case SASL_OAUTH2: 777 /* Create the authorization message */ 778 if(sasl->authused == SASL_MECH_OAUTHBEARER) { 779 result = Curl_auth_create_oauth_bearer_message(conn->user, 780 hostname, 781 port, 782 oauth_bearer, 783 &resp); 784 785 /* Failures maybe sent by the server as continuations for OAUTHBEARER */ 786 newstate = SASL_OAUTH2_RESP; 787 } 788 else 789 result = Curl_auth_create_xoauth_bearer_message(conn->user, 790 oauth_bearer, 791 &resp); 792 break; 793 794 case SASL_OAUTH2_RESP: 795 /* The continuation is optional so check the response code */ 796 if(code == sasl->params->finalcode) { 797 /* Final response was received so we are done */ 798 *progress = SASL_DONE; 799 sasl_state(sasl, data, SASL_STOP); 800 return result; 801 } 802 else if(code == sasl->params->contcode) { 803 /* Acknowledge the continuation by sending a 0x01 response. */ 804 Curl_bufref_set(&resp, "\x01", 1, NULL); 805 break; 806 } 807 else { 808 *progress = SASL_DONE; 809 sasl_state(sasl, data, SASL_STOP); 810 return CURLE_LOGIN_DENIED; 811 } 812 813 case SASL_CANCEL: 814 /* Remove the offending mechanism from the supported list */ 815 sasl->authmechs ^= sasl->authused; 816 817 /* Start an alternative SASL authentication */ 818 return Curl_sasl_start(sasl, data, sasl->force_ir, progress); 819 default: 820 failf(data, "Unsupported SASL authentication mechanism"); 821 result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */ 822 break; 823 } 824 825 Curl_bufref_free(&serverdata); 826 827 switch(result) { 828 case CURLE_BAD_CONTENT_ENCODING: 829 /* Cancel dialog */ 830 result = sasl->params->cancelauth(data, sasl->curmech); 831 newstate = SASL_CANCEL; 832 break; 833 case CURLE_OK: 834 result = build_message(sasl, &resp); 835 if(!result) 836 result = sasl->params->contauth(data, sasl->curmech, &resp); 837 break; 838 default: 839 newstate = SASL_STOP; /* Stop on error */ 840 *progress = SASL_DONE; 841 break; 842 } 843 844 Curl_bufref_free(&resp); 845 846 sasl_state(sasl, data, newstate); 847 848 return result; 849 } 850 851 #ifndef CURL_DISABLE_VERBOSE_STRINGS 852 static void sasl_unchosen(struct Curl_easy *data, unsigned short mech, 853 unsigned short enabledmechs, 854 bool built_in, bool platform, 855 const char *param_missing) 856 { 857 const char *mname = NULL; 858 size_t i; 859 860 if(!(enabledmechs & mech)) 861 return; 862 863 for(i = 0; mechtable[i].name; ++i) { 864 if(mechtable[i].bit == mech) { 865 mname = mechtable[i].name; 866 break; 867 } 868 } 869 if(!mname) /* should not happen */ 870 return; 871 if(!built_in) 872 infof(data, "SASL: %s not builtin", mname); 873 else if(!platform) 874 infof(data, "SASL: %s not supported by the platform/libraries", mname); 875 else { 876 if(param_missing) 877 infof(data, "SASL: %s is missing %s", mname, param_missing); 878 if(!data->state.aptr.user) 879 infof(data, "SASL: %s is missing username", mname); 880 } 881 } 882 #endif /* CURL_DISABLE_VERBOSE_STRINGS */ 883 884 CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) 885 { 886 #ifndef CURL_DISABLE_VERBOSE_STRINGS 887 #ifdef USE_KERBEROS5 888 #define CURL_SASL_KERBEROS5 TRUE 889 #else 890 #define CURL_SASL_KERBEROS5 FALSE 891 #endif 892 #ifdef USE_GSASL 893 #define CURL_SASL_GASL TRUE 894 #else 895 #define CURL_SASL_GASL FALSE 896 #endif 897 #ifdef CURL_DISABLE_DIGEST_AUTH 898 #define CURL_SASL_DIGEST TRUE 899 #else 900 #define CURL_SASL_DIGEST FALSE 901 #endif 902 #ifndef USE_NTLM 903 #define CURL_SASL_NTLM TRUE 904 #else 905 #define CURL_SASL_NTLM FALSE 906 #endif 907 /* Failing SASL authentication is a pain. Give a helping hand if 908 * we were unable to select an AUTH mechanism. 909 * `sasl->authmechs` are mechanisms offered by the peer 910 * `sasl->prefmech` are mechanisms preferred by us */ 911 unsigned short enabledmechs = sasl->authmechs & sasl->prefmech; 912 913 if(!sasl->authmechs) 914 infof(data, "SASL: no auth mechanism was offered or recognized"); 915 else if(!enabledmechs) 916 infof(data, "SASL: no overlap between offered and configured " 917 "auth mechanisms"); 918 else { 919 infof(data, "SASL: no auth mechanism offered could be selected"); 920 if((enabledmechs & SASL_MECH_EXTERNAL) && data->conn->passwd[0]) 921 infof(data, "SASL: auth EXTERNAL not chosen with password"); 922 sasl_unchosen(data, SASL_MECH_GSSAPI, enabledmechs, 923 CURL_SASL_KERBEROS5, Curl_auth_is_gssapi_supported(), NULL); 924 sasl_unchosen(data, SASL_MECH_SCRAM_SHA_256, enabledmechs, 925 CURL_SASL_GASL, FALSE, NULL); 926 sasl_unchosen(data, SASL_MECH_SCRAM_SHA_1, enabledmechs, 927 CURL_SASL_GASL, FALSE, NULL); 928 sasl_unchosen(data, SASL_MECH_DIGEST_MD5, enabledmechs, 929 CURL_SASL_DIGEST, Curl_auth_is_digest_supported(), NULL); 930 sasl_unchosen(data, SASL_MECH_CRAM_MD5, enabledmechs, 931 CURL_SASL_DIGEST, TRUE, NULL); 932 sasl_unchosen(data, SASL_MECH_NTLM, enabledmechs, 933 CURL_SASL_NTLM, Curl_auth_is_ntlm_supported(), NULL); 934 sasl_unchosen(data, SASL_MECH_OAUTHBEARER, enabledmechs, TRUE, TRUE, 935 data->set.str[STRING_BEARER] ? 936 NULL : "CURLOPT_XOAUTH2_BEARER"); 937 sasl_unchosen(data, SASL_MECH_XOAUTH2, enabledmechs, TRUE, TRUE, 938 data->set.str[STRING_BEARER] ? 939 NULL : "CURLOPT_XOAUTH2_BEARER"); 940 } 941 #endif /* CURL_DISABLE_VERBOSE_STRINGS */ 942 (void)sasl; 943 (void)data; 944 return CURLE_LOGIN_DENIED; 945 } 946 947 #endif /* protocols are enabled that use SASL */