1/*
2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * verify_cert.c
24 */
25
26#include <Security/SecTrust.h>
27#include <Security/SecKeychain.h>
28#include <Security/SecPolicy.h>
29#include <Security/SecPolicySearch.h>
30#include <Security/cssmapple.h>
31#include <Security/oidsalg.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include "trusted_cert_utils.h"
35
36/*
37 * Read file as a cert, add to a CFArray, creating the array if necessary
38 */
39static int addCertFile(
40	const char *fileName,
41	CFMutableArrayRef *array)
42{
43	SecCertificateRef certRef;
44
45	if(readCertFile(fileName, &certRef)) {
46		return -1;
47	}
48	if(*array == NULL) {
49		*array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
50	}
51	CFArrayAppendValue(*array, certRef);
52	CFRelease(certRef);
53	return 0;
54}
55
56int
57verify_cert(int argc, char * const *argv)
58{
59	extern char			*optarg;
60	extern int			optind;
61	OSStatus			ortn;
62	int					arg;
63	CFMutableArrayRef	certs = NULL;
64	CFMutableArrayRef	roots = NULL;
65	CFMutableArrayRef	keychains = NULL;
66	const CSSM_OID		*policy = &CSSMOID_APPLE_X509_BASIC;
67	SecKeychainRef		kcRef = NULL;
68	int					ourRtn = 0;
69	bool				quiet = false;
70	SecPolicyRef		policyRef = NULL;
71	SecTrustRef			trustRef = NULL;
72	SecPolicySearchRef	searchRef = NULL;
73	const char			*emailAddrs = NULL;
74	const char			*sslHost = NULL;
75	CSSM_APPLE_TP_SSL_OPTIONS	sslOpts;
76	CSSM_APPLE_TP_SMIME_OPTIONS	smimeOpts;
77	CSSM_APPLE_TP_ACTION_FLAGS actionFlags = 0;
78	bool				forceActionFlags = false;
79	CSSM_APPLE_TP_ACTION_DATA	actionData;
80	CSSM_DATA			optionData;
81	CFDataRef			cfActionData = NULL;
82	SecTrustResultType	resultType;
83	OSStatus			ocrtn;
84
85	if(argc < 2) {
86		return 2; /* @@@ Return 2 triggers usage message. */
87	}
88	/* permit network cert fetch unless explicitly turned off with '-L' */
89	actionFlags |= CSSM_TP_ACTION_FETCH_CERT_FROM_NET;
90	optind = 1;
91	while ((arg = getopt(argc, argv, "c:r:p:k:e:s:Llnq")) != -1) {
92		switch (arg) {
93			case 'c':
94				/* this can be specified multiple times */
95				if(addCertFile(optarg, &certs)) {
96					ourRtn = 1;
97					goto errOut;
98				}
99				break;
100			case 'r':
101				/* this can be specified multiple times */
102				if(addCertFile(optarg, &roots)) {
103					ourRtn = 1;
104					goto errOut;
105				}
106				break;
107			case 'p':
108				policy = policyStringToOid(optarg);
109				if(policy == NULL) {
110					ourRtn = 2;
111					goto errOut;
112				}
113				break;
114			case 'k':
115				ortn = SecKeychainOpen(optarg, &kcRef);
116				if(ortn) {
117					cssmPerror("SecKeychainOpen", ortn);
118					ourRtn = 1;
119					goto errOut;
120				}
121				/* this can be specified multiple times */
122				if(keychains == NULL) {
123					keychains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
124				}
125				CFArrayAppendValue(keychains, kcRef);
126				CFRelease(kcRef);
127				break;
128			case 'L':
129				actionFlags &= ~CSSM_TP_ACTION_FETCH_CERT_FROM_NET;
130				forceActionFlags = true;
131				break;
132			case 'l':
133				actionFlags |= CSSM_TP_ACTION_LEAF_IS_CA;
134				break;
135			case 'n':
136				/* No keychains, signalled by empty keychain array */
137				if(keychains != NULL) {
138					fprintf(stderr, "-k and -n are mutually exclusive\n");
139					ourRtn = 2;
140					goto errOut;
141				}
142				keychains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
143				break;
144			case 'e':
145				emailAddrs = optarg;
146				break;
147			case 's':
148				sslHost = optarg;
149				break;
150			case 'q':
151				quiet = true;
152				break;
153			default:
154				ourRtn = 2;
155				goto errOut;
156		}
157	}
158	if(optind != argc) {
159		ourRtn = 2;
160		goto errOut;
161	}
162
163	if(certs == NULL) {
164		if(roots == NULL) {
165			fprintf(stderr, "***No certs specified.\n");
166			ourRtn = 2;
167			goto errOut;
168		}
169		if(CFArrayGetCount(roots) != 1) {
170			fprintf(stderr, "***Multiple roots and no certs not allowed.\n");
171			ourRtn = 2;
172			goto errOut;
173		}
174
175		/* no certs and one root: verify the root */
176		certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
177		CFArrayAppendValue(certs, CFArrayGetValueAtIndex(roots, 0));
178		actionFlags |= CSSM_TP_ACTION_LEAF_IS_CA;
179	}
180
181	/* cook up a SecPolicyRef */
182	ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
183		policy,
184		NULL,				// policy opts
185		&searchRef);
186	if(ortn) {
187		cssmPerror("SecPolicySearchCreate", ortn);
188		ourRtn = 1;
189		goto errOut;
190	}
191	ortn = SecPolicySearchCopyNext(searchRef, &policyRef);
192	if(ortn) {
193		cssmPerror("SecPolicySearchCopyNext", ortn);
194		ourRtn = 1;
195		goto errOut;
196	}
197
198	/* per-policy options */
199	if(compareOids(policy, &CSSMOID_APPLE_TP_SSL) || compareOids(policy, &CSSMOID_APPLE_TP_APPLEID_SHARING)) {
200		if(sslHost != NULL) {
201			memset(&sslOpts, 0, sizeof(sslOpts));
202			sslOpts.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
203			sslOpts.ServerName = sslHost;
204			sslOpts.ServerNameLen = strlen(sslHost);
205			optionData.Data = (uint8 *)&sslOpts;
206			optionData.Length = sizeof(sslOpts);
207			ortn = SecPolicySetValue(policyRef, &optionData);
208			if(ortn) {
209				cssmPerror("SecPolicySetValue", ortn);
210				ourRtn = 1;
211				goto errOut;
212			}
213		}
214	}
215	if(compareOids(policy, &CSSMOID_APPLE_TP_SMIME)) {
216		if(emailAddrs != NULL) {
217			memset(&smimeOpts, 0, sizeof(smimeOpts));
218			smimeOpts.Version = CSSM_APPLE_TP_SMIME_OPTS_VERSION;
219			smimeOpts.SenderEmail = emailAddrs;
220			smimeOpts.SenderEmailLen = strlen(emailAddrs);
221			optionData.Data = (uint8 *)&smimeOpts;
222			optionData.Length = sizeof(smimeOpts);
223			ortn = SecPolicySetValue(policyRef, &optionData);
224			if(ortn) {
225				cssmPerror("SecPolicySetValue", ortn);
226				ourRtn = 1;
227				goto errOut;
228			}
229		}
230	}
231
232	/* Now create a SecTrustRef and set its options */
233	ortn = SecTrustCreateWithCertificates(certs, policyRef, &trustRef);
234	if(ortn) {
235		cssmPerror("SecTrustCreateWithCertificates", ortn);
236		ourRtn = 1;
237		goto errOut;
238	}
239
240	/* roots (anchors) are optional */
241	if(roots != NULL) {
242		ortn = SecTrustSetAnchorCertificates(trustRef, roots);
243		if(ortn) {
244			cssmPerror("SecTrustSetAnchorCertificates", ortn);
245			ourRtn = 1;
246			goto errOut;
247		}
248	}
249	if(actionFlags || forceActionFlags) {
250		memset(&actionData, 0, sizeof(actionData));
251		actionData.Version = CSSM_APPLE_TP_ACTION_VERSION;
252		actionData.ActionFlags = actionFlags;
253		cfActionData = CFDataCreate(NULL, (UInt8 *)&actionData, sizeof(actionData));
254		ortn = SecTrustSetParameters(trustRef, CSSM_TP_ACTION_DEFAULT, cfActionData);
255		if(ortn) {
256			cssmPerror("SecTrustSetParameters", ortn);
257			ourRtn = 1;
258			goto errOut;
259		}
260	}
261	if(keychains) {
262		ortn = SecTrustSetKeychains(trustRef, keychains);
263		if(ortn) {
264			cssmPerror("SecTrustSetKeychains", ortn);
265			ourRtn = 1;
266			goto errOut;
267		}
268	}
269
270	/* GO */
271	ortn = SecTrustEvaluate(trustRef, &resultType);
272	if(ortn) {
273		/* should never fail - error on this doesn't mean the cert verified badly */
274		cssmPerror("SecTrustEvaluate", ortn);
275		ourRtn = 1;
276		goto errOut;
277	}
278	switch(resultType) {
279		case kSecTrustResultUnspecified:
280			/* cert chain valid, no special UserTrust assignments */
281		case kSecTrustResultProceed:
282			/* cert chain valid AND user explicitly trusts this */
283			break;
284		case kSecTrustResultDeny:
285			if(!quiet) {
286				fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultDeny\n");
287			}
288			ourRtn = 1;
289			break;
290		case kSecTrustResultConfirm:
291			/*
292			 * Cert chain may well have verified OK, but user has flagged
293			 * one of these certs as untrustable.
294			 */
295			if(!quiet) {
296				fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultConfirm\n");
297			}
298			ourRtn = 1;
299			break;
300		default:
301			ourRtn = 1;
302			if(!quiet) {
303				/* See what the TP had to say about this */
304				ortn = SecTrustGetCssmResultCode(trustRef, &ocrtn);
305				if(ortn) {
306					cssmPerror("SecTrustGetCssmResultCode", ortn);
307				}
308				else  {
309					cssmPerror("Cert Verify Result", ocrtn);
310				}
311			}
312			break;
313	}
314
315	if((ourRtn == 0) & !quiet) {
316		printf("...certificate verification successful.\n");
317	}
318errOut:
319	/* cleanup */
320	CFRELEASE(certs);
321	CFRELEASE(roots);
322	CFRELEASE(keychains);
323	CFRELEASE(policyRef);
324	CFRELEASE(trustRef);
325	CFRELEASE(searchRef);
326	CFRELEASE(cfActionData);
327	return ourRtn;
328}
329