1/*-
2 * Copyright (c) 2009 - 2011 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 - 2013 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include "mech_locl.h"
31#include <heim_threads.h>
32
33#include <Security/Security.h>
34#include "krb5.h"
35
36/**
37 * Acquire a new initial credentials using long term credentials (password, certificate).
38 *
39 * Credentials acquired should be free-ed with gss_release_cred() or
40 * destroyed with (removed from storage) gss_destroy_cred().
41 *
42 * Some mechanism types can not directly acquire or validate
43 * credential (for example PK-U2U, SCRAM, NTLM or IAKERB), for those
44 * mechanisms its instead the gss_init_sec_context() that will either acquire or
45 * force validation of the credential.
46 *
47 * This function is blocking and should not be used on threads used for UI updates.
48 *
49 * @param desired_name name to use to acquire credential. Import the name using gss_import_name(). The type of the name has to be supported by the desired_mech used.
50 *
51 * @param desired_mech mechanism to use to acquire credential. GSS_C_NO_OID is not valid input and a mechanism must be selected. For example GSS_KRB5_MECHANISM, GSS_NTLM_MECHNISM or any other mechanisms supported by the implementation. See gss_indicate_mechs().
52 *
53 * @param attributes CFDictionary that contains how to acquire the credential, see below for examples
54 *
55 * @param output_cred_handle the resulting credential handle, value is set to GSS_C_NO_CREDENTIAL on failure.
56 *
57 * @param error an CFErrorRef returned in case of an error, that needs to be released with CFRelease() by the caller, input can be NULL.
58 *
59 * @returns a gss_error code, see the CFErrorRef passed back in error for the failure message.
60 *
61 * attributes must contains one of the following keys
62 * * kGSSICPassword - CFStringRef password
63 * * kGSSICCertificate - SecIdentityRef to the certificate to use with PKINIT/PKU2U
64 *
65 * optional keys
66 * * kGSSCredentialUsage - one of kGSS_C_INITIATE, kGSS_C_ACCEPT, kGSS_C_BOTH, default if not given is kGSS_C_INITIATE
67 * * kGSSICVerifyCredential - validate the credential with a trusted source that there was no MITM
68 * * kGSSICLKDCHostname - CFStringRef hostname of LKDC hostname
69 * * kGSSICKerberosCacheName - CFStringRef name of cache that will be created (including type)
70 * * kGSSICAppIdentifierACL - CFArrayRef[CFStringRef] prefix of bundle ID allowed to access this credential
71 *
72 *
73 *
74 * @ingroup gssapi
75 */
76
77OM_uint32 GSSAPI_LIB_FUNCTION
78gss_aapl_initial_cred(const gss_name_t desired_name,
79		      gss_const_OID desired_mech,
80		      CFDictionaryRef attributes,
81		      gss_cred_id_t * output_cred_handle,
82		      CFErrorRef *error)
83{
84    OM_uint32 major_status, minor_status;
85    gss_buffer_desc credential;
86    CFStringRef usage;
87    CFTypeRef password, certificate;
88    gss_cred_usage_t cred_usage = GSS_C_INITIATE;
89    gss_const_OID cred_type;
90    void *cred_value;
91
92    credential.value = NULL;
93    credential.length = 0;
94
95    HEIM_WARN_BLOCKING("gss_aapl_initial_cred", warn_once);
96
97    if (error)
98	*error = NULL;
99
100    if (desired_mech == GSS_C_NO_OID)
101	return GSS_S_BAD_MECH;
102    if (desired_name == GSS_C_NO_NAME)
103	return GSS_S_BAD_NAME;
104
105    if (output_cred_handle == NULL)
106	return GSS_S_CALL_INACCESSIBLE_READ;
107
108    *output_cred_handle = GSS_C_NO_CREDENTIAL;
109
110    /* require password or certificate */
111    password = CFDictionaryGetValue(attributes, kGSSICPassword);
112    certificate = CFDictionaryGetValue(attributes, kGSSICCertificate);
113    if (password == NULL && certificate == NULL) {
114	return GSS_S_CALL_INACCESSIBLE_READ;
115    }
116
117    /* check usage */
118    usage = CFDictionaryGetValue(attributes, kGSSCredentialUsage);
119    if (usage && CFGetTypeID(usage) == CFStringGetTypeID()) {
120	if (CFStringCompare(usage, kGSS_C_INITIATE, 0) == kCFCompareEqualTo)
121	    cred_usage = GSS_C_INITIATE;
122	else if (CFStringCompare(usage, kGSS_C_ACCEPT, 0) == kCFCompareEqualTo)
123	    cred_usage = GSS_C_ACCEPT;
124	else if (CFStringCompare(usage, kGSS_C_BOTH, 0) == kCFCompareEqualTo)
125	    cred_usage = GSS_C_BOTH;
126	else
127	    return GSS_S_FAILURE;
128    }
129
130    if (gss_oid_equal(desired_mech, GSS_KRB5_MECHANISM)) {
131
132	cred_value = (void *)attributes;
133	cred_type = GSS_C_CRED_HEIMBASE;
134
135    } else if (password && CFGetTypeID(password) == CFStringGetTypeID()) {
136	char *str = rk_cfstring2cstring(password);
137	if (str == NULL)
138	    return GSS_S_FAILURE;
139
140	credential.value = str;
141	credential.length = strlen(str);
142	cred_value = &credential;
143	cred_type = GSS_C_CRED_PASSWORD;
144
145    } else if (password && CFGetTypeID(password) == CFDataGetTypeID()) {
146	credential.value = malloc(CFDataGetLength(password));
147	if (credential.value == NULL)
148	    return GSS_S_FAILURE;
149
150	credential.length = CFDataGetLength(password);
151	memcpy(credential.value, CFDataGetBytePtr(password), CFDataGetLength(password));
152
153	cred_value = &credential;
154	cred_type = GSS_C_CRED_PASSWORD;
155    } else if (certificate && CFGetTypeID(certificate) == SecIdentityGetTypeID()) {
156	cred_value = rk_UNCONST(certificate);
157	cred_type = GSS_C_CRED_SecIdentity;
158    } else if (certificate && CFGetTypeID(certificate) == SecCertificateGetTypeID()) {
159	cred_value = rk_UNCONST(certificate);
160	cred_type = GSS_C_CRED_SecIdentity;
161    } else
162	return GSS_S_FAILURE;
163
164    major_status = gss_acquire_cred_ext(&minor_status,
165					desired_name,
166					cred_type,
167					cred_value,
168					GSS_C_INDEFINITE,
169					desired_mech,
170					cred_usage,
171					output_cred_handle);
172    if (credential.length) {
173	memset(credential.value, 0, credential.length);
174	free(credential.value);
175    }
176
177    if (major_status && error) {
178	*error = _gss_mg_create_cferror(major_status, minor_status, desired_mech);
179	return major_status;
180    }
181
182    /**
183     * The credential can be validated by adding kGSSICVerifyCredential to the attributes with any value.
184     */
185
186    if (CFDictionaryGetValue(attributes, kGSSICVerifyCredential)) {
187	gss_buffer_set_t bufferset = GSS_C_NO_BUFFER_SET;
188
189	major_status = gss_inquire_cred_by_oid(&minor_status, *output_cred_handle,
190					       GSS_C_CRED_VALIDATE, &bufferset);
191	if (major_status == GSS_S_COMPLETE)
192	    gss_release_buffer_set(&minor_status, &bufferset);
193	else {
194	    if (error)
195		*error = _gss_mg_create_cferror(major_status, minor_status, desired_mech);
196	    gss_destroy_cred(&minor_status, output_cred_handle);
197	}
198    }
199
200    return major_status;
201}
202
203/**
204 * Change pasword for a gss name
205 *
206 * @param name name to change password for
207 * @param mech mechanism to use
208 * @param attributes old and new password (kGSSChangePasswordOldPassword and kGSSChangePasswordNewPassword) and other attributes.
209 * @param error if not NULL, error might be set case function doesn't
210 *       return GSS_S_COMPLETE, in that case is must be released with
211 *       CFRelease().
212 *
213 * @returns returns GSS_S_COMPLETE on success, error might be set if passed in.
214 *
215 * @ingroup gssapi
216 */
217
218OM_uint32 GSSAPI_LIB_FUNCTION
219gss_aapl_change_password(const gss_name_t name,
220			 gss_const_OID mech,
221			 CFDictionaryRef attributes,
222			 CFErrorRef *error)
223{
224    struct _gss_mechanism_name *mn = NULL;
225    char *oldpw = NULL, *newpw = NULL;
226    OM_uint32 maj_stat, min_stat;
227    gssapi_mech_interface m;
228    CFStringRef old, new;
229
230    _gss_load_mech();
231
232    m = __gss_get_mechanism(mech);
233    if (m == NULL) {
234	maj_stat = GSS_S_BAD_MECH;
235	min_stat = 0;
236	goto out;
237    }
238
239    if (m->gm_aapl_change_password == NULL) {
240	maj_stat = GSS_S_UNAVAILABLE;
241	min_stat = 0;
242	goto out;
243    }
244
245    maj_stat = _gss_find_mn(&min_stat, (struct _gss_name *)name, mech, &mn);
246    if (maj_stat != GSS_S_COMPLETE)
247	goto out;
248
249    old = CFDictionaryGetValue(attributes, kGSSChangePasswordOldPassword);
250    new = CFDictionaryGetValue(attributes, kGSSChangePasswordNewPassword);
251
252    heim_assert(old != NULL, "old password missing");
253    heim_assert(new != NULL, "new password missing");
254
255    oldpw = rk_cfstring2cstring(old);
256    newpw = rk_cfstring2cstring(new);
257
258    if (oldpw == NULL || newpw == NULL) {
259	maj_stat = GSS_S_FAILURE;
260	min_stat = 0;
261	goto out;
262    }
263
264    maj_stat = m->gm_aapl_change_password(&min_stat,
265					  mn->gmn_name,
266					  oldpw, newpw);
267    if (maj_stat)
268	_gss_mg_error(m, min_stat);
269
270 out:
271    if (maj_stat && error)
272	*error = _gss_mg_create_cferror(maj_stat, min_stat, mech);
273
274    if (oldpw) {
275	memset(oldpw, 0, strlen(oldpw));
276	free(oldpw);
277    }
278    if (newpw) {
279	memset(newpw, 0, strlen(newpw));
280	free(newpw);
281    }
282
283    return maj_stat;
284}
285
286/**
287 * Returns a copy of the UUID of the GSS credential
288 *
289 * @param credential credential
290 *
291 * @returns CFUUIDRef that can be used to turn into a credential,
292 * normal CoreFoundaton rules for rules applies so the CFUUIDRef needs
293 * to be released.
294 *
295 * @ingroup gssapi
296 */
297
298CFUUIDRef
299GSSCredentialCopyUUID(gss_cred_id_t credential)
300{
301    OM_uint32 major, minor;
302    gss_buffer_set_t dataset = GSS_C_NO_BUFFER_SET;
303    krb5_error_code ret;
304    krb5_uuid uuid;
305    CFUUIDBytes cfuuid;
306
307    major = gss_inquire_cred_by_oid(&minor, credential, GSS_C_NT_UUID, &dataset);
308    if (major || dataset->count != 1) {
309	gss_release_buffer_set(&minor, &dataset);
310	return NULL;
311    }
312
313    if (dataset->elements[0].length != 36) {
314	gss_release_buffer_set(&minor, &dataset);
315	return NULL;
316    }
317
318    ret = krb5_string_to_uuid(dataset->elements[0].value, uuid);
319    gss_release_buffer_set(&minor, &dataset);
320    if (ret)
321	return NULL;
322
323    memcpy(&cfuuid, uuid, sizeof(uuid));
324
325    return CFUUIDCreateFromUUIDBytes(NULL, cfuuid);
326}
327
328/**
329 * Returns a GSS credential for a given UUID if the credential exists.
330 *
331 * @param uuid the UUID of the credential to fetch
332 *
333 * @returns a gss_cred_id_t, normal CoreFoundaton rules for rules
334 * applies so the CFUUIDRef needs to be released with either CFRelease() or gss_release_name().
335 *
336 * @ingroup gssapi
337 */
338
339gss_cred_id_t GSSAPI_LIB_FUNCTION
340GSSCreateCredentialFromUUID(CFUUIDRef uuid)
341{
342    OM_uint32 min_stat, maj_stat;
343    gss_cred_id_t cred;
344    CFStringRef name;
345    gss_name_t gname;
346
347    name = CFUUIDCreateString(NULL, uuid);
348    if (name == NULL)
349	return NULL;
350
351    gname = GSSCreateName(name, GSS_C_NT_UUID, NULL);
352    CFRelease(name);
353    if (gname == NULL)
354	return NULL;
355
356    maj_stat = gss_acquire_cred(&min_stat, gname, GSS_C_INDEFINITE, NULL,
357				GSS_C_INITIATE, &cred, NULL, NULL);
358    gss_release_name(&min_stat, &gname);
359    if (maj_stat != GSS_S_COMPLETE)
360	return NULL;
361
362    return cred;
363}
364
365static CFStringRef
366CopyFoldString(CFStringRef host)
367{
368    CFMutableStringRef string = CFStringCreateMutableCopy(NULL, 0, host);
369    static dispatch_once_t once;
370    static CFLocaleRef locale;
371    dispatch_once(&once, ^{
372	    locale = CFLocaleCreate(NULL, CFSTR("C"));
373	});
374    CFStringFold(string, kCFCompareCaseInsensitive, locale);
375    return string;
376}
377
378static bool
379FoldedHostName(CFStringRef stringOrURL, CFStringRef *scheme, CFStringRef *host, CFStringRef *path)
380{
381    CFRange range;
382
383    *scheme = NULL;
384    *host = NULL;
385    *path = NULL;
386
387    range = CFStringFind(stringOrURL, CFSTR(":"), 0);
388    if (range.location != kCFNotFound) {
389	CFURLRef url;
390
391	url = CFURLCreateWithString(NULL, stringOrURL, NULL);
392	if (url) {
393	    CFStringRef hn = CFURLCopyHostName(url);
394	    if (hn == NULL) {
395		*host = CFSTR("");
396	    } else {
397		*host = CopyFoldString(hn);
398		CFRelease(hn);
399		if (*host == NULL) {
400		    CFRelease(url);
401		    return false;
402		}
403	    }
404
405	    *scheme = CFURLCopyScheme(url);
406	    if (*scheme == NULL)
407		*scheme = CFSTR("");
408
409	    *path = CFURLCopyPath(url);
410	    if (*path == NULL || CFStringCompare(*path, CFSTR(""), 0) == kCFCompareEqualTo) {
411		if (*path)
412		    CFRelease(*path);
413		*path = CFSTR("/");
414	    }
415	    CFRelease(url);
416	}
417    }
418
419    if (*host == NULL) {
420	*host = CopyFoldString(stringOrURL);
421	if (*scheme)
422	    CFRelease(*scheme);
423	*scheme = CFSTR("any");
424	*path = CFSTR("/");
425    }
426
427    return true;
428}
429
430/*
431 *
432 */
433
434void
435GSSRuleAddMatch(CFMutableDictionaryRef rules, CFStringRef host, CFStringRef value)
436{
437    CFStringRef scheme = NULL, hostname = NULL, path = NULL;
438    CFMutableDictionaryRef match;
439
440    if (!FoldedHostName(host, &scheme, &hostname, &path))
441	return;
442
443    match = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
444    if (match == NULL)
445	goto out;
446
447    CFDictionarySetValue(match, CFSTR("scheme"), scheme);
448    CFDictionarySetValue(match, CFSTR("path"), path);
449    CFDictionarySetValue(match, CFSTR("value"), value);
450
451    CFArrayRef array = CFDictionaryGetValue(rules, hostname);
452    CFMutableArrayRef mutable;
453
454    if (array) {
455	mutable = CFArrayCreateMutableCopy(NULL, 0, array);
456    } else {
457	mutable = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
458    }
459    if (mutable) {
460
461	CFIndex n, count = CFArrayGetCount(mutable);
462
463	for (n = 0; n < count; n++) {
464	    CFDictionaryRef item = (CFDictionaryRef)CFArrayGetValueAtIndex(mutable, n);
465	    CFStringRef p = CFDictionaryGetValue(item, CFSTR("path"));
466	    CFStringRef s = CFDictionaryGetValue(item, CFSTR("scheme"));
467
468	    if (CFStringCompare(s, scheme, kCFCompareCaseInsensitive) == kCFCompareLessThan)
469		continue;
470
471	    if (CFStringHasPrefix(path, p)) {
472		CFArrayInsertValueAtIndex(mutable, n, match);
473		break;
474	    }
475	}
476	if (n >= count)
477	    CFArrayAppendValue(mutable, match);
478
479	CFDictionarySetValue(rules, hostname, mutable);
480	CFRelease(mutable);
481    }
482
483out:
484    CFRelease(scheme);
485    CFRelease(hostname);
486    CFRelease(path);
487    if (match)
488	CFRelease(match);
489}
490
491/*
492 * host is a URL string or hostname string
493 */
494
495CFStringRef
496GSSRuleGetMatch(CFDictionaryRef rules, CFStringRef hostname)
497{
498    CFStringRef scheme = NULL, hostFolded = NULL, path = NULL;
499    CFTypeRef result = NULL;
500    const char *p;
501
502    if (!FoldedHostName(hostname, &scheme, &hostFolded, &path))
503	return NULL;
504
505    char *host = rk_cfstring2cstring(hostFolded);
506    CFRelease(hostFolded);
507    if (host == NULL) {
508	CFRelease(path);
509	return NULL;
510    }
511
512    if (host[0] == '\0') {
513	CFRelease(scheme);
514	free(host);
515	CFRelease(path);
516	return NULL;
517    }
518
519    for (p = host; p != NULL && result == NULL; p = strchr(p + 1, '.')) {
520	CFStringRef partial = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8);
521	CFArrayRef array = (CFArrayRef)CFDictionaryGetValue(rules, partial);
522
523	CFRelease(partial);
524
525	if (array) {
526	    CFIndex n, count = CFArrayGetCount(array);
527
528	    for (n = 0; n < count && result == NULL; n++) {
529		CFDictionaryRef item = (CFDictionaryRef)CFArrayGetValueAtIndex(array, n);
530
531		CFStringRef matchScheme = CFDictionaryGetValue(item, CFSTR("scheme"));
532		if (CFStringCompare(scheme, matchScheme, kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
533		    CFStringCompare(CFSTR("any"), matchScheme, kCFCompareCaseInsensitive) != kCFCompareEqualTo)
534		    continue;
535
536		CFStringRef matchPath = CFDictionaryGetValue(item, CFSTR("path"));
537		if (CFStringHasPrefix(path, matchPath))
538		    result = CFDictionaryGetValue(item, CFSTR("value"));
539
540	    }
541	}
542    }
543    CFRelease(scheme);
544    free(host);
545    CFRelease(path);
546    return result;
547}
548
549/**
550 * Create a GSS name from a buffer and type.
551 *
552 * @param name name buffer describing a credential, can be either a CFDataRef or CFStringRef of a name.
553 * @param name_type on OID of the GSS_C_NT_* OIDs constants specifiy the name type.
554 * @param error if an error happen, this may be set to a CFErrorRef describing the failure futher.
555 *
556 * @returns returns gss_name_t or NULL on failure. Must be freed using gss_release_name() or CFRelease(). Follows CoreFoundation Create/Copy rule.
557 *
558 * @ingroup gssapi
559 */
560
561gss_name_t
562GSSCreateName(CFTypeRef name, gss_const_OID name_type, CFErrorRef *error)
563{
564    OM_uint32 maj_stat, min_stat;
565    gss_buffer_desc buffer;
566    int free_data = 0;
567    gss_name_t n;
568
569    if (error)
570	*error = NULL;
571
572    if (CFGetTypeID(name) == CFStringGetTypeID()) {
573	buffer.value = rk_cfstring2cstring(name);
574	if (buffer.value == NULL)
575	    return GSS_S_FAILURE;
576	buffer.length = strlen((char *)buffer.value);
577	free_data = 1;
578    } else if (CFGetTypeID(name) == CFDataGetTypeID()) {
579	buffer.value = (void *)CFDataGetBytePtr(name);
580	buffer.length = (OM_uint32)CFDataGetLength(name);
581    } else {
582	return GSS_C_NO_NAME;
583    }
584
585    maj_stat = gss_import_name(&min_stat, &buffer, name_type, &n);
586
587    if (free_data)
588	free(buffer.value);
589
590    if (maj_stat)
591	return GSS_C_NO_NAME;
592
593    return n;
594}
595
596/**
597 * Copy the name describing the credential
598 *
599 * @param cred the credential to get the name from
600 *
601 * @returns returns gss_name_t or NULL on failure. Must be freed using gss_release_name() or CFRelease(). Follows CoreFoundation Create/Copy rule.
602 *
603 * @ingroup gssapi
604 */
605
606gss_name_t
607GSSCredentialCopyName(gss_cred_id_t cred)
608{
609    OM_uint32 major, minor;
610    gss_name_t name;
611
612    major = gss_inquire_cred(&minor, cred, &name, NULL, NULL, NULL);
613    if (major != GSS_S_COMPLETE)
614	return NULL;
615
616    return name;
617}
618
619/**
620 * Return the lifetime (in seconds) left of the credential.
621 *
622 * @param cred the credential to get the name from
623 *
624 * @returns the lifetime of the credentials. 0 on failure and
625 * GSS_C_INDEFINITE on credentials that never expire.
626 *
627 * @ingroup gssapi
628 */
629
630OM_uint32
631GSSCredentialGetLifetime(gss_cred_id_t cred)
632{
633    OM_uint32 maj_stat, min_stat;
634    OM_uint32 lifetime;
635
636    maj_stat = gss_inquire_cred(&min_stat, cred, NULL, &lifetime, NULL, NULL);
637    if (maj_stat != GSS_S_COMPLETE)
638	return 0;
639
640    return lifetime;
641}
642
643/**
644 * Returns a string that is suitable for displaying to user, must not
645 * be used for verify subjects on an ACLs.
646 *
647 * @param name to get a display strings from
648 *
649 * @returns a string that is printable. Follows CoreFoundation Create/Copy rule.
650 *
651 * @ingroup gssapi
652 */
653
654CFStringRef
655GSSNameCreateDisplayString(gss_name_t name)
656{
657    OM_uint32 maj_stat, min_stat;
658    gss_buffer_desc buffer;
659    CFStringRef str;
660
661    maj_stat = gss_display_name(&min_stat, name, &buffer, NULL);
662    if (maj_stat != GSS_S_COMPLETE)
663	return NULL;
664
665    str = CFStringCreateWithBytes(NULL, (const UInt8 *)buffer.value, buffer.length, kCFStringEncodingUTF8, false);
666    gss_release_buffer(&min_stat, &buffer);
667
668    return str;
669}
670
671/*
672 * Create a CFErrorRef from GSS-API major and minor status code.
673 *
674 * @param major_status Major status code returned by the funcation that failed
675 * @param major_status Major status code returned by the funcation that failed
676 * @param mech Mechanism passed in, if not available GSS_C_NO_OID should be used
677 *
678 * @returns a CFErrorRef in the domain org.h5l.GSS domain
679 *
680 * @ingroup gssapi
681 */
682
683CFErrorRef
684GSSCreateError(gss_const_OID mech,
685	       OM_uint32 major_status,
686	       OM_uint32 minor_status)
687{
688    return _gss_mg_create_cferror(major_status,  minor_status, mech);
689}
690
691
692/* deprecated */
693OM_uint32
694GSSCredGetLifetime(gss_cred_id_t cred)
695{
696    return GSSCredentialGetLifetime(cred);
697}
698
699gss_name_t
700GSSCredCopyName(gss_cred_id_t cred)
701{
702    return GSSCredentialCopyName(cred);
703}
704