#define CFRELEASE(cf) if (cf) { CFRelease(cf); }
#include <Security/SecCertificate.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecTrust.h>
#include <Security/SecPolicy.h>
#include <Security/SecPolicyPriv.h>
#include <utilities/fileIo.h>
#include <sys/stat.h>
#include <stdio.h>
#include <time.h>
#include "SecurityCommands.h"
CFStringRef policyToConstant(const char *policy);
int verify_cert(int argc, char * const *argv);
static int addCertFile(const char *fileName, CFMutableArrayRef *array) {
SecCertificateRef certRef = NULL;
CFDataRef dataRef = NULL;
unsigned char *buf = NULL;
size_t numBytes;
int rtn = 0;
if (readFileSizet(fileName, &buf, &numBytes)) {
rtn = -1;
goto errOut;
}
dataRef = CFDataCreate(NULL, buf, numBytes);
certRef = SecCertificateCreateWithData(NULL, dataRef);
if (!certRef) {
certRef = SecCertificateCreateWithPEM(NULL, dataRef);
if (!certRef) {
rtn = -1;
goto errOut;
}
}
if (*array == NULL) {
*array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(*array, certRef);
errOut:
free(buf);
CFRELEASE(dataRef);
CFRELEASE(certRef);
return rtn;
}
CFStringRef policyToConstant(const char *policy) {
if (policy == NULL) {
return NULL;
}
else if (!strcmp(policy, "basic")) {
return kSecPolicyAppleX509Basic;
}
else if (!strcmp(policy, "ssl")) {
return kSecPolicyAppleSSL;
}
else if (!strcmp(policy, "smime")) {
return kSecPolicyAppleSMIME;
}
else if (!strcmp(policy, "eap")) {
return kSecPolicyAppleEAP;
}
else if (!strcmp(policy, "IPSec")) {
return kSecPolicyAppleIPsec;
}
else if (!strcmp(policy, "appleID")) {
return kSecPolicyAppleIDValidation;
}
else if (!strcmp(policy, "codeSign")) {
return kSecPolicyAppleCodeSigning;
}
else if (!strcmp(policy, "timestamping")) {
return kSecPolicyAppleTimeStamping;
}
else if (!strcmp(policy, "revocation")) {
return kSecPolicyAppleRevocation;
}
else if (!strcmp(policy, "passbook")) {
return NULL;
}
else {
return NULL;
}
}
static CFOptionFlags revCheckOptionStringToFlags(
const char *revCheckOption)
{
CFOptionFlags result = 0;
if(revCheckOption == NULL) {
return result;
}
else if(!strcmp(revCheckOption, "ocsp")) {
result |= kSecRevocationOCSPMethod;
}
else if(!strcmp(revCheckOption, "crl")) {
result |= kSecRevocationCRLMethod;
}
else if(!strcmp(revCheckOption, "require")) {
result |= kSecRevocationRequirePositiveResponse;
}
else if(!strcmp(revCheckOption, "offline")) {
result |= kSecRevocationNetworkAccessDisabled;
}
else if(!strcmp(revCheckOption, "online")) {
result |= kSecRevocationOnlineCheck;
}
return result;
}
int verify_cert(int argc, char * const *argv) {
extern char *optarg;
extern int optind;
int arg;
CFMutableArrayRef certs = NULL;
CFMutableArrayRef roots = NULL;
CFMutableArrayRef policies = NULL;
CFMutableDictionaryRef dict = NULL;
CFStringRef name = NULL;
CFBooleanRef client = kCFBooleanFalse;
CFOptionFlags revOptions = 0;
OSStatus ortn;
int ourRtn = 0;
bool quiet = false;
struct tm time;
CFGregorianDate gregorianDate;
CFDateRef dateRef = NULL;
CFStringRef policy = NULL;
SecPolicyRef policyRef = NULL;
SecPolicyRef revPolicyRef = NULL;
Boolean fetch = true;
SecTrustRef trustRef = NULL;
SecTrustResultType resultType;
if (argc < 2) {
return SHOW_USAGE_MESSAGE;
}
optind = 1;
while ((arg = getopt(argc, argv, "Cc:r:p:d:n:LqR:")) != -1) {
switch (arg) {
case 'c':
if (addCertFile(optarg, &certs)) {
fprintf(stderr, "Cert file error\n");
ourRtn = 1;
goto errOut;
}
break;
case 'r':
if (addCertFile(optarg, &roots)) {
fprintf(stderr, "Root file error\n");
ourRtn = 1;
goto errOut;
}
break;
case 'p':
policy = policyToConstant(optarg);
if (policy == NULL) {
fprintf(stderr, "Policy processing error\n");
ourRtn = 2;
goto errOut;
}
break;
case 'L':
fetch = false;
break;
case 'n':
if (name == NULL) {
name = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
}
break;
case 'q':
quiet = true;
break;
case 'C':
client = kCFBooleanTrue;
break;
case 'd':
memset(&time, 0, sizeof(struct tm));
if (strptime(optarg, "%Y-%m-%d-%H:%M:%S", &time) == NULL) {
if (strptime(optarg, "%Y-%m-%d", &time) == NULL) {
fprintf(stderr, "Date processing error\n");
ourRtn = 2;
goto errOut;
}
}
gregorianDate.second = time.tm_sec;
gregorianDate.minute = time.tm_min;
gregorianDate.hour = time.tm_hour;
gregorianDate.day = time.tm_mday;
gregorianDate.month = time.tm_mon + 1;
gregorianDate.year = time.tm_year + 1900;
if (dateRef == NULL) {
dateRef = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gregorianDate, NULL));
}
break;
case 'R':
revOptions |= revCheckOptionStringToFlags(optarg);
break;
default:
fprintf(stderr, "Usage error\n");
ourRtn = 2;
goto errOut;
}
}
if (optind != argc) {
ourRtn = 2;
goto errOut;
}
if (policy == NULL) {
policy = kSecPolicyAppleX509Basic;
}
if (certs == NULL) {
if (roots == NULL) {
fprintf(stderr, "No certificates specified.\n");
ourRtn = 2;
goto errOut;
}
if (CFArrayGetCount(roots) != 1) {
fprintf(stderr, "Multiple roots and no certificates not allowed.\n");
ourRtn = 2;
goto errOut;
}
certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(certs, CFArrayGetValueAtIndex(roots, 0));
}
if (!CFStringCompare(policy, kSecPolicyAppleSSL, 0) || !CFStringCompare(policy, kSecPolicyAppleIPsec, 0)) {
dict = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (name == NULL) {
fprintf(stderr, "Name not specified for IPsec or SSL policy. '-n' is a required option for these policies.");
ourRtn = 2;
goto errOut;
}
CFDictionaryAddValue(dict, kSecPolicyName, name);
CFDictionaryAddValue(dict, kSecPolicyClient, client);
}
else if (!CFStringCompare(policy, kSecPolicyAppleEAP, 0)) {
dict = CFDictionaryCreateMutable(NULL, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(dict, kSecPolicyClient, client);
}
else if (!CFStringCompare(policy, kSecPolicyAppleSMIME, 0)) {
dict = CFDictionaryCreateMutable(NULL, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (name == NULL) {
fprintf(stderr, "Name not specified for SMIME policy. '-n' is a required option for this policy.");
ourRtn = 2;
goto errOut;
}
CFDictionaryAddValue(dict, kSecPolicyName, name);
}
policyRef = SecPolicyCreateWithProperties(policy, dict);
policies = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(policies, policyRef);
if(revOptions != 0) {
revPolicyRef = SecPolicyCreateRevocation(revOptions);
CFArrayAppendValue(policies, revPolicyRef);
}
ortn = SecTrustCreateWithCertificates(certs, policies, &trustRef);
if (ortn) {
fprintf(stderr, "SecTrustCreateWithCertificates\n");
ourRtn = 1;
goto errOut;
}
if (roots != NULL) {
ortn = SecTrustSetAnchorCertificates(trustRef, roots);
if (ortn) {
fprintf(stderr, "SecTrustSetAnchorCertificates\n");
ourRtn = 1;
goto errOut;
}
}
if (fetch == false) {
ortn = SecTrustSetNetworkFetchAllowed(trustRef, fetch);
if (ortn) {
fprintf(stderr, "SecTrustSetNetworkFetchAllowed\n");
ourRtn = 1;
goto errOut;
}
}
if (dateRef != NULL) {
ortn = SecTrustSetVerifyDate(trustRef, dateRef);
if (ortn) {
fprintf(stderr, "SecTrustSetVerifyDate\n");
ourRtn = 1;
goto errOut;
}
}
ortn = SecTrustEvaluate(trustRef, &resultType);
if (ortn) {
fprintf(stderr, "SecTrustEvaluate\n");
ourRtn = 1;
goto errOut;
}
switch (resultType) {
case kSecTrustResultUnspecified:
case kSecTrustResultProceed:
break;
case kSecTrustResultDeny:
if (!quiet) {
fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultDeny\n");
}
ourRtn = 1;
break;
case kSecTrustResultInvalid:
if (!quiet) {
fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultInvalid\n");
}
ourRtn = 1;
break;
case kSecTrustResultRecoverableTrustFailure:
if (!quiet) {
fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultRecoverableTrustFailure\n");
}
ourRtn = 1;
break;
case kSecTrustResultFatalTrustFailure:
if (!quiet) {
fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultFatalTrustFailure\n");
}
ourRtn = 1;
break;
case kSecTrustResultOtherError:
if (!quiet) {
fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultOtherError\n");
}
ourRtn = 1;
break;
default:
if (!quiet) {
fprintf(stderr, "Cert Verify Result: %u\n", resultType);
}
ourRtn = 1;
break;
}
if ((ourRtn == 0) && !quiet) {
printf("...certificate verification successful.\n");
}
errOut:
CFRELEASE(certs);
CFRELEASE(roots);
CFRELEASE(dateRef);
CFRELEASE(dict);
CFRELEASE(policies);
CFRELEASE(revPolicyRef);
CFRELEASE(policyRef);
CFRELEASE(trustRef);
CFRELEASE(name);
return ourRtn;
}