1/* Copyright (c) 2002-2004,2006,2008 Apple Inc. 2 * 3 * sslSubjName.c 4 * 5 * Verify comparision of app-specified host name vs. various 6 * forms of hostname in a cert. 7 * 8 */ 9 10#include <utilLib/common.h> 11#include <utilLib/cspwrap.h> 12#include <clAppUtils/clutils.h> 13#include <clAppUtils/certVerify.h> 14#include <clAppUtils/BlobList.h> 15#include <stdlib.h> 16#include <stdio.h> 17#include <string.h> 18#include <Security/cssm.h> 19#include <Security/x509defs.h> 20#include <Security/oidsattr.h> 21#include <Security/oidscert.h> 22#include <Security/oidsalg.h> 23#include <Security/certextensions.h> 24#include <Security/cssmapple.h> 25#include <string.h> 26#include <security_cdsa_utils/cuFileIo.h> 27 28/* key labels */ 29#define SUBJ_KEY_LABEL "subjectKey" 30#define ROOT_KEY_LABEL "rootKey" 31 32/* key and signature algorithm - shouldn't matter for this test */ 33#define SIG_ALG_DEFAULT CSSM_ALGID_SHA1WithRSA 34#define SIG_OID_DEFAULT CSSMOID_SHA1WithRSA 35#define KEY_ALG_DEFAULT CSSM_ALGID_RSA 36 37#define KEY_SIZE_DEFAULT 512 38 39#define CERT_FILE "sslCert.cer" 40 41static void usage(char **argv) 42{ 43 printf("Usage: %s [options]\n", argv[0]); 44 printf("Options:\n"); 45 printf(" w(write certs)\n"); 46 printf(" q(uiet)\n"); 47 printf(" v(erbose)\n"); 48 exit(1); 49} 50 51/* 52 * RDN components for root, subject 53 */ 54CSSM_APPLE_TP_NAME_OID rootRdn[] = 55{ 56 { "Apple Computer", &CSSMOID_OrganizationName }, 57 { "The Big Cheese", &CSSMOID_Title } 58}; 59#define NUM_ROOT_NAMES (sizeof(rootRdn) / sizeof(CSSM_APPLE_TP_NAME_OID)) 60 61#define SUBJ_COMMON_NAME "something.org" 62 63CSSM_APPLE_TP_NAME_OID subjRdn[] = 64{ 65 { "Apple Computer", &CSSMOID_OrganizationName }, 66 /* overridden when creating the cert */ 67 { NULL, &CSSMOID_CommonName } 68}; 69#define SUBJ_COMMON_NAME_DEX 1 70 71#define NUM_SUBJ_NAMES (sizeof(subjRdn) / sizeof(CSSM_APPLE_TP_NAME_OID)) 72 73 74/* 75 * Test cases 76 */ 77typedef struct { 78 /* test description */ 79 const char *testDesc; 80 81 /* host names for leaf cert - zero or one of these */ 82 const char *certDnsName; 83 const char *certIpAddr; 84 85 /* subject common name */ 86 const char *commonName; 87 88 /* host name for CertGroupVerify */ 89 const char *vfyHostName; 90 91 /* expected error - NULL or e.g. "CSSMERR_APPLETP_CRL_NOT_TRUSTED" */ 92 const char *expectErrStr; 93 94 /* one optional per-cert error string */ 95 const char *certErrorStr; 96 97} SSN_TestCase; 98 99SSN_TestCase testCases[] = 100{ 101 { 102 "DNS Name foo.bar, vfyName foo.bar", 103 "foo.bar", NULL, SUBJ_COMMON_NAME, "foo.bar", 104 NULL, 105 NULL 106 }, 107 { 108 "DNS Name foo.bar, vfyName something.org, expect fail due to " 109 "DNS present", 110 "foo.bar", NULL, SUBJ_COMMON_NAME, "something.org", 111 "CSSMERR_APPLETP_HOSTNAME_MISMATCH", 112 "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH" 113 }, 114 { 115 "DNS Name foo.bar, vfyName foo.foo.bar, expect fail", 116 "foo.bar", NULL, SUBJ_COMMON_NAME, "foo.foo.bar", 117 "CSSMERR_APPLETP_HOSTNAME_MISMATCH", 118 "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH" 119 }, 120 { 121 "IP Name 1.0.5.8, vfyName 1.0.5.8", 122 NULL, "1.0.5.8", SUBJ_COMMON_NAME, "1.0.5.8", 123 NULL, 124 NULL 125 }, 126 { 127 "IP Name 1.0.5.8, vfyName 1.00.5.008", 128 NULL, "1.0.5.8", SUBJ_COMMON_NAME, "1.00.5.008", 129 NULL, 130 NULL 131 }, 132 { 133 "IP Name 1.0.5.8, vfyName something.org", 134 NULL, "1.0.5.8", SUBJ_COMMON_NAME, "something.org", 135 NULL, 136 NULL 137 }, 138 { 139 "IP Name 1.0.5.8, vfyName 2.0.5.8, expect fail", 140 NULL, "1.0.5.8", SUBJ_COMMON_NAME, "2.0.5.8", 141 "CSSMERR_APPLETP_HOSTNAME_MISMATCH", 142 "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH" 143 }, 144 { 145 "DNS Name *.foo.bar, vfyName bar.foo.bar", 146 "*.foo.bar", NULL, SUBJ_COMMON_NAME, "bar.foo.bar", 147 NULL, 148 NULL 149 }, 150 { 151 "DNS Name *.foo.bar, vfyName foo.bar, expect fail", 152 "*.foo.bar", NULL, SUBJ_COMMON_NAME, "foo.bar", 153 "CSSMERR_APPLETP_HOSTNAME_MISMATCH", 154 "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH" 155 }, 156 { 157 "DNS Name *foo.bar, vfyName barfoo.bar", 158 "*foo.bar", NULL, SUBJ_COMMON_NAME, "barfoo.bar", 159 NULL, 160 NULL 161 }, 162 { 163 "DNS Name *foo*.bar, vfyName barfoo.bar", 164 "*foo*.bar", NULL, SUBJ_COMMON_NAME, "barfoo.bar", 165 NULL, 166 NULL 167 }, 168 { 169 "DNS Name *foo*.bar, vfyName foobar.bar", 170 "*foo*.bar", NULL, SUBJ_COMMON_NAME, "foobar.bar", 171 NULL, 172 NULL 173 }, 174 { 175 "DNS Name *foo*.bar, vfyName foo.bar", 176 "*foo*.bar", NULL, SUBJ_COMMON_NAME, "foo.bar", 177 NULL, 178 NULL 179 }, 180 { 181 "DNS Name *foo.bar, vfyName bar.foo.bar, should fail", 182 "*foo.bar", NULL, SUBJ_COMMON_NAME, "bar.foo.bar", 183 "CSSMERR_APPLETP_HOSTNAME_MISMATCH", 184 "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH" 185 }, 186 { 187 "DNS Name *foo.bar, vfyName foobar.bar, should fail", 188 "*foo.bar", NULL, SUBJ_COMMON_NAME, "foobar.bar", 189 "CSSMERR_APPLETP_HOSTNAME_MISMATCH", 190 "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH" 191 }, 192 { 193 "No DNS or IP name, commonName = vfyName = 1.0.5.8", 194 NULL, NULL, "1.0.5.8", "1.0.5.8", 195 "CSSMERR_APPLETP_HOSTNAME_MISMATCH", 196 "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH" 197 }, 198}; 199 200#define NUM_TEST_CASES (sizeof(testCases) / sizeof(SSN_TestCase)) 201 202/* 203 * Convert a string containing a dotted IP address to 4 bytes. 204 * Returns nonzero on error. 205 * FIXME - should handle 16-byte IP addresses. 206 */ 207static int convertIp( 208 const char *str, 209 uint8 *buf) 210{ 211 char cbuf[4]; 212 for(unsigned dex=0; dex<3; dex++) { 213 char *nextDot = strchr(str, '.'); 214 if(nextDot == NULL) { 215 return 1; 216 } 217 memset(cbuf, 0, sizeof(cbuf)); 218 memmove(cbuf, str, nextDot - str); 219 *buf = atoi(cbuf); 220 buf++; // next out char 221 str = nextDot + 1; // next in char after dot 222 223 } 224 /* str points to last char */ 225 if(str == NULL) { 226 return 1; 227 } 228 *buf = atoi(str); 229 return 0; 230} 231 232/* 233 * Generate a pair of certs. 234 */ 235static CSSM_RETURN genCerts( 236 CSSM_CL_HANDLE clHand, 237 CSSM_CSP_HANDLE cspHand, 238 CSSM_TP_HANDLE tpHand, 239 CSSM_KEY_PTR rootPrivKey, 240 CSSM_KEY_PTR rootPubKey, 241 CSSM_KEY_PTR subjPubKey, 242 /* one of these goes into leaf's subjectAltName */ 243 const char *subjIpAddr, 244 const char *subjDnsName, 245 const char *commonName, 246 CSSM_DATA &rootCert, // RETURNED 247 CSSM_DATA &subjCert) // RETURNED 248 249{ 250 CSSM_DATA refId; 251 // mallocd by CSSM_TP_SubmitCredRequest 252 CSSM_RETURN crtn; 253 CSSM_APPLE_TP_CERT_REQUEST certReq; 254 CSSM_TP_REQUEST_SET reqSet; 255 sint32 estTime; 256 CSSM_BOOL confirmRequired; 257 CSSM_TP_RESULT_SET_PTR resultSet; 258 CSSM_ENCODED_CERT *encCert; 259 CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext; 260 CSSM_FIELD policyId; 261 CE_GeneralNames genNames; 262 CE_GeneralName genName; 263 uint8 ipNameBuf[4]; 264 /* 265 * Two extensions. Subject has two (KeyUsage and possibly 266 * subjectAltName); root has KeyUsage and BasicConstraints. 267 */ 268 CE_DataAndType rootExts[2]; 269 CE_DataAndType leafExts[2]; 270 unsigned numLeafExts; 271 272 if(subjIpAddr && subjDnsName) { 273 printf("***Max of one of {subjIpAddr, subjDnsName} at a " 274 "time, please.\n"); 275 exit(1); 276 } 277 if(subjIpAddr) { 278 if(convertIp(subjIpAddr, ipNameBuf)) { 279 printf("**Malformed IP address. Aborting.\n"); 280 exit(1); 281 } 282 } 283 284 /* A KeyUsage extension for both certs */ 285 rootExts[0].type = DT_KeyUsage; 286 rootExts[0].critical = CSSM_FALSE; 287 rootExts[0].extension.keyUsage = 288 CE_KU_DigitalSignature | CE_KU_KeyCertSign; 289 290 leafExts[0].type = DT_KeyUsage; 291 leafExts[0].critical = CSSM_FALSE; 292 leafExts[0].extension.keyUsage = CE_KU_DigitalSignature; 293 294 /* BasicConstraints for root only */ 295 rootExts[1].type = DT_BasicConstraints; 296 rootExts[1].critical = CSSM_TRUE; 297 rootExts[1].extension.basicConstraints.cA = CSSM_TRUE; 298 rootExts[1].extension.basicConstraints.pathLenConstraintPresent = 299 CSSM_TRUE; 300 rootExts[1].extension.basicConstraints.pathLenConstraint = 2; 301 302 /* possible subjectAltName for leaf */ 303 numLeafExts = 1; 304 if(subjIpAddr || subjDnsName) { 305 numLeafExts++; 306 leafExts[1].type = DT_SubjectAltName; 307 leafExts[1].critical = CSSM_TRUE; 308 309 genName.berEncoded = CSSM_FALSE; 310 if(subjIpAddr) { 311 genName.name.Data = (uint8 *)ipNameBuf; 312 genName.name.Length = 4; 313 genName.nameType = GNT_IPAddress; 314 } 315 else { 316 genName.name.Data = (uint8 *)subjDnsName; 317 genName.nameType = GNT_DNSName; 318 genName.name.Length = strlen(subjDnsName); 319 } 320 genNames.numNames = 1; 321 genNames.generalName = &genName; 322 leafExts[1].extension.subjectAltName = genNames; 323 } 324 325 /* certReq for root */ 326 memset(&certReq, 0, sizeof(CSSM_APPLE_TP_CERT_REQUEST)); 327 certReq.cspHand = cspHand; 328 certReq.clHand = clHand; 329 certReq.serialNumber = 0x12345678; 330 certReq.numSubjectNames = NUM_ROOT_NAMES; 331 certReq.subjectNames = rootRdn; 332 certReq.numIssuerNames = 0; 333 certReq.issuerNames = NULL; 334 certReq.certPublicKey = rootPubKey; 335 certReq.issuerPrivateKey = rootPrivKey; 336 certReq.signatureAlg = SIG_ALG_DEFAULT; 337 certReq.signatureOid = SIG_OID_DEFAULT; 338 certReq.notBefore = 0; // now 339 certReq.notAfter = 10000; // seconds from now 340 certReq.numExtensions = 2; 341 certReq.extensions = rootExts; 342 343 reqSet.NumberOfRequests = 1; 344 reqSet.Requests = &certReq; 345 346 /* a big CSSM_TP_CALLERAUTH_CONTEXT just to specify an OID */ 347 memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT)); 348 memset(&policyId, 0, sizeof(CSSM_FIELD)); 349 policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN; 350 CallerAuthContext.Policy.NumberOfPolicyIds = 1; 351 CallerAuthContext.Policy.PolicyIds = &policyId; 352 353 /* generate root cert */ 354 crtn = CSSM_TP_SubmitCredRequest(tpHand, 355 NULL, // PreferredAuthority 356 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, 357 &reqSet, 358 &CallerAuthContext, 359 &estTime, 360 &refId); 361 if(crtn) { 362 printError("CSSM_TP_SubmitCredRequest", crtn); 363 return crtn; 364 } 365 crtn = CSSM_TP_RetrieveCredResult(tpHand, 366 &refId, 367 NULL, // CallerAuthCredentials 368 &estTime, 369 &confirmRequired, 370 &resultSet); 371 if(crtn) { 372 printError("CSSM_TP_RetrieveCredResult", crtn); 373 return crtn; 374 } 375 if(resultSet == NULL) { 376 printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n"); 377 return crtn; 378 } 379 encCert = (CSSM_ENCODED_CERT *)resultSet->Results; 380 rootCert = encCert->CertBlob; 381 382 /* now a subject cert signed by the root cert */ 383 certReq.serialNumber = 0x8765; 384 certReq.numSubjectNames = NUM_SUBJ_NAMES; 385 subjRdn[SUBJ_COMMON_NAME_DEX].string = commonName; 386 certReq.subjectNames = subjRdn; 387 certReq.numIssuerNames = NUM_ROOT_NAMES; 388 certReq.issuerNames = rootRdn; 389 certReq.certPublicKey = subjPubKey; 390 certReq.issuerPrivateKey = rootPrivKey; 391 certReq.numExtensions = numLeafExts; 392 certReq.extensions = leafExts; 393 394 crtn = CSSM_TP_SubmitCredRequest(tpHand, 395 NULL, // PreferredAuthority 396 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, 397 &reqSet, 398 &CallerAuthContext, 399 &estTime, 400 &refId); 401 if(crtn) { 402 printError("CSSM_TP_SubmitCredRequest (2)", crtn); 403 return crtn; 404 } 405 crtn = CSSM_TP_RetrieveCredResult(tpHand, 406 &refId, 407 NULL, // CallerAuthCredentials 408 &estTime, 409 &confirmRequired, 410 &resultSet); // leaks..... 411 if(crtn) { 412 printError("CSSM_TP_RetrieveCredResult (2)", crtn); 413 return crtn; 414 } 415 if(resultSet == NULL) { 416 printf("***CSSM_TP_RetrieveCredResult (2) returned NULL " 417 "result set.\n"); 418 return crtn; 419 } 420 encCert = (CSSM_ENCODED_CERT *)resultSet->Results; 421 subjCert = encCert->CertBlob; 422 423 return CSSM_OK; 424} 425 426int main(int argc, char **argv) 427{ 428 CSSM_CL_HANDLE clHand; // CL handle 429 CSSM_CSP_HANDLE cspHand; // CSP handle 430 CSSM_TP_HANDLE tpHand; // TP handle 431 CSSM_DATA rootCert; 432 CSSM_DATA subjCert; 433 CSSM_KEY subjPubKey; // subject's RSA public key blob 434 CSSM_KEY subjPrivKey; // subject's RSA private key - ref format 435 CSSM_KEY rootPubKey; // root's RSA public key blob 436 CSSM_KEY rootPrivKey; // root's RSA private key - ref format 437 CSSM_RETURN crtn = CSSM_OK; 438 int vfyRtn = 0; 439 int arg; 440 SSN_TestCase *testCase; 441 unsigned testNum; 442 443 CSSM_BOOL quiet = CSSM_FALSE; 444 CSSM_BOOL verbose = CSSM_FALSE; 445 CSSM_BOOL writeCerts = CSSM_FALSE; 446 447 for(arg=1; arg<argc; arg++) { 448 char *argp = argv[arg]; 449 switch(argp[0]) { 450 case 'q': 451 quiet = CSSM_TRUE; 452 break; 453 case 'v': 454 verbose = CSSM_TRUE; 455 break; 456 case 'w': 457 writeCerts = CSSM_TRUE; 458 break; 459 default: 460 usage(argv); 461 } 462 } 463 464 testStartBanner("sslSubjName", argc, argv); 465 466 /* connect to CL, TP, and CSP */ 467 clHand = clStartup(); 468 if(clHand == 0) { 469 return 0; 470 } 471 tpHand = tpStartup(); 472 if(tpHand == 0) { 473 return 0; 474 } 475 cspHand = cspStartup(); 476 if(cspHand == 0) { 477 return 0; 478 } 479 480 /* subsequent errors to abort: to detach */ 481 482 /* cook up an RSA key pair for the subject */ 483 crtn = cspGenKeyPair(cspHand, 484 KEY_ALG_DEFAULT, 485 SUBJ_KEY_LABEL, 486 strlen(SUBJ_KEY_LABEL), 487 KEY_SIZE_DEFAULT, 488 &subjPubKey, 489 CSSM_FALSE, // pubIsRef 490 CSSM_KEYUSE_VERIFY, 491 CSSM_KEYBLOB_RAW_FORMAT_NONE, 492 &subjPrivKey, 493 CSSM_TRUE, // privIsRef - doesn't matter 494 CSSM_KEYUSE_SIGN, 495 CSSM_KEYBLOB_RAW_FORMAT_NONE, 496 CSSM_FALSE); 497 if(crtn) { 498 return crtn; 499 } 500 501 /* and the root */ 502 crtn = cspGenKeyPair(cspHand, 503 KEY_ALG_DEFAULT, 504 ROOT_KEY_LABEL, 505 strlen(ROOT_KEY_LABEL), 506 KEY_SIZE_DEFAULT, 507 &rootPubKey, 508 CSSM_FALSE, // pubIsRef 509 CSSM_KEYUSE_VERIFY, 510 CSSM_KEYBLOB_RAW_FORMAT_NONE, 511 &rootPrivKey, 512 CSSM_TRUE, // privIsRef - doesn't matter 513 CSSM_KEYUSE_SIGN, 514 CSSM_KEYBLOB_RAW_FORMAT_NONE, 515 CSSM_FALSE); 516 if(crtn) { 517 goto abort; 518 } 519 520 for(testNum=0; testNum<NUM_TEST_CASES; testNum++) { 521 testCase = &testCases[testNum]; 522 if(!quiet) { 523 printf("%s\n", testCase->testDesc); 524 } 525 crtn = genCerts(clHand, cspHand, tpHand, 526 &rootPrivKey, &rootPubKey, &subjPubKey, 527 testCase->certIpAddr, testCase->certDnsName, testCase->commonName, 528 rootCert, subjCert); 529 BlobList leaf; 530 BlobList root; 531 /* BlobList uses regular free() on the referent of the blobs */ 532 leaf.addBlob(subjCert, CSSM_TRUE); 533 root.addBlob(rootCert, CSSM_TRUE); 534 if(crtn) { 535 if(testError(quiet)) { 536 break; 537 } 538 } 539 if(writeCerts) { 540 if(writeFile(CERT_FILE, subjCert.Data, subjCert.Length)) { 541 printf("***Error writing cert to %s\n", CERT_FILE); 542 } 543 else { 544 printf("...wrote %lu bytes to %s\n", subjCert.Length, CERT_FILE); 545 } 546 } 547 vfyRtn = certVerifySimple(tpHand, clHand, cspHand, 548 leaf, root, 549 CSSM_FALSE, // useSystemAnchors 550 CSSM_FALSE, // leafCertIsCA 551 CSSM_FALSE, // allow expired root 552 CVP_SSL, 553 testCase->vfyHostName, 554 CSSM_FALSE, // sslClient 555 NULL, 556 NULL, 557 testCase->expectErrStr, 558 testCase->certErrorStr ? 1 : 0, 559 testCase->certErrorStr ? (const char **)&testCase->certErrorStr : 560 NULL, 561 0, NULL, // certStatus 562 CSSM_FALSE, // trustSettings 563 quiet, 564 verbose); 565 if(vfyRtn) { 566 if(testError(quiet)) { 567 break; 568 } 569 } 570 /* cert data freed by ~BlobList */ 571 } 572 573 /* free keys */ 574 cspFreeKey(cspHand, &rootPubKey); 575 cspFreeKey(cspHand, &rootPrivKey); 576 cspFreeKey(cspHand, &subjPubKey); 577 cspFreeKey(cspHand, &subjPrivKey); 578 579abort: 580 if(cspHand != 0) { 581 CSSM_ModuleDetach(cspHand); 582 } 583 if(clHand != 0) { 584 CSSM_ModuleDetach(clHand); 585 } 586 if(tpHand != 0) { 587 CSSM_ModuleDetach(tpHand); 588 } 589 if(!vfyRtn && !crtn && !quiet) { 590 printf("...test passed\n"); 591 } 592 return 0; 593} 594 595 596