/* * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ * * verify_cert.c */ #include #include #include #include #include #include #include #include #include "trusted_cert_utils.h" /* * Read file as a cert, add to a CFArray, creating the array if necessary */ static int addCertFile( const char *fileName, CFMutableArrayRef *array) { SecCertificateRef certRef; if(readCertFile(fileName, &certRef)) { return -1; } if(*array == NULL) { *array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); } CFArrayAppendValue(*array, certRef); CFRelease(certRef); return 0; } int verify_cert(int argc, char * const *argv) { extern char *optarg; extern int optind; OSStatus ortn; int arg; CFMutableArrayRef certs = NULL; CFMutableArrayRef roots = NULL; CFMutableArrayRef keychains = NULL; const CSSM_OID *policy = &CSSMOID_APPLE_X509_BASIC; SecKeychainRef kcRef = NULL; int ourRtn = 0; bool quiet = false; SecPolicyRef policyRef = NULL; SecTrustRef trustRef = NULL; SecPolicySearchRef searchRef = NULL; const char *emailAddrs = NULL; const char *sslHost = NULL; CSSM_APPLE_TP_SSL_OPTIONS sslOpts; CSSM_APPLE_TP_SMIME_OPTIONS smimeOpts; CSSM_APPLE_TP_ACTION_FLAGS actionFlags = 0; bool forceActionFlags = false; CSSM_APPLE_TP_ACTION_DATA actionData; CSSM_DATA optionData; CFDataRef cfActionData = NULL; SecTrustResultType resultType; OSStatus ocrtn; if(argc < 2) { return 2; /* @@@ Return 2 triggers usage message. */ } /* permit network cert fetch unless explicitly turned off with '-L' */ actionFlags |= CSSM_TP_ACTION_FETCH_CERT_FROM_NET; optind = 1; while ((arg = getopt(argc, argv, "c:r:p:k:e:s:Llnq")) != -1) { switch (arg) { case 'c': /* this can be specified multiple times */ if(addCertFile(optarg, &certs)) { ourRtn = 1; goto errOut; } break; case 'r': /* this can be specified multiple times */ if(addCertFile(optarg, &roots)) { ourRtn = 1; goto errOut; } break; case 'p': policy = policyStringToOid(optarg); if(policy == NULL) { ourRtn = 2; goto errOut; } break; case 'k': ortn = SecKeychainOpen(optarg, &kcRef); if(ortn) { cssmPerror("SecKeychainOpen", ortn); ourRtn = 1; goto errOut; } /* this can be specified multiple times */ if(keychains == NULL) { keychains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); } CFArrayAppendValue(keychains, kcRef); CFRelease(kcRef); break; case 'L': actionFlags &= ~CSSM_TP_ACTION_FETCH_CERT_FROM_NET; forceActionFlags = true; break; case 'l': actionFlags |= CSSM_TP_ACTION_LEAF_IS_CA; break; case 'n': /* No keychains, signalled by empty keychain array */ if(keychains != NULL) { fprintf(stderr, "-k and -n are mutually exclusive\n"); ourRtn = 2; goto errOut; } keychains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); break; case 'e': emailAddrs = optarg; break; case 's': sslHost = optarg; break; case 'q': quiet = true; break; default: ourRtn = 2; goto errOut; } } if(optind != argc) { ourRtn = 2; goto errOut; } if(certs == NULL) { if(roots == NULL) { fprintf(stderr, "***No certs specified.\n"); ourRtn = 2; goto errOut; } if(CFArrayGetCount(roots) != 1) { fprintf(stderr, "***Multiple roots and no certs not allowed.\n"); ourRtn = 2; goto errOut; } /* no certs and one root: verify the root */ certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); CFArrayAppendValue(certs, CFArrayGetValueAtIndex(roots, 0)); actionFlags |= CSSM_TP_ACTION_LEAF_IS_CA; } /* cook up a SecPolicyRef */ ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3, policy, NULL, // policy opts &searchRef); if(ortn) { cssmPerror("SecPolicySearchCreate", ortn); ourRtn = 1; goto errOut; } ortn = SecPolicySearchCopyNext(searchRef, &policyRef); if(ortn) { cssmPerror("SecPolicySearchCopyNext", ortn); ourRtn = 1; goto errOut; } /* per-policy options */ if(compareOids(policy, &CSSMOID_APPLE_TP_SSL) || compareOids(policy, &CSSMOID_APPLE_TP_APPLEID_SHARING)) { if(sslHost != NULL) { memset(&sslOpts, 0, sizeof(sslOpts)); sslOpts.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION; sslOpts.ServerName = sslHost; sslOpts.ServerNameLen = strlen(sslHost); optionData.Data = (uint8 *)&sslOpts; optionData.Length = sizeof(sslOpts); ortn = SecPolicySetValue(policyRef, &optionData); if(ortn) { cssmPerror("SecPolicySetValue", ortn); ourRtn = 1; goto errOut; } } } if(compareOids(policy, &CSSMOID_APPLE_TP_SMIME)) { if(emailAddrs != NULL) { memset(&smimeOpts, 0, sizeof(smimeOpts)); smimeOpts.Version = CSSM_APPLE_TP_SMIME_OPTS_VERSION; smimeOpts.SenderEmail = emailAddrs; smimeOpts.SenderEmailLen = strlen(emailAddrs); optionData.Data = (uint8 *)&smimeOpts; optionData.Length = sizeof(smimeOpts); ortn = SecPolicySetValue(policyRef, &optionData); if(ortn) { cssmPerror("SecPolicySetValue", ortn); ourRtn = 1; goto errOut; } } } /* Now create a SecTrustRef and set its options */ ortn = SecTrustCreateWithCertificates(certs, policyRef, &trustRef); if(ortn) { cssmPerror("SecTrustCreateWithCertificates", ortn); ourRtn = 1; goto errOut; } /* roots (anchors) are optional */ if(roots != NULL) { ortn = SecTrustSetAnchorCertificates(trustRef, roots); if(ortn) { cssmPerror("SecTrustSetAnchorCertificates", ortn); ourRtn = 1; goto errOut; } } if(actionFlags || forceActionFlags) { memset(&actionData, 0, sizeof(actionData)); actionData.Version = CSSM_APPLE_TP_ACTION_VERSION; actionData.ActionFlags = actionFlags; cfActionData = CFDataCreate(NULL, (UInt8 *)&actionData, sizeof(actionData)); ortn = SecTrustSetParameters(trustRef, CSSM_TP_ACTION_DEFAULT, cfActionData); if(ortn) { cssmPerror("SecTrustSetParameters", ortn); ourRtn = 1; goto errOut; } } if(keychains) { ortn = SecTrustSetKeychains(trustRef, keychains); if(ortn) { cssmPerror("SecTrustSetKeychains", ortn); ourRtn = 1; goto errOut; } } /* GO */ ortn = SecTrustEvaluate(trustRef, &resultType); if(ortn) { /* should never fail - error on this doesn't mean the cert verified badly */ cssmPerror("SecTrustEvaluate", ortn); ourRtn = 1; goto errOut; } switch(resultType) { case kSecTrustResultUnspecified: /* cert chain valid, no special UserTrust assignments */ case kSecTrustResultProceed: /* cert chain valid AND user explicitly trusts this */ break; case kSecTrustResultDeny: if(!quiet) { fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultDeny\n"); } ourRtn = 1; break; case kSecTrustResultConfirm: /* * Cert chain may well have verified OK, but user has flagged * one of these certs as untrustable. */ if(!quiet) { fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultConfirm\n"); } ourRtn = 1; break; default: ourRtn = 1; if(!quiet) { /* See what the TP had to say about this */ ortn = SecTrustGetCssmResultCode(trustRef, &ocrtn); if(ortn) { cssmPerror("SecTrustGetCssmResultCode", ortn); } else { cssmPerror("Cert Verify Result", ocrtn); } } break; } if((ourRtn == 0) & !quiet) { printf("...certificate verification successful.\n"); } errOut: /* cleanup */ CFRELEASE(certs); CFRELEASE(roots); CFRELEASE(keychains); CFRELEASE(policyRef); CFRELEASE(trustRef); CFRELEASE(searchRef); CFRELEASE(cfActionData); return ourRtn; }