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