mech_gssapi.c revision 1.5
1/* $NetBSD: mech_gssapi.c,v 1.5 2011/02/12 23:21:32 christos Exp $ */ 2 3/* Copyright (c) 2010 The NetBSD Foundation, Inc. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Mateusz Kocielski. 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 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the NetBSD 20 * Foundation, Inc. and its contributors. 21 * 4. Neither the name of The NetBSD Foundation nor the names of its 22 * contributors may be used to endorse or promote products derived 23 * from this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 26 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37#include <sys/cdefs.h> 38__RCSID("$NetBSD: mech_gssapi.c,v 1.5 2011/02/12 23:21:32 christos Exp $"); 39 40#include <assert.h> 41#include <errno.h> 42#include <limits.h> /* for LINE_MAX */ 43#include <saslc.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <string.h> 47 48#include <gssapi/gssapi.h> 49 50#include "buffer.h" 51#include "list.h" 52#include "mech.h" 53#include "msg.h" 54#include "saslc_private.h" 55 56 57/* See RFC 2222 section 7.2.1. */ 58 59/* properties */ 60#define SASLC_GSSAPI_AUTHCID SASLC_PROP_AUTHCID 61#define SASLC_GSSAPI_HOSTNAME SASLC_PROP_HOSTNAME 62#define SASLC_GSSAPI_SERVICE SASLC_PROP_SERVICE 63#define SASLC_GSSAPI_QOPMASK SASLC_PROP_QOPMASK 64 65#define DEFAULT_QOP_MASK (F_QOP_NONE | F_QOP_INT | F_QOP_CONF) 66 67/* authentication steps */ 68typedef enum { /* see RFC2222 7.2.1 section */ 69 GSSAPI_AUTH_FIRST, /* first authentication stage */ 70 GSSAPI_AUTH_NEXT, /* next authentication stage(s) */ 71 GSSAPI_AUTH_LAST, /* final authentication stage */ 72 GSSAPI_AUTH_DONE /* authenticated */ 73} saslc__mech_gssapi_status_t; 74 75/* gssapi mechanism session */ 76typedef struct { 77 saslc__mech_sess_t mech_sess; /* mechanism session */ 78 saslc__mech_gssapi_status_t status; /* authentication status */ 79 gss_ctx_id_t gss_ctx; /* GSSAPI context */ 80 gss_name_t server_name; /* server name: service@host */ 81 gss_name_t client_name; /* client name - XXX: unused! */ 82 uint32_t qop_mask; /* available QOP services */ 83 uint32_t omaxbuf; /* maximum output buffer size */ 84 uint32_t imaxbuf; /* maximum input buffer size */ 85 saslc__buffer32_context_t *dec_ctx; /* decode buffer context */ 86 saslc__buffer_context_t *enc_ctx; /* encode buffer context */ 87} saslc__mech_gssapi_sess_t; 88 89/** 90 * @brief creates gssapi mechanism session. 91 * Function initializes also default options for the session. 92 * @param sess sasl session 93 * @return 0 on success, -1 on failure. 94 */ 95static int 96saslc__mech_gssapi_create(saslc_sess_t *sess) 97{ 98 saslc__mech_gssapi_sess_t *c; 99 100 c = sess->mech_sess = calloc(1, sizeof(*c)); 101 if (c == NULL) 102 return -1; 103 104 sess->mech_sess = c; 105 106 c->gss_ctx = GSS_C_NO_CONTEXT; 107 c->server_name = GSS_C_NO_NAME; 108 c->client_name = GSS_C_NO_NAME; 109 110 return 0; 111} 112 113/** 114 * @brief destroys gssapi mechanism session. 115 * Function also is freeing assigned resources to the session. 116 * @param sess sasl session 117 * @return Functions always returns 0. 118 */ 119static int 120saslc__mech_gssapi_destroy(saslc_sess_t *sess) 121{ 122 saslc__mech_gssapi_sess_t *ms; 123 OM_uint32 min_s; 124 125 ms = sess->mech_sess; 126 127 if (ms->gss_ctx != GSS_C_NO_CONTEXT) 128 gss_delete_sec_context(&min_s, &ms->gss_ctx, GSS_C_NO_BUFFER); 129 if (ms->server_name != GSS_C_NO_NAME) 130 gss_release_name(&min_s, &ms->server_name); 131 if (ms->client_name != GSS_C_NO_NAME) 132 gss_release_name(&min_s, &ms->client_name); 133 134 saslc__buffer_destroy(ms->enc_ctx); 135 saslc__buffer32_destroy(ms->dec_ctx); 136 free(ms); 137 sess->mech_sess = NULL; 138 139 return 0; 140} 141 142/** 143 * @brief translate the major and minor statuses an error message for 144 * the given mechanism 145 * @param maj_s major status 146 * @param min_s minor status 147 * @param mech mechanism 148 * @return pointer to a static buffer with error message 149 */ 150static char * 151saslc__mech_gssapi_err(OM_uint32 maj_s, OM_uint32 min_s, gss_OID mech) 152{ 153 static char errbuf[LINE_MAX]; 154 gss_buffer_desc maj_error_message; 155 gss_buffer_desc min_error_message; 156 OM_uint32 disp_min_s; 157 OM_uint32 msg_ctx; 158 159 msg_ctx = 0; 160 maj_error_message.length = 0; 161 maj_error_message.value = NULL; 162 min_error_message.length = 0; 163 min_error_message.value = NULL; 164 165 (void)gss_display_status(&disp_min_s, maj_s, GSS_C_GSS_CODE, 166 mech, &msg_ctx, &maj_error_message); 167 (void)gss_display_status(&disp_min_s, min_s, GSS_C_MECH_CODE, 168 mech, &msg_ctx, &min_error_message); 169 170 (void)snprintf(errbuf, sizeof(errbuf), 171 "gss-code: %lu %.*s\nmech-code: %lu %.*s", 172 (unsigned long)maj_s, 173 (int)maj_error_message.length, 174 (char *)maj_error_message.value, 175 (unsigned long)min_s, 176 (int)min_error_message.length, 177 (char *)min_error_message.value); 178 179 (void)gss_release_buffer(&disp_min_s, &maj_error_message); 180 (void)gss_release_buffer(&disp_min_s, &min_error_message); 181 182 return errbuf; 183} 184 185/** 186 * @brief set a session error message using saslc__mech_gssapi_err() 187 * @param sess the session 188 * @param err error number to set 189 * @param maj_s major status 190 * @param min_s minor status 191 * @return pointer to a static buffer with error message 192 */ 193static void 194saslc__mech_gssapi_set_err(saslc_sess_t *sess, int err, OM_uint32 maj_s, OM_uint32 min_s) 195{ 196 197 saslc__error_set(ERR(sess), err, 198 saslc__mech_gssapi_err(maj_s, min_s, GSS_C_NO_OID)); 199} 200 201/** 202 * @brief convert an initialization output token into the out and outlen format. 203 * Also releases the output token. 204 * @param sess saslc session 205 * @param outbuf gss buffer token 206 * @param out pointer to a void pointer 207 * @param outlen pointer to size_t length storage 208 * @returns 0 on success, -1 on failure 209 */ 210static int 211prep_output(saslc_sess_t *sess, gss_buffer_t outbuf, void **out, size_t *outlen) 212{ 213 OM_uint32 min_s; 214 215 if (outbuf == GSS_C_NO_BUFFER || outbuf->value == NULL) { 216 *outlen = 0; 217 *out = NULL; 218 return 0; 219 } 220 if (outbuf->length == 0) { 221 *outlen = 0; 222 *out = NULL; 223 gss_release_buffer(&min_s, outbuf); 224 return 0; 225 } 226 *out = malloc(outbuf->length); 227 if (*out == NULL) { 228 *outlen = 0; 229 gss_release_buffer(&min_s, outbuf); 230 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 231 return -1; 232 } 233 *outlen = outbuf->length; 234 memcpy(*out, outbuf->value, outbuf->length); 235 gss_release_buffer(&min_s, outbuf); 236 return 0; 237} 238 239/** 240 * @brief convert an output token into a valid packet where the first 241 * 4 bytes are the payload length in network byte order. 242 * Also releases the output token. 243 * @param sess saslc session 244 * @param outbuf gss buffer token 245 * @param out pointer to a void pointer 246 * @param outlen pointer to size_t length storage 247 * @returns 0 on success, -1 on failure 248 */ 249static int 250prep_packet(saslc_sess_t *sess, gss_buffer_t outbuf, void **out, size_t *outlen) 251{ 252 saslc__mech_gssapi_sess_t *ms; 253 OM_uint32 min_s; 254 char *buf; 255 size_t buflen; 256 257 ms = sess->mech_sess; 258 259 if (outbuf == GSS_C_NO_BUFFER || outbuf->value == NULL) { 260 *outlen = 0; 261 *out = NULL; 262 return 0; 263 } 264 if (outbuf->length == 0) { 265 *outlen = 0; 266 *out = NULL; 267 gss_release_buffer(&min_s, outbuf); 268 return 0; 269 } 270 buflen = outbuf->length + 4; 271 if (buflen > ms->omaxbuf) { 272 saslc__error_set(ERR(sess), ERROR_MECH, 273 "output exceeds server maxbuf size"); 274 gss_release_buffer(&min_s, outbuf); 275 return -1; 276 } 277 buf = malloc(buflen); 278 if (buf == NULL) { 279 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 280 return -1; 281 } 282 be32enc(buf, (uint32_t)outbuf->length); 283 memcpy(buf + 4, outbuf->value, outbuf->length); 284 gss_release_buffer(&min_s, outbuf); 285 286 *out = buf; 287 *outlen = buflen; 288 return 0; 289} 290 291/** 292 * @brief encodes one block of data using the negotiated security layer. 293 * @param sess sasl session 294 * @param in input data 295 * @param inlen input data length 296 * @param out place to store output data 297 * @param outlen output data length 298 * @return number of bytes consumed, zero if more needed, or -1 on failure. 299 */ 300static ssize_t 301saslc__mech_gssapi_encode(saslc_sess_t *sess, const void *in, size_t inlen, 302 void **out, size_t *outlen) 303{ 304 saslc__mech_gssapi_sess_t *ms; 305 gss_buffer_desc input, output; 306 OM_uint32 min_s, maj_s; 307 uint8_t *buf; 308 size_t buflen; 309 ssize_t len; 310 311 ms = sess->mech_sess; 312 assert(ms->mech_sess.qop != QOP_NONE); 313 if (ms->mech_sess.qop == QOP_NONE) 314 return -1; 315 316 len = saslc__buffer_fetch(ms->enc_ctx, in, inlen, &buf, &buflen); 317 if (len == -1) 318 return -1; 319 320 if (buflen == 0) { 321 *out = NULL; 322 *outlen = 0; 323 return len; 324 } 325 326 input.value = buf; 327 input.length = buflen; 328 output.value = NULL; 329 output.length = 0; 330 331 maj_s = gss_wrap(&min_s, ms->gss_ctx, ms->mech_sess.qop == QOP_CONF, 332 GSS_C_QOP_DEFAULT, &input, NULL, &output); 333 334 if (GSS_ERROR(maj_s)) { 335 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s); 336 return -1; 337 } 338 if (prep_packet(sess, &output, out, outlen) == -1) 339 return -1; 340 341 return len; 342} 343 344/** 345 * @brief decodes one block of data using the negotiated security layer. 346 * @param sess sasl session 347 * @param in input data 348 * @param inlen input data length 349 * @param out place to store output data 350 * @param outlen output data length 351 * @return number of bytes consumed, zero if more needed, or -1 on failure. 352 */ 353static ssize_t 354saslc__mech_gssapi_decode(saslc_sess_t *sess, const void *in, size_t inlen, 355 void **out, size_t *outlen) 356{ 357 saslc__mech_gssapi_sess_t *ms; 358 gss_buffer_desc input, output; 359 OM_uint32 min_s, maj_s; 360 uint8_t *buf; 361 size_t buflen; 362 ssize_t len; 363 364 ms = sess->mech_sess; 365 assert(ms->mech_sess.qop != QOP_NONE); 366 if (ms->mech_sess.qop == QOP_NONE) 367 return -1; 368 369 len = saslc__buffer32_fetch(ms->dec_ctx, in, inlen, &buf, &buflen); 370 if (len == -1) 371 return -1; 372 373 if (buflen == 0) { 374 *out = NULL; 375 *outlen = 0; 376 return len; 377 } 378 379 /* buf -> szbuf (4 bytes) followed by the payload buffer */ 380 input.value = buf + 4; 381 input.length = buflen - 4; 382 output.value = NULL; 383 output.length = 0; 384 385 maj_s = gss_unwrap(&min_s, ms->gss_ctx, &input, &output, NULL, NULL); 386 387 if (GSS_ERROR(maj_s)) { 388 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s); 389 return -1; 390 } 391 392 if (prep_output(sess, &output, out, outlen) == -1) 393 return -1; 394 395 return len; 396} 397 398/** 399 * @brief get service name from properties 400 * ("<servicename>@<hostname>") and store it in service token. 401 * @param sess the session context 402 * @param service the gs_name_t token to return service name in 403 * @return 0 on success, -1 on error 404 */ 405static int 406get_service(saslc_sess_t *sess, gss_name_t *service) 407{ 408 gss_buffer_desc bufdesc; 409 const char *hostname, *servicename; 410 char *buf; 411 int buflen; 412 OM_uint32 min_s, maj_s; 413 414 hostname = saslc_sess_getprop(sess, SASLC_GSSAPI_HOSTNAME); 415 if (hostname == NULL) { 416 saslc__error_set(ERR(sess), ERROR_MECH, 417 "hostname is required for an authentication"); 418 return -1; 419 } 420 servicename = saslc_sess_getprop(sess, SASLC_GSSAPI_SERVICE); 421 if (servicename == NULL) { 422 saslc__error_set(ERR(sess), ERROR_MECH, 423 "service is required for an authentication"); 424 return -1; 425 } 426 buflen = asprintf(&buf, "%s@%s", servicename, hostname); 427 if (buflen == -1) { 428 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 429 return -1; 430 } 431 bufdesc.value = buf; 432 bufdesc.length = buflen + 1; 433 434 saslc__msg_dbg("%s: buf='%s'", __func__, buf); 435 436 maj_s = gss_import_name(&min_s, &bufdesc, GSS_C_NT_HOSTBASED_SERVICE, 437 service); 438 free(buf); 439 if (GSS_ERROR(maj_s)) { 440 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s); 441 return -1; 442 } 443 return 0; 444} 445 446/** 447 * @brief gss_init_sec_context() wrapper 448 * @param sess session context 449 * @param inbuf input token 450 * @param outbuf output token 451 * @return 0 if GSS_S_COMPLETE, 1 if GSS_S_CONTINUE_NEEDED, -1 on failure 452 */ 453static int 454init_sec_context(saslc_sess_t *sess, gss_buffer_t inbuf, gss_buffer_t outbuf) 455{ 456 saslc__mech_gssapi_sess_t *ms; 457 OM_uint32 min_s, maj_s; 458 459 ms = sess->mech_sess; 460 461 outbuf->length = 0; 462 outbuf->value = NULL; 463 maj_s = gss_init_sec_context( 464 &min_s, /* minor status */ 465 GSS_C_NO_CREDENTIAL, /* use current login context credential */ 466 &ms->gss_ctx, /* initially GSS_C_NO_CONTEXT */ 467 ms->server_name, /* server@hostname */ 468 GSS_C_NO_OID, /* use default mechanism */ 469#if 1 470 GSS_C_REPLAY_FLAG | /* message replay detection */ 471 GSS_C_INTEG_FLAG | /* request integrity */ 472 GSS_C_CONF_FLAG | /* request confirmation */ 473#endif 474 GSS_C_MUTUAL_FLAG | /* mutual authentication */ 475 GSS_C_SEQUENCE_FLAG, /* message sequence checking */ 476 0, /* default lifetime (2 hrs) */ 477 GSS_C_NO_CHANNEL_BINDINGS, 478 inbuf, /* input token */ 479 /* output parameters follow */ 480 NULL, /* mechanism type for context */ 481 outbuf, /* output token */ 482 NULL, /* services available for context */ 483 NULL); /* lifetime of context */ 484 485 switch (maj_s) { 486 case GSS_S_COMPLETE: 487 return 0; 488 case GSS_S_CONTINUE_NEEDED: 489 return 1; 490 default: 491 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s); 492 return -1; 493 } 494} 495 496/** 497 * @brief unwrap the authentication token received from the server. 498 * This contains the qop_mask and maxbuf values which are updated in 499 * saslc__mech_gssapi_sess_t. 500 * @param sess the session context 501 * @param inbuf the received authentication token. 502 * @return 0 on success, -1 on error. 503 */ 504static int 505unwrap_input_token(saslc_sess_t *sess, gss_buffer_t inbuf) 506{ 507 saslc__mech_gssapi_sess_t *ms; 508 OM_uint32 min_s, maj_s; 509 gss_buffer_t outbuf; 510 gss_buffer_desc outdesc; 511 unsigned char *p; 512 513 /********************************************************************/ 514 /* [RFC 2222 section 7.2.1] */ 515 /* The client passes this token to GSS_Unwrap and interprets */ 516 /* the first octet of resulting cleartext as a bit-mask specifying */ 517 /* the security layers supported by the server and the second */ 518 /* through fourth octets as the maximum size output_message to send */ 519 /* to the server. */ 520 /********************************************************************/ 521 522 ms = sess->mech_sess; 523 524 outbuf = &outdesc; 525 maj_s = gss_unwrap(&min_s, ms->gss_ctx, inbuf, outbuf, NULL, NULL); 526 527 if (GSS_ERROR(maj_s)) { 528 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s); 529 return -1; 530 } 531 if (outbuf->length != 4) { 532 saslc__error_set(ERR(sess), ERROR_MECH, 533 "invalid unwrap length"); 534 return -1; 535 } 536 p = outbuf->value; 537 ms->qop_mask = p[0]; 538 ms->omaxbuf = (be32dec(p) & 0xffffff); 539 540 saslc__msg_dbg("%s: qop_mask=0x%02x omaxbuf=%d", 541 __func__, ms->qop_mask, ms->omaxbuf); 542 543 if (ms->qop_mask == QOP_NONE && ms->omaxbuf != 0) { 544 saslc__error_set(ERR(sess), ERROR_MECH, 545 "server has no security layer support, but maxbuf != 0"); 546 return -1; 547 } 548 maj_s = gss_release_buffer(&min_s, outbuf); 549 if (GSS_ERROR(maj_s)) { 550 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s); 551 return -1; 552 } 553 return 0; 554} 555 556/** 557 * @brief construct and wrap up an authentication token and put it in 558 * outbuf. The outbuf token data is structured as follows: 559 * struct { 560 * uint8_t qop; // qop to use 561 * uint8_t maxbuf[3] // maxbuf for client (network byte order) 562 * uint8_t authcid[] // variable length authentication id (username) 563 * } __packed; 564 * @param sess the session 565 * @param outbuf the gss_buffer_t token to return to server. 566 * @return 0 on success, -1 on error. 567 */ 568static int 569wrap_output_token(saslc_sess_t *sess, gss_buffer_t outbuf) 570{ 571 saslc__mech_gssapi_sess_t *ms; 572 gss_buffer_desc indesc; 573 char *input_value; 574 int len; 575 const char *authcid; 576 OM_uint32 min_s, maj_s; 577 unsigned char *p; 578 579 /********************************************************************/ 580 /* [RFC 2222 section 7.2.1] */ 581 /* The client then constructs data, with the first octet containing */ 582 /* the bit-mask specifying the selected security layer, the second */ 583 /* through fourth octets containing in network byte order the */ 584 /* maximum size output_message the client is able to receive, and */ 585 /* the remaining octets containing the authorization identity. The */ 586 /* client passes the data to GSS_Wrap with conf_flag set to FALSE, */ 587 /* and responds with the generated output_message. The client can */ 588 /* then consider the server authenticated. */ 589 /********************************************************************/ 590 591 ms = sess->mech_sess; 592 593 if ((authcid = saslc_sess_getprop(sess, SASLC_GSSAPI_AUTHCID)) 594 == NULL) { 595 saslc__error_set(ERR(sess), ERROR_MECH, 596 "authcid is required for an authentication"); 597 return -1; 598 } 599 600 len = asprintf(&input_value, "qmax%s", authcid); 601 if (len == -1) { 602 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 603 return -1; 604 } 605 be32enc(input_value, ms->imaxbuf); 606 input_value[0] = saslc__mech_qop_flag(ms->mech_sess.qop); 607 608 indesc.value = input_value; 609 indesc.length = len; /* XXX: don't count the '\0' */ 610 611 p = (unsigned char *)input_value; 612 saslc__msg_dbg("%s: input_value='%02x %02x %02x %02x %s", 613 __func__, p[0], p[1], p[2], p[3], input_value + 4); 614 615 maj_s = gss_wrap(&min_s, ms->gss_ctx, 0 /* FALSE - RFC2222 */, 616 GSS_C_QOP_DEFAULT, &indesc, NULL, outbuf); 617 618 free(input_value); 619 620 if (GSS_ERROR(maj_s)) { 621 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s); 622 return -1; 623 } 624 return 0; 625} 626 627/************************************************************************ 628 * XXX: Share this with mech_digestmd5.c? They are almost identical. 629 */ 630/** 631 * @brief choose the best qop based on what was provided by the 632 * challenge and a possible user mask. 633 * @param sess the session context 634 * @param qop_flags the qop flags parsed from the challenge string 635 * @return the selected saslc__mech_sess_qop_t or -1 if no match 636 */ 637static int 638choose_qop(saslc_sess_t *sess, uint32_t qop_flags) 639{ 640 list_t *list; 641 const char *user_qop; 642 643 qop_flags &= DEFAULT_QOP_MASK; 644 user_qop = saslc_sess_getprop(sess, SASLC_GSSAPI_QOPMASK); 645 if (user_qop != NULL) { 646 list = saslc__list_parse(user_qop); 647 qop_flags &= saslc__mech_qop_list_flags(list); 648 saslc__list_free(list); 649 } 650 651 /* 652 * Select the most secure supported qop. 653 */ 654 if ((qop_flags & F_QOP_CONF) != 0) 655 return QOP_CONF; 656 if ((qop_flags & F_QOP_INT) != 0) 657 return QOP_INT; 658 if ((qop_flags & F_QOP_NONE) != 0) 659 return QOP_NONE; 660 661 saslc__error_set(ERR(sess), ERROR_MECH, 662 "cannot choose an acceptable qop"); 663 return -1; 664} 665/************************************************************************/ 666 667/** 668 * @brief compute the maximum buffer length we can use and not 669 * overflow the servers maxbuf. 670 * @param sess the session context 671 * @param maxbuf the server's maxbuf value 672 */ 673static int 674wrap_size_limit(saslc_sess_t *sess, OM_uint32 maxbuf) 675{ 676 saslc__mech_gssapi_sess_t *ms; 677 OM_uint32 min_s, maj_s; 678 OM_uint32 max_input; 679 680 ms = sess->mech_sess; 681 682 maj_s = gss_wrap_size_limit(&min_s, ms->gss_ctx, 1, GSS_C_QOP_DEFAULT, 683 maxbuf, &max_input); 684 685 if (GSS_ERROR(maj_s)) { 686 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s); 687 return -1; 688 } 689 690 /* XXX: from cyrus-sasl: gssapi.c */ 691 if (max_input > maxbuf) { 692 /* Heimdal appears to get this wrong */ 693 maxbuf -= (max_input - maxbuf); 694 } else { 695 /* This code is actually correct */ 696 maxbuf = max_input; 697 } 698 return maxbuf; 699} 700 701/** 702 * @brief set our imaxbuf (from omaxbuf or from properties) and 703 * then reset omaxbuf in saslc__mech_gssapi_sess_t. 704 * @param sess the session context 705 * @return 0 on success, -1 on error 706 * 707 * Note: on entry the omaxbuf is the server's maxbuf size. On exit 708 * the omaxbuf is the maximum buffer we can fill that will not 709 * overflow the servers maxbuf after it is encoded. This value is 710 * given by wrap_size_limit(). 711 */ 712static int 713set_maxbufs(saslc_sess_t *sess) 714{ 715 saslc__mech_gssapi_sess_t *ms; 716 const char *p; 717 char *q; 718 unsigned long val; 719 int rv; 720 721 ms = sess->mech_sess; 722 723 /* by default, we use the same input maxbuf as the server. */ 724 ms->imaxbuf = ms->omaxbuf; 725 p = saslc_sess_getprop(sess, SASLC_PROP_MAXBUF); 726 if (p != NULL) { 727 val = strtol(p, &q, 0); 728 if (p[0] == '\0' || *q != '\0') { 729 730 return MECH_ERROR; 731 } 732 if (errno == ERANGE && val == ULONG_MAX) { 733 734 return MECH_ERROR; 735 } 736 if (val > 0xffffff) 737 val = 0xffffff; 738 ms->imaxbuf = (uint32_t)val; 739 } 740 rv = wrap_size_limit(sess, ms->omaxbuf); 741 if (rv == -1) 742 return MECH_ERROR; 743 ms->omaxbuf = rv; /* maxbuf size for unencoded output data */ 744 745 return 0; 746} 747 748/** 749 * @brief do one step of the sasl authentication 750 * @param sess sasl session 751 * @param in input data 752 * @param inlen input data length 753 * @param out place to store output data 754 * @param outlen output data length 755 * @return MECH_OK on success, MECH_STEP if more steps are needed, 756 * MECH_ERROR on failure 757 */ 758static int 759saslc__mech_gssapi_cont(saslc_sess_t *sess, const void *in, size_t inlen, 760 void **out, size_t *outlen) 761{ 762 saslc__mech_gssapi_sess_t *ms; 763 gss_buffer_desc input, output; 764 int rv; 765 766 /**************************************************************************/ 767 /* [RFC 2222 section 7.2.1] */ 768 /* The client calls GSS_Init_sec_context, passing in 0 for */ 769 /* input_context_handle (initially) and a targ_name equal to output_name */ 770 /* from GSS_Import_Name called with input_name_type of */ 771 /* GSS_C_NT_HOSTBASED_SERVICE and input_name_string of */ 772 /* "service@hostname" where "service" is the service name specified in */ 773 /* the protocol's profile, and "hostname" is the fully qualified host */ 774 /* name of the server. The client then responds with the resulting */ 775 /* output_token. If GSS_Init_sec_context returns GSS_S_CONTINUE_NEEDED, */ 776 /* then the client should expect the server to issue a token in a */ 777 /* subsequent challenge. The client must pass the token to another call */ 778 /* to GSS_Init_sec_context, repeating the actions in this paragraph. */ 779 /* */ 780 /* When GSS_Init_sec_context returns GSS_S_COMPLETE, the client takes */ 781 /* the following actions: If the last call to GSS_Init_sec_context */ 782 /* returned an output_token, then the client responds with the */ 783 /* output_token, otherwise the client responds with no data. The client */ 784 /* should then expect the server to issue a token in a subsequent */ 785 /* challenge. The client passes this token to GSS_Unwrap and interprets */ 786 /* the first octet of resulting cleartext as a bit-mask specifying the */ 787 /* security layers supported by the server and the second through fourth */ 788 /* octets as the maximum size output_message to send to the server. The */ 789 /* client then constructs data, with the first octet containing the */ 790 /* bit-mask specifying the selected security layer, the second through */ 791 /* fourth octets containing in network byte order the maximum size */ 792 /* output_message the client is able to receive, and the remaining */ 793 /* octets containing the authorization identity. The client passes the */ 794 /* data to GSS_Wrap with conf_flag set to FALSE, and responds with the */ 795 /* generated output_message. The client can then consider the server */ 796 /* authenticated. */ 797 /**************************************************************************/ 798 799 ms = sess->mech_sess; 800 801 switch(ms->status) { 802 case GSSAPI_AUTH_FIRST: 803 saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_FIRST"); 804 805 if (get_service(sess, &ms->server_name) == -1) 806 return MECH_ERROR; 807 808 rv = init_sec_context(sess, GSS_C_NO_BUFFER, &output); 809 if (rv == -1) 810 return MECH_ERROR; 811 812 if (prep_output(sess, &output, out, outlen) == -1) 813 return MECH_ERROR; 814 815 ms->status = rv == 0 ? GSSAPI_AUTH_LAST : GSSAPI_AUTH_NEXT; 816 return MECH_STEP; 817 818 case GSSAPI_AUTH_NEXT: 819 saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_NEXT"); 820 821 input.value = __UNCONST(in); 822 input.length = inlen; 823 if ((rv = init_sec_context(sess, &input, &output)) == -1) 824 return MECH_ERROR; 825 826 if (prep_output(sess, &output, out, outlen) == -1) 827 return MECH_ERROR; 828 829 if (rv == 0) 830 ms->status = GSSAPI_AUTH_LAST; 831 return MECH_STEP; 832 833 case GSSAPI_AUTH_LAST: 834 saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_LAST"); 835 836 input.value = __UNCONST(in); 837 input.length = inlen; 838 if (unwrap_input_token(sess, &input) == -1) 839 return MECH_ERROR; 840 841 if ((rv = choose_qop(sess, ms->qop_mask)) == -1) 842 return MECH_ERROR; 843 844 ms->mech_sess.qop = rv; 845 846 if (ms->mech_sess.qop != QOP_NONE) { 847 if (ms->mech_sess.qop == QOP_CONF) { 848 /* 849 * XXX: where do we negotiate the cipher, 850 * or do we? 851 */ 852 } 853 if (set_maxbufs(sess) == -1) 854 return MECH_ERROR; 855 ms->dec_ctx = saslc__buffer32_create(sess, ms->imaxbuf); 856 ms->enc_ctx = saslc__buffer_create(sess, ms->omaxbuf); 857 } 858 if (wrap_output_token(sess, &output) == -1) 859 return MECH_ERROR; 860 861 if (prep_output(sess, &output, out, outlen) == -1) 862 return MECH_ERROR; 863 864 ms->status = GSSAPI_AUTH_DONE; 865 return MECH_OK; 866 867 case GSSAPI_AUTH_DONE: 868 assert(/*CONSTCOND*/0); /* XXX: impossible */ 869 saslc__error_set(ERR(sess), ERROR_MECH, 870 "already authenticated"); 871 return MECH_ERROR; 872 873#if 0 /* no default so the compiler can tell us if we miss an enum */ 874 default: 875 assert(/*CONSTCOND*/0); /* impossible */ 876 /*NOTREACHED*/ 877#endif 878 } 879 /*LINTED*/ 880 assert(/*CONSTCOND*/0); /* XXX: impossible */ 881 return MECH_ERROR; 882} 883 884/* mechanism definition */ 885const saslc__mech_t saslc__mech_gssapi = { 886 .name = "GSSAPI", 887 .flags = FLAG_NONE, 888 .create = saslc__mech_gssapi_create, 889 .cont = saslc__mech_gssapi_cont, 890 .encode = saslc__mech_gssapi_encode, 891 .decode = saslc__mech_gssapi_decode, 892 .destroy = saslc__mech_gssapi_destroy 893}; 894