1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel (at) 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 * RFC2195 CRAM-MD5 authentication 22 * RFC2617 Basic and Digest Access Authentication 23 * RFC2831 DIGEST-MD5 authentication 24 * RFC4422 Simple Authentication and Security Layer (SASL) 25 * RFC4616 PLAIN authentication 26 * RFC6749 OAuth 2.0 Authorization Framework 27 * RFC7628 A Set of SASL Mechanisms for OAuth 28 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> 29 * 30 ***************************************************************************/ 31 32 #include "curl_setup.h" 33 34 #include <curl/curl.h> 35 #include "urldata.h" 36 37 #include "curl_base64.h" 38 #include "curl_md5.h" 39 #include "vauth/vauth.h" 40 #include "vtls/vtls.h" 41 #include "curl_hmac.h" 42 #include "curl_sasl.h" 43 #include "warnless.h" 44 #include "strtok.h" 45 #include "sendf.h" 46 #include "non-ascii.h" /* included for Curl_convert_... prototypes */ 47 /* The last 3 #include files should be in this order */ 48 #include "curl_printf.h" 49 #include "curl_memory.h" 50 #include "memdebug.h" 51 52 /* Supported mechanisms */ 53 static const struct { 54 const char *name; /* Name */ 55 size_t len; /* Name length */ 56 unsigned int bit; /* Flag bit */ 57 } mechtable[] = { 58 { "LOGIN", 5, SASL_MECH_LOGIN }, 59 { "PLAIN", 5, SASL_MECH_PLAIN }, 60 { "CRAM-MD5", 8, SASL_MECH_CRAM_MD5 }, 61 { "DIGEST-MD5", 10, SASL_MECH_DIGEST_MD5 }, 62 { "GSSAPI", 6, SASL_MECH_GSSAPI }, 63 { "EXTERNAL", 8, SASL_MECH_EXTERNAL }, 64 { "NTLM", 4, SASL_MECH_NTLM }, 65 { "XOAUTH2", 7, SASL_MECH_XOAUTH2 }, 66 { "OAUTHBEARER", 11, SASL_MECH_OAUTHBEARER }, 67 { ZERO_NULL, 0, 0 } 68 }; 69 70 /* 71 * Curl_sasl_cleanup() 72 * 73 * This is used to cleanup any libraries or curl modules used by the sasl 74 * functions. 75 * 76 * Parameters: 77 * 78 * conn [in] - The connection data. 79 * authused [in] - The authentication mechanism used. 80 */ 81 void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused) 82 { 83 #if defined(USE_KERBEROS5) 84 /* Cleanup the gssapi structure */ 85 if(authused == SASL_MECH_GSSAPI) { 86 Curl_auth_gssapi_cleanup(&conn->krb5); 87 } 88 #endif 89 90 #if defined(USE_NTLM) 91 /* Cleanup the NTLM structure */ 92 if(authused == SASL_MECH_NTLM) { 93 Curl_auth_ntlm_cleanup(&conn->ntlm); 94 } 95 #endif 96 97 #if !defined(USE_KERBEROS5) && !defined(USE_NTLM) 98 /* Reserved for future use */ 99 (void)conn; 100 (void)authused; 101 #endif 102 } 103 104 /* 105 * Curl_sasl_decode_mech() 106 * 107 * Convert a SASL mechanism name into a token. 108 * 109 * Parameters: 110 * 111 * ptr [in] - The mechanism string. 112 * maxlen [in] - Maximum mechanism string length. 113 * len [out] - If not NULL, effective name length. 114 * 115 * Returns the SASL mechanism token or 0 if no match. 116 */ 117 unsigned int Curl_sasl_decode_mech(const char *ptr, size_t maxlen, size_t *len) 118 { 119 unsigned int i; 120 char c; 121 122 for(i = 0; mechtable[i].name; i++) { 123 if(maxlen >= mechtable[i].len && 124 !memcmp(ptr, mechtable[i].name, mechtable[i].len)) { 125 if(len) 126 *len = mechtable[i].len; 127 128 if(maxlen == mechtable[i].len) 129 return mechtable[i].bit; 130 131 c = ptr[mechtable[i].len]; 132 if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_') 133 return mechtable[i].bit; 134 } 135 } 136 137 return 0; 138 } 139 140 /* 141 * Curl_sasl_parse_url_auth_option() 142 * 143 * Parse the URL login options. 144 */ 145 CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, 146 const char *value, size_t len) 147 { 148 CURLcode result = CURLE_OK; 149 size_t mechlen; 150 151 if(!len) 152 return CURLE_URL_MALFORMAT; 153 154 if(sasl->resetprefs) { 155 sasl->resetprefs = FALSE; 156 sasl->prefmech = SASL_AUTH_NONE; 157 } 158 159 if(!strncmp(value, "*", len)) 160 sasl->prefmech = SASL_AUTH_DEFAULT; 161 else { 162 unsigned int mechbit = Curl_sasl_decode_mech(value, len, &mechlen); 163 if(mechbit && mechlen == len) 164 sasl->prefmech |= mechbit; 165 else 166 result = CURLE_URL_MALFORMAT; 167 } 168 169 return result; 170 } 171 172 /* 173 * Curl_sasl_init() 174 * 175 * Initializes the SASL structure. 176 */ 177 void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params) 178 { 179 sasl->params = params; /* Set protocol dependent parameters */ 180 sasl->state = SASL_STOP; /* Not yet running */ 181 sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */ 182 sasl->prefmech = SASL_AUTH_DEFAULT; /* Prefer all mechanisms */ 183 sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */ 184 sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */ 185 sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */ 186 sasl->force_ir = FALSE; /* Respect external option */ 187 } 188 189 /* 190 * state() 191 * 192 * This is the ONLY way to change SASL state! 193 */ 194 static void state(struct SASL *sasl, struct connectdata *conn, 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 "CANCEL", 216 "FINAL", 217 /* LAST */ 218 }; 219 220 if(sasl->state != newstate) 221 infof(conn->data, "SASL %p state change from %s to %s\n", 222 (void *)sasl, names[sasl->state], names[newstate]); 223 #else 224 (void) conn; 225 #endif 226 227 sasl->state = newstate; 228 } 229 230 /* 231 * Curl_sasl_can_authenticate() 232 * 233 * Check if we have enough auth data and capabilities to authenticate. 234 */ 235 bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn) 236 { 237 /* Have credentials been provided? */ 238 if(conn->bits.user_passwd) 239 return TRUE; 240 241 /* EXTERNAL can authenticate without a user name and/or password */ 242 if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL) 243 return TRUE; 244 245 return FALSE; 246 } 247 248 /* 249 * Curl_sasl_start() 250 * 251 * Calculate the required login details for SASL authentication. 252 */ 253 CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, 254 bool force_ir, saslprogress *progress) 255 { 256 CURLcode result = CURLE_OK; 257 struct Curl_easy *data = conn->data; 258 unsigned int enabledmechs; 259 const char *mech = NULL; 260 char *resp = NULL; 261 size_t len = 0; 262 saslstate state1 = SASL_STOP; 263 saslstate state2 = SASL_FINAL; 264 const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : 265 conn->host.name; 266 const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; 267 #if defined(USE_KERBEROS5) || defined(USE_NTLM) 268 const char *service = data->set.str[STRING_SERVICE_NAME] ? 269 data->set.str[STRING_SERVICE_NAME] : 270 sasl->params->service; 271 #endif 272 273 sasl->force_ir = force_ir; /* Latch for future use */ 274 sasl->authused = 0; /* No mechanism used yet */ 275 enabledmechs = sasl->authmechs & sasl->prefmech; 276 *progress = SASL_IDLE; 277 278 /* Calculate the supported authentication mechanism, by decreasing order of 279 security, as well as the initial response where appropriate */ 280 if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) { 281 mech = SASL_MECH_STRING_EXTERNAL; 282 state1 = SASL_EXTERNAL; 283 sasl->authused = SASL_MECH_EXTERNAL; 284 285 if(force_ir || data->set.sasl_ir) 286 result = Curl_auth_create_external_message(data, conn->user, &resp, 287 &len); 288 } 289 else if(conn->bits.user_passwd) { 290 #if defined(USE_KERBEROS5) 291 if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() && 292 Curl_auth_user_contains_domain(conn->user)) { 293 sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */ 294 mech = SASL_MECH_STRING_GSSAPI; 295 state1 = SASL_GSSAPI; 296 state2 = SASL_GSSAPI_TOKEN; 297 sasl->authused = SASL_MECH_GSSAPI; 298 299 if(force_ir || data->set.sasl_ir) 300 result = Curl_auth_create_gssapi_user_message(data, conn->user, 301 conn->passwd, 302 service, 303 data->conn->host.name, 304 sasl->mutual_auth, 305 NULL, &conn->krb5, 306 &resp, &len); 307 } 308 else 309 #endif 310 #ifndef CURL_DISABLE_CRYPTO_AUTH 311 if((enabledmechs & SASL_MECH_DIGEST_MD5) && 312 Curl_auth_is_digest_supported()) { 313 mech = SASL_MECH_STRING_DIGEST_MD5; 314 state1 = SASL_DIGESTMD5; 315 sasl->authused = SASL_MECH_DIGEST_MD5; 316 } 317 else if(enabledmechs & SASL_MECH_CRAM_MD5) { 318 mech = SASL_MECH_STRING_CRAM_MD5; 319 state1 = SASL_CRAMMD5; 320 sasl->authused = SASL_MECH_CRAM_MD5; 321 } 322 else 323 #endif 324 #ifdef USE_NTLM 325 if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) { 326 mech = SASL_MECH_STRING_NTLM; 327 state1 = SASL_NTLM; 328 state2 = SASL_NTLM_TYPE2MSG; 329 sasl->authused = SASL_MECH_NTLM; 330 331 if(force_ir || data->set.sasl_ir) 332 result = Curl_auth_create_ntlm_type1_message(data, 333 conn->user, conn->passwd, 334 service, 335 hostname, 336 &conn->ntlm, &resp, 337 &len); 338 } 339 else 340 #endif 341 if((enabledmechs & SASL_MECH_OAUTHBEARER) && conn->oauth_bearer) { 342 mech = SASL_MECH_STRING_OAUTHBEARER; 343 state1 = SASL_OAUTH2; 344 state2 = SASL_OAUTH2_RESP; 345 sasl->authused = SASL_MECH_OAUTHBEARER; 346 347 if(force_ir || data->set.sasl_ir) 348 result = Curl_auth_create_oauth_bearer_message(data, conn->user, 349 hostname, 350 port, 351 conn->oauth_bearer, 352 &resp, &len); 353 } 354 else if((enabledmechs & SASL_MECH_XOAUTH2) && conn->oauth_bearer) { 355 mech = SASL_MECH_STRING_XOAUTH2; 356 state1 = SASL_OAUTH2; 357 sasl->authused = SASL_MECH_XOAUTH2; 358 359 if(force_ir || data->set.sasl_ir) 360 result = Curl_auth_create_oauth_bearer_message(data, conn->user, 361 NULL, 0, 362 conn->oauth_bearer, 363 &resp, &len); 364 } 365 else if(enabledmechs & SASL_MECH_PLAIN) { 366 mech = SASL_MECH_STRING_PLAIN; 367 state1 = SASL_PLAIN; 368 sasl->authused = SASL_MECH_PLAIN; 369 370 if(force_ir || data->set.sasl_ir) 371 result = Curl_auth_create_plain_message(data, conn->user, conn->passwd, 372 &resp, &len); 373 } 374 else if(enabledmechs & SASL_MECH_LOGIN) { 375 mech = SASL_MECH_STRING_LOGIN; 376 state1 = SASL_LOGIN; 377 state2 = SASL_LOGIN_PASSWD; 378 sasl->authused = SASL_MECH_LOGIN; 379 380 if(force_ir || data->set.sasl_ir) 381 result = Curl_auth_create_login_message(data, conn->user, &resp, &len); 382 } 383 } 384 385 if(!result && mech) { 386 if(resp && sasl->params->maxirlen && 387 strlen(mech) + len > sasl->params->maxirlen) { 388 free(resp); 389 resp = NULL; 390 } 391 392 result = sasl->params->sendauth(conn, mech, resp); 393 if(!result) { 394 *progress = SASL_INPROGRESS; 395 state(sasl, conn, resp ? state2 : state1); 396 } 397 } 398 399 free(resp); 400 401 return result; 402 } 403 404 /* 405 * Curl_sasl_continue() 406 * 407 * Continue the authentication. 408 */ 409 CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, 410 int code, saslprogress *progress) 411 { 412 CURLcode result = CURLE_OK; 413 struct Curl_easy *data = conn->data; 414 saslstate newstate = SASL_FINAL; 415 char *resp = NULL; 416 const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : 417 conn->host.name; 418 const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; 419 #if !defined(CURL_DISABLE_CRYPTO_AUTH) 420 char *chlg = NULL; 421 size_t chlglen = 0; 422 #endif 423 #if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \ 424 defined(USE_NTLM) 425 const char *service = data->set.str[STRING_SERVICE_NAME] ? 426 data->set.str[STRING_SERVICE_NAME] : 427 sasl->params->service; 428 char *serverdata; 429 #endif 430 size_t len = 0; 431 432 *progress = SASL_INPROGRESS; 433 434 if(sasl->state == SASL_FINAL) { 435 if(code != sasl->params->finalcode) 436 result = CURLE_LOGIN_DENIED; 437 *progress = SASL_DONE; 438 state(sasl, conn, SASL_STOP); 439 return result; 440 } 441 442 if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP && 443 code != sasl->params->contcode) { 444 *progress = SASL_DONE; 445 state(sasl, conn, SASL_STOP); 446 return CURLE_LOGIN_DENIED; 447 } 448 449 switch(sasl->state) { 450 case SASL_STOP: 451 *progress = SASL_DONE; 452 return result; 453 case SASL_PLAIN: 454 result = Curl_auth_create_plain_message(data, conn->user, conn->passwd, 455 &resp, 456 &len); 457 break; 458 case SASL_LOGIN: 459 result = Curl_auth_create_login_message(data, conn->user, &resp, &len); 460 newstate = SASL_LOGIN_PASSWD; 461 break; 462 case SASL_LOGIN_PASSWD: 463 result = Curl_auth_create_login_message(data, conn->passwd, &resp, &len); 464 break; 465 case SASL_EXTERNAL: 466 result = Curl_auth_create_external_message(data, conn->user, &resp, &len); 467 break; 468 469 #ifndef CURL_DISABLE_CRYPTO_AUTH 470 case SASL_CRAMMD5: 471 sasl->params->getmessage(data->state.buffer, &serverdata); 472 result = Curl_auth_decode_cram_md5_message(serverdata, &chlg, &chlglen); 473 if(!result) 474 result = Curl_auth_create_cram_md5_message(data, chlg, conn->user, 475 conn->passwd, &resp, &len); 476 free(chlg); 477 break; 478 case SASL_DIGESTMD5: 479 sasl->params->getmessage(data->state.buffer, &serverdata); 480 result = Curl_auth_create_digest_md5_message(data, serverdata, 481 conn->user, conn->passwd, 482 service, 483 &resp, &len); 484 newstate = SASL_DIGESTMD5_RESP; 485 break; 486 case SASL_DIGESTMD5_RESP: 487 resp = strdup(""); 488 if(!resp) 489 result = CURLE_OUT_OF_MEMORY; 490 break; 491 #endif 492 493 #ifdef USE_NTLM 494 case SASL_NTLM: 495 /* Create the type-1 message */ 496 result = Curl_auth_create_ntlm_type1_message(data, 497 conn->user, conn->passwd, 498 service, hostname, 499 &conn->ntlm, &resp, &len); 500 newstate = SASL_NTLM_TYPE2MSG; 501 break; 502 case SASL_NTLM_TYPE2MSG: 503 /* Decode the type-2 message */ 504 sasl->params->getmessage(data->state.buffer, &serverdata); 505 result = Curl_auth_decode_ntlm_type2_message(data, serverdata, 506 &conn->ntlm); 507 if(!result) 508 result = Curl_auth_create_ntlm_type3_message(data, conn->user, 509 conn->passwd, &conn->ntlm, 510 &resp, &len); 511 break; 512 #endif 513 514 #if defined(USE_KERBEROS5) 515 case SASL_GSSAPI: 516 result = Curl_auth_create_gssapi_user_message(data, conn->user, 517 conn->passwd, 518 service, 519 data->conn->host.name, 520 sasl->mutual_auth, NULL, 521 &conn->krb5, 522 &resp, &len); 523 newstate = SASL_GSSAPI_TOKEN; 524 break; 525 case SASL_GSSAPI_TOKEN: 526 sasl->params->getmessage(data->state.buffer, &serverdata); 527 if(sasl->mutual_auth) { 528 /* Decode the user token challenge and create the optional response 529 message */ 530 result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, 531 NULL, NULL, 532 sasl->mutual_auth, 533 serverdata, &conn->krb5, 534 &resp, &len); 535 newstate = SASL_GSSAPI_NO_DATA; 536 } 537 else 538 /* Decode the security challenge and create the response message */ 539 result = Curl_auth_create_gssapi_security_message(data, serverdata, 540 &conn->krb5, 541 &resp, &len); 542 break; 543 case SASL_GSSAPI_NO_DATA: 544 sasl->params->getmessage(data->state.buffer, &serverdata); 545 /* Decode the security challenge and create the response message */ 546 result = Curl_auth_create_gssapi_security_message(data, serverdata, 547 &conn->krb5, 548 &resp, &len); 549 break; 550 #endif 551 552 case SASL_OAUTH2: 553 /* Create the authorisation message */ 554 if(sasl->authused == SASL_MECH_OAUTHBEARER) { 555 result = Curl_auth_create_oauth_bearer_message(data, conn->user, 556 hostname, 557 port, 558 conn->oauth_bearer, 559 &resp, &len); 560 561 /* Failures maybe sent by the server as continuations for OAUTHBEARER */ 562 newstate = SASL_OAUTH2_RESP; 563 } 564 else 565 result = Curl_auth_create_oauth_bearer_message(data, conn->user, 566 NULL, 0, 567 conn->oauth_bearer, 568 &resp, &len); 569 break; 570 571 case SASL_OAUTH2_RESP: 572 /* The continuation is optional so check the response code */ 573 if(code == sasl->params->finalcode) { 574 /* Final response was received so we are done */ 575 *progress = SASL_DONE; 576 state(sasl, conn, SASL_STOP); 577 return result; 578 } 579 else if(code == sasl->params->contcode) { 580 /* Acknowledge the continuation by sending a 0x01 response base64 581 encoded */ 582 resp = strdup("AQ=="); 583 if(!resp) 584 result = CURLE_OUT_OF_MEMORY; 585 break; 586 } 587 else { 588 *progress = SASL_DONE; 589 state(sasl, conn, SASL_STOP); 590 return CURLE_LOGIN_DENIED; 591 } 592 593 case SASL_CANCEL: 594 /* Remove the offending mechanism from the supported list */ 595 sasl->authmechs ^= sasl->authused; 596 597 /* Start an alternative SASL authentication */ 598 result = Curl_sasl_start(sasl, conn, sasl->force_ir, progress); 599 newstate = sasl->state; /* Use state from Curl_sasl_start() */ 600 break; 601 default: 602 failf(data, "Unsupported SASL authentication mechanism"); 603 result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */ 604 break; 605 } 606 607 switch(result) { 608 case CURLE_BAD_CONTENT_ENCODING: 609 /* Cancel dialog */ 610 result = sasl->params->sendcont(conn, "*"); 611 newstate = SASL_CANCEL; 612 break; 613 case CURLE_OK: 614 if(resp) 615 result = sasl->params->sendcont(conn, resp); 616 break; 617 default: 618 newstate = SASL_STOP; /* Stop on error */ 619 *progress = SASL_DONE; 620 break; 621 } 622 623 free(resp); 624 625 state(sasl, conn, newstate); 626 627 return result; 628 } 629