1
2/*
3 * Copyright (c) 2001-2004 Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * The contents of this file constitute Original Code as defined in and
8 * are subject to the Apple Public Source License Version 1.1 (the
9 * "License").  You may not use this file except in compliance with the
10 * License.  Please obtain a copy of the License at
11 * http://www.apple.com/publicsource and read it before using this file.
12 *
13 * This Original Code and all software distributed under the License are
14 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
18 * License for the specific language governing rights and limitations
19 * under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25/*
26 * Racoon module for verifying and signing certificates through Security
27 * Framework and CSSM
28 */
29
30#include <Security/SecCertificate.h>
31#include <Security/SecPolicy.h>
32#include <Security/SecTrust.h>
33#include <Security/SecKey.h>
34#include <Security/SecIdentity.h>
35#include <Security/SecItem.h>
36#include <TargetConditionals.h>
37#if TARGET_OS_EMBEDDED
38#include <Security/SecTrustPriv.h>
39#include <Security/SecPolicyPriv.h>
40#include <Security/SecCertificatePriv.h>
41#else
42#include <Security/SecBase.h>
43#include <Security/SecIdentityPriv.h>
44#include <Security/SecIdentitySearch.h>
45#include <Security/SecKeychain.h>
46#include <Security/SecKeychainItem.h>
47#include <Security/SecKeychainItemPriv.h>
48#include <Security/SecCertificateOIDs.h>
49#include <Security/SecKeyPriv.h>
50#include <Security/oidsalg.h>
51#include <Security/cssmapi.h>
52#include <Security/SecPolicySearch.h>
53#endif
54#include <CoreFoundation/CoreFoundation.h>
55#if !TARGET_OS_EMBEDDED
56#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
57#endif
58#include "plog.h"
59#include "debug.h"
60#include "misc.h"
61#include "oakley.h"
62#include "gcmalloc.h"
63
64
65#include "crypto_cssm.h"
66
67
68static OSStatus EvaluateCert(SecCertificateRef evalCertArray[], CFIndex evalCertArrayNumValues, CFTypeRef policyRef, SecKeyRef *publicKeyRef);
69
70#if !TARGET_OS_EMBEDDED
71#endif
72
73static SecPolicyRef
74crypto_cssm_x509cert_get_SecPolicyRef (CFStringRef hostname)
75{
76	SecPolicyRef		policyRef = NULL;
77	CFDictionaryRef		properties = NULL;
78	const void			*key[] = { kSecPolicyName };
79	const void			*value[] = { hostname };
80
81	if (hostname) {
82		properties = CFDictionaryCreate(NULL, key, value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
83		if (properties == NULL) {
84			plog(ASL_LEVEL_ERR,
85				"unable to create dictionary for policy properties.\n");
86		}
87	}
88	policyRef = SecPolicyCreateWithProperties(kSecPolicyAppleIPsec, properties);
89	if (properties)
90		CFRelease(properties);
91	return policyRef;
92}
93
94SecCertificateRef
95crypto_cssm_x509cert_CreateSecCertificateRef (vchar_t *cert)
96{
97	SecCertificateRef	certRef = NULL;
98
99	CFDataRef cert_data = CFDataCreateWithBytesNoCopy(NULL, (uint8_t*)cert->v, cert->l, kCFAllocatorNull);
100    if (cert_data) {
101        certRef = SecCertificateCreateWithData(NULL, cert_data);
102        CFRelease(cert_data);
103    }
104
105	if (certRef == NULL) {
106		plog(ASL_LEVEL_ERR,
107			 "unable to get a certifcate reference.\n");
108	}
109	return certRef;
110}
111
112/* HACK!!! - temporary until this prototype gets moved */
113extern CFDataRef SecCertificateCopySubjectSequence( SecCertificateRef certificate);
114
115CFDataRef
116crypto_cssm_CopySubjectSequence(SecCertificateRef certRef)
117{
118    CFDataRef subject = NULL;
119
120    subject = SecCertificateCopySubjectSequence(certRef);
121    return subject;
122
123}
124
125
126static cert_status_t
127crypto_cssm_check_x509cert_dates (SecCertificateRef certificateRef)
128{
129	cert_status_t       certStatus = CERT_STATUS_OK;
130#if TARGET_OS_EMBEDDED
131	CFAbsoluteTime		timeNow = 0;
132	CFAbsoluteTime		notvalidbeforedate = 0;
133	CFAbsoluteTime		notvalidafterdate = 0;
134	CFDateRef			nowcfdatedata = NULL;
135	CFDateRef			notvalidbeforedatedata = NULL;
136	CFDateRef			notvalidafterdatedata = NULL;
137	CFArrayRef			certProparray = NULL;
138	CFDictionaryRef		propDict = NULL;
139	const void			*datevalue = NULL;
140	const void			*labelvalue = NULL;
141	CFGregorianDate		gregoriandate;
142	CFIndex				count;
143	CFIndex				i;
144
145	if ((certProparray = SecCertificateCopyProperties(certificateRef))){
146		if ((count = CFArrayGetCount( certProparray ))){
147			for( i = 0; i < count; i++) {
148				if ((propDict = CFArrayGetValueAtIndex(certProparray, i))) {
149					if ( CFDictionaryGetValueIfPresent(propDict, kSecPropertyKeyValue, (const void**)&datevalue)){
150						/* get kSecPropertyKeyLabel */
151						if ( (datevalue) && (CFDictionaryGetValueIfPresent(propDict, kSecPropertyKeyLabel, (const void**)&labelvalue))){
152							if ( (labelvalue) && (CFStringCompare( (CFStringRef)labelvalue, CFSTR("Not Valid Before"), 0) == kCFCompareEqualTo)){
153								if ( (notvalidbeforedate = CFDateGetAbsoluteTime(datevalue))) {
154									if (notvalidbeforedatedata) {
155										CFRelease(notvalidbeforedatedata);
156									}
157									notvalidbeforedatedata = CFDateCreate(NULL, notvalidbeforedate);
158								}
159							}else if ((labelvalue) && (CFStringCompare( (CFStringRef)labelvalue, CFSTR("Not Valid After"), 0 ) == kCFCompareEqualTo)){
160								if ( (notvalidafterdate = CFDateGetAbsoluteTime(datevalue))) {
161									if (notvalidafterdatedata) {
162										CFRelease(notvalidafterdatedata);
163									}
164									notvalidafterdatedata = CFDateCreate(NULL, notvalidafterdate);
165								}
166							}
167						}
168					}
169				}
170			}
171		}
172	}
173
174	if ( (timeNow = CFAbsoluteTimeGetCurrent()) && (nowcfdatedata = CFDateCreate( NULL, timeNow))){
175		if ( notvalidbeforedatedata ){
176			gregoriandate = CFAbsoluteTimeGetGregorianDate(notvalidbeforedate, NULL);
177			plog(ASL_LEVEL_DEBUG,
178				 "Certificate not valid before yr %d, mon %d, days %d, hours %d, min %d\n", (int)gregoriandate.year, gregoriandate.month, gregoriandate.day, gregoriandate.hour, gregoriandate.minute);
179			gregoriandate = CFAbsoluteTimeGetGregorianDate(notvalidafterdate, NULL);
180			plog(ASL_LEVEL_DEBUG,
181				 "Certificate not valid after yr %d, mon %d, days %d, hours %d, min %d\n", (int)gregoriandate.year, gregoriandate.month, gregoriandate.day, gregoriandate.hour, gregoriandate.minute);
182			if ( CFDateCompare( nowcfdatedata, notvalidbeforedatedata, NULL ) == kCFCompareLessThan){
183				plog(ASL_LEVEL_ERR,
184					 "current time before valid time\n");
185				certStatus = CERT_STATUS_PREMATURE;
186			} else if (notvalidafterdatedata && (CFDateCompare( nowcfdatedata, notvalidafterdatedata, NULL ) == kCFCompareGreaterThan)){
187				plog(ASL_LEVEL_ERR,
188					 "current time after valid time\n");
189				certStatus = CERT_STATUS_EXPIRED;
190			}else {
191				plog(ASL_LEVEL_INFO, "Certificate expiration date is OK\n");
192				certStatus = CERT_STATUS_OK;
193			}
194		}
195	}
196
197	if (notvalidbeforedatedata)
198		CFRelease(notvalidbeforedatedata);
199	if (notvalidafterdatedata)
200		CFRelease(notvalidafterdatedata);
201	if (certProparray)
202		CFRelease(certProparray);
203	if (nowcfdatedata)
204		CFRelease(nowcfdatedata);
205#endif
206	return certStatus;
207}
208
209/*
210 * Verify cert using security framework
211 */
212int crypto_cssm_check_x509cert (cert_t *hostcert, cert_t *certchain, CFStringRef hostname, SecKeyRef *publicKeyRef)
213{
214	cert_t             *p;
215	cert_status_t       certStatus = 0;
216	OSStatus			status;
217	CFIndex             certArrayRefNumValues = 0;
218	CFIndex             n = 0;
219	int                 certArraySiz;
220	SecCertificateRef  *certArrayRef = NULL;
221	SecPolicyRef		policyRef = crypto_cssm_x509cert_get_SecPolicyRef(hostname);
222
223	if (!hostcert || !certchain) {
224		return -1;
225	}
226
227	// find the total number of certs
228	for (p = certchain; p; p = p->chain, n++);
229	if (n> 1) {
230		plog(ASL_LEVEL_DEBUG,
231			 "%s: checking chain of %d certificates.\n", __FUNCTION__, (int)n);
232	}
233
234	certArraySiz = n * sizeof(CFTypeRef);
235	certArrayRef = CFAllocatorAllocate(NULL, certArraySiz, 0);
236	if (!certArrayRef) {
237		return -1;
238	}
239	bzero(certArrayRef, certArraySiz);
240	if ((certArrayRef[certArrayRefNumValues] = crypto_cssm_x509cert_CreateSecCertificateRef(&hostcert->cert))) {
241		/* don't overwrite any pending status */
242		if (!hostcert->status) {
243			hostcert->status = crypto_cssm_check_x509cert_dates(certArrayRef[certArrayRefNumValues]);
244			if (hostcert->status) {
245				plog(ASL_LEVEL_ERR,
246					 "host certificate failed date verification: %d.\n", hostcert->status);
247				certStatus = hostcert->status;
248			}
249		}
250		certArrayRefNumValues++;
251	}
252	for (p = certchain; p && certArrayRefNumValues < n; p = p->chain) {
253		if (p != hostcert) {
254			if ((certArrayRef[certArrayRefNumValues] = crypto_cssm_x509cert_CreateSecCertificateRef(&p->cert))) {
255				/* don't overwrite any pending status */
256				if (!p->status) {
257					p->status = crypto_cssm_check_x509cert_dates(certArrayRef[certArrayRefNumValues]);
258					if (p->status) {
259						plog(ASL_LEVEL_ERR,
260							 "other certificate in chain failed date verification: %d.\n", p->status);
261						if (!certStatus) {
262							certStatus = p->status;
263						}
264					}
265				}
266				certArrayRefNumValues++;
267			}
268		}
269	}
270
271	// evaluate cert
272	status = EvaluateCert(certArrayRef, certArrayRefNumValues, policyRef, publicKeyRef);
273
274	while (certArrayRefNumValues) {
275		CFRelease(certArrayRef[--certArrayRefNumValues]);
276	}
277	CFAllocatorDeallocate(NULL, certArrayRef);
278
279	if (policyRef)
280		CFRelease(policyRef);
281
282	if (status != noErr && status != -1) {
283		plog(ASL_LEVEL_ERR,
284			 "error %d %s.\n", (int)status, GetSecurityErrorString(status));
285		status = -1;
286	} else if (certStatus == CERT_STATUS_PREMATURE || certStatus == CERT_STATUS_EXPIRED) {
287		status = -1;
288	}
289	return status;
290
291}
292
293
294int crypto_cssm_verify_x509sign(SecKeyRef publicKeyRef, vchar_t *hash, vchar_t *signature, Boolean useSHA1)
295{
296	return SecKeyRawVerify(publicKeyRef, useSHA1 ? kSecPaddingPKCS1SHA1 : kSecPaddingPKCS1, (uint8_t*)hash->v, hash->l, (uint8_t*)signature->v, signature->l);
297}
298
299/*
300 * Encrypt a hash via CSSM using the private key in the keychain
301 * from an identity.
302 */
303vchar_t* crypto_cssm_getsign(CFDataRef persistentCertRef, vchar_t* hash)
304{
305
306	OSStatus						status = -1;
307	SecIdentityRef 					identityRef = NULL;
308	SecKeyRef						privateKeyRef = NULL;
309	vchar_t							*sig = NULL;
310
311
312	CFDictionaryRef		persistFind = NULL;
313	const void			*keys_persist[] = { kSecReturnRef, kSecValuePersistentRef, kSecClass};
314	const void			*values_persist[] = { kCFBooleanTrue, persistentCertRef, kSecClassIdentity};
315
316#define SIG_BUF_SIZE 1024
317
318	/* find identity by persistent ref */
319	persistFind = CFDictionaryCreate(NULL, keys_persist, values_persist,
320                                     (sizeof(keys_persist) / sizeof(*keys_persist)), NULL, NULL);
321	if (persistFind == NULL)
322		goto end;
323
324	status = SecItemCopyMatching(persistFind, (CFTypeRef *)&identityRef);
325	if (status != noErr)
326		goto end;
327
328	status = SecIdentityCopyPrivateKey(identityRef, &privateKeyRef);
329	if (status != noErr)
330		goto end;
331
332	// alloc buffer for result
333	sig = vmalloc(SIG_BUF_SIZE);
334	if (sig == NULL)
335		goto end;
336
337	status = SecKeyRawSign(privateKeyRef, kSecPaddingPKCS1, (uint8_t*)hash->v,
338                           hash->l, (uint8_t*)sig->v, &sig->l);
339
340
341end:
342	if (identityRef)
343		CFRelease(identityRef);
344	if (privateKeyRef)
345		CFRelease(privateKeyRef);
346
347	if (persistFind)
348		CFRelease(persistFind);
349
350	if (status != noErr) {
351		if (sig) {
352			vfree(sig);
353			sig = NULL;
354		}
355	}
356
357	if (status != noErr && status != -1) {
358		plog(ASL_LEVEL_ERR,
359			"error %d %s.\n", (int)status, GetSecurityErrorString(status));
360		status = -1;
361	}
362	return sig;
363
364}
365
366
367/*
368 * Retrieve a cert from the keychain
369 */
370vchar_t* crypto_cssm_get_x509cert(CFDataRef persistentCertRef,
371                                  cert_status_t *certStatus)
372{
373
374	OSStatus				status = -1;
375	vchar_t					*cert = NULL;
376	SecCertificateRef		certificateRef = NULL;
377	CFDictionaryRef         persistFind = NULL;
378	size_t                  dataLen;
379	CFDataRef               certData = NULL;
380	SecIdentityRef 			identityRef = NULL;
381	const void              *keys_persist[] = { kSecReturnRef, kSecValuePersistentRef, kSecClass };
382	const void              *values_persist[] = { kCFBooleanTrue, persistentCertRef, kSecClassIdentity };
383
384	/* find identity by persistent ref */
385	persistFind = CFDictionaryCreate(NULL, keys_persist, values_persist,
386		(sizeof(keys_persist) / sizeof(*keys_persist)), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
387	if (persistFind == NULL)
388		goto end;
389
390    status = SecItemCopyMatching(persistFind, (CFTypeRef *)&identityRef);
391	if (status != noErr)
392		goto end;
393
394	status = SecIdentityCopyCertificate(identityRef, &certificateRef);
395	if (status != noErr)
396		goto end;
397
398	certData = SecCertificateCopyData(certificateRef);
399	if (certData == NULL)
400		goto end;
401
402	dataLen = CFDataGetLength(certData);
403	if (dataLen == 0)
404		goto end;
405
406	cert = vmalloc(dataLen);
407	if (cert == NULL)
408		goto end;
409
410	CFDataGetBytes(certData, CFRangeMake(0, dataLen), (uint8_t*)cert->v);
411
412	// verify expiry or missing fields
413	if (certStatus) {
414		*certStatus = crypto_cssm_check_x509cert_dates(certificateRef);
415	}
416
417end:
418    if (identityRef)
419		CFRelease(identityRef);
420	if (certificateRef)
421		CFRelease(certificateRef);
422	if (persistFind)
423		CFRelease(persistFind);
424	if (certData)
425		CFRelease(certData);
426
427	if (status != noErr && status != -1) {
428		plog(ASL_LEVEL_ERR,
429			"error %d %s.\n", (int)status, GetSecurityErrorString(status));
430		status = -1;
431	}
432	return cert;
433
434}
435
436/*
437 * Evaluate the trust of a cert using the policy provided
438 */
439static OSStatus EvaluateCert(SecCertificateRef evalCertArray[], CFIndex evalCertArrayNumValues, CFTypeRef policyRef, SecKeyRef *publicKeyRef)
440{
441	OSStatus					status;
442	SecTrustRef					trustRef = 0;
443	SecTrustResultType 			evalResult;
444
445	CFArrayRef					errorStrings;
446
447	CFArrayRef	cfCertRef = CFArrayCreate((CFAllocatorRef) NULL, (void*)evalCertArray, evalCertArrayNumValues,
448								&kCFTypeArrayCallBacks);
449
450	if (!cfCertRef) {
451		plog(ASL_LEVEL_ERR,
452			"unable to create CFArray.\n");
453		return -1;
454	}
455
456	status = SecTrustCreateWithCertificates(cfCertRef, policyRef, &trustRef);
457	if (status != noErr)
458		goto end;
459
460	status = SecTrustEvaluate(trustRef, &evalResult);
461	if (status != noErr)
462		goto end;
463
464	if (evalResult != kSecTrustResultProceed && evalResult != kSecTrustResultUnspecified) {
465		plog(ASL_LEVEL_ERR, "Error evaluating certificate.\n");
466
467		switch (evalResult) {
468			case kSecTrustResultInvalid:
469				plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultInvalid.\n");
470				break;
471			case kSecTrustResultProceed:
472				plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultProceed.\n");
473				break;
474			case kSecTrustResultDeny:
475				plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultDeny.\n");
476				break;
477			case kSecTrustResultUnspecified:
478				plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultUnspecified.\n");
479				break;
480			case kSecTrustResultRecoverableTrustFailure:
481				plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultRecoverableTrustFailure.\n");
482				break;
483			case kSecTrustResultFatalTrustFailure:
484				plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultFatalTrustFailure.\n");
485				break;
486			case kSecTrustResultOtherError:
487				plog(ASL_LEVEL_DEBUG, "eval result = kSecTrustResultOtherError.\n");
488				break;
489			default:
490				plog(ASL_LEVEL_DEBUG, "eval result unknown: value = %d.\n", (int)evalResult);
491				break;
492		}
493
494		errorStrings =  SecTrustCopyProperties(trustRef);
495		if (errorStrings) {
496
497			CFDictionaryRef dict;
498			CFStringRef val;
499			const char *str;
500			CFIndex count, maxcount = CFArrayGetCount(errorStrings);
501
502			plog(ASL_LEVEL_ERR, "---------------Returned error strings: ---------------.\n");
503			for (count = 0; count < maxcount; count++) {
504				dict = CFArrayGetValueAtIndex(errorStrings, count);
505				if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) {
506					val = CFDictionaryGetValue(dict, kSecPropertyKeyType);
507					if (val && (CFGetTypeID(val) == CFStringGetTypeID())) {
508						str = CFStringGetCStringPtr(val, kCFStringEncodingMacRoman);
509						if (str)
510							plog(ASL_LEVEL_ERR, "type = %s.\n", str);
511					}
512					val = CFDictionaryGetValue(dict, kSecPropertyKeyValue);
513					if (val && (CFGetTypeID(val) == CFStringGetTypeID())) {
514						str = CFStringGetCStringPtr(val, kCFStringEncodingMacRoman);
515						if (str)
516							plog(ASL_LEVEL_ERR, "value = %s.\n", str);
517					}
518				}
519			}
520			plog(ASL_LEVEL_ERR, "-----------------------------------------------------.\n");
521			CFRelease(errorStrings);
522		}
523
524		status = -1;
525		goto end;
526	}
527
528	/* get and return the public key */
529	*publicKeyRef = SecTrustCopyPublicKey(trustRef);
530
531end:
532	if (cfCertRef)
533		CFRelease(cfCertRef);
534	if (trustRef)
535		CFRelease(trustRef);
536
537	if (status != noErr && status != -1) {
538		plog(ASL_LEVEL_ERR,
539			"error %d %s.\n", (int)status, GetSecurityErrorString(status));
540		status = -1;
541	}
542	return status;
543}
544
545/*
546 * Return string representation of Security-related OSStatus.
547 */
548const char *
549GetSecurityErrorString(OSStatus err)
550{
551    switch(err) {
552		case noErr:
553			return "noErr";
554
555		/* SecBase.h: */
556		case errSecNotAvailable:
557			return "errSecNotAvailable";
558
559#if !TARGET_OS_EMBEDDED
560        case memFullErr:
561			return "memFullErr";
562		case paramErr:
563			return "paramErr";
564		case unimpErr:
565			return "unimpErr";
566
567        /* SecBase.h: */
568		case errSecReadOnly:
569			return "errSecReadOnly";
570		case errSecAuthFailed:
571			return "errSecAuthFailed";
572		case errSecNoSuchKeychain:
573			return "errSecNoSuchKeychain";
574		case errSecInvalidKeychain:
575			return "errSecInvalidKeychain";
576		case errSecDuplicateKeychain:
577			return "errSecDuplicateKeychain";
578		case errSecDuplicateCallback:
579			return "errSecDuplicateCallback";
580		case errSecInvalidCallback:
581			return "errSecInvalidCallback";
582		case errSecBufferTooSmall:
583			return "errSecBufferTooSmall";
584		case errSecDataTooLarge:
585			return "errSecDataTooLarge";
586		case errSecNoSuchAttr:
587			return "errSecNoSuchAttr";
588		case errSecInvalidItemRef:
589			return "errSecInvalidItemRef";
590		case errSecInvalidSearchRef:
591			return "errSecInvalidSearchRef";
592		case errSecNoSuchClass:
593			return "errSecNoSuchClass";
594		case errSecNoDefaultKeychain:
595			return "errSecNoDefaultKeychain";
596		case errSecInteractionNotAllowed:
597			return "errSecInteractionNotAllowed";
598		case errSecReadOnlyAttr:
599			return "errSecReadOnlyAttr";
600		case errSecWrongSecVersion:
601			return "errSecWrongSecVersion";
602		case errSecKeySizeNotAllowed:
603			return "errSecKeySizeNotAllowed";
604		case errSecNoStorageModule:
605			return "errSecNoStorageModule";
606		case errSecNoCertificateModule:
607			return "errSecNoCertificateModule";
608		case errSecNoPolicyModule:
609			return "errSecNoPolicyModule";
610		case errSecInteractionRequired:
611			return "errSecInteractionRequired";
612		case errSecDataNotAvailable:
613			return "errSecDataNotAvailable";
614		case errSecDataNotModifiable:
615			return "errSecDataNotModifiable";
616		case errSecCreateChainFailed:
617			return "errSecCreateChainFailed";
618		case errSecACLNotSimple:
619			return "errSecACLNotSimple";
620		case errSecPolicyNotFound:
621			return "errSecPolicyNotFound";
622		case errSecInvalidTrustSetting:
623			return "errSecInvalidTrustSetting";
624		case errSecNoAccessForItem:
625			return "errSecNoAccessForItem";
626		case errSecInvalidOwnerEdit:
627			return "errSecInvalidOwnerEdit";
628#endif
629		case errSecDuplicateItem:
630			return "errSecDuplicateItem";
631		case errSecItemNotFound:
632			return "errSecItemNotFound";
633		default:
634			return "<unknown>";
635    }
636}
637
638