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