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