1 /* $OpenBSD: verify.c,v 1.9 2020/10/26 12:11:47 beck 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
passwd_cb(char * buf,int size,int rwflag,void * u)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
certs_from_file(const char * filename,STACK_OF (X509)** certs)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
verify_cert_cb(int ok,X509_STORE_CTX * xsc)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
verify_cert(X509_STORE * store,const char * roots_file,const char * bundle_file,const char * cert_file,int * ip,int * dns)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 int verify_err;
111
112 *ip = *dns = 0;
113
114 if (!certs_from_file(roots_file, &roots))
115 errx(1, "failed to load roots from '%s'", roots_file);
116 if (!certs_from_file(bundle_file, &bundle))
117 errx(1, "failed to load bundle from '%s'", bundle_file);
118 if (!certs_from_file(cert_file, &cert))
119 errx(1, "failed to load cert from '%s'", cert_file);
120 if (sk_X509_num(cert) < 1)
121 errx(1, "no certs in cert bundle %s", cert_file);
122 leaf = sk_X509_shift(cert);
123
124 if ((xsc = X509_STORE_CTX_new()) == NULL)
125 errx(1, "X509_STORE_CTX");
126
127 if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) {
128 ERR_print_errors_fp(stderr);
129 errx(1, "failed to init store context");
130 }
131
132 if (verbose)
133 X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
134
135 if ((param = X509_STORE_CTX_get0_param(xsc)) == NULL) {
136 ERR_print_errors_fp(stderr);
137 errx(1, "failed to get verify parameters");
138 }
139 flags = X509_VERIFY_PARAM_get_flags(param);
140 X509_VERIFY_PARAM_set_flags(param, flags);
141 X509_VERIFY_PARAM_set_time(param, 1600000000);
142 X509_VERIFY_PARAM_set1_host(param, "localhost.local",
143 strlen("localhost.local"));
144
145 X509_STORE_CTX_set0_trusted_stack(xsc, roots);
146
147 if (X509_verify_cert(xsc) == 1)
148 *dns = 1;
149 verify_err = X509_STORE_CTX_get_error(xsc);
150 if (verify_err == X509_V_OK && *dns == 0) {
151 fprintf(stderr, "X509_V_OK on failure!\n");
152 *dns = 1;
153 }
154
155 if ((xscip = X509_STORE_CTX_new()) == NULL)
156 errx(1, "X509_STORE_CTX");
157
158 if (!X509_STORE_CTX_init(xscip, store, leaf, bundle)) {
159 ERR_print_errors_fp(stderr);
160 errx(1, "failed to init store context");
161 }
162
163 if (verbose)
164 X509_STORE_CTX_set_verify_cb(xscip, verify_cert_cb);
165
166 if ((paramip = X509_STORE_CTX_get0_param(xscip)) == NULL) {
167 ERR_print_errors_fp(stderr);
168 errx(1, "failed to get verify parameters");
169 }
170 flagsip = X509_VERIFY_PARAM_get_flags(paramip);
171 X509_VERIFY_PARAM_set_flags(paramip, flagsip);
172 X509_VERIFY_PARAM_set_time(paramip, 1600000000);
173 X509_VERIFY_PARAM_set1_ip_asc(paramip, "127.0.0.1");
174
175 X509_STORE_CTX_set0_trusted_stack(xscip, roots);
176
177 if (X509_verify_cert(xscip) == 1)
178 *ip = 1;
179 verify_err = X509_STORE_CTX_get_error(xscip);
180 if (verify_err == X509_V_OK && *ip == 0) {
181 fprintf(stderr, "X509_V_OK on failure!\n");
182 *ip = 1;
183 }
184
185 sk_X509_pop_free(roots, X509_free);
186 sk_X509_pop_free(bundle, X509_free);
187 sk_X509_pop_free(cert, X509_free);
188 X509_STORE_CTX_free(xsc);
189 X509_STORE_CTX_free(xscip);
190 X509_free(leaf);
191 }
192
193 static void
bettertls_cert_test(const char * certs_path)194 bettertls_cert_test(const char *certs_path)
195 {
196 X509_STORE *store;
197 char *roots_file, *bundle_file, *cert_file;
198 int i;
199
200 if ((store = X509_STORE_new()) == NULL)
201 errx(1, "X509_STORE_new");
202
203 X509_STORE_set_default_paths(store);
204
205 if (asprintf(&roots_file, "%s/root.crt", certs_path) == -1)
206 errx(1, "asprintf");
207
208 for(i = 1;; i++) {
209 int ip, dns;
210 struct stat sb;
211 if (asprintf(&cert_file, "%s/%d.crt", certs_path, i) == -1)
212 errx(1, "asprintf");
213 if (asprintf(&bundle_file, "%s/%d.chain", certs_path, i) == -1)
214 errx(1, "asprintf");
215 if (stat(cert_file, &sb) == -1)
216 break;
217 if (stat(bundle_file, &sb) == -1)
218 break;
219 verify_cert(store, roots_file, bundle_file, cert_file, &ip, &dns);
220 /* Mmm. json. with my avocado toast */
221 if (i > 1 && json)
222 fprintf(stdout, ",");
223 if (json)
224 fprintf(stdout, "{\"id\":%d,\"dnsResult\":%s,\""
225 "ipResult\":%s}", i, dns ? "true" : "false",
226 ip ? "true" : "false");
227 else
228 fprintf(stdout, "%d,%s,%s\n", i, dns ? "OK" : "ERROR",
229 ip ? "OK" : "ERROR");
230 free(bundle_file);
231 free(cert_file);
232 }
233 free(bundle_file);
234 free(cert_file);
235 free(roots_file);
236 X509_STORE_free(store);
237 }
238
239 int
main(int argc,char ** argv)240 main(int argc, char **argv)
241 {
242 if (argc != 2) {
243 fprintf(stderr, "usage: %s <certs_path>\n", argv[0]);
244 exit(1);
245 }
246 if (json)
247 fprintf(stdout, "{\"testVersion\":1,\"date\":%lld,\"userAgent\""
248 ":\"LibreSSL OpenBSD 6.8\\n\",\"results\":[", time(NULL));
249
250 bettertls_cert_test(argv[1]);
251
252 if (json)
253 fprintf(stdout, "],\"osVersion\":\"OpenBSD 6.7\\n\"}\n");
254
255 return 0;
256 }
257