1 /* $NetBSD: gss_mo.c,v 1.1.1.2 2014/04/24 12:45:29 pettai Exp $ */ 2 3 /* 4 * Copyright (c) 2010 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Portions Copyright (c) 2010 Apple Inc. All rights reserved. 9 * Portions Copyright (c) 2010 PADL Software Pty Ltd. All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * 3. Neither the name of the Institute nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include "mech_locl.h" 40 41 #include <crypto-headers.h> 42 43 static int 44 get_option_def(int def, gss_const_OID mech, gss_mo_desc *mo, gss_buffer_t value) 45 { 46 return def; 47 } 48 49 int 50 _gss_mo_get_option_1(gss_const_OID mech, gss_mo_desc *mo, gss_buffer_t value) 51 { 52 return get_option_def(1, mech, mo, value); 53 } 54 55 int 56 _gss_mo_get_option_0(gss_const_OID mech, gss_mo_desc *mo, gss_buffer_t value) 57 { 58 return get_option_def(0, mech, mo, value); 59 } 60 61 int 62 _gss_mo_get_ctx_as_string(gss_const_OID mech, gss_mo_desc *mo, gss_buffer_t value) 63 { 64 if (value) { 65 value->value = strdup((char *)mo->ctx); 66 if (value->value == NULL) 67 return GSS_S_FAILURE; 68 value->length = strlen((char *)mo->ctx); 69 } 70 return GSS_S_COMPLETE; 71 } 72 73 GSSAPI_LIB_FUNCTION int GSSAPI_LIB_CALL 74 gss_mo_set(gss_const_OID mech, gss_const_OID option, 75 int enable, gss_buffer_t value) 76 { 77 gssapi_mech_interface m; 78 size_t n; 79 80 if ((m = __gss_get_mechanism(mech)) == NULL) 81 return GSS_S_BAD_MECH; 82 83 for (n = 0; n < m->gm_mo_num; n++) 84 if (gss_oid_equal(option, m->gm_mo[n].option) && m->gm_mo[n].set) 85 return m->gm_mo[n].set(mech, &m->gm_mo[n], enable, value); 86 87 return GSS_S_UNAVAILABLE; 88 } 89 90 GSSAPI_LIB_FUNCTION int GSSAPI_LIB_CALL 91 gss_mo_get(gss_const_OID mech, gss_const_OID option, gss_buffer_t value) 92 { 93 gssapi_mech_interface m; 94 size_t n; 95 96 _mg_buffer_zero(value); 97 98 if ((m = __gss_get_mechanism(mech)) == NULL) 99 return GSS_S_BAD_MECH; 100 101 for (n = 0; n < m->gm_mo_num; n++) 102 if (gss_oid_equal(option, m->gm_mo[n].option) && m->gm_mo[n].get) 103 return m->gm_mo[n].get(mech, &m->gm_mo[n], value); 104 105 return GSS_S_UNAVAILABLE; 106 } 107 108 static void 109 add_all_mo(gssapi_mech_interface m, gss_OID_set *options, OM_uint32 mask) 110 { 111 OM_uint32 minor; 112 size_t n; 113 114 for (n = 0; n < m->gm_mo_num; n++) 115 if ((m->gm_mo[n].flags & mask) == mask) 116 gss_add_oid_set_member(&minor, m->gm_mo[n].option, options); 117 } 118 119 GSSAPI_LIB_FUNCTION void GSSAPI_LIB_CALL 120 gss_mo_list(gss_const_OID mech, gss_OID_set *options) 121 { 122 gssapi_mech_interface m; 123 OM_uint32 major, minor; 124 125 if (options == NULL) 126 return; 127 128 *options = GSS_C_NO_OID_SET; 129 130 if ((m = __gss_get_mechanism(mech)) == NULL) 131 return; 132 133 major = gss_create_empty_oid_set(&minor, options); 134 if (major != GSS_S_COMPLETE) 135 return; 136 137 add_all_mo(m, options, 0); 138 } 139 140 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL 141 gss_mo_name(gss_const_OID mech, gss_const_OID option, gss_buffer_t name) 142 { 143 gssapi_mech_interface m; 144 size_t n; 145 146 if (name == NULL) 147 return GSS_S_BAD_NAME; 148 149 if ((m = __gss_get_mechanism(mech)) == NULL) 150 return GSS_S_BAD_MECH; 151 152 for (n = 0; n < m->gm_mo_num; n++) { 153 if (gss_oid_equal(option, m->gm_mo[n].option)) { 154 /* 155 * If there is no name, its because its a GSS_C_MA and 156 * there is already a table for that. 157 */ 158 if (m->gm_mo[n].name) { 159 name->value = strdup(m->gm_mo[n].name); 160 if (name->value == NULL) 161 return GSS_S_BAD_NAME; 162 name->length = strlen(m->gm_mo[n].name); 163 return GSS_S_COMPLETE; 164 } else { 165 OM_uint32 junk; 166 return gss_display_mech_attr(&junk, option, 167 NULL, name, NULL); 168 } 169 } 170 } 171 return GSS_S_BAD_NAME; 172 } 173 174 /* 175 * Helper function to allow NULL name 176 */ 177 178 static OM_uint32 179 mo_value(const gss_const_OID mech, gss_const_OID option, gss_buffer_t name) 180 { 181 if (name == NULL) 182 return GSS_S_COMPLETE; 183 184 return gss_mo_get(mech, option, name); 185 } 186 187 /* code derived from draft-ietf-cat-sasl-gssapi-01 */ 188 static char basis_32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; 189 190 static OM_uint32 191 make_sasl_name(OM_uint32 *minor, const gss_OID mech, char sasl_name[16]) 192 { 193 EVP_MD_CTX *ctx; 194 char *p = sasl_name; 195 u_char hdr[2], hash[20], *h = hash; 196 197 if (mech->length > 127) 198 return GSS_S_BAD_MECH; 199 200 hdr[0] = 0x06; 201 hdr[1] = mech->length; 202 203 ctx = EVP_MD_CTX_create(); 204 EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); 205 EVP_DigestUpdate(ctx, hdr, 2); 206 EVP_DigestUpdate(ctx, mech->elements, mech->length); 207 EVP_DigestFinal_ex(ctx, hash, NULL); 208 209 memcpy(p, "GS2-", 4); 210 p += 4; 211 212 *p++ = basis_32[(h[0] >> 3)]; 213 *p++ = basis_32[((h[0] & 7) << 2) | (h[1] >> 6)]; 214 *p++ = basis_32[(h[1] & 0x3f) >> 1]; 215 *p++ = basis_32[((h[1] & 1) << 4) | (h[2] >> 4)]; 216 *p++ = basis_32[((h[2] & 0xf) << 1) | (h[3] >> 7)]; 217 *p++ = basis_32[(h[3] & 0x7f) >> 2]; 218 *p++ = basis_32[((h[3] & 3) << 3) | (h[4] >> 5)]; 219 *p++ = basis_32[(h[4] & 0x1f)]; 220 *p++ = basis_32[(h[5] >> 3)]; 221 *p++ = basis_32[((h[5] & 7) << 2) | (h[6] >> 6)]; 222 *p++ = basis_32[(h[6] & 0x3f) >> 1]; 223 224 *p = '\0'; 225 226 return GSS_S_COMPLETE; 227 } 228 229 /* 230 * gss_inquire_saslname_for_mech() wrapper that uses MIT SPI 231 */ 232 static OM_uint32 233 inquire_saslname_for_mech_compat(OM_uint32 *minor, 234 const gss_OID desired_mech, 235 gss_buffer_t sasl_mech_name, 236 gss_buffer_t mech_name, 237 gss_buffer_t mech_description) 238 { 239 struct gss_mech_compat_desc_struct *gmc; 240 gssapi_mech_interface m; 241 OM_uint32 major; 242 243 m = __gss_get_mechanism(desired_mech); 244 if (m == NULL) 245 return GSS_S_BAD_MECH; 246 247 gmc = m->gm_compat; 248 249 if (gmc != NULL && gmc->gmc_inquire_saslname_for_mech != NULL) { 250 major = gmc->gmc_inquire_saslname_for_mech(minor, 251 desired_mech, 252 sasl_mech_name, 253 mech_name, 254 mech_description); 255 } else { 256 major = GSS_S_UNAVAILABLE; 257 } 258 259 return major; 260 } 261 262 /** 263 * Returns different protocol names and description of the mechanism. 264 * 265 * @param minor_status minor status code 266 * @param desired_mech mech list query 267 * @param sasl_mech_name SASL GS2 protocol name 268 * @param mech_name gssapi protocol name 269 * @param mech_description description of gssapi mech 270 * 271 * @return returns GSS_S_COMPLETE or a error code. 272 * 273 * @ingroup gssapi 274 */ 275 276 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL 277 gss_inquire_saslname_for_mech(OM_uint32 *minor_status, 278 const gss_OID desired_mech, 279 gss_buffer_t sasl_mech_name, 280 gss_buffer_t mech_name, 281 gss_buffer_t mech_description) 282 { 283 OM_uint32 major; 284 285 _mg_buffer_zero(sasl_mech_name); 286 _mg_buffer_zero(mech_name); 287 _mg_buffer_zero(mech_description); 288 289 if (minor_status) 290 *minor_status = 0; 291 292 if (desired_mech == NULL) 293 return GSS_S_BAD_MECH; 294 295 major = mo_value(desired_mech, GSS_C_MA_SASL_MECH_NAME, sasl_mech_name); 296 if (major == GSS_S_COMPLETE) { 297 /* Native SPI */ 298 major = mo_value(desired_mech, GSS_C_MA_MECH_NAME, mech_name); 299 if (GSS_ERROR(major)) 300 return major; 301 302 major = mo_value(desired_mech, GSS_C_MA_MECH_DESCRIPTION, mech_description); 303 if (GSS_ERROR(major)) 304 return major; 305 } 306 307 if (GSS_ERROR(major)) { 308 /* API-as-SPI compatibility */ 309 major = inquire_saslname_for_mech_compat(minor_status, 310 desired_mech, 311 sasl_mech_name, 312 mech_name, 313 mech_description); 314 } 315 316 if (GSS_ERROR(major)) { 317 /* Algorithmically dervied SASL mechanism name */ 318 char buf[16]; 319 gss_buffer_desc tmp = { sizeof(buf) - 1, buf }; 320 321 major = make_sasl_name(minor_status, desired_mech, buf); 322 if (GSS_ERROR(major)) 323 return major; 324 325 major = _gss_copy_buffer(minor_status, &tmp, sasl_mech_name); 326 if (GSS_ERROR(major)) 327 return major; 328 } 329 330 return major; 331 } 332 333 /** 334 * Find a mech for a sasl name 335 * 336 * @param minor_status minor status code 337 * @param sasl_mech_name 338 * @param mech_type 339 * 340 * @return returns GSS_S_COMPLETE or an error code. 341 */ 342 343 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL 344 gss_inquire_mech_for_saslname(OM_uint32 *minor_status, 345 const gss_buffer_t sasl_mech_name, 346 gss_OID *mech_type) 347 { 348 struct _gss_mech_switch *m; 349 gss_buffer_desc name; 350 OM_uint32 major, junk; 351 char buf[16]; 352 353 _gss_load_mech(); 354 355 *mech_type = NULL; 356 357 HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) { 358 struct gss_mech_compat_desc_struct *gmc; 359 360 /* Native SPI */ 361 major = mo_value(&m->gm_mech_oid, GSS_C_MA_SASL_MECH_NAME, &name); 362 if (major == GSS_S_COMPLETE && 363 name.length == sasl_mech_name->length && 364 memcmp(name.value, sasl_mech_name->value, name.length) == 0) { 365 gss_release_buffer(&junk, &name); 366 *mech_type = &m->gm_mech_oid; 367 return GSS_S_COMPLETE; 368 } 369 gss_release_buffer(&junk, &name); 370 371 if (GSS_ERROR(major)) { 372 /* API-as-SPI compatibility */ 373 gmc = m->gm_mech.gm_compat; 374 if (gmc && gmc->gmc_inquire_mech_for_saslname) { 375 major = gmc->gmc_inquire_mech_for_saslname(minor_status, 376 sasl_mech_name, 377 mech_type); 378 if (major == GSS_S_COMPLETE) 379 return GSS_S_COMPLETE; 380 } 381 } 382 383 if (GSS_ERROR(major)) { 384 /* Algorithmically dervied SASL mechanism name */ 385 if (sasl_mech_name->length == 16 && 386 make_sasl_name(minor_status, &m->gm_mech_oid, buf) == GSS_S_COMPLETE && 387 memcmp(buf, sasl_mech_name->value, 16) == 0) { 388 *mech_type = &m->gm_mech_oid; 389 return GSS_S_COMPLETE; 390 } 391 } 392 } 393 394 return GSS_S_BAD_MECH; 395 } 396 397 /* 398 * Test mechanism against indicated attributes using both Heimdal and 399 * MIT SPIs. 400 */ 401 static int 402 test_mech_attrs(gssapi_mech_interface mi, 403 gss_const_OID_set mech_attrs, 404 gss_const_OID_set against_attrs, 405 int except) 406 { 407 size_t n, m; 408 int eq = 0; 409 410 if (against_attrs == GSS_C_NO_OID_SET) 411 return 1; 412 413 for (n = 0; n < against_attrs->count; n++) { 414 for (m = 0; m < mi->gm_mo_num; m++) { 415 eq = gss_oid_equal(mi->gm_mo[m].option, 416 &against_attrs->elements[n]); 417 if (eq) 418 break; 419 } 420 if (mech_attrs != GSS_C_NO_OID_SET) { 421 for (m = 0; m < mech_attrs->count; m++) { 422 eq = gss_oid_equal(&mech_attrs->elements[m], 423 &against_attrs->elements[n]); 424 if (eq) 425 break; 426 } 427 } 428 if (!eq ^ except) 429 return 0; 430 } 431 432 return 1; 433 } 434 435 /** 436 * Return set of mechanism that fullfill the criteria 437 * 438 * @param minor_status minor status code 439 * @param desired_mech_attrs 440 * @param except_mech_attrs 441 * @param critical_mech_attrs 442 * @param mechs returned mechs, free with gss_release_oid_set(). 443 * 444 * @return returns GSS_S_COMPLETE or an error code. 445 */ 446 447 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL 448 gss_indicate_mechs_by_attrs(OM_uint32 * minor_status, 449 gss_const_OID_set desired_mech_attrs, 450 gss_const_OID_set except_mech_attrs, 451 gss_const_OID_set critical_mech_attrs, 452 gss_OID_set *mechs) 453 { 454 struct _gss_mech_switch *ms; 455 gss_OID_set mech_attrs = GSS_C_NO_OID_SET; 456 gss_OID_set known_mech_attrs = GSS_C_NO_OID_SET; 457 OM_uint32 major; 458 459 major = gss_create_empty_oid_set(minor_status, mechs); 460 if (GSS_ERROR(major)) 461 return major; 462 463 _gss_load_mech(); 464 465 HEIM_SLIST_FOREACH(ms, &_gss_mechs, gm_link) { 466 gssapi_mech_interface mi = &ms->gm_mech; 467 struct gss_mech_compat_desc_struct *gmc = mi->gm_compat; 468 OM_uint32 tmp; 469 470 if (gmc && gmc->gmc_inquire_attrs_for_mech) { 471 major = gmc->gmc_inquire_attrs_for_mech(minor_status, 472 &mi->gm_mech_oid, 473 &mech_attrs, 474 &known_mech_attrs); 475 if (GSS_ERROR(major)) 476 continue; 477 } 478 479 /* 480 * Test mechanism supports all of desired_mech_attrs; 481 * none of except_mech_attrs; 482 * and knows of all critical_mech_attrs. 483 */ 484 if (test_mech_attrs(mi, mech_attrs, desired_mech_attrs, 0) && 485 test_mech_attrs(mi, mech_attrs, except_mech_attrs, 1) && 486 test_mech_attrs(mi, known_mech_attrs, critical_mech_attrs, 0)) { 487 major = gss_add_oid_set_member(minor_status, &mi->gm_mech_oid, mechs); 488 } 489 490 gss_release_oid_set(&tmp, &mech_attrs); 491 gss_release_oid_set(&tmp, &known_mech_attrs); 492 493 if (GSS_ERROR(major)) 494 break; 495 } 496 497 return major; 498 } 499 500 /** 501 * List support attributes for a mech and/or all mechanisms. 502 * 503 * @param minor_status minor status code 504 * @param mech given together with mech_attr will return the list of 505 * attributes for mechanism, can optionally be GSS_C_NO_OID. 506 * @param mech_attr see mech parameter, can optionally be NULL, 507 * release with gss_release_oid_set(). 508 * @param known_mech_attrs all attributes for mechanisms supported, 509 * release with gss_release_oid_set(). 510 * 511 * @ingroup gssapi 512 */ 513 514 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL 515 gss_inquire_attrs_for_mech(OM_uint32 * minor_status, 516 gss_const_OID mech, 517 gss_OID_set *mech_attr, 518 gss_OID_set *known_mech_attrs) 519 { 520 OM_uint32 major, junk; 521 522 if (known_mech_attrs) 523 *known_mech_attrs = GSS_C_NO_OID_SET; 524 525 if (mech_attr && mech) { 526 gssapi_mech_interface m; 527 struct gss_mech_compat_desc_struct *gmc; 528 529 if ((m = __gss_get_mechanism(mech)) == NULL) { 530 *minor_status = 0; 531 return GSS_S_BAD_MECH; 532 } 533 534 gmc = m->gm_compat; 535 536 if (gmc && gmc->gmc_inquire_attrs_for_mech) { 537 major = gmc->gmc_inquire_attrs_for_mech(minor_status, 538 mech, 539 mech_attr, 540 known_mech_attrs); 541 } else { 542 major = gss_create_empty_oid_set(minor_status, mech_attr); 543 if (major == GSS_S_COMPLETE) 544 add_all_mo(m, mech_attr, GSS_MO_MA); 545 } 546 if (GSS_ERROR(major)) 547 return major; 548 } 549 550 if (known_mech_attrs) { 551 struct _gss_mech_switch *m; 552 553 if (*known_mech_attrs == GSS_C_NO_OID_SET) { 554 major = gss_create_empty_oid_set(minor_status, known_mech_attrs); 555 if (GSS_ERROR(major)) { 556 if (mech_attr) 557 gss_release_oid_set(&junk, mech_attr); 558 return major; 559 } 560 } 561 562 _gss_load_mech(); 563 564 HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) 565 add_all_mo(&m->gm_mech, known_mech_attrs, GSS_MO_MA); 566 } 567 568 569 return GSS_S_COMPLETE; 570 } 571 572 /** 573 * Return names and descriptions of mech attributes 574 * 575 * @param minor_status minor status code 576 * @param mech_attr 577 * @param name 578 * @param short_desc 579 * @param long_desc 580 * 581 * @return returns GSS_S_COMPLETE or an error code. 582 */ 583 584 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL 585 gss_display_mech_attr(OM_uint32 * minor_status, 586 gss_const_OID mech_attr, 587 gss_buffer_t name, 588 gss_buffer_t short_desc, 589 gss_buffer_t long_desc) 590 { 591 struct _gss_oid_name_table *ma = NULL; 592 OM_uint32 major; 593 size_t n; 594 595 _mg_buffer_zero(name); 596 _mg_buffer_zero(short_desc); 597 _mg_buffer_zero(long_desc); 598 599 if (minor_status) 600 *minor_status = 0; 601 602 for (n = 0; ma == NULL && _gss_ont_ma[n].oid; n++) 603 if (gss_oid_equal(mech_attr, _gss_ont_ma[n].oid)) 604 ma = &_gss_ont_ma[n]; 605 606 if (ma == NULL) 607 return GSS_S_BAD_MECH_ATTR; 608 609 if (name) { 610 gss_buffer_desc bd; 611 bd.value = rk_UNCONST(ma->name); 612 bd.length = strlen(ma->name); 613 major = _gss_copy_buffer(minor_status, &bd, name); 614 if (major != GSS_S_COMPLETE) 615 return major; 616 } 617 618 if (short_desc) { 619 gss_buffer_desc bd; 620 bd.value = rk_UNCONST(ma->short_desc); 621 bd.length = strlen(ma->short_desc); 622 major = _gss_copy_buffer(minor_status, &bd, short_desc); 623 if (major != GSS_S_COMPLETE) 624 return major; 625 } 626 627 if (long_desc) { 628 gss_buffer_desc bd; 629 bd.value = rk_UNCONST(ma->long_desc); 630 bd.length = strlen(ma->long_desc); 631 major = _gss_copy_buffer(minor_status, &bd, long_desc); 632 if (major != GSS_S_COMPLETE) 633 return major; 634 } 635 636 return GSS_S_COMPLETE; 637 } 638