diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2017-06-16 14:36:43 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2017-06-19 20:26:42 +0200 |
commit | 8b0ddb0c8b0293fc5d2c16ed0e70a8fc109e1559 (patch) | |
tree | e007a16741422d26d8966b1c00dfdb6226c93a7b /src | |
parent | 86884b19ddc47b06ceb3b854e6a7757623874fae (diff) | |
download | gnutls-8b0ddb0c8b0293fc5d2c16ed0e70a8fc109e1559.tar.gz |
ocsptool: added --load-chain option
This option allows to directly verify all the members of a certificate
chain.
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/certtool-common.h | 3 | ||||
-rw-r--r-- | src/ocsptool-args.def | 35 | ||||
-rw-r--r-- | src/ocsptool.c | 381 |
4 files changed, 224 insertions, 197 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 827638ec8b..23dd8b6f69 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -95,7 +95,7 @@ libcmd_psk_la_SOURCES = psktool-args.def psktool-args.c psktool-args.h if ENABLE_OCSP -ocsptool_SOURCES = ocsptool.c ocsptool-common.h ocsptool-common.c \ +ocsptool_SOURCES = ocsptool.c ocsptool-common.h ocsptool-common.c certtool-common.c \ socket.c common.c common.h socket.h ocsptool_LDADD = ../lib/libgnutls.la libcmd-ocsp.la $(LIBOPTS) ../gl/libgnu.la ocsptool_LDADD += $(LTLIBINTL) $(GETADDRINFO_LIB) gl/libgnu_gpl.la $(LIBIDN_LIBS) diff --git a/src/certtool-common.h b/src/certtool-common.h index 91a4c6d1e9..9c5fb977e1 100644 --- a/src/certtool-common.h +++ b/src/certtool-common.h @@ -23,6 +23,7 @@ #include <gnutls/x509.h> #include <stdio.h> +#include <stdbool.h> #define TYPE_CRT 1 #define TYPE_CRQ 2 @@ -78,6 +79,8 @@ typedef struct common_info { } common_info_st; /* this must be provided by the app */ +const char *get_pass(void); +const char *get_confirmed_pass(bool empty_ok); void app_exit(int val) #ifdef __GNUC__ __attribute__ ((noreturn)) diff --git a/src/ocsptool-args.def b/src/ocsptool-args.def index b3a62734a7..c293863bc7 100644 --- a/src/ocsptool-args.def +++ b/src/ocsptool-args.def @@ -19,8 +19,6 @@ flag = { arg-name = "server name|url"; arg-optional; descrip = "Ask an OCSP/HTTP server on a certificate validity"; - flags-must = load-cert; - flags-must = load-issuer; doc = "Connects to the specified HTTP OCSP server and queries on the validity of the loaded certificate."; }; @@ -61,6 +59,14 @@ flag = { }; flag = { + name = load-chain; + arg-type = file; + file-exists = yes; + descrip = "Read certificate and issuer chain from file"; + doc = ""; +}; + +flag = { name = load-issuer; arg-type = file; file-exists = yes; @@ -236,18 +242,12 @@ copy of the server certificate chain. The server is not required to send this information, but this particular one is configured to do so. @example -$ echo | gnutls-cli -p 443 blog.josefsson.org --print-cert > chain.pem +$ echo | gnutls-cli -p 443 blog.josefsson.org --save-cert chain.pem @end example -Use a text editor on @code{chain.pem} to create three files for each -separate certificates, called @code{cert.pem} for the first -certificate for the domain itself, secondly @code{issuer.pem} for the -intermediate certificate and @code{root.pem} for the final root -certificate. - -The domain certificate normally contains a pointer to where the OCSP +The saved certificates normally contain a pointer to where the OCSP responder is located, in the Authority Information Access Information -extension. For example, from @code{certtool -i < cert.pem} there is +extension. For example, from @code{certtool -i < chain.pem} there is this information: @example @@ -256,16 +256,17 @@ this information: Access Location URI: http://ocsp.CAcert.org/ @end example -This means the CA support OCSP queries over HTTP. We are now ready to -create a OCSP request for the certificate. +This means that ocsptool can discover the servers to contact over HTTP. +We can now request information on the chain certificates. @example -$ ocsptool --ask ocsp.CAcert.org --load-issuer issuer.pem \ - --load-cert cert.pem --outfile ocsp-response.der +$ ocsptool --ask --load-chain chain.pem @end example -The request is sent via HTTP to the OCSP server address specified. If the -address is omitted ocsptool will use the address stored in the certificate. +The request is sent via HTTP to the OCSP server address found in +the certificates. It is possible to override the address of the +OCSP server as well as ask information on a particular certificate +using --load-cert and --load-issuer. _EOF_; }; diff --git a/src/ocsptool.c b/src/ocsptool.c index 2aa01dde18..1a694a24d7 100644 --- a/src/ocsptool.c +++ b/src/ocsptool.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011-2014 Free Software Foundation, Inc. + * Copyright (C) 2016-2017 Red Hat, Inc. * * This file is part of GnuTLS. * @@ -30,18 +31,42 @@ #include <gnutls/x509.h> #include <gnutls/crypto.h> +#include <unistd.h> /* getpass */ + /* Gnulib portability files. */ #include <read-file.h> #include <socket.h> +#include <minmax.h> #include <ocsptool-common.h> #include <ocsptool-args.h> +#include "certtool-common.h" FILE *outfile; +static const char *outfile_name = NULL; /* to delete on exit */ FILE *infile; static unsigned int encoding; unsigned int verbose = 0; +const char *get_pass(void) +{ + return getpass("Enter password: "); +} + +const char *get_confirmed_pass(bool ign) +{ + return getpass("Enter password: "); +} + +void app_exit(int val) +{ + if (val != 0) { + if (outfile_name) + (void)remove(outfile_name); + } + exit(val); +} + static void tls_log_func(int level, const char *str) { fprintf(stderr, "|<%d>| %s", level, str); @@ -67,7 +92,7 @@ static void request_info(void) ret = gnutls_ocsp_req_init(&req); if (ret < 0) { fprintf(stderr, "ocsp_req_init: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } if (HAVE_OPT(LOAD_REQUEST)) @@ -78,7 +103,7 @@ static void request_info(void) dat.data = (void *) fread_file(infile, &size); if (dat.data == NULL) { fprintf(stderr, "error reading request\n"); - exit(1); + app_exit(1); } dat.size = size; @@ -87,14 +112,14 @@ static void request_info(void) if (ret < 0) { fprintf(stderr, "error importing request: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } ret = gnutls_ocsp_req_print(req, GNUTLS_OCSP_PRINT_FULL, &dat); if (ret != 0) { fprintf(stderr, "ocsp_req_print: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } printf("%.*s", dat.size, dat.data); @@ -109,18 +134,23 @@ static void _response_info(const gnutls_datum_t * data) int ret; gnutls_datum_t buf; + if (data->size == 0) { + fprintf(stderr, "Received empty response\n"); + app_exit(1); + } + ret = gnutls_ocsp_resp_init(&resp); if (ret < 0) { fprintf(stderr, "ocsp_resp_init: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } ret = gnutls_ocsp_resp_import(resp, data); if (ret < 0) { fprintf(stderr, "importing response: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } if (ENABLED_OPT(VERBOSE)) @@ -134,7 +164,7 @@ static void _response_info(const gnutls_datum_t * data) if (ret != 0) { fprintf(stderr, "ocsp_resp_print: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } printf("%.*s", buf.size, buf.data); @@ -156,7 +186,7 @@ static void response_info(void) dat.data = (void *) fread_file(infile, &size); if (dat.data == NULL) { fprintf(stderr, "error reading response\n"); - exit(1); + app_exit(1); } dat.size = size; @@ -164,127 +194,31 @@ static void response_info(void) gnutls_free(dat.data); } -static gnutls_x509_crt_t load_issuer(void) -{ - gnutls_x509_crt_t crt; - int ret; - gnutls_datum_t dat; - size_t size; - - if (!HAVE_OPT(LOAD_ISSUER)) { - fprintf(stderr, "missing --load-issuer\n"); - exit(1); - } - - ret = gnutls_x509_crt_init(&crt); - if (ret < 0) { - fprintf(stderr, "crt_init: %s\n", gnutls_strerror(ret)); - exit(1); - } - - dat.data = (void *) read_binary_file(OPT_ARG(LOAD_ISSUER), &size); - dat.size = size; - - if (!dat.data) { - fprintf(stderr, "error reading --load-issuer: %s\n", - OPT_ARG(LOAD_ISSUER)); - exit(1); - } - - ret = gnutls_x509_crt_import(crt, &dat, encoding); - free(dat.data); - if (ret < 0) { - fprintf(stderr, "error importing --load-issuer: %s: %s\n", - OPT_ARG(LOAD_ISSUER), gnutls_strerror(ret)); - exit(1); - } - - return crt; -} - -static gnutls_x509_crt_t load_signer(void) -{ - gnutls_x509_crt_t crt; - int ret; - gnutls_datum_t dat; - size_t size; - - if (!HAVE_OPT(LOAD_SIGNER)) { - fprintf(stderr, "missing --load-signer\n"); - exit(1); - } - - ret = gnutls_x509_crt_init(&crt); - if (ret < 0) { - fprintf(stderr, "crt_init: %s\n", gnutls_strerror(ret)); - exit(1); - } - - dat.data = (void *) read_binary_file(OPT_ARG(LOAD_SIGNER), &size); - dat.size = size; - - if (!dat.data) { - fprintf(stderr, "error reading --load-signer: %s\n", - OPT_ARG(LOAD_SIGNER)); - exit(1); - } - - ret = gnutls_x509_crt_import(crt, &dat, encoding); - free(dat.data); - if (ret < 0) { - fprintf(stderr, "importing --load-signer: %s: %s\n", - OPT_ARG(LOAD_SIGNER), gnutls_strerror(ret)); - exit(1); - } - - return crt; -} - -static gnutls_x509_crt_t load_cert(void) +static void generate_request(gnutls_datum_t *nonce) { - gnutls_x509_crt_t crt; - int ret; gnutls_datum_t dat; - size_t size; + gnutls_x509_crt_t cert, issuer; + common_info_st info; + memset(&info, 0, sizeof(info)); + info.verbose = verbose; if (!HAVE_OPT(LOAD_CERT)) { - fprintf(stderr, "missing --load-cert\n"); - exit(1); - } - - ret = gnutls_x509_crt_init(&crt); - if (ret < 0) { - fprintf(stderr, "crt_init: %s\n", gnutls_strerror(ret)); - exit(1); + fprintf(stderr, "Missing option --load-cert\n"); + app_exit(1); } + info.cert = OPT_ARG(LOAD_CERT); - dat.data = (void *) read_binary_file(OPT_ARG(LOAD_CERT), &size); - dat.size = size; - - if (!dat.data) { - fprintf(stderr, "error reading --load-cert: %s\n", - OPT_ARG(LOAD_CERT)); - exit(1); - } + cert = load_cert(1, &info); - ret = gnutls_x509_crt_import(crt, &dat, encoding); - free(dat.data); - if (ret < 0) { - fprintf(stderr, "importing --load-cert: %s: %s\n", - OPT_ARG(LOAD_CERT), gnutls_strerror(ret)); - exit(1); + memset(&info, 0, sizeof(info)); + info.verbose = verbose; + if (!HAVE_OPT(LOAD_ISSUER)) { + fprintf(stderr, "Missing option --load-issuer\n"); + app_exit(1); } + info.cert = OPT_ARG(LOAD_ISSUER); - return crt; -} - -static void generate_request(gnutls_datum_t *nonce) -{ - gnutls_datum_t dat; - gnutls_x509_crt_t cert, issuer; - - cert = load_cert(); - issuer = load_issuer(); + issuer = load_cert(1, &info); _generate_request(cert, issuer, &dat, nonce); @@ -312,14 +246,14 @@ static int _verify_response(gnutls_datum_t * data, gnutls_datum_t * nonce, if (ret < 0) { fprintf(stderr, "ocsp_resp_init: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } ret = gnutls_ocsp_resp_import(resp, data); if (ret < 0) { fprintf(stderr, "importing response: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } if (nonce) { @@ -329,13 +263,13 @@ static int _verify_response(gnutls_datum_t * data, gnutls_datum_t * nonce, if (ret < 0) { fprintf(stderr, "could not read response's nonce: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } if (rnonce.size != nonce->size || memcmp(nonce->data, rnonce.data, nonce->size) != 0) { fprintf(stderr, "nonce in the response doesn't match\n"); - exit(1); + app_exit(1); } gnutls_free(rnonce.data); @@ -347,7 +281,7 @@ static int _verify_response(gnutls_datum_t * data, gnutls_datum_t * nonce, if (dat.data == NULL) { fprintf(stderr, "error reading --load-trust: %s\n", OPT_ARG(LOAD_TRUST)); - exit(1); + app_exit(1); } dat.size = size; @@ -355,7 +289,7 @@ static int _verify_response(gnutls_datum_t * data, gnutls_datum_t * nonce, if (ret < 0) { fprintf(stderr, "gnutls_x509_trust_list_init: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } ret = @@ -365,7 +299,7 @@ static int _verify_response(gnutls_datum_t * data, gnutls_datum_t * nonce, if (ret < 0 || x509_ncas < 1) { fprintf(stderr, "error parsing CAs: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } if (HAVE_OPT(VERBOSE)) { @@ -382,7 +316,7 @@ static int _verify_response(gnutls_datum_t * data, gnutls_datum_t * nonce, fprintf(stderr, "gnutls_x509_crt_print: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } printf("%d: %.*s\n", i, out.size, @@ -398,7 +332,7 @@ static int _verify_response(gnutls_datum_t * data, gnutls_datum_t * nonce, if (ret < 0) { fprintf(stderr, "gnutls_x509_trust_add_cas: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } if (HAVE_OPT(VERBOSE)) @@ -409,7 +343,7 @@ static int _verify_response(gnutls_datum_t * data, gnutls_datum_t * nonce, if (ret < 0) { fprintf(stderr, "gnutls_ocsp_resp_verify: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } } else if (signer) { if (HAVE_OPT(VERBOSE)) { @@ -423,7 +357,7 @@ static int _verify_response(gnutls_datum_t * data, gnutls_datum_t * nonce, fprintf(stderr, "gnutls_x509_crt_print: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } printf("Signer: %.*s\n", out.size, out.data); @@ -436,16 +370,16 @@ static int _verify_response(gnutls_datum_t * data, gnutls_datum_t * nonce, 0); if (ret < 0) { fprintf(stderr, - "gnutls_ocsp_resp_verify_direct: %s\n", + "\nVerifying OCSP Response: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } } else { fprintf(stderr, "missing --load-trust or --load-signer\n"); - exit(1); + app_exit(1); } - printf("Verifying OCSP Response: "); + printf("\nVerifying OCSP Response: "); print_ocsp_verify_res(verify); printf(".\n"); @@ -459,6 +393,7 @@ static void verify_response(gnutls_datum_t *nonce) gnutls_datum_t dat; size_t size; gnutls_x509_crt_t signer; + common_info_st info; int v; if (HAVE_OPT(LOAD_RESPONSE)) @@ -469,11 +404,19 @@ static void verify_response(gnutls_datum_t *nonce) dat.data = (void *) fread_file(infile, &size); if (dat.data == NULL) { fprintf(stderr, "error reading response\n"); - exit(1); + app_exit(1); } dat.size = size; - signer = load_signer(); + memset(&info, 0, sizeof(info)); + info.verbose = verbose; + if (!HAVE_OPT(LOAD_SIGNER)) { + fprintf(stderr, "Missing option --load-signer\n"); + app_exit(1); + } + info.cert = OPT_ARG(LOAD_SIGNER); + + signer = load_cert(1, &info); v = _verify_response(&dat, nonce, signer); @@ -481,65 +424,140 @@ static void verify_response(gnutls_datum_t *nonce) free(dat.data); if (v && !HAVE_OPT(IGNORE_ERRORS)) - exit(1); + app_exit(1); +} + +#define MAX_CHAIN_SIZE 8 + +static +unsigned load_chain(gnutls_x509_crt_t chain[MAX_CHAIN_SIZE]) +{ + if (HAVE_OPT(LOAD_CHAIN)) { + common_info_st info; + size_t list_size; + + memset(&info, 0, sizeof(info)); + gnutls_x509_crt_t *list; + unsigned i; + + info.verbose = verbose; + info.cert = OPT_ARG(LOAD_CHAIN); + list = load_cert_list(1, &list_size, &info); + + if (list_size > MAX_CHAIN_SIZE) { + fprintf(stderr, "Too many certificates in chain\n"); + app_exit(1); + } + + for (i=0;i<list_size;i++) + chain[i] = list[i]; + gnutls_free(list); + return list_size; + } else { + common_info_st info; + + memset(&info, 0, sizeof(info)); + info.verbose = verbose; + if (!HAVE_OPT(LOAD_CERT)) { + fprintf(stderr, "Missing option --load-cert\n"); + app_exit(1); + } + info.cert = OPT_ARG(LOAD_CERT); + + chain[0] = load_cert(1, &info); + + memset(&info, 0, sizeof(info)); + info.verbose = verbose; + if (!HAVE_OPT(LOAD_ISSUER)) { + fprintf(stderr, "Missing option --load-issuer\n"); + app_exit(1); + } + info.cert = OPT_ARG(LOAD_ISSUER); + + chain[1] = load_cert(1, &info); + return 2; + } } + + static void ask_server(const char *url) { gnutls_datum_t resp_data; - int ret, v = 0; - gnutls_x509_crt_t cert, issuer; + int ret, v = 0, total_v = 0; unsigned char noncebuf[23]; gnutls_datum_t nonce = { noncebuf, sizeof(noncebuf) }; gnutls_datum_t *n; + gnutls_x509_crt_t chain[MAX_CHAIN_SIZE]; + unsigned chain_size, counter; + unsigned idx = 0; + common_info_st info; + + chain_size = load_chain(chain); + + if (chain_size > 2 && HAVE_OPT(OUTFILE)) { + fprintf(stderr, "You cannot combine --outfile when more than 2 certificates are found in a chain\n"); + app_exit(1); + } - cert = load_cert(); - issuer = load_issuer(); + counter = chain_size; + while(counter > 1) { + if (ENABLED_OPT(NONCE)) { + ret = + gnutls_rnd(GNUTLS_RND_NONCE, nonce.data, nonce.size); + if (ret < 0) { + fprintf(stderr, "gnutls_rnd: %s\n", + gnutls_strerror(ret)); + app_exit(1); + } + n = &nonce; + } else { + n = NULL; + } - if (ENABLED_OPT(NONCE)) { ret = - gnutls_rnd(GNUTLS_RND_NONCE, nonce.data, nonce.size); + send_ocsp_request(url, chain[idx], chain[idx+1], &resp_data, n); if (ret < 0) { - fprintf(stderr, "gnutls_rnd: %s\n", - gnutls_strerror(ret)); - exit(1); + fprintf(stderr, "Cannot send OCSP request\n"); + app_exit(1); } - n = &nonce; - } else { - n = NULL; - } + if (HAVE_OPT(OUTFILE)) { + fwrite(resp_data.data, 1, resp_data.size, outfile); + } - ret = - send_ocsp_request(url, cert, issuer, &resp_data, n); - if (ret < 0) { - fprintf(stderr, "Cannot send OCSP request\n"); - exit(1); - } + _response_info(&resp_data); - _response_info(&resp_data); + if (HAVE_OPT(LOAD_TRUST)) { + v = _verify_response(&resp_data, n, NULL); + } else if (HAVE_OPT(LOAD_SIGNER)) { + memset(&info, 0, sizeof(info)); + info.verbose = verbose; + info.cert = OPT_ARG(LOAD_SIGNER); - if (HAVE_OPT(LOAD_TRUST)) { - v = _verify_response(&resp_data, n, NULL); - } else if (HAVE_OPT(LOAD_SIGNER)) { - v = _verify_response(&resp_data, n, load_signer()); - } else { - fprintf(stderr, - "\nAssuming response's signer = issuer (use --load-signer to override).\n"); + v = _verify_response(&resp_data, n, load_cert(1, &info)); + } else { + if (!HAVE_OPT(LOAD_CHAIN)) + fprintf(stderr, + "\nAssuming response's signer = issuer (use --load-signer to override).\n"); - v = _verify_response(&resp_data, n, issuer); - } + v = _verify_response(&resp_data, n, chain[idx+1]); + } - if (HAVE_OPT(OUTFILE) && (v == 0 || HAVE_OPT(IGNORE_ERRORS))) { - fwrite(resp_data.data, 1, resp_data.size, outfile); + total_v += v; + + free(resp_data.data); + idx++; + counter--; + printf("\n"); } - free(resp_data.data); - gnutls_x509_crt_deinit(issuer); - gnutls_x509_crt_deinit(cert); + for (idx = 0;idx<chain_size;idx++) { + gnutls_x509_crt_deinit(chain[idx]); + } - if (v && !HAVE_OPT(IGNORE_ERRORS)) - exit(1); + if (total_v && !HAVE_OPT(IGNORE_ERRORS)) + app_exit(1); } int main(int argc, char **argv) @@ -548,7 +566,7 @@ int main(int argc, char **argv) if ((ret = gnutls_global_init()) < 0) { fprintf(stderr, "global_init: %s\n", gnutls_strerror(ret)); - exit(1); + app_exit(1); } optionProcess(&ocsptoolOptions, argc, argv); @@ -560,8 +578,9 @@ int main(int argc, char **argv) outfile = fopen(OPT_ARG(OUTFILE), "wb"); if (outfile == NULL) { fprintf(stderr, "%s\n", OPT_ARG(OUTFILE)); - exit(1); + app_exit(1); } + outfile_name = OPT_ARG(OUTFILE); } else outfile = stdout; @@ -569,7 +588,7 @@ int main(int argc, char **argv) infile = fopen(OPT_ARG(INFILE), "rb"); if (infile == NULL) { fprintf(stderr, "%s\n", OPT_ARG(INFILE)); - exit(1); + app_exit(1); } } else infile = stdin; @@ -587,9 +606,13 @@ int main(int argc, char **argv) generate_request(NULL); else if (HAVE_OPT(VERIFY_RESPONSE)) verify_response(NULL); - else if (HAVE_OPT(ASK)) + else if (HAVE_OPT(ASK)) { + if ((!HAVE_OPT(LOAD_CERT)) && (!HAVE_OPT(LOAD_CHAIN))) { + fprintf(stderr, "This option required --load-chain or --load-cert\n"); + app_exit(1); + } ask_server(OPT_ARG(ASK)); - else { + } else { USAGE(1); } |