2  * Copyright (c) 2006-2013 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   6  * This file contains Original Code and/or Modifications of Original Code 
   7  * as defined in and that are subject to the Apple Public Source License 
   8  * Version 2.0 (the 'License'). You may not use this file except in 
   9  * compliance with the License. Please obtain a copy of the License at 
  10  * http://www.opensource.apple.com/apsl/ and read it before using this 
  13  * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  18  * Please see the License for the specific language governing rights and 
  19  * limitations under the License. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  25 // csutilities - miscellaneous utilities for the code signing implementation 
  28 #include "csutilities.h" 
  29 #include <libDER/DER_Encode.h> 
  30 #include <libDER/DER_Keys.h> 
  31 #include <libDER/asn1Types.h> 
  32 #include <libDER/oids.h> 
  33 #include <security_asn1/SecAsn1Coder.h> 
  34 #include <security_asn1/SecAsn1Templates.h> 
  35 #include <Security/SecCertificatePriv.h> 
  36 #include <Security/SecCertificate.h> 
  37 #include <Security/SecPolicyPriv.h> 
  38 #include <utilities/SecAppleAnchorPriv.h> 
  39 #include <utilities/SecInternalReleasePriv.h> 
  40 #include "requirement.h" 
  41 #include <security_utilities/hashing.h> 
  42 #include <security_utilities/debugging.h> 
  43 #include <security_utilities/errors.h> 
  44 #include <sys/mount.h> 
  45 #include <sys/utsname.h> 
  48 #include <sys/xattr.h> 
  50 #include "debugging.h" 
  54 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return 
  55  an absoluteTime if the date was valid and properly decoded.  Return 
  56  NULL_TIME otherwise. */ 
  57 CFAbsoluteTime 
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
, 
  63 namespace CodeSigning 
{ 
  67 // Test for the canonical Apple CA certificate 
  69 bool isAppleCA(SecCertificateRef cert
) 
  71         SecAppleTrustAnchorFlags flags 
= 0; 
  72         if (SecIsInternalRelease()) 
  73                 flags 
|= kSecAppleTrustAnchorFlagsIncludeTestAnchors
; 
  74         return SecIsAppleTrustAnchor(cert
, flags
); 
  79 // Calculate the canonical hash of a certificate, given its raw (DER) data. 
  81 void hashOfCertificate(const void *certData
, size_t certLength
, SHA1::Digest digest
) 
  84         hasher(certData
, certLength
); 
  85         hasher
.finish(digest
); 
  90 // Ditto, given a SecCertificateRef 
  92 void hashOfCertificate(SecCertificateRef cert
, SHA1::Digest digest
) 
  95     hashOfCertificate(SecCertificateGetBytePtr(cert
), SecCertificateGetLength(cert
), digest
); 
 100 // One-stop hash-certificate-and-compare 
 102 bool verifyHash(SecCertificateRef cert
, const Hashing::Byte 
*digest
) 
 105         hashOfCertificate(cert
, dig
); 
 106         return !memcmp(dig
, digest
, SHA1::digestLength
); 
 111 // Check to see if a certificate contains a particular field, by OID. This works for extensions, 
 112 // even ones not recognized by the local CL. It does not return any value, only presence. 
 114 bool certificateHasField(SecCertificateRef cert
, const CSSM_OID 
&oid
) 
 116         CFDataRef oidData 
= NULL
; 
 117         CFDataRef data 
= NULL
; 
 118         bool isCritical 
= false; 
 119         bool matched 
= false; 
 121         oidData 
= CFDataCreateWithBytesNoCopy(NULL
, oid
.Data
, oid
.Length
, 
 123         if (!(cert 
&& oidData
)) { 
 126         data 
= SecCertificateCopyExtensionValue(cert
, oidData
, &isCritical
); 
 143 // Retrieve X.509 policy extension OIDs, if any. 
 144 // This currently ignores policy qualifiers. 
 146 bool certificateHasPolicy(SecCertificateRef cert
, const CSSM_OID 
&policyOid
) 
 148         bool matched 
= false; 
 149         CFDataRef oidData 
= CFDataCreateWithBytesNoCopy(NULL
, policyOid
.Data
, policyOid
.Length
, 
 151         if (!(cert 
&& oidData
)) { 
 154         matched 
= SecPolicyCheckCertCertificatePolicy(cert
, oidData
); 
 163 CFDateRef 
certificateCopyFieldDate(SecCertificateRef cert
, const CSSM_OID 
&policyOid
) 
 165         CFDataRef oidData 
= NULL
; 
 166         CFDateRef value 
= NULL
; 
 167         CFDataRef data 
= NULL
; 
 168         SecAsn1CoderRef coder 
= NULL
; 
 169         CSSM_DATA str 
= { 0 }; 
 170         CFAbsoluteTime time 
= 0.0; 
 174         oidData 
= CFDataCreateWithBytesNoCopy(NULL
, policyOid
.Data
, policyOid
.Length
, 
 177         if (oidData 
== NULL
) { 
 181         data 
= SecCertificateCopyExtensionValue(cert
, oidData
, &isCritical
); 
 187         status 
= SecAsn1CoderCreate(&coder
); 
 192         // We currently only support UTF8 strings. 
 193         status 
= SecAsn1Decode(coder
, CFDataGetBytePtr(data
), CFDataGetLength(data
), 
 194                                                    kSecAsn1UTF8StringTemplate
, &str
); 
 199         time 
= SecAbsoluteTimeFromDateContent(ASN1_GENERALIZED_TIME
, 
 200                                                                                   str
.Data
, str
.Length
); 
 206         value 
= CFDateCreate(NULL
, time
); 
 209                 SecAsn1CoderRelease(coder
); 
 227         if (!(mState 
= copyfile_state_alloc())) 
 228                 UnixError::throwMe(); 
 231 void Copyfile::set(uint32_t flag
, const void *value
) 
 233         check(::copyfile_state_set(mState
, flag
, value
)); 
 236 void Copyfile::get(uint32_t flag
, void *value
) 
 238         check(::copyfile_state_set(mState
, flag
, value
)); 
 241 void Copyfile::operator () (const char *src
, const char *dst
, copyfile_flags_t flags
) 
 243         check(::copyfile(src
, dst
, mState
, flags
)); 
 246 void Copyfile::check(int rc
) 
 249                 UnixError::throwMe(); 
 254 // MessageTracer support 
 256 MessageTrace::MessageTrace(const char *domain
, const char *signature
) 
 258         mAsl 
= asl_new(ASL_TYPE_MSG
); 
 260                 asl_set(mAsl
, "com.apple.message.domain", domain
); 
 262                 asl_set(mAsl
, "com.apple.message.signature", signature
); 
 265 void MessageTrace::add(const char *key
, const char *format
, ...) 
 268         va_start(args
, format
); 
 270         vsnprintf(value
, sizeof(value
), format
, args
); 
 272         asl_set(mAsl
, (string("com.apple.message.") + key
).c_str(), value
); 
 275 void MessageTrace::send(const char *format
, ...) 
 278         va_start(args
, format
); 
 279         asl_vlog(NULL
, mAsl
, ASL_LEVEL_NOTICE
, format
, args
); 
 285 // Resource limited async workers for doing work on nested bundles 
 286 LimitedAsync::LimitedAsync(bool async
) 
 288         // validate multiple resources concurrently if bundle resides on solid-state media 
 290         // How many async workers to spin off. If zero, validating only happens synchronously. 
 291         long async_workers 
= 0; 
 293         long ncpu 
= sysconf(_SC_NPROCESSORS_ONLN
); 
 295         if (async 
&& ncpu 
> 0) 
 296                 async_workers 
= ncpu 
- 1; // one less because this thread also validates 
 298         mResourceSemaphore 
= new Dispatch::Semaphore(async_workers
); 
 301 LimitedAsync::LimitedAsync(LimitedAsync 
&limitedAsync
) 
 303         mResourceSemaphore 
= new Dispatch::Semaphore(*limitedAsync
.mResourceSemaphore
); 
 306 LimitedAsync::~LimitedAsync() 
 308         delete mResourceSemaphore
; 
 311 bool LimitedAsync::perform(Dispatch::Group 
&groupRef
, void (^block
)()) { 
 312         __block 
Dispatch::SemaphoreWait 
wait(*mResourceSemaphore
, DISPATCH_TIME_NOW
); 
 314         if (wait
.acquired()) { 
 315                 dispatch_queue_t defaultQueue 
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0); 
 317                 groupRef
.enqueue(defaultQueue
, ^{ 
 318                         // Hold the semaphore count until the worker is done validating. 
 319                         Dispatch::SemaphoreWait 
innerWait(wait
); 
 329 bool isOnRootFilesystem(const char *path
) 
 334         rc 
= statfs(path
, &sfb
); 
 336                 secerror("Unable to check if path is on rootfs: %d, %s", errno
, path
); 
 339         return ((sfb
.f_flags 
& MNT_ROOTFS
) == MNT_ROOTFS
); 
 342 bool pathExists(const char *path
) 
 347                 secerror("path is NULL"); 
 351         rc 
= access(path
, F_OK
); 
 353                 if (errno 
!= ENOENT
) { 
 354                         secerror("Unable to check if path exists: %d, %s", errno
, path
); 
 362 bool pathMatchesXattrFilenameSpec(const char *path
) 
 364         char *baseName 
= NULL
; 
 368                 secerror("path is NULL"); 
 372         // Extra byte for NULL storage. 
 373         baseName 
= (char *)malloc(strlen(path
) + 1); 
 375                 secerror("Unable to allocate space for storing basename: %d [%s]", errno
, strerror(errno
)); 
 379         // basename_r will return a "/" if path is only slashes. It will return 
 380         // a "." for a NULL/empty path. Both of these cases are handled by the logic 
 381         // later. The only situation where basename_r will return a NULL is when path 
 382         // is longer than MAXPATHLEN. 
 384         if (basename_r(path
, baseName
) == NULL
) { 
 385                 secerror("Could not get basename of %s: %d [%s]", path
, errno
, strerror(errno
)); 
 389         // The file name must start with "._", followed by the name 
 390         // of the file for which it stores the xattrs. Hence, its length 
 391         // must be at least three --> 2 for "._" and 1 for a non-empty file 
 393         if (strlen(baseName
) < 3) { 
 397         if (baseName
[0] != '.' || baseName
[1] != '_') { 
 411 bool pathIsRegularFile(const char *path
) 
 414                 secerror("path is NULL"); 
 419         if (stat(path
, &sb
)) { 
 420                 secerror("Unable to stat %s: %d [%s]", path
, errno
, strerror(errno
)); 
 424         return (sb
.st_mode 
& S_IFREG
) == S_IFREG
; 
 427 bool pathHasXattrs(const char *path
) 
 430                 secerror("path is NULL"); 
 434         ssize_t xattrSize 
= listxattr(path
, NULL
, 0, 0); 
 435         if (xattrSize 
== -1) { 
 436                 secerror("Unable to acquire the xattr list from %s", path
); 
 440         return (xattrSize 
> 0); 
 443 bool pathFileSystemUsesXattrFiles(const char *path
) 
 445         struct _VolumeCapabilitiesWrapped 
{ 
 447                 vol_capabilities_attr_t volume_capabilities
; 
 448         } __attribute__((aligned(4), packed
)); 
 450         struct attrlist attr_list
; 
 451         struct _VolumeCapabilitiesWrapped volume_cap_wrapped
; 
 455                 secerror("path is NULL"); 
 459         int ret 
= statfs(path
, &sfb
); 
 461                 secerror("Unable to convert %s to its filesystem mount [statfs failed]: %d [%s]", path
, errno
, strerror(errno
)); 
 464         path 
= sfb
.f_mntonname
; 
 466         memset(&attr_list
, 0, sizeof(attr_list
)); 
 467         attr_list
.bitmapcount 
= ATTR_BIT_MAP_COUNT
; 
 468         attr_list
.volattr 
= ATTR_VOL_INFO 
| ATTR_VOL_CAPABILITIES
; 
 470         ret 
= getattrlist(path
, &attr_list
, &volume_cap_wrapped
, sizeof(volume_cap_wrapped
), 0); 
 472                 secerror("Unable to get volume capabilities from %s: %d [%s]", path
, errno
, strerror(errno
)); 
 476         if (volume_cap_wrapped
.length 
!= sizeof(volume_cap_wrapped
)) { 
 477                 secerror("getattrlist return length incorrect, expected %lu, got %u", sizeof(volume_cap_wrapped
), volume_cap_wrapped
.length
); 
 481         // The valid bit tells us whether the corresponding bit in capabilities is valid 
 482         // or not. For file systems where the valid bit isn't set, we can safely assume that 
 483         // extended attributes aren't supported natively. 
 485         bool xattr_valid 
= (volume_cap_wrapped
.volume_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] & VOL_CAP_INT_EXTENDED_ATTR
) == VOL_CAP_INT_EXTENDED_ATTR
; 
 490         bool xattr_capability 
= (volume_cap_wrapped
.volume_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] & VOL_CAP_INT_EXTENDED_ATTR
) == VOL_CAP_INT_EXTENDED_ATTR
; 
 491         if (!xattr_capability
) { 
 498 bool pathIsValidXattrFile(const string fullPath
, const char *scope
) 
 500         // Confirm that fullPath begins from root. 
 501         if (fullPath
[0] != '/') { 
 502                 secinfo(scope
, "%s isn't a full path, but a relative path", fullPath
.c_str()); 
 506         // Confirm that fullPath is a regular file. 
 507         if (!pathIsRegularFile(fullPath
.c_str())) { 
 508                 secinfo(scope
, "%s isn't a regular file", fullPath
.c_str()); 
 512         // Check that the file name matches the Xattr file spec. 
 513         if (!pathMatchesXattrFilenameSpec(fullPath
.c_str())) { 
 514                 secinfo(scope
, "%s doesn't match Xattr file path spec", fullPath
.c_str()); 
 518         // We are guaranteed to have at least one "/" by virtue of fullPath 
 519         // being a path from the root of the filesystem hierarchy. 
 521         // We construct the real file name by copying everything up to 
 522         // the last "/", adding the "/" back in, then skipping 
 523         // over the backslash (+1) and the "._" (+2) in the rest of the 
 526         size_t lastBackSlash 
= fullPath
.find_last_of("/"); 
 527         const string realFilePath 
= fullPath
.substr(0, lastBackSlash
) + "/" + fullPath
.substr(lastBackSlash 
+ 1 + 2); 
 529         if (!pathExists(realFilePath
.c_str())) { 
 530                 secinfo(scope
, "%s does not exist, forcing resource validation on %s", realFilePath
.c_str(), fullPath
.c_str()); 
 534         // Lastly, we need to confirm that the real file contains some xattrs. If not, 
 535         // then the file represented by fullPath isn't an xattr file. 
 536         if (!pathHasXattrs(realFilePath
.c_str())) { 
 537                 secinfo(scope
, "%s does not contain xattrs, forcing resource validation on %s", realFilePath
.c_str(), fullPath
.c_str()); 
 544 } // end namespace CodeSigning 
 545 } // end namespace Security