Also change ossl_cmp_ctx_set0_validatedSrvCert() to ossl_cmp_ctx_set1_validatedSrvCert(),
and add respective tests as well as the -srvcertout CLI option using the new function.
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
(Merged from https://github.com/openssl/openssl/pull/18656)
static char *opt_expect_sender = NULL;
static int opt_ignore_keyusage = 0;
static int opt_unprotected_errors = 0;
+static char *opt_srvcertout = NULL;
static char *opt_extracertsout = NULL;
static char *opt_cacertsout = NULL;
OPT_TRUSTED, OPT_UNTRUSTED, OPT_SRVCERT,
OPT_EXPECT_SENDER,
OPT_IGNORE_KEYUSAGE, OPT_UNPROTECTED_ERRORS,
- OPT_EXTRACERTSOUT, OPT_CACERTSOUT,
+ OPT_SRVCERTOUT, OPT_EXTRACERTSOUT, OPT_CACERTSOUT,
OPT_REF, OPT_SECRET, OPT_CERT, OPT_OWN_TRUSTED, OPT_KEY, OPT_KEYPASS,
OPT_DIGEST, OPT_MAC, OPT_EXTRACERTS,
"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"},
+ { "srvcertout", OPT_SRVCERTOUT, 's',
+ "File to save the server cert used and validated for CMP response protection"},
{"extracertsout", OPT_EXTRACERTSOUT, 's',
"File to save extra certificates received in the extraCerts field"},
{"cacertsout", OPT_CACERTSOUT, 's',
{&opt_trusted}, {&opt_untrusted}, {&opt_srvcert},
{&opt_expect_sender},
{(char **)&opt_ignore_keyusage}, {(char **)&opt_unprotected_errors},
- {&opt_extracertsout}, {&opt_cacertsout},
+ {&opt_srvcertout}, {&opt_extracertsout}, {&opt_cacertsout},
{&opt_ref}, {&opt_secret},
{&opt_cert}, {&opt_own_trusted}, {&opt_key}, {&opt_keypass},
if (opt_mac != NULL) {
int mac = OBJ_ln2nid(opt_mac);
+
if (mac == NID_undef) {
CMP_err1("MAC algorithm name not recognized: '%s'", opt_mac);
return 0;
}
/*
- * If destFile != NULL writes out a stack of certs to the given file.
- * In any case frees the certs.
+ * If file != NULL writes out a stack of certs to the given file.
+ * If certs is NULL, the file is emptied.
+ * Frees the certs if present.
* Depending on options use either PEM or DER format,
* where DER does not make much sense for writing more than one cert!
* Returns number of written certificates on success, -1 on error.
*/
-static int save_free_certs(OSSL_CMP_CTX *ctx,
- STACK_OF(X509) *certs, char *destFile, char *desc)
+static int save_free_certs(OSSL_CMP_CTX *ctx, STACK_OF(X509) *certs,
+ const char *file, const char *desc)
{
BIO *bio = NULL;
int i;
- int n = sk_X509_num(certs);
+ int n = sk_X509_num(certs /* may be NULL */);
- if (destFile == NULL)
+ if (n < 0)
+ n = 0;
+ if (file == NULL)
goto end;
- CMP_info3("received %d %s certificate(s), saving to file '%s'",
- n, desc, destFile);
+ if (certs != NULL)
+ CMP_info3("received %d %s certificate(s), saving to file '%s'",
+ n, desc, file);
if (n > 1 && opt_certform != FORMAT_PEM)
CMP_warn("saving more than one certificate in non-PEM format");
- if (destFile == NULL || (bio = BIO_new(BIO_s_file())) == NULL
- || !BIO_write_filename(bio, (char *)destFile)) {
- CMP_err1("could not open file '%s' for writing", destFile);
+ if ((bio = BIO_new(BIO_s_file())) == NULL
+ || !BIO_write_filename(bio, (char *)file)) {
+ CMP_err3("could not open file '%s' for %s %s certificate(s)",
+ file, certs == NULL ? "deleting" : "writing", desc);
n = -1;
goto end;
}
for (i = 0; i < n; i++) {
if (!write_cert(bio, sk_X509_value(certs, i))) {
- CMP_err1("cannot write certificate to file '%s'", destFile);
+ CMP_err2("cannot write %s certificate to file '%s'", desc, file);
n = -1;
goto end;
}
return n;
}
+static int delete_certfile(const char *file, const char *desc)
+{
+ if (file == NULL)
+ return 1;
+
+ if (unlink(file) != 0 && errno != ENOENT) {
+ CMP_err2("Failed to delete %s, which should be done to indicate there is no %s cert",
+ file, desc);
+ return 0;
+ }
+ return 1;
+}
+
+static int save_cert(OSSL_CMP_CTX *ctx, X509 *cert,
+ const char *file, const char *desc)
+{
+ if (file == NULL || cert == NULL) {
+ return 1;
+ } else {
+ STACK_OF(X509) *certs = sk_X509_new_null();
+
+ if (!X509_add_cert(certs, cert, X509_ADD_FLAG_UP_REF)) {
+ sk_X509_free(certs);
+ return 0;
+ }
+ return save_free_certs(ctx, certs, file, desc) >= 0;
+ }
+}
+
static int print_itavs(const STACK_OF(OSSL_CMP_ITAV) *itavs)
{
int i, ret = 1;
case OPT_UNPROTECTED_ERRORS:
opt_unprotected_errors = 1;
break;
+ case OPT_SRVCERTOUT:
+ opt_srvcertout = opt_str();
+ break;
case OPT_EXTRACERTSOUT:
opt_extracertsout = opt_str();
break;
opt_section, configfile);
} else {
const char *end = opt_section + strlen(opt_section);
+
while ((end = prev_item(opt_section, end)) != NULL) {
if (!NCONF_get_section(conf, opt_item)) {
CMP_err2("no [%s] section found in config file '%s'",
ret = get_opts(argc, argv);
if (ret <= 0)
goto err;
+
ret = 0;
+ if (!delete_certfile(opt_srvcertout, "validated server")
+ || !delete_certfile(opt_certout, "enrolled")
+ || save_free_certs(NULL, NULL, opt_extracertsout, "extra") < 0
+ || save_free_certs(NULL, NULL, opt_cacertsout, "CA") < 0
+ || save_free_certs(NULL, NULL, opt_chainout, "chain") < 0)
+ goto err;
+
if (!app_RAND_load())
goto err;
if (opt_infotype != NID_undef) {
OSSL_CMP_ITAV *itav =
OSSL_CMP_ITAV_create(OBJ_nid2obj(opt_infotype), NULL);
+
if (itav == NULL)
goto err;
OSSL_CMP_CTX_push0_genm_ITAV(cmp_ctx, itav);
if (!ret)
goto err;
ret = 0;
+ if (!save_cert(cmp_ctx, OSSL_CMP_CTX_get0_validatedSrvCert(cmp_ctx),
+ opt_srvcertout, "validated server"))
+ goto err;
if (save_free_certs(cmp_ctx, OSSL_CMP_CTX_get1_caPubs(cmp_ctx),
opt_cacertsout, "CA") < 0)
goto err;
- if (newcert != NULL) {
- STACK_OF(X509) *certs = sk_X509_new_null();
-
- if (!X509_add_cert(certs, newcert, X509_ADD_FLAG_UP_REF)) {
- sk_X509_free(certs);
- goto err;
- }
- if (save_free_certs(cmp_ctx, certs, opt_certout, "enrolled") < 0)
- goto err;
- }
+ if (!save_cert(cmp_ctx, newcert, opt_certout, "enrolled"))
+ goto err;
if (save_free_certs(cmp_ctx, OSSL_CMP_CTX_get1_newChain(cmp_ctx),
opt_chainout, "chain") < 0)
goto err;
&& ossl_cmp_ctx_set1_newChain(ctx, NULL)
&& ossl_cmp_ctx_set1_caPubs(ctx, NULL)
&& ossl_cmp_ctx_set1_extraCertsIn(ctx, NULL)
- && ossl_cmp_ctx_set0_validatedSrvCert(ctx, NULL)
+ && ossl_cmp_ctx_set1_validatedSrvCert(ctx, NULL)
&& OSSL_CMP_CTX_set1_transactionID(ctx, NULL)
&& OSSL_CMP_CTX_set1_senderNonce(ctx, NULL)
&& ossl_cmp_ctx_set1_recipNonce(ctx, NULL);
DEFINE_OSSL_set0(ossl_cmp_ctx, statusString, OSSL_CMP_PKIFREETEXT)
-int ossl_cmp_ctx_set0_validatedSrvCert(OSSL_CMP_CTX *ctx, X509 *cert)
-{
- if (!ossl_assert(ctx != NULL))
- return 0;
- X509_free(ctx->validatedSrvCert);
- ctx->validatedSrvCert = cert;
- return 1;
-}
-
/* Set callback function for checking if the cert is ok or should be rejected */
DEFINE_OSSL_set(OSSL_CMP_CTX, certConf_cb, OSSL_CMP_certConf_cb_t)
return 1; \
}
+DEFINE_OSSL_set1_up_ref(ossl_cmp_ctx, validatedSrvCert, X509)
+
/*
* Pins the server certificate to be directly trusted (even if it is expired)
* for verifying response messages.
*/
DEFINE_OSSL_set0(ossl_cmp_ctx, newCert, X509)
+/* Get successfully validated server cert, if any, of current transaction */
+DEFINE_OSSL_CMP_CTX_get0(validatedSrvCert, X509)
+
/*
* Get the (newly received in IP/KUP/CP) client certificate from the context
* This only permits for one client cert to be received...
# define ossl_cmp_info(ctx, msg) ossl_cmp_log(INFO, ctx, msg)
# define ossl_cmp_debug(ctx, msg) ossl_cmp_log(DEBUG, ctx, msg)
# define ossl_cmp_trace(ctx, msg) ossl_cmp_log(TRACE, ctx, msg)
-int ossl_cmp_ctx_set0_validatedSrvCert(OSSL_CMP_CTX *ctx, X509 *cert);
+int ossl_cmp_ctx_set1_validatedSrvCert(OSSL_CMP_CTX *ctx, X509 *cert);
int ossl_cmp_ctx_set_status(OSSL_CMP_CTX *ctx, int status);
int ossl_cmp_ctx_set0_statusString(OSSL_CMP_CTX *ctx,
OSSL_CMP_PKIFREETEXT *text);
/*-
* Try all certs in given list for verifying msg, normally or in 3GPP mode.
* If already_checked1 == NULL then certs are assumed to be the msg->extraCerts.
- * On success cache the found cert using ossl_cmp_ctx_set0_validatedSrvCert().
+ * On success cache the found cert using ossl_cmp_ctx_set1_validatedSrvCert().
*/
static int check_msg_with_certs(OSSL_CMP_CTX *ctx, const STACK_OF(X509) *certs,
const char *desc,
if (mode_3gpp ? check_cert_path_3gpp(ctx, msg, cert)
: check_cert_path(ctx, ctx->trusted, cert)) {
/* store successful sender cert for further msgs in transaction */
- if (!X509_up_ref(cert))
- return 0;
- if (!ossl_cmp_ctx_set0_validatedSrvCert(ctx, cert)) {
- X509_free(cert);
- return 0;
- }
- return 1;
+ return ossl_cmp_ctx_set1_validatedSrvCert(ctx, cert);
}
}
if (in_extraCerts && n_acceptable_certs == 0)
/*-
* Verify msg trying first ctx->untrusted, which should include extraCerts
* at its front, then trying the trusted certs in truststore (if any) of ctx.
- * On success cache the found cert using ossl_cmp_ctx_set0_validatedSrvCert().
+ * On success cache the found cert using ossl_cmp_ctx_set1_validatedSrvCert().
*/
static int check_msg_all_certs(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg,
int mode_3gpp)
/*-
* Verify message signature with any acceptable and valid candidate cert.
- * On success cache the found cert using ossl_cmp_ctx_set0_validatedSrvCert().
+ * On success cache the found cert using ossl_cmp_ctx_set1_validatedSrvCert().
*/
static int check_msg_find_cert(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg)
{
return 1;
}
/* cached sender cert has shown to be no more successfully usable */
- (void)ossl_cmp_ctx_set0_validatedSrvCert(ctx, NULL);
+ (void)ossl_cmp_ctx_set1_validatedSrvCert(ctx, NULL);
/* re-do the above check (just) for adding diagnostic information */
ossl_cmp_info(ctx,
"trying to verify msg signature with previously validated cert");
* the sender certificate can have been pinned by providing it in ctx->srvCert,
* else it is searched in msg->extraCerts, ctx->untrusted, in ctx->trusted
* (in this order) and is path is validated against ctx->trusted.
- * On success cache the found cert using ossl_cmp_ctx_set0_validatedSrvCert().
+ * On success cache the found cert using ossl_cmp_ctx_set1_validatedSrvCert().
*
* If ctx->permitTAInExtraCertsForIR is true and when validating a CMP IP msg,
* the trust anchor for validating the IP msg may be taken from msg->extraCerts
ossl_cmp_warn(ctx, "no trust store nor pinned server cert available for verifying signature-based CMP message protection");
return 1;
}
- if (check_msg_find_cert(ctx, msg))
+ if (check_msg_find_cert(ctx, msg)) {
+ ossl_cmp_debug(ctx,
+ "sucessfully validated signature-based CMP message protection using trust store");
return 1;
+ }
} else { /* use pinned sender cert */
/* use ctx->srvCert for signature check even if not acceptable */
if (verify_signature(ctx, msg, scrt)) {
ossl_cmp_debug(ctx,
- "successfully validated signature-based CMP message protection");
-
- return 1;
+ "successfully validated signature-based CMP message protection using pinned server cert");
+ return ossl_cmp_ctx_set1_validatedSrvCert(ctx, scrt);
}
ossl_cmp_warn(ctx, "CMP message signature verification failed");
ERR_raise(ERR_LIB_CMP, CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG);
[B<-expect_sender> I<name>]
[B<-ignore_keyusage>]
[B<-unprotected_errors>]
+[B<-srvcertout> I<filename>]
[B<-extracertsout> I<filename>]
[B<-cacertsout> I<filename>]
=back
+=item B<-srvcertout> I<filename>
+
+The file where to save the successfully validated certificate, if any,
+that the CMP server used for signature-based response message protection.
+
=item B<-extracertsout> I<filename>
The file where to save all certificates contained in the extraCerts field
OSSL_CMP_CTX_get_status,
OSSL_CMP_CTX_get0_statusString,
OSSL_CMP_CTX_get_failInfoCode,
+OSSL_CMP_CTX_get0_validatedSrvCert,
OSSL_CMP_CTX_get0_newCert,
OSSL_CMP_CTX_get1_newChain,
OSSL_CMP_CTX_get1_caPubs,
OSSL_CMP_PKIFREETEXT *OSSL_CMP_CTX_get0_statusString(const OSSL_CMP_CTX *ctx);
int OSSL_CMP_CTX_get_failInfoCode(const OSSL_CMP_CTX *ctx);
+ X509 *OSSL_CMP_CTX_get0_validatedSrvCert(const OSSL_CMP_CTX *ctx);
X509 *OSSL_CMP_CTX_get0_newCert(const OSSL_CMP_CTX *ctx);
STACK_OF(X509) *OSSL_CMP_CTX_get1_newChain(const OSSL_CMP_CTX *ctx);
STACK_OF(X509) *OSSL_CMP_CTX_get1_caPubs(const OSSL_CMP_CTX *ctx);
The flags start with OSSL_CMP_CTX_FAILINFO, for example:
OSSL_CMP_CTX_FAILINFO_badAlg. Returns -1 if the failInfoCode field is unset.
+OSSL_CMP_CTX_get0_validatedSrvCert() returns
+the successfully validated certificate, if any, that the CMP server used
+in the current transaction for signature-based response message protection,
+or NULL if the server used MAC-based protection.
+The value is relevant only at the end of a successful transaction.
+It may be used to check the authorization of the server based on its cert.
+
OSSL_CMP_CTX_get0_newCert() returns the pointer to the newly obtained
certificate in case it is available, else NULL.
OSSL_CMP_CTX_get0_newPkey(),
OSSL_CMP_CTX_get_certConf_cb_arg(),
OSSL_CMP_CTX_get0_statusString(),
+OSSL_CMP_CTX_get0_validatedSrvCert(),
OSSL_CMP_CTX_get0_newCert(),
OSSL_CMP_CTX_get0_newChain(),
OSSL_CMP_CTX_get1_caPubs(), and
using macros, while keeping the old names for backward compatibility,
in OpenSSL 3.1.
+OSSL_CMP_CTX_get0_validatedSrvCert() was added in OpenSSL 3.1.
+
=head1 COPYRIGHT
Copyright 2007-2022 The OpenSSL Project Authors. All Rights Reserved.
OSSL_CMP_PKIFREETEXT *OSSL_CMP_CTX_get0_statusString(const OSSL_CMP_CTX *ctx);
int OSSL_CMP_CTX_get_failInfoCode(const OSSL_CMP_CTX *ctx);
# define OSSL_CMP_PKISI_BUFLEN 1024
+X509 *OSSL_CMP_CTX_get0_validatedSrvCert(const OSSL_CMP_CTX *ctx);
X509 *OSSL_CMP_CTX_get0_newCert(const OSSL_CMP_CTX *ctx);
STACK_OF(X509) *OSSL_CMP_CTX_get1_newChain(const OSSL_CMP_CTX *ctx);
STACK_OF(X509) *OSSL_CMP_CTX_get1_caPubs(const OSSL_CMP_CTX *ctx);
|| !ossl_cmp_ctx_set1_newChain(ctx, certs)
|| !ossl_cmp_ctx_set1_caPubs(ctx, certs)
|| !ossl_cmp_ctx_set1_extraCertsIn(ctx, certs)
- || !ossl_cmp_ctx_set0_validatedSrvCert(ctx, X509_dup(test_cert))
+ || !ossl_cmp_ctx_set1_validatedSrvCert(ctx, test_cert)
|| !TEST_ptr(bytes = ASN1_OCTET_STRING_new())
|| !OSSL_CMP_CTX_set1_transactionID(ctx, bytes)
|| !OSSL_CMP_CTX_set1_senderNonce(ctx, bytes)
DEFINE_SET_GET_P_VOID_TEST(transfer_cb_arg)
DEFINE_SET_TEST(OSSL_CMP, CTX, 1, 0, srvCert, X509)
-DEFINE_SET_TEST(ossl_cmp, ctx, 0, 0, validatedSrvCert, X509)
+DEFINE_SET_GET_TEST(ossl_cmp, ctx, 1, 0, 0, validatedSrvCert, X509)
DEFINE_SET_TEST(OSSL_CMP, CTX, 1, 1, expected_sender, X509_NAME)
DEFINE_SET_GET_BASE_TEST(OSSL_CMP_CTX, set0, get0, 0, trusted,
X509_STORE *, NULL,
ADD_TEST(test_CTX_set_get_transfer_cb_arg);
/* server authentication: */
ADD_TEST(test_CTX_set1_get0_srvCert);
- ADD_TEST(test_CTX_set0_get0_validatedSrvCert);
+ ADD_TEST(test_CTX_set1_get0_validatedSrvCert);
ADD_TEST(test_CTX_set1_get0_expected_sender);
ADD_TEST(test_CTX_set0_get0_trusted);
ADD_TEST(test_CTX_set1_get0_untrusted);
}
#endif
+/* indirectly checks also OSSL_CMP_validate_msg() */
static int execute_validate_msg_test(CMP_VFY_TEST_FIXTURE *fixture)
{
- return TEST_int_eq(fixture->expected,
- ossl_cmp_msg_check_update(fixture->cmp_ctx, fixture->msg,
- NULL, 0));
+ int res = TEST_int_eq(fixture->expected,
+ ossl_cmp_msg_check_update(fixture->cmp_ctx,
+ fixture->msg, NULL, 0));
+ X509 *validated = OSSL_CMP_CTX_get0_validatedSrvCert(fixture->cmp_ctx);
+
+ return res && (!fixture->expected || TEST_ptr_eq(validated, fixture->cert));
}
static int execute_validate_cert_path_test(CMP_VFY_TEST_FIXTURE *fixture)
};
SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->cert = NULL;
fixture->expected = 1;
if (!TEST_true(OSSL_CMP_CTX_set1_secretValue(fixture->cmp_ctx, sec_1,
};
SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->cert = NULL;
fixture->expected = 0;
if (!TEST_true(OSSL_CMP_CTX_set1_secretValue(fixture->cmp_ctx, sec_bad,
X509_STORE *ts;
SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->cert = srvcert;
ts = OSSL_CMP_CTX_get0_trusted(fixture->cmp_ctx);
fixture->expected = !expired;
static int test_validate_msg_signature_srvcert(int bad_sig)
{
SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->cert = srvcert;
fixture->expected = !bad_sig;
if (!TEST_ptr(fixture->msg = load_pkimsg(ir_protected_f, libctx))
|| !TEST_true(OSSL_CMP_CTX_set1_srvCert(fixture->cmp_ctx, srvcert))
static int test_validate_msg_signature_sender_cert_untrusted(void)
{
SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->cert = insta_cert;
fixture->expected = 1;
if (!TEST_ptr(fixture->msg = load_pkimsg(ir_protected_0_extracerts, libctx))
|| !add_trusted(fixture->cmp_ctx, instaca_cert)
static int test_validate_msg_signature_sender_cert_trusted(void)
{
SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->cert = insta_cert;
fixture->expected = 1;
if (!TEST_ptr(fixture->msg = load_pkimsg(ir_protected_0_extracerts, libctx))
|| !add_trusted(fixture->cmp_ctx, instaca_cert)
tear_down(fixture);
fixture = NULL;
}
+ fixture->cert = sk_X509_value(fixture->msg->extraCerts, 1); /* Insta CA */
EXECUTE_TEST(execute_validate_msg_test, tear_down);
return result;
}
static int test_validate_with_sender(const X509_NAME *name, int expected)
{
SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+ fixture->cert = srvcert;
fixture->expected = expected;
if (!TEST_ptr(fixture->msg = load_pkimsg(ir_protected_f, libctx))
|| !TEST_true(OSSL_CMP_CTX_set1_expected_sender(fixture->cmp_ctx, name))
ASYNC_set_mem_functions ? 3_1_0 EXIST::FUNCTION:
ASYNC_get_mem_functions ? 3_1_0 EXIST::FUNCTION:
BIO_ADDR_dup ? 3_1_0 EXIST::FUNCTION:SOCK
+OSSL_CMP_CTX_get0_validatedSrvCert ? 3_1_0 EXIST::FUNCTION:CMP
CMS_final_digest ? 3_1_0 EXIST::FUNCTION:CMS
CMS_EnvelopedData_it ? 3_1_0 EXIST::FUNCTION:CMS
CMS_EnvelopedData_decrypt ? 3_1_0 EXIST::FUNCTION:CMS