1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2012 - 2016, 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 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(conn->user, conn->passwd, 335 &conn->ntlm, &resp, &len); 336 } 337 else 338 #endif 339 if((enabledmechs & SASL_MECH_OAUTHBEARER) && conn->oauth_bearer) { 340 mech = SASL_MECH_STRING_OAUTHBEARER; 341 state1 = SASL_OAUTH2; 342 state2 = SASL_OAUTH2_RESP; 343 sasl->authused = SASL_MECH_OAUTHBEARER; 344 345 if(force_ir || data->set.sasl_ir) 346 result = Curl_auth_create_oauth_bearer_message(data, conn->user, 347 hostname, 348 port, 349 conn->oauth_bearer, 350 &resp, &len); 351 } 352 else if((enabledmechs & SASL_MECH_XOAUTH2) && conn->oauth_bearer) { 353 mech = SASL_MECH_STRING_XOAUTH2; 354 state1 = SASL_OAUTH2; 355 sasl->authused = SASL_MECH_XOAUTH2; 356 357 if(force_ir || data->set.sasl_ir) 358 result = Curl_auth_create_oauth_bearer_message(data, conn->user, 359 NULL, 0, 360 conn->oauth_bearer, 361 &resp, &len); 362 } 363 else if(enabledmechs & SASL_MECH_LOGIN) { 364 mech = SASL_MECH_STRING_LOGIN; 365 state1 = SASL_LOGIN; 366 state2 = SASL_LOGIN_PASSWD; 367 sasl->authused = SASL_MECH_LOGIN; 368 369 if(force_ir || data->set.sasl_ir) 370 result = Curl_auth_create_login_message(data, conn->user, &resp, &len); 371 } 372 else if(enabledmechs & SASL_MECH_PLAIN) { 373 mech = SASL_MECH_STRING_PLAIN; 374 state1 = SASL_PLAIN; 375 sasl->authused = SASL_MECH_PLAIN; 376 377 if(force_ir || data->set.sasl_ir) 378 result = Curl_auth_create_plain_message(data, conn->user, conn->passwd, 379 &resp, &len); 380 } 381 } 382 383 if(!result && mech) { 384 if(resp && sasl->params->maxirlen && 385 strlen(mech) + len > sasl->params->maxirlen) { 386 free(resp); 387 resp = NULL; 388 } 389 390 result = sasl->params->sendauth(conn, mech, resp); 391 if(!result) { 392 *progress = SASL_INPROGRESS; 393 state(sasl, conn, resp ? state2 : state1); 394 } 395 } 396 397 free(resp); 398 399 return result; 400 } 401 402 /* 403 * Curl_sasl_continue() 404 * 405 * Continue the authentication. 406 */ 407 CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, 408 int code, saslprogress *progress) 409 { 410 CURLcode result = CURLE_OK; 411 struct Curl_easy *data = conn->data; 412 saslstate newstate = SASL_FINAL; 413 char *resp = NULL; 414 const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : 415 conn->host.name; 416 const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; 417 #if !defined(CURL_DISABLE_CRYPTO_AUTH) 418 char *serverdata; 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 size_t len = 0; 428 429 *progress = SASL_INPROGRESS; 430 431 if(sasl->state == SASL_FINAL) { 432 if(code != sasl->params->finalcode) 433 result = CURLE_LOGIN_DENIED; 434 *progress = SASL_DONE; 435 state(sasl, conn, SASL_STOP); 436 return result; 437 } 438 439 if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP && 440 code != sasl->params->contcode) { 441 *progress = SASL_DONE; 442 state(sasl, conn, SASL_STOP); 443 return CURLE_LOGIN_DENIED; 444 } 445 446 switch(sasl->state) { 447 case SASL_STOP: 448 *progress = SASL_DONE; 449 return result; 450 case SASL_PLAIN: 451 result = Curl_auth_create_plain_message(data, conn->user, conn->passwd, 452 &resp, 453 &len); 454 break; 455 case SASL_LOGIN: 456 result = Curl_auth_create_login_message(data, conn->user, &resp, &len); 457 newstate = SASL_LOGIN_PASSWD; 458 break; 459 case SASL_LOGIN_PASSWD: 460 result = Curl_auth_create_login_message(data, conn->passwd, &resp, &len); 461 break; 462 case SASL_EXTERNAL: 463 result = Curl_auth_create_external_message(data, conn->user, &resp, &len); 464 break; 465 466 #ifndef CURL_DISABLE_CRYPTO_AUTH 467 case SASL_CRAMMD5: 468 sasl->params->getmessage(data->state.buffer, &serverdata); 469 result = Curl_auth_decode_cram_md5_message(serverdata, &chlg, &chlglen); 470 if(!result) 471 result = Curl_auth_create_cram_md5_message(data, chlg, conn->user, 472 conn->passwd, &resp, &len); 473 free(chlg); 474 break; 475 case SASL_DIGESTMD5: 476 sasl->params->getmessage(data->state.buffer, &serverdata); 477 result = Curl_auth_create_digest_md5_message(data, serverdata, 478 conn->user, conn->passwd, 479 service, 480 &resp, &len); 481 newstate = SASL_DIGESTMD5_RESP; 482 break; 483 case SASL_DIGESTMD5_RESP: 484 resp = strdup(""); 485 if(!resp) 486 result = CURLE_OUT_OF_MEMORY; 487 break; 488 #endif 489 490 #ifdef USE_NTLM 491 case SASL_NTLM: 492 /* Create the type-1 message */ 493 result = Curl_auth_create_ntlm_type1_message(conn->user, conn->passwd, 494 &conn->ntlm, &resp, &len); 495 newstate = SASL_NTLM_TYPE2MSG; 496 break; 497 case SASL_NTLM_TYPE2MSG: 498 /* Decode the type-2 message */ 499 sasl->params->getmessage(data->state.buffer, &serverdata); 500 result = Curl_auth_decode_ntlm_type2_message(data, serverdata, 501 &conn->ntlm); 502 if(!result) 503 result = Curl_auth_create_ntlm_type3_message(data, conn->user, 504 conn->passwd, &conn->ntlm, 505 &resp, &len); 506 break; 507 #endif 508 509 #if defined(USE_KERBEROS5) 510 case SASL_GSSAPI: 511 result = Curl_auth_create_gssapi_user_message(data, conn->user, 512 conn->passwd, 513 service, 514 data->easy_conn->host.name, 515 sasl->mutual_auth, NULL, 516 &conn->krb5, 517 &resp, &len); 518 newstate = SASL_GSSAPI_TOKEN; 519 break; 520 case SASL_GSSAPI_TOKEN: 521 sasl->params->getmessage(data->state.buffer, &serverdata); 522 if(sasl->mutual_auth) { 523 /* Decode the user token challenge and create the optional response 524 message */ 525 result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, 526 NULL, NULL, 527 sasl->mutual_auth, 528 serverdata, &conn->krb5, 529 &resp, &len); 530 newstate = SASL_GSSAPI_NO_DATA; 531 } 532 else 533 /* Decode the security challenge and create the response message */ 534 result = Curl_auth_create_gssapi_security_message(data, serverdata, 535 &conn->krb5, 536 &resp, &len); 537 break; 538 case SASL_GSSAPI_NO_DATA: 539 sasl->params->getmessage(data->state.buffer, &serverdata); 540 /* Decode the security challenge and create the response message */ 541 result = Curl_auth_create_gssapi_security_message(data, serverdata, 542 &conn->krb5, 543 &resp, &len); 544 break; 545 #endif 546 547 case SASL_OAUTH2: 548 /* Create the authorisation message */ 549 if(sasl->authused == SASL_MECH_OAUTHBEARER) { 550 result = Curl_auth_create_oauth_bearer_message(data, conn->user, 551 hostname, 552 port, 553 conn->oauth_bearer, 554 &resp, &len); 555 556 /* Failures maybe sent by the server as continuations for OAUTHBEARER */ 557 newstate = SASL_OAUTH2_RESP; 558 } 559 else 560 result = Curl_auth_create_oauth_bearer_message(data, conn->user, 561 NULL, 0, 562 conn->oauth_bearer, 563 &resp, &len); 564 break; 565 566 case SASL_OAUTH2_RESP: 567 /* The continuation is optional so check the response code */ 568 if(code == sasl->params->finalcode) { 569 /* Final response was received so we are done */ 570 *progress = SASL_DONE; 571 state(sasl, conn, SASL_STOP); 572 return result; 573 } 574 else if(code == sasl->params->contcode) { 575 /* Acknowledge the continuation by sending a 0x01 response base64 576 encoded */ 577 resp = strdup("AQ=="); 578 if(!resp) 579 result = CURLE_OUT_OF_MEMORY; 580 break; 581 } 582 else { 583 *progress = SASL_DONE; 584 state(sasl, conn, SASL_STOP); 585 return CURLE_LOGIN_DENIED; 586 } 587 588 case SASL_CANCEL: 589 /* Remove the offending mechanism from the supported list */ 590 sasl->authmechs ^= sasl->authused; 591 592 /* Start an alternative SASL authentication */ 593 result = Curl_sasl_start(sasl, conn, sasl->force_ir, progress); 594 newstate = sasl->state; /* Use state from Curl_sasl_start() */ 595 break; 596 default: 597 failf(data, "Unsupported SASL authentication mechanism"); 598 result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */ 599 break; 600 } 601 602 switch(result) { 603 case CURLE_BAD_CONTENT_ENCODING: 604 /* Cancel dialog */ 605 result = sasl->params->sendcont(conn, "*"); 606 newstate = SASL_CANCEL; 607 break; 608 case CURLE_OK: 609 if(resp) 610 result = sasl->params->sendcont(conn, resp); 611 break; 612 default: 613 newstate = SASL_STOP; /* Stop on error */ 614 *progress = SASL_DONE; 615 break; 616 } 617 618 free(resp); 619 620 state(sasl, conn, newstate); 621 622 return result; 623 } 624