1/*
2 * Attempt to decode either one file, or every file in cwd,
3 * as a cert. Used to study vulnerability to NISCC cert DOS attacks.
4 */
5#include <Security/SecAsn1Coder.h>
6#include <Security/X509Templates.h>
7#include <security_cdsa_utils/cuFileIo.h>
8#include <sys/types.h>
9#include <dirent.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <strings.h>
13
14static void usage(char **argv)
15{
16	printf("usage: %s [-l(oop))] [certFile]\n", argv[0]);
17	exit(1);
18}
19
20/*
21 * Known file names to NOT parse
22 */
23static const char *skipTheseFiles[] =
24{
25	/* standard entries */
26	".",
27	"..",
28	"CVS",
29	".cvsignore",
30	/* the certs we know crash */
31	#if 0
32	"00000668",
33	"00000681",
34	"00001980",
35	"00002040",
36	"00002892",
37	"00007472",
38	"00008064",
39	"00008656",
40	"00009840",
41	"00010432",
42	"00011614",	// trouble somewhere in this neighborhood
43	"00011615",
44	"00011616",
45	#endif
46	NULL
47};
48
49/* returns false if specified fileName is in skipTheseFiles[] */
50static bool shouldWeParse(
51	const char *fileName)		// C string
52{
53	for(const char **stf=skipTheseFiles; *stf!=NULL; stf++) {
54		const char *tf = *stf;
55		if(!strcmp(fileName, *stf)) {
56			return false;
57		}
58	}
59	return true;
60}
61
62/*
63 * Just try to decode - if SecAsn1Decode returns, good 'nuff.
64 * Returns true if it does (i.e. ignore decode error; we're trying
65 * to detect a crash when the decoder should return an error).
66 */
67bool decodeCert(
68	const void *certData,
69	size_t certDataLen)
70{
71	SecAsn1CoderRef coder = NULL;
72	NSS_Certificate nssCert;
73	NSS_SignedCertOrCRL certOrCrl;
74
75	SecAsn1CoderCreate(&coder);
76
77	/* first the full decode */
78	memset(&nssCert, 0, sizeof(nssCert));
79	SecAsn1Decode(coder, certData, certDataLen,	kSecAsn1SignedCertTemplate, &nssCert);
80
81	/* now the "just TBS and sig" decode - this is actually harder
82	 * due to nested SEC_ASN1_SAVE ops */
83	memset(&certOrCrl, 0, sizeof(NSS_SignedCertOrCRL));
84	SecAsn1Decode(coder, certData, certDataLen,	kSecAsn1SignedCertOrCRLTemplate, &certOrCrl);
85
86	SecAsn1CoderRelease(coder);
87	return true;
88}
89
90int main(int argc, char **argv)
91{
92	bool quiet = false;
93	unsigned char *certData;
94	unsigned certDataLen;
95	bool loop = false;
96	int filearg = 1;
97
98	if(argc > 3 ) {
99		usage(argv);
100	}
101	if((argc > 1) && (argv[1][0] == '-')) {
102		switch(argv[1][1]) {
103			case 'l':
104				loop = true;
105				break;
106			default:
107				usage(argv);
108		}
109		filearg++;
110		argc--;
111	}
112	if(argc == 2) {
113		/* read & parse one file */
114		char *oneFile = argv[filearg];
115		if(readFile(oneFile, &certData, &certDataLen)) {
116			printf("\n***Error reading file %s. Aborting.\n", oneFile);
117			exit(1);
118		}
119		do {
120			if(!quiet) {
121				printf("...%s", oneFile);
122				fflush(stdout);
123			}
124			if(!decodeCert(certData, certDataLen)) {
125				printf("\n***GOT AN EXCEPTION ON %s\n", oneFile);
126				exit(1);
127			}
128		} while(loop);
129		free(certData);
130		exit(0);
131	}
132	DIR *dir = opendir(".");
133	if(dir == NULL) {
134		printf("Huh? Can't open . as a directory.\n");
135		exit(1);
136	}
137	struct dirent *de = readdir(dir);
138	while(de != NULL) {
139		char filename[MAXNAMLEN + 1];
140		memmove(filename, de->d_name, de->d_namlen);
141		filename[de->d_namlen] = '\0';
142		if(shouldWeParse(filename)) {
143			if(!quiet) {
144				printf("...%s", filename);
145				fflush(stdout);
146			}
147			if(readFile(filename, &certData, &certDataLen)) {
148				printf("\n***Error reading file %s. Aborting.\n", filename);
149				exit(1);
150			}
151			if(!decodeCert(certData, certDataLen)) {
152				printf("\n***GOT AN EXCEPTION ON %s\n", filename);
153				exit(1);
154			}
155			free(certData);
156		}
157		de = readdir(dir);
158	}
159	closedir(dir);
160	printf("\ncertDecode did not crash.\n");
161	return 0;
162}
163
164