1/* 2 * Verify one CAC cert using standard system-wide cert and CRL keychains. 3 */ 4#include <stdlib.h> 5#include <stdio.h> 6#include <security_cdsa_utils/cuFileIo.h> 7#include <Security/cuCdsaUtils.h> /* private */ 8//#include "clutils.h" 9#include <Security/cssmerrno.h> /* private cssmErrorString() */ 10#include <Security/cssm.h> 11#include <Security/oidsalg.h> 12#include <Security/SecTrust.h> 13 14/* 15 * More-or-less hard-coded locations of system-wide keychains containing 16 * intermediate certs (which are required for this operation) and 17 * CRLs (which is optional; it's just a cache for performance reasons). 18 */ 19#define X509_CERT_DB "/System/Library/Keychains/X509Certificates" 20#define X509_CRL_DB "/private/var/db/crls/crlcache.db" 21 22static void usage(char **argv) 23{ 24 printf("Usage: %s certFileName [options]\n", argv[0]); 25 printf("Options:\n"); 26 printf(" a allow unverified certs\n"); 27 printf(" d disable CRL verification\n"); 28 printf(" n no network fetch of CRLs\n"); 29 exit(1); 30} 31 32/*** Display verify results ***/ 33 34static void statusBitTest( 35 CSSM_TP_APPLE_CERT_STATUS certStatus, 36 uint32 bit, 37 const char *str) 38{ 39 if(certStatus & bit) { 40 printf("%s ", str); 41 } 42} 43 44static void printCertInfo( 45 unsigned numCerts, // from CertGroup 46 const CSSM_TP_APPLE_EVIDENCE_INFO *info) 47{ 48 CSSM_TP_APPLE_CERT_STATUS cs; 49 50 for(unsigned i=0; i<numCerts; i++) { 51 const CSSM_TP_APPLE_EVIDENCE_INFO *thisInfo = &info[i]; 52 cs = thisInfo->StatusBits; 53 printf(" cert %u:\n", i); 54 printf(" StatusBits : 0x%x", (unsigned)cs); 55 if(cs) { 56 printf(" ( "); 57 statusBitTest(cs, CSSM_CERT_STATUS_EXPIRED, "EXPIRED"); 58 statusBitTest(cs, CSSM_CERT_STATUS_NOT_VALID_YET, 59 "NOT_VALID_YET"); 60 statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_INPUT_CERTS, 61 "IS_IN_INPUT_CERTS"); 62 statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_ANCHORS, 63 "IS_IN_ANCHORS"); 64 statusBitTest(cs, CSSM_CERT_STATUS_IS_ROOT, "IS_ROOT"); 65 statusBitTest(cs, CSSM_CERT_STATUS_IS_FROM_NET, "IS_FROM_NET"); 66 printf(")\n"); 67 } 68 else { 69 printf("\n"); 70 } 71 printf(" NumStatusCodes : %u ", 72 thisInfo->NumStatusCodes); 73 for(unsigned j=0; j<thisInfo->NumStatusCodes; j++) { 74 printf("%s ", 75 cssmErrorString(thisInfo->StatusCodes[j]).c_str()); 76 } 77 printf("\n"); 78 printf(" Index: %u\n", thisInfo->Index); 79 } 80 return; 81} 82 83/* we really only need CSSM_EVIDENCE_FORM_APPLE_CERT_INFO */ 84#define SHOW_ALL_VFY_RESULTS 0 85 86static void dumpVfyResult( 87 const CSSM_TP_VERIFY_CONTEXT_RESULT *vfyResult) 88{ 89 unsigned numEvidences = vfyResult->NumberOfEvidences; 90 unsigned numCerts = 0; 91 printf("Returned evidence:\n"); 92 for(unsigned dex=0; dex<numEvidences; dex++) { 93 CSSM_EVIDENCE_PTR ev = &vfyResult->Evidence[dex]; 94 #if SHOW_ALL_VFY_RESULTS 95 printf(" Evidence %u:\n", dex); 96 #endif 97 switch(ev->EvidenceForm) { 98 case CSSM_EVIDENCE_FORM_APPLE_HEADER: 99 { 100 #if SHOW_ALL_VFY_RESULTS 101 const CSSM_TP_APPLE_EVIDENCE_HEADER *hdr = 102 (const CSSM_TP_APPLE_EVIDENCE_HEADER *)(ev->Evidence); 103 printf(" Form = HEADER; Version = %u\n", hdr->Version); 104 #endif 105 break; 106 } 107 case CSSM_EVIDENCE_FORM_APPLE_CERTGROUP: 108 { 109 const CSSM_CERTGROUP *grp = (const CSSM_CERTGROUP *)ev->Evidence; 110 numCerts = grp->NumCerts; 111 #if SHOW_ALL_VFY_RESULTS 112 /* parse the rest of this eventually */ 113 /* Note we depend on this coming before the CERT_INFO */ 114 printf(" Form = CERTGROUP; numCerts = %u\n", numCerts); 115 #endif 116 break; 117 } 118 case CSSM_EVIDENCE_FORM_APPLE_CERT_INFO: 119 { 120 const CSSM_TP_APPLE_EVIDENCE_INFO *info = 121 (const CSSM_TP_APPLE_EVIDENCE_INFO *)ev->Evidence; 122 printCertInfo(numCerts, info); 123 break; 124 } 125 default: 126 printf("***UNKNOWN Evidence form (%u)\n", 127 (unsigned)ev->EvidenceForm); 128 break; 129 } 130 } 131} 132 133/* free a CSSM_CERT_GROUP */ 134void tpFreeCertGroup( 135 CSSM_CERTGROUP_PTR certGroup, 136 CSSM_BOOL freeCertData, // free individual CertList.Data 137 CSSM_BOOL freeStruct) // free the overall CSSM_CERTGROUP 138{ 139 unsigned dex; 140 141 if(certGroup == NULL) { 142 return; 143 } 144 145 if(freeCertData) { 146 /* free the individual cert Data fields */ 147 for(dex=0; dex<certGroup->NumCerts; dex++) { 148 APP_FREE(certGroup->GroupList.CertList[dex].Data); 149 } 150 } 151 152 /* and the array of CSSM_DATAs */ 153 if(certGroup->GroupList.CertList) { 154 APP_FREE(certGroup->GroupList.CertList); 155 } 156 157 if(freeStruct) { 158 APP_FREE(certGroup); 159 } 160} 161 162 163/* 164 * Free the contents of a CSSM_TP_VERIFY_CONTEXT_RESULT returned from 165 * CSSM_TP_CertGroupVerify(). 166 */ 167CSSM_RETURN freeVfyResult( 168 CSSM_TP_VERIFY_CONTEXT_RESULT *ctx) 169{ 170 int numCerts = -1; 171 CSSM_RETURN crtn = CSSM_OK; 172 173 for(unsigned i=0; i<ctx->NumberOfEvidences; i++) { 174 CSSM_EVIDENCE_PTR evp = &ctx->Evidence[i]; 175 switch(evp->EvidenceForm) { 176 case CSSM_EVIDENCE_FORM_APPLE_HEADER: 177 /* Evidence = (CSSM_TP_APPLE_EVIDENCE_HEADER *) */ 178 APP_FREE(evp->Evidence); 179 evp->Evidence = NULL; 180 break; 181 case CSSM_EVIDENCE_FORM_APPLE_CERTGROUP: 182 { 183 /* Evidence = CSSM_CERTGROUP_PTR */ 184 CSSM_CERTGROUP_PTR cgp = (CSSM_CERTGROUP_PTR)evp->Evidence; 185 numCerts = cgp->NumCerts; 186 tpFreeCertGroup(cgp, CSSM_TRUE, CSSM_TRUE); 187 evp->Evidence = NULL; 188 break; 189 } 190 case CSSM_EVIDENCE_FORM_APPLE_CERT_INFO: 191 { 192 /* Evidence = array of CSSM_TP_APPLE_EVIDENCE_INFO */ 193 if(numCerts < 0) { 194 /* Haven't gotten a CSSM_CERTGROUP_PTR! */ 195 printf("***Malformed VerifyContextResult (2)\n"); 196 crtn = CSSMERR_TP_INTERNAL_ERROR; 197 break; 198 } 199 CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = 200 (CSSM_TP_APPLE_EVIDENCE_INFO *)evp->Evidence; 201 for(unsigned k=0; k<(unsigned)numCerts; k++) { 202 /* Dispose of StatusCodes, UniqueRecord */ 203 CSSM_TP_APPLE_EVIDENCE_INFO *thisEvInfo = 204 &evInfo[k]; 205 if(thisEvInfo->StatusCodes) { 206 APP_FREE(thisEvInfo->StatusCodes); 207 } 208 if(thisEvInfo->UniqueRecord) { 209 CSSM_RETURN crtn = 210 CSSM_DL_FreeUniqueRecord(thisEvInfo->DlDbHandle, 211 thisEvInfo->UniqueRecord); 212 if(crtn) { 213 cuPrintError("CSSM_DL_FreeUniqueRecord", crtn); 214 break; 215 } 216 thisEvInfo->UniqueRecord = NULL; 217 } 218 } /* for each cert info */ 219 APP_FREE(evp->Evidence); 220 evp->Evidence = NULL; 221 break; 222 } /* CSSM_EVIDENCE_FORM_APPLE_CERT_INFO */ 223 } /* switch(evp->EvidenceForm) */ 224 } /* for each evidence */ 225 if(ctx->Evidence) { 226 APP_FREE(ctx->Evidence); 227 ctx->Evidence = NULL; 228 } 229 return crtn; 230} 231 232static int testError(CSSM_BOOL quiet) 233{ 234 char resp; 235 236 if(quiet) { 237 printf("\n***Test aborting.\n"); 238 exit(1); 239 } 240 fpurge(stdin); 241 printf("a to abort, c to continue: "); 242 resp = getchar(); 243 return (resp == 'a'); 244} 245 246/* 247 * The heart of CAC certification verification. 248 */ 249int vfyCert( 250 CSSM_TP_HANDLE tpHand, 251 CSSM_CL_HANDLE clHand, 252 CSSM_CSP_HANDLE cspHand, 253 const void * certData, 254 unsigned certLength, 255 256 /* these three booleans will eventually come from system preferences */ 257 bool enableCrlCheck, 258 bool requireFullCrlVerify, 259 bool enableFetchFromNet, 260 CSSM_DL_DB_HANDLE_PTR certKeychain, 261 CSSM_DL_DB_HANDLE_PTR crlKeychain) 262{ 263 /* main job is building a CSSM_TP_VERIFY_CONTEXT and its components */ 264 CSSM_TP_VERIFY_CONTEXT vfyCtx; 265 CSSM_TP_CALLERAUTH_CONTEXT authCtx; 266 CSSM_TP_VERIFY_CONTEXT_RESULT vfyResult; 267 268 memset(&vfyCtx, 0, sizeof(CSSM_TP_VERIFY_CONTEXT)); 269 memset(&authCtx, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT)); 270 271 /* CSSM_TP_CALLERAUTH_CONTEXT components */ 272 /* 273 typedef struct cssm_tp_callerauth_context { 274 CSSM_TP_POLICYINFO Policy; 275 CSSM_TIMESTRING VerifyTime; 276 CSSM_TP_STOP_ON VerificationAbortOn; 277 CSSM_TP_VERIFICATION_RESULTS_CALLBACK CallbackWithVerifiedCert; 278 uint32 NumberOfAnchorCerts; 279 CSSM_DATA_PTR AnchorCerts; 280 CSSM_DL_DB_LIST_PTR DBList; 281 CSSM_ACCESS_CREDENTIALS_PTR CallerCredentials; 282 } CSSM_TP_CALLERAUTH_CONTEXT, *CSSM_TP_CALLERAUTH_CONTEXT_PTR; 283 */ 284 /* two policies */ 285 CSSM_FIELD policyIds[2]; 286 CSSM_APPLE_TP_CRL_OPTIONS crlOpts; 287 policyIds[0].FieldOid = CSSMOID_APPLE_X509_BASIC; 288 policyIds[0].FieldValue.Data = NULL; 289 policyIds[0].FieldValue.Length = 0; 290 if(enableCrlCheck) { 291 policyIds[1].FieldOid = CSSMOID_APPLE_TP_REVOCATION_CRL; 292 policyIds[1].FieldValue.Data = (uint8 *)&crlOpts; 293 policyIds[1].FieldValue.Length = sizeof(crlOpts); 294 crlOpts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION; 295 crlOpts.CrlFlags = 0; 296 if(requireFullCrlVerify) { 297 crlOpts.CrlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT; 298 } 299 if(enableFetchFromNet) { 300 crlOpts.CrlFlags |= CSSM_TP_ACTION_FETCH_CRL_FROM_NET; 301 } 302 /* optional, may well not exist */ 303 /* FIXME: as of 12/4/02 this field is ignored by the TP 304 * and may well go away from the CSSM_APPLE_TP_CRL_OPTIONS 305 * struct. */ 306 crlOpts.crlStore = crlKeychain; 307 308 authCtx.Policy.NumberOfPolicyIds = 2; 309 } 310 else { 311 /* No CRL checking */ 312 authCtx.Policy.NumberOfPolicyIds = 1; 313 } 314 authCtx.Policy.PolicyIds = policyIds; 315 authCtx.Policy.PolicyControl = NULL; 316 317 authCtx.VerifyTime = NULL; 318 authCtx.VerificationAbortOn = CSSM_TP_STOP_ON_POLICY; 319 authCtx.CallbackWithVerifiedCert = NULL; 320 321 /* anchors */ 322 const CSSM_DATA *anchors; 323 uint32 anchorCount; 324 OSStatus ortn; 325 ortn = SecTrustGetCSSMAnchorCertificates(&anchors, &anchorCount); 326 if(ortn) { 327 printf("SecTrustGetCSSMAnchorCertificates returned %u\n", ortn); 328 return -1; 329 } 330 authCtx.NumberOfAnchorCerts = anchorCount; 331 authCtx.AnchorCerts = const_cast<CSSM_DATA_PTR>(anchors); 332 333 /* DBList of intermediate certs and CRLs */ 334 CSSM_DL_DB_HANDLE handles[2]; 335 unsigned numDbs = 0; 336 if(certKeychain != NULL) { 337 handles[0] = *certKeychain; 338 numDbs++; 339 } 340 if(crlKeychain != NULL) { 341 handles[numDbs] = *crlKeychain; 342 numDbs++; 343 } 344 CSSM_DL_DB_LIST dlDbList; 345 dlDbList.NumHandles = numDbs; 346 dlDbList.DLDBHandle = &handles[0]; 347 348 authCtx.DBList = &dlDbList; 349 authCtx.CallerCredentials = NULL; 350 351 /* CSSM_TP_VERIFY_CONTEXT */ 352 vfyCtx.ActionData.Data = NULL; 353 vfyCtx.ActionData.Length = 0; 354 vfyCtx.Action = CSSM_TP_ACTION_DEFAULT; 355 vfyCtx.Cred = &authCtx; 356 357 /* cook up cert group */ 358 CSSM_CERTGROUP cssmCerts; 359 cssmCerts.CertType = CSSM_CERT_X_509v3; 360 cssmCerts.CertEncoding = CSSM_CERT_ENCODING_DER; 361 cssmCerts.NumCerts = 1; 362 CSSM_DATA cert; 363 cert.Data = (uint8 *)certData; 364 cert.Length = certLength; 365 cssmCerts.GroupList.CertList = &cert; 366 cssmCerts.CertGroupType = CSSM_CERTGROUP_DATA; 367 368 CSSM_RETURN crtn = CSSM_TP_CertGroupVerify(tpHand, 369 clHand, 370 cspHand, 371 &cssmCerts, 372 &vfyCtx, 373 &vfyResult); 374 if(crtn) { 375 cuPrintError("CSSM_TP_CertGroupVerify", crtn); 376 } 377 else { 378 printf("...verify successful\n"); 379 } 380 dumpVfyResult(&vfyResult); 381 freeVfyResult(&vfyResult); 382 if(crtn) { 383 return testError(0); 384 } 385 else { 386 return 0; 387 } 388} 389 390int main(int argc, char **argv) 391{ 392 int rtn; 393 CSSM_RETURN crtn; 394 CSSM_DL_HANDLE dlHand; 395 CSSM_CSP_HANDLE cspHand; 396 CSSM_CL_HANDLE clHand; 397 CSSM_TP_HANDLE tpHand; 398 CSSM_DL_DB_HANDLE certKeychain; 399 CSSM_DL_DB_HANDLE crlKeychain; 400 CSSM_DL_DB_HANDLE_PTR certKeychainPtr = NULL; 401 CSSM_DL_DB_HANDLE_PTR crlKeychainPtr = NULL; 402 unsigned char *certData; 403 unsigned certLength; 404 bool requireFullCrlVerify = true; 405 bool enableFetchFromNet = true; 406 bool enableCrlCheck = true; 407 int arg; 408 char *argp; 409 410 if(argc < 2) { 411 usage(argv); 412 } 413 for(arg=2; arg<argc; arg++) { 414 argp = argv[arg]; 415 switch(argp[0]) { 416 case 'a': 417 requireFullCrlVerify = false; 418 break; 419 case 'd': 420 enableCrlCheck = false; 421 break; 422 case 'n': 423 enableFetchFromNet = false; 424 break; 425 default: 426 usage(argv); 427 } 428 } 429 430 /* in the real world all these should be verified to be nonzero */ 431 cspHand = cuCspStartup(CSSM_TRUE); 432 clHand = cuClStartup(); 433 tpHand = cuTpStartup(); 434 dlHand = cuDlStartup(); 435 436 /* get the cert */ 437 rtn = readFile(argv[1], &certData, &certLength); 438 if(rtn) { 439 printf("***Error reading cert file %s\n", argv[1]); 440 exit(1); 441 } 442 443 /* get the intermediate certs */ 444 crtn = CSSM_DL_DbOpen(dlHand, 445 X509_CERT_DB, 446 NULL, // DbLocation 447 CSSM_DB_ACCESS_READ, 448 NULL, // CSSM_ACCESS_CREDENTIALS *AccessCred 449 NULL, // void *OpenParameters 450 &certKeychain.DBHandle); 451 if(crtn) { 452 cuPrintError("CSSM_DL_DbOpen", crtn); 453 printf("***Error opening intermediate cert file %s. This op will" 454 "probably fail.n", X509_CERT_DB); 455 } 456 else { 457 certKeychain.DLHandle = dlHand; 458 certKeychainPtr = &certKeychain; 459 } 460 461 /* and the CRL cache */ 462 crtn = CSSM_DL_DbOpen(dlHand, 463 X509_CRL_DB, 464 NULL, // DbLocation 465 CSSM_DB_ACCESS_READ, 466 NULL, // CSSM_ACCESS_CREDENTIALS *AccessCred 467 NULL, // void *OpenParameters 468 &crlKeychain.DBHandle); 469 if(crtn == CSSM_OK) { 470 crlKeychain.DLHandle = dlHand; 471 crlKeychainPtr = &crlKeychain; 472 } 473 474 /* go for it */ 475 vfyCert(tpHand, clHand, cspHand, certData, certLength, 476 enableCrlCheck, requireFullCrlVerify, enableFetchFromNet, 477 certKeychainPtr, crlKeychainPtr); 478 479 /* Cleanup - release handles, free certData, etc. */ 480 free(certData); 481 if(crlKeychainPtr != NULL) { 482 CSSM_DL_DbClose(crlKeychain); 483 } 484 if(certKeychainPtr != NULL) { 485 CSSM_DL_DbClose(certKeychain); 486 } 487 CSSM_ModuleDetach(dlHand); 488 CSSM_ModuleDetach(clHand); 489 CSSM_ModuleDetach(tpHand); 490 CSSM_ModuleDetach(cspHand); 491 492 return 0; 493 494} 495