/ SecurityTool / macOS / trusted_cert_utils.c
trusted_cert_utils.c
  1  /*
  2   * Copyright (c) 2003-2004,2006,2009-2019 Apple 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   * trusted_cert_utils.c
 24   */
 25  
 26  #include "trusted_cert_utils.h"
 27  #include "trusted_cert_ssl.h"
 28  #include "SecBase64.h"
 29  #include <Security/SecCertificatePriv.h>
 30  #include <Security/SecPolicyPriv.h>
 31  #include <Security/SecBasePriv.h>
 32  #include <Security/SecTrustSettings.h>
 33  #include <Security/cssmapple.h>
 34  #include <Security/oidsalg.h>
 35  #include <utilities/fileIo.h>
 36  #include <utilities/SecCFRelease.h>
 37  #include <security_cdsa_utils/cuPem.h>
 38  
 39  static int indentSize = 0;
 40  void indentIncr(void)	{ indentSize += 3; }
 41  void indentDecr(void)	{ indentSize -= 3; }
 42  
 43  void indent(void)
 44  {
 45  	int dex;
 46  	if(indentSize < 0) {
 47  		/* bug */
 48  		indentSize = 0;
 49  	}
 50  	for (dex=0; dex<indentSize; dex++) {
 51  		putchar(' ');
 52  	}
 53  }
 54  
 55  void printAscii(
 56  	const char *buf,
 57  	unsigned len,
 58  	unsigned maxLen)
 59  {
 60  	bool doEllipsis = false;
 61  	unsigned dex;
 62  	if(len > maxLen) {
 63  		len = maxLen;
 64  		doEllipsis = true;
 65  	}
 66  	for(dex=0; dex<len; dex++) {
 67  		char c = *buf++;
 68  		if(isalnum(c)) {
 69  			putchar(c);
 70  		}
 71  		else {
 72  			putchar('.');
 73  		}
 74  		fflush(stdout);
 75  	}
 76  	if(doEllipsis) {
 77  		printf("...etc.");
 78  	}
 79  }
 80  
 81  void printHex(
 82  	const unsigned char *buf,
 83  	unsigned len,
 84  	unsigned maxLen)
 85  {
 86  	bool doEllipsis = false;
 87  	unsigned dex;
 88  	if(len > maxLen) {
 89  		len = maxLen;
 90  		doEllipsis = true;
 91  	}
 92  	for(dex=0; dex<len; dex++) {
 93  		printf("%02X ", *buf++);
 94  	}
 95  	if(doEllipsis) {
 96  		printf("...etc.");
 97  	}
 98  }
 99  
100  /* print the contents of a CFString */
101  void printCfStr(
102  	CFStringRef cfstr)
103  {
104  	if(cfstr == NULL) {
105  		printf("<NULL>");
106  		return;
107  	}
108  	CFDataRef strData = CFStringCreateExternalRepresentation(NULL, cfstr,
109  		kCFStringEncodingUTF8, true);
110  	CFIndex dex;
111  
112  	if(strData == NULL) {
113  		printf("<<string decode error>>");
114  		return;
115  	}
116  	const char *cp = (const char *)CFDataGetBytePtr(strData);
117  	CFIndex len = CFDataGetLength(strData);
118  	for(dex=0; dex<len; dex++) {
119  		if (*cp == '\n' || *cp == '\r') {
120  			printf("\n"); /* handle line breaks */
121  			cp++;
122  		} else {
123  			putchar(*cp++);
124  		}
125  	}
126  	CFRelease(strData);
127  }
128  
129  /* print a CFDateRef */
130  static const char *months[12] = {
131  	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
132  	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
133  };
134  
135  void printCFDate(
136  	CFDateRef dateRef)
137  {
138  	CFAbsoluteTime absTime = CFDateGetAbsoluteTime(dateRef);
139  	if(absTime == 0.0) {
140  		printf("<<Malformed CFDateRef>>\n");
141  		return;
142  	}
143  	CFGregorianDate gregDate = CFAbsoluteTimeGetGregorianDate(absTime, NULL);
144  	const char *month = "Unknown";
145  	if((gregDate.month > 12) || (gregDate.month <= 0)) {
146  		printf("Huh? GregDate.month > 11. These amps only GO to 11.\n");
147  	}
148  	else {
149  		month = months[gregDate.month - 1];
150  	}
151  	printf("%s %d, %d %02d:%02d",
152  		month, gregDate.day, (int)gregDate.year, gregDate.hour, gregDate.minute);
153  }
154  
155  /* print a CFNumber */
156  void printCfNumber(
157  	CFNumberRef cfNum)
158  {
159  	SInt32 s;
160  	if(!CFNumberGetValue(cfNum, kCFNumberSInt32Type, &s)) {
161  		printf("***CFNumber overflow***");
162  		return;
163  	}
164  	printf("%d", (int)s);
165  }
166  
167  /* print a CFNumber as a SecTrustSettingsResult */
168  void printResultType(
169  	CFNumberRef cfNum)
170  {
171  	SInt32 n;
172  	if(!CFNumberGetValue(cfNum, kCFNumberSInt32Type, &n)) {
173  		printf("***CFNumber overflow***");
174  		return;
175  	}
176  	const char *s;
177  	char bogus[100];
178  	switch(n) {
179  		case kSecTrustSettingsResultInvalid: s = "kSecTrustSettingsResultInvalid"; break;
180  		case kSecTrustSettingsResultTrustRoot: s = "kSecTrustSettingsResultTrustRoot"; break;
181  		case kSecTrustSettingsResultTrustAsRoot: s = "kSecTrustSettingsResultTrustAsRoot"; break;
182  		case kSecTrustSettingsResultDeny:    s = "kSecTrustSettingsResultDeny"; break;
183  		case kSecTrustSettingsResultUnspecified: s = "kSecTrustSettingsResultUnspecified"; break;
184  		default:
185  			sprintf(bogus, "Unknown SecTrustSettingsResult (%d)", (int)n);
186  			s = bogus;
187  			break;
188  	}
189  	printf("%s", s);
190  }
191  
192  /* print a CFNumber as SecTrustSettingsKeyUsage */
193  void printKeyUsage(
194  	CFNumberRef cfNum)
195  {
196  	SInt32 s;
197  	if(!CFNumberGetValue(cfNum, kCFNumberSInt32Type, &s)) {
198  		printf("***CFNumber overflow***");
199  		return;
200  	}
201  	uint32 n = (uint32)s;
202  	if(n == kSecTrustSettingsKeyUseAny) {
203  		printf("<any>");
204  		return;
205  	}
206  	else if(n == 0) {
207  		printf("<none>");
208  		return;
209  	}
210  	printf("< ");
211  	if(n & kSecTrustSettingsKeyUseSignature) {
212  		printf("Signature ");
213  	}
214  	if(n & kSecTrustSettingsKeyUseEnDecryptData) {
215  		printf("EnDecryptData ");
216  	}
217  	if(n & kSecTrustSettingsKeyUseEnDecryptKey) {
218  		printf("EnDecryptKey ");
219  	}
220  	if(n & kSecTrustSettingsKeyUseSignCert) {
221  		printf("SignCert ");
222  	}
223  	if(n & kSecTrustSettingsKeyUseSignRevocation) {
224  		printf("SignRevocation ");
225  	}
226  	if(n & kSecTrustSettingsKeyUseKeyExchange) {
227  		printf("KeyExchange ");
228  	}
229  	printf(" >");
230  }
231  
232  /* print a CFNumber as CSSM_RETURN string */
233  void printCssmErr(
234  	CFNumberRef cfNum)
235  {
236  	SInt32 s;
237  	if(!CFNumberGetValue(cfNum, kCFNumberSInt32Type, &s)) {
238  		printf("***CFNumber overflow***");
239  		return;
240  	}
241  	printf("%s", cssmErrorString((CSSM_RETURN)s));
242  }
243  
244  /* convert an OID to a SecPolicyRef */
245  SecPolicyRef oidToPolicy(
246  	const CSSM_OID *oid)
247  {
248  	OSStatus ortn;
249  	SecPolicyRef policyRef = NULL;
250  
251  	ortn = SecPolicyCopy(CSSM_CERT_X_509v3, oid, &policyRef);
252  	if(ortn) {
253  		cssmPerror("SecPolicyCopy", ortn);
254  		return NULL;
255  	}
256  	return policyRef;
257  }
258  
259  typedef struct {
260  	const CSSM_OID *oid;
261  	const char *oidStr;
262  } OidString;
263  
264  static OidString oidStrings[] =
265  {
266  	{ &CSSMOID_APPLE_ISIGN, "iSign" },
267  	{ &CSSMOID_APPLE_X509_BASIC, "Apple X509 Basic" },
268  	{ &CSSMOID_APPLE_TP_SSL, "SSL" },
269  	{ &CSSMOID_APPLE_TP_SMIME, "SMIME" },
270  	{ &CSSMOID_APPLE_TP_EAP, "EAP" },
271  	{ &CSSMOID_APPLE_TP_SW_UPDATE_SIGNING, "SW Update Signing" },
272  	{ &CSSMOID_APPLE_TP_IP_SEC, "IPSec" },
273  	{ &CSSMOID_APPLE_TP_ICHAT, "iChat" },
274  	{ &CSSMOID_APPLE_TP_RESOURCE_SIGN, "Resource Signing" },
275  	{ &CSSMOID_APPLE_TP_PKINIT_CLIENT, "PKINIT Client" },
276  	{ &CSSMOID_APPLE_TP_PKINIT_SERVER, "PKINIT Server" },
277  	{ &CSSMOID_APPLE_TP_CODE_SIGNING, "Code Signing" },
278  	{ &CSSMOID_APPLE_TP_PACKAGE_SIGNING, "Package Signing" },
279  	{ &CSSMOID_APPLE_TP_MACAPPSTORE_RECEIPT, "Mac App Store" },
280  	{ &CSSMOID_APPLE_TP_APPLEID_SHARING, "AppleID Sharing" }
281  };
282  #define NUM_OID_STRINGS		(sizeof(oidStrings) / sizeof(oidStrings[0]))
283  
284  /* convert a policy string to a SecPolicyRef */
285  SecPolicyRef oidStringToPolicy(
286  	const char *oidStr)
287  {
288  	/* OID string to an OID pointer */
289  	const CSSM_OID *oid = NULL;
290  	unsigned dex;
291  
292  	for(dex=0; dex<NUM_OID_STRINGS; dex++) {
293  		OidString *os = &oidStrings[dex];
294  		if(!strcmp(oidStr, os->oidStr)) {
295  			oid = os->oid;
296  			break;
297  		}
298  	}
299  	if(oid == NULL) {
300  		return NULL;
301  	}
302  
303  	/* OID to SecPolicyRef */
304  	return oidToPolicy(oid);
305  }
306  
307  /* CSSM_OID --> OID string */
308  const char *oidToOidString(
309  	const CSSM_OID *oid)
310  {
311  	unsigned dex;
312  	static char unknownOidString[200];
313  
314  	for(dex=0; dex<NUM_OID_STRINGS; dex++) {
315  		OidString *os = &oidStrings[dex];
316  		if(compareOids(oid, os->oid)) {
317  			return os->oidStr;
318  		}
319  	}
320  	sprintf(unknownOidString, "Unknown OID length %ld, value { ", oid->Length);
321  	for(dex=0; dex<oid->Length; dex++) {
322  		char tmp[6];
323  		sprintf(tmp, "%02X ", oid->Data[dex]);
324  		strcat(unknownOidString, tmp);
325  	}
326  	strcat(unknownOidString, " }");
327  	return unknownOidString;
328  }
329  
330  /* compare OIDs; returns 1 if identical, else returns 0 */
331  int compareOids(
332  	const CSSM_OID *oid1,
333  	const CSSM_OID *oid2)
334  {
335  	if((oid1 == NULL) || (oid2 == NULL)) {
336  		return 0;
337  	}
338  	if(oid1->Length != oid2->Length) {
339  		return 0;
340  	}
341  	if(memcmp(oid1->Data, oid2->Data, oid1->Length)) {
342  		return 0;
343  	}
344  	return 1;
345  }
346  
347  /* app path string to SecTrustedApplicationRef */
348  SecTrustedApplicationRef appPathToAppRef(
349  	const char *appPath)
350  {
351  	SecTrustedApplicationRef appRef = NULL;
352  	OSStatus ortn;
353  
354  	if(appPath == NULL) {
355  		return NULL;
356  	}
357  	ortn = SecTrustedApplicationCreateFromPath(appPath, &appRef);
358  	if(ortn) {
359  		cssmPerror("SecTrustedApplicationCreateFromPath", ortn);
360  		return NULL;
361  	}
362  	return appRef;
363  }
364  
365  int readCertFile(
366  	const char *fileName,
367  	SecCertificateRef *certRef)
368  {
369  	unsigned char *cp = NULL;
370  	size_t len = 0;
371  	CSSM_DATA certData;
372  	OSStatus ortn;
373  	unsigned char *decoded = NULL;
374  	unsigned decodedLen = 0;
375  
376  	if(readFileSizet(fileName, &cp, &len)) {
377  		printf("***Error reading file %s\n", fileName);
378  		return -1;
379  	}
380  	if(isPem(cp, (unsigned) len)) {
381  		if(pemDecode(cp, (unsigned) len, &decoded, &decodedLen)) {
382  			fprintf(stderr, "Error decoding cert file %s\n", fileName);
383  			return -1;
384  		}
385  		certData.Length = decodedLen;
386  		certData.Data = decoded;
387  	}
388  	else {
389  		certData.Length = len;
390  		certData.Data = cp;
391  	}
392  	ortn = SecCertificateCreateFromData(&certData,
393  			CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, certRef);
394  	free(cp);
395  	if(decoded) {
396  		free(decoded);
397  	}
398  	if(ortn) {
399  		cssmPerror("SecCertificateCreateFromData", ortn);
400  		return -1;
401  	}
402  	return 0;
403  }
404  
405  /* policy string --> CSSM_OID */
406  const CSSM_OID *policyStringToOid(
407  	const char *policy,
408  	bool *useTLS)
409  {
410  	if(!policy || !useTLS) {
411  		return NULL;
412  	}
413  	if(!strcmp(policy, "ssl")) {
414  		*useTLS = true;
415  		return &CSSMOID_APPLE_TP_SSL;
416  	}
417  	else if(!strcmp(policy, "smime")) {
418  		return &CSSMOID_APPLE_TP_SMIME;
419  	}
420  	else if(!strcmp(policy, "codeSign")) {
421  		return &CSSMOID_APPLE_TP_CODE_SIGNING;
422  	}
423  	else if(!strcmp(policy, "IPSec")) {
424  		*useTLS = true;
425  		return &CSSMOID_APPLE_TP_IP_SEC;
426  	}
427  	else if(!strcmp(policy, "basic")) {
428  		return &CSSMOID_APPLE_X509_BASIC;
429  	}
430  	else if(!strcmp(policy, "swUpdate")) {
431  		return &CSSMOID_APPLE_TP_SW_UPDATE_SIGNING;
432  	}
433  	else if(!strcmp(policy, "pkgSign")) {
434  		return &CSSMOID_APPLE_TP_PACKAGE_SIGNING;
435  	}
436  	else if(!strcmp(policy, "eap")) {
437  		*useTLS = true;
438  		return &CSSMOID_APPLE_TP_EAP;
439  	}
440  	else if(!strcmp(policy, "macappstore")) {
441  		return &CSSMOID_APPLE_TP_MACAPPSTORE_RECEIPT;
442  	}
443  	else if(!strcmp(policy, "appleID")) {
444  		return &CSSMOID_APPLE_TP_APPLEID_SHARING;
445  	}
446  	else if(!strcmp(policy, "timestamping")) {
447  		return &CSSMOID_APPLE_TP_TIMESTAMPING;
448  	}
449  	else {
450  		fprintf(stderr, "***unknown policy spec (%s)\n", policy);
451  		return NULL;
452  	}
453  }
454  
455  CFOptionFlags revCheckOptionStringToFlags(
456  	const char *revCheckOption)
457  {
458  	CFOptionFlags result = 0;
459  	if(revCheckOption == NULL) {
460  		return result;
461  	}
462  	else if(!strcmp(revCheckOption, "ocsp")) {
463  		result |= kSecRevocationOCSPMethod;
464  	}
465  	else if(!strcmp(revCheckOption, "crl")) {
466  		result |= kSecRevocationCRLMethod;
467  	}
468  	else if(!strcmp(revCheckOption, "require")) {
469  		result |= kSecRevocationRequirePositiveResponse;
470  	}
471  	else if(!strcmp(revCheckOption, "offline")) {
472  		result |= kSecRevocationNetworkAccessDisabled;
473  	}
474  	else if(!strcmp(revCheckOption, "online")) {
475  		result |= kSecRevocationOnlineCheck;
476  	}
477  	return result;
478  }
479  
480  static size_t print_buffer_pem(FILE *stream,
481  	const char *pem_name, size_t length, const uint8_t *bytes)
482  {
483  	size_t pem_name_len = strlen(pem_name);
484  	size_t b64_len = SecBase64Encode2(NULL, length, NULL, 0,
485  			kSecB64_F_LINE_LEN_USE_PARAM, 64, NULL);
486  	char *buffer = malloc(33 + 2 * pem_name_len + b64_len);
487  	char *p = buffer;
488  	p += sprintf(buffer, "-----BEGIN %s-----\n", pem_name);
489  	SecBase64Result result;
490  	p += SecBase64Encode2(bytes, length, p, b64_len,\
491  			kSecB64_F_LINE_LEN_USE_PARAM, 64, &result);
492  	if (result) {
493  		free(buffer);
494  		return result;
495  	}
496  	p += sprintf(p, "\n-----END %s-----\n", pem_name);
497  	size_t res = fwrite(buffer, 1, p - buffer, stream);
498  	fflush(stream);
499  	bzero(buffer, p - buffer);
500  	free(buffer);
501  	return res;
502  }
503  
504  void printCertLabel(SecCertificateRef certRef)
505  {
506  	CFStringRef label = SecCertificateCopySubjectSummary(certRef);
507  	printCfStr(label);
508  	CFReleaseSafe(label);
509  }
510  
511  void printCertDescription(SecCertificateRef certRef)
512  {
513  	CFStringRef description = CFCopyDescription((CFTypeRef)certRef);
514  	printCfStr(description);
515  	CFReleaseSafe(description);
516  }
517  
518  void printCertText(SecCertificateRef certRef)
519  {
520  	CFStringRef certStr = CopyCertificateTextRepresentation(certRef);
521  	printf("\n");
522  	printCfStr(certStr);
523  	printf("\n\n");
524  	CFReleaseSafe(certStr);
525  }
526  
527  void printCertChain(SecTrustRef trustRef, bool printPem, bool printText)
528  {
529  	CFIndex idx, count = SecTrustGetCertificateCount(trustRef);
530  	for (idx = 0; idx < count; idx++) {
531  		SecCertificateRef cert = SecTrustGetCertificateAtIndex(trustRef, idx);
532  		fprintf(stdout, " %ld: ", idx);
533  		printCertLabel(cert);
534  		fprintf(stdout, "\n    ");
535  		if (!cert) { continue; }
536  		printCertDescription(cert);
537  		fprintf(stdout, "\n");
538  		if (printText) {
539  			printCertText(cert);
540  		}
541  		if (printPem) {
542  			print_buffer_pem(stdout, "CERTIFICATE",
543  					SecCertificateGetLength(cert),
544  					SecCertificateGetBytePtr(cert));
545  		}
546  	}
547  }
548