Skip to content

Commit

Permalink
CMP: add support for genm with crlStatusList and genp with crls
Browse files Browse the repository at this point in the history
Introduce the capability to retrieve and update Certificate Revocation Lists
(CRLs) in the CMP client, as specified in section 4.3.4 of RFC 9483.

To request a CRL update, the CMP client can send a genm message with the
option -infotype crlStatusList. The server will respond with a genp message
containing the updated CRL, using the -infoType id-it-crls. The client can
then save the CRL in a specified file using the -crlout parameter.

Co-authored-by: Rajeev Ranjan <ranjan.rajeev@siemens.com>

Reviewed-by: Todd Short <todd.short@me.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from #23768)
  • Loading branch information
DDvO authored and t8m committed May 1, 2024
1 parent 1848c56 commit 40a200f
Show file tree
Hide file tree
Showing 27 changed files with 905 additions and 50 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ OpenSSL 3.3

*Neil Horman*

* Added support for requesting CRL in CMP.

*Rajeev Ranjan, Siemens AG*

* Added `-set_issuer` and `-set_subject` options to `openssl x509` to
override the Issuer and Subject when creating a certificate. The `-subj`
option now is an alias for `-set_subject`.
Expand Down
163 changes: 159 additions & 4 deletions apps/cmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ static char *opt_oldwithold = NULL;
static char *opt_newwithnew = NULL;
static char *opt_newwithold = NULL;
static char *opt_oldwithnew = NULL;
static char *opt_crlcert = NULL;
static char *opt_oldcrl = NULL;
static char *opt_crlout = NULL;

/* client authentication */
static char *opt_ref = NULL;
Expand Down Expand Up @@ -143,6 +146,8 @@ static int opt_revreason = CRL_REASON_NONE;
/* credentials format */
static char *opt_certform_s = "PEM";
static int opt_certform = FORMAT_PEM;
static char *opt_crlform_s = "DER";
static int opt_crlform = FORMAT_ASN1;
static char *opt_keyform_s = NULL;
static int opt_keyform = FORMAT_UNDEF;
static char *opt_otherpass = NULL;
Expand Down Expand Up @@ -187,6 +192,7 @@ static char *opt_srv_trusted = NULL;
static char *opt_srv_untrusted = NULL;
static char *opt_ref_cert = NULL;
static char *opt_rsp_cert = NULL;
static char *opt_rsp_crl = NULL;
static char *opt_rsp_extracerts = NULL;
static char *opt_rsp_capubs = NULL;
static char *opt_rsp_newwithnew = NULL;
Expand Down Expand Up @@ -237,12 +243,13 @@ typedef enum OPTION_choice {
OPT_IGNORE_KEYUSAGE, OPT_UNPROTECTED_ERRORS, OPT_NO_CACHE_EXTRACERTS,
OPT_SRVCERTOUT, OPT_EXTRACERTSOUT, OPT_CACERTSOUT,
OPT_OLDWITHOLD, OPT_NEWWITHNEW, OPT_NEWWITHOLD, OPT_OLDWITHNEW,
OPT_CRLCERT, OPT_OLDCRL, OPT_CRLOUT,

OPT_REF, OPT_SECRET, OPT_CERT, OPT_OWN_TRUSTED, OPT_KEY, OPT_KEYPASS,
OPT_DIGEST, OPT_MAC, OPT_EXTRACERTS,
OPT_UNPROTECTED_REQUESTS,

OPT_CERTFORM, OPT_KEYFORM,
OPT_CERTFORM, OPT_CRLFORM, OPT_KEYFORM,
OPT_OTHERPASS,
#ifndef OPENSSL_NO_ENGINE
OPT_ENGINE,
Expand All @@ -267,7 +274,7 @@ typedef enum OPTION_choice {
OPT_SRV_REF, OPT_SRV_SECRET,
OPT_SRV_CERT, OPT_SRV_KEY, OPT_SRV_KEYPASS,
OPT_SRV_TRUSTED, OPT_SRV_UNTRUSTED,
OPT_REF_CERT, OPT_RSP_CERT, OPT_RSP_EXTRACERTS, OPT_RSP_CAPUBS,
OPT_REF_CERT, OPT_RSP_CERT, OPT_RSP_CRL, OPT_RSP_EXTRACERTS, OPT_RSP_CAPUBS,
OPT_RSP_NEWWITHNEW, OPT_RSP_NEWWITHOLD, OPT_RSP_OLDWITHNEW,
OPT_POLL_COUNT, OPT_CHECK_AFTER,
OPT_GRANT_IMPLICITCONF,
Expand Down Expand Up @@ -428,6 +435,12 @@ const OPTIONS cmp_options[] = {
"File to save NewWithOld cert received in genp of type rootCaKeyUpdate"},
{ "oldwithnew", OPT_OLDWITHNEW, 's',
"File to save OldWithNew cert received in genp of type rootCaKeyUpdate"},
{ "crlcert", OPT_CRLCERT, 's',
"certificate to request a CRL for in genm of type crlStatusList"},
{ "oldcrl", OPT_OLDCRL, 's',
"CRL to request update for in genm of type crlStatusList"},
{ "crlout", OPT_CRLOUT, 's',
"File to save new CRL received in genp of type 'crls'"},

OPT_SECTION("Client authentication"),
{"ref", OPT_REF, 's',
Expand Down Expand Up @@ -459,6 +472,8 @@ const OPTIONS cmp_options[] = {
OPT_SECTION("Credentials format"),
{"certform", OPT_CERTFORM, 's',
"Format (PEM or DER) to use when saving a certificate to a file. Default PEM"},
{"crlform", OPT_CRLFORM, 's',
"Format (PEM or DER) to use when saving a CRL to a file. Default DER"},
{"keyform", OPT_KEYFORM, 's',
"Format of the key input (ENGINE, other values ignored)"},
{"otherpass", OPT_OTHERPASS, 's',
Expand Down Expand Up @@ -544,6 +559,8 @@ const OPTIONS cmp_options[] = {
"Certificate to be expected for rr and any oldCertID in kur messages"},
{"rsp_cert", OPT_RSP_CERT, 's',
"Certificate to be returned as mock enrollment result"},
{"rsp_crl", OPT_RSP_CRL, 's',
"CRL to be returned in genp of type crls"},
{"rsp_extracerts", OPT_RSP_EXTRACERTS, 's',
"Extra certificates to be included in mock certification responses"},
{"rsp_capubs", OPT_RSP_CAPUBS, 's',
Expand Down Expand Up @@ -623,13 +640,14 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */
{(char **)&opt_no_cache_extracerts},
{&opt_srvcertout}, {&opt_extracertsout}, {&opt_cacertsout},
{&opt_oldwithold}, {&opt_newwithnew}, {&opt_newwithold}, {&opt_oldwithnew},
{&opt_crlcert}, {&opt_oldcrl}, {&opt_crlout},

{&opt_ref}, {&opt_secret},
{&opt_cert}, {&opt_own_trusted}, {&opt_key}, {&opt_keypass},
{&opt_digest}, {&opt_mac}, {&opt_extracerts},
{(char **)&opt_unprotected_requests},

{&opt_certform_s}, {&opt_keyform_s},
{&opt_certform_s}, {&opt_crlform_s}, {&opt_keyform_s},
{&opt_otherpass},
#ifndef OPENSSL_NO_ENGINE
{&opt_engine},
Expand All @@ -652,7 +670,8 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */
{&opt_srv_ref}, {&opt_srv_secret},
{&opt_srv_cert}, {&opt_srv_key}, {&opt_srv_keypass},
{&opt_srv_trusted}, {&opt_srv_untrusted},
{&opt_ref_cert}, {&opt_rsp_cert}, {&opt_rsp_extracerts}, {&opt_rsp_capubs},
{&opt_ref_cert}, {&opt_rsp_cert}, {&opt_rsp_crl},
{&opt_rsp_extracerts}, {&opt_rsp_capubs},
{&opt_rsp_newwithnew}, {&opt_rsp_newwithold}, {&opt_rsp_oldwithnew},

{(char **)&opt_poll_count}, {(char **)&opt_check_after},
Expand Down Expand Up @@ -1010,6 +1029,19 @@ static int setup_certs(char *files, const char *desc, void *ctx,
return ok;
}

static int setup_mock_crlout(void *ctx, const char *file, const char *desc)
{
X509_CRL *crl;
int ok;

if (file == NULL)
return 1;
if ((crl = load_crl(file, FORMAT_UNDEF, 0, desc)) == NULL)
return 0;
ok = ossl_cmp_mock_srv_set1_crlOut(ctx, crl);
X509_CRL_free(crl);
return ok;
}
/*
* parse and transform some options, checking their syntax.
* Returns 1 on success, 0 on error
Expand Down Expand Up @@ -1057,6 +1089,11 @@ static int transform_opts(void)
CMP_err("unknown option given for certificate storing format");
return 0;
}
if (opt_crlform_s != NULL
&& !opt_format(opt_crlform_s, OPT_FMT_PEMDER, &opt_crlform)) {
CMP_err("unknown option given for CRL storing format");
return 0;
}

return 1;
}
Expand Down Expand Up @@ -1152,6 +1189,9 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine)
(add_X509_fn_t)ossl_cmp_mock_srv_set1_certOut))
goto err;
}
if (!setup_mock_crlout(srv_ctx, opt_rsp_crl,
"CRL to be returned by the mock server"))
goto err;
if (!setup_certs(opt_rsp_extracerts,
"CMP extra certificates for mock server", srv_ctx,
(add_X509_stack_fn_t)ossl_cmp_mock_srv_set1_chainOut))
Expand Down Expand Up @@ -2248,6 +2288,18 @@ static int write_cert(BIO *bio, X509 *cert)
return 0;
}

static int write_crl(BIO *bio, X509_CRL *crl)
{
if (opt_crlform != FORMAT_PEM && opt_crlform != FORMAT_ASN1) {
BIO_printf(bio_err, "error: unsupported type '%s' for writing CRLs\n",
opt_crlform_s);
return 0;
}

return opt_crlform == FORMAT_PEM ? PEM_write_bio_X509_CRL(bio, crl)
: i2d_X509_CRL_bio(bio, crl);
}

/*
* If file != NULL writes out a stack of certs to the given file.
* If certs is NULL, the file is emptied.
Expand Down Expand Up @@ -2295,6 +2347,35 @@ static int save_free_certs(STACK_OF(X509) *certs,
return n;
}

static int save_crl(X509_CRL *crl,
const char *file, const char *desc)
{
BIO *bio = NULL;
int res = 0;

if (file == NULL)
return 1;
if (crl != NULL)
CMP_info2("received %s, saving to file '%s'", desc, file);

if ((bio = BIO_new(BIO_s_file())) == NULL
|| !BIO_write_filename(bio, (char *)file)) {
CMP_err2("could not open file '%s' for writing %s",
file, desc);
goto end;
}

if (!write_crl(bio, crl)) {
CMP_err2("cannot write %s to file '%s'", desc, file);
goto end;
}
res = 1;

end:
BIO_free(bio);
return res;
}

static int delete_file(const char *file, const char *desc)
{
if (file == NULL)
Expand Down Expand Up @@ -2328,6 +2409,13 @@ static int save_cert_or_delete(X509 *cert, const char *file, const char *desc)
}
}

static int save_crl_or_delete(X509_CRL *crl, const char *file, const char *desc)
{
if (file == NULL)
return 1;
return (crl == NULL) ? delete_file(file, desc) : save_crl(crl, file, desc);
}

static int print_itavs(const STACK_OF(OSSL_CMP_ITAV) *itavs)
{
int i, ret = 1;
Expand Down Expand Up @@ -2727,6 +2815,15 @@ static int get_opts(int argc, char **argv)
case OPT_OLDWITHNEW:
opt_oldwithnew = opt_str();
break;
case OPT_CRLCERT:
opt_crlcert = opt_str();
break;
case OPT_OLDCRL:
opt_oldcrl = opt_str();
break;
case OPT_CRLOUT:
opt_crlout = opt_str();
break;

case OPT_V_CASES:
if (!opt_verify(o, vpm))
Expand Down Expand Up @@ -2822,6 +2919,9 @@ static int get_opts(int argc, char **argv)
case OPT_CERTFORM:
opt_certform_s = opt_str();
break;
case OPT_CRLFORM:
opt_crlform_s = opt_str();
break;
case OPT_KEYFORM:
opt_keyform_s = opt_str();
break;
Expand Down Expand Up @@ -2905,6 +3005,9 @@ static int get_opts(int argc, char **argv)
case OPT_RSP_CERT:
opt_rsp_cert = opt_str();
break;
case OPT_RSP_CRL:
opt_rsp_crl = opt_str();
break;
case OPT_RSP_EXTRACERTS:
opt_rsp_extracerts = opt_str();
break;
Expand Down Expand Up @@ -3141,6 +3244,58 @@ static int do_genm(OSSL_CMP_CTX *ctx)
end_upd:
X509_free(oldwithold);
return res;
} else if (opt_infotype == NID_id_it_crlStatusList) {
X509_CRL *oldcrl = NULL, *crl = NULL;
X509 *crlcert = NULL;
int res = 0;
const char *desc = "CRL from genp of type 'crls'";

if (opt_oldcrl == NULL && opt_crlcert == NULL) {
CMP_err("Missing -oldcrl and no -crlcert given for -infotype crlStatusList");
return 0;
}
if (opt_crlout == NULL) {
CMP_err("Missing -crlout for -infotype crlStatusList");
return 0;
}

if (opt_crlcert != NULL) {
crlcert = load_cert_pwd(opt_crlcert, opt_otherpass,
"Cert for genm with -infotype crlStatusList");
if (crlcert == NULL)
goto end_crlupd;
}

if (opt_oldcrl != NULL) {
oldcrl = load_crl(opt_oldcrl, FORMAT_UNDEF, 0,
"CRL for genm with -infotype crlStatusList");
if (oldcrl == NULL)
goto end_crlupd;
}

if (opt_oldcrl != NULL && opt_crlcert != NULL) {
if (X509_NAME_cmp(X509_CRL_get_issuer(oldcrl),
X509_get_issuer_name(crlcert))
!= 0)
CMP_warn("-oldcrl and -crlcert have different issuer");
}

if (!OSSL_CMP_get1_crlUpdate(ctx, crlcert, oldcrl, &crl))
goto end_crlupd;

if (crl == NULL)
CMP_info("no CRL update available");
if (!save_crl_or_delete(crl, opt_crlout, desc))
goto end_crlupd;

res = 1;

end_crlupd:
X509_free(crlcert);
X509_CRL_free(oldcrl);
X509_CRL_free(crl);
return res;

} else {
OSSL_CMP_ITAV *req;
STACK_OF(OSSL_CMP_ITAV) *itavs;
Expand Down
1 change: 1 addition & 0 deletions apps/include/cmp_mock_srv.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ void ossl_cmp_mock_srv_free(OSSL_CMP_SRV_CTX *srv_ctx);

int ossl_cmp_mock_srv_set1_refCert(OSSL_CMP_SRV_CTX *srv_ctx, X509 *cert);
int ossl_cmp_mock_srv_set1_certOut(OSSL_CMP_SRV_CTX *srv_ctx, X509 *cert);
int ossl_cmp_mock_srv_set1_crlOut(OSSL_CMP_SRV_CTX *srv_ctx, X509_CRL *crl);
int ossl_cmp_mock_srv_set1_chainOut(OSSL_CMP_SRV_CTX *srv_ctx,
STACK_OF(X509) *chain);
int ossl_cmp_mock_srv_set1_caPubsOut(OSSL_CMP_SRV_CTX *srv_ctx,
Expand Down

0 comments on commit 40a200f

Please sign in to comment.