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