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