1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2012 - 2017, 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 unsigned int mechbit; 150 size_t mechlen; 151 152 if(!len) 153 return CURLE_URL_MALFORMAT; 154 155 if(sasl->resetprefs) { 156 sasl->resetprefs = FALSE; 157 sasl->prefmech = SASL_AUTH_NONE; 158 } 159 160 if(!strncmp(value, "*", len)) 161 sasl->prefmech = SASL_AUTH_DEFAULT; 162 else { 163 mechbit = Curl_sasl_decode_mech(value, len, &mechlen); 164 if(mechbit && mechlen == len) 165 sasl->prefmech |= mechbit; 166 else 167 result = CURLE_URL_MALFORMAT; 168 } 169 170 return result; 171 } 172 173 /* 174 * Curl_sasl_init() 175 * 176 * Initializes the SASL structure. 177 */ 178 void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params) 179 { 180 sasl->params = params; /* Set protocol dependent parameters */ 181 sasl->state = SASL_STOP; /* Not yet running */ 182 sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */ 183 sasl->prefmech = SASL_AUTH_DEFAULT; /* Prefer all mechanisms */ 184 sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */ 185 sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */ 186 sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */ 187 sasl->force_ir = FALSE; /* Respect external option */ 188 } 189 190 /* 191 * state() 192 * 193 * This is the ONLY way to change SASL state! 194 */ 195 static void state(struct SASL *sasl, struct connectdata *conn, 196 saslstate newstate) 197 { 198 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 199 /* for debug purposes */ 200 static const char * const names[]={ 201 "STOP", 202 "PLAIN", 203 "LOGIN", 204 "LOGIN_PASSWD", 205 "EXTERNAL", 206 "CRAMMD5", 207 "DIGESTMD5", 208 "DIGESTMD5_RESP", 209 "NTLM", 210 "NTLM_TYPE2MSG", 211 "GSSAPI", 212 "GSSAPI_TOKEN", 213 "GSSAPI_NO_DATA", 214 "OAUTH2", 215 "OAUTH2_RESP", 216 "CANCEL", 217 "FINAL", 218 /* LAST */ 219 }; 220 221 if(sasl->state != newstate) 222 infof(conn->data, "SASL %p state change from %s to %s\n", 223 (void *)sasl, names[sasl->state], names[newstate]); 224 #else 225 (void) conn; 226 #endif 227 228 sasl->state = newstate; 229 } 230 231 /* 232 * Curl_sasl_can_authenticate() 233 * 234 * Check if we have enough auth data and capabilities to authenticate. 235 */ 236 bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn) 237 { 238 /* Have credentials been provided? */ 239 if(conn->bits.user_passwd) 240 return TRUE; 241 242 /* EXTERNAL can authenticate without a user name and/or password */ 243 if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL) 244 return TRUE; 245 246 return FALSE; 247 } 248 249 /* 250 * Curl_sasl_start() 251 * 252 * Calculate the required login details for SASL authentication. 253 */ 254 CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, 255 bool force_ir, saslprogress *progress) 256 { 257 CURLcode result = CURLE_OK; 258 struct Curl_easy *data = conn->data; 259 unsigned int enabledmechs; 260 const char *mech = NULL; 261 char *resp = NULL; 262 size_t len = 0; 263 saslstate state1 = SASL_STOP; 264 saslstate state2 = SASL_FINAL; 265 const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : 266 conn->host.name; 267 const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; 268 #if defined(USE_KERBEROS5) 269 const char *service = data->set.str[STRING_SERVICE_NAME] ? 270 data->set.str[STRING_SERVICE_NAME] : 271 sasl->params->service; 272 #endif 273 274 sasl->force_ir = force_ir; /* Latch for future use */ 275 sasl->authused = 0; /* No mechanism used yet */ 276 enabledmechs = sasl->authmechs & sasl->prefmech; 277 *progress = SASL_IDLE; 278 279 /* Calculate the supported authentication mechanism, by decreasing order of 280 security, as well as the initial response where appropriate */ 281 if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) { 282 mech = SASL_MECH_STRING_EXTERNAL; 283 state1 = SASL_EXTERNAL; 284 sasl->authused = SASL_MECH_EXTERNAL; 285 286 if(force_ir || data->set.sasl_ir) 287 result = Curl_auth_create_external_message(data, conn->user, &resp, 288 &len); 289 } 290 else if(conn->bits.user_passwd) { 291 #if defined(USE_KERBEROS5) 292 if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() && 293 Curl_auth_user_contains_domain(conn->user)) { 294 sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */ 295 mech = SASL_MECH_STRING_GSSAPI; 296 state1 = SASL_GSSAPI; 297 state2 = SASL_GSSAPI_TOKEN; 298 sasl->authused = SASL_MECH_GSSAPI; 299 300 if(force_ir || data->set.sasl_ir) 301 result = Curl_auth_create_gssapi_user_message(data, conn->user, 302 conn->passwd, 303 service, 304 data->easy_conn-> 305 host.name, 306 sasl->mutual_auth, 307 NULL, &conn->krb5, 308 &resp, &len); 309 } 310 else 311 #endif 312 #ifndef CURL_DISABLE_CRYPTO_AUTH 313 if((enabledmechs & SASL_MECH_DIGEST_MD5) && 314 Curl_auth_is_digest_supported()) { 315 mech = SASL_MECH_STRING_DIGEST_MD5; 316 state1 = SASL_DIGESTMD5; 317 sasl->authused = SASL_MECH_DIGEST_MD5; 318 } 319 else if(enabledmechs & SASL_MECH_CRAM_MD5) { 320 mech = SASL_MECH_STRING_CRAM_MD5; 321 state1 = SASL_CRAMMD5; 322 sasl->authused = SASL_MECH_CRAM_MD5; 323 } 324 else 325 #endif 326 #ifdef USE_NTLM 327 if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) { 328 mech = SASL_MECH_STRING_NTLM; 329 state1 = SASL_NTLM; 330 state2 = SASL_NTLM_TYPE2MSG; 331 sasl->authused = SASL_MECH_NTLM; 332 333 if(force_ir || data->set.sasl_ir) 334 result = Curl_auth_create_ntlm_type1_message(data, 335 conn->user, conn->passwd, 336 &conn->ntlm, &resp, &len); 337 } 338 else 339 #endif 340 if((enabledmechs & SASL_MECH_OAUTHBEARER) && conn->oauth_bearer) { 341 mech = SASL_MECH_STRING_OAUTHBEARER; 342 state1 = SASL_OAUTH2; 343 state2 = SASL_OAUTH2_RESP; 344 sasl->authused = SASL_MECH_OAUTHBEARER; 345 346 if(force_ir || data->set.sasl_ir) 347 result = Curl_auth_create_oauth_bearer_message(data, conn->user, 348 hostname, 349 port, 350 conn->oauth_bearer, 351 &resp, &len); 352 } 353 else if((enabledmechs & SASL_MECH_XOAUTH2) && conn->oauth_bearer) { 354 mech = SASL_MECH_STRING_XOAUTH2; 355 state1 = SASL_OAUTH2; 356 sasl->authused = SASL_MECH_XOAUTH2; 357 358 if(force_ir || data->set.sasl_ir) 359 result = Curl_auth_create_oauth_bearer_message(data, conn->user, 360 NULL, 0, 361 conn->oauth_bearer, 362 &resp, &len); 363 } 364 else if(enabledmechs & SASL_MECH_LOGIN) { 365 mech = SASL_MECH_STRING_LOGIN; 366 state1 = SASL_LOGIN; 367 state2 = SASL_LOGIN_PASSWD; 368 sasl->authused = SASL_MECH_LOGIN; 369 370 if(force_ir || data->set.sasl_ir) 371 result = Curl_auth_create_login_message(data, conn->user, &resp, &len); 372 } 373 else if(enabledmechs & SASL_MECH_PLAIN) { 374 mech = SASL_MECH_STRING_PLAIN; 375 state1 = SASL_PLAIN; 376 sasl->authused = SASL_MECH_PLAIN; 377 378 if(force_ir || data->set.sasl_ir) 379 result = Curl_auth_create_plain_message(data, conn->user, conn->passwd, 380 &resp, &len); 381 } 382 } 383 384 if(!result && mech) { 385 if(resp && sasl->params->maxirlen && 386 strlen(mech) + len > sasl->params->maxirlen) { 387 free(resp); 388 resp = NULL; 389 } 390 391 result = sasl->params->sendauth(conn, mech, resp); 392 if(!result) { 393 *progress = SASL_INPROGRESS; 394 state(sasl, conn, resp ? state2 : state1); 395 } 396 } 397 398 free(resp); 399 400 return result; 401 } 402 403 /* 404 * Curl_sasl_continue() 405 * 406 * Continue the authentication. 407 */ 408 CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, 409 int code, saslprogress *progress) 410 { 411 CURLcode result = CURLE_OK; 412 struct Curl_easy *data = conn->data; 413 saslstate newstate = SASL_FINAL; 414 char *resp = NULL; 415 const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : 416 conn->host.name; 417 const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; 418 #if !defined(CURL_DISABLE_CRYPTO_AUTH) 419 char *chlg = NULL; 420 size_t chlglen = 0; 421 #endif 422 #if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) 423 const char *service = data->set.str[STRING_SERVICE_NAME] ? 424 data->set.str[STRING_SERVICE_NAME] : 425 sasl->params->service; 426 #endif 427 #if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \ 428 defined(USE_NTLM) 429 char *serverdata; 430 #endif 431 size_t len = 0; 432 433 *progress = SASL_INPROGRESS; 434 435 if(sasl->state == SASL_FINAL) { 436 if(code != sasl->params->finalcode) 437 result = CURLE_LOGIN_DENIED; 438 *progress = SASL_DONE; 439 state(sasl, conn, SASL_STOP); 440 return result; 441 } 442 443 if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP && 444 code != sasl->params->contcode) { 445 *progress = SASL_DONE; 446 state(sasl, conn, SASL_STOP); 447 return CURLE_LOGIN_DENIED; 448 } 449 450 switch(sasl->state) { 451 case SASL_STOP: 452 *progress = SASL_DONE; 453 return result; 454 case SASL_PLAIN: 455 result = Curl_auth_create_plain_message(data, conn->user, conn->passwd, 456 &resp, 457 &len); 458 break; 459 case SASL_LOGIN: 460 result = Curl_auth_create_login_message(data, conn->user, &resp, &len); 461 newstate = SASL_LOGIN_PASSWD; 462 break; 463 case SASL_LOGIN_PASSWD: 464 result = Curl_auth_create_login_message(data, conn->passwd, &resp, &len); 465 break; 466 case SASL_EXTERNAL: 467 result = Curl_auth_create_external_message(data, conn->user, &resp, &len); 468 break; 469 470 #ifndef CURL_DISABLE_CRYPTO_AUTH 471 case SASL_CRAMMD5: 472 sasl->params->getmessage(data->state.buffer, &serverdata); 473 result = Curl_auth_decode_cram_md5_message(serverdata, &chlg, &chlglen); 474 if(!result) 475 result = Curl_auth_create_cram_md5_message(data, chlg, conn->user, 476 conn->passwd, &resp, &len); 477 free(chlg); 478 break; 479 case SASL_DIGESTMD5: 480 sasl->params->getmessage(data->state.buffer, &serverdata); 481 result = Curl_auth_create_digest_md5_message(data, serverdata, 482 conn->user, conn->passwd, 483 service, 484 &resp, &len); 485 newstate = SASL_DIGESTMD5_RESP; 486 break; 487 case SASL_DIGESTMD5_RESP: 488 resp = strdup(""); 489 if(!resp) 490 result = CURLE_OUT_OF_MEMORY; 491 break; 492 #endif 493 494 #ifdef USE_NTLM 495 case SASL_NTLM: 496 /* Create the type-1 message */ 497 result = Curl_auth_create_ntlm_type1_message(data, 498 conn->user, conn->passwd, 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->easy_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