--- /dev/null
+/* Copyright (c) 2002-2004,2006,2008 Apple Inc.
+ *
+ * sslSubjName.c
+ *
+ * Verify comparision of app-specified host name vs. various
+ * forms of hostname in a cert.
+ *
+ */
+
+#include <utilLib/common.h>
+#include <utilLib/cspwrap.h>
+#include <clAppUtils/clutils.h>
+#include <clAppUtils/certVerify.h>
+#include <clAppUtils/BlobList.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <Security/cssm.h>
+#include <Security/x509defs.h>
+#include <Security/oidsattr.h>
+#include <Security/oidscert.h>
+#include <Security/oidsalg.h>
+#include <Security/certextensions.h>
+#include <Security/cssmapple.h>
+#include <string.h>
+#include <security_cdsa_utils/cuFileIo.h>
+
+/* key labels */
+#define SUBJ_KEY_LABEL "subjectKey"
+#define ROOT_KEY_LABEL "rootKey"
+
+/* key and signature algorithm - shouldn't matter for this test */
+#define SIG_ALG_DEFAULT CSSM_ALGID_SHA1WithRSA
+#define SIG_OID_DEFAULT CSSMOID_SHA1WithRSA
+#define KEY_ALG_DEFAULT CSSM_ALGID_RSA
+
+#define KEY_SIZE_DEFAULT 512
+
+#define CERT_FILE "sslCert.cer"
+
+static void usage(char **argv)
+{
+ printf("Usage: %s [options]\n", argv[0]);
+ printf("Options:\n");
+ printf(" w(write certs)\n");
+ printf(" q(uiet)\n");
+ printf(" v(erbose)\n");
+ exit(1);
+}
+
+/*
+ * RDN components for root, subject
+ */
+CSSM_APPLE_TP_NAME_OID rootRdn[] =
+{
+ { "Apple Computer", &CSSMOID_OrganizationName },
+ { "The Big Cheese", &CSSMOID_Title }
+};
+#define NUM_ROOT_NAMES (sizeof(rootRdn) / sizeof(CSSM_APPLE_TP_NAME_OID))
+
+#define SUBJ_COMMON_NAME "something.org"
+
+CSSM_APPLE_TP_NAME_OID subjRdn[] =
+{
+ { "Apple Computer", &CSSMOID_OrganizationName },
+ /* overridden when creating the cert */
+ { NULL, &CSSMOID_CommonName }
+};
+#define SUBJ_COMMON_NAME_DEX 1
+
+#define NUM_SUBJ_NAMES (sizeof(subjRdn) / sizeof(CSSM_APPLE_TP_NAME_OID))
+
+
+/*
+ * Test cases
+ */
+typedef struct {
+ /* test description */
+ const char *testDesc;
+
+ /* host names for leaf cert - zero or one of these */
+ const char *certDnsName;
+ const char *certIpAddr;
+
+ /* subject common name */
+ const char *commonName;
+
+ /* host name for CertGroupVerify */
+ const char *vfyHostName;
+
+ /* expected error - NULL or e.g. "CSSMERR_APPLETP_CRL_NOT_TRUSTED" */
+ const char *expectErrStr;
+
+ /* one optional per-cert error string */
+ const char *certErrorStr;
+
+} SSN_TestCase;
+
+SSN_TestCase testCases[] =
+{
+ {
+ "DNS Name foo.bar, vfyName foo.bar",
+ "foo.bar", NULL, SUBJ_COMMON_NAME, "foo.bar",
+ NULL,
+ NULL
+ },
+ {
+ "DNS Name foo.bar, vfyName something.org, expect fail due to "
+ "DNS present",
+ "foo.bar", NULL, SUBJ_COMMON_NAME, "something.org",
+ "CSSMERR_APPLETP_HOSTNAME_MISMATCH",
+ "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH"
+ },
+ {
+ "DNS Name foo.bar, vfyName foo.foo.bar, expect fail",
+ "foo.bar", NULL, SUBJ_COMMON_NAME, "foo.foo.bar",
+ "CSSMERR_APPLETP_HOSTNAME_MISMATCH",
+ "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH"
+ },
+ {
+ "IP Name 1.0.5.8, vfyName 1.0.5.8",
+ NULL, "1.0.5.8", SUBJ_COMMON_NAME, "1.0.5.8",
+ NULL,
+ NULL
+ },
+ {
+ "IP Name 1.0.5.8, vfyName 1.00.5.008",
+ NULL, "1.0.5.8", SUBJ_COMMON_NAME, "1.00.5.008",
+ NULL,
+ NULL
+ },
+ {
+ "IP Name 1.0.5.8, vfyName something.org",
+ NULL, "1.0.5.8", SUBJ_COMMON_NAME, "something.org",
+ NULL,
+ NULL
+ },
+ {
+ "IP Name 1.0.5.8, vfyName 2.0.5.8, expect fail",
+ NULL, "1.0.5.8", SUBJ_COMMON_NAME, "2.0.5.8",
+ "CSSMERR_APPLETP_HOSTNAME_MISMATCH",
+ "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH"
+ },
+ {
+ "DNS Name *.foo.bar, vfyName bar.foo.bar",
+ "*.foo.bar", NULL, SUBJ_COMMON_NAME, "bar.foo.bar",
+ NULL,
+ NULL
+ },
+ {
+ "DNS Name *.foo.bar, vfyName foo.bar, expect fail",
+ "*.foo.bar", NULL, SUBJ_COMMON_NAME, "foo.bar",
+ "CSSMERR_APPLETP_HOSTNAME_MISMATCH",
+ "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH"
+ },
+ {
+ "DNS Name *foo.bar, vfyName barfoo.bar",
+ "*foo.bar", NULL, SUBJ_COMMON_NAME, "barfoo.bar",
+ NULL,
+ NULL
+ },
+ {
+ "DNS Name *foo*.bar, vfyName barfoo.bar",
+ "*foo*.bar", NULL, SUBJ_COMMON_NAME, "barfoo.bar",
+ NULL,
+ NULL
+ },
+ {
+ "DNS Name *foo*.bar, vfyName foobar.bar",
+ "*foo*.bar", NULL, SUBJ_COMMON_NAME, "foobar.bar",
+ NULL,
+ NULL
+ },
+ {
+ "DNS Name *foo*.bar, vfyName foo.bar",
+ "*foo*.bar", NULL, SUBJ_COMMON_NAME, "foo.bar",
+ NULL,
+ NULL
+ },
+ {
+ "DNS Name *foo.bar, vfyName bar.foo.bar, should fail",
+ "*foo.bar", NULL, SUBJ_COMMON_NAME, "bar.foo.bar",
+ "CSSMERR_APPLETP_HOSTNAME_MISMATCH",
+ "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH"
+ },
+ {
+ "DNS Name *foo.bar, vfyName foobar.bar, should fail",
+ "*foo.bar", NULL, SUBJ_COMMON_NAME, "foobar.bar",
+ "CSSMERR_APPLETP_HOSTNAME_MISMATCH",
+ "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH"
+ },
+ {
+ "No DNS or IP name, commonName = vfyName = 1.0.5.8",
+ NULL, NULL, "1.0.5.8", "1.0.5.8",
+ "CSSMERR_APPLETP_HOSTNAME_MISMATCH",
+ "0:CSSMERR_APPLETP_HOSTNAME_MISMATCH"
+ },
+};
+
+#define NUM_TEST_CASES (sizeof(testCases) / sizeof(SSN_TestCase))
+
+/*
+ * Convert a string containing a dotted IP address to 4 bytes.
+ * Returns nonzero on error.
+ * FIXME - should handle 16-byte IP addresses.
+ */
+static int convertIp(
+ const char *str,
+ uint8 *buf)
+{
+ char cbuf[4];
+ for(unsigned dex=0; dex<3; dex++) {
+ char *nextDot = strchr(str, '.');
+ if(nextDot == NULL) {
+ return 1;
+ }
+ memset(cbuf, 0, sizeof(cbuf));
+ memmove(cbuf, str, nextDot - str);
+ *buf = atoi(cbuf);
+ buf++; // next out char
+ str = nextDot + 1; // next in char after dot
+
+ }
+ /* str points to last char */
+ if(str == NULL) {
+ return 1;
+ }
+ *buf = atoi(str);
+ return 0;
+}
+
+/*
+ * Generate a pair of certs.
+ */
+static CSSM_RETURN genCerts(
+ CSSM_CL_HANDLE clHand,
+ CSSM_CSP_HANDLE cspHand,
+ CSSM_TP_HANDLE tpHand,
+ CSSM_KEY_PTR rootPrivKey,
+ CSSM_KEY_PTR rootPubKey,
+ CSSM_KEY_PTR subjPubKey,
+ /* one of these goes into leaf's subjectAltName */
+ const char *subjIpAddr,
+ const char *subjDnsName,
+ const char *commonName,
+ CSSM_DATA &rootCert, // RETURNED
+ CSSM_DATA &subjCert) // RETURNED
+
+{
+ CSSM_DATA refId;
+ // mallocd by CSSM_TP_SubmitCredRequest
+ CSSM_RETURN crtn;
+ CSSM_APPLE_TP_CERT_REQUEST certReq;
+ CSSM_TP_REQUEST_SET reqSet;
+ sint32 estTime;
+ CSSM_BOOL confirmRequired;
+ CSSM_TP_RESULT_SET_PTR resultSet;
+ CSSM_ENCODED_CERT *encCert;
+ CSSM_TP_CALLERAUTH_CONTEXT CallerAuthContext;
+ CSSM_FIELD policyId;
+ CE_GeneralNames genNames;
+ CE_GeneralName genName;
+ uint8 ipNameBuf[4];
+ /*
+ * Two extensions. Subject has two (KeyUsage and possibly
+ * subjectAltName); root has KeyUsage and BasicConstraints.
+ */
+ CE_DataAndType rootExts[2];
+ CE_DataAndType leafExts[2];
+ unsigned numLeafExts;
+
+ if(subjIpAddr && subjDnsName) {
+ printf("***Max of one of {subjIpAddr, subjDnsName} at a "
+ "time, please.\n");
+ exit(1);
+ }
+ if(subjIpAddr) {
+ if(convertIp(subjIpAddr, ipNameBuf)) {
+ printf("**Malformed IP address. Aborting.\n");
+ exit(1);
+ }
+ }
+
+ /* A KeyUsage extension for both certs */
+ rootExts[0].type = DT_KeyUsage;
+ rootExts[0].critical = CSSM_FALSE;
+ rootExts[0].extension.keyUsage =
+ CE_KU_DigitalSignature | CE_KU_KeyCertSign;
+
+ leafExts[0].type = DT_KeyUsage;
+ leafExts[0].critical = CSSM_FALSE;
+ leafExts[0].extension.keyUsage = CE_KU_DigitalSignature;
+
+ /* BasicConstraints for root only */
+ rootExts[1].type = DT_BasicConstraints;
+ rootExts[1].critical = CSSM_TRUE;
+ rootExts[1].extension.basicConstraints.cA = CSSM_TRUE;
+ rootExts[1].extension.basicConstraints.pathLenConstraintPresent =
+ CSSM_TRUE;
+ rootExts[1].extension.basicConstraints.pathLenConstraint = 2;
+
+ /* possible subjectAltName for leaf */
+ numLeafExts = 1;
+ if(subjIpAddr || subjDnsName) {
+ numLeafExts++;
+ leafExts[1].type = DT_SubjectAltName;
+ leafExts[1].critical = CSSM_TRUE;
+
+ genName.berEncoded = CSSM_FALSE;
+ if(subjIpAddr) {
+ genName.name.Data = (uint8 *)ipNameBuf;
+ genName.name.Length = 4;
+ genName.nameType = GNT_IPAddress;
+ }
+ else {
+ genName.name.Data = (uint8 *)subjDnsName;
+ genName.nameType = GNT_DNSName;
+ genName.name.Length = strlen(subjDnsName);
+ }
+ genNames.numNames = 1;
+ genNames.generalName = &genName;
+ leafExts[1].extension.subjectAltName = genNames;
+ }
+
+ /* certReq for root */
+ memset(&certReq, 0, sizeof(CSSM_APPLE_TP_CERT_REQUEST));
+ certReq.cspHand = cspHand;
+ certReq.clHand = clHand;
+ certReq.serialNumber = 0x12345678;
+ certReq.numSubjectNames = NUM_ROOT_NAMES;
+ certReq.subjectNames = rootRdn;
+ certReq.numIssuerNames = 0;
+ certReq.issuerNames = NULL;
+ certReq.certPublicKey = rootPubKey;
+ certReq.issuerPrivateKey = rootPrivKey;
+ certReq.signatureAlg = SIG_ALG_DEFAULT;
+ certReq.signatureOid = SIG_OID_DEFAULT;
+ certReq.notBefore = 0; // now
+ certReq.notAfter = 10000; // seconds from now
+ certReq.numExtensions = 2;
+ certReq.extensions = rootExts;
+
+ reqSet.NumberOfRequests = 1;
+ reqSet.Requests = &certReq;
+
+ /* a big CSSM_TP_CALLERAUTH_CONTEXT just to specify an OID */
+ memset(&CallerAuthContext, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
+ memset(&policyId, 0, sizeof(CSSM_FIELD));
+ policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN;
+ CallerAuthContext.Policy.NumberOfPolicyIds = 1;
+ CallerAuthContext.Policy.PolicyIds = &policyId;
+
+ /* generate root cert */
+ crtn = CSSM_TP_SubmitCredRequest(tpHand,
+ NULL, // PreferredAuthority
+ CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
+ &reqSet,
+ &CallerAuthContext,
+ &estTime,
+ &refId);
+ if(crtn) {
+ printError("CSSM_TP_SubmitCredRequest", crtn);
+ return crtn;
+ }
+ crtn = CSSM_TP_RetrieveCredResult(tpHand,
+ &refId,
+ NULL, // CallerAuthCredentials
+ &estTime,
+ &confirmRequired,
+ &resultSet);
+ if(crtn) {
+ printError("CSSM_TP_RetrieveCredResult", crtn);
+ return crtn;
+ }
+ if(resultSet == NULL) {
+ printf("***CSSM_TP_RetrieveCredResult returned NULL result set.\n");
+ return crtn;
+ }
+ encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
+ rootCert = encCert->CertBlob;
+
+ /* now a subject cert signed by the root cert */
+ certReq.serialNumber = 0x8765;
+ certReq.numSubjectNames = NUM_SUBJ_NAMES;
+ subjRdn[SUBJ_COMMON_NAME_DEX].string = commonName;
+ certReq.subjectNames = subjRdn;
+ certReq.numIssuerNames = NUM_ROOT_NAMES;
+ certReq.issuerNames = rootRdn;
+ certReq.certPublicKey = subjPubKey;
+ certReq.issuerPrivateKey = rootPrivKey;
+ certReq.numExtensions = numLeafExts;
+ certReq.extensions = leafExts;
+
+ crtn = CSSM_TP_SubmitCredRequest(tpHand,
+ NULL, // PreferredAuthority
+ CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,
+ &reqSet,
+ &CallerAuthContext,
+ &estTime,
+ &refId);
+ if(crtn) {
+ printError("CSSM_TP_SubmitCredRequest (2)", crtn);
+ return crtn;
+ }
+ crtn = CSSM_TP_RetrieveCredResult(tpHand,
+ &refId,
+ NULL, // CallerAuthCredentials
+ &estTime,
+ &confirmRequired,
+ &resultSet); // leaks.....
+ if(crtn) {
+ printError("CSSM_TP_RetrieveCredResult (2)", crtn);
+ return crtn;
+ }
+ if(resultSet == NULL) {
+ printf("***CSSM_TP_RetrieveCredResult (2) returned NULL "
+ "result set.\n");
+ return crtn;
+ }
+ encCert = (CSSM_ENCODED_CERT *)resultSet->Results;
+ subjCert = encCert->CertBlob;
+
+ return CSSM_OK;
+}
+
+int main(int argc, char **argv)
+{
+ CSSM_CL_HANDLE clHand; // CL handle
+ CSSM_CSP_HANDLE cspHand; // CSP handle
+ CSSM_TP_HANDLE tpHand; // TP handle
+ CSSM_DATA rootCert;
+ CSSM_DATA subjCert;
+ CSSM_KEY subjPubKey; // subject's RSA public key blob
+ CSSM_KEY subjPrivKey; // subject's RSA private key - ref format
+ CSSM_KEY rootPubKey; // root's RSA public key blob
+ CSSM_KEY rootPrivKey; // root's RSA private key - ref format
+ CSSM_RETURN crtn = CSSM_OK;
+ int vfyRtn = 0;
+ int arg;
+ SSN_TestCase *testCase;
+ unsigned testNum;
+
+ CSSM_BOOL quiet = CSSM_FALSE;
+ CSSM_BOOL verbose = CSSM_FALSE;
+ CSSM_BOOL writeCerts = CSSM_FALSE;
+
+ for(arg=1; arg<argc; arg++) {
+ char *argp = argv[arg];
+ switch(argp[0]) {
+ case 'q':
+ quiet = CSSM_TRUE;
+ break;
+ case 'v':
+ verbose = CSSM_TRUE;
+ break;
+ case 'w':
+ writeCerts = CSSM_TRUE;
+ break;
+ default:
+ usage(argv);
+ }
+ }
+
+ testStartBanner("sslSubjName", argc, argv);
+
+ /* connect to CL, TP, and CSP */
+ clHand = clStartup();
+ if(clHand == 0) {
+ return 0;
+ }
+ tpHand = tpStartup();
+ if(tpHand == 0) {
+ return 0;
+ }
+ cspHand = cspStartup();
+ if(cspHand == 0) {
+ return 0;
+ }
+
+ /* subsequent errors to abort: to detach */
+
+ /* cook up an RSA key pair for the subject */
+ crtn = cspGenKeyPair(cspHand,
+ KEY_ALG_DEFAULT,
+ SUBJ_KEY_LABEL,
+ strlen(SUBJ_KEY_LABEL),
+ KEY_SIZE_DEFAULT,
+ &subjPubKey,
+ CSSM_FALSE, // pubIsRef
+ CSSM_KEYUSE_VERIFY,
+ CSSM_KEYBLOB_RAW_FORMAT_NONE,
+ &subjPrivKey,
+ CSSM_TRUE, // privIsRef - doesn't matter
+ CSSM_KEYUSE_SIGN,
+ CSSM_KEYBLOB_RAW_FORMAT_NONE,
+ CSSM_FALSE);
+ if(crtn) {
+ return crtn;
+ }
+
+ /* and the root */
+ crtn = cspGenKeyPair(cspHand,
+ KEY_ALG_DEFAULT,
+ ROOT_KEY_LABEL,
+ strlen(ROOT_KEY_LABEL),
+ KEY_SIZE_DEFAULT,
+ &rootPubKey,
+ CSSM_FALSE, // pubIsRef
+ CSSM_KEYUSE_VERIFY,
+ CSSM_KEYBLOB_RAW_FORMAT_NONE,
+ &rootPrivKey,
+ CSSM_TRUE, // privIsRef - doesn't matter
+ CSSM_KEYUSE_SIGN,
+ CSSM_KEYBLOB_RAW_FORMAT_NONE,
+ CSSM_FALSE);
+ if(crtn) {
+ goto abort;
+ }
+
+ for(testNum=0; testNum<NUM_TEST_CASES; testNum++) {
+ testCase = &testCases[testNum];
+ if(!quiet) {
+ printf("%s\n", testCase->testDesc);
+ }
+ crtn = genCerts(clHand, cspHand, tpHand,
+ &rootPrivKey, &rootPubKey, &subjPubKey,
+ testCase->certIpAddr, testCase->certDnsName, testCase->commonName,
+ rootCert, subjCert);
+ BlobList leaf;
+ BlobList root;
+ /* BlobList uses regular free() on the referent of the blobs */
+ leaf.addBlob(subjCert, CSSM_TRUE);
+ root.addBlob(rootCert, CSSM_TRUE);
+ if(crtn) {
+ if(testError(quiet)) {
+ break;
+ }
+ }
+ if(writeCerts) {
+ if(writeFile(CERT_FILE, subjCert.Data, subjCert.Length)) {
+ printf("***Error writing cert to %s\n", CERT_FILE);
+ }
+ else {
+ printf("...wrote %lu bytes to %s\n", subjCert.Length, CERT_FILE);
+ }
+ }
+ vfyRtn = certVerifySimple(tpHand, clHand, cspHand,
+ leaf, root,
+ CSSM_FALSE, // useSystemAnchors
+ CSSM_FALSE, // leafCertIsCA
+ CSSM_FALSE, // allow expired root
+ CVP_SSL,
+ testCase->vfyHostName,
+ CSSM_FALSE, // sslClient
+ NULL,
+ NULL,
+ testCase->expectErrStr,
+ testCase->certErrorStr ? 1 : 0,
+ testCase->certErrorStr ? (const char **)&testCase->certErrorStr :
+ NULL,
+ 0, NULL, // certStatus
+ CSSM_FALSE, // trustSettings
+ quiet,
+ verbose);
+ if(vfyRtn) {
+ if(testError(quiet)) {
+ break;
+ }
+ }
+ /* cert data freed by ~BlobList */
+ }
+
+ /* free keys */
+ cspFreeKey(cspHand, &rootPubKey);
+ cspFreeKey(cspHand, &rootPrivKey);
+ cspFreeKey(cspHand, &subjPubKey);
+ cspFreeKey(cspHand, &subjPrivKey);
+
+abort:
+ if(cspHand != 0) {
+ CSSM_ModuleDetach(cspHand);
+ }
+ if(clHand != 0) {
+ CSSM_ModuleDetach(clHand);
+ }
+ if(tpHand != 0) {
+ CSSM_ModuleDetach(tpHand);
+ }
+ if(!vfyRtn && !crtn && !quiet) {
+ printf("...test passed\n");
+ }
+ return 0;
+}
+
+