summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/usr.sbin/ocspcheck/ocspcheck.c158
1 files changed, 95 insertions, 63 deletions
diff --git a/src/usr.sbin/ocspcheck/ocspcheck.c b/src/usr.sbin/ocspcheck/ocspcheck.c
index d9f7104bec..dec548e0b2 100644
--- a/src/usr.sbin/ocspcheck/ocspcheck.c
+++ b/src/usr.sbin/ocspcheck/ocspcheck.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: ocspcheck.c,v 1.27 2020/09/04 04:17:46 tb Exp $ */ 1/* $OpenBSD: ocspcheck.c,v 1.28 2020/10/16 01:16:55 beck Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2017 Bob Beck <beck@openbsd.org> 4 * Copyright (c) 2017,2020 Bob Beck <beck@openbsd.org>
5 * 5 *
6 * Permission to use, copy, modify, and distribute this software for any 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 7 * purpose with or without fee is hereby granted, provided that the above
@@ -155,7 +155,7 @@ url2host(const char *host, short *port, char **path)
155 *path = strdup(ep); 155 *path = strdup(ep);
156 *ep = '\0'; 156 *ep = '\0';
157 } else 157 } else
158 *path = strdup(""); 158 *path = strdup("/");
159 159
160 if (*path == NULL) { 160 if (*path == NULL) {
161 warn("strdup"); 161 warn("strdup");
@@ -163,6 +163,21 @@ url2host(const char *host, short *port, char **path)
163 return (NULL); 163 return (NULL);
164 } 164 }
165 165
166 /* Check to see if there is a port in the url */
167 if ((ep = strchr(url, ':')) != NULL) {
168 const char *errstr;
169 short pp;
170 pp = strtonum(ep + 1, 1, SHRT_MAX, &errstr);
171 if (errstr != NULL) {
172 warnx("error parsing port from '%s': %s", url, errstr);
173 free(url);
174 free(*path);
175 return NULL;
176 }
177 *port = pp;
178 *ep = '\0';
179 }
180
166 return (url); 181 return (url);
167} 182}
168 183
@@ -221,7 +236,7 @@ read_cacerts(const char *file, const char *dir)
221 } 236 }
222 return store; 237 return store;
223 238
224end: 239 end:
225 X509_STORE_free(store); 240 X509_STORE_free(store);
226 return NULL; 241 return NULL;
227} 242}
@@ -239,14 +254,12 @@ read_fullchain(const char *file, int *count)
239 254
240 if ((bio = BIO_new_file(file, "r")) == NULL) { 255 if ((bio = BIO_new_file(file, "r")) == NULL) {
241 warn("Unable to read a certificate from %s", file); 256 warn("Unable to read a certificate from %s", file);
242 return NULL; 257 goto end;
243 } 258 }
244 if ((xis = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL)) == NULL) { 259 if ((xis = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL)) == NULL) {
245 warnx("Unable to read PEM format from %s", file); 260 warnx("Unable to read PEM format from %s", file);
246 return NULL; 261 goto end;
247 } 262 }
248 BIO_free(bio);
249
250 if (sk_X509_INFO_num(xis) <= 0) { 263 if (sk_X509_INFO_num(xis) <= 0) {
251 warnx("No certificates in file %s", file); 264 warnx("No certificates in file %s", file);
252 goto end; 265 goto end;
@@ -269,7 +282,8 @@ read_fullchain(const char *file, int *count)
269 xi->x509 = NULL; 282 xi->x509 = NULL;
270 (*count)++; 283 (*count)++;
271 } 284 }
272end: 285 end:
286 BIO_free(bio);
273 sk_X509_INFO_pop_free(xis, X509_INFO_free); 287 sk_X509_INFO_pop_free(xis, X509_INFO_free);
274 return rv; 288 return rv;
275} 289}
@@ -280,38 +294,37 @@ cert_from_chain(STACK_OF(X509) *fullchain)
280 return sk_X509_value(fullchain, 0); 294 return sk_X509_value(fullchain, 0);
281} 295}
282 296
283static X509 * 297static const X509 *
284issuer_from_chain(STACK_OF(X509) *fullchain) 298issuer_from_chain(STACK_OF(X509) *fullchain)
285{ 299{
286 X509 *cert, *issuer; 300 const X509 *cert;
287 X509_NAME *issuer_name; 301 X509_NAME *issuer_name;
288 302
289 cert = cert_from_chain(fullchain); 303 cert = cert_from_chain(fullchain);
290 if ((issuer_name = X509_get_issuer_name(cert)) == NULL) 304 if ((issuer_name = X509_get_issuer_name(cert)) == NULL)
291 return NULL; 305 return NULL;
292 306
293 issuer = X509_find_by_subject(fullchain, issuer_name); 307 return X509_find_by_subject(fullchain, issuer_name);
294 return issuer;
295} 308}
296 309
297static ocsp_request * 310static ocsp_request *
298ocsp_request_new_from_cert(const char *cadir, char *file, int nonce) 311ocsp_request_new_from_cert(const char *cadir, char *file, int nonce)
299{ 312{
300 X509 *cert = NULL; 313 X509 *cert;
301 int count = 0; 314 int count = 0;
302 OCSP_CERTID *id; 315 OCSP_CERTID *id = NULL;
303 ocsp_request *request; 316 ocsp_request *request = NULL;
304 const EVP_MD *cert_id_md = NULL; 317 const EVP_MD *cert_id_md = NULL;
305 X509 *issuer = NULL; 318 const X509 *issuer;
306 STACK_OF(OPENSSL_STRING) *urls; 319 STACK_OF(OPENSSL_STRING) *urls = NULL;
307 320
308 if ((request = calloc(1, sizeof(ocsp_request))) == NULL) { 321 if ((request = calloc(1, sizeof(ocsp_request))) == NULL) {
309 warn("malloc"); 322 warn("malloc");
310 return NULL; 323 goto err;
311 } 324 }
312 325
313 if ((request->req = OCSP_REQUEST_new()) == NULL) 326 if ((request->req = OCSP_REQUEST_new()) == NULL)
314 return NULL; 327 goto err;
315 328
316 request->fullchain = read_fullchain(file, &count); 329 request->fullchain = read_fullchain(file, &count);
317 if (cadir == NULL) { 330 if (cadir == NULL) {
@@ -319,39 +332,43 @@ ocsp_request_new_from_cert(const char *cadir, char *file, int nonce)
319 if (pledge("stdio inet dns", NULL) == -1) 332 if (pledge("stdio inet dns", NULL) == -1)
320 err(1, "pledge"); 333 err(1, "pledge");
321 } 334 }
322 if (request->fullchain == NULL) 335 if (request->fullchain == NULL) {
323 return NULL; 336 warnx("Unable to read cert chain from file %s", file);
337 goto err;
338 }
324 if (count <= 1) { 339 if (count <= 1) {
325 warnx("File %s does not contain a cert chain", file); 340 warnx("File %s does not contain a cert chain", file);
326 return NULL; 341 goto err;
327 } 342 }
328 if ((cert = cert_from_chain(request->fullchain)) == NULL) { 343 if ((cert = cert_from_chain(request->fullchain)) == NULL) {
329 warnx("No certificate found in %s", file); 344 warnx("No certificate found in %s", file);
330 return NULL; 345 goto err;
331 } 346 }
332 if ((issuer = issuer_from_chain(request->fullchain)) == NULL) { 347 if ((issuer = issuer_from_chain(request->fullchain)) == NULL) {
333 warnx("Unable to find issuer for cert in %s", file); 348 warnx("Unable to find issuer for cert in %s", file);
334 return NULL; 349 goto err;
335 } 350 }
336 351
337 urls = X509_get1_ocsp(cert); 352 urls = X509_get1_ocsp(cert);
338 if (urls == NULL || sk_OPENSSL_STRING_num(urls) <= 0) { 353 if (urls == NULL || sk_OPENSSL_STRING_num(urls) <= 0) {
339 warnx("Certificate in %s contains no OCSP url", file); 354 warnx("Certificate in %s contains no OCSP url", file);
340 return NULL; 355 goto err;
341 } 356 }
342 if ((request->url = strdup(sk_OPENSSL_STRING_value(urls, 0))) == NULL) 357 if ((request->url = strdup(sk_OPENSSL_STRING_value(urls, 0))) == NULL)
343 return NULL; 358 goto err;
344 X509_email_free(urls); 359 X509_email_free(urls);
360 urls = NULL;
345 361
346 cert_id_md = EVP_sha1(); /* XXX. This sucks but OCSP is poopy */ 362 cert_id_md = EVP_sha1(); /* XXX. This sucks but OCSP is poopy */
347 if ((id = OCSP_cert_to_id(cert_id_md, cert, issuer)) == NULL) { 363 if ((id = OCSP_cert_to_id(cert_id_md, cert, issuer)) == NULL) {
348 warnx("Unable to get certificate id from cert in %s", file); 364 warnx("Unable to get certificate id from cert in %s", file);
349 return NULL; 365 goto err;
350 } 366 }
351 if (OCSP_request_add0_id(request->req, id) == NULL) { 367 if (OCSP_request_add0_id(request->req, id) == NULL) {
352 warnx("Unable to add certificate id to request"); 368 warnx("Unable to add certificate id to request");
353 return NULL; 369 goto err;
354 } 370 }
371 id = NULL;
355 372
356 request->nonce = nonce; 373 request->nonce = nonce;
357 if (request->nonce) 374 if (request->nonce)
@@ -360,13 +377,25 @@ ocsp_request_new_from_cert(const char *cadir, char *file, int nonce)
360 if ((request->size = i2d_OCSP_REQUEST(request->req, 377 if ((request->size = i2d_OCSP_REQUEST(request->req,
361 &request->data)) <= 0) { 378 &request->data)) <= 0) {
362 warnx("Unable to encode ocsp request"); 379 warnx("Unable to encode ocsp request");
363 return NULL; 380 goto err;
364 } 381 }
365 if (request->data == NULL) { 382 if (request->data == NULL) {
366 warnx("Unable to allocte memory"); 383 warnx("Unable to allocte memory");
367 return NULL; 384 goto err;
385 }
386 return request;
387
388 err:
389 if (request != NULL) {
390 sk_X509_pop_free(request->fullchain, X509_free);
391 free(request->url);
392 OCSP_REQUEST_free(request->req);
393 free(request->data);
368 } 394 }
369 return (request); 395 X509_email_free(urls);
396 OCSP_CERTID_free(id);
397 free(request);
398 return NULL;
370} 399}
371 400
372 401
@@ -376,40 +405,41 @@ validate_response(char *buf, size_t size, ocsp_request *request,
376{ 405{
377 ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL; 406 ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL;
378 const unsigned char **p = (const unsigned char **)&buf; 407 const unsigned char **p = (const unsigned char **)&buf;
379 int status, cert_status=0, crl_reason=0; 408 int status, cert_status = 0, crl_reason = 0;
380 time_t now, rev_t = -1, this_t, next_t; 409 time_t now, rev_t = -1, this_t, next_t;
381 OCSP_RESPONSE *resp; 410 OCSP_RESPONSE *resp = NULL;
382 OCSP_BASICRESP *bresp; 411 OCSP_BASICRESP *bresp = NULL;
383 OCSP_CERTID *cid; 412 OCSP_CERTID *cid = NULL;
384 X509 *cert, *issuer; 413 const X509 *cert, *issuer;
414 int ret = 0;
385 415
386 if ((cert = cert_from_chain(request->fullchain)) == NULL) { 416 if ((cert = cert_from_chain(request->fullchain)) == NULL) {
387 warnx("No certificate found in %s", file); 417 warnx("No certificate found in %s", file);
388 return 0; 418 goto err;
389 } 419 }
390 if ((issuer = issuer_from_chain(request->fullchain)) == NULL) { 420 if ((issuer = issuer_from_chain(request->fullchain)) == NULL) {
391 warnx("Unable to find certificate issuer for cert in %s", file); 421 warnx("Unable to find certificate issuer for cert in %s", file);
392 return 0; 422 goto err;
393 } 423 }
394 if ((cid = OCSP_cert_to_id(NULL, cert, issuer)) == NULL) { 424 if ((cid = OCSP_cert_to_id(NULL, cert, issuer)) == NULL) {
395 warnx("Unable to get issuer cert/CID in %s", file); 425 warnx("Unable to get issuer cert/CID in %s", file);
396 return 0; 426 goto err;
397 } 427 }
398 428
399 if ((resp = d2i_OCSP_RESPONSE(NULL, p, size)) == NULL) { 429 if ((resp = d2i_OCSP_RESPONSE(NULL, p, size)) == NULL) {
400 warnx("OCSP response unserializable from host %s", host); 430 warnx("OCSP response unserializable from host %s", host);
401 return 0; 431 goto err;
402 } 432 }
403 433
404 if ((bresp = OCSP_response_get1_basic(resp)) == NULL) { 434 if ((bresp = OCSP_response_get1_basic(resp)) == NULL) {
405 warnx("Failed to load OCSP response from %s", host); 435 warnx("Failed to load OCSP response from %s", host);
406 return 0; 436 goto err;
407 } 437 }
408 438
409 if (OCSP_basic_verify(bresp, request->fullchain, store, 439 if (OCSP_basic_verify(bresp, request->fullchain, store,
410 OCSP_TRUSTOTHER) != 1) { 440 OCSP_TRUSTOTHER) != 1) {
411 warnx("OCSP verify failed from %s", host); 441 warnx("OCSP verify failed from %s", host);
412 return 0; 442 goto err;
413 } 443 }
414 dspew("OCSP response signature validated from %s\n", host); 444 dspew("OCSP response signature validated from %s\n", host);
415 445
@@ -417,7 +447,7 @@ validate_response(char *buf, size_t size, ocsp_request *request,
417 if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { 447 if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
418 warnx("OCSP Failure: code %d (%s) from host %s", 448 warnx("OCSP Failure: code %d (%s) from host %s",
419 status, OCSP_response_status_str(status), host); 449 status, OCSP_response_status_str(status), host);
420 return 0; 450 goto err;
421 } 451 }
422 dspew("OCSP response status %d from host %s\n", status, host); 452 dspew("OCSP response status %d from host %s\n", status, host);
423 453
@@ -426,19 +456,19 @@ validate_response(char *buf, size_t size, ocsp_request *request,
426 if (request->nonce) { 456 if (request->nonce) {
427 if (OCSP_check_nonce(request->req, bresp) <= 0) { 457 if (OCSP_check_nonce(request->req, bresp) <= 0) {
428 warnx("No OCSP nonce, or mismatch, from host %s", host); 458 warnx("No OCSP nonce, or mismatch, from host %s", host);
429 return 0; 459 goto err;
430 } 460 }
431 } 461 }
432 462
433 if (OCSP_resp_find_status(bresp, cid, &cert_status, &crl_reason, 463 if (OCSP_resp_find_status(bresp, cid, &cert_status, &crl_reason,
434 &revtime, &thisupd, &nextupd) != 1) { 464 &revtime, &thisupd, &nextupd) != 1) {
435 warnx("OCSP verify failed: no result for cert"); 465 warnx("OCSP verify failed: no result for cert");
436 return 0; 466 goto err;
437 } 467 }
438 468
439 if (revtime && (rev_t = parse_ocsp_time(revtime)) == -1) { 469 if (revtime && (rev_t = parse_ocsp_time(revtime)) == -1) {
440 warnx("Unable to parse revocation time in OCSP reply"); 470 warnx("Unable to parse revocation time in OCSP reply");
441 return 0; 471 goto err;
442 } 472 }
443 /* 473 /*
444 * Belt and suspenders, Treat it as revoked if there is either 474 * Belt and suspenders, Treat it as revoked if there is either
@@ -448,21 +478,21 @@ validate_response(char *buf, size_t size, ocsp_request *request,
448 warnx("Invalid OCSP reply: certificate is revoked"); 478 warnx("Invalid OCSP reply: certificate is revoked");
449 if (rev_t != -1) 479 if (rev_t != -1)
450 warnx("Certificate revoked at: %s", ctime(&rev_t)); 480 warnx("Certificate revoked at: %s", ctime(&rev_t));
451 return 0; 481 goto err;
452 } 482 }
453 if ((this_t = parse_ocsp_time(thisupd)) == -1) { 483 if ((this_t = parse_ocsp_time(thisupd)) == -1) {
454 warnx("unable to parse this update time in OCSP reply"); 484 warnx("unable to parse this update time in OCSP reply");
455 return 0; 485 goto err;
456 } 486 }
457 if ((next_t = parse_ocsp_time(nextupd)) == -1) { 487 if ((next_t = parse_ocsp_time(nextupd)) == -1) {
458 warnx("unable to parse next update time in OCSP reply"); 488 warnx("unable to parse next update time in OCSP reply");
459 return 0; 489 goto err;
460 } 490 }
461 491
462 /* Don't allow this update to precede next update */ 492 /* Don't allow this update to precede next update */
463 if (this_t >= next_t) { 493 if (this_t >= next_t) {
464 warnx("Invalid OCSP reply: this update >= next update"); 494 warnx("Invalid OCSP reply: this update >= next update");
465 return 0; 495 goto err;
466 } 496 }
467 497
468 now = time(NULL); 498 now = time(NULL);
@@ -471,9 +501,9 @@ validate_response(char *buf, size_t size, ocsp_request *request,
471 * in the future. 501 * in the future.
472 */ 502 */
473 if (this_t > now + JITTER_SEC) { 503 if (this_t > now + JITTER_SEC) {
474 warnx("Invalid OCSP reply: this update is in the future (%s)", 504 warnx("Invalid OCSP reply: this update is in the future at %s",
475 ctime(&this_t)); 505 ctime(&this_t));
476 return 0; 506 goto err;
477 } 507 }
478 508
479 /* 509 /*
@@ -481,24 +511,29 @@ validate_response(char *buf, size_t size, ocsp_request *request,
481 * in the past. 511 * in the past.
482 */ 512 */
483 if (this_t < now - MAXAGE_SEC) { 513 if (this_t < now - MAXAGE_SEC) {
484 warnx("Invalid OCSP reply: this update is too old (%s)", 514 warnx("Invalid OCSP reply: this update is too old %s",
485 ctime(&this_t)); 515 ctime(&this_t));
486 return 0; 516 goto err;
487 } 517 }
488 518
489 /* 519 /*
490 * Check that next update is still valid 520 * Check that next update is still valid
491 */ 521 */
492 if (next_t < now - JITTER_SEC) { 522 if (next_t < now - JITTER_SEC) {
493 warnx("Invalid OCSP reply: reply has expired (%s)", 523 warnx("Invalid OCSP reply: reply has expired at %s",
494 ctime(&next_t)); 524 ctime(&next_t));
495 return 0; 525 goto err;
496 } 526 }
497 527
498 vspew("OCSP response validated from %s\n", host); 528 vspew("OCSP response validated from %s\n", host);
499 vspew(" This Update: %s", ctime(&this_t)); 529 vspew(" This Update: %s", ctime(&this_t));
500 vspew(" Next Update: %s", ctime(&next_t)); 530 vspew(" Next Update: %s", ctime(&next_t));
501 return 1; 531 ret = 1;
532 err:
533 OCSP_RESPONSE_free(resp);
534 OCSP_BASICRESP_free(bresp);
535 OCSP_CERTID_free(cid);
536 return ret;
502} 537}
503 538
504static void 539static void
@@ -514,7 +549,7 @@ int
514main(int argc, char **argv) 549main(int argc, char **argv)
515{ 550{
516 const char *cafile = NULL, *cadir = NULL; 551 const char *cafile = NULL, *cadir = NULL;
517 char *host = NULL, *path = "/", *certfile = NULL, *outfile = NULL, 552 char *host = NULL, *path = NULL, *certfile = NULL, *outfile = NULL,
518 *instaple = NULL, *infile = NULL; 553 *instaple = NULL, *infile = NULL;
519 struct addr addrs[MAX_SERVERS_DNS] = {{0}}; 554 struct addr addrs[MAX_SERVERS_DNS] = {{0}};
520 struct source sources[MAX_SERVERS_DNS]; 555 struct source sources[MAX_SERVERS_DNS];
@@ -551,7 +586,7 @@ main(int argc, char **argv)
551 argc -= optind; 586 argc -= optind;
552 argv += optind; 587 argv += optind;
553 588
554 if ((certfile = argv[0]) == NULL) 589 if (argc != 1 || (certfile = argv[0]) == NULL)
555 usage(); 590 usage();
556 591
557 if (outfile != NULL) { 592 if (outfile != NULL) {
@@ -611,8 +646,6 @@ main(int argc, char **argv)
611 if ((host = url2host(request->url, &port, &path)) == NULL) 646 if ((host = url2host(request->url, &port, &path)) == NULL)
612 errx(1, "Invalid OCSP url %s from %s", request->url, 647 errx(1, "Invalid OCSP url %s from %s", request->url,
613 certfile); 648 certfile);
614 if (*path == '\0')
615 path = "/";
616 649
617 if (infd == -1) { 650 if (infd == -1) {
618 /* Get a new OCSP response from the indicated server */ 651 /* Get a new OCSP response from the indicated server */
@@ -713,7 +746,6 @@ main(int argc, char **argv)
713 if (errno != EINTR && errno != EAGAIN) 746 if (errno != EINTR && errno != EAGAIN)
714 err(1, "Write of OCSP response failed"); 747 err(1, "Write of OCSP response failed");
715 } 748 }
716 w = 0;
717 written = 0; 749 written = 0;
718 while (written < instaplesz) { 750 while (written < instaplesz) {
719 w = write(staplefd, instaple + written, 751 w = write(staplefd, instaple + written,