--- /dev/null
+/*
+ * Given a cert, produce a complete ordered cert chain back to a root.
+ * Intermediate certs can be in any user keychain.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <security_cdsa_utils/cuFileIo.h> /* private */
+#include <security_cdsa_utils/cuPrintCert.h> /* private */
+#include <Security/Security.h>
+#include <Security/SecTrustPriv.h> /* private */
+
+static void usage(char **argv)
+{
+ printf("Usage:\n");
+ printf(" %s certFileName [d(isable intermediates) [f filebase] [n(o cert dump)]\n", argv[0]);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ unsigned char *certData = NULL; // subject cert, raw data
+ unsigned certDataLen = 0;
+ OSStatus ortn;
+ SecTrustRef secTrust = NULL;
+ CFMutableArrayRef subjCerts = NULL;
+ SecPolicyRef policy = NULL;
+ SecPolicySearchRef policySearch = NULL;
+ SecTrustResultType secTrustResult;
+ CSSM_RETURN crtn = CSSM_OK;
+ CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv; // not used
+ CFArrayRef certChain = NULL; // constructed chain
+ CFIndex numCerts;
+ bool disableLocalIntermediates = false;
+ char *fileBase = NULL;
+ bool enableCertDump = true;
+
+ if(argc < 2) {
+ usage(argv);
+ }
+ if(readFile(argv[1], &certData, &certDataLen)) {
+ printf("***Error reading cert from %s. Aborting.\n", argv[1]);
+ exit(1);
+ }
+ for(int arg=2; arg<argc; arg++) {
+ char *argp = argv[arg];
+ switch(argp[0]) {
+ case 'd':
+ disableLocalIntermediates = true;
+ break;
+ case 'n':
+ enableCertDump = false;
+ break;
+ case 'f':
+ arg++;
+ if(arg == argc) {
+ usage(argv);
+ }
+ fileBase = argv[arg];
+ break;
+ default:
+ usage(argv);
+ }
+ }
+
+ /* SecCertificateRef form of subject cert */
+ SecCertificateRef certRef = NULL;
+ CSSM_DATA cdata = {(uint32)certDataLen, (uint8 *)certData};
+ ortn = SecCertificateCreateFromData(&cdata,
+ CSSM_CERT_X_509v3,
+ CSSM_CERT_ENCODING_DER,
+ &certRef);
+ if(ortn) {
+ cssmPerror("SecCertificateCreateFromData", ortn);
+ goto errOut;
+ }
+
+ /* make a one-element array */
+ subjCerts = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);
+ CFArraySetValueAtIndex(subjCerts, 0, certRef);
+
+ /* the array owns the subject cert ref now */
+ CFRelease(certRef);
+
+ /* Get a SecPolicyRef for generic X509 cert chain verification */
+ ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
+ &CSSMOID_APPLE_X509_BASIC,
+ NULL, // value
+ &policySearch);
+ if(ortn) {
+ cssmPerror("SecPolicySearchCreate", ortn);
+ goto errOut;
+ }
+ ortn = SecPolicySearchCopyNext(policySearch, &policy);
+ if(ortn) {
+ cssmPerror("SecPolicySearchCopyNext", ortn);
+ goto errOut;
+ }
+
+ /* build a SecTrustRef for specified policy and certs */
+ ortn = SecTrustCreateWithCertificates(subjCerts,
+ policy, &secTrust);
+ if(ortn) {
+ cssmPerror("SecTrustCreateWithCertificates", ortn);
+ goto errOut;
+ }
+
+ if(disableLocalIntermediates) {
+ /*
+ * Avoid searching user keychains for intermediate certs
+ * by specifying an empty array of keychains
+ */
+ CFMutableArrayRef kcList;
+ kcList = CFArrayCreateMutable(NULL, 0, NULL);
+ if(kcList == NULL) {
+ printf("***CFArrayCreateMutable error\n");
+ ortn = -1;
+ goto errOut;
+ }
+ ortn = SecTrustSetKeychains(secTrust, kcList);
+ if(ortn) {
+ cssmPerror("SecTrustSetKeychains", ortn);
+ goto errOut;
+ }
+ CFRelease(kcList);
+ }
+
+ /* evaluate: GO */
+ ortn = SecTrustEvaluate(secTrust, &secTrustResult);
+ if(ortn) {
+ cssmPerror("SecTrustEvaluate", ortn);
+ goto errOut;
+ }
+ switch(secTrustResult) {
+ case kSecTrustResultUnspecified:
+ /* cert chain valid, no special UserTrust assignments */
+ case kSecTrustResultProceed:
+ /* cert chain valid AND user explicitly trusts this */
+ break;
+ case kSecTrustResultDeny:
+ case kSecTrustResultConfirm:
+ /*
+ * Cert chain may well have verified OK, but user has flagged
+ * one of these certs as untrustable.
+ */
+ printf("***User specified that a cert in this chain is untrusted.\n");
+ goto errOut;
+
+ default:
+ {
+ /* private SPI to get low-level CSSM error */
+ OSStatus osCrtn;
+ ortn = SecTrustGetCssmResultCode(secTrust, (OSStatus *)&crtn);
+ if(ortn) {
+ cssmPerror("SecTrustEvaluate", ortn);
+ goto errOut;
+ }
+ }
+ }
+ if(crtn) {
+ /* get some detailed error info */
+ switch(crtn) {
+ case CSSMERR_TP_INVALID_ANCHOR_CERT:
+ printf("***Verified to unknown anchor cert\n");
+ break;
+ case CSSMERR_TP_NOT_TRUSTED:
+ printf("***Can not verify to a root cert \n");
+ break;
+ case CSSMERR_TP_CERT_EXPIRED:
+ printf("***A cert in this chain has expired\n");
+ break;
+ case CSSMERR_TP_CERT_NOT_VALID_YET:
+ printf("***A cert in this chain is not yet valid\n");
+ break;
+ default:
+ printf("Other error from SecTrustEvaluate\n");
+ cssmPerror("SecTrustEvaluate", crtn);
+ break;
+ }
+ } /* SecTrustEvaluate error */
+
+ /* get resulting constructed cert chain */
+ ortn = SecTrustGetResult(secTrust, &secTrustResult, &certChain, &dummyEv);
+ if(ortn) {
+ cssmPerror("SecTrustEvaluate", ortn);
+ goto errOut;
+ }
+
+ /* display the results */
+ numCerts = CFArrayGetCount(certChain);
+ printf("Number of certs in constructed cert chain = %d\n", (int)numCerts);
+ if(enableCertDump) {
+ for(unsigned i=0; i<numCerts; i++) {
+ CSSM_DATA cd;
+ certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, i);
+ ortn = SecCertificateGetData(certRef, &cd);
+ if(ortn) {
+ printf("***SecCertificateGetData returned %d\n", (int)ortn);
+ continue;
+ }
+ printf("\n================== Cert %d ===================\n\n", i);
+ printCert(cd.Data, cd.Length, CSSM_FALSE);
+ printf("\n=============== End of Cert %d ===============\n", i);
+
+ if((fileBase != NULL) & (i > 0)) {
+ char fname[200];
+ sprintf(fname, "%s_%u", fileBase, i);
+ if(writeFile(fname, cd.Data, cd.Length)) {
+ printf("***Error writing to %s\n", fname);
+ }
+ else {
+ printf("...write %lu bytes to %s\n", cd.Length, fname);
+ }
+ }
+ }
+ }
+errOut:
+ if(certData) {
+ /* mallocds by readFile() */
+ free(certData);
+ }
+ if(secTrust) {
+ CFRelease(secTrust);
+ }
+ if(subjCerts) {
+ CFRelease(subjCerts);
+ }
+ if(policy) {
+ CFRelease(policy);
+ }
+ if(policySearch) {
+ CFRelease(policySearch);
+ }
+ return (int)ortn;
+}