1/* 2 * Copyright (c) 2010, JANET(UK) 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * 3. Neither the name of JANET(UK) nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32/* 33 * Copyright (c) 1998-2003 Carnegie Mellon University. 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 40 * 1. Redistributions of source code must retain the above copyright 41 * notice, this list of conditions and the following disclaimer. 42 * 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in 45 * the documentation and/or other materials provided with the 46 * distribution. 47 * 48 * 3. The name "Carnegie Mellon University" must not be used to 49 * endorse or promote products derived from this software without 50 * prior written permission. For permission or any other legal 51 * details, please contact 52 * Office of Technology Transfer 53 * Carnegie Mellon University 54 * 5000 Forbes Avenue 55 * Pittsburgh, PA 15213-3890 56 * (412) 268-4387, fax: (412) 268-7395 57 * tech-transfer@andrew.cmu.edu 58 * 59 * 4. Redistributions of any form whatsoever must retain the following 60 * acknowledgment: 61 * "This product includes software developed by Computing Services 62 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 63 * 64 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 65 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 66 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 67 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 68 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 69 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 70 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 71 */ 72 73#include <config.h> 74#include <GSS/gssapi.h> 75#include <GSS/gssapi_spi.h> 76#include <GSS/gssapi_private.h> // for gss_inquire_saslname_for_mech, gss_indicate_mechs_by_attrs, gss_inquire_mech_for_saslname 77 78#ifndef KRB5_HEIMDAL 79#ifdef HAVE_GSSAPI_GSSAPI_EXT_H 80#include <gssapi/gssapi_ext.h> 81#endif 82#endif 83 84#include <fcntl.h> 85#include <stdio.h> 86#include <sasl.h> 87#include <saslutil.h> 88#include <saslplug.h> 89 90#include "plugin_common.h" 91 92#ifdef HAVE_UNISTD_H 93#include <unistd.h> 94#endif 95 96#include <errno.h> 97#include <assert.h> 98#include "gs2_token.h" 99 100#define GS2_CB_FLAG_MASK 0x0F 101#define GS2_CB_FLAG_N 0x00 102#define GS2_CB_FLAG_P 0x01 103#define GS2_CB_FLAG_Y 0x02 104#define GS2_NONSTD_FLAG 0x10 105 106typedef struct context { 107 gss_ctx_id_t gss_ctx; 108 gss_name_t client_name; 109 gss_name_t server_name; 110 gss_cred_id_t server_creds; 111 gss_cred_id_t client_creds; 112 char *out_buf; 113 unsigned out_buf_len; 114 const sasl_utils_t *utils; 115 char *authid; 116 char *authzid; 117 union { 118 sasl_client_plug_t *client; 119 sasl_server_plug_t *server; 120 } plug; 121 gss_OID mechanism; 122 int gs2_flags; 123 char *cbindingname; 124 struct gss_channel_bindings_struct gss_cbindings; 125 sasl_secret_t *password; 126 unsigned int free_password; 127 OM_uint32 lifetime; 128} context_t; 129 130static gss_OID_set gs2_mechs = GSS_C_NO_OID_SET; 131 132static int gs2_get_init_creds(context_t *context, 133 sasl_client_params_t *params, 134 sasl_interact_t **prompt_need, 135 sasl_out_params_t *oparams); 136 137static int gs2_verify_initial_message(context_t *text, 138 sasl_server_params_t *sparams, 139 const char *in, 140 unsigned inlen, 141 gss_buffer_t token); 142 143static int gs2_make_header(context_t *text, 144 sasl_client_params_t *cparams, 145 const char *authzid, 146 char **out, 147 unsigned *outlen); 148 149static int gs2_make_message(context_t *text, 150 sasl_client_params_t *cparams, 151 int initialContextToken, 152 gss_buffer_t token, 153 char **out, 154 unsigned *outlen); 155 156static int gs2_get_mech_attrs(const sasl_utils_t *utils, 157 const gss_OID mech, 158 unsigned int *security_flags, 159 unsigned int *features, 160 const unsigned long **prompts); 161 162static int gs2_indicate_mechs(const sasl_utils_t *utils); 163 164static int gs2_map_sasl_name(const sasl_utils_t *utils, 165 const char *mech, 166 gss_OID *oid); 167 168static int gs2_duplicate_buffer(const sasl_utils_t *utils, 169 const gss_buffer_t src, 170 gss_buffer_t dst); 171 172static int gs2_unescape_authzid(const sasl_utils_t *utils, 173 char **in, 174 unsigned *inlen, 175 char **authzid); 176 177static int gs2_escape_authzid(const sasl_utils_t *utils, 178 const char *in, 179 unsigned inlen, 180 char **authzid); 181 182/* sasl_gs_log: only logs status string returned from gss_display_status() */ 183#define sasl_gs2_log(x,y,z) sasl_gs2_seterror_(x,y,z,1) 184#define sasl_gs2_seterror(x,y,z) sasl_gs2_seterror_(x,y,z,0) 185 186static int 187sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min, 188 int logonly); 189 190static context_t * 191sasl_gs2_new_context(const sasl_utils_t *utils) 192{ 193 context_t *ret; 194 195 ret = utils->malloc(sizeof(context_t)); 196 if (ret == NULL) 197 return NULL; 198 199 memset(ret, 0, sizeof(context_t)); 200 ret->utils = utils; 201 202 return ret; 203} 204 205static int 206sasl_gs2_free_context_contents(context_t *text) 207{ 208 OM_uint32 min_stat; 209 210 if (text == NULL) 211 return SASL_OK; 212 213 if (text->gss_ctx != GSS_C_NO_CONTEXT) { 214 gss_delete_sec_context(&min_stat,&text->gss_ctx, 215 GSS_C_NO_BUFFER); 216 text->gss_ctx = GSS_C_NO_CONTEXT; 217 } 218 219 if (text->client_name != GSS_C_NO_NAME) { 220 gss_release_name(&min_stat,&text->client_name); 221 text->client_name = GSS_C_NO_NAME; 222 } 223 224 if (text->server_name != GSS_C_NO_NAME) { 225 gss_release_name(&min_stat,&text->server_name); 226 text->server_name = GSS_C_NO_NAME; 227 } 228 229 if (text->server_creds != GSS_C_NO_CREDENTIAL) { 230 gss_release_cred(&min_stat, &text->server_creds); 231 text->server_creds = GSS_C_NO_CREDENTIAL; 232 } 233 234 if (text->client_creds != GSS_C_NO_CREDENTIAL) { 235 gss_release_cred(&min_stat, &text->client_creds); 236 text->client_creds = GSS_C_NO_CREDENTIAL; 237 } 238 239 if (text->authid != NULL) { 240 text->utils->free(text->authid); 241 text->authid = NULL; 242 } 243 244 if (text->authzid != NULL) { 245 text->utils->free(text->authzid); 246 text->authzid = NULL; 247 } 248 249 gss_release_buffer(&min_stat, &text->gss_cbindings.application_data); 250 251 if (text->out_buf != NULL) { 252 text->utils->free(text->out_buf); 253 text->out_buf = NULL; 254 } 255 256 text->out_buf_len = 0; 257 258 if (text->cbindingname != NULL) { 259 text->utils->free(text->cbindingname); 260 text->cbindingname = NULL; 261 } 262 263 if (text->free_password) 264 _plug_free_secret(text->utils, &text->password); 265 266 memset(text, 0, sizeof(*text)); 267 268 return SASL_OK; 269} 270 271static void 272gs2_common_mech_dispose(void *conn_context, const sasl_utils_t *utils) 273{ 274 sasl_gs2_free_context_contents((context_t *)(conn_context)); 275 utils->free(conn_context); 276} 277 278static void 279gs2_common_mech_free(void *global_context __attribute__((unused)), 280 const sasl_utils_t *utils) 281{ 282 OM_uint32 minor; 283 284 if (gs2_mechs != GSS_C_NO_OID_SET) { 285 gss_release_oid_set(&minor, &gs2_mechs); 286 gs2_mechs = GSS_C_NO_OID_SET; 287 } 288} 289 290/***************************** Server Section *****************************/ 291 292static int 293gs2_server_mech_new(void *glob_context, 294 sasl_server_params_t *params, 295 const char *challenge __attribute__((unused)), 296 unsigned challen __attribute__((unused)), 297 void **conn_context) 298{ 299 context_t *text; 300 int ret; 301 302 text = sasl_gs2_new_context(params->utils); 303 if (text == NULL) { 304 MEMERROR(params->utils); 305 return SASL_NOMEM; 306 } 307 308 text->gss_ctx = GSS_C_NO_CONTEXT; 309 text->client_name = GSS_C_NO_NAME; 310 text->server_name = GSS_C_NO_NAME; 311 text->server_creds = GSS_C_NO_CREDENTIAL; 312 text->client_creds = GSS_C_NO_CREDENTIAL; 313 text->plug.server = glob_context; 314 315 ret = gs2_map_sasl_name(params->utils, text->plug.server->mech_name, 316 &text->mechanism); 317 if (ret != SASL_OK) { 318 gs2_common_mech_dispose(text, params->utils); 319 return ret; 320 } 321 322 *conn_context = text; 323 324 return SASL_OK; 325} 326 327static int 328gs2_server_mech_step(void *conn_context, 329 sasl_server_params_t *params, 330 const char *clientin, 331 unsigned clientinlen, 332 const char **serverout, 333 unsigned *serveroutlen, 334 sasl_out_params_t *oparams) 335{ 336 context_t *text = (context_t *)conn_context; 337 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; 338 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; 339 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0; 340 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER; 341 gss_buffer_desc short_name_buf = GSS_C_EMPTY_BUFFER; 342 gss_name_t without = GSS_C_NO_NAME; 343 gss_OID_set_desc mechs; 344 OM_uint32 out_flags = 0; 345 int ret = SASL_OK, equal = 0; 346 int initialContextToken = (text->gss_ctx == GSS_C_NO_CONTEXT); 347 char *p; 348 349 if (serverout == NULL) { 350 PARAMERROR(text->utils); 351 return SASL_BADPARAM; 352 } 353 354 *serverout = NULL; 355 *serveroutlen = 0; 356 357 if (initialContextToken) { 358 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN); 359 name_buf.value = params->utils->malloc(name_buf.length + 1); 360 if (name_buf.value == NULL) { 361 MEMERROR(text->utils); 362 ret = SASL_NOMEM; 363 goto cleanup; 364 } 365 snprintf(name_buf.value, name_buf.length + 1, 366 "%s@%s", params->service, params->serverFQDN); 367 maj_stat = gss_import_name(&min_stat, 368 &name_buf, 369 GSS_C_NT_HOSTBASED_SERVICE, 370 &text->server_name); 371 params->utils->free(name_buf.value); 372 name_buf.value = NULL; 373 374 if (GSS_ERROR(maj_stat)) 375 goto cleanup; 376 377 assert(text->server_creds == GSS_C_NO_CREDENTIAL); 378 379 mechs.count = 1; 380 mechs.elements = (gss_OID)text->mechanism; 381 382 if (params->gss_creds == GSS_C_NO_CREDENTIAL) { 383 maj_stat = gss_acquire_cred(&min_stat, 384 text->server_name, 385 GSS_C_INDEFINITE, 386 &mechs, 387 GSS_C_ACCEPT, 388 &text->server_creds, 389 NULL, 390 &text->lifetime); 391 if (GSS_ERROR(maj_stat)) 392 goto cleanup; 393 } 394 395 ret = gs2_verify_initial_message(text, 396 params, 397 clientin, 398 clientinlen, 399 &input_token); 400 if (ret != SASL_OK) 401 goto cleanup; 402 } else { 403 input_token.value = (void *)clientin; 404 input_token.length = clientinlen; 405 } 406 407 maj_stat = gss_accept_sec_context(&min_stat, 408 &text->gss_ctx, 409 (params->gss_creds != GSS_C_NO_CREDENTIAL) 410 ? (gss_cred_id_t)params->gss_creds 411 : text->server_creds, 412 &input_token, 413 &text->gss_cbindings, 414 &text->client_name, 415 NULL, 416 &output_token, 417 &out_flags, 418 &text->lifetime, 419 &text->client_creds); 420 if (GSS_ERROR(maj_stat)) { 421 sasl_gs2_log(text->utils, maj_stat, min_stat); 422 text->utils->seterror(text->utils->conn, SASL_NOLOG, 423 "GS2 Failure: gss_accept_sec_context"); 424 ret = (maj_stat == GSS_S_BAD_BINDINGS) ? SASL_BADBINDING : SASL_BADAUTH; 425 goto cleanup; 426 } 427 428 *serveroutlen = output_token.length; 429 if (output_token.value != NULL) { 430 ret = _plug_buf_alloc(text->utils, &text->out_buf, 431 &text->out_buf_len, *serveroutlen); 432 if (ret != SASL_OK) 433 goto cleanup; 434 memcpy(text->out_buf, output_token.value, *serveroutlen); 435 *serverout = text->out_buf; 436 } else { 437 /* No output token, send an empty string */ 438 *serverout = ""; 439 serveroutlen = 0; 440 } 441 442 if (maj_stat == GSS_S_CONTINUE_NEEDED) { 443 ret = SASL_CONTINUE; 444 goto cleanup; 445 } 446 447 assert(maj_stat == GSS_S_COMPLETE); 448 449 if ((out_flags & GSS_C_SEQUENCE_FLAG) == 0) { 450 ret = SASL_BADAUTH; 451 goto cleanup; 452 } 453 454 maj_stat = gss_display_name(&min_stat, text->client_name, 455 &name_buf, NULL); 456 if (GSS_ERROR(maj_stat)) 457 goto cleanup; 458 459 ret = gs2_duplicate_buffer(params->utils, &name_buf, &short_name_buf); 460 if (ret != 0) 461 goto cleanup; 462 463 p = (char *)memchr(name_buf.value, '@', name_buf.length); 464 if (p != NULL) { 465 short_name_buf.length = (p - (char *)name_buf.value); 466 467 maj_stat = gss_import_name(&min_stat, 468 &short_name_buf, 469 GSS_C_NT_USER_NAME, 470 &without); 471 if (GSS_ERROR(maj_stat)) { 472 goto cleanup; 473 } 474 475 maj_stat = gss_compare_name(&min_stat, text->client_name, 476 without, &equal); 477 if (GSS_ERROR(maj_stat)) { 478 goto cleanup; 479 } 480 481 if (equal) 482 ((char *)short_name_buf.value)[short_name_buf.length] = '\0'; 483 } 484 485 text->authid = (char *)short_name_buf.value; 486 short_name_buf.value = NULL; 487 short_name_buf.length = 0; 488 489 if (text->authzid != NULL) { 490 ret = params->canon_user(params->utils->conn, 491 text->authzid, 0, 492 SASL_CU_AUTHZID, oparams); 493 if (ret != SASL_OK) { 494 goto cleanup; 495 } 496 } 497 498 ret = params->canon_user(params->utils->conn, 499 text->authid, 0, 500 text->authzid == NULL 501 ? (SASL_CU_AUTHZID | SASL_CU_AUTHID) 502 : SASL_CU_AUTHID, 503 oparams); 504 if (ret != SASL_OK) { 505 goto cleanup; 506 } 507 508 switch (text->gs2_flags & GS2_CB_FLAG_MASK) { 509 case GS2_CB_FLAG_N: 510 oparams->cbindingdisp = SASL_CB_DISP_NONE; 511 break; 512 case GS2_CB_FLAG_P: 513 oparams->cbindingdisp = SASL_CB_DISP_USED; 514 oparams->cbindingname = text->cbindingname; 515 break; 516 case GS2_CB_FLAG_Y: 517 oparams->cbindingdisp = SASL_CB_DISP_WANT; 518 break; 519 } 520 521 if (text->client_creds != GSS_C_NO_CREDENTIAL) 522 oparams->client_creds = &text->client_creds; 523 else 524 oparams->client_creds = NULL; 525 526 oparams->gss_peer_name = text->client_name; 527 oparams->gss_local_name = text->server_name; 528 oparams->maxoutbuf = 0xFFFFFF; 529 oparams->encode = NULL; 530 oparams->decode = NULL; 531 oparams->mech_ssf = 0; 532 oparams->doneflag = 1; 533 534 ret = SASL_OK; 535 536cleanup: 537 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) { 538 sasl_gs2_seterror(text->utils, maj_stat, min_stat); 539 ret = SASL_FAIL; 540 } 541 542 if (initialContextToken) { 543 gss_release_buffer(&min_stat, &input_token); 544 } 545 gss_release_buffer(&min_stat, &name_buf); 546 gss_release_buffer(&min_stat, &short_name_buf); 547 gss_release_buffer(&min_stat, &output_token); 548 gss_release_name(&min_stat, &without); 549 550 if (ret < SASL_OK) { 551 sasl_gs2_free_context_contents(text); 552 } 553 554 return ret; 555} 556 557static int 558gs2_common_plug_init(const sasl_utils_t *utils, 559 size_t plugsize, 560 int (*plug_alloc)(const sasl_utils_t *, 561 void *, 562 const gss_buffer_t, 563 const gss_OID), 564 void **pluglist, 565 int *plugcount) 566{ 567 OM_uint32 major, minor; 568 size_t i, count = 0; 569 void *plugs = NULL; 570 571 *pluglist = NULL; 572 *plugcount = 0; 573 574 if (gs2_indicate_mechs(utils) != SASL_OK) { 575 return SASL_NOMECH; 576 } 577 578 plugs = utils->malloc(gs2_mechs->count * plugsize); 579 if (plugs == NULL) { 580 MEMERROR(utils); 581 return SASL_NOMEM; 582 } 583 memset(plugs, 0, gs2_mechs->count * plugsize); 584 585 for (i = 0; i < gs2_mechs->count; i++) { 586 gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER; 587 588 major = gss_inquire_saslname_for_mech(&minor, 589 &gs2_mechs->elements[i], 590 &sasl_mech_name, 591 GSS_C_NO_BUFFER, 592 GSS_C_NO_BUFFER); 593 if (GSS_ERROR(major)) 594 continue; 595 596#define PLUG_AT(index) (void *)((unsigned char *)plugs + (count * plugsize)) 597 598 if (plug_alloc(utils, PLUG_AT(count), &sasl_mech_name, 599 &gs2_mechs->elements[i]) == SASL_OK) 600 count++; 601 602 gss_release_buffer(&minor, &sasl_mech_name); 603 } 604 605 if (count == 0) { 606 utils->free(plugs); 607 return SASL_NOMECH; 608 } 609 610 *pluglist = plugs; 611 *plugcount = count; 612 613 return SASL_OK; 614} 615 616static int 617gs2_server_plug_alloc(const sasl_utils_t *utils, 618 void *plug, 619 gss_buffer_t sasl_name, 620 gss_OID mech) 621{ 622 int ret; 623 sasl_server_plug_t *splug = (sasl_server_plug_t *)plug; 624 gss_buffer_desc buf; 625 626 memset(splug, 0, sizeof(*splug)); 627 628 ret = gs2_get_mech_attrs(utils, mech, 629 &splug->security_flags, 630 &splug->features, 631 NULL); 632 if (ret != SASL_OK) 633 return ret; 634 635 ret = gs2_duplicate_buffer(utils, sasl_name, &buf); 636 if (ret != SASL_OK) 637 return ret; 638 639 splug->mech_name = (char *)buf.value; 640 splug->glob_context = plug; 641 splug->mech_new = gs2_server_mech_new; 642 splug->mech_step = gs2_server_mech_step; 643 splug->mech_dispose = gs2_common_mech_dispose; 644 splug->mech_free = gs2_common_mech_free; 645 646 return SASL_OK; 647} 648 649static sasl_server_plug_t *gs2_server_plugins; 650static int gs2_server_plugcount; 651 652int 653gs2_server_plug_init(const sasl_utils_t *utils, 654 int maxversion, 655 int *outversion, 656 sasl_server_plug_t **pluglist, 657 int *plugcount) 658{ 659 int ret; 660 661 *pluglist = NULL; 662 *plugcount = 0; 663 664 if (maxversion < SASL_SERVER_PLUG_VERSION) 665 return SASL_BADVERS; 666 667 *outversion = SASL_SERVER_PLUG_VERSION; 668 669 if (gs2_server_plugins == NULL) { 670 ret = gs2_common_plug_init(utils, 671 sizeof(sasl_server_plug_t), 672 gs2_server_plug_alloc, 673 (void **)&gs2_server_plugins, 674 &gs2_server_plugcount); 675 if (ret != SASL_OK) 676 return ret; 677 } 678 679 *pluglist = gs2_server_plugins; 680 *plugcount = gs2_server_plugcount; 681 682 return SASL_OK; 683} 684 685/***************************** Client Section *****************************/ 686 687static int gs2_client_mech_step(void *conn_context, 688 sasl_client_params_t *params, 689 const char *serverin, 690 unsigned serverinlen, 691 sasl_interact_t **prompt_need, 692 const char **clientout, 693 unsigned *clientoutlen, 694 sasl_out_params_t *oparams) 695{ 696 context_t *text = (context_t *)conn_context; 697 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; 698 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; 699 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER; 700 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0; 701 OM_uint32 req_flags, ret_flags; 702 int ret = SASL_FAIL; 703 int initialContextToken; 704 705 *clientout = NULL; 706 *clientoutlen = 0; 707 708 if (text->gss_ctx == GSS_C_NO_CONTEXT) { 709 ret = gs2_get_init_creds(text, params, prompt_need, oparams); 710 if (ret != SASL_OK) { 711 goto cleanup; 712 } 713 714 initialContextToken = 1; 715 } else { 716 initialContextToken = 0; 717 } 718 719 if (text->server_name == GSS_C_NO_NAME) { /* only once */ 720 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN); 721 name_buf.value = params->utils->malloc(name_buf.length + 1); 722 if (name_buf.value == NULL) { 723 ret = SASL_NOMEM; 724 goto cleanup; 725 } 726 if (params->serverFQDN == NULL || 727 strlen(params->serverFQDN) == 0) { 728 SETERROR(text->utils, "GS2 Failure: no serverFQDN"); 729 ret = SASL_FAIL; 730 goto cleanup; 731 } 732 733 snprintf(name_buf.value, name_buf.length + 1, 734 "%s@%s", params->service, params->serverFQDN); 735 736 maj_stat = gss_import_name(&min_stat, 737 &name_buf, 738 GSS_C_NT_HOSTBASED_SERVICE, 739 &text->server_name); 740 params->utils->free(name_buf.value); 741 name_buf.value = NULL; 742 743 if (GSS_ERROR(maj_stat)) { 744 ret = SASL_OK; 745 goto cleanup; 746 } 747 } 748 749 /* From GSSAPI plugin: apparently this is for some IMAP bug workaround */ 750 if (serverinlen == 0 && text->gss_ctx != GSS_C_NO_CONTEXT) { 751 gss_delete_sec_context(&min_stat, &text->gss_ctx, GSS_C_NO_BUFFER); 752 text->gss_ctx = GSS_C_NO_CONTEXT; 753 } 754 755 input_token.value = (void *)serverin; 756 input_token.length = serverinlen; 757 758 if (initialContextToken) { 759 if ((text->plug.client->features & SASL_FEAT_GSS_FRAMING) == 0) 760 text->gs2_flags |= GS2_NONSTD_FLAG; 761 762 switch (params->cbindingdisp) { 763 case SASL_CB_DISP_NONE: 764 text->gs2_flags |= GS2_CB_FLAG_N; 765 break; 766 case SASL_CB_DISP_USED: 767 text->gs2_flags |= GS2_CB_FLAG_P; 768 break; 769 case SASL_CB_DISP_WANT: 770 text->gs2_flags |= GS2_CB_FLAG_Y; 771 break; 772 } 773 774 ret = gs2_make_header(text, params, 775 strcmp(oparams->user, oparams->authid) ? 776 (char *) oparams->user : NULL, 777 &text->out_buf, &text->out_buf_len); 778 if (ret != 0) { 779 goto cleanup; 780 } 781 } 782 783 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; 784 785 maj_stat = gss_init_sec_context(&min_stat, 786 (params->gss_creds != GSS_C_NO_CREDENTIAL) 787 ? (gss_cred_id_t)params->gss_creds 788 : text->client_creds, 789 &text->gss_ctx, 790 text->server_name, 791 (gss_OID)text->mechanism, 792 req_flags, 793 GSS_C_INDEFINITE, 794 &text->gss_cbindings, 795 serverinlen ? &input_token : GSS_C_NO_BUFFER, 796 NULL, 797 &output_token, 798 &ret_flags, 799 &text->lifetime); 800 if (GSS_ERROR(maj_stat)) { 801 ret = SASL_OK; 802 goto cleanup; 803 } 804 805 ret = gs2_make_message(text, params, initialContextToken, &output_token, 806 &text->out_buf, &text->out_buf_len); 807 if (ret != 0) { 808 goto cleanup; 809 } 810 811 *clientout = text->out_buf; 812 *clientoutlen = text->out_buf_len; 813 814 if (maj_stat == GSS_S_CONTINUE_NEEDED) { 815 ret = SASL_CONTINUE; 816 goto cleanup; 817 } 818 819 if (text->client_name != GSS_C_NO_NAME) { 820 gss_release_name(&min_stat, &text->client_name); 821 } 822 maj_stat = gss_inquire_context(&min_stat, 823 text->gss_ctx, 824 &text->client_name, 825 NULL, 826 &text->lifetime, 827 NULL, 828 &ret_flags, /* flags */ 829 NULL, 830 NULL); 831 if (GSS_ERROR(maj_stat)) { 832 ret = SASL_OK; 833 goto cleanup; 834 } 835 836 if ((ret_flags & req_flags) != req_flags) { 837 ret = SASL_BADAUTH; 838 goto cleanup; 839 } 840 841 maj_stat = gss_display_name(&min_stat, 842 text->client_name, 843 &name_buf, 844 NULL); 845 if (GSS_ERROR(maj_stat)) { 846 ret = SASL_OK; 847 goto cleanup; 848 } 849 850 oparams->gss_peer_name = text->server_name; 851 oparams->gss_local_name = text->client_name; 852 oparams->encode = NULL; 853 oparams->decode = NULL; 854 oparams->mech_ssf = 0; 855 oparams->maxoutbuf = 0xFFFFFF; 856 oparams->doneflag = 1; 857 858 ret = SASL_OK; 859 860cleanup: 861 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) { 862 sasl_gs2_seterror(text->utils, maj_stat, min_stat); 863 ret = SASL_FAIL; 864 } 865 866 gss_release_buffer(&min_stat, &output_token); 867 gss_release_buffer(&min_stat, &name_buf); 868 869 if (ret < SASL_OK) { 870 sasl_gs2_free_context_contents(text); 871 } 872 873 return ret; 874} 875 876static int gs2_client_mech_new(void *glob_context, 877 sasl_client_params_t *params, 878 void **conn_context) 879{ 880 context_t *text; 881 int ret; 882 883 text = sasl_gs2_new_context(params->utils); 884 if (text == NULL) { 885 MEMERROR(params->utils); 886 return SASL_NOMEM; 887 } 888 889 text->gss_ctx = GSS_C_NO_CONTEXT; 890 text->client_name = GSS_C_NO_NAME; 891 text->server_creds = GSS_C_NO_CREDENTIAL; 892 text->client_creds = GSS_C_NO_CREDENTIAL; 893 text->plug.client = glob_context; 894 895 ret = gs2_map_sasl_name(params->utils, text->plug.client->mech_name, 896 &text->mechanism); 897 if (ret != SASL_OK) { 898 gs2_common_mech_dispose(text, params->utils); 899 return ret; 900 } 901 902 *conn_context = text; 903 904 return SASL_OK; 905} 906 907static int 908gs2_client_plug_alloc(const sasl_utils_t *utils, 909 void *plug, 910 gss_buffer_t sasl_name, 911 gss_OID mech) 912{ 913 int ret; 914 sasl_client_plug_t *cplug = (sasl_client_plug_t *)plug; 915 gss_buffer_desc buf; 916 917 memset(cplug, 0, sizeof(*cplug)); 918 919 ret = gs2_get_mech_attrs(utils, mech, 920 &cplug->security_flags, 921 &cplug->features, 922 &cplug->required_prompts); 923 if (ret != SASL_OK) 924 return ret; 925 926 ret = gs2_duplicate_buffer(utils, sasl_name, &buf); 927 if (ret != SASL_OK) 928 return ret; 929 930 cplug->mech_name = (char *)buf.value; 931 cplug->features |= SASL_FEAT_NEEDSERVERFQDN; 932 cplug->glob_context = plug; 933 cplug->mech_new = gs2_client_mech_new; 934 cplug->mech_step = gs2_client_mech_step; 935 cplug->mech_dispose = gs2_common_mech_dispose; 936 cplug->mech_free = gs2_common_mech_free; 937 938 return SASL_OK; 939} 940 941static sasl_client_plug_t *gs2_client_plugins; 942static int gs2_client_plugcount; 943 944int 945gs2_client_plug_init(const sasl_utils_t *utils, 946 int maxversion, 947 int *outversion, 948 sasl_client_plug_t **pluglist, 949 int *plugcount) 950{ 951 int ret; 952 953 *pluglist = NULL; 954 *plugcount = 0; 955 956 if (maxversion < SASL_CLIENT_PLUG_VERSION) 957 return SASL_BADVERS; 958 959 *outversion = SASL_CLIENT_PLUG_VERSION; 960 961 if (gs2_client_plugins == NULL) { 962 ret = gs2_common_plug_init(utils, 963 sizeof(sasl_client_plug_t), 964 gs2_client_plug_alloc, 965 (void **)&gs2_client_plugins, 966 &gs2_client_plugcount); 967 if (ret != SASL_OK) 968 return ret; 969 } 970 971 *pluglist = gs2_client_plugins; 972 *plugcount = gs2_client_plugcount; 973 974 return SASL_OK; 975} 976 977/* 978 * Copy header and application channel bindings to GSS channel bindings 979 * structure in context. 980 */ 981static int 982gs2_save_cbindings(context_t *text, 983 gss_buffer_t header, 984 const sasl_channel_binding_t *cbinding) 985{ 986 gss_buffer_t gss_cbindings = &text->gss_cbindings.application_data; 987 size_t len; 988 unsigned char *p; 989 990 assert(gss_cbindings->value == NULL); 991 992 /* 993 * The application-data field MUST be set to the gs2-header, excluding 994 * the initial [gs2-nonstd-flag ","] part, concatenated with, when a 995 * gs2-cb-flag of "p" is used, the application's channel binding data. 996 */ 997 len = header->length; 998 if (text->gs2_flags & GS2_NONSTD_FLAG) { 999 assert(len > 2); 1000 len -= 2; 1001 } 1002 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P && 1003 cbinding != NULL) { 1004 len += cbinding->len; 1005 } 1006 1007 gss_cbindings->length = len; 1008 gss_cbindings->value = text->utils->malloc(len); 1009 if (gss_cbindings->value == NULL) 1010 return SASL_NOMEM; 1011 1012 p = (unsigned char *)gss_cbindings->value; 1013 if (text->gs2_flags & GS2_NONSTD_FLAG) { 1014 memcpy(p, (unsigned char *)header->value + 2, header->length - 2); 1015 p += header->length - 2; 1016 } else { 1017 memcpy(p, header->value, header->length); 1018 p += header->length; 1019 } 1020 1021 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P && 1022 cbinding != NULL) { 1023 memcpy(p, cbinding->data, cbinding->len); 1024 } 1025 1026 return SASL_OK; 1027} 1028 1029#define CHECK_REMAIN(n) do { if (remain < (n)) return SASL_BADPROT; } while (0) 1030 1031/* 1032 * Verify gs2-header, save authzid and channel bindings to context. 1033 */ 1034static int 1035gs2_verify_initial_message(context_t *text, 1036 sasl_server_params_t *sparams, 1037 const char *in, 1038 unsigned inlen, 1039 gss_buffer_t token) 1040{ 1041 OM_uint32 major, minor; 1042 char *p = (char *)in; 1043 unsigned remain = inlen; 1044 int ret; 1045 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER; 1046 1047 assert(text->cbindingname == NULL); 1048 assert(text->authzid == NULL); 1049 1050 token->length = 0; 1051 token->value = NULL; 1052 1053 /* minimum header includes CB flag and non-zero GSS token */ 1054 CHECK_REMAIN(4); /* [pny],,. */ 1055 1056 /* non-standard GSS framing flag */ 1057 if (remain > 1 && memcmp(p, "F,", 2) == 0) { 1058 text->gs2_flags |= GS2_NONSTD_FLAG; 1059 remain -= 2; 1060 p += 2; 1061 } 1062 1063 /* SASL channel bindings */ 1064 CHECK_REMAIN(1); /* [pny] */ 1065 remain--; 1066 switch (*p++) { 1067 case 'p': 1068 CHECK_REMAIN(1); /* = */ 1069 remain--; 1070 if (*p++ != '=') 1071 return SASL_BADPROT; 1072 1073 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->cbindingname); 1074 if (ret != SASL_OK) 1075 return ret; 1076 1077 text->gs2_flags |= GS2_CB_FLAG_P; 1078 break; 1079 case 'n': 1080 text->gs2_flags |= GS2_CB_FLAG_N; 1081 break; 1082 case 'y': 1083 text->gs2_flags |= GS2_CB_FLAG_Y; 1084 break; 1085 } 1086 1087 CHECK_REMAIN(1); /* , */ 1088 remain--; 1089 if (*p++ != ',') 1090 return SASL_BADPROT; 1091 1092 /* authorization identity */ 1093 if (remain > 1 && memcmp(p, "a=", 2) == 0) { 1094 CHECK_REMAIN(2); 1095 remain -= 2; 1096 p += 2; 1097 1098 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->authzid); 1099 if (ret != SASL_OK) 1100 return ret; 1101 } 1102 1103 /* end of header */ 1104 CHECK_REMAIN(1); /* , */ 1105 remain--; 1106 if (*p++ != ',') 1107 return SASL_BADPROT; 1108 1109 buf.length = inlen - remain; 1110 buf.value = (void *)in; 1111 1112 /* stash channel bindings to pass into gss_accept_sec_context() */ 1113 ret = gs2_save_cbindings(text, &buf, sparams->cbinding); 1114 if (ret != SASL_OK) 1115 return ret; 1116 1117 if (text->gs2_flags & GS2_NONSTD_FLAG) { 1118 buf.length = remain; 1119 buf.value = p; 1120 } else { 1121 gss_buffer_desc tmp; 1122 1123 tmp.length = remain; 1124 tmp.value = p; 1125 1126 major = gss_encapsulate_token(&tmp, text->mechanism, &buf); 1127 if (GSS_ERROR(major)) 1128 return SASL_NOMEM; 1129 } 1130 1131 token->value = text->utils->malloc(buf.length); 1132 if (token->value == NULL) 1133 return SASL_NOMEM; 1134 1135 token->length = buf.length; 1136 memcpy(token->value, buf.value, buf.length); 1137 1138 if ((text->gs2_flags & GS2_NONSTD_FLAG) == 0) 1139 gss_release_buffer(&minor, &buf); 1140 1141 return SASL_OK; 1142} 1143 1144/* 1145 * Create gs2-header, save channel bindings to context. 1146 */ 1147static int 1148gs2_make_header(context_t *text, 1149 sasl_client_params_t *cparams, 1150 const char *authzid, 1151 char **out, 1152 unsigned *outlen) 1153{ 1154 size_t required = 0; 1155 size_t wire_authzid_len = 0, cbnamelen = 0; 1156 char *wire_authzid = NULL; 1157 char *p; 1158 int ret; 1159 gss_buffer_desc buf; 1160 1161 *out = NULL; 1162 *outlen = 0; 1163 1164 /* non-standard GSS framing flag */ 1165 if (text->gs2_flags & GS2_NONSTD_FLAG) 1166 required += 2; /* F, */ 1167 1168 /* SASL channel bindings */ 1169 switch (text->gs2_flags & GS2_CB_FLAG_MASK) { 1170 case GS2_CB_FLAG_P: 1171 if (!SASL_CB_PRESENT(cparams)) 1172 return SASL_BADPARAM; 1173 cbnamelen = strlen(cparams->cbinding->name); 1174 required += 1 /*=*/ + cbnamelen; 1175 /* fallthrough */ 1176 case GS2_CB_FLAG_N: 1177 case GS2_CB_FLAG_Y: 1178 required += 2; /* [pny], */ 1179 break; 1180 default: 1181 return SASL_BADPARAM; 1182 } 1183 1184 /* authorization identity */ 1185 if (authzid != NULL) { 1186 ret = gs2_escape_authzid(text->utils, authzid, 1187 strlen(authzid), &wire_authzid); 1188 if (ret != SASL_OK) 1189 return ret; 1190 1191 wire_authzid_len = strlen(wire_authzid); 1192 required += 2 /* a= */ + wire_authzid_len; 1193 } 1194 1195 required += 1; /* trailing comma */ 1196 1197 ret = _plug_buf_alloc(text->utils, out, outlen, required); 1198 if (ret != SASL_OK) { 1199 text->utils->free(wire_authzid); 1200 return ret; 1201 } 1202 1203 *out = text->out_buf; 1204 *outlen = required; 1205 1206 p = (char *)text->out_buf; 1207 if (text->gs2_flags & GS2_NONSTD_FLAG) { 1208 *p++ = 'F'; 1209 *p++ = ','; 1210 } 1211 switch (text->gs2_flags & GS2_CB_FLAG_MASK) { 1212 case GS2_CB_FLAG_P: 1213 memcpy(p, "p=", 2); 1214 memcpy(p + 2, cparams->cbinding->name, cbnamelen); 1215 p += 2 + cbnamelen; 1216 break; 1217 case GS2_CB_FLAG_N: 1218 *p++ = 'n'; 1219 break; 1220 case GS2_CB_FLAG_Y: 1221 *p++ = 'y'; 1222 break; 1223 } 1224 *p++ = ','; 1225 if (wire_authzid != NULL) { 1226 memcpy(p, "a=", 2); 1227 memcpy(p + 2, wire_authzid, wire_authzid_len); 1228 text->utils->free(wire_authzid); 1229 p += 2 + wire_authzid_len; 1230 } 1231 *p++ = ','; 1232 1233 assert(p == (char *)text->out_buf + required); 1234 1235 buf.length = required; 1236 buf.value = *out; 1237 1238 ret = gs2_save_cbindings(text, &buf, cparams->cbinding); 1239 if (ret != SASL_OK) 1240 return ret; 1241 1242 return SASL_OK; 1243} 1244 1245/* 1246 * Convert a GSS token to a GS2 one 1247 */ 1248static int 1249gs2_make_message(context_t *text, 1250 sasl_client_params_t *cparams __attribute__((unused)), 1251 int initialContextToken, 1252 gss_buffer_t token, 1253 char **out, 1254 unsigned *outlen) 1255{ 1256 OM_uint32 major, minor; 1257 int ret; 1258 unsigned header_len = 0; 1259 gss_buffer_desc decap_token = GSS_C_EMPTY_BUFFER; 1260 1261 if (initialContextToken) { 1262 header_len = *outlen; 1263 1264 major = gss_decapsulate_token(token, text->mechanism, &decap_token); 1265 if ((major == GSS_S_DEFECTIVE_TOKEN && 1266 (text->plug.client->features & SASL_FEAT_GSS_FRAMING)) || 1267 GSS_ERROR(major)) 1268 return SASL_FAIL; 1269 1270 token = &decap_token; 1271 } 1272 1273 ret = _plug_buf_alloc(text->utils, out, outlen, 1274 header_len + token->length); 1275 if (ret != 0) 1276 return ret; 1277 1278 memcpy(*out + header_len, token->value, token->length); 1279 *outlen = header_len + token->length; 1280 1281 if (initialContextToken) 1282 gss_release_buffer(&minor, &decap_token); 1283 1284 return SASL_OK; 1285} 1286 1287static const unsigned long gs2_required_prompts[] = { 1288 SASL_CB_LIST_END 1289}; 1290 1291/* 1292 * Map GSS mechanism attributes to SASL ones 1293 */ 1294static int 1295gs2_get_mech_attrs(const sasl_utils_t *utils, 1296 const gss_OID mech, 1297 unsigned int *security_flags, 1298 unsigned int *features, 1299 const unsigned long **prompts) 1300{ 1301 OM_uint32 major, minor; 1302 int present; 1303 gss_OID_set attrs = GSS_C_NO_OID_SET; 1304 1305 major = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL); 1306 if (GSS_ERROR(major)) { 1307 utils->seterror(utils->conn, SASL_NOLOG, 1308 "GS2 Failure: gss_inquire_attrs_for_mech"); 1309 return SASL_FAIL; 1310 } 1311 1312 *security_flags = SASL_SEC_NOPLAINTEXT | SASL_SEC_NOACTIVE; 1313 *features = SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_CHANNEL_BINDING; 1314 if (prompts != NULL) 1315 *prompts = gs2_required_prompts; 1316 1317#define MA_PRESENT(a) (gss_test_oid_set_member(&minor, (gss_OID)(a), \ 1318 attrs, &present) == GSS_S_COMPLETE && \ 1319 present) 1320 1321 if (MA_PRESENT(GSS_C_MA_PFS)) 1322 *security_flags |= SASL_SEC_FORWARD_SECRECY; 1323 if (!MA_PRESENT(GSS_C_MA_AUTH_INIT_ANON)) 1324 *security_flags |= SASL_SEC_NOANONYMOUS; 1325 if (MA_PRESENT(GSS_C_MA_DELEG_CRED)) 1326 *security_flags |= SASL_SEC_PASS_CREDENTIALS; 1327 if (MA_PRESENT(GSS_C_MA_AUTH_TARG)) 1328 *security_flags |= SASL_SEC_MUTUAL_AUTH; 1329 if (MA_PRESENT(GSS_C_MA_AUTH_INIT_INIT) && prompts != NULL) 1330 *prompts = NULL; 1331 if (MA_PRESENT(GSS_C_MA_ITOK_FRAMED)) 1332 *features |= SASL_FEAT_GSS_FRAMING; 1333 1334 gss_release_oid_set(&minor, &attrs); 1335 1336 return SASL_OK; 1337} 1338 1339/* 1340 * Enumerate GSS mechanisms that can be used for GS2 1341 */ 1342static int gs2_indicate_mechs(const sasl_utils_t *utils) 1343{ 1344 OM_uint32 major, minor; 1345 gss_OID_desc desired_oids[3]; 1346 gss_OID_set_desc desired_attrs; 1347 gss_OID_desc except_oids[3]; 1348 gss_OID_set_desc except_attrs; 1349 1350 if (gs2_mechs != GSS_C_NO_OID_SET) 1351 return SASL_OK; 1352 1353 desired_oids[0] = *GSS_C_MA_AUTH_INIT; 1354 desired_oids[1] = *GSS_C_MA_AUTH_TARG; 1355 desired_oids[2] = *GSS_C_MA_CBINDINGS; 1356 desired_attrs.count = sizeof(desired_oids)/sizeof(desired_oids[0]); 1357 desired_attrs.elements = desired_oids; 1358 1359 except_oids[0] = *GSS_C_MA_MECH_NEGO; 1360 except_oids[1] = *GSS_C_MA_NOT_MECH; 1361 except_oids[2] = *GSS_C_MA_DEPRECATED; 1362 1363 except_attrs.count = sizeof(except_oids)/sizeof(except_oids[0]); 1364 except_attrs.elements = except_oids; 1365 1366 major = gss_indicate_mechs_by_attrs(&minor, 1367 &desired_attrs, 1368 &except_attrs, 1369 GSS_C_NO_OID_SET, 1370 &gs2_mechs); 1371 if (GSS_ERROR(major)) { 1372 utils->seterror(utils->conn, SASL_NOLOG, 1373 "GS2 Failure: gss_indicate_mechs_by_attrs"); 1374 return SASL_FAIL; 1375 } 1376 1377 return (gs2_mechs->count > 0) ? SASL_OK : SASL_NOMECH; 1378} 1379 1380/* 1381 * Map SASL mechanism name to OID 1382 */ 1383static int 1384gs2_map_sasl_name(const sasl_utils_t *utils, 1385 const char *mech, 1386 gss_OID *oid) 1387{ 1388 OM_uint32 major, minor; 1389 gss_buffer_desc buf; 1390 1391 buf.length = strlen(mech); 1392 buf.value = (void *)mech; 1393 1394 major = gss_inquire_mech_for_saslname(&minor, &buf, oid); 1395 if (GSS_ERROR(major)) { 1396 utils->seterror(utils->conn, SASL_NOLOG, 1397 "GS2 Failure: gss_inquire_mech_for_saslname"); 1398 return SASL_FAIL; 1399 } 1400 1401 return SASL_OK; 1402} 1403 1404static int 1405gs2_duplicate_buffer(const sasl_utils_t *utils, 1406 const gss_buffer_t src, 1407 gss_buffer_t dst) 1408{ 1409 dst->value = utils->malloc(src->length + 1); 1410 if (dst->value == NULL) 1411 return SASL_NOMEM; 1412 1413 memcpy(dst->value, src->value, src->length); 1414 ((char *)dst->value)[src->length] = '\0'; 1415 dst->length = src->length; 1416 1417 return SASL_OK; 1418} 1419 1420static int 1421gs2_unescape_authzid(const sasl_utils_t *utils, 1422 char **endp, 1423 unsigned *remain, 1424 char **authzid) 1425{ 1426 char *in = *endp; 1427 size_t i, len, inlen = *remain; 1428 char *p; 1429 1430 *endp = NULL; 1431 1432 for (i = 0, len = 0; i < inlen; i++) { 1433 if (in[i] == ',') { 1434 *endp = &in[i]; 1435 *remain -= i; 1436 break; 1437 } else if (in[i] == '=') { 1438 if (inlen <= i + 2) 1439 return SASL_BADPROT; 1440 i += 2; 1441 } 1442 len++; 1443 } 1444 1445 if (len == 0 || *endp == NULL) 1446 return SASL_BADPROT; 1447 1448 p = *authzid = utils->malloc(len + 1); 1449 if (*authzid == NULL) 1450 return SASL_NOMEM; 1451 1452 for (i = 0; i < inlen; i++) { 1453 if (in[i] == ',') 1454 break; 1455 else if (in[i] == '=') { 1456 if (memcmp(&in[i + 1], "2C", 2) == 0) 1457 *p++ = ','; 1458 else if (memcmp(&in[i + 1], "3D", 2) == 0) 1459 *p++ = '='; 1460 else { 1461 utils->free(*authzid); 1462 *authzid = NULL; 1463 return SASL_BADPROT; 1464 } 1465 i += 2; 1466 } else 1467 *p++ = in[i]; 1468 } 1469 1470 *p = '\0'; 1471 1472 return SASL_OK; 1473} 1474 1475static int 1476gs2_escape_authzid(const sasl_utils_t *utils, 1477 const char *in, 1478 unsigned inlen, 1479 char **authzid) 1480{ 1481 size_t i; 1482 char *p; 1483 1484 p = *authzid = utils->malloc((inlen * 3) + 1); 1485 if (*authzid == NULL) 1486 return SASL_NOMEM; 1487 1488 for (i = 0; i < inlen; i++) { 1489 if (in[i] == ',') { 1490 memcpy(p, "=2C", 3); 1491 p += 3; 1492 } else if (in[i] == '=') { 1493 memcpy(p, "=3D", 3); 1494 p += 3; 1495 } else { 1496 *p++ = in[i]; 1497 } 1498 } 1499 1500 *p = '\0'; 1501 1502 return SASL_OK; 1503} 1504 1505#define GOT_CREDS(text, params) ((text)->client_creds != NULL || (params)->gss_creds != NULL) 1506#define CRED_ERROR(status) ((status) == GSS_S_CRED_UNAVAIL || (status) == GSS_S_NO_CRED) 1507 1508/* 1509 * Determine the authentication identity from the application supplied 1510 * GSS credential, the application supplied identity, and the default 1511 * GSS credential, in that order. Then, acquire credentials. 1512 */ 1513static int 1514gs2_get_init_creds(context_t *text, 1515 sasl_client_params_t *params, 1516 sasl_interact_t **prompt_need, 1517 sasl_out_params_t *oparams) 1518{ 1519 int result = SASL_OK; 1520 const char *authid = NULL, *userid = NULL; 1521 int user_result = SASL_OK; 1522 int auth_result = SASL_OK; 1523 int pass_result = SASL_OK; 1524 OM_uint32 maj_stat = GSS_S_COMPLETE, min_stat = 0; 1525 gss_OID_set_desc mechs; 1526 gss_buffer_desc cred_authid = GSS_C_EMPTY_BUFFER; 1527 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER; 1528 1529 mechs.count = 1; 1530 mechs.elements = (gss_OID)text->mechanism; 1531 1532 /* 1533 * Get the authentication identity from the application. 1534 */ 1535 if (oparams->authid == NULL) { 1536 auth_result = _plug_get_authid(params->utils, &authid, prompt_need); 1537 if (auth_result != SASL_OK && auth_result != SASL_INTERACT) { 1538 result = auth_result; 1539 goto cleanup; 1540 } 1541 } 1542 1543 /* 1544 * Get the authorization identity from the application. 1545 */ 1546 if (oparams->user == NULL) { 1547 user_result = _plug_get_userid(params->utils, &userid, prompt_need); 1548 if (user_result != SASL_OK && user_result != SASL_INTERACT) { 1549 result = user_result; 1550 goto cleanup; 1551 } 1552 } 1553 1554 /* 1555 * Canonicalize the authentication and authorization identities before 1556 * calling GSS_Import_name. 1557 */ 1558 if (auth_result == SASL_OK && user_result == SASL_OK && 1559 oparams->authid == NULL) { 1560 if (userid == NULL || userid[0] == '\0') { 1561 result = params->canon_user(params->utils->conn, authid, 0, 1562 SASL_CU_AUTHID | SASL_CU_AUTHZID, 1563 oparams); 1564 } else { 1565 result = params->canon_user(params->utils->conn, 1566 authid, 0, SASL_CU_AUTHID, oparams); 1567 if (result != SASL_OK) 1568 goto cleanup; 1569 1570 result = params->canon_user(params->utils->conn, 1571 userid, 0, SASL_CU_AUTHZID, oparams); 1572 if (result != SASL_OK) 1573 goto cleanup; 1574 } 1575 1576 if (oparams->authid != NULL) { 1577 name_buf.length = strlen(oparams->authid); 1578 name_buf.value = (void *)oparams->authid; 1579 1580 assert(text->client_name == GSS_C_NO_NAME); 1581 1582 maj_stat = gss_import_name(&min_stat, 1583 &name_buf, 1584 GSS_C_NT_USER_NAME, 1585 &text->client_name); 1586 if (GSS_ERROR(maj_stat)) 1587 goto cleanup; 1588 } 1589 } 1590 1591 /* 1592 * If application didn't provide an authid, then use the default 1593 * credential. If that doesn't work, give up. 1594 */ 1595 if (!GOT_CREDS(text, params) && oparams->authid == NULL) { 1596 maj_stat = gss_acquire_cred(&min_stat, 1597 GSS_C_NO_NAME, 1598 GSS_C_INDEFINITE, 1599 &mechs, 1600 GSS_C_INITIATE, 1601 &text->client_creds, 1602 NULL, 1603 &text->lifetime); 1604 if (GSS_ERROR(maj_stat)) 1605 goto cleanup; 1606 1607 assert(text->client_name == GSS_C_NO_NAME); 1608 1609 maj_stat = gss_inquire_cred(&min_stat, 1610 params->gss_creds 1611 ? (gss_cred_id_t)params->gss_creds 1612 : text->client_creds, 1613 &text->client_name, 1614 NULL, 1615 NULL, 1616 NULL); 1617 if (GSS_ERROR(maj_stat)) 1618 goto cleanup; 1619 1620 maj_stat = gss_display_name(&min_stat, 1621 text->client_name, 1622 &cred_authid, 1623 NULL); 1624 if (GSS_ERROR(maj_stat)) 1625 goto cleanup; 1626 1627 if (userid == NULL || userid[0] == '\0') { 1628 result = params->canon_user(params->utils->conn, 1629 cred_authid.value, cred_authid.length, 1630 SASL_CU_AUTHID | SASL_CU_AUTHZID, 1631 oparams); 1632 } else { 1633 result = params->canon_user(params->utils->conn, 1634 cred_authid.value, cred_authid.length, 1635 SASL_CU_AUTHID, oparams); 1636 if (result != SASL_OK) 1637 goto cleanup; 1638 1639 result = params->canon_user(params->utils->conn, 1640 cred_authid.value, cred_authid.length, 1641 SASL_CU_AUTHZID, oparams); 1642 if (result != SASL_OK) 1643 goto cleanup; 1644 } 1645 } 1646 1647 /* 1648 * Armed with the authentication identity, try to get a credential without 1649 * a password. 1650 */ 1651 if (!GOT_CREDS(text, params) && text->client_name != GSS_C_NO_NAME) { 1652 maj_stat = gss_acquire_cred(&min_stat, 1653 text->client_name, 1654 GSS_C_INDEFINITE, 1655 &mechs, 1656 GSS_C_INITIATE, 1657 &text->client_creds, 1658 NULL, 1659 &text->lifetime); 1660 if (GSS_ERROR(maj_stat) && !CRED_ERROR(maj_stat)) 1661 goto cleanup; 1662 } 1663 1664 /* 1665 * If that failed, try to get a credential with a password. 1666 */ 1667 if (!GOT_CREDS(text, params)) { 1668 if (text->password == NULL) { 1669 pass_result = _plug_get_password(params->utils, &text->password, 1670 &text->free_password, prompt_need); 1671 if (pass_result != SASL_OK && pass_result != SASL_INTERACT) { 1672 result = pass_result; 1673 goto cleanup; 1674 } 1675 } 1676 1677 if (text->password != NULL) { 1678 gss_buffer_desc password_buf; 1679 1680 password_buf.length = text->password->len; 1681 password_buf.value = text->password->data; 1682 1683 maj_stat = gss_acquire_cred_with_password(&min_stat, 1684 text->client_name, 1685 &password_buf, 1686 GSS_C_INDEFINITE, 1687 &mechs, 1688 GSS_C_INITIATE, 1689 &text->client_creds, 1690 NULL, 1691 &text->lifetime); 1692 if (GSS_ERROR(maj_stat)) 1693 goto cleanup; 1694 } 1695 } 1696 1697 maj_stat = GSS_S_COMPLETE; 1698 1699 /* free prompts we got */ 1700 if (prompt_need && *prompt_need) { 1701 params->utils->free(*prompt_need); 1702 *prompt_need = NULL; 1703 } 1704 1705 /* if there are prompts not filled in */ 1706 if (user_result == SASL_INTERACT || auth_result == SASL_INTERACT || 1707 pass_result == SASL_INTERACT) { 1708 /* make the prompt list */ 1709 result = 1710 _plug_make_prompts(params->utils, prompt_need, 1711 user_result == SASL_INTERACT ? 1712 "Please enter your authorization name" : NULL, 1713 NULL, 1714 auth_result == SASL_INTERACT ? 1715 "Please enter your authentication name" : NULL, 1716 NULL, 1717 pass_result == SASL_INTERACT ? 1718 "Please enter your password" : NULL, NULL, 1719 NULL, NULL, NULL, 1720 NULL, 1721 NULL, NULL); 1722 if (result == SASL_OK) 1723 result = SASL_INTERACT; 1724 } 1725 1726cleanup: 1727 if (result == SASL_OK && maj_stat != GSS_S_COMPLETE) { 1728 sasl_gs2_seterror(text->utils, maj_stat, min_stat); 1729 result = SASL_FAIL; 1730 } 1731 1732 gss_release_buffer(&min_stat, &cred_authid); 1733 1734 return result; 1735} 1736 1737static int 1738sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min, 1739 int logonly) 1740{ 1741 OM_uint32 maj_stat, min_stat; 1742 gss_buffer_desc msg; 1743 OM_uint32 msg_ctx; 1744 int ret; 1745 char *out = NULL; 1746 unsigned int len, curlen = 0; 1747 const char prefix[] = "GS2 Error: "; 1748 1749 len = sizeof(prefix); 1750 ret = _plug_buf_alloc(utils, &out, &curlen, 256); 1751 if (ret != SASL_OK) 1752 return SASL_OK; 1753 1754 strcpy(out, prefix); 1755 1756 msg_ctx = 0; 1757 while (1) { 1758 maj_stat = gss_display_status(&min_stat, maj, 1759 GSS_C_GSS_CODE, GSS_C_NULL_OID, 1760 &msg_ctx, &msg); 1761 1762 if (GSS_ERROR(maj_stat)) { 1763 if (logonly) { 1764 utils->log(utils->conn, SASL_LOG_FAIL, 1765 "GS2 Failure: (could not get major error message)"); 1766 } else { 1767 utils->seterror(utils->conn, 0, 1768 "GS2 Failure " 1769 "(could not get major error message)"); 1770 } 1771 utils->free(out); 1772 return SASL_OK; 1773 } 1774 1775 len += len + msg.length; 1776 ret = _plug_buf_alloc(utils, &out, &curlen, len); 1777 if (ret != SASL_OK) { 1778 utils->free(out); 1779 return SASL_OK; 1780 } 1781 1782 strcat(out, msg.value); 1783 1784 gss_release_buffer(&min_stat, &msg); 1785 1786 if (!msg_ctx) 1787 break; 1788 } 1789 1790 /* Now get the minor status */ 1791 1792 len += 2; 1793 ret = _plug_buf_alloc(utils, &out, &curlen, len); 1794 if (ret != SASL_OK) { 1795 utils->free(out); 1796 return SASL_NOMEM; 1797 } 1798 1799 strcat(out, " ("); 1800 1801 msg_ctx = 0; 1802 while (1) { 1803 maj_stat = gss_display_status(&min_stat, min, 1804 GSS_C_MECH_CODE, GSS_C_NULL_OID, 1805 &msg_ctx, &msg); 1806 1807 if (GSS_ERROR(maj_stat)) { 1808 if (logonly) { 1809 utils->log(utils->conn, SASL_LOG_FAIL, 1810 "GS2 Failure: (could not get minor error message)"); 1811 } else { 1812 utils->seterror(utils->conn, 0, 1813 "GS2 Failure " 1814 "(could not get minor error message)"); 1815 } 1816 utils->free(out); 1817 return SASL_OK; 1818 } 1819 1820 len += len + msg.length; 1821 1822 ret = _plug_buf_alloc(utils, &out, &curlen, len); 1823 if (ret != SASL_OK) { 1824 utils->free(out); 1825 return SASL_NOMEM; 1826 } 1827 1828 strcat(out, msg.value); 1829 1830 gss_release_buffer(&min_stat, &msg); 1831 1832 if (!msg_ctx) 1833 break; 1834 } 1835 1836 len += 1; 1837 ret = _plug_buf_alloc(utils, &out, &curlen, len); 1838 if (ret != SASL_OK) { 1839 utils->free(out); 1840 return SASL_NOMEM; 1841 } 1842 1843 strcat(out, ")"); 1844 1845 if (logonly) { 1846 utils->log(utils->conn, SASL_LOG_FAIL, out); 1847 } else { 1848 utils->seterror(utils->conn, 0, out); 1849 } 1850 utils->free(out); 1851 1852 return SASL_OK; 1853} 1854