xref: /openbsd-src/regress/lib/libcrypto/x509/bettertls/verify.c (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
1 /* $OpenBSD: verify.c,v 1.8 2020/10/10 10:19:45 tb Exp $ */
2 /*
3  * Copyright (c) 2020 Joel Sing <jsing@openbsd.org>
4  * Copyright (c) 2020 Bob Beck <beck@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <err.h>
20 #include <string.h>
21 
22 #include <sys/stat.h>
23 
24 #include <openssl/bio.h>
25 #include <openssl/err.h>
26 #include <openssl/pem.h>
27 #include <openssl/x509.h>
28 #include <openssl/x509v3.h>
29 #include <openssl/x509_vfy.h>
30 
31 static int verbose = 0;
32 static int json = 0; /* print out json like bettertls expects resuls in */
33 
34 static int
35 passwd_cb(char *buf, int size, int rwflag, void *u)
36 {
37 	memset(buf, 0, size);
38 	return (0);
39 }
40 
41 static int
42 certs_from_file(const char *filename, STACK_OF(X509) **certs)
43 {
44 	STACK_OF(X509_INFO) *xis = NULL;
45 	STACK_OF(X509) *xs = NULL;
46 	BIO *bio = NULL;
47 	X509 *x;
48 	int i;
49 
50 	if ((xs = sk_X509_new_null()) == NULL)
51 		errx(1, "failed to create X509 stack");
52 	if ((bio = BIO_new_file(filename, "r")) == NULL) {
53 		ERR_print_errors_fp(stderr);
54 		errx(1, "failed to create bio");
55 	}
56 	if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL)
57 		errx(1, "failed to read PEM");
58 
59 	for (i = 0; i < sk_X509_INFO_num(xis); i++) {
60 		if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL)
61 			continue;
62 		if (!sk_X509_push(xs, x))
63 			errx(1, "failed to push X509");
64 		X509_up_ref(x);
65 	}
66 
67 	*certs = xs;
68 	xs = NULL;
69 
70 	sk_X509_INFO_pop_free(xis, X509_INFO_free);
71 	sk_X509_pop_free(xs, X509_free);
72 	BIO_free(bio);
73 
74 	return 1;
75 }
76 
77 static int
78 verify_cert_cb(int ok, X509_STORE_CTX *xsc)
79 {
80 	X509 *current_cert;
81 	int verify_err;
82 
83 	current_cert = X509_STORE_CTX_get_current_cert(xsc);
84 	if (current_cert != NULL) {
85 		X509_NAME_print_ex_fp(stderr,
86 		    X509_get_subject_name(current_cert), 0, XN_FLAG_ONELINE);
87 		fprintf(stderr, "\n");
88 	}
89 
90 	verify_err = X509_STORE_CTX_get_error(xsc);
91 	if (verify_err != X509_V_OK) {
92 		fprintf(stderr, "verify error at depth %d: %s\n",
93 		    X509_STORE_CTX_get_error_depth(xsc),
94 		    X509_verify_cert_error_string(verify_err));
95 	}
96 
97 	return ok;
98 }
99 
100 static void
101 verify_cert(X509_STORE *store, const char *roots_file, const char *bundle_file,
102     const char *cert_file, int *ip, int *dns)
103 {
104 	STACK_OF(X509) *roots = NULL, *bundle = NULL, *cert = NULL;
105 	X509_STORE_CTX *xsc = NULL;
106 	X509_STORE_CTX *xscip = NULL;
107 	X509_VERIFY_PARAM *param, *paramip;
108 	X509 *leaf = NULL;
109 	unsigned long flags, flagsip;
110 
111 	*ip = *dns = 0;
112 
113 	if (!certs_from_file(roots_file, &roots))
114 		errx(1, "failed to load roots from '%s'", roots_file);
115 	if (!certs_from_file(bundle_file, &bundle))
116 		errx(1, "failed to load bundle from '%s'", bundle_file);
117 	if (!certs_from_file(cert_file, &cert))
118 		errx(1, "failed to load cert from '%s'", cert_file);
119 	if (sk_X509_num(cert) < 1)
120 		errx(1, "no certs in cert bundle %s", cert_file);
121 	leaf = sk_X509_shift(cert);
122 
123 	if ((xsc = X509_STORE_CTX_new()) == NULL)
124 		errx(1, "X509_STORE_CTX");
125 
126 	if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) {
127 		ERR_print_errors_fp(stderr);
128 		errx(1, "failed to init store context");
129 	}
130 
131 	if (verbose)
132 		X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
133 
134 	if ((param = X509_STORE_CTX_get0_param(xsc)) == NULL) {
135 		ERR_print_errors_fp(stderr);
136 		errx(1, "failed to get verify parameters");
137 	}
138 	flags = X509_VERIFY_PARAM_get_flags(param);
139 	X509_VERIFY_PARAM_set_flags(param, flags);
140 	X509_VERIFY_PARAM_set_time(param, 1600000000);
141 	X509_VERIFY_PARAM_set1_host(param, "localhost.local",
142 	    strlen("localhost.local"));
143 
144 	X509_STORE_CTX_set0_trusted_stack(xsc, roots);
145 
146 	if (X509_verify_cert(xsc) == 1)
147 		*dns = 1;
148 
149 	if ((xscip = X509_STORE_CTX_new()) == NULL)
150 		errx(1, "X509_STORE_CTX");
151 
152 	if (!X509_STORE_CTX_init(xscip, store, leaf, bundle)) {
153 		ERR_print_errors_fp(stderr);
154 		errx(1, "failed to init store context");
155 	}
156 
157 	if (verbose)
158 		X509_STORE_CTX_set_verify_cb(xscip, verify_cert_cb);
159 
160 	if ((paramip = X509_STORE_CTX_get0_param(xscip)) == NULL) {
161 		ERR_print_errors_fp(stderr);
162 		errx(1, "failed to get verify parameters");
163 	}
164 	flagsip = X509_VERIFY_PARAM_get_flags(paramip);
165 	X509_VERIFY_PARAM_set_flags(paramip, flagsip);
166 	X509_VERIFY_PARAM_set_time(paramip, 1600000000);
167 	X509_VERIFY_PARAM_set1_ip_asc(paramip, "127.0.0.1");
168 
169 	X509_STORE_CTX_set0_trusted_stack(xscip, roots);
170 
171 	if (X509_verify_cert(xscip) == 1)
172 		*ip = 1;
173 
174 	sk_X509_pop_free(roots, X509_free);
175 	sk_X509_pop_free(bundle, X509_free);
176 	sk_X509_pop_free(cert, X509_free);
177 	X509_STORE_CTX_free(xsc);
178 	X509_STORE_CTX_free(xscip);
179 	X509_free(leaf);
180 }
181 
182 static void
183 bettertls_cert_test(const char *certs_path)
184 {
185 	X509_STORE *store;
186 	char *roots_file, *bundle_file, *cert_file;
187 	int i;
188 
189 	if ((store = X509_STORE_new()) == NULL)
190 		errx(1, "X509_STORE_new");
191 
192 	X509_STORE_set_default_paths(store);
193 
194 	if (asprintf(&roots_file, "%s/root.crt", certs_path) == -1)
195 		errx(1, "asprintf");
196 
197 	for(i = 1;; i++) {
198 		int ip, dns;
199 		struct stat sb;
200 		if (asprintf(&cert_file, "%s/%d.crt", certs_path, i) == -1)
201 			errx(1, "asprintf");
202 		if (asprintf(&bundle_file, "%s/%d.chain", certs_path, i) == -1)
203 			errx(1, "asprintf");
204 		if (stat(cert_file, &sb) == -1)
205 			break;
206 		if (stat(bundle_file, &sb) == -1)
207 			break;
208 		verify_cert(store, roots_file, bundle_file, cert_file, &ip, &dns);
209 		/* Mmm. json. with my avocado toast */
210 		if (i > 1 && json)
211 			fprintf(stdout, ",");
212 		if (json)
213 			fprintf(stdout, "{\"id\":%d,\"dnsResult\":%s,\""
214 			    "ipResult\":%s}", i, dns ? "true" : "false",
215 			    ip ? "true" : "false");
216 		else
217 			fprintf(stdout, "%d,%s,%s\n", i, dns ? "OK" : "ERROR",
218 			    ip ? "OK" : "ERROR");
219 		free(bundle_file);
220 		free(cert_file);
221 	}
222 	free(bundle_file);
223 	free(cert_file);
224 	free(roots_file);
225 	X509_STORE_free(store);
226 }
227 
228 int
229 main(int argc, char **argv)
230 {
231 	if (argc != 2) {
232 		fprintf(stderr, "usage: %s <certs_path>\n", argv[0]);
233 		exit(1);
234 	}
235 	if (json)
236 		fprintf(stdout, "{\"testVersion\":1,\"date\":%lld,\"userAgent\""
237 		    ":\"LibreSSL OpenBSD 6.8\\n\",\"results\":[", time(NULL));
238 
239 	bettertls_cert_test(argv[1]);
240 
241 	if (json)
242 		fprintf(stdout, "],\"osVersion\":\"OpenBSD 6.7\\n\"}\n");
243 
244 	return 0;
245 }
246