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