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
388 * @param 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 * @param minor_status minor status code
489 * @param desired_mech_attrs
490 * @param except_mech_attrs
491 * @param critical_mech_attrs
492 * @param mechs returned mechs, free with gss_release_oid_set().
493 *
494 * @return returns GSS_S_COMPLETE or an error code.
495 */
496
497GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
498gss_indicate_mechs_by_attrs(OM_uint32 * minor_status,
499			    gss_const_OID_set desired_mech_attrs,
500			    gss_const_OID_set except_mech_attrs,
501			    gss_const_OID_set critical_mech_attrs,
502			    gss_OID_set *mechs)
503{
504    struct _gss_mech_switch *ms;
505    gss_OID_set mech_attrs = GSS_C_NO_OID_SET;
506    gss_OID_set known_mech_attrs = GSS_C_NO_OID_SET;
507    OM_uint32 major;
508
509    major = gss_create_empty_oid_set(minor_status, mechs);
510    if (GSS_ERROR(major))
511	return major;
512
513    _gss_load_mech();
514
515    HEIM_SLIST_FOREACH(ms, &_gss_mechs, gm_link) {
516	gssapi_mech_interface mi = &ms->gm_mech;
517        struct gss_mech_compat_desc_struct *gmc = mi->gm_compat;
518        OM_uint32 tmp;
519
520        if (gmc && gmc->gmc_inquire_attrs_for_mech) {
521            major = gmc->gmc_inquire_attrs_for_mech(minor_status,
522                                                    &mi->gm_mech_oid,
523                                                    &mech_attrs,
524                                                    &known_mech_attrs);
525            if (GSS_ERROR(major))
526                continue;
527        }
528
529        /*
530         * Test mechanism supports all of desired_mech_attrs;
531         * none of except_mech_attrs;
532         * and knows of all critical_mech_attrs.
533         */
534        if (test_mech_attrs(mi, mech_attrs,       desired_mech_attrs,  0) &&
535            test_mech_attrs(mi, mech_attrs,       except_mech_attrs,   1) &&
536            test_mech_attrs(mi, known_mech_attrs, critical_mech_attrs, 0)) {
537            major = gss_add_oid_set_member(minor_status, &mi->gm_mech_oid, mechs);
538        }
539
540        gss_release_oid_set(&tmp, &mech_attrs);
541        gss_release_oid_set(&tmp, &known_mech_attrs);
542
543        if (GSS_ERROR(major))
544            break;
545    }
546
547    return major;
548}
549
550/**
551 * List support attributes for a mech and/or all mechanisms.
552 *
553 * @param minor_status minor status code
554 * @param mech given together with mech_attr will return the list of
555 *        attributes for mechanism, can optionally be GSS_C_NO_OID.
556 * @param mech_attr see mech parameter, can optionally be NULL,
557 *        release with gss_release_oid_set().
558 * @param known_mech_attrs all attributes for mechanisms supported,
559 *        release with gss_release_oid_set().
560 *
561 * @ingroup gssapi
562 */
563
564GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
565gss_inquire_attrs_for_mech(OM_uint32 * minor_status,
566			   gss_const_OID mech,
567			   gss_OID_set *mech_attr,
568			   gss_OID_set *known_mech_attrs)
569{
570    OM_uint32 major, junk;
571
572    if (known_mech_attrs)
573        *known_mech_attrs = GSS_C_NO_OID_SET;
574
575    if (mech_attr && mech) {
576	gssapi_mech_interface m;
577        struct gss_mech_compat_desc_struct *gmc;
578
579	if ((m = __gss_get_mechanism(mech)) == NULL) {
580	    *minor_status = 0;
581	    return GSS_S_BAD_MECH;
582	}
583
584        gmc = m->gm_compat;
585
586        if (gmc && gmc->gmc_inquire_attrs_for_mech) {
587            major = gmc->gmc_inquire_attrs_for_mech(minor_status,
588                                                    mech,
589                                                    mech_attr,
590                                                    known_mech_attrs);
591        } else {
592	    major = gss_create_empty_oid_set(minor_status, mech_attr);
593            if (major == GSS_S_COMPLETE)
594	        add_all_mo(m, mech_attr, GSS_MO_MA);
595        }
596	if (GSS_ERROR(major))
597	    return major;
598    }
599
600    if (known_mech_attrs) {
601	struct _gss_mech_switch *m;
602
603        if (*known_mech_attrs == GSS_C_NO_OID_SET) {
604	    major = gss_create_empty_oid_set(minor_status, known_mech_attrs);
605	    if (GSS_ERROR(major)) {
606	        if (mech_attr)
607		    gss_release_oid_set(&junk, mech_attr);
608	        return major;
609	    }
610        }
611
612	_gss_load_mech();
613
614	HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link)
615	    add_all_mo(&m->gm_mech, known_mech_attrs, GSS_MO_MA);
616    }
617
618
619    return GSS_S_COMPLETE;
620}
621
622/**
623 * Return names and descriptions of mech attributes
624 *
625 * @param minor_status minor status code
626 * @param mech_attr
627 * @param name
628 * @param short_desc
629 * @param long_desc
630 *
631 * @return returns GSS_S_COMPLETE or an error code.
632 */
633
634GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
635gss_display_mech_attr(OM_uint32 * minor_status,
636		      gss_const_OID mech_attr,
637		      gss_buffer_t name,
638		      gss_buffer_t short_desc,
639		      gss_buffer_t long_desc)
640{
641    struct _gss_oid_name_table *ma = NULL;
642    OM_uint32 major;
643    size_t n;
644
645    _mg_buffer_zero(name);
646    _mg_buffer_zero(short_desc);
647    _mg_buffer_zero(long_desc);
648
649    if (minor_status)
650	*minor_status = 0;
651
652    for (n = 0; ma == NULL && _gss_ont_ma[n].oid; n++)
653	if (gss_oid_equal(mech_attr, _gss_ont_ma[n].oid))
654	    ma = &_gss_ont_ma[n];
655
656    if (ma == NULL)
657	return GSS_S_BAD_MECH_ATTR;
658
659    if (name) {
660	gss_buffer_desc bd;
661	bd.value = rk_UNCONST(ma->name);
662	bd.length = strlen(ma->name);
663	major = _gss_copy_buffer(minor_status, &bd, name);
664	if (major != GSS_S_COMPLETE)
665	    return major;
666    }
667
668    if (short_desc) {
669	gss_buffer_desc bd;
670	bd.value = rk_UNCONST(ma->short_desc);
671	bd.length = strlen(ma->short_desc);
672	major = _gss_copy_buffer(minor_status, &bd, short_desc);
673	if (major != GSS_S_COMPLETE)
674	    return major;
675    }
676
677    if (long_desc) {
678	gss_buffer_desc bd;
679	bd.value = rk_UNCONST(ma->long_desc);
680	bd.length = strlen(ma->long_desc);
681	major = _gss_copy_buffer(minor_status, &bd, long_desc);
682	if (major != GSS_S_COMPLETE)
683	    return major;
684    }
685
686    return GSS_S_COMPLETE;
687}
688