summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbeck <>2020-10-16 01:16:55 +0000
committerbeck <>2020-10-16 01:16:55 +0000
commit02e3c352953d6092e9148dfaca026fa935952d0f (patch)
tree2ffe48089f785e7609d4d6310fa64084c64f5129
parent7c6ffc946324eead9f89ecc9ac680e2c861f8741 (diff)
downloadopenbsd-02e3c352953d6092e9148dfaca026fa935952d0f.tar.gz
openbsd-02e3c352953d6092e9148dfaca026fa935952d0f.tar.bz2
openbsd-02e3c352953d6092e9148dfaca026fa935952d0f.zip
Refactor a bunch of oscpcheck for single return to clean it up,
and add the ability to parse a port in the specified ocsp url. Since this will now pass them, enable regress tests previously committed for ocspcheck. mostly by me with some cleanup by tb after an obvious yak was found to shave in the OCSP routines in libcrypto ok tb@
-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,