]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/csutilities.cpp
Security-59754.60.13.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / csutilities.cpp
1 /*
2 * Copyright (c) 2006-2013 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 //
25 // csutilities - miscellaneous utilities for the code signing implementation
26 //
27
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>
46 #include <errno.h>
47 #include <sys/attr.h>
48 #include <sys/xattr.h>
49 #include <libgen.h>
50 #include "debugging.h"
51
52 extern "C" {
53
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,
58 size_t length);
59
60 }
61
62 namespace Security {
63 namespace CodeSigning {
64
65
66 //
67 // Test for the canonical Apple CA certificate
68 //
69 bool isAppleCA(SecCertificateRef cert)
70 {
71 SecAppleTrustAnchorFlags flags = 0;
72 if (SecIsInternalRelease())
73 flags |= kSecAppleTrustAnchorFlagsIncludeTestAnchors;
74 return SecIsAppleTrustAnchor(cert, flags);
75 }
76
77
78 //
79 // Calculate the canonical hash of a certificate, given its raw (DER) data.
80 //
81 void hashOfCertificate(const void *certData, size_t certLength, SHA1::Digest digest)
82 {
83 SHA1 hasher;
84 hasher(certData, certLength);
85 hasher.finish(digest);
86 }
87
88
89 //
90 // Ditto, given a SecCertificateRef
91 //
92 void hashOfCertificate(SecCertificateRef cert, SHA1::Digest digest)
93 {
94 assert(cert);
95 hashOfCertificate(SecCertificateGetBytePtr(cert), SecCertificateGetLength(cert), digest);
96 }
97
98
99 //
100 // One-stop hash-certificate-and-compare
101 //
102 bool verifyHash(SecCertificateRef cert, const Hashing::Byte *digest)
103 {
104 SHA1::Digest dig;
105 hashOfCertificate(cert, dig);
106 return !memcmp(dig, digest, SHA1::digestLength);
107 }
108
109 #if TARGET_OS_OSX
110 //
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.
113 //
114 bool certificateHasField(SecCertificateRef cert, const CSSM_OID &oid)
115 {
116 CFDataRef oidData = NULL;
117 CFDataRef data = NULL;
118 bool isCritical = false;
119 bool matched = false;
120
121 oidData = CFDataCreateWithBytesNoCopy(NULL, oid.Data, oid.Length,
122 kCFAllocatorNull);
123 if (!(cert && oidData)) {
124 goto out;
125 }
126 data = SecCertificateCopyExtensionValue(cert, oidData, &isCritical);
127 if (data == NULL) {
128 goto out;
129 }
130 matched = true;
131 out:
132 if (data) {
133 CFRelease(data);
134 }
135 if (oidData) {
136 CFRelease(oidData);
137 }
138 return matched;
139 }
140
141
142 //
143 // Retrieve X.509 policy extension OIDs, if any.
144 // This currently ignores policy qualifiers.
145 //
146 bool certificateHasPolicy(SecCertificateRef cert, const CSSM_OID &policyOid)
147 {
148 bool matched = false;
149 CFDataRef oidData = CFDataCreateWithBytesNoCopy(NULL, policyOid.Data, policyOid.Length,
150 kCFAllocatorNull);
151 if (!(cert && oidData)) {
152 goto out;
153 }
154 matched = SecPolicyCheckCertCertificatePolicy(cert, oidData);
155 out:
156 if (oidData) {
157 CFRelease(oidData);
158 }
159 return matched;
160 }
161
162
163 CFDateRef certificateCopyFieldDate(SecCertificateRef cert, const CSSM_OID &policyOid)
164 {
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;
171 OSStatus status = 0;
172 bool isCritical;
173
174 oidData = CFDataCreateWithBytesNoCopy(NULL, policyOid.Data, policyOid.Length,
175 kCFAllocatorNull);
176
177 if (oidData == NULL) {
178 goto out;
179 }
180
181 data = SecCertificateCopyExtensionValue(cert, oidData, &isCritical);
182
183 if (data == NULL) {
184 goto out;
185 }
186
187 status = SecAsn1CoderCreate(&coder);
188 if (status != 0) {
189 goto out;
190 }
191
192 // We currently only support UTF8 strings.
193 status = SecAsn1Decode(coder, CFDataGetBytePtr(data), CFDataGetLength(data),
194 kSecAsn1UTF8StringTemplate, &str);
195 if (status != 0) {
196 goto out;
197 }
198
199 time = SecAbsoluteTimeFromDateContent(ASN1_GENERALIZED_TIME,
200 str.Data, str.Length);
201
202 if (time == 0.0) {
203 goto out;
204 }
205
206 value = CFDateCreate(NULL, time);
207 out:
208 if (coder) {
209 SecAsn1CoderRelease(coder);
210 }
211 if (data) {
212 CFRelease(data);
213 }
214 if (oidData) {
215 CFRelease(oidData);
216 }
217
218 return value;
219 }
220 #endif
221
222 //
223 // Copyfile
224 //
225 Copyfile::Copyfile()
226 {
227 if (!(mState = copyfile_state_alloc()))
228 UnixError::throwMe();
229 }
230
231 void Copyfile::set(uint32_t flag, const void *value)
232 {
233 check(::copyfile_state_set(mState, flag, value));
234 }
235
236 void Copyfile::get(uint32_t flag, void *value)
237 {
238 check(::copyfile_state_set(mState, flag, value));
239 }
240
241 void Copyfile::operator () (const char *src, const char *dst, copyfile_flags_t flags)
242 {
243 check(::copyfile(src, dst, mState, flags));
244 }
245
246 void Copyfile::check(int rc)
247 {
248 if (rc < 0)
249 UnixError::throwMe();
250 }
251
252
253 //
254 // MessageTracer support
255 //
256 MessageTrace::MessageTrace(const char *domain, const char *signature)
257 {
258 mAsl = asl_new(ASL_TYPE_MSG);
259 if (domain)
260 asl_set(mAsl, "com.apple.message.domain", domain);
261 if (signature)
262 asl_set(mAsl, "com.apple.message.signature", signature);
263 }
264
265 void MessageTrace::add(const char *key, const char *format, ...)
266 {
267 va_list args;
268 va_start(args, format);
269 char value[200];
270 vsnprintf(value, sizeof(value), format, args);
271 va_end(args);
272 asl_set(mAsl, (string("com.apple.message.") + key).c_str(), value);
273 }
274
275 void MessageTrace::send(const char *format, ...)
276 {
277 va_list args;
278 va_start(args, format);
279 asl_vlog(NULL, mAsl, ASL_LEVEL_NOTICE, format, args);
280 va_end(args);
281 }
282
283
284
285 // Resource limited async workers for doing work on nested bundles
286 LimitedAsync::LimitedAsync(bool async)
287 {
288 // validate multiple resources concurrently if bundle resides on solid-state media
289
290 // How many async workers to spin off. If zero, validating only happens synchronously.
291 long async_workers = 0;
292
293 long ncpu = sysconf(_SC_NPROCESSORS_ONLN);
294
295 if (async && ncpu > 0)
296 async_workers = ncpu - 1; // one less because this thread also validates
297
298 mResourceSemaphore = new Dispatch::Semaphore(async_workers);
299 }
300
301 LimitedAsync::LimitedAsync(LimitedAsync &limitedAsync)
302 {
303 mResourceSemaphore = new Dispatch::Semaphore(*limitedAsync.mResourceSemaphore);
304 }
305
306 LimitedAsync::~LimitedAsync()
307 {
308 delete mResourceSemaphore;
309 }
310
311 bool LimitedAsync::perform(Dispatch::Group &groupRef, void (^block)()) {
312 __block Dispatch::SemaphoreWait wait(*mResourceSemaphore, DISPATCH_TIME_NOW);
313
314 if (wait.acquired()) {
315 dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
316
317 groupRef.enqueue(defaultQueue, ^{
318 // Hold the semaphore count until the worker is done validating.
319 Dispatch::SemaphoreWait innerWait(wait);
320 block();
321 });
322 return true;
323 } else {
324 block();
325 return false;
326 }
327 }
328
329 bool isOnRootFilesystem(const char *path)
330 {
331 int rc = 0;
332 struct statfs sfb;
333
334 rc = statfs(path, &sfb);
335 if (rc != 0) {
336 secerror("Unable to check if path is on rootfs: %d, %s", errno, path);
337 return false;
338 }
339 return ((sfb.f_flags & MNT_ROOTFS) == MNT_ROOTFS);
340 }
341
342 bool pathExists(const char *path)
343 {
344 int rc;
345
346 if (!path) {
347 secerror("path is NULL");
348 return false;
349 }
350
351 rc = access(path, F_OK);
352 if (rc != 0) {
353 if (errno != ENOENT) {
354 secerror("Unable to check if path exists: %d, %s", errno, path);
355 }
356 return false;
357 }
358
359 return true;
360 }
361
362 bool pathMatchesXattrFilenameSpec(const char *path)
363 {
364 char *baseName = NULL;
365 bool ret = false;
366
367 if (!path) {
368 secerror("path is NULL");
369 goto done;
370 }
371
372 // Extra byte for NULL storage.
373 baseName = (char *)malloc(strlen(path) + 1);
374 if (!baseName) {
375 secerror("Unable to allocate space for storing basename: %d [%s]", errno, strerror(errno));
376 goto done;
377 }
378
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.
383
384 if (basename_r(path, baseName) == NULL) {
385 secerror("Could not get basename of %s: %d [%s]", path, errno, strerror(errno));
386 goto done;
387 }
388
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
392 // name.
393 if (strlen(baseName) < 3) {
394 goto done;
395 }
396
397 if (baseName[0] != '.' || baseName[1] != '_') {
398 goto done;
399 }
400
401 ret = true;
402
403 done:
404 if (baseName) {
405 free(baseName);
406 }
407
408 return ret;
409 }
410
411 bool pathIsRegularFile(const char *path)
412 {
413 if (!path) {
414 secerror("path is NULL");
415 return false;
416 }
417
418 struct stat sb;
419 if (stat(path, &sb)) {
420 secerror("Unable to stat %s: %d [%s]", path, errno, strerror(errno));
421 return false;
422 }
423
424 return (sb.st_mode & S_IFREG) == S_IFREG;
425 }
426
427 bool pathHasXattrs(const char *path)
428 {
429 if (!path) {
430 secerror("path is NULL");
431 return false;
432 }
433
434 ssize_t xattrSize = listxattr(path, NULL, 0, 0);
435 if (xattrSize == -1) {
436 secerror("Unable to acquire the xattr list from %s", path);
437 return false;
438 }
439
440 return (xattrSize > 0);
441 }
442
443 bool pathFileSystemUsesXattrFiles(const char *path)
444 {
445 struct _VolumeCapabilitiesWrapped {
446 uint32_t length;
447 vol_capabilities_attr_t volume_capabilities;
448 } __attribute__((aligned(4), packed));
449
450 struct attrlist attr_list;
451 struct _VolumeCapabilitiesWrapped volume_cap_wrapped;
452 struct statfs sfb;
453
454 if (!path) {
455 secerror("path is NULL");
456 return false;
457 }
458
459 int ret = statfs(path, &sfb);
460 if (ret != 0) {
461 secerror("Unable to convert %s to its filesystem mount [statfs failed]: %d [%s]", path, errno, strerror(errno));
462 return false;
463 }
464 path = sfb.f_mntonname;
465
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;
469
470 ret = getattrlist(path, &attr_list, &volume_cap_wrapped, sizeof(volume_cap_wrapped), 0);
471 if (ret) {
472 secerror("Unable to get volume capabilities from %s: %d [%s]", path, errno, strerror(errno));
473 return false;
474 }
475
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);
478 return false;
479 }
480
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.
484
485 bool xattr_valid = (volume_cap_wrapped.volume_capabilities.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_EXTENDED_ATTR) == VOL_CAP_INT_EXTENDED_ATTR;
486 if (!xattr_valid) {
487 return true;
488 }
489
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) {
492 return true;
493 }
494
495 return false;
496 }
497
498 bool pathIsValidXattrFile(const string fullPath, const char *scope)
499 {
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());
503 return false;
504 }
505
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());
509 return false;
510 }
511
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());
515 return false;
516 }
517
518 // We are guaranteed to have at least one "/" by virtue of fullPath
519 // being a path from the root of the filesystem hierarchy.
520 //
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
524 // string.
525
526 size_t lastBackSlash = fullPath.find_last_of("/");
527 const string realFilePath = fullPath.substr(0, lastBackSlash) + "/" + fullPath.substr(lastBackSlash + 1 + 2);
528
529 if (!pathExists(realFilePath.c_str())) {
530 secinfo(scope, "%s does not exist, forcing resource validation on %s", realFilePath.c_str(), fullPath.c_str());
531 return false;
532 }
533
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());
538 return false;
539 }
540
541 return true;
542 }
543
544 } // end namespace CodeSigning
545 } // end namespace Security