/* * Copyright (C) 2011-2014 Free Software Foundation, Inc. * Copyright (C) 2016-2017 Red Hat, Inc. * * This file is part of GnuTLS. * * GnuTLS is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GnuTLS is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see * . */ #include #include #include #include #include #include #include #include #include #include /* getpass */ /* Gnulib portability files. */ #include #include #include #include #include #include "certtool-common.h" FILE *outfile; static unsigned int incert_format, outcert_format; static const char *outfile_name = NULL; /* to delete on exit */ FILE *infile; static unsigned int encoding; unsigned int verbose = 0; static unsigned int vflags = 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); } gnutls_session_t init_tls_session(const char *host) { return NULL; } int do_handshake(socket_st * socket) { return -1; } static void request_info(void) { gnutls_ocsp_req_t req; int ret; gnutls_datum_t dat, rbuf; size_t size; ret = gnutls_ocsp_req_init(&req); if (ret < 0) { fprintf(stderr, "ocsp_req_init: %s\n", gnutls_strerror(ret)); app_exit(1); } if (HAVE_OPT(LOAD_REQUEST)) dat.data = (void *) read_file(OPT_ARG(LOAD_REQUEST), RF_BINARY, &size); else dat.data = (void *) fread_file(infile, 0, &size); if (dat.data == NULL) { fprintf(stderr, "error reading request\n"); app_exit(1); } dat.size = size; ret = gnutls_ocsp_req_import(req, &dat); free(dat.data); if (ret < 0) { fprintf(stderr, "error importing request: %s\n", gnutls_strerror(ret)); 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)); app_exit(1); } if (HAVE_OPT(OUTFILE)) { ret = gnutls_ocsp_req_export(req, &rbuf); if (ret < 0) { fprintf(stderr, "error exporting request: %s\n", gnutls_strerror(ret)); app_exit(1); } if (outcert_format == GNUTLS_X509_FMT_PEM) { fprintf(stderr, "Cannot export requests into PEM form\n"); app_exit(1); } else { fwrite(rbuf.data, 1, rbuf.size, outfile); } gnutls_free(rbuf.data); } else { printf("%.*s", dat.size, dat.data); } gnutls_free(dat.data); gnutls_ocsp_req_deinit(req); } static void _response_info(const gnutls_datum_t * data, unsigned force_print) { gnutls_ocsp_resp_t resp; int ret; gnutls_datum_t buf, rbuf; 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)); app_exit(1); } ret = gnutls_ocsp_resp_import2(resp, data, incert_format); if (ret == GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR) { int ret2 = gnutls_ocsp_resp_import(resp, data); if (ret2 >= 0) ret = ret2; } if (ret < 0) { fprintf(stderr, "error importing response: %s\n", gnutls_strerror(ret)); app_exit(1); } if (ENABLED_OPT(VERBOSE)) ret = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &buf); else ret = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_COMPACT, &buf); if (ret != 0) { fprintf(stderr, "ocsp_resp_print: %s\n", gnutls_strerror(ret)); app_exit(1); } if (HAVE_OPT(OUTFILE)) { ret = gnutls_ocsp_resp_export2(resp, &rbuf, outcert_format); if (ret < 0) { fprintf(stderr, "error exporting response: %s\n", gnutls_strerror(ret)); app_exit(1); } if (outcert_format == GNUTLS_X509_FMT_PEM) fprintf(outfile, "%.*s\n", buf.size, buf.data); fwrite(rbuf.data, 1, rbuf.size, outfile); if (outcert_format == GNUTLS_X509_FMT_PEM) fprintf(outfile, "\n"); gnutls_free(rbuf.data); } if (force_print || !HAVE_OPT(OUTFILE)) { ret = gnutls_ocsp_resp_export2(resp, &rbuf, GNUTLS_X509_FMT_PEM); if (ret < 0) { fprintf(stderr, "error exporting response: %s\n", gnutls_strerror(ret)); app_exit(1); } fprintf(stdout, "%.*s\n", buf.size, buf.data); fwrite(rbuf.data, 1, rbuf.size, stdout); gnutls_free(rbuf.data); } gnutls_free(buf.data); gnutls_ocsp_resp_deinit(resp); } static void response_info(void) { gnutls_datum_t dat; size_t size; if (HAVE_OPT(LOAD_RESPONSE)) dat.data = (void *) read_file(OPT_ARG(LOAD_RESPONSE), RF_BINARY, &size); else dat.data = (void *) fread_file(infile, 0, &size); if (dat.data == NULL) { fprintf(stderr, "error reading response\n"); app_exit(1); } dat.size = size; _response_info(&dat, 0); gnutls_free(dat.data); } static void generate_request(gnutls_datum_t *nonce) { gnutls_datum_t dat; 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 option --load-cert\n"); app_exit(1); } info.cert = OPT_ARG(LOAD_CERT); cert = 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); issuer = load_cert(1, &info); _generate_request(cert, issuer, &dat, nonce); gnutls_x509_crt_deinit(cert); gnutls_x509_crt_deinit(issuer); fwrite(dat.data, 1, dat.size, outfile); gnutls_free(dat.data); } static int _verify_response(gnutls_datum_t * data, gnutls_datum_t * nonce, gnutls_x509_crt_t signer, unsigned print_resp) { gnutls_ocsp_resp_t resp; int ret; size_t size; gnutls_x509_crt_t *x509_ca_list = NULL; gnutls_x509_trust_list_t list; unsigned int x509_ncas = 0; unsigned verify; gnutls_datum_t dat; ret = gnutls_ocsp_resp_init(&resp); if (ret < 0) { fprintf(stderr, "ocsp_resp_init: %s\n", gnutls_strerror(ret)); app_exit(1); } ret = gnutls_ocsp_resp_import(resp, data); if (ret < 0) { fprintf(stderr, "importing response: %s\n", gnutls_strerror(ret)); app_exit(1); } if (print_resp) { ret = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_COMPACT, &dat); if (ret < 0) { fprintf(stderr, "ocsp_resp_print: %s\n", gnutls_strerror(ret)); app_exit(1); } printf("%s\n", dat.data); gnutls_free(dat.data); } if (nonce) { gnutls_datum_t rnonce; ret = gnutls_ocsp_resp_get_nonce(resp, NULL, &rnonce); if (ret < 0) { fprintf(stderr, "could not read response's nonce: %s\n", gnutls_strerror(ret)); 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"); app_exit(1); } gnutls_free(rnonce.data); } if (HAVE_OPT(LOAD_TRUST)) { dat.data = (void *) read_file(OPT_ARG(LOAD_TRUST), RF_BINARY, &size); if (dat.data == NULL) { fprintf(stderr, "error reading --load-trust: %s\n", OPT_ARG(LOAD_TRUST)); app_exit(1); } dat.size = size; ret = gnutls_x509_trust_list_init(&list, 0); if (ret < 0) { fprintf(stderr, "gnutls_x509_trust_list_init: %s\n", gnutls_strerror(ret)); app_exit(1); } ret = gnutls_x509_crt_list_import2(&x509_ca_list, &x509_ncas, &dat, GNUTLS_X509_FMT_PEM, 0); if (ret < 0 || x509_ncas < 1) { fprintf(stderr, "error parsing CAs: %s\n", gnutls_strerror(ret)); app_exit(1); } if (HAVE_OPT(VERBOSE)) { unsigned int i; printf("Trust anchors:\n"); for (i = 0; i < x509_ncas; i++) { gnutls_datum_t out; ret = gnutls_x509_crt_print(x509_ca_list[i], GNUTLS_CRT_PRINT_ONELINE, &out); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_print: %s\n", gnutls_strerror(ret)); app_exit(1); } printf("%d: %.*s\n", i, out.size, out.data); gnutls_free(out.data); } printf("\n"); } ret = gnutls_x509_trust_list_add_cas(list, x509_ca_list, x509_ncas, 0); if (ret < 0) { fprintf(stderr, "gnutls_x509_trust_add_cas: %s\n", gnutls_strerror(ret)); app_exit(1); } if (HAVE_OPT(VERBOSE)) fprintf(stdout, "Loaded %d trust anchors\n", x509_ncas); ret = gnutls_ocsp_resp_verify(resp, list, &verify, vflags); if (ret < 0) { fprintf(stderr, "gnutls_ocsp_resp_verify: %s\n", gnutls_strerror(ret)); app_exit(1); } } else if (signer) { if (HAVE_OPT(VERBOSE)) { gnutls_datum_t out; ret = gnutls_x509_crt_print(signer, GNUTLS_CRT_PRINT_ONELINE, &out); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_print: %s\n", gnutls_strerror(ret)); app_exit(1); } printf("Signer: %.*s\n", out.size, out.data); gnutls_free(out.data); printf("\n"); } ret = gnutls_ocsp_resp_verify_direct(resp, signer, &verify, vflags); if (ret < 0) { fprintf(stderr, "\nVerifying OCSP Response: %s\n", gnutls_strerror(ret)); app_exit(1); } } else { fprintf(stderr, "missing --load-trust or --load-signer\n"); app_exit(1); } printf("\nVerifying OCSP Response: "); print_ocsp_verify_res(verify); printf(".\n"); gnutls_ocsp_resp_deinit(resp); return verify; } #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); info.sort_chain = 1; 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 2 && HAVE_OPT(OUTFILE)) { if (outcert_format != GNUTLS_X509_FMT_PEM) { fprintf(stderr, "error: You cannot combine --outfile when more than 2 certificates are found in a chain\n"); fprintf(stderr, "Did you mean to use --outpem?\n"); app_exit(1); } } 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; } ret = send_ocsp_request(url, chain[idx], chain[idx+1], &resp_data, n); if (ret < 0) { fprintf(stderr, "Cannot send OCSP request\n"); app_exit(1); } _response_info(&resp_data, 1); if (HAVE_OPT(LOAD_TRUST)) { v = _verify_response(&resp_data, n, NULL, 0); } else if (HAVE_OPT(LOAD_SIGNER)) { memset(&info, 0, sizeof(info)); info.verbose = verbose; info.cert = OPT_ARG(LOAD_SIGNER); v = _verify_response(&resp_data, n, load_cert(1, &info), 0); } 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, chain[idx+1], 0); } total_v += v; free(resp_data.data); idx++; counter--; printf("\n"); } for (idx = 0;idx