1/* GSSAPI SASL plugin 2 * Leif Johansson 3 * Rob Siemborski (SASL v2 Conversion) 4 * $Id: gssapi.c,v 1.11 2006/02/03 22:33:14 snsimon Exp $ 5 */ 6/* 7 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The name "Carnegie Mellon University" must not be used to 22 * endorse or promote products derived from this software without 23 * prior written permission. For permission or any other legal 24 * details, please contact 25 * Office of Technology Transfer 26 * Carnegie Mellon University 27 * 5000 Forbes Avenue 28 * Pittsburgh, PA 15213-3890 29 * (412) 268-4387, fax: (412) 268-7395 30 * tech-transfer@andrew.cmu.edu 31 * 32 * 4. Redistributions of any form whatsoever must retain the following 33 * acknowledgment: 34 * "This product includes software developed by Computing Services 35 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 36 * 37 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 38 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 39 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 40 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 41 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 42 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 43 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 44 */ 45 46#include <config.h> 47 48#ifdef HAVE_GSSAPI_H 49#include <gssapi.h> 50#include <gssapi_krb5.h> 51#else 52#include <gssapi/gssapi.h> 53#include <gssapi/gssapi_krb5.h> 54#endif 55 56 57#ifdef WIN32 58# include <winsock2.h> 59 60# ifndef R_OK 61# define R_OK 04 62# endif 63/* we also need io.h for access() prototype */ 64# include <io.h> 65#else 66# include <sys/param.h> 67# include <sys/socket.h> 68# include <netinet/in.h> 69# include <arpa/inet.h> 70# include <netdb.h> 71#endif /* WIN32 */ 72#include <fcntl.h> 73#include <stdio.h> 74#include <sasl.h> 75#include <saslutil.h> 76#include <saslplug.h> 77 78#include "plugin_common.h" 79 80#ifdef HAVE_UNISTD_H 81#include <unistd.h> 82#endif 83 84#include <errno.h> 85 86/***************************** Common Section *****************************/ 87 88//static const char plugin_id[] = "$Id: gssapi.c,v 1.11 2006/02/03 22:33:14 snsimon Exp $"; 89 90static const char * GSSAPI_BLANK_STRING = ""; 91 92#ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE 93extern gss_OID gss_nt_service_name; 94#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name 95#endif 96 97#ifdef WANT_KERBEROS5_3DES 98/* Check if CyberSafe flag is defined */ 99#ifdef CSF_GSS_C_DES3_FLAG 100#define K5_MAX_SSF 112 101#endif 102 103/* Heimdal and MIT use the following */ 104#ifdef GSS_KRB5_CONF_C_QOP_DES3_KD 105#define K5_MAX_SSF 112 106#endif 107 108#endif 109 110#ifndef K5_MAX_SSF 111/* All Kerberos implementations support DES */ 112#define K5_MAX_SSF 56 113#endif 114 115/* GSSAPI SASL Mechanism by Leif Johansson <leifj@matematik.su.se> 116 * inspired by the kerberos mechanism and the gssapi_server and 117 * gssapi_client from the heimdal distribution by Assar Westerlund 118 * <assar@sics.se> and Johan Danielsson <joda@pdc.kth.se>. 119 * See the configure.in file for details on dependencies. 120 * 121 * Important contributions from Sam Hartman <hartmans@fundsxpress.com>. 122 * 123 * This code was tested with the following distributions of Kerberos: 124 * Heimdal (http://www.pdc.kth.se/heimdal), MIT (http://web.mit.edu/kerberos/www/) 125 * CyberSafe (http://www.cybersafe.com/) and SEAM. 126 */ 127 128#ifdef GSS_USE_MUTEXES 129#define GSS_LOCK_MUTEX(utils) \ 130 if(((sasl_utils_t *)(utils))->mutex_lock(gss_mutex) != 0) { \ 131 return SASL_FAIL; \ 132 } 133 134#define GSS_UNLOCK_MUTEX(utils) \ 135 if(((sasl_utils_t *)(utils))->mutex_unlock(gss_mutex) != 0) { \ 136 return SASL_FAIL; \ 137 } 138 139static void *gss_mutex = NULL; 140#else 141#define GSS_LOCK_MUTEX(utils) 142#define GSS_UNLOCK_MUTEX(utils) 143#endif 144 145typedef struct context { 146 int state; 147 148 gss_ctx_id_t gss_ctx; 149 gss_name_t client_name; 150 gss_name_t server_name; 151 gss_cred_id_t server_creds; 152 gss_cred_id_t client_creds; 153 154 sasl_ssf_t limitssf, requiressf; /* application defined bounds, for the 155 server */ 156 const sasl_utils_t *utils; 157 158 /* layers buffering */ 159 decode_context_t decode_context; 160 161 unsigned char *encode_buf; /* For encoding/decoding mem management */ 162 unsigned char *decode_buf; 163 unsigned char *decode_once_buf; 164 unsigned encode_buf_len; 165 unsigned decode_buf_len; 166 unsigned decode_once_buf_len; 167 buffer_info_t *enc_in_buf; 168 169 unsigned char *out_buf; /* per-step mem management */ 170 unsigned out_buf_len; 171 172 char *authid; /* hold the authid between steps - server */ 173 const char *user; /* hold the userid between steps - client */ 174} context_t; 175 176 177typedef struct authdata_info { 178 uint32_t structID; // always 0xFFD5AA96 179 uint32_t version; // 1 180 uint32_t length; 181 void *data; 182 char *realm; 183} authdata_info; 184 185 186enum { 187 SASL_GSSAPI_STATE_AUTHNEG = 1, 188 SASL_GSSAPI_STATE_SSFCAP = 2, 189 SASL_GSSAPI_STATE_SSFREQ = 3, 190 SASL_GSSAPI_STATE_AUTHENTICATED = 4 191}; 192 193/* sasl_gss_log: only logs status string returned from gss_display_status() */ 194#define sasl_gss_log(x,y,z) sasl_gss_seterror_(x,y,z,1) 195#define sasl_gss_seterror(x,y,z) sasl_gss_seterror_(x,y,z,0) 196 197static int 198sasl_gss_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min, 199 int logonly) 200{ 201 OM_uint32 maj_stat, min_stat; 202 gss_buffer_desc msg; 203 OM_uint32 msg_ctx; 204 int ret; 205 unsigned char *out = NULL; 206 size_t len = 0; 207 unsigned int curlen = 0; 208 const char prefix[] = "GSSAPI Error: "; 209 210 if(!utils) 211 return SASL_FAIL; 212 213 len = strlen(prefix); 214 ret = _plug_buf_alloc(utils, &out, &curlen, 256); 215 if(ret != SASL_OK) return SASL_OK; 216 217 strcpy((char *)out, prefix); 218 219 msg_ctx = 0; 220 while (1) { 221 GSS_LOCK_MUTEX(utils); 222 maj_stat = gss_display_status(&min_stat, maj, 223 GSS_C_GSS_CODE, GSS_C_NULL_OID, 224 &msg_ctx, &msg); 225 GSS_UNLOCK_MUTEX(utils); 226 227 if(GSS_ERROR(maj_stat)) { 228 if (logonly) { 229 utils->log(utils->conn, SASL_LOG_FAIL, 230 "GSSAPI Failure: (could not get major error message)"); 231 } else { 232 utils->seterror(utils->conn, 0, 233 "GSSAPI Failure " 234 "(could not get major error message)"); 235 } 236 utils->free(out); 237 return SASL_OK; 238 } 239 240 len += msg.length; 241 ret = _plug_buf_alloc(utils, &out, &curlen, len); 242 243 if(ret != SASL_OK) { 244 utils->free(out); 245 return SASL_OK; 246 } 247 248 memcpy(out + strlen(out), msg.value, msg.length); 249 out[len-1] = '\0'; 250 251 GSS_LOCK_MUTEX(utils); 252 gss_release_buffer(&min_stat, &msg); 253 GSS_UNLOCK_MUTEX(utils); 254 255 if (!msg_ctx) 256 break; 257 } 258 259 /* Now get the minor status */ 260 261 len += 2; 262 ret = _plug_buf_alloc(utils, &out, &curlen, len); 263 if(ret != SASL_OK) { 264 utils->free(out); 265 return SASL_NOMEM; 266 } 267 268 strcat((char *)out, " ("); 269 270 msg_ctx = 0; 271 while (1) { 272 GSS_LOCK_MUTEX(utils); 273 maj_stat = gss_display_status(&min_stat, min, 274 GSS_C_MECH_CODE, GSS_C_NULL_OID, 275 &msg_ctx, &msg); 276 GSS_UNLOCK_MUTEX(utils); 277 278 if(GSS_ERROR(maj_stat)) { 279 if (logonly) { 280 utils->log(utils->conn, SASL_LOG_FAIL, 281 "GSSAPI Failure: (could not get minor error message)"); 282 } else { 283 utils->seterror(utils->conn, 0, 284 "GSSAPI Failure " 285 "(could not get minor error message)"); 286 } 287 utils->free(out); 288 return SASL_OK; 289 } 290 291 len += msg.length; 292 293 ret = _plug_buf_alloc(utils, &out, &curlen, len); 294 if(ret != SASL_OK) { 295 utils->free(out); 296 return SASL_NOMEM; 297 } 298 299 memcpy(out + strlen(out), msg.value, msg.length); 300 out[len-1] = '\0'; 301 302 GSS_LOCK_MUTEX(utils); 303 gss_release_buffer(&min_stat, &msg); 304 GSS_UNLOCK_MUTEX(utils); 305 306 if (!msg_ctx) 307 break; 308 } 309 310 len += 1; 311 ret = _plug_buf_alloc(utils, &out, &curlen, len); 312 if(ret != SASL_OK) { 313 utils->free(out); 314 return SASL_NOMEM; 315 } 316 317 strcat((char *)out, ")"); 318 319 if (logonly) { 320 utils->log(utils->conn, SASL_LOG_FAIL, (char *)out); 321 } else { 322 utils->seterror(utils->conn, 0, (char *)out); 323 } 324 utils->free(out); 325 326 return SASL_OK; 327} 328 329static int 330sasl_gss_encode(void *context, const struct iovec *invec, unsigned numiov, 331 const char **output, unsigned *outputlen, int privacy) 332{ 333 context_t *text = (context_t *)context; 334 OM_uint32 maj_stat, min_stat; 335 gss_buffer_t input_token, output_token; 336 gss_buffer_desc real_input_token, real_output_token; 337 int ret; 338 struct buffer_info *inblob, bufinfo; 339 340 if(!output) return SASL_BADPARAM; 341 342 if(numiov > 1) { 343 ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf); 344 if(ret != SASL_OK) return ret; 345 inblob = text->enc_in_buf; 346 } else { 347 bufinfo.data = invec[0].iov_base; 348 bufinfo.curlen = invec[0].iov_len; 349 inblob = &bufinfo; 350 } 351 352 if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) return SASL_NOTDONE; 353 354 input_token = &real_input_token; 355 356 real_input_token.value = inblob->data; 357 real_input_token.length = inblob->curlen; 358 359 output_token = &real_output_token; 360 output_token->value = NULL; 361 output_token->length = 0; 362 363 GSS_LOCK_MUTEX(text->utils); 364 maj_stat = gss_wrap (&min_stat, 365 text->gss_ctx, 366 privacy, 367 GSS_C_QOP_DEFAULT, 368 input_token, 369 NULL, 370 output_token); 371 GSS_UNLOCK_MUTEX(text->utils); 372 373 if (GSS_ERROR(maj_stat)) 374 { 375 sasl_gss_seterror(text->utils, maj_stat, min_stat); 376 if (output_token->value) { 377 GSS_LOCK_MUTEX(text->utils); 378 gss_release_buffer(&min_stat, output_token); 379 GSS_UNLOCK_MUTEX(text->utils); 380 } 381 return SASL_FAIL; 382 } 383 384 if (output_token->value && output) { 385 int len; 386 387 ret = _plug_buf_alloc(text->utils, &(text->encode_buf), 388 &(text->encode_buf_len), output_token->length + 4); 389 390 if (ret != SASL_OK) { 391 GSS_LOCK_MUTEX(text->utils); 392 gss_release_buffer(&min_stat, output_token); 393 GSS_UNLOCK_MUTEX(text->utils); 394 return ret; 395 } 396 397 len = htonl(output_token->length); 398 memcpy(text->encode_buf, &len, 4); 399 memcpy(text->encode_buf + 4, output_token->value, output_token->length); 400 } 401 402 if (outputlen) { 403 *outputlen = output_token->length + 4; 404 } 405 406 *output = (const char *)text->encode_buf; 407 408 if (output_token->value) { 409 GSS_LOCK_MUTEX(text->utils); 410 gss_release_buffer(&min_stat, output_token); 411 GSS_UNLOCK_MUTEX(text->utils); 412 } 413 return SASL_OK; 414} 415 416static int gssapi_privacy_encode(void *context, const struct iovec *invec, 417 unsigned numiov, const char **output, 418 unsigned *outputlen) 419{ 420 return sasl_gss_encode(context, invec, numiov, output, outputlen, 1); 421} 422 423static int gssapi_integrity_encode(void *context, const struct iovec *invec, 424 unsigned numiov, const char **output, 425 unsigned *outputlen) 426{ 427 return sasl_gss_encode(context, invec, numiov, output, outputlen, 0); 428} 429 430static int gssapi_decode_packet(void *context, 431 const unsigned char *input, unsigned inputlen, 432 unsigned char **output, unsigned *outputlen) 433{ 434 context_t *text = (context_t *) context; 435 OM_uint32 maj_stat, min_stat; 436 gss_buffer_t input_token, output_token; 437 gss_buffer_desc real_input_token, real_output_token; 438 int result; 439 440 if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) { 441 SETERROR(text->utils, "GSSAPI Failure"); 442 return SASL_NOTDONE; 443 } 444 445 input_token = &real_input_token; 446 real_input_token.value = (char *) input; 447 real_input_token.length = inputlen; 448 449 output_token = &real_output_token; 450 output_token->value = NULL; 451 output_token->length = 0; 452 453 GSS_LOCK_MUTEX(text->utils); 454 maj_stat = gss_unwrap (&min_stat, 455 text->gss_ctx, 456 input_token, 457 output_token, 458 NULL, 459 NULL); 460 GSS_UNLOCK_MUTEX(text->utils); 461 462 if (GSS_ERROR(maj_stat)) 463 { 464 sasl_gss_seterror(text->utils,maj_stat,min_stat); 465 if (output_token->value) { 466 GSS_LOCK_MUTEX(text->utils); 467 gss_release_buffer(&min_stat, output_token); 468 GSS_UNLOCK_MUTEX(text->utils); 469 } 470 return SASL_FAIL; 471 } 472 473 if (outputlen) 474 *outputlen = output_token->length; 475 476 if (output_token->value) { 477 if (output) { 478 result = _plug_buf_alloc(text->utils, &text->decode_once_buf, 479 &text->decode_once_buf_len, 480 *outputlen); 481 if(result != SASL_OK) { 482 GSS_LOCK_MUTEX(text->utils); 483 gss_release_buffer(&min_stat, output_token); 484 GSS_UNLOCK_MUTEX(text->utils); 485 return result; 486 } 487 *output = text->decode_once_buf; 488 memcpy(*output, output_token->value, *outputlen); 489 } 490 GSS_LOCK_MUTEX(text->utils); 491 gss_release_buffer(&min_stat, output_token); 492 GSS_UNLOCK_MUTEX(text->utils); 493 } 494 495 return SASL_OK; 496} 497 498static int gssapi_decode(void *context, 499 const char *input, unsigned inputlen, 500 const char **output, unsigned *outputlen) 501{ 502 context_t *text = (context_t *) context; 503 int ret; 504 505 ret = _plug_decode(&text->decode_context, (const unsigned char *)input, inputlen, 506 &text->decode_buf, &text->decode_buf_len, outputlen, 507 gssapi_decode_packet, text); 508 509 *output = (const char *)text->decode_buf; 510 511 return ret; 512} 513 514static context_t *sasl_gss_new_context(const sasl_utils_t *utils) 515{ 516 context_t *ret; 517 518 ret = utils->malloc(sizeof(context_t)); 519 if(!ret) return NULL; 520 521 memset(ret,0,sizeof(context_t)); 522 ret->utils = utils; 523 524 return ret; 525} 526 527static int sasl_gss_free_context_contents(context_t *text) 528{ 529 OM_uint32 maj_stat, min_stat; 530 531 if (!text) return SASL_OK; 532 533 GSS_LOCK_MUTEX(text->utils); 534 535 if (text->gss_ctx != GSS_C_NO_CONTEXT) { 536 maj_stat = gss_delete_sec_context(&min_stat,&text->gss_ctx, 537 GSS_C_NO_BUFFER); 538 text->gss_ctx = GSS_C_NO_CONTEXT; 539 } 540 541 if (text->client_name != GSS_C_NO_NAME) { 542 maj_stat = gss_release_name(&min_stat,&text->client_name); 543 text->client_name = GSS_C_NO_NAME; 544 } 545 546 if (text->server_name != GSS_C_NO_NAME) { 547 maj_stat = gss_release_name(&min_stat,&text->server_name); 548 text->server_name = GSS_C_NO_NAME; 549 } 550 551 if ( text->server_creds != GSS_C_NO_CREDENTIAL) { 552 maj_stat = gss_release_cred(&min_stat, &text->server_creds); 553 text->server_creds = GSS_C_NO_CREDENTIAL; 554 } 555 556 if ( text->client_creds != GSS_C_NO_CREDENTIAL) { 557 maj_stat = gss_release_cred(&min_stat, &text->client_creds); 558 text->client_creds = GSS_C_NO_CREDENTIAL; 559 } 560 561 GSS_UNLOCK_MUTEX(text->utils); 562 563 if (text->out_buf) { 564 text->utils->free(text->out_buf); 565 text->out_buf = NULL; 566 } 567 568 if (text->encode_buf) { 569 text->utils->free(text->encode_buf); 570 text->encode_buf = NULL; 571 } 572 573 if (text->decode_buf) { 574 text->utils->free(text->decode_buf); 575 text->decode_buf = NULL; 576 } 577 578 if (text->decode_once_buf) { 579 text->utils->free(text->decode_once_buf); 580 text->decode_once_buf = NULL; 581 } 582 583 if (text->enc_in_buf) { 584 if(text->enc_in_buf->data) text->utils->free(text->enc_in_buf->data); 585 text->utils->free(text->enc_in_buf); 586 text->enc_in_buf = NULL; 587 } 588 589 _plug_decode_free(&text->decode_context); 590 591 if (text->authid) { /* works for both client and server */ 592 text->utils->free(text->authid); 593 text->authid = NULL; 594 } 595 596 return SASL_OK; 597 598} 599 600static void gssapi_common_mech_dispose(void *conn_context, 601 const sasl_utils_t *utils) 602{ 603 sasl_gss_free_context_contents((context_t *)(conn_context)); 604 utils->free(conn_context); 605} 606 607static void gssapi_common_mech_free(void *global_context __attribute__((unused)), 608 const sasl_utils_t *utils) 609{ 610#ifdef GSS_USE_MUTEXES 611 if (gss_mutex) { 612 utils->mutex_free(gss_mutex); 613 gss_mutex=NULL; 614 } 615#endif 616} 617 618/***************************** Server Section *****************************/ 619 620static int 621gssapi_server_mech_new(void *glob_context __attribute__((unused)), 622 sasl_server_params_t *params, 623 const char *challenge __attribute__((unused)), 624 unsigned challen __attribute__((unused)), 625 void **conn_context) 626{ 627 context_t *text; 628 629 text = sasl_gss_new_context(params->utils); 630 if (text == NULL) { 631 MEMERROR(params->utils); 632 return SASL_NOMEM; 633 } 634 635 text->gss_ctx = GSS_C_NO_CONTEXT; 636 text->client_name = GSS_C_NO_NAME; 637 text->server_name = GSS_C_NO_NAME; 638 text->server_creds = GSS_C_NO_CREDENTIAL; 639 text->client_creds = GSS_C_NO_CREDENTIAL; 640 text->state = SASL_GSSAPI_STATE_AUTHNEG; 641 642 *conn_context = text; 643 644 return SASL_OK; 645} 646 647static int 648gssapi_server_mech_step(void *conn_context, 649 sasl_server_params_t *params, 650 const char *clientin, 651 unsigned clientinlen, 652 const char **serverout, 653 unsigned *serveroutlen, 654 sasl_out_params_t *oparams) 655{ 656 context_t *text = (context_t *)conn_context; 657 gss_buffer_t input_token, output_token; 658 gss_buffer_desc real_input_token, real_output_token; 659 OM_uint32 maj_stat = 0, min_stat = 0; 660 OM_uint32 max_input; 661 gss_buffer_desc name_token; 662 int ret = 0; 663 uint32_t out_flags = 0 ; 664 665 input_token = &real_input_token; 666 output_token = &real_output_token; 667 output_token->value = NULL; output_token->length = 0; 668 input_token->value = NULL; input_token->length = 0; 669 670 if(!serverout) { 671 PARAMERROR(text->utils); 672 return SASL_BADPARAM; 673 } 674 675 *serverout = NULL; 676 *serveroutlen = 0; 677 678 switch (text->state) { 679 680 case SASL_GSSAPI_STATE_AUTHNEG: 681#if 0 /* Disabling this code is the fix for <rdar://problem/8933333> */ 682 if (text->server_name == GSS_C_NO_NAME) { /* only once */ 683 name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN); 684 name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char)); 685 if (name_token.value == NULL) { 686 MEMERROR(text->utils); 687 sasl_gss_free_context_contents(text); 688 return SASL_NOMEM; 689 } 690 sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN); 691 692 GSS_LOCK_MUTEX(params->utils); 693 maj_stat = gss_import_name (&min_stat, 694 &name_token, 695 GSS_C_NT_HOSTBASED_SERVICE, 696 &text->server_name); 697 GSS_UNLOCK_MUTEX(params->utils); 698 699 params->utils->free(name_token.value); 700 name_token.value = NULL; 701 702 if (GSS_ERROR(maj_stat)) { 703 sasl_gss_seterror(text->utils, maj_stat, min_stat); 704 sasl_gss_free_context_contents(text); 705 return SASL_FAIL; 706 } 707 708 if ( text->server_creds != GSS_C_NO_CREDENTIAL) { 709 GSS_LOCK_MUTEX(params->utils); 710 maj_stat = gss_release_cred(&min_stat, &text->server_creds); 711 GSS_UNLOCK_MUTEX(params->utils); 712 text->server_creds = GSS_C_NO_CREDENTIAL; 713 } 714 715 GSS_LOCK_MUTEX(params->utils); 716 maj_stat = gss_acquire_cred(&min_stat, 717 text->server_name, 718 GSS_C_INDEFINITE, 719 GSS_C_NO_OID_SET, 720 GSS_C_ACCEPT, 721 &text->server_creds, 722 NULL, 723 NULL); 724 GSS_UNLOCK_MUTEX(params->utils); 725 726 if (GSS_ERROR(maj_stat)) { 727 sasl_gss_seterror(text->utils, maj_stat, min_stat); 728 sasl_gss_free_context_contents(text); 729 return SASL_FAIL; 730 } 731 } 732#endif /* <rdar://problem/8933333> */ 733 734 if (clientinlen) { 735 real_input_token.value = (void *)clientin; 736 real_input_token.length = clientinlen; 737 } 738 739 740 GSS_LOCK_MUTEX(params->utils); 741 maj_stat = 742 gss_accept_sec_context(&min_stat, 743 &(text->gss_ctx), 744 text->server_creds, 745 input_token, 746 GSS_C_NO_CHANNEL_BINDINGS, 747 &text->client_name, 748 NULL, 749 output_token, 750 &out_flags, 751 NULL, 752 &(text->client_creds)); 753 GSS_UNLOCK_MUTEX(params->utils); 754 755 if (GSS_ERROR(maj_stat)) { 756 sasl_gss_log(text->utils, maj_stat, min_stat); 757 text->utils->seterror(text->utils->conn, SASL_NOLOG, "GSSAPI Failure: gss_accept_sec_context"); 758 if (output_token->value) { 759 GSS_LOCK_MUTEX(params->utils); 760 gss_release_buffer(&min_stat, output_token); 761 GSS_UNLOCK_MUTEX(params->utils); 762 } 763 sasl_gss_free_context_contents(text); 764 return SASL_BADAUTH; 765 } 766 if ( maj_stat == GSS_S_COMPLETE ) 767 { 768 void *some_lucid_ctx = NULL; 769 apple_gss_krb5_authdata_if_relevant *key; 770 uint32_t vers; 771 authdata_info *authdataInfoPtr; 772 773 min_stat = 0; 774 maj_stat = apple_gss_krb5_export_authdata_if_relevant_context(&min_stat, &(text->gss_ctx), 1, &some_lucid_ctx); 775 if (maj_stat == GSS_S_COMPLETE) { 776 if(some_lucid_ctx != NULL) { 777 key = (apple_gss_krb5_authdata_if_relevant*)some_lucid_ctx; 778 779 // put data in property 780 authdataInfoPtr = (authdata_info *) calloc( 1, sizeof(authdata_info) ); 781 if ( authdataInfoPtr == NULL ) 782 return SASL_NOMEM; 783 784 authdataInfoPtr->structID = 0xFFD5AA96; 785 authdataInfoPtr->version = 1; 786 authdataInfoPtr->length = key->length; 787 authdataInfoPtr->data = key->data; 788 789 oparams->spare_ptr3 = authdataInfoPtr; 790 } 791 } 792 else { 793 text->utils->seterror(text->utils->conn, SASL_LOG_WARN, "apple_gss_krb5_export_authdata_if_relevant_context"); 794 return SASL_BADPARAM; 795 } 796 if(some_lucid_ctx) { 797 apple_gss_krb5_free_authdata_if_relevant(&min_stat, some_lucid_ctx); 798 } 799 } 800 if ((params->props.security_flags & SASL_SEC_PASS_CREDENTIALS) && 801 (!(out_flags & GSS_C_DELEG_FLAG) || 802 text->client_creds == GSS_C_NO_CREDENTIAL) ) 803 { 804 text->utils->seterror(text->utils->conn, SASL_LOG_WARN, 805 "GSSAPI warning: no credentials were passed"); 806 /* continue with authentication */ 807 } 808 809 if (serveroutlen) 810 *serveroutlen = output_token->length; 811 if (output_token->value) { 812 if (serverout) { 813 ret = _plug_buf_alloc(text->utils, &(text->out_buf), 814 &(text->out_buf_len), *serveroutlen); 815 if(ret != SASL_OK) { 816 GSS_LOCK_MUTEX(params->utils); 817 gss_release_buffer(&min_stat, output_token); 818 GSS_UNLOCK_MUTEX(params->utils); 819 return ret; 820 } 821 memcpy(text->out_buf, output_token->value, *serveroutlen); 822 *serverout = (char *)text->out_buf; 823 } 824 825 GSS_LOCK_MUTEX(params->utils); 826 gss_release_buffer(&min_stat, output_token); 827 GSS_UNLOCK_MUTEX(params->utils); 828 } else { 829 /* No output token, send an empty string */ 830 *serverout = GSSAPI_BLANK_STRING; 831 serveroutlen = 0; 832 } 833 834 if (maj_stat == GSS_S_COMPLETE) { 835 /* Switch to ssf negotiation */ 836 text->state = SASL_GSSAPI_STATE_SSFCAP; 837 } 838 839 return SASL_CONTINUE; 840 841 case SASL_GSSAPI_STATE_SSFCAP: { 842 unsigned char sasldata[4]; 843 gss_buffer_desc name_token; 844 gss_buffer_desc name_without_realm; 845 char *realm_prefix = NULL; 846 gss_name_t without = NULL; 847 int equal; 848 849 name_token.value = NULL; 850 name_without_realm.value = NULL; 851 852 /* We ignore whatever the client sent us at this stage */ 853 854 GSS_LOCK_MUTEX(params->utils); 855 maj_stat = gss_display_name (&min_stat, 856 text->client_name, 857 &name_token, 858 NULL); 859 GSS_UNLOCK_MUTEX(params->utils); 860 861 if (GSS_ERROR(maj_stat)) { 862 if (without) { 863 GSS_LOCK_MUTEX(params->utils); 864 gss_release_name(&min_stat, &without); 865 GSS_UNLOCK_MUTEX(params->utils); 866 } 867 SETERROR(text->utils, "GSSAPI Failure"); 868 sasl_gss_free_context_contents(text); 869 return SASL_BADAUTH; 870 } 871 872 /* If the id contains a realm get the identifier for the user 873 without the realm and see if it's the same id (i.e. 874 tmartin == tmartin@ANDREW.CMU.EDU. If this is the case we just want 875 to return the id (i.e. just "tmartin" */ 876 if (strchr((char *) name_token.value, (int) '@') != NULL) { 877 /* NOTE: libc malloc, as it is freed below by a gssapi internal 878 * function! */ 879 name_without_realm.value = params->utils->malloc(strlen(name_token.value)+1); 880 if (name_without_realm.value == NULL) { 881 if (name_token.value) { 882 GSS_LOCK_MUTEX(params->utils); 883 gss_release_buffer(&min_stat, &name_token); 884 GSS_UNLOCK_MUTEX(params->utils); 885 } 886 MEMERROR(text->utils); 887 return SASL_NOMEM; 888 } 889 890 strcpy(name_without_realm.value, name_token.value); 891 892 /* cut off string at '@' */ 893 (strchr(name_without_realm.value,'@'))[0] = '\0'; 894 895 name_without_realm.length = strlen( (char *) name_without_realm.value ); 896 897 if ((realm_prefix = strchr(name_token.value, '@')) != '\0') { 898 if (oparams->spare_ptr3) { 899 authdata_info *authdataInfoPtr = (authdata_info *)oparams->spare_ptr3; 900 authdataInfoPtr->realm = params->utils->malloc(strlen(name_token.value)+1); 901 strcpy(authdataInfoPtr->realm, realm_prefix+1); 902 } 903 } 904 905 GSS_LOCK_MUTEX(params->utils); 906 maj_stat = gss_import_name (&min_stat, 907 &name_without_realm, 908 /* Solaris 8/9 gss_import_name doesn't accept GSS_C_NULL_OID here, 909 so use GSS_C_NT_USER_NAME instead if available. */ 910#ifdef HAVE_GSS_C_NT_USER_NAME 911 GSS_C_NT_USER_NAME, 912#else 913 GSS_C_NULL_OID, 914#endif 915 &without); 916 GSS_UNLOCK_MUTEX(params->utils); 917 918 if (GSS_ERROR(maj_stat)) { 919 params->utils->free(name_without_realm.value); 920 if (name_token.value) { 921 GSS_LOCK_MUTEX(params->utils); 922 gss_release_buffer(&min_stat, &name_token); 923 GSS_UNLOCK_MUTEX(params->utils); 924 } 925 SETERROR(text->utils, "GSSAPI Failure"); 926 sasl_gss_free_context_contents(text); 927 return SASL_BADAUTH; 928 } 929 930 GSS_LOCK_MUTEX(params->utils); 931 maj_stat = gss_compare_name(&min_stat, 932 text->client_name, 933 without, 934 &equal); 935 GSS_UNLOCK_MUTEX(params->utils); 936 937 if (GSS_ERROR(maj_stat)) { 938 params->utils->free(name_without_realm.value); 939 if (name_token.value) { 940 GSS_LOCK_MUTEX(params->utils); 941 gss_release_buffer(&min_stat, &name_token); 942 GSS_UNLOCK_MUTEX(params->utils); 943 } 944 if (without) { 945 GSS_LOCK_MUTEX(params->utils); 946 gss_release_name(&min_stat, &without); 947 GSS_UNLOCK_MUTEX(params->utils); 948 } 949 SETERROR(text->utils, "GSSAPI Failure"); 950 sasl_gss_free_context_contents(text); 951 return SASL_BADAUTH; 952 } 953 954 GSS_LOCK_MUTEX(params->utils); 955 gss_release_name(&min_stat,&without); 956 GSS_UNLOCK_MUTEX(params->utils); 957 958 } else { 959 equal = 0; 960 } 961 962 if (equal) { 963 text->authid = strdup(name_without_realm.value); 964 965 if (text->authid == NULL) { 966 MEMERROR(params->utils); 967 return SASL_NOMEM; 968 } 969 } else { 970 text->authid = strdup(name_token.value); 971 972 if (text->authid == NULL) { 973 MEMERROR(params->utils); 974 return SASL_NOMEM; 975 } 976 } 977 978 if (name_token.value) { 979 GSS_LOCK_MUTEX(params->utils); 980 gss_release_buffer(&min_stat, &name_token); 981 GSS_UNLOCK_MUTEX(params->utils); 982 } 983 if (name_without_realm.value) { 984 params->utils->free(name_without_realm.value); 985 } 986 987 /* we have to decide what sort of encryption/integrity/etc., 988 we support */ 989 if (params->props.max_ssf < params->external_ssf) { 990 text->limitssf = 0; 991 } else { 992 text->limitssf = params->props.max_ssf - params->external_ssf; 993 } 994 if (params->props.min_ssf < params->external_ssf) { 995 text->requiressf = 0; 996 } else { 997 text->requiressf = params->props.min_ssf - params->external_ssf; 998 } 999 1000 /* build up our security properties token */ 1001 if (params->props.maxbufsize > 0xFFFFFF) { 1002 /* make sure maxbufsize isn't too large */ 1003 /* maxbufsize = 0xFFFFFF */ 1004 sasldata[1] = sasldata[2] = sasldata[3] = 0xFF; 1005 } else { 1006 sasldata[1] = (params->props.maxbufsize >> 16) & 0xFF; 1007 sasldata[2] = (params->props.maxbufsize >> 8) & 0xFF; 1008 sasldata[3] = (params->props.maxbufsize >> 0) & 0xFF; 1009 } 1010 sasldata[0] = 0; 1011 if(text->requiressf != 0 && !params->props.maxbufsize) { 1012 params->utils->seterror(params->utils->conn, 0, 1013 "GSSAPI needs a security layer but one is forbidden"); 1014 return SASL_TOOWEAK; 1015 } 1016 1017 if (text->requiressf == 0) { 1018 sasldata[0] |= 1; /* authentication */ 1019 } 1020 if (text->requiressf <= 1 && text->limitssf >= 1 1021 && params->props.maxbufsize) { 1022 sasldata[0] |= 2; 1023 } 1024 if (text->requiressf <= K5_MAX_SSF && text->limitssf >= K5_MAX_SSF 1025 && params->props.maxbufsize) { 1026 sasldata[0] |= 4; 1027 } 1028 1029 real_input_token.value = (void *)sasldata; 1030 real_input_token.length = 4; 1031 1032 GSS_LOCK_MUTEX(params->utils); 1033 maj_stat = gss_wrap(&min_stat, 1034 text->gss_ctx, 1035 0, /* Just integrity checking here */ 1036 GSS_C_QOP_DEFAULT, 1037 input_token, 1038 NULL, 1039 output_token); 1040 GSS_UNLOCK_MUTEX(params->utils); 1041 1042 if (GSS_ERROR(maj_stat)) { 1043 sasl_gss_seterror(text->utils, maj_stat, min_stat); 1044 if (output_token->value) { 1045 GSS_LOCK_MUTEX(params->utils); 1046 gss_release_buffer(&min_stat, output_token); 1047 GSS_UNLOCK_MUTEX(params->utils); 1048 } 1049 sasl_gss_free_context_contents(text); 1050 return SASL_FAIL; 1051 } 1052 1053 1054 if (serveroutlen) 1055 *serveroutlen = output_token->length; 1056 if (output_token->value) { 1057 if (serverout) { 1058 ret = _plug_buf_alloc(text->utils, &(text->out_buf), 1059 &(text->out_buf_len), *serveroutlen); 1060 if(ret != SASL_OK) { 1061 GSS_LOCK_MUTEX(params->utils); 1062 gss_release_buffer(&min_stat, output_token); 1063 GSS_UNLOCK_MUTEX(params->utils); 1064 return ret; 1065 } 1066 memcpy(text->out_buf, output_token->value, *serveroutlen); 1067 *serverout = (char *)text->out_buf; 1068 } 1069 1070 GSS_LOCK_MUTEX(params->utils); 1071 gss_release_buffer(&min_stat, output_token); 1072 GSS_UNLOCK_MUTEX(params->utils); 1073 } 1074 1075 /* Wait for ssf request and authid */ 1076 text->state = SASL_GSSAPI_STATE_SSFREQ; 1077 1078 return SASL_CONTINUE; 1079 } 1080 1081 case SASL_GSSAPI_STATE_SSFREQ: { 1082 int layerchoice; 1083 1084 real_input_token.value = (void *)clientin; 1085 real_input_token.length = clientinlen; 1086 1087 GSS_LOCK_MUTEX(params->utils); 1088 maj_stat = gss_unwrap(&min_stat, 1089 text->gss_ctx, 1090 input_token, 1091 output_token, 1092 NULL, 1093 NULL); 1094 GSS_UNLOCK_MUTEX(params->utils); 1095 1096 if (GSS_ERROR(maj_stat)) { 1097 sasl_gss_seterror(text->utils, maj_stat, min_stat); 1098 sasl_gss_free_context_contents(text); 1099 return SASL_FAIL; 1100 } 1101 1102 layerchoice = (int)(((char *)(output_token->value))[0]); 1103 if (layerchoice == 1 && text->requiressf == 0) { /* no encryption */ 1104 oparams->encode = NULL; 1105 oparams->decode = NULL; 1106 oparams->mech_ssf = 0; 1107 } else if (layerchoice == 2 && text->requiressf <= 1 && 1108 text->limitssf >= 1) { /* integrity */ 1109 oparams->encode = &gssapi_integrity_encode; 1110 oparams->decode = &gssapi_decode; 1111 oparams->mech_ssf=1; 1112 } else if (layerchoice == 4 && text->requiressf <= K5_MAX_SSF && 1113 text->limitssf >= K5_MAX_SSF) { /* privacy */ 1114 oparams->encode = &gssapi_privacy_encode; 1115 oparams->decode = &gssapi_decode; 1116 /* FIX ME: Need to extract the proper value here */ 1117 oparams->mech_ssf = K5_MAX_SSF; 1118 } else { 1119 /* not a supported encryption layer */ 1120 SETERROR(text->utils, 1121 "protocol violation: client requested invalid layer"); 1122 /* Mark that we attempted negotiation */ 1123 oparams->mech_ssf = 2; 1124 if (output_token->value) { 1125 GSS_LOCK_MUTEX(params->utils); 1126 gss_release_buffer(&min_stat, output_token); 1127 GSS_UNLOCK_MUTEX(params->utils); 1128 } 1129 sasl_gss_free_context_contents(text); 1130 return SASL_FAIL; 1131 } 1132 1133 if (output_token->length > 4) { 1134 int ret; 1135 1136 ret = params->canon_user(params->utils->conn, 1137 ((char *) output_token->value) + 4, 1138 (output_token->length - 4) * sizeof(char), 1139 SASL_CU_AUTHZID, oparams); 1140 1141 if (ret != SASL_OK) { 1142 sasl_gss_free_context_contents(text); 1143 return ret; 1144 } 1145 1146 ret = params->canon_user(params->utils->conn, 1147 text->authid, 1148 0, /* strlen(text->authid) */ 1149 SASL_CU_AUTHID, oparams); 1150 if (ret != SASL_OK) { 1151 sasl_gss_free_context_contents(text); 1152 return ret; 1153 } 1154 } else if(output_token->length == 4) { 1155 /* null authzid */ 1156 int ret; 1157 1158 ret = params->canon_user(params->utils->conn, 1159 text->authid, 1160 0, /* strlen(text->authid) */ 1161 SASL_CU_AUTHZID | SASL_CU_AUTHID, 1162 oparams); 1163 1164 if (ret != SASL_OK) { 1165 sasl_gss_free_context_contents(text); 1166 return ret; 1167 } 1168 } else { 1169 SETERROR(text->utils, 1170 "token too short"); 1171 GSS_LOCK_MUTEX(params->utils); 1172 gss_release_buffer(&min_stat, output_token); 1173 GSS_UNLOCK_MUTEX(params->utils); 1174 sasl_gss_free_context_contents(text); 1175 return SASL_FAIL; 1176 } 1177 1178 /* No matter what, set the rest of the oparams */ 1179 1180 if (text->client_creds != GSS_C_NO_CREDENTIAL) { 1181 oparams->client_creds = &text->client_creds; 1182 } 1183 else { 1184 oparams->client_creds = NULL; 1185 } 1186 1187 oparams->maxoutbuf = 1188 (((unsigned char *) output_token->value)[1] << 16) | 1189 (((unsigned char *) output_token->value)[2] << 8) | 1190 (((unsigned char *) output_token->value)[3] << 0); 1191 1192 if (oparams->mech_ssf) { 1193 maj_stat = gss_wrap_size_limit( &min_stat, 1194 text->gss_ctx, 1195 1, 1196 GSS_C_QOP_DEFAULT, 1197 (OM_uint32) oparams->maxoutbuf, 1198 &max_input); 1199 1200 if(max_input > oparams->maxoutbuf) { 1201 /* Heimdal appears to get this wrong */ 1202 oparams->maxoutbuf -= (max_input - oparams->maxoutbuf); 1203 } else { 1204 /* This code is actually correct */ 1205 oparams->maxoutbuf = max_input; 1206 } 1207 } 1208 1209 GSS_LOCK_MUTEX(params->utils); 1210 gss_release_buffer(&min_stat, output_token); 1211 GSS_UNLOCK_MUTEX(params->utils); 1212 1213 text->state = SASL_GSSAPI_STATE_AUTHENTICATED; 1214 1215 if(oparams && oparams->spare_ptr3) { 1216 authdata_info *authdataInfoPtr = (authdata_info *)oparams->spare_ptr3; 1217 if(authdataInfoPtr->realm) { 1218 free(authdataInfoPtr->realm); 1219 authdataInfoPtr->realm = NULL; 1220 } 1221 free(authdataInfoPtr); 1222 oparams->spare_ptr3 = NULL; 1223 } 1224 1225 /* used by layers */ 1226 _plug_decode_init(&text->decode_context, text->utils, 1227 (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : 1228 params->props.maxbufsize); 1229 1230 oparams->doneflag = 1; 1231 1232 return SASL_OK; 1233 } 1234 1235 default: 1236 params->utils->log(NULL, SASL_LOG_ERR, 1237 "Invalid GSSAPI server step %d\n", text->state); 1238 return SASL_FAIL; 1239 } 1240 1241 return SASL_FAIL; /* should never get here */ 1242} 1243 1244static sasl_server_plug_t gssapi_server_plugins[] = 1245{ 1246 { 1247 "GSSAPI", /* mech_name */ 1248 K5_MAX_SSF, /* max_ssf */ 1249 SASL_SEC_NOPLAINTEXT 1250 | SASL_SEC_NOACTIVE 1251 | SASL_SEC_NOANONYMOUS 1252 | SASL_SEC_MUTUAL_AUTH /* security_flags */ 1253 | SASL_SEC_PASS_CREDENTIALS, 1254 SASL_FEAT_WANT_CLIENT_FIRST 1255 | SASL_FEAT_ALLOWS_PROXY, /* features */ 1256 NULL, /* glob_context */ 1257 &gssapi_server_mech_new, /* mech_new */ 1258 &gssapi_server_mech_step, /* mech_step */ 1259 &gssapi_common_mech_dispose, /* mech_dispose */ 1260 &gssapi_common_mech_free, /* mech_free */ 1261 NULL, /* setpass */ 1262 NULL, /* user_query */ 1263 NULL, /* idle */ 1264 NULL, /* mech_avail */ 1265 NULL /* spare */ 1266 } 1267}; 1268 1269int gssapiv2_server_plug_init( 1270#ifndef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY 1271 const sasl_utils_t *utils __attribute__((unused)), 1272#else 1273 const sasl_utils_t *utils, 1274#endif 1275 int maxversion, 1276 int *out_version, 1277 sasl_server_plug_t **pluglist, 1278 int *plugcount) 1279{ 1280#ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY 1281 const char *keytab = NULL; 1282 char keytab_path[1024]; 1283 unsigned int rl; 1284#endif 1285 1286 if (maxversion < SASL_SERVER_PLUG_VERSION) { 1287 return SASL_BADVERS; 1288 } 1289 1290#ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY 1291 /* unfortunately, we don't check for readability of keytab if it's 1292 the standard one, since we don't know where it is */ 1293 1294 /* FIXME: This code is broken */ 1295 1296 utils->getopt(utils->getopt_context, "GSSAPI", "keytab", &keytab, &rl); 1297 if (keytab != NULL) { 1298 if (access(keytab, R_OK) != 0) { 1299 utils->log(NULL, SASL_LOG_ERR, 1300 "Could not find keytab file: %s: %m", 1301 keytab, errno); 1302 return SASL_FAIL; 1303 } 1304 1305 if(strlen(keytab) > 1024) { 1306 utils->log(NULL, SASL_LOG_ERR, 1307 "path to keytab is > 1024 characters"); 1308 return SASL_BUFOVER; 1309 } 1310 1311 strncpy(keytab_path, keytab, 1024); 1312 1313 gsskrb5_register_acceptor_identity(keytab_path); 1314 } 1315#endif 1316 1317 *out_version = SASL_SERVER_PLUG_VERSION; 1318 *pluglist = gssapi_server_plugins; 1319 *plugcount = 1; 1320 1321#ifdef GSS_USE_MUTEXES 1322 if (!gss_mutex) { 1323 gss_mutex = utils->mutex_alloc(); 1324 if (!gss_mutex) { 1325 return SASL_FAIL; 1326 } 1327 } 1328#endif 1329 1330 return SASL_OK; 1331} 1332 1333/***************************** Client Section *****************************/ 1334 1335static int gssapi_client_mech_new(void *glob_context __attribute__((unused)), 1336 sasl_client_params_t *params, 1337 void **conn_context) 1338{ 1339 context_t *text; 1340 1341 /* holds state are in */ 1342 text = sasl_gss_new_context(params->utils); 1343 if (text == NULL) { 1344 MEMERROR(params->utils); 1345 return SASL_NOMEM; 1346 } 1347 1348 text->state = SASL_GSSAPI_STATE_AUTHNEG; 1349 text->gss_ctx = GSS_C_NO_CONTEXT; 1350 text->client_name = GSS_C_NO_NAME; 1351 text->server_creds = GSS_C_NO_CREDENTIAL; 1352 text->client_creds = GSS_C_NO_CREDENTIAL; 1353 1354 *conn_context = text; 1355 1356 return SASL_OK; 1357} 1358 1359static int gssapi_client_mech_step(void *conn_context, 1360 sasl_client_params_t *params, 1361 const char *serverin, 1362 unsigned serverinlen, 1363 sasl_interact_t **prompt_need, 1364 const char **clientout, 1365 unsigned *clientoutlen, 1366 sasl_out_params_t *oparams) 1367{ 1368 context_t *text = (context_t *)conn_context; 1369 gss_buffer_t input_token, output_token; 1370 gss_buffer_desc real_input_token, real_output_token; 1371 gss_cred_id_t credential; 1372 const void *krb5princ = NULL; 1373 OM_uint32 maj_stat = 0, min_stat = 0; 1374 OM_uint32 max_input; 1375 gss_buffer_desc name_token; 1376 int ret; 1377 OM_uint32 req_flags = 0, out_req_flags = 0; 1378 input_token = &real_input_token; 1379 output_token = &real_output_token; 1380 output_token->value = NULL; 1381 input_token->value = NULL; 1382 input_token->length = 0; 1383 1384 *clientout = NULL; 1385 *clientoutlen = 0; 1386 1387 switch (text->state) { 1388 1389 case SASL_GSSAPI_STATE_AUTHNEG: 1390 /* try to get the userid */ 1391 if (text->user == NULL) { 1392 int user_result = SASL_OK; 1393 1394 user_result = _plug_get_userid(params->utils, &text->user, 1395 prompt_need); 1396 1397 if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) { 1398 sasl_gss_free_context_contents(text); 1399 return user_result; 1400 } 1401 1402 /* free prompts we got */ 1403 if (prompt_need && *prompt_need) { 1404 params->utils->free(*prompt_need); 1405 *prompt_need = NULL; 1406 } 1407 1408 /* if there are prompts not filled in */ 1409 if (user_result == SASL_INTERACT) { 1410 /* make the prompt list */ 1411 int result = 1412 _plug_make_prompts(params->utils, prompt_need, 1413 user_result == SASL_INTERACT ? 1414 "Please enter your authorization name" : NULL, NULL, 1415 NULL, NULL, 1416 NULL, NULL, 1417 NULL, NULL, NULL, 1418 NULL, NULL, NULL); 1419 if (result != SASL_OK) return result; 1420 1421 return SASL_INTERACT; 1422 } 1423 } 1424 1425 if (text->server_name == GSS_C_NO_NAME) { /* only once */ 1426 1427 gss_buffer_desc name_token; 1428 int newMethod = 0; 1429 1430 if (params->serverFQDN == NULL || params->serverFQDN[0] == '\0' ) { 1431 SETERROR(text->utils, "GSSAPI Failure: no serverFQDN"); 1432 return SASL_FAIL; 1433 } 1434 1435 /* look for new GSSAPI property, if so, we set our flag to use the new method */ 1436 if ( params->props.property_names != NULL && params->props.property_values != NULL ) 1437 { 1438 int ii; 1439 for ( ii = 0; params->props.property_names[ii] != NULL; ii++ ) 1440 { 1441 /* if the property_value is non-NULL, we assume it is set */ 1442 if ( strcmp(params->props.property_names[ii], "KRB5-GSSAPI") == 0 1443 && params->props.property_values[ii] != NULL ) { 1444 1445 newMethod = 1; 1446 break; 1447 } else if( strcmp(params->props.property_names[ii], "USE-KRB5-PRINCIPAL") == 0 && params->props.property_values[ii] != NULL ) { 1448 krb5princ = params->props.property_values[ii]; 1449 } 1450 } 1451 } 1452 1453 /* newMethod is forcing Kerberos to use the existing provided FQDN instead of causing 1454 * a reverse lookup */ 1455 if ( newMethod == 1 ) { 1456 1457 krb5_context context = NULL; 1458 krb5_principal servicePrinc = NULL; 1459 char *principalName = NULL; 1460 1461 /* kerberos locks are not needed since Krb 1.5 is thread safe 1462 try kerberos parse first, if that fails allow generic GSS to do it's thing */ 1463 krb5_init_context( &context ); 1464 if (context == NULL) { 1465 SETERROR(text->utils, "GSSAPI Failure: krb5_init_context failed"); 1466 return SASL_FAIL; 1467 } 1468 1469 if (krb5_sname_to_principal( context, params->serverFQDN, params->service, 1470 KRB5_NT_UNKNOWN, &servicePrinc ) != 0) { 1471 krb5_free_context( context ); 1472 SETERROR(text->utils, "GSSAPI Failure: krb5_sname_to_principal failed"); 1473 return SASL_FAIL; 1474 } 1475 1476 /* unparse the name, we'll deal with and error after we clean up */ 1477 krb5_unparse_name( context, servicePrinc, &principalName ); 1478 1479 /* we're done with the principal */ 1480 krb5_free_principal( context, servicePrinc ); 1481 servicePrinc = NULL; 1482 1483 /* we're also done with the context */ 1484 krb5_free_context( context ); 1485 context = NULL; 1486 1487 if (principalName == NULL) { 1488 SETERROR(text->utils, "GSSAPI Failure: krb5_unparse_name failed"); 1489 return SASL_FAIL; 1490 } 1491 1492 name_token.value = principalName; 1493 name_token.length = strlen( principalName ); 1494 1495 GSS_LOCK_MUTEX(params->utils); 1496 maj_stat = gss_import_name( &min_stat, 1497 &name_token, 1498 (gss_OID) GSS_KRB5_NT_PRINCIPAL_NAME, 1499 &text->server_name ); 1500 GSS_UNLOCK_MUTEX(params->utils); 1501 1502 /* we use free here because it's what krb5_unparse requires */ 1503 free( principalName ); 1504 principalName = NULL; 1505 1506 if (GSS_ERROR(maj_stat)) { 1507 sasl_gss_seterror(text->utils, maj_stat, min_stat); 1508 return SASL_FAIL; 1509 } 1510 } else { 1511 if( krb5princ ) { 1512 name_token.value = krb5princ; 1513 name_token.length = sizeof(krb5_principal); 1514 GSS_LOCK_MUTEX(params->utils); 1515 maj_stat = gss_import_name (&min_stat, 1516 &name_token, 1517 (gss_OID)gss_nt_krb5_principal, 1518 &text->server_name); 1519 GSS_UNLOCK_MUTEX(params->utils); 1520 if (GSS_ERROR(maj_stat)) { 1521 sasl_gss_seterror(text->utils, maj_stat, min_stat); 1522 sasl_gss_free_context_contents(text); 1523 return SASL_FAIL; 1524 } 1525 1526 GSS_LOCK_MUTEX(params->utils); 1527 maj_stat = gss_acquire_cred(&min_stat, 1528 text->server_name, 1529 0, 1530 GSS_C_NO_OID_SET, 1531 GSS_C_INITIATE, 1532 &credential, 1533 NULL, 1534 NULL); 1535 GSS_UNLOCK_MUTEX(params->utils); 1536 1537 if (GSS_ERROR(maj_stat)) { 1538 sasl_gss_seterror(text->utils, maj_stat, min_stat); 1539 if (output_token->value) { 1540 GSS_LOCK_MUTEX(params->utils); 1541 gss_release_buffer(&min_stat, output_token); 1542 GSS_UNLOCK_MUTEX(params->utils); 1543 } 1544 sasl_gss_free_context_contents(text); 1545 return SASL_FAIL; 1546 } 1547 } 1548 1549 /* this is the original GSS generic code */ 1550 name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN); 1551 name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char)); 1552 if (name_token.value == NULL) { 1553 sasl_gss_free_context_contents(text); 1554 return SASL_NOMEM; 1555 } 1556 1557 sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN); 1558 1559 GSS_LOCK_MUTEX(params->utils); 1560 maj_stat = gss_import_name (&min_stat, 1561 &name_token, 1562 GSS_C_NT_HOSTBASED_SERVICE, 1563 &text->server_name); 1564 GSS_UNLOCK_MUTEX(params->utils); 1565 1566 params->utils->free(name_token.value); 1567 name_token.value = NULL; 1568 1569 if (GSS_ERROR(maj_stat)) { 1570 sasl_gss_seterror(text->utils, maj_stat, min_stat); 1571 sasl_gss_free_context_contents(text); 1572 return SASL_FAIL; 1573 } 1574 } 1575 } 1576 1577 if (serverinlen == 0) 1578 input_token = GSS_C_NO_BUFFER; 1579 1580 if (serverinlen) { 1581 real_input_token.value = (void *)serverin; 1582 real_input_token.length = serverinlen; 1583 } 1584 else if (text->gss_ctx != GSS_C_NO_CONTEXT ) { 1585 /* This can't happen under GSSAPI: we have a non-null context 1586 * and no input from the server. However, thanks to Imap, 1587 * which discards our first output, this happens all the time. 1588 * Throw away the context and try again. */ 1589 GSS_LOCK_MUTEX(params->utils); 1590 maj_stat = gss_delete_sec_context (&min_stat,&text->gss_ctx,GSS_C_NO_BUFFER); 1591 GSS_UNLOCK_MUTEX(params->utils); 1592 text->gss_ctx = GSS_C_NO_CONTEXT; 1593 } 1594 1595 /* Setup req_flags properly */ 1596 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; 1597 if(params->props.max_ssf > params->external_ssf) { 1598 /* We are requesting a security layer */ 1599 req_flags |= GSS_C_INTEG_FLAG; 1600 /* Any SSF bigger than 1 is confidentiality. */ 1601 /* Let's check if the client of the API requires confidentiality, 1602 and it wasn't already provided by an external layer */ 1603 if(params->props.max_ssf - params->external_ssf > 1) { 1604 /* We want to try for privacy */ 1605 req_flags |= GSS_C_CONF_FLAG; 1606 } 1607 } 1608 1609 if (params->props.security_flags & SASL_SEC_PASS_CREDENTIALS) 1610 req_flags = req_flags | GSS_C_DELEG_FLAG; 1611 1612 if( krb5princ ) { 1613 GSS_LOCK_MUTEX(params->utils); 1614 maj_stat = gss_init_sec_context(&min_stat, 1615 credential, 1616 &text->gss_ctx, 1617 text->server_name, 1618 GSS_C_NO_OID, 1619 req_flags, 1620 0, 1621 GSS_C_NO_CHANNEL_BINDINGS, 1622 input_token, 1623 NULL, 1624 output_token, 1625 &out_req_flags, 1626 NULL); 1627 GSS_UNLOCK_MUTEX(params->utils); 1628 1629 if (GSS_ERROR(maj_stat)) { 1630 sasl_gss_seterror(text->utils, maj_stat, min_stat); 1631 if (output_token->value) { 1632 GSS_LOCK_MUTEX(params->utils); 1633 gss_release_buffer(&min_stat, output_token); 1634 GSS_UNLOCK_MUTEX(params->utils); 1635 } 1636 sasl_gss_free_context_contents(text); 1637 return SASL_FAIL; 1638 } 1639 1640 } else { 1641 GSS_LOCK_MUTEX(params->utils); 1642 maj_stat = gss_init_sec_context(&min_stat, 1643 GSS_C_NO_CREDENTIAL, 1644 &text->gss_ctx, 1645 text->server_name, 1646 GSS_C_NO_OID, 1647 req_flags, 1648 0, 1649 GSS_C_NO_CHANNEL_BINDINGS, 1650 input_token, 1651 NULL, 1652 output_token, 1653 &out_req_flags, 1654 NULL); 1655 GSS_UNLOCK_MUTEX(params->utils); 1656 1657 if (GSS_ERROR(maj_stat)) { 1658 sasl_gss_seterror(text->utils, maj_stat, min_stat); 1659 if (output_token->value) { 1660 GSS_LOCK_MUTEX(params->utils); 1661 gss_release_buffer(&min_stat, output_token); 1662 GSS_UNLOCK_MUTEX(params->utils); 1663 } 1664 sasl_gss_free_context_contents(text); 1665 return SASL_FAIL; 1666 } 1667 } 1668 1669 if ((out_req_flags & GSS_C_DELEG_FLAG) != (req_flags & GSS_C_DELEG_FLAG)) { 1670 text->utils->seterror(text->utils->conn, SASL_LOG_WARN, "GSSAPI warning: no credentials were passed"); 1671 /* not a fatal error */ 1672 } 1673 1674 *clientoutlen = output_token->length; 1675 1676 if (output_token->value) { 1677 if (clientout) { 1678 ret = _plug_buf_alloc(text->utils, &(text->out_buf), 1679 &(text->out_buf_len), *clientoutlen); 1680 if(ret != SASL_OK) { 1681 GSS_LOCK_MUTEX(params->utils); 1682 gss_release_buffer(&min_stat, output_token); 1683 GSS_UNLOCK_MUTEX(params->utils); 1684 return ret; 1685 } 1686 memcpy(text->out_buf, output_token->value, *clientoutlen); 1687 *clientout = (char *)text->out_buf; 1688 } 1689 1690 GSS_LOCK_MUTEX(params->utils); 1691 gss_release_buffer(&min_stat, output_token); 1692 GSS_UNLOCK_MUTEX(params->utils); 1693 } 1694 1695 if (maj_stat == GSS_S_COMPLETE) { 1696 GSS_LOCK_MUTEX(params->utils); 1697 maj_stat = gss_inquire_context(&min_stat, 1698 text->gss_ctx, 1699 &text->client_name, 1700 NULL, /* targ_name */ 1701 NULL, /* lifetime */ 1702 NULL, /* mech */ 1703 /* FIX ME: Should check the resulting flags here */ 1704 NULL, /* flags */ 1705 NULL, /* local init */ 1706 NULL); /* open */ 1707 GSS_UNLOCK_MUTEX(params->utils); 1708 1709 if (GSS_ERROR(maj_stat)) { 1710 sasl_gss_seterror(text->utils, maj_stat, min_stat); 1711 sasl_gss_free_context_contents(text); 1712 return SASL_FAIL; 1713 } 1714 1715 name_token.length = 0; 1716 GSS_LOCK_MUTEX(params->utils); 1717 maj_stat = gss_display_name(&min_stat, 1718 text->client_name, 1719 &name_token, 1720 NULL); 1721 GSS_UNLOCK_MUTEX(params->utils); 1722 1723 if (GSS_ERROR(maj_stat)) { 1724 if (name_token.value) { 1725 GSS_LOCK_MUTEX(params->utils); 1726 gss_release_buffer(&min_stat, &name_token); 1727 GSS_UNLOCK_MUTEX(params->utils); 1728 } 1729 SETERROR(text->utils, "GSSAPI Failure"); 1730 sasl_gss_free_context_contents(text); 1731 return SASL_FAIL; 1732 } 1733 1734 if (text->user && text->user[0]) { 1735 ret = params->canon_user(params->utils->conn, 1736 text->user, 0, 1737 SASL_CU_AUTHZID, oparams); 1738 if (ret == SASL_OK) 1739 ret = params->canon_user(params->utils->conn, 1740 name_token.value, 0, 1741 SASL_CU_AUTHID, oparams); 1742 } else { 1743 ret = params->canon_user(params->utils->conn, 1744 name_token.value, 0, 1745 SASL_CU_AUTHID | SASL_CU_AUTHZID, 1746 oparams); 1747 } 1748 GSS_LOCK_MUTEX(params->utils); 1749 gss_release_buffer(&min_stat, &name_token); 1750 GSS_UNLOCK_MUTEX(params->utils); 1751 1752 if (ret != SASL_OK) return ret; 1753 1754 /* Switch to ssf negotiation */ 1755 text->state = SASL_GSSAPI_STATE_SSFCAP; 1756 } 1757 1758 return SASL_CONTINUE; 1759 1760 case SASL_GSSAPI_STATE_SSFCAP: { 1761 sasl_security_properties_t *secprops = &(params->props); 1762 unsigned int alen, external = params->external_ssf; 1763 sasl_ssf_t need, allowed; 1764 char serverhas, mychoice; 1765 1766 real_input_token.value = (void *) serverin; 1767 real_input_token.length = serverinlen; 1768 1769 GSS_LOCK_MUTEX(params->utils); 1770 maj_stat = gss_unwrap(&min_stat, 1771 text->gss_ctx, 1772 input_token, 1773 output_token, 1774 NULL, 1775 NULL); 1776 GSS_UNLOCK_MUTEX(params->utils); 1777 1778 if (GSS_ERROR(maj_stat)) { 1779 sasl_gss_seterror(text->utils, maj_stat, min_stat); 1780 sasl_gss_free_context_contents(text); 1781 if (output_token->value) { 1782 GSS_LOCK_MUTEX(params->utils); 1783 gss_release_buffer(&min_stat, output_token); 1784 GSS_UNLOCK_MUTEX(params->utils); 1785 } 1786 return SASL_FAIL; 1787 } 1788 1789 /* taken from kerberos.c */ 1790 if (secprops->min_ssf > (K5_MAX_SSF + external)) { 1791 return SASL_TOOWEAK; 1792 } else if (secprops->min_ssf > secprops->max_ssf) { 1793 return SASL_BADPARAM; 1794 } 1795 1796 /* need bits of layer -- sasl_ssf_t is unsigned so be careful */ 1797 if (secprops->max_ssf >= external) { 1798 allowed = secprops->max_ssf - external; 1799 } else { 1800 allowed = 0; 1801 } 1802 if (secprops->min_ssf >= external) { 1803 need = secprops->min_ssf - external; 1804 } else { 1805 /* good to go */ 1806 need = 0; 1807 } 1808 1809 /* bit mask of server support */ 1810 serverhas = ((char *)output_token->value)[0]; 1811 1812 /* if client didn't set use strongest layer available */ 1813 if (allowed >= K5_MAX_SSF && need <= K5_MAX_SSF && (serverhas & 4)) { 1814 /* encryption */ 1815 oparams->encode = &gssapi_privacy_encode; 1816 oparams->decode = &gssapi_decode; 1817 /* FIX ME: Need to extract the proper value here */ 1818 oparams->mech_ssf = K5_MAX_SSF; 1819 mychoice = 4; 1820 } else if (allowed >= 1 && need <= 1 && (serverhas & 2)) { 1821 /* integrity */ 1822 oparams->encode = &gssapi_integrity_encode; 1823 oparams->decode = &gssapi_decode; 1824 oparams->mech_ssf = 1; 1825 mychoice = 2; 1826 } else if (need <= 0 && (serverhas & 1)) { 1827 /* no layer */ 1828 oparams->encode = NULL; 1829 oparams->decode = NULL; 1830 oparams->mech_ssf = 0; 1831 mychoice = 1; 1832 } else { 1833 /* there's no appropriate layering for us! */ 1834 sasl_gss_free_context_contents(text); 1835 return SASL_TOOWEAK; 1836 } 1837 1838 oparams->maxoutbuf = 1839 (((unsigned char *) output_token->value)[1] << 16) | 1840 (((unsigned char *) output_token->value)[2] << 8) | 1841 (((unsigned char *) output_token->value)[3] << 0); 1842 1843 if(oparams->mech_ssf) { 1844 maj_stat = gss_wrap_size_limit( &min_stat, 1845 text->gss_ctx, 1846 1, 1847 GSS_C_QOP_DEFAULT, 1848 (OM_uint32) oparams->maxoutbuf, 1849 &max_input); 1850 1851 if(max_input > oparams->maxoutbuf) { 1852 /* Heimdal appears to get this wrong */ 1853 oparams->maxoutbuf -= (max_input - oparams->maxoutbuf); 1854 } else { 1855 /* This code is actually correct */ 1856 oparams->maxoutbuf = max_input; 1857 } 1858 } 1859 1860 GSS_LOCK_MUTEX(params->utils); 1861 gss_release_buffer(&min_stat, output_token); 1862 GSS_UNLOCK_MUTEX(params->utils); 1863 1864 /* oparams->user is always set, due to canon_user requirements. 1865 * Make sure the client actually requested it though, by checking 1866 * if our context was set. 1867 */ 1868 if (text->user && text->user[0]) 1869 alen = strlen(oparams->user); 1870 else 1871 alen = 0; 1872 1873 input_token->length = 4 + alen; 1874 input_token->value = 1875 (char *)params->utils->malloc((input_token->length + 1)*sizeof(char)); 1876 if (input_token->value == NULL) { 1877 sasl_gss_free_context_contents(text); 1878 return SASL_NOMEM; 1879 } 1880 1881 if (alen) 1882 memcpy((char *)input_token->value+4,oparams->user,alen); 1883 1884 /* build up our security properties token */ 1885 if (params->props.maxbufsize > 0xFFFFFF) { 1886 /* make sure maxbufsize isn't too large */ 1887 /* maxbufsize = 0xFFFFFF */ 1888 ((unsigned char *)input_token->value)[1] = 0xFF; 1889 ((unsigned char *)input_token->value)[2] = 0xFF; 1890 ((unsigned char *)input_token->value)[3] = 0xFF; 1891 } else { 1892 ((unsigned char *)input_token->value)[1] = 1893 (params->props.maxbufsize >> 16) & 0xFF; 1894 ((unsigned char *)input_token->value)[2] = 1895 (params->props.maxbufsize >> 8) & 0xFF; 1896 ((unsigned char *)input_token->value)[3] = 1897 (params->props.maxbufsize >> 0) & 0xFF; 1898 } 1899 ((unsigned char *)input_token->value)[0] = mychoice; 1900 1901 GSS_LOCK_MUTEX(params->utils); 1902 maj_stat = gss_wrap (&min_stat, 1903 text->gss_ctx, 1904 0, /* Just integrity checking here */ 1905 GSS_C_QOP_DEFAULT, 1906 input_token, 1907 NULL, 1908 output_token); 1909 GSS_UNLOCK_MUTEX(params->utils); 1910 1911 params->utils->free(input_token->value); 1912 input_token->value = NULL; 1913 1914 if (GSS_ERROR(maj_stat)) { 1915 sasl_gss_seterror(text->utils, maj_stat, min_stat); 1916 if (output_token->value) { 1917 GSS_LOCK_MUTEX(params->utils); 1918 gss_release_buffer(&min_stat, output_token); 1919 GSS_UNLOCK_MUTEX(params->utils); 1920 } 1921 sasl_gss_free_context_contents(text); 1922 return SASL_FAIL; 1923 } 1924 1925 if (clientoutlen) 1926 *clientoutlen = output_token->length; 1927 if (output_token->value) { 1928 if (clientout) { 1929 ret = _plug_buf_alloc(text->utils, &(text->out_buf), 1930 &(text->out_buf_len), *clientoutlen); 1931 if (ret != SASL_OK) { 1932 GSS_LOCK_MUTEX(params->utils); 1933 gss_release_buffer(&min_stat, output_token); 1934 GSS_UNLOCK_MUTEX(params->utils); 1935 return ret; 1936 } 1937 memcpy(text->out_buf, output_token->value, *clientoutlen); 1938 *clientout = (char *)text->out_buf; 1939 } 1940 1941 GSS_LOCK_MUTEX(params->utils); 1942 gss_release_buffer(&min_stat, output_token); 1943 GSS_UNLOCK_MUTEX(params->utils); 1944 1945 } 1946 1947 text->state = SASL_GSSAPI_STATE_AUTHENTICATED; 1948 1949 oparams->doneflag = 1; 1950 1951 /* used by layers */ 1952 _plug_decode_init(&text->decode_context, text->utils, 1953 (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : 1954 params->props.maxbufsize); 1955 1956 return SASL_OK; 1957 } 1958 1959 default: 1960 params->utils->log(NULL, SASL_LOG_ERR, 1961 "Invalid GSSAPI client step %d\n", text->state); 1962 return SASL_FAIL; 1963 } 1964 1965 return SASL_FAIL; /* should never get here */ 1966} 1967 1968static const unsigned long gssapi_required_prompts[] = { 1969 SASL_CB_LIST_END 1970}; 1971 1972static sasl_client_plug_t gssapi_client_plugins[] = 1973{ 1974 { 1975 "GSSAPI", /* mech_name */ 1976 K5_MAX_SSF, /* max_ssf */ 1977 SASL_SEC_NOPLAINTEXT 1978 | SASL_SEC_NOACTIVE 1979 | SASL_SEC_NOANONYMOUS 1980 | SASL_SEC_MUTUAL_AUTH 1981 | SASL_SEC_PASS_CREDENTIALS, /* security_flags */ 1982 SASL_FEAT_NEEDSERVERFQDN 1983 | SASL_FEAT_WANT_CLIENT_FIRST 1984 | SASL_FEAT_ALLOWS_PROXY, /* features */ 1985 gssapi_required_prompts, /* required_prompts */ 1986 NULL, /* glob_context */ 1987 &gssapi_client_mech_new, /* mech_new */ 1988 &gssapi_client_mech_step, /* mech_step */ 1989 &gssapi_common_mech_dispose, /* mech_dispose */ 1990 &gssapi_common_mech_free, /* mech_free */ 1991 NULL, /* idle */ 1992 NULL, /* spare */ 1993 NULL /* spare */ 1994 } 1995}; 1996 1997int gssapiv2_client_plug_init(const sasl_utils_t *utils __attribute__((unused)), 1998 int maxversion, 1999 int *out_version, 2000 sasl_client_plug_t **pluglist, 2001 int *plugcount) 2002{ 2003 if (maxversion < SASL_CLIENT_PLUG_VERSION) { 2004 SETERROR(utils, "Version mismatch in GSSAPI"); 2005 return SASL_BADVERS; 2006 } 2007 2008 *out_version = SASL_CLIENT_PLUG_VERSION; 2009 *pluglist = gssapi_client_plugins; 2010 *plugcount = 1; 2011 2012#ifdef GSS_USE_MUTEXES 2013 if(!gss_mutex) { 2014 gss_mutex = utils->mutex_alloc(); 2015 if(!gss_mutex) { 2016 return SASL_FAIL; 2017 } 2018 } 2019#endif 2020 2021 return SASL_OK; 2022} 2023 2024