2 cc -g -O0 -Wall -Werror -Wmost -stabs -o codesign_wrapper codesign_wrapper.c -framework CoreFoundation
16 #include <sys/socket.h>
21 #include <CoreFoundation/CFData.h>
22 #include <CoreFoundation/CFDictionary.h>
23 #include <CoreFoundation/CFPropertyList.h>
24 #include <CoreFoundation/CFString.h>
27 #include <Security/SecCmsMessage.h>
28 #include <Security/SecCmsSignedData.h>
29 #include <Security/SecCmsContentInfo.h>
30 #include <Security/SecCmsSignerInfo.h>
31 #include <Security/SecCmsEncoder.h>
32 #include <Security/SecCmsDecoder.h>
33 #include <Security/SecCmsDigestContext.h>
34 #include <Security/oidsalg.h>
35 #include <Security/cmspriv.h>
37 #include <Security/SecCMS.h>
39 #include <Security/SecPolicy.h>
40 #include <Security/SecCertificate.h>
41 #include <Security/SecCertificatePriv.h>
43 /* entitlement whitelist checking */
44 #include <MISEntitlement.h>
46 #define DEBUG_ASSERT_PRODUCTION_CODE 0
47 #include <AssertMacros.h>
49 #include "codesign_wrapper.h"
52 extern bool do_verify(const char *path
, CFArrayRef certificates
);
54 static char * codesign_binary
= "/usr/bin/codesign";
55 static char * processing_path
= "/var/tmp/signingbox";
56 static char * processing_file
= "codesign_wrapper";
57 static char * processing_prefix
= NULL
;
58 static char * auditing_postfix
= "_auditing.plist";
59 static char * audition_plist_path
= NULL
;
60 static char * entitlements_plist_path
= NULL
;
61 static char * entitlements_postfix
= "_entitlements.plist";
64 #define CODESIGN_WRAPPER_VERSION "0.7.10"
65 #define log(format, args...) \
66 fprintf(stderr, "codesign_wrapper-" CODESIGN_WRAPPER_VERSION ": " format "\n", ##args);
67 #define cflog(format, args...) do { \
68 CFStringRef logstr = CFStringCreateWithFormat(NULL, NULL, CFSTR(format), ##args);\
69 if (logstr) { CFShow(logstr); CFRelease(logstr); } \
72 const char *_root_ca_name = ANCHOR;
74 static pid_t kill_child
= -1;
75 static void child_timeout(int sig
)
77 if (kill_child
!= -1) {
78 kill(kill_child
, sig
);
84 close_all_fd(void *arg __unused
)
85 /* close down any files that might have been open at this point
86 but make sure 0, 1 and 2 are set to /dev/null so they don't
89 int maxDescriptors
= getdtablesize ();
92 int devnull
= open(_PATH_DEVNULL
, O_RDWR
, 0);
94 if (devnull
>= 0) for (i
= 0; i
< 3; ++i
)
97 for (i
= 3; i
< maxDescriptors
; ++i
)
103 fork_child(void (*pre_exec
)(void *arg
), void *pre_exec_arg
,
104 const char * const argv
[])
106 unsigned delay
= 1, maxDelay
= 60;
109 switch (pid
= fork()) {
110 case -1: /* fork failed */
113 continue; /* no problem */
115 if (delay
< maxDelay
) {
125 assert(-1); /* unreached */
129 pre_exec(pre_exec_arg
);
130 execv(argv
[0], (char * const *)argv
);
134 default: /* parent */
145 fork_child_timeout(void (*pre_exec
)(), char *pre_exec_arg
,
146 const char * const argv
[], int timeout
)
148 int exit_status
= -1;
149 pid_t child_pid
= fork_child(pre_exec
, pre_exec_arg
, argv
);
151 kill_child
= child_pid
;
155 int err
= wait4(child_pid
, &exit_status
, 0, NULL
);
161 if (err
== child_pid
) {
162 if (WIFSIGNALED(exit_status
)) {
163 log("child %d received signal %d", child_pid
, WTERMSIG(exit_status
));
164 kill(child_pid
, SIGHUP
);
167 if (WIFEXITED(exit_status
))
168 return WEXITSTATUS(exit_status
);
178 dup2(arg
[0], arg
[1]);
184 fork_child_timeout_output(int child_fd
, int *parent_fd
, const char * const argv
[], int timeout
)
187 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, output
))
189 fcntl(output
[1], F_SETFD
, 1); /* close in child */
190 int redirect_child
[] = { output
[0], child_fd
};
191 int err
= fork_child_timeout(dup_io
, (void*)redirect_child
, argv
, timeout
);
193 close(output
[0]); /* close the child side in the parent */
194 *parent_fd
= output
[1];
201 pass_signal_to_children(int sig
)
203 signal(sig
, SIG_DFL
);
209 mk_temp_dir(const char *path
)
211 char *pos
= NULL
, *tmp_path
= strdup(path
);
212 if (!path
) return -1;
213 pos
= index(tmp_path
, '/');
215 while ((pos
= index(pos
+ 1, '/'))) {
217 if ((0 != mkdir(tmp_path
, 0755)) &&
222 if ((0 != mkdir(tmp_path
, 0755)) &&
230 lock_file(const char *lock_file_prefix
, const char *lock_filename
)
234 char *tempfile
= NULL
;
236 if (!asprintf(&tempfile
, "%s.%d", lock_file_prefix
, getpid()))
238 FILE *temp
= fopen(tempfile
, "w");
241 if (fprintf(temp
, "%d\n", getpid()) <= 0)
244 if(!link(tempfile
, lock_filename
)) {
249 FILE* lock
= fopen(lock_filename
, "r");
252 if (fscanf(lock
, "%d\n", &pid
) <= 0)
255 if (!unlink(lock_filename
) &&
256 !link(tempfile
, lock_filename
)) {
272 read_fd(int fd
, void **buffer
)
275 size_t capacity
= 1024;
276 char * data
= malloc(capacity
);
279 int bytes_left
= capacity
- size
;
280 int bytes_read
= read(fd
, data
+ size
, bytes_left
);
281 if (bytes_read
>= 0) {
283 if (capacity
== size
) {
285 data
= realloc(data
, capacity
);
307 enum { CSMAGIC_EMBEDDED_ENTITLEMENTS
= 0xfade7171 };
315 extract_entitlements_blob(const uint8_t *data
, size_t length
)
317 CFDataRef entitlements
= NULL
;
318 cs_blob_index
*csbi
= (cs_blob_index
*)data
;
320 require(data
&& length
, out
);
321 require(csbi
->type
== ntohl(CSMAGIC_EMBEDDED_ENTITLEMENTS
), out
);
322 require(length
== ntohl(csbi
->offset
), out
);
323 entitlements
= CFDataCreate(kCFAllocatorDefault
,
324 (uint8_t*)(data
+ sizeof(cs_blob_index
)),
325 (CFIndex
)(length
- sizeof(cs_blob_index
)));
331 build_entitlements_blob(const uint8_t *data
, size_t length
)
333 cs_blob_index csbi
= { htonl(CSMAGIC_EMBEDDED_ENTITLEMENTS
),
334 htonl(length
+sizeof(csbi
)) };
335 CFMutableDataRef blob
= CFDataCreateMutable(kCFAllocatorDefault
, sizeof(csbi
)+length
);
337 CFDataAppendBytes(blob
, (uint8_t*)&csbi
, sizeof(csbi
));
338 CFDataAppendBytes(blob
, data
, length
);
343 static CFMutableDictionaryRef
344 dump_auditing_info(const char *path
)
347 CFMutableDictionaryRef dict
= NULL
;
348 void *requirements
= NULL
;
349 ssize_t requirements_size
= 0;
350 void *entitlements
= NULL
;
351 ssize_t entitlements_size
= 0;
354 const char * const extract_requirements
[] =
355 { codesign_binary
, "--display", "-v", "-v", path
, NULL
};
357 if ((exit_status
= fork_child_timeout_output(STDERR_FILENO
, &requirements_fd
,
358 extract_requirements
, 0))) {
359 fprintf(stderr
, "failed to extract requirements data: %d\n", exit_status
);
363 requirements_size
= read_fd(requirements_fd
, &requirements
);
364 if (requirements_size
== -1)
366 close(requirements_fd
);
371 const char * const extract_entitlements
[] =
372 { codesign_binary
, "--display", "--entitlements", "-", path
, NULL
};
374 if ((exit_status
= fork_child_timeout_output(STDOUT_FILENO
, &entitlements_fd
,
375 extract_entitlements
, 0))) {
376 fprintf(stderr
, "failed to extract entitlements: %d\n", exit_status
);
380 entitlements_size
= read_fd(entitlements_fd
, &entitlements
);
381 if (entitlements_size
== -1)
383 close(entitlements_fd
);
388 dict
= CFDictionaryCreateMutable(
389 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
390 &kCFTypeDictionaryValueCallBacks
);
392 if (requirements
&& requirements_size
) {
393 CFDataRef req
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
394 requirements
, requirements_size
, kCFAllocatorMalloc
);
395 CFDictionarySetValue(dict
, CFSTR("Requirements"), req
);
399 if (entitlements
&& entitlements_size
) {
400 CFDataRef ent
= extract_entitlements_blob(entitlements
, entitlements_size
);
403 CFPropertyListRef entitlements_dict
=
404 CFPropertyListCreateWithData(kCFAllocatorDefault
,
405 ent
, kCFPropertyListImmutable
, NULL
, NULL
);
407 require(entitlements_dict
, out
);
408 CFDictionarySetValue(dict
, CFSTR("Entitlements"), entitlements_dict
);
409 CFRelease(entitlements_dict
);
419 write_data(const char *path
, CFDataRef data
)
421 int fd
= open(path
, O_CREAT
|O_TRUNC
|O_WRONLY
, 0644);
422 ssize_t length
= CFDataGetLength(data
);
425 int bytes_written
= write(fd
, CFDataGetBytePtr(data
), length
);
428 if (bytes_written
!= length
) {
429 fprintf(stderr
, "failed to write auditing info to %s\n", path
);
438 write_auditing_data(const char *path
, CFMutableDictionaryRef info
)
440 CFTypeRef entitlements
= CFDictionaryGetValue(info
, CFSTR("Entitlements"));
442 CFDataRef entitlements_xml
= CFPropertyListCreateXMLData(kCFAllocatorDefault
, entitlements
);
443 if (!entitlements_xml
)
445 CFDictionarySetValue(info
, CFSTR("Entitlements"), entitlements_xml
);
446 CFRelease(entitlements_xml
);
449 CFDataRef plist
= CFPropertyListCreateXMLData(kCFAllocatorDefault
, info
);
453 return write_data(path
, plist
); /* consumes plist */
457 write_filtered_entitlements(const char *path
, CFDictionaryRef info
)
459 CFDataRef plist
= CFPropertyListCreateXMLData(kCFAllocatorDefault
, info
);
462 CFDataRef entitlements_blob
=
463 build_entitlements_blob(CFDataGetBytePtr(plist
), CFDataGetLength(plist
));
465 if (!entitlements_blob
)
467 return write_data(path
, entitlements_blob
); /* consumes entitlements_blob */
472 cfdata_read_file(const char *filename
)
474 int data_file
= open(filename
, O_RDONLY
);
478 ssize_t size
= read_fd(data_file
, &data
);
480 return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
481 data
, size
, kCFAllocatorMalloc
);
486 static CFDictionaryRef
487 load_profile(const char *profile_path
)
489 SecCmsMessageRef cmsg
= NULL
;
490 CFDictionaryRef entitlements
= NULL
;
491 CFArrayRef certificates
= NULL
;
492 CFDictionaryRef profile
= NULL
;
494 CFDataRef message
= cfdata_read_file(profile_path
);
495 require(message
, out
);
496 SecAsn1Item encoded_message
= { CFDataGetLength(message
),
497 (uint8_t*)CFDataGetBytePtr(message
) };
498 require_noerr(SecCmsMessageDecode(&encoded_message
,
499 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &cmsg
), out
);
501 /* expected to be a signed data message at the top level */
502 SecCmsContentInfoRef cinfo
;
503 SecCmsSignedDataRef sigd
;
504 require(cinfo
= SecCmsMessageContentLevel(cmsg
, 0), out
);
505 require(SecCmsContentInfoGetContentTypeTag(cinfo
) ==
506 SEC_OID_PKCS7_SIGNED_DATA
, out
);
507 require(sigd
= (SecCmsSignedDataRef
)SecCmsContentInfoGetContent(cinfo
), out
);
509 SecPolicyRef policy
= NULL
;
510 SecTrustRef trust
= NULL
;
511 policy
= SecPolicyCreateBasicX509();
512 int nsigners
= SecCmsSignedDataSignerInfoCount(sigd
);
513 require(nsigners
== 1, out
);
514 require_noerr(SecCmsSignedDataVerifySignerInfo(sigd
, 0, NULL
, policy
, &trust
), out
);
515 SecCertificateRef apple_ca_cert
= NULL
;
516 CFArrayRef apple_ca_cert_anchors
= NULL
;
517 require(apple_ca_cert
= SecCertificateCreateWithBytes(NULL
, _profile_anchor
, sizeof(_profile_anchor
)), out
);
518 require(apple_ca_cert_anchors
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&apple_ca_cert
, 1, NULL
), out
);
519 require_noerr(SecTrustSetAnchorCertificates(trust
, apple_ca_cert_anchors
), out
);
520 log("using %s for profile evaluation", _root_ca_name
);
521 SecTrustResultType trust_result
;
522 require_noerr(SecTrustEvaluate(trust
, &trust_result
), out
);
524 /* doesn't mean much, but I don't have the root */
525 require(trust_result
== kSecTrustResultRecoverableTrustFailure
, out
);
527 require(trust_result
== kSecTrustResultUnspecified
, out
);
529 CFRelease(apple_ca_cert_anchors
);
531 // FIXME require proper intermediate and leaf certs
532 // require_noerr(SecCertificateCopyCommonName(SecCertificateRef certificate, CFStringRef *commonName);
536 SecCmsSignerInfoRef sinfo
= SecCmsSignedDataGetSignerInfo(sigd
, 0);
538 CFStringRef commonname
= SecCmsSignerInfoGetSignerCommonName(sinfo
);
539 require(commonname
, out
);
541 require(CFEqual(CFSTR("Alpha Config Profile Signing Certificate"), commonname
), out
);
543 require(CFEqual(CFSTR("Apple iPhone OS Provisioning Profile Signing"), commonname
) ||
544 CFEqual(CFSTR("TEST Apple iPhone OS Provisioning Profile Signing TEST"), commonname
), out
);
546 CFRelease(commonname
);
549 const SecAsn1Item
*content
= SecCmsMessageGetContent(cmsg
);
550 require(content
&& content
->Length
&& content
->Data
, out
);
552 CFDataRef attached_contents
= CFDataCreate(kCFAllocatorDefault
,
553 content
->Data
, content
->Length
);
554 CFPropertyListRef plist
= CFPropertyListCreateWithData(kCFAllocatorDefault
,
555 attached_contents
, kCFPropertyListImmutable
, NULL
, NULL
);
556 CFRelease(attached_contents
);
557 require(plist
&& CFGetTypeID(plist
) == CFDictionaryGetTypeID(), out
);
559 CFTypeRef profile_certificates
= CFDictionaryGetValue(plist
, CFSTR("DeveloperCertificates"));
560 if (profile_certificates
&& CFGetTypeID(profile_certificates
) == CFArrayGetTypeID())
562 certificates
= CFArrayCreateCopy(kCFAllocatorDefault
, profile_certificates
);
564 CFIndex i
, cert_count
= CFArrayGetCount(certificates
);
565 for (i
= 0; i
< cert_count
; i
++) {
566 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, CFArrayGetValueAtIndex(certificates
, i
));
573 CFTypeRef profile_entitlements
= CFDictionaryGetValue(plist
, CFSTR("Entitlements"));
574 if (profile_entitlements
&& CFGetTypeID(profile_entitlements
) == CFDictionaryGetTypeID())
576 entitlements
= CFDictionaryCreateCopy(kCFAllocatorDefault
,
577 (CFDictionaryRef
)profile_entitlements
);
582 if (cmsg
) SecCmsMessageDestroy(cmsg
);
583 if (entitlements
&& certificates
) {
584 const void *keys
[] = { CFSTR("Entitlements"), CFSTR("Certificates") };
585 const void *vals
[] = { entitlements
, certificates
};
586 profile
= CFDictionaryCreate(kCFAllocatorDefault
, keys
, vals
, 2,
587 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
589 if (entitlements
) CFRelease(entitlements
);
590 if (certificates
) CFRelease(certificates
);
595 CFDictionaryRef whitelist
;
596 CFMutableDictionaryRef filtered_list
;
597 bool allowed_entitlements
;
598 } filter_whitelist_ctx
;
601 filter_entitlement(const void *key
, const void *value
,
602 filter_whitelist_ctx
*ctx
)
604 /* filter out get-task-allow, no error */
605 if (CFEqual(key
, CFSTR("get-task-allow")))
608 /* whitelist data protection entitlement, otherwise validate */
609 if (!CFEqual(key
, CFSTR("DataProtectionClass")) &&
610 !CFEqual(key
, CFSTR("data-protection-class")) &&
611 !MISEntitlementDictionaryAllowsEntitlementValue(ctx
->whitelist
, key
, value
)) {
612 ctx
->allowed_entitlements
= false;
613 cflog("Illegal entitlement key/value pair: %@, %@", key
, value
);
617 if (ctx
->filtered_list
)
618 CFDictionarySetValue(ctx
->filtered_list
, key
, value
);
622 filter_entitlements(CFDictionaryRef whitelist
, CFDictionaryRef entitlements
,
623 CFMutableDictionaryRef filtered_entitlements
)
628 filter_whitelist_ctx ctx
= { whitelist
, filtered_entitlements
, true };
629 CFDictionaryApplyFunction(entitlements
,
630 (CFDictionaryApplierFunction
)filter_entitlement
, &ctx
);
631 return ctx
.allowed_entitlements
;
634 static SecCertificateRef
635 cms_verify_signer(CFDataRef message
, CFDataRef detached
)
637 SecCertificateRef signer_cert
= NULL
;
638 require(message
, out
);
640 SecPolicyRef policy
= NULL
;
641 SecTrustRef trust
= NULL
;
642 policy
= SecPolicyCreateBasicX509();
644 SecCmsMessageRef cmsg
= NULL
;
645 SecCmsContentInfoRef cinfo
;
646 SecCmsSignedDataRef sigd
= NULL
;
648 SecAsn1Item encoded_message
= { CFDataGetLength(message
), (uint8_t*)CFDataGetBytePtr(message
) };
649 require_noerr(SecCmsMessageDecode(&encoded_message
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &cmsg
),
651 /* expected to be a signed data message at the top level */
652 require(cinfo
= SecCmsMessageContentLevel(cmsg
, 0), out
);
653 require(SecCmsContentInfoGetContentTypeTag(cinfo
) == SEC_OID_PKCS7_SIGNED_DATA
, out
);
654 require(sigd
= (SecCmsSignedDataRef
)SecCmsContentInfoGetContent(cinfo
), out
);
657 require(!SecCmsSignedDataHasDigests(sigd
), out
);
658 SECAlgorithmID
**digestalgs
= SecCmsSignedDataGetDigestAlgs(sigd
);
659 SecCmsDigestContextRef digcx
= SecCmsDigestContextStartMultiple(digestalgs
);
660 SecCmsDigestContextUpdate(digcx
, CFDataGetBytePtr(detached
), CFDataGetLength(detached
));
661 SecCmsSignedDataSetDigestContext(sigd
, digcx
);
662 SecCmsDigestContextDestroy(digcx
);
665 int nsigners
= SecCmsSignedDataSignerInfoCount(sigd
);
666 require_quiet(nsigners
== 1, out
);
667 require_noerr_string(SecCmsSignedDataVerifySignerInfo(sigd
, 0, NULL
, policy
, &trust
), out
, "bad signature");
669 signer_cert
= SecTrustGetCertificateAtIndex(trust
, 0);
670 CFRetain(signer_cert
);
680 cms_verify(CFDataRef message
, CFDataRef detached
, CFArrayRef certificates
)
683 SecCertificateRef signer_cert
= cms_verify_signer(message
, detached
);
684 require(signer_cert
, out
);
686 CFDataRef cert_cfdata
= SecCertificateCopyData(signer_cert
);
687 CFRange all_certs
= CFRangeMake(0, CFArrayGetCount(certificates
));
688 result
= CFArrayContainsValue(certificates
, all_certs
, cert_cfdata
);
689 CFRelease(cert_cfdata
);
691 CFArrayRef commonNames
= SecCertificateCopyCommonNames(signer_cert
);
692 require(CFArrayGetCount(commonNames
) == 1, out
);
693 CFStringRef commonName
= (CFStringRef
)CFArrayGetValueAtIndex(commonNames
, 0);
694 require(commonName
, out
);
695 result
= CFEqual(CFSTR("Apple iPhone OS Application Signing"), commonName
)
696 || CFEqual(CFSTR("TEST Apple iPhone OS Application Signing TEST"), commonName
);
697 CFRelease(commonNames
);
701 fprintf(stderr
, "Disallowed signer\n");
704 if (signer_cert
) CFRelease(signer_cert
);
711 verify_code_signatures(CFArrayRef code_signatures
, CFArrayRef certificates
)
713 require(code_signatures
, out
);
714 CFIndex i
, signature_count
= CFArrayGetCount(code_signatures
);
716 /* Each slice can have their own entitlements and be properly signed
717 but codesign(1) picks the first when listing and smashes that one
718 down when re-signing */
719 CFDataRef first_entitlement_hash
= NULL
;
720 for (i
= 0; i
< signature_count
; i
++) {
721 CFDictionaryRef code_signature
= CFArrayGetValueAtIndex(code_signatures
, i
);
723 CFDataRef signature
= CFDictionaryGetValue(code_signature
, CFSTR("SignedData"));
724 require(signature
, out
);
725 CFDataRef code_directory
= CFDictionaryGetValue(code_signature
, CFSTR("CodeDirectory"));
726 require(code_directory
, out
);
727 CFDataRef entitlements
= CFDictionaryGetValue(code_signature
, CFSTR("Entitlements"));
728 CFDataRef entitlements_hash
= CFDictionaryGetValue(code_signature
, CFSTR("EntitlementsHash"));
729 CFDataRef entitlements_cdhash
= CFDictionaryGetValue(code_signature
, CFSTR("EntitlementsCDHash"));
730 require(entitlements
, out
);
731 require(entitlements_hash
, out
);
732 require(entitlements_cdhash
, out
);
733 require(CFEqual(entitlements_hash
, entitlements_cdhash
), out
);
735 if (!first_entitlement_hash
)
736 first_entitlement_hash
= entitlements_hash
;
738 require(entitlements_hash
&& CFEqual(first_entitlement_hash
, entitlements_hash
), out
);
740 /* was the application signed by a certificate in the profile */
741 require(cms_verify(signature
, code_directory
, certificates
), out
);
752 signal(SIGHUP
, pass_signal_to_children
);
753 signal(SIGINT
, pass_signal_to_children
);
754 signal(SIGTERM
, pass_signal_to_children
);
755 //signal(SIGCHLD, SIG_IGN);
756 signal(SIGALRM
, child_timeout
);
758 const char *codesign_binary_env
= getenv("CODESIGN");
759 if (codesign_binary_env
)
760 codesign_binary
= strdup(codesign_binary_env
);
762 const char *processing_path_env
= getenv("PROCESS_PATH");
763 if (processing_path_env
)
764 processing_path
= strdup(processing_path_env
);
766 processing_prefix
= calloc(1, strlen(processing_path
) +
767 strlen(processing_file
) + 1/*'/'*/ + 1/*'\0'*/);
768 strcat(processing_prefix
, processing_path
);
769 strcat(processing_prefix
, "/");
770 strcat(processing_prefix
, processing_file
);
772 audition_plist_path
= calloc(1, strlen(processing_prefix
) +
773 strlen(auditing_postfix
) + 1);
774 strcat(audition_plist_path
, processing_prefix
);
775 strcat(audition_plist_path
, auditing_postfix
);
777 entitlements_plist_path
= calloc(1, strlen(processing_prefix
) +
778 strlen(entitlements_postfix
) + 1);
779 strcat(entitlements_plist_path
, processing_prefix
);
780 strcat(entitlements_plist_path
, entitlements_postfix
);
785 const struct option options
[] = {
786 { "sign", required_argument
, NULL
, 's' },
787 { "entitlements", required_argument
, NULL
, 'z' },
788 { "no-profile", no_argument
, NULL
, 'Z' },
789 { "verify", no_argument
, NULL
, 'V' }, /* map to V to let verbose v pass */
790 { "timeout", required_argument
, NULL
, 't' },
794 struct securityd
*gSecurityd
;
795 void securityd_init();
796 CFArrayRef
SecAccessGroupsGetCurrent(void);
799 SecSecurityClientGet(void)
801 static SecurityClient client
;
802 static dispatch_once_t onceToken
;
803 dispatch_once(&onceToken
, ^{
804 client
.task
= SecTaskGetSelf();
805 client
.accessGroups
= NULL
;
806 client
.allowSystemKeychain
= false;
807 client
.allowSyncBubbleKeychain
= false;
808 client
.isNetworkExtension
= false,
813 CFArrayRef
SecAccessGroupsGetCurrent(void) {
817 OSStatus
ServerCommandSendReceive(uint32_t id
, CFTypeRef in
, CFTypeRef
*out
);
818 OSStatus
ServerCommandSendReceive(uint32_t id
, CFTypeRef in
, CFTypeRef
*out
)
827 main(int argc
, char *argv
[])
832 bool sign_op
= false, noprofile
= false,
838 while ((ch
= getopt_long(argc
, argv
, "fvr:s:R:", options
, NULL
)) != -1)
841 case 's': sign_op
= true; break;
842 case 'z': { log("codesign_wrapper reserves the entitlements option for itself");
843 exit(1); /* XXX load entitlements from optarg */
845 case 'Z': noprofile
= true; break;
846 case 'V': verify_op
= true; break;
847 case 't': timeout
= atoi(optarg
); break;
850 int arg_index_files
= optind
;
851 if ((!sign_op
&& !verify_op
) || arg_index_files
== argc
) {
852 log("not a signing/verify operation, or no file to sign given");
853 return 1; /* short circuit to codesign binary: not signing, no files */
855 if (arg_index_files
+ 1 != argc
) {
856 log("cannot sign more than one file in an operation");
857 return 1; /* we don't do more than one file at a time, so we can rejigger */
861 if (mk_temp_dir(processing_path
)) {
862 log("failed to create directory %s", processing_path
);
866 CFMutableDictionaryRef auditing_info
=
867 dump_auditing_info(argv
[arg_index_files
]);
868 if (!auditing_info
) {
869 log("failed to extract auditing_info from %s", argv
[arg_index_files
]);
873 /* load up entitlements requested */
874 CFDictionaryRef entitlements_requested
=
875 CFDictionaryGetValue(auditing_info
, CFSTR("Entitlements"));
876 require_string(entitlements_requested
, out
, "At least need an application-identifier entitlements");
877 CFMutableDictionaryRef allowable_entitlements
= NULL
;
880 /* XXX if (verify_op) require it to be store signed */
882 /* load the code signature */
883 CFArrayRef code_signatures
=
884 load_code_signatures(argv
[arg_index_files
]);
885 require(code_signatures
, out
);
886 require(verify_code_signatures(code_signatures
, NULL
), out
);
887 CFRelease(code_signatures
);
891 /* do the same checks, pass signed in entitlements along for audit */
892 require(CFDictionaryGetValue(entitlements_requested
,
893 CFSTR("application-identifier")), out
);
895 CFDictionarySetValue(auditing_info
, CFSTR("Entitlements"),
896 entitlements_requested
);
898 allowable_entitlements
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, entitlements_requested
);
900 /* For the 2-pass signing, where the app is signed first, then encrypted
901 and then resigned, we need to by pass the initial validation, so we
902 allow signing without checking the entitlements to the profile. */
904 log("You shouldn't want to sign without a profile.");
910 /* load up the profile */
911 char profile_path
[_POSIX_PATH_MAX
] = {};
912 snprintf(profile_path
, sizeof(profile_path
), "%s/embedded.mobileprovision", argv
[arg_index_files
]);
913 CFDictionaryRef profile
= load_profile(profile_path
);
914 require_action(profile
, out
, log("Failed to load provision profile from: %s", profile_path
));
915 CFDictionaryRef entitlements_whitelist
= CFDictionaryGetValue(profile
, CFSTR("Entitlements"));
916 require(entitlements_whitelist
, out
);
917 CFArrayRef certificates
= CFDictionaryGetValue(profile
, CFSTR("Certificates"));
918 require(certificates
, out
);
921 require_noerr(unlink(profile_path
), out
);
923 /* only allow identifiers whitelisted by profile */
924 allowable_entitlements
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
925 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
926 require(allowable_entitlements
, out
);
927 require(filter_entitlements(entitlements_whitelist
,
928 entitlements_requested
, allowable_entitlements
), out
);
930 /* must have valid application-identifier */
931 require(CFDictionaryGetValue(allowable_entitlements
,
932 CFSTR("application-identifier")), out
);
934 CFDictionarySetValue(auditing_info
, CFSTR("Entitlements"),
935 allowable_entitlements
);
938 /* load the code signature */
939 CFArrayRef code_signatures
=
940 load_code_signatures(argv
[arg_index_files
]);
941 require(code_signatures
, out
);
942 require(verify_code_signatures(code_signatures
, certificates
), out
);
943 CFRelease(code_signatures
);
947 char *lock_filename
= NULL
;
950 if (!asprintf(&lock_filename
, "%s.lock", processing_prefix
)) {
951 log("failed to alloc %s.lock", processing_prefix
);
955 while (lock_file(processing_prefix
, lock_filename
)) {
956 log("waiting for lock");
960 err
= write_auditing_data(audition_plist_path
, auditing_info
);
962 if (!err
&& allowable_entitlements
) {
963 err
|= write_filtered_entitlements(entitlements_plist_path
, allowable_entitlements
);
967 log("failed to write auditing data");
971 char *orig_args
[argc
+1+2];
972 /* size_t argv_size = argc * sizeof(*argv); args = malloc(argv_size); */
973 memcpy(orig_args
, argv
, (argc
-1) * sizeof(*argv
));
975 int arg
= 0, argo
= 0;
976 while (arg
< argc
- 1) {
977 if (strcmp("--no-profile", orig_args
[arg
]) &&
978 strncmp("--timeout", orig_args
[arg
], strlen("--timeout"))) {
979 orig_args
[argo
] = argv
[arg
];
984 if (entitlements_requested
&& allowable_entitlements
) {
985 orig_args
[argo
++] = "--entitlements";
986 orig_args
[argo
++] = entitlements_plist_path
;
988 orig_args
[argo
++] = argv
[arg_index_files
];
989 orig_args
[argo
++] = NULL
;
990 orig_args
[0] = codesign_binary
;
992 log("Caling codesign with the following args:");
994 for(ix
= 0; ix
<= argc
; ix
++)
995 log(" %s", orig_args
[ix
] ? orig_args
[ix
] : "NULL");
997 err
= fork_child_timeout(NULL
, NULL
, (const char * const *)orig_args
, timeout
);
1001 unlink(audition_plist_path
);
1002 unlink(entitlements_plist_path
);
1004 free(audition_plist_path
);
1005 free(entitlements_plist_path
);
1008 log("executing codesign(1) timed out");
1009 const char * const kill_tokens
[] = { "/usr/bin/killall", "Ingrian", NULL
};
1010 fork_child_timeout(close_all_fd
, NULL
, kill_tokens
, 0);
1011 const char * const load_tokens
[] = { "/usr/bin/killall", "-USR2", "securityd", NULL
};
1012 fork_child_timeout(close_all_fd
, NULL
, load_tokens
, 0);
1015 unlink(lock_filename
);
1016 free(lock_filename
);
1020 log("delayed exit with timeout return value now we've tried to reload tokens");
1028 log("failed to execute codesign(1)");
1032 #endif /* UNIT_TESTING */
1034 /* vim: set et : set sw=4 : set ts=4 : set sts=4 : */