diff options
author | Dr. David von Oheimb <David.von.Oheimb@siemens.com> | 2020-04-03 10:43:58 +0200 |
---|---|---|
committer | Dr. David von Oheimb <David.von.Oheimb@siemens.com> | 2020-05-13 19:42:00 +0200 |
commit | 8d9a4d833f12b0669f053a504268d13a46c079ad (patch) | |
tree | bb55ea419d32aec2827d1294fd668e3c9d38fc34 | |
parent | 3c38fa4b797848a76b83f91e423de470adbb3b61 (diff) |
Chunk 11 of CMP contribution to OpenSSL: CMP command-line interface
Certificate Management Protocol (CMP, RFC 4210) extension to OpenSSL
Also includes CRMF (RFC 4211) and HTTP transfer (RFC 6712).
Adds the CMP and CRMF API to libcrypto and the "cmp" app to the CLI.
Adds extensive documentation and tests.
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
(Merged from https://github.com/openssl/openssl/pull/11470)
-rw-r--r-- | CHANGES.md | 6 | ||||
-rw-r--r-- | NEWS.md | 2 | ||||
-rw-r--r-- | apps/build.info | 2 | ||||
-rw-r--r-- | apps/cmp.c | 3329 | ||||
-rw-r--r-- | apps/openssl-vms.cnf | 56 | ||||
-rw-r--r-- | apps/openssl.cnf | 56 | ||||
-rw-r--r-- | doc/man1/build.info | 3 | ||||
-rw-r--r-- | doc/man1/openssl-cmp.pod.in | 1157 | ||||
-rwxr-xr-x | test/insta.priv.pem | 27 | ||||
-rwxr-xr-x | test/insta_ca.cert.pem | 22 |
10 files changed, 4655 insertions, 5 deletions
diff --git a/CHANGES.md b/CHANGES.md index 51ed264cb0..6ee0b1efde 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -93,10 +93,10 @@ OpenSSL 3.0 *Richard Levitte* * Added an implementation of CMP and CRMF (RFC 4210, RFC 4211 RFC 6712). - This adds crypto/cmp/, crpyto/crmf/, and test/cmp_*. - See L<OSSL_CMP_exec_IR_ses(3)> as starting point. + This adds crypto/cmp/, crpyto/crmf/, apps/cmp.c, and test/cmp_*. + See L<openssl-cmp(1)> and L<OSSL_CMP_exec_IR_ses(3)> as starting points. - *David von Oheimb* + *David von Oheimb, Martin Peylo* * Generalized the HTTP client code from crypto/ocsp/ into crpyto/http/. The legacy OCSP-focused and only partly documented API is retained. @@ -34,7 +34,7 @@ OpenSSL 3.0 disabled; the project uses address sanitize/leak-detect instead. * Added a Certificate Management Protocol (CMP, RFC 4210) implementation also covering CRMF (RFC 4211) and HTTP transfer (RFC 6712). - It is part of the crypto lib, while a 'cmp' app using it is in preparation. + It is part of the crypto lib and adds a 'cmp' app with a demo configuration. All widely used CMP features are supported for both clients and servers. * Added a proper HTTP(S) client to libcrypto supporting GET and POST, redirection, plain and ASN.1-encoded contents, proxies, and timeouts. diff --git a/apps/build.info b/apps/build.info index f2c62c94dc..d51e825bc5 100644 --- a/apps/build.info +++ b/apps/build.info @@ -52,7 +52,7 @@ IF[{- !$disabled{'deprecated-3.0'} -}] ENDIF ENDIF IF[{- !$disabled{'cmp'} -}] - $OPENSSLSRC=$OPENSSLSRC cmp_mock_srv.c + $OPENSSLSRC=$OPENSSLSRC cmp.c cmp_mock_srv.c ENDIF IF[{- !$disabled{apps} -}] diff --git a/apps/cmp.c b/apps/cmp.c new file mode 100644 index 0000000000..24a7fcbe6c --- /dev/null +++ b/apps/cmp.c @@ -0,0 +1,3329 @@ +/* + * Copyright 2007-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright Nokia 2007-2019 + * Copyright Siemens AG 2015-2019 + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <string.h> +#include <ctype.h> + +#include "apps.h" +#include "http_server.h" +#include "s_apps.h" +#include "progs.h" + +#include "cmp_mock_srv.h" + +/* tweaks needed due to missing unistd.h on Windows */ +#ifdef _WIN32 +# define access _access +#endif +#ifndef F_OK +# define F_OK 0 +#endif + +#include <openssl/ui.h> +#include <openssl/pkcs12.h> +#include <openssl/ssl.h> + +/* explicit #includes not strictly needed since implied by the above: */ +#include <stdlib.h> +#include <openssl/cmp.h> +#include <openssl/cmp_util.h> +#include <openssl/crmf.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/store.h> +#include <openssl/objects.h> +#include <openssl/x509.h> + +DEFINE_STACK_OF(X509) +DEFINE_STACK_OF(X509_EXTENSION) +DEFINE_STACK_OF(OSSL_CMP_ITAV) + +/* start TODO remove when PR #11755 is merged */ +static char *get_passwd(const char *pass, const char *desc) +{ + char *result = NULL; + + app_passwd(pass, NULL, &result, NULL); + return result; +} + +static void cleanse(char *str) +{ + if (str != NULL) + OPENSSL_cleanse(str, strlen(str)); +} + +static void clear_free(char *str) +{ + if (str != NULL) + OPENSSL_clear_free(str, strlen(str)); +} + +static int load_key_cert_crl(const char *uri, int maybe_stdin, + const char *pass, const char *desc, + EVP_PKEY **ppkey, X509 **pcert, X509_CRL **pcrl) +{ + PW_CB_DATA uidata; + OSSL_STORE_CTX *ctx = NULL; + int ret = 0; + + if (ppkey != NULL) + *ppkey = NULL; + if (pcert != NULL) + *pcert = NULL; + if (pcrl != NULL) + *pcrl = NULL; + + uidata.password = pass; + uidata.prompt_info = uri; + + ctx = OSSL_STORE_open(uri, get_ui_method(), &uidata, NULL, NULL); + if (ctx == NULL) { + BIO_printf(bio_err, "Could not open file or uri %s for loading %s\n", + uri, desc); + goto end; + } + + for (;;) { + OSSL_STORE_INFO *info = OSSL_STORE_load(ctx); + int type = info == NULL ? 0 : OSSL_STORE_INFO_get_type(info); + const char *infostr = + info == NULL ? NULL : OSSL_STORE_INFO_type_string(type); + int err = 0; + + if (info == NULL) { + if (OSSL_STORE_eof(ctx)) + ret = 1; + break; + } + + switch (type) { + case OSSL_STORE_INFO_PKEY: + if (ppkey != NULL && *ppkey == NULL) + err = ((*ppkey = OSSL_STORE_INFO_get1_PKEY(info)) == NULL); + break; + case OSSL_STORE_INFO_CERT: + if (pcert != NULL && *pcert == NULL) + err = ((*pcert = OSSL_STORE_INFO_get1_CERT(info)) == NULL); + break; + case OSSL_STORE_INFO_CRL: + if (pcrl != NULL && *pcrl == NULL) + err = ((*pcrl = OSSL_STORE_INFO_get1_CRL(info)) == NULL); + break; + default: + /* skip any other type */ + break; + } + OSSL_STORE_INFO_free(info); + if (err) { + BIO_printf(bio_err, "Could not read %s of %s from %s\n", + infostr, desc, uri); + break; + } + } + + end: + if (ctx != NULL) + OSSL_STORE_close(ctx); + if (!ret) + ERR_print_errors(bio_err); + return ret; +} + +static +EVP_PKEY *load_key_preliminary(const char *uri, int format, int may_stdin, + const char *pass, ENGINE *e, const char *desc) +{ + EVP_PKEY *pkey = NULL; + + if (desc == NULL) + desc = "private key"; + + if (format == FORMAT_ENGINE) { + if (e == NULL) { + BIO_printf(bio_err, "No engine specified for loading %s\n", desc); + } else { +#ifndef OPENSSL_NO_ENGINE + PW_CB_DATA cb_data; + + cb_data.password = pass; + cb_data.prompt_info = uri; + if (ENGINE_init(e)) { + pkey = ENGINE_load_private_key(e, uri, + (UI_METHOD *)get_ui_method(), + &cb_data); + ENGINE_finish(e); + } + if (pkey == NULL) { + BIO_printf(bio_err, "Cannot load %s from engine\n", desc); + ERR_print_errors(bio_err); + } +#else + BIO_printf(bio_err, "Engines not supported for loading %s\n", desc); +#endif + } + } else { + (void)load_key_cert_crl(uri, may_stdin, pass, desc, &pkey, NULL, NULL); + } + + if (pkey == NULL) { + BIO_printf(bio_err, "Unable to load %s\n", desc); + ERR_print_errors(bio_err); + } + return pkey; +} + +static X509 *load_cert_pass(const char *uri, int maybe_stdin, + const char *pass, const char *desc) +{ + X509 *cert = NULL; + + if (desc == NULL) + desc = "certificate"; + (void)load_key_cert_crl(uri, maybe_stdin, pass, desc, NULL, &cert, NULL); + if (cert == NULL) { + BIO_printf(bio_err, "Unable to load %s\n", desc); + ERR_print_errors(bio_err); + } + return cert; +} +/* end TODO remove when PR #11755 is merged */ + +static char *opt_config = NULL; +#define CMP_SECTION "cmp" +#define SECTION_NAME_MAX 40 /* max length of section name */ +#define DEFAULT_SECTION "default" +static char *opt_section = CMP_SECTION; + +#undef PROG +#define PROG cmp_main +static char *prog = "cmp"; + +static int read_config(void); + +static CONF *conf = NULL; /* OpenSSL config file context structure */ +static OSSL_CMP_CTX *cmp_ctx = NULL; /* the client-side CMP context */ + +/* TODO remove when new setup_engine_flags() is in apps/lib/apps.c (PR #4277) */ +static +ENGINE *setup_engine_flags(const char *engine, unsigned int flags, int debug) +{ + return setup_engine(engine, debug); +} + +/* the type of cmp command we want to send */ +typedef enum { + CMP_IR, + CMP_KUR, + CMP_CR, + CMP_P10CR, + CMP_RR, + CMP_GENM +} cmp_cmd_t; + +/* message transfer */ +static char *opt_server = NULL; +static char server_port_s[32] = { '\0' }; +static int server_port = 0; +static char *opt_proxy = NULL; +static char *opt_no_proxy = NULL; +static char *opt_path = "/"; +static int opt_msg_timeout = -1; +static int opt_total_timeout = -1; + +/* server authentication */ +static char *opt_trusted = NULL; +static char *opt_untrusted = NULL; +static char *opt_srvcert = NULL; +static char *opt_recipient = NULL; +static char *opt_expect_sender = NULL; +static int opt_ignore_keyusage = 0; +static int opt_unprotected_errors = 0; +static char *opt_extracertsout = NULL; +static char *opt_cacertsout = NULL; + +/* client authentication */ +static char *opt_ref = NULL; +static char *opt_secret = NULL; +static char *opt_cert = NULL; +static char *opt_key = NULL; +static char *opt_keypass = NULL; +static char *opt_digest = NULL; +static char *opt_mac = NULL; +static char *opt_extracerts = NULL; +static int opt_unprotected_requests = 0; + +/* generic message */ +static char *opt_cmd_s = NULL; +static int opt_cmd = -1; +static char *opt_geninfo = NULL; +static char *opt_infotype_s = NULL; +static int opt_infotype = NID_undef; + +/* certificate enrollment */ +static char *opt_newkey = NULL; +static char *opt_newkeypass = NULL; +static char *opt_subject = NULL; +static char *opt_issuer = NULL; +static int opt_days = 0; +static char *opt_reqexts = NULL; +static char *opt_sans = NULL; +static int opt_san_nodefault = 0; +static char *opt_policies = NULL; +static char *opt_policy_oids = NULL; +static int opt_policy_oids_critical = 0; +static int opt_popo = OSSL_CRMF_POPO_NONE - 1; +static char *opt_csr = NULL; +static char *opt_out_trusted = NULL; +static int opt_implicit_confirm = 0; +static int opt_disable_confirm = 0; +static char *opt_certout = NULL; + +/* certificate enrollment and revocation */ +static char *opt_oldcert = NULL; +static int opt_revreason = CRL_REASON_NONE; + +/* credentials format */ +static char *opt_certform_s = "PEM"; +static int opt_certform = FORMAT_PEM; +static char *opt_keyform_s = "PEM"; +static int opt_keyform = FORMAT_PEM; +static char *opt_certsform_s = "PEM"; +static int opt_certsform = FORMAT_PEM; +static char *opt_otherpass = NULL; +static char *opt_engine = NULL; + +/* TLS connection */ +static int opt_tls_used = 0; +static char *opt_tls_cert = NULL; +static char *opt_tls_key = NULL; +static char *opt_tls_keypass = NULL; +static char *opt_tls_extra = NULL; +static char *opt_tls_trusted = NULL; +static char *opt_tls_host = NULL; + +/* client-side debugging */ +static int opt_batch = 0; +static int opt_repeat = 1; +static char *opt_reqin = NULL; +static char *opt_reqout = NULL; +static char *opt_rspin = NULL; +static char *opt_rspout = NULL; +static int opt_use_mock_srv = 0; + +/* server-side debugging */ +static char *opt_port = NULL; +static int opt_max_msgs = 0; + +static char *opt_srv_ref = NULL; +static char *opt_srv_secret = NULL; +static char *opt_srv_cert = NULL; +static char *opt_srv_key = NULL; +static char *opt_srv_keypass = NULL; + +static char *opt_srv_trusted = NULL; +static char *opt_srv_untrusted = NULL; +static char *opt_rsp_cert = NULL; +static char *opt_rsp_extracerts = NULL; +static char *opt_rsp_capubs = NULL; +static int opt_poll_count = 0; +static int opt_check_after = 1; +static int opt_grant_implicitconf = 0; + +static int opt_pkistatus = OSSL_CMP_PKISTATUS_accepted; +static int opt_failure = INT_MIN; +static int opt_failurebits = 0; +static char *opt_statusstring = NULL; +static int opt_send_error = 0; +static int opt_send_unprotected = 0; +static int opt_send_unprot_err = 0; +static int opt_accept_unprotected = 0; +static int opt_accept_unprot_err = 0; +static int opt_accept_raverified = 0; + +static X509_VERIFY_PARAM *vpm = NULL; + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_CONFIG, OPT_SECTION, + + OPT_CMD, OPT_INFOTYPE, OPT_GENINFO, + + OPT_NEWKEY, OPT_NEWKEYPASS, OPT_SUBJECT, OPT_ISSUER, + OPT_DAYS, OPT_REQEXTS, + OPT_SANS, OPT_SAN_NODEFAULT, + OPT_POLICIES, OPT_POLICY_OIDS, OPT_POLICY_OIDS_CRITICAL, + OPT_POPO, OPT_CSR, + OPT_OUT_TRUSTED, OPT_IMPLICIT_CONFIRM, OPT_DISABLE_CONFIRM, + OPT_CERTOUT, + + OPT_OLDCERT, OPT_REVREASON, + + OPT_SERVER, OPT_PROXY, OPT_NO_PROXY, OPT_PATH, + OPT_MSG_TIMEOUT, OPT_TOTAL_TIMEOUT, + + OPT_TRUSTED, OPT_UNTRUSTED, OPT_SRVCERT, + OPT_RECIPIENT, OPT_EXPECT_SENDER, + OPT_IGNORE_KEYUSAGE, OPT_UNPROTECTED_ERRORS, + OPT_EXTRACERTSOUT, OPT_CACERTSOUT, + + OPT_REF, OPT_SECRET, OPT_CERT, OPT_KEY, OPT_KEYPASS, + OPT_DIGEST, OPT_MAC, OPT_EXTRACERTS, + OPT_UNPROTECTED_REQUESTS, + + OPT_CERTFORM, OPT_KEYFORM, OPT_CERTSFORM, + OPT_OTHERPASS, +#ifndef OPENSSL_NO_ENGINE + OPT_ENGINE, +#endif + OPT_PROV_ENUM, + + OPT_TLS_USED, OPT_TLS_CERT, OPT_TLS_KEY, + OPT_TLS_KEYPASS, + OPT_TLS_EXTRA, OPT_TLS_TRUSTED, OPT_TLS_HOST, + + OPT_BATCH, OPT_REPEAT, + OPT_REQIN, OPT_REQOUT, OPT_RSPIN, OPT_RSPOUT, + + OPT_USE_MOCK_SRV, OPT_PORT, OPT_MAX_MSGS, + OPT_SRV_REF, OPT_SRV_SECRET, + OPT_SRV_CERT, OPT_SRV_KEY, OPT_SRV_KEYPASS, + OPT_SRV_TRUSTED, OPT_SRV_UNTRUSTED, + OPT_RSP_CERT, OPT_RSP_EXTRACERTS, OPT_RSP_CAPUBS, + OPT_POLL_COUNT, OPT_CHECK_AFTER, + OPT_GRANT_IMPLICITCONF, + OPT_PKISTATUS, OPT_FAILURE, + OPT_FAILUREBITS, OPT_STATUSSTRING, + OPT_SEND_ERROR, OPT_SEND_UNPROTECTED, + OPT_SEND_UNPROT_ERR, OPT_ACCEPT_UNPROTECTED, + OPT_ACCEPT_UNPROT_ERR, OPT_ACCEPT_RAVERIFIED, + + OPT_V_ENUM +} OPTION_CHOICE; + +const OPTIONS cmp_options[] = { + /* entries must be in the same order as enumerated above!! */ + {"help", OPT_HELP, '-', "Display this summary"}, + {"config", OPT_CONFIG, 's', + "Configuration file to use. \"\" = none. Default from env variable OPENSSL_CONF"}, + {"section", OPT_SECTION, 's', + "Section(s) in config file to get options from. \"\" = 'default'. Default 'cmp'"}, + + OPT_SECTION("Generic message"), + {"cmd", OPT_CMD, 's', "CMP request to send: ir/cr/kur/p10cr/rr/genm"}, + {"infotype", OPT_INFOTYPE, 's', + "InfoType name for requesting specific info in genm, e.g. 'signKeyPairTypes'"}, + {"geninfo", OPT_GENINFO, 's', + "generalInfo integer values to place in request PKIHeader with given OID"}, + {OPT_MORE_STR, 0, 0, + "specified in the form <OID>:int:<n>, e.g. \"1.2.3:int:987\""}, + + OPT_SECTION("Certificate enrollment"), + {"newkey", OPT_NEWKEY, 's', + "Private or public key for the requested cert. Default: CSR key or client key"}, + {"newkeypass", OPT_NEWKEYPASS, 's', "New private key pass phrase source"}, + {"subject", OPT_SUBJECT, 's', + "Distinguished Name (DN) of subject to use in the requested cert template"}, + {OPT_MORE_STR, 0, 0, + "For kur, default is the subject DN of the reference cert (see -oldcert);"}, + {OPT_MORE_STR, 0, 0, + "this default is used for ir and cr only if no Subject Alt Names are set"}, + {"issuer", OPT_ISSUER, 's', + "DN of the issuer to place in the requested certificate template"}, + {OPT_MORE_STR, 0, 0, + "also used as recipient if neither -recipient nor -srvcert are given"}, + {"days", OPT_DAYS, 'n', + "Requested validity time of the new certificate in number of days"}, + {"reqexts", OPT_REQEXTS, 's', + "Name of config file section defining certificate request extensions"}, + {"sans", OPT_SANS, 's', + "Subject Alt Names (IPADDR/DNS/URI) to add as (critical) cert req extension"}, + {"san_nodefault", OPT_SAN_NODEFAULT, '-', + "Do not take default SANs from reference certificate (see -oldcert)"}, + {"policies", OPT_POLICIES, 's', + "Name of config file section defining policies certificate request extension"}, + {"policy_oids", OPT_POLICY_OIDS, 's', + "Policy OID(s) to add as policies certificate request extension"}, + {"policy_oids_critical", OPT_POLICY_OIDS_CRITICAL, '-', + "Flag the policy OID(s) given with -policy_oids as critical"}, + {"popo", OPT_POPO, 'n', + "Proof-of-Possession (POPO) method to use for ir/cr/kur where"}, + {OPT_MORE_STR, 0, 0, + "-1 = NONE, 0 = RAVERIFIED, 1 = SIGNATURE (default), 2 = KEYENC"}, + {"csr", OPT_CSR, 's', + "CSR file in PKCS#10 format to use in p10cr for legacy support"}, + {"out_trusted", OPT_OUT_TRUSTED, 's', + "Certificates to trust when verifying newly enrolled certificates"}, + {"implicit_confirm", OPT_IMPLICIT_CONFIRM, '-', + "Request implicit confirmation of newly enrolled certificates"}, + {"disable_confirm", OPT_DISABLE_CONFIRM, '-', + "Do not confirm newly enrolled certificate w/o requesting implicit"}, + {OPT_MORE_STR, 0, 0, + "confirmation. WARNING: This leads to behavior violating RFC 4210"}, + {"certout", OPT_CERTOUT, 's', + "File to save newly enrolled certificate"}, + + OPT_SECTION("Certificate enrollment and revocation"), + + {"oldcert", OPT_OLDCERT, 's', + "Certificate to be updated (defaulting to -cert) or to be revoked in rr;"}, + {OPT_MORE_STR, 0, 0, + "also used as reference (defaulting to -cert) for subject DN and SANs."}, + {OPT_MORE_STR, 0, 0, + "Its issuer is used as recipient unless -srvcert, -recipient or -issuer given"}, + {"revreason", OPT_REVREASON, 'n', + "Reason code to include in revocation request (rr); possible values:"}, + {OPT_MORE_STR, 0, 0, + "0..6, 8..10 (see RFC5280, 5.3.1) or -1. Default -1 = none included"}, + + OPT_SECTION("Message transfer"), + {"server", OPT_SERVER, 's', + "[http[s]://]address[:port] of CMP server. Default port 80 or 443."}, + {OPT_MORE_STR, 0, 0, + "The address may be a DNS name or an IP address"}, + {"proxy", OPT_PROXY, 's', + "[http[s]://]address[:port][/path] of HTTP(S) proxy to use; path is ignored"}, + {"no_proxy", OPT_NO_PROXY, 's', + "List of addresses of servers not to use HTTP(S) proxy for"}, + {OPT_MORE_STR, 0, 0, + "Default from environment variable 'no_proxy', else 'NO_PROXY', else none"}, + {"path", OPT_PATH, 's', + "HTTP path (aka CMP alias) at the CMP server. Default \"/\""}, + {"msg_timeout", OPT_MSG_TIMEOUT, 'n', + "Timeout per CMP message round trip (or 0 for none). Default 120 seconds"}, + {"total_timeout", OPT_TOTAL_TIMEOUT, 'n', + "Overall time an enrollment incl. polling may take. Default 0 = infinite"}, + + OPT_SECTION("Server authentication"), + {"trusted", OPT_TRUSTED, 's', + "Trusted certs used for CMP server authentication when verifying responses"}, + {OPT_MORE_STR, 0, 0, "unless -srvcert is given"}, + {"untrusted", OPT_UNTRUSTED, 's', + "Intermediate certs for chain construction verifying CMP/TLS/enrolled certs"}, + {"srvcert", OPT_SRVCERT, 's', + "Specific CMP server cert to use and trust directly when verifying responses"}, + {"recipient", OPT_RECIPIENT, 's', + "Distinguished Name (DN) of the recipient to use unless -srvcert is given"}, + {"expect_sender", OPT_EXPECT_SENDER, 's', + "DN of expected response sender. Defaults to DN of -srvcert, if provided"}, + {"ignore_keyusage", OPT_IGNORE_KEYUSAGE, '-', + "Ignore CMP signer cert key usage, else 'digitalSignature' must be allowed"}, + {"unprotected_errors", OPT_UNPROTECTED_ERRORS, '-', + "Accept missing or invalid protection of regular error messages and negative"}, + {OPT_MORE_STR, 0, 0, + "certificate responses (ip/cp/kup), revocation responses (rp), and PKIConf"}, + {OPT_MORE_STR, 0, 0, + "WARNING: This setting leads to behavior allowing violation of RFC 4210"}, + {"extracertsout", OPT_EXTRACERTSOUT, 's', + "File to save extra certificates received in the extraCerts field"}, + {"cacertsout", OPT_CACERTSOUT, 's', + "File to save CA certificates received in the caPubs field of 'ip' messages"}, + + OPT_SECTION("Client authentication"), + {"ref", OPT_REF, 's', + "Reference value to use as senderKID in case no -cert is given"}, + {"secret", OPT_SECRET, 's', + "Password source for client authentication with a pre-shared key (secret)"}, + {"cert", OPT_CERT, 's', + "Client's current certificate (needed unless using -secret for PBM);"}, + {OPT_MORE_STR, 0, 0, + "any further certs included are appended in extraCerts field"}, + {"key", OPT_KEY, 's', "Private key for the client's current certificate"}, + {"keypass", OPT_KEYPASS, 's', + "Client private key (and cert and old cert file) pass phrase source"}, + {"digest", OPT_DIGEST, 's', + "Digest to use in message protection and POPO signatures. Default \"sha256\""}, + {"mac", OPT_MAC, 's', + "MAC algorithm to use in PBM-based message protection. Default \"hmac-sha1\""}, + {"extracerts", OPT_EXTRACERTS, 's', + "Certificates to append in extraCerts field of outgoing messages"}, + {"unprotected_requests", OPT_UNPROTECTED_REQUESTS, '-', + "Send messages without CMP-level protection"}, + + OPT_SECTION("Credentials format"), + {"certform", OPT_CERTFORM, 's', + "Format (PEM or DER) to use when saving a certificate to a file. Default PEM"}, + {OPT_MORE_STR, 0, 0, + "This also determines format to use for writing (not supported for P12)"}, + {"keyform", OPT_KEYFORM, 's', + "Format to assume when reading key files. Default PEM"}, + {"certsform", OPT_CERTSFORM, 's', + "Format (PEM/DER/P12) to try first reading multiple certs. Default PEM"}, + {"otherpass", OPT_OTHERPASS, 's', + "Pass phrase source potentially needed for loading certificates of others"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', + "Use crypto engine with given identifier, possibly a hardware device."}, + {OPT_MORE_STR, 0, 0, + "Engines may be defined in OpenSSL config file engine section."}, + {OPT_MORE_STR, 0, 0, + "Options like -key specifying keys held in the engine can give key IDs"}, + {OPT_MORE_STR, 0, 0, + "prefixed by 'engine:', e.g. '-key engine:pkcs11:object=mykey;pin-value=1234'"}, +#endif + OPT_PROV_OPTIONS, + + OPT_SECTION("TLS connection"), + {"tls_used", OPT_TLS_USED, '-', + "Enable using TLS (also when other TLS options are not set)"}, + {"tls_cert", OPT_TLS_CERT, 's', + "Client's TLS certificate. May include chain to be provided to TLS server"}, + {"tls_key", OPT_TLS_KEY, 's', + "Private key for the client's TLS certificate"}, + {"tls_keypass", OPT_TLS_KEYPASS, 's', + "Pass phrase source for the client's private TLS key (and TLS cert file)"}, + {"tls_extra", OPT_TLS_EXTRA, 's', + "Extra certificates to provide to TLS server during TLS handshake"}, + {"tls_trusted", OPT_TLS_TRUSTED, 's', + "Trusted certificates to use for verifying the TLS server certificate;"}, + {OPT_MORE_STR, 0, 0, "this implies host name validation"}, + {"tls_host", OPT_TLS_HOST, 's', + "Address to be checked (rather than -server) during TLS host name validation"}, + + OPT_SECTION("Client-side debugging"), + {"batch", OPT_BATCH, '-', + "Do not interactively prompt for input when a password is required etc."}, + {"repeat", OPT_REPEAT, 'n', + "Invoke the transaction the given number of times. Default 1"}, + {"reqin", OPT_REQIN, 's', "Take sequence of CMP requests from file(s)"}, + {"reqout", OPT_REQOUT, 's', "Save sequence of CMP requests to file(s)"}, + {"rspin", OPT_RSPIN, 's', + "Process sequence of CMP responses provided in file(s), skipping server"}, + {"rspout", OPT_RSPOUT, 's', "Save sequence of CMP responses to file(s)"}, + + {"use_mock_srv", OPT_USE_MOCK_SRV, '-', "Use mock server at API level, bypassing HTTP"}, + + OPT_SECTION("Mock server"), + {"port", OPT_PORT, 's', "Act as HTTP mock server listening on given port"}, + {"max_msgs", OPT_MAX_MSGS, 'n', + "max number of messages handled by HTTP mock server. Default: 0 = unlimited"}, + + {"srv_ref", OPT_SRV_REF, 's', + "Reference value to use as senderKID of server in case no -srv_cert is given"}, + {"srv_secret", OPT_SRV_SECRET, 's', + "Password source for server authentication with a pre-shared key (secret)"}, + {"srv_cert", OPT_SRV_CERT, 's', "Certificate of the server"}, + {"srv_key", OPT_SRV_KEY, 's', + "Private key used by the server for signing messages"}, + {"srv_keypass", OPT_SRV_KEYPASS, 's', + "Server private key (and cert) file pass phrase source"}, + + {"srv_trusted", OPT_SRV_TRUSTED, 's', + "Trusted certificates for client authentication"}, + {"srv_untrusted", OPT_SRV_UNTRUSTED, 's', + "Intermediate certs for constructing chains for CMP protection by client"}, + {"rsp_cert", OPT_RSP_CERT, 's', + "Certificate to be returned as mock enrollment result"}, + {"rsp_extracerts", OPT_RSP_EXTRACERTS, 's', + "Extra certificates to be included in mock certification responses"}, + {"rsp_capubs", OPT_RSP_CAPUBS, 's', + "CA certificates to be included in mock ip response"}, + {"poll_count", OPT_POLL_COUNT, 'n', + "Number of times the client must poll before receiving a certificate"}, + {"check_after", OPT_CHECK_AFTER, 'n', + "The check_after value (time to wait) to include in poll response"}, + {"grant_implicitconf", OPT_GRANT_IMPLICITCONF, '-', + "Grant implicit confirmation of newly enrolled certificate"}, + + {"pkistatus", OPT_PKISTATUS, 'n', + "PKIStatus to be included in server response. Possible values: 0..6"}, + {"failure", OPT_FAILURE, 'n', + "A single failure info bit number to include in server response, 0..26"}, + {"failurebits", OPT_FAILUREBITS, 'n', + "Number representing failure bits to include in server response, 0..2^27 - 1"}, + {"statusstring", OPT_STATUSSTRING, 's', + "Status string to be included in server response"}, + {"send_error", OPT_SEND_ERROR, '-', + "Force server to reply with error message"}, + {"send_unprotected", OPT_SEND_UNPROTECTED, '-', + "Send response messages without CMP-level protection"}, + {"send_unprot_err", OPT_SEND_UNPROT_ERR, '-', + "In case of negative responses, server shall send unprotected error messages,"}, + {OPT_MORE_STR, 0, 0, + "certificate responses (ip/cp/kup), and revocation responses (rp)."}, + {OPT_MORE_STR, 0, 0, + "WARNING: This setting leads to behavior violating RFC 4210"}, + {"accept_unprotected", OPT_ACCEPT_UNPROTECTED, '-', + "Accept missing or invalid protection of requests"}, + {"accept_unprot_err", OPT_ACCEPT_UNPROT_ERR, '-', + "Accept unprotected error messages from client"}, + {"accept_raverified", OPT_ACCEPT_RAVERIFIED, '-', + "Accept RAVERIFIED as proof-of-possession (POPO)"}, + + OPT_V_OPTIONS, + {NULL} +}; + +typedef union { + char **txt; + int *num; + long *num_long; +} varref; +static varref cmp_vars[] = { /* must be in same order as enumerated above! */ + {&opt_config}, {&opt_section}, + + {&opt_cmd_s}, {&opt_infotype_s}, {&opt_geninfo}, + + {&opt_newkey}, {&opt_newkeypass}, {&opt_subject}, {&opt_issuer}, + {(char **)&opt_days}, {&opt_reqexts}, + {&opt_sans}, {(char **)&opt_san_nodefault}, + {&opt_policies}, {&opt_policy_oids}, {(char **)&opt_policy_oids_critical}, + {(char **)&opt_popo}, {&opt_csr}, + {&opt_out_trusted}, + {(char **)&opt_implicit_confirm}, {(char **)&opt_disable_confirm}, + {&opt_certout}, + + {&opt_oldcert}, {(char **)&opt_revreason}, + + {&opt_server}, {&opt_proxy}, {&opt_no_proxy}, {&opt_path}, + {(char **)&opt_msg_timeout}, {(char **)&opt_total_timeout}, + + {&opt_trusted}, {&opt_untrusted}, {&opt_srvcert}, + {&opt_recipient}, {&opt_expect_sender}, + {(char **)&opt_ignore_keyusage}, {(char **)&opt_unprotected_errors}, + {&opt_extracertsout}, {&opt_cacertsout}, + + {&opt_ref}, {&opt_secret}, {&opt_cert}, {&opt_key}, {&opt_keypass}, + {&opt_digest}, {&opt_mac}, {&opt_extracerts}, + {(char **)&opt_unprotected_requests}, + + {&opt_certform_s}, {&opt_keyform_s}, {&opt_certsform_s}, + {&opt_otherpass}, +#ifndef OPENSSL_NO_ENGINE + {&opt_engine}, +#endif + + {(char **)&opt_tls_used}, {&opt_tls_cert}, {&opt_tls_key}, + {&opt_tls_keypass}, + {&opt_tls_extra}, {&opt_tls_trusted}, {&opt_tls_host}, + + {(char **)&opt_batch}, {(char **)&opt_repeat}, + {&opt_reqin}, {&opt_reqout}, {&opt_rspin}, {&opt_rspout}, + + {(char **)&opt_use_mock_srv}, {&opt_port}, {(char **)&opt_max_msgs}, + {&opt_srv_ref}, {&opt_srv_secret}, + {&opt_srv_cert}, {&opt_srv_key}, {&opt_srv_keypass}, + {&opt_srv_trusted}, {&opt_srv_untrusted}, + {&opt_rsp_cert}, {&opt_rsp_extracerts}, {&opt_rsp_capubs}, + {(char **)&opt_poll_count}, {(char **)&opt_check_after}, + {(char **)&opt_grant_implicitconf}, + {(char **)&opt_pkistatus}, {(char **)&opt_failure}, + {(char **)&opt_failurebits}, {&opt_statusstring}, + {(char **)&opt_send_error}, {(char **)&opt_send_unprotected}, + {(char **)&opt_send_unprot_err}, {(char **)&opt_accept_unprotected}, + {(char **)&opt_accept_unprot_err}, {(char **)&opt_accept_raverified}, + + {NULL} +}; + +#ifndef NDEBUG +# define FUNC (strcmp(OPENSSL_FUNC, "(unknown function)") == 0 \ + ? "CMP" : "OPENSSL_FUNC") +# define PRINT_LOCATION(bio) BIO_printf(bio, "%s:%s:%d:", \ + FUNC, OPENSSL_FILE, OPENSSL_LINE) +#else +# define PRINT_LOCATION(bio) ((void)0) +#endif +#define CMP_print(bio, prefix, msg, a1, a2, a3) \ + (PRINT_LOCATION(bio), \ + BIO_printf(bio, "CMP %s: " msg "\n", prefix, a1, a2, a3)) +#define CMP_INFO(msg, a1, a2, a3) CMP_print(bio_out, "info", msg, a1, a2, a3) +#define CMP_info(msg) CMP_INFO(msg"%s%s%s", "", "", "") +#define CMP_info1(msg, a1) CMP_INFO(msg"%s%s", a1, "", "") +#define CMP_info2(msg, a1, a2) CMP_INFO(msg"%s", a1, a2, "") +#define CMP_info3(msg, a1, a2, a3) CMP_INFO(msg, a1, a2, a3) +#define CMP_WARN(m, a1, a2, a3) CMP_print(bio_out, "warning", m, a1, a2, a3) +#define CMP_warn(msg) CMP_WARN(msg"%s%s%s", "", "", "") +#define CMP_warn1(msg, a1) CMP_WARN(msg"%s%s", a1, "", "") +#define CMP_warn2(msg, a1, a2) CMP_WARN(msg"%s", a1, a2, "") +#define CMP_warn3(msg, a1, a2, a3) CMP_WARN(msg, a1, a2, a3) +#define CMP_ERR(msg, a1, a2, a3) CMP_print(bio_err, "error", msg, a1, a2, a3) +#define CMP_err(msg) CMP_ERR(msg"%s%s%s", "", "", "") +#define CMP_err1(msg, a1) CMP_ERR(msg"%s%s", a1, "", "") +#define CMP_err2(msg, a1, a2) CMP_ERR(msg"%s", a1, a2, "") +#define CMP_err3(msg, a1, a2, a3) CMP_ERR(msg, a1, a2, a3) + +static int print_to_bio_out(const char *func, const char *file, int line, + OSSL_CMP_severity level, const char *msg) +{ + return OSSL_CMP_print_to_bio(bio_out, func, file, line, level, msg); +} + +/* code duplicated from crypto/cmp/cmp_util.c */ +static int sk_X509_add1_cert(STACK_OF(X509) *sk, X509 *cert, + int no_dup, int prepend) +{ + if (no_dup) { + /* + * not using sk_X509_set_cmp_func() and sk_X509_find() + * because this re-orders the certs on the stack + */ + int i; + + for (i = 0; i < sk_X509_num(sk); i++) { + if (X509_cmp(sk_X509_value(sk, i), cert) == 0) + return 1; + } + } + if (!X509_up_ref(cert)) + return 0; + if (!sk_X509_insert(sk, cert, prepend ? 0 : -1)) { + X509_free(cert); + return 0; + } + return 1; +} + +/* code duplicated from crypto/cmp/cmp_util.c */ +static int sk_X509_add1_certs(STACK_OF(X509) *sk, STACK_OF(X509) *certs, + int no_self_signed, int no_dups, int prepend) +/* compiler would allow 'const' for the list of certs, yet they are up-ref'ed */ +{ + int i; + + if (sk == NULL) + return 0; + if (certs == NULL) + return 1; + for (i = 0; i < sk_X509_num(certs); i++) { + X509 *cert = sk_X509_value(certs, i); + + if (!no_self_signed || X509_check_issued(cert, cert) != X509_V_OK) { + if (!sk_X509_add1_cert(sk, cert, no_dups, prepend)) + return 0; + } + } + return 1; +} + +/* TODO potentially move to apps/lib/apps.c */ +static char *next_item(char *opt) /* in list separated by comma and/or space */ +{ + /* advance to separator (comma or whitespace), if any */ + while (*opt != ',' && !isspace(*opt) && *opt != '\0') { + if (*opt == '\\' && opt[1] != '\0') + /* skip and unescape '\' escaped char */ + memmove(opt, opt + 1, strlen(opt)); + opt++; + } + if (*opt != '\0') { + /* terminate current item */ + *opt++ = '\0'; + /* skip over any whitespace after separator */ + while (isspace |