]> git.ipfire.org Git - thirdparty/openssl.git/blobdiff - apps/cmp.c
CMP: add support for genm with rootCaCert and genp with rootCaKeyUpdate
[thirdparty/openssl.git] / apps / cmp.c
index fa2f49585f04e469b240cef6b58a26ba59068d38..dbc609a2e00375ad2cedf87ae390aa626d3a913a 100644 (file)
@@ -68,7 +68,7 @@ typedef enum {
 } cmp_cmd_t;
 
 /* message transfer */
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
 static char *opt_server = NULL;
 static char *opt_proxy = NULL;
 static char *opt_no_proxy = NULL;
@@ -89,6 +89,10 @@ static int opt_unprotected_errors = 0;
 static char *opt_srvcertout = NULL;
 static char *opt_extracertsout = NULL;
 static char *opt_cacertsout = NULL;
+static char *opt_oldwithold = NULL;
+static char *opt_newwithnew = NULL;
+static char *opt_newwithold = NULL;
+static char *opt_oldwithnew = NULL;
 
 /* client authentication */
 static char *opt_ref = NULL;
@@ -113,7 +117,6 @@ static int opt_infotype = NID_undef;
 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;
@@ -131,6 +134,8 @@ static char *opt_chainout = NULL;
 
 /* certificate enrollment and revocation */
 static char *opt_oldcert = NULL;
+static char *opt_issuer = NULL;
+static char *opt_serial = NULL;
 static int opt_revreason = CRL_REASON_NONE;
 
 /* credentials format */
@@ -141,7 +146,7 @@ static int opt_keyform = FORMAT_UNDEF;
 static char *opt_otherpass = NULL;
 static char *opt_engine = NULL;
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
 /* TLS connection */
 static int opt_tls_used = 0;
 static char *opt_tls_cert = NULL;
@@ -164,7 +169,7 @@ static char *opt_rspout = NULL;
 static int opt_use_mock_srv = 0;
 
 /* mock server */
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
 static char *opt_port = NULL;
 static int opt_max_msgs = 0;
 #endif
@@ -180,6 +185,10 @@ static char *opt_ref_cert = NULL;
 static char *opt_rsp_cert = NULL;
 static char *opt_rsp_extracerts = NULL;
 static char *opt_rsp_capubs = NULL;
+static char *opt_rsp_newwithnew = NULL;
+static char *opt_rsp_newwithold = NULL;
+static char *opt_rsp_oldwithnew = NULL;
+
 static int opt_poll_count = 0;
 static int opt_check_after = 1;
 static int opt_grant_implicitconf = 0;
@@ -203,7 +212,7 @@ typedef enum OPTION_choice {
 
     OPT_CMD, OPT_INFOTYPE, OPT_GENINFO,
 
-    OPT_NEWKEY, OPT_NEWKEYPASS, OPT_SUBJECT, OPT_ISSUER,
+    OPT_NEWKEY, OPT_NEWKEYPASS, OPT_SUBJECT,
     OPT_DAYS, OPT_REQEXTS,
     OPT_SANS, OPT_SAN_NODEFAULT,
     OPT_POLICIES, OPT_POLICY_OIDS, OPT_POLICY_OIDS_CRITICAL,
@@ -211,9 +220,9 @@ typedef enum OPTION_choice {
     OPT_OUT_TRUSTED, OPT_IMPLICIT_CONFIRM, OPT_DISABLE_CONFIRM,
     OPT_CERTOUT, OPT_CHAINOUT,
 
-    OPT_OLDCERT, OPT_REVREASON,
+    OPT_OLDCERT, OPT_ISSUER, OPT_SERIAL, OPT_REVREASON,
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     OPT_SERVER, OPT_PROXY, OPT_NO_PROXY,
 #endif
     OPT_RECIPIENT, OPT_PATH,
@@ -223,6 +232,7 @@ typedef enum OPTION_choice {
     OPT_EXPECT_SENDER,
     OPT_IGNORE_KEYUSAGE, OPT_UNPROTECTED_ERRORS,
     OPT_SRVCERTOUT, OPT_EXTRACERTSOUT, OPT_CACERTSOUT,
+    OPT_OLDWITHOLD, OPT_NEWWITHNEW, OPT_NEWWITHOLD, OPT_OLDWITHNEW,
 
     OPT_REF, OPT_SECRET, OPT_CERT, OPT_OWN_TRUSTED, OPT_KEY, OPT_KEYPASS,
     OPT_DIGEST, OPT_MAC, OPT_EXTRACERTS,
@@ -236,7 +246,7 @@ typedef enum OPTION_choice {
     OPT_PROV_ENUM,
     OPT_R_ENUM,
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     OPT_TLS_USED, OPT_TLS_CERT, OPT_TLS_KEY,
     OPT_TLS_KEYPASS,
     OPT_TLS_EXTRA, OPT_TLS_TRUSTED, OPT_TLS_HOST,
@@ -246,13 +256,14 @@ typedef enum OPTION_choice {
     OPT_REQIN, OPT_REQIN_NEW_TID, OPT_REQOUT, OPT_RSPIN, OPT_RSPOUT,
     OPT_USE_MOCK_SRV,
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     OPT_PORT, OPT_MAX_MSGS,
 #endif
     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_RSP_NEWWITHNEW, OPT_RSP_NEWWITHOLD, OPT_RSP_OLDWITHNEW,
     OPT_POLL_COUNT, OPT_CHECK_AFTER,
     OPT_GRANT_IMPLICITCONF,
     OPT_PKISTATUS, OPT_FAILURE,
@@ -277,7 +288,9 @@ const OPTIONS cmp_options[] = {
     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. 'caCerts'"},
+     "InfoType name for requesting specific info in genm, with specific support"},
+    {OPT_MORE_STR, 0, 0,
+     "for 'caCerts' and 'rootCaCert'"},
     {"geninfo", OPT_GENINFO, 's',
      "generalInfo integer values to place in request PKIHeader with given OID"},
     {OPT_MORE_STR, 0, 0,
@@ -293,10 +306,6 @@ const OPTIONS cmp_options[] = {
      "For kur, default is subject of -csr arg or 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',
@@ -340,15 +349,21 @@ const OPTIONS cmp_options[] = {
      "also used as reference (defaulting to -cert) for subject DN and SANs."},
     {OPT_MORE_STR, 0, 0,
      "Issuer is used as recipient unless -recipient, -srvcert, or -issuer given"},
+    {"issuer", OPT_ISSUER, 's',
+     "DN of the issuer to place in the certificate template of ir/cr/kur/rr;"},
+    {OPT_MORE_STR, 0, 0,
+     "also used as recipient if neither -recipient nor -srvcert are given"},
+    {"serial", OPT_SERIAL, 's',
+     "Serial number of certificate to be revoked in revocation request (rr)"},
     {"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"),
-#ifdef OPENSSL_NO_SOCK
+#if defined(OPENSSL_NO_SOCK) || defined(OPENSSL_NO_HTTP)
     {OPT_MORE_STR, 0, 0,
-     "NOTE: -server, -proxy, and -no_proxy not supported due to no-sock build"},
+     "NOTE: -server, -proxy, and -no_proxy not supported due to no-sock/no-http build"},
 #else
     {"server", OPT_SERVER, 's',
      "[http[s]://]address[:port][/path] of CMP server. Default port 80 or 443."},
@@ -396,6 +411,14 @@ const OPTIONS cmp_options[] = {
      "File to save extra certificates received in the extraCerts field"},
     {"cacertsout", OPT_CACERTSOUT, 's',
      "File to save CA certs received in caPubs field or genp with id-it-caCerts"},
+    { "oldwithold", OPT_OLDWITHOLD, 's',
+      "Root CA certificate to request update for in genm of type rootCaCert"},
+    { "newwithnew", OPT_NEWWITHNEW, 's',
+      "File to save NewWithNew cert received in genp of type rootCaKeyUpdate"},
+    { "newwithold", OPT_NEWWITHOLD, 's',
+      "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"},
 
     OPT_SECTION("Client authentication"),
     {"ref", OPT_REF, 's',
@@ -441,9 +464,9 @@ const OPTIONS cmp_options[] = {
     OPT_R_OPTIONS,
 
     OPT_SECTION("TLS connection"),
-#ifdef OPENSSL_NO_SOCK
+#if defined(OPENSSL_NO_SOCK) || defined(OPENSSL_NO_HTTP)
     {OPT_MORE_STR, 0, 0,
-     "NOTE: -tls_used and all other TLS options not supported due to no-sock build"},
+     "NOTE: -tls_used and all other TLS options not supported due to no-sock/no-http build"},
 #else
     {"tls_used", OPT_TLS_USED, '-',
      "Enable using TLS (also when other TLS options are not set)"},
@@ -482,9 +505,9 @@ const OPTIONS cmp_options[] = {
      "Use internal mock server at API level, bypassing socket-based HTTP"},
 
     OPT_SECTION("Mock server"),
-#ifdef OPENSSL_NO_SOCK
+#if defined(OPENSSL_NO_SOCK) || defined(OPENSSL_NO_HTTP)
     {OPT_MORE_STR, 0, 0,
-     "NOTE: -port and -max_msgs not supported due to no-sock build"},
+     "NOTE: -port and -max_msgs not supported due to no-sock/no-http build"},
 #else
     {"port", OPT_PORT, 's',
      "Act as HTTP-based mock server listening on given port"},
@@ -514,6 +537,12 @@ const OPTIONS cmp_options[] = {
      "Extra certificates to be included in mock certification responses"},
     {"rsp_capubs", OPT_RSP_CAPUBS, 's',
      "CA certificates to be included in mock ip response"},
+    {"rsp_newwithnew", OPT_RSP_NEWWITHNEW, 's',
+     "New root CA certificate to include in genp of type rootCaKeyUpdate"},
+    {"rsp_newwithold", OPT_RSP_NEWWITHOLD, 's',
+     "NewWithOld transition cert to include in genp of type rootCaKeyUpdate"},
+    {"rsp_oldwithnew", OPT_RSP_OLDWITHNEW, 's',
+     "OldWithNew transition cert to include in genp of type rootCaKeyUpdate"},
     {"poll_count", OPT_POLL_COUNT, 'N',
      "Number of times the client must poll before receiving a certificate"},
     {"check_after", OPT_CHECK_AFTER, 'N',
@@ -560,7 +589,7 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */
 
     {&opt_cmd_s}, {&opt_infotype_s}, {&opt_geninfo},
 
-    {&opt_newkey}, {&opt_newkeypass}, {&opt_subject}, {&opt_issuer},
+    {&opt_newkey}, {&opt_newkeypass}, {&opt_subject},
     {(char **)&opt_days}, {&opt_reqexts},
     {&opt_sans}, {(char **)&opt_san_nodefault},
     {&opt_policies}, {&opt_policy_oids}, {(char **)&opt_policy_oids_critical},
@@ -569,9 +598,9 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */
     {(char **)&opt_implicit_confirm}, {(char **)&opt_disable_confirm},
     {&opt_certout}, {&opt_chainout},
 
-    {&opt_oldcert}, {(char **)&opt_revreason},
+    {&opt_oldcert}, {&opt_issuer}, {&opt_serial}, {(char **)&opt_revreason},
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     {&opt_server}, {&opt_proxy}, {&opt_no_proxy},
 #endif
     {&opt_recipient}, {&opt_path}, {(char **)&opt_keep_alive},
@@ -581,6 +610,7 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */
     {&opt_expect_sender},
     {(char **)&opt_ignore_keyusage}, {(char **)&opt_unprotected_errors},
     {&opt_srvcertout}, {&opt_extracertsout}, {&opt_cacertsout},
+    {&opt_oldwithold}, {&opt_newwithnew}, {&opt_newwithold}, {&opt_oldwithnew},
 
     {&opt_ref}, {&opt_secret},
     {&opt_cert}, {&opt_own_trusted}, {&opt_key}, {&opt_keypass},
@@ -593,7 +623,7 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */
     {&opt_engine},
 #endif
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     {(char **)&opt_tls_used}, {&opt_tls_cert}, {&opt_tls_key},
     {&opt_tls_keypass},
     {&opt_tls_extra}, {&opt_tls_trusted}, {&opt_tls_host},
@@ -604,13 +634,15 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */
     {&opt_reqout}, {&opt_rspin}, {&opt_rspout},
 
     {(char **)&opt_use_mock_srv},
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     {&opt_port}, {(char **)&opt_max_msgs},
 #endif
     {&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_rsp_newwithnew}, {&opt_rsp_newwithold}, {&opt_rsp_oldwithnew},
+
     {(char **)&opt_poll_count}, {(char **)&opt_check_after},
     {(char **)&opt_grant_implicitconf},
     {(char **)&opt_pkistatus}, {(char **)&opt_failure},
@@ -807,7 +839,7 @@ static OSSL_CMP_MSG *read_write_req_resp(OSSL_CMP_CTX *ctx,
                 CMP_warn("too few -rspin filename arguments; resorting to using mock server");
             res = OSSL_CMP_CTX_server_perform(ctx, actual_req);
         } else {
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
             if (opt_server == NULL) {
                 CMP_err("missing -server or -use_mock_srv option, or too few -rspin filename arguments");
                 goto err;
@@ -816,7 +848,7 @@ static OSSL_CMP_MSG *read_write_req_resp(OSSL_CMP_CTX *ctx,
                 CMP_warn("too few -rspin filename arguments; resorting to contacting server");
             res = OSSL_CMP_MSG_http_perform(ctx, actual_req);
 #else
-            CMP_err("-server not supported on no-sock build; missing -use_mock_srv option or too few -rspin filename arguments");
+            CMP_err("-server not supported on no-sock/no-http build; missing -use_mock_srv option or too few -rspin filename arguments");
 #endif
         }
         rspin_in_use = 0;
@@ -926,8 +958,23 @@ static X509_STORE *load_trusted(char *input, int for_new_cert, const char *desc)
     return NULL;
 }
 
-typedef int (*add_X509_stack_fn_t)(void *ctx, const STACK_OF(X509) *certs);
+typedef int (*add_X509_fn_t)(void *ctx, const X509 *cert);
+static int setup_cert(void *ctx, const char *file, const char *pass,
+                      const char *desc, add_X509_fn_t set1_fn)
+{
+    X509 *cert;
+    int ok;
+
+    if (file == NULL)
+        return 1;
+    if ((cert = load_cert_pwd(file, pass, desc)) == NULL)
+        return 0;
+    ok = (*set1_fn)(ctx, cert);
+    X509_free(cert);
+    return ok;
+}
 
+typedef int (*add_X509_stack_fn_t)(void *ctx, const STACK_OF(X509) *certs);
 static int setup_certs(char *files, const char *desc, void *ctx,
                        add_X509_stack_fn_t set1_fn)
 {
@@ -1040,16 +1087,10 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine)
         CMP_err("must give both -srv_cert and -srv_key options or neither");
         goto err;
     }
-    if (opt_srv_cert != NULL) {
-        X509 *srv_cert = load_cert_pwd(opt_srv_cert, opt_srv_keypass,
-                                       "certificate of the mock server");
-
-        if (srv_cert == NULL || !OSSL_CMP_CTX_set1_cert(ctx, srv_cert)) {
-            X509_free(srv_cert);
-            goto err;
-        }
-        X509_free(srv_cert);
-    }
+    if (!setup_cert(ctx, opt_srv_cert, opt_srv_keypass,
+                    "signer certificate of the mock server",
+                    (add_X509_fn_t)OSSL_CMP_CTX_set1_cert))
+        goto err;
     if (opt_srv_key != NULL) {
         EVP_PKEY *pkey = load_key_pwd(opt_srv_key, opt_keyform,
                                       opt_srv_keypass,
@@ -1079,31 +1120,17 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine)
                      (add_X509_stack_fn_t)OSSL_CMP_CTX_set1_untrusted))
         goto err;
 
-    if (opt_ref_cert != NULL) {
-        X509 *cert = load_cert_pwd(opt_ref_cert, opt_keypass,
-                                   "reference cert to be expected by the mock server");
-
-        if (cert == NULL)
+    if (!setup_cert(srv_ctx, opt_ref_cert, opt_otherpass,
+                    "reference cert to be expected by the mock server",
+                    (add_X509_fn_t)ossl_cmp_mock_srv_set1_refCert))
             goto err;
-        if (!ossl_cmp_mock_srv_set1_refCert(srv_ctx, cert)) {
-            X509_free(cert);
-            goto err;
-        }
-        X509_free(cert);
-    }
     if (opt_rsp_cert == NULL) {
         CMP_warn("no -rsp_cert given for mock server");
     } else {
-        X509 *cert = load_cert_pwd(opt_rsp_cert, opt_keypass,
-                                   "cert to be returned by the mock server");
-
-        if (cert == NULL)
+        if (!setup_cert(srv_ctx, opt_rsp_cert, opt_keypass,
+                        "cert the mock server returns on certificate requests",
+                        (add_X509_fn_t)ossl_cmp_mock_srv_set1_certOut))
             goto err;
-        if (!ossl_cmp_mock_srv_set1_certOut(srv_ctx, cert)) {
-            X509_free(cert);
-            goto err;
-        }
-        X509_free(cert);
     }
     if (!setup_certs(opt_rsp_extracerts,
                      "CMP extra certificates for mock server", srv_ctx,
@@ -1112,6 +1139,16 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *engine)
     if (!setup_certs(opt_rsp_capubs, "caPubs for mock server", srv_ctx,
                      (add_X509_stack_fn_t)ossl_cmp_mock_srv_set1_caPubsOut))
         goto err;
+    if (!setup_cert(srv_ctx, opt_rsp_newwithnew, opt_otherpass,
+                    "NewWithNew cert the mock server returns in rootCaKeyUpdate",
+                    (add_X509_fn_t)ossl_cmp_mock_srv_set1_newWithNew)
+        || !setup_cert(srv_ctx, opt_rsp_newwithold, opt_otherpass,
+                       "NewWithOld cert the mock server returns in rootCaKeyUpdate",
+                       (add_X509_fn_t)ossl_cmp_mock_srv_set1_newWithOld)
+        || !setup_cert(srv_ctx, opt_rsp_oldwithnew, opt_otherpass,
+                       "OldWithNew cert the mock server returns in rootCaKeyUpdate",
+                       (add_X509_fn_t)ossl_cmp_mock_srv_set1_oldWithNew))
+        goto err;
     (void)ossl_cmp_mock_srv_set_pollCount(srv_ctx, opt_poll_count);
     (void)ossl_cmp_mock_srv_set_checkAfterTime(srv_ctx, opt_check_after);
     if (opt_grant_implicitconf)
@@ -1168,10 +1205,6 @@ static int setup_verification_ctx(OSSL_CMP_CTX *ctx)
         return 0;
 
     if (opt_srvcert != NULL || opt_trusted != NULL) {
-        X509 *srvcert;
-        X509_STORE *ts;
-        int ok;
-
         if (opt_srvcert != NULL) {
             if (opt_trusted != NULL) {
                 CMP_warn("-trusted option is ignored since -srvcert option is present");
@@ -1181,14 +1214,14 @@ static int setup_verification_ctx(OSSL_CMP_CTX *ctx)
                 CMP_warn("-recipient option is ignored since -srvcert option is present");
                 opt_recipient = NULL;
             }
-            srvcert = load_cert_pwd(opt_srvcert, opt_otherpass,
-                                    "directly trusted CMP server certificate");
-            ok = srvcert != NULL && OSSL_CMP_CTX_set1_srvCert(ctx, srvcert);
-            X509_free(srvcert);
-            if (!ok)
+            if (!setup_cert(ctx, opt_srvcert, opt_otherpass,
+                            "directly trusted CMP server certificate",
+                            (add_X509_fn_t)OSSL_CMP_CTX_set1_srvCert))
                 return 0;
         }
         if (opt_trusted != NULL) {
+            X509_STORE *ts;
+
             /*
              * the 0 arg below clears any expected host/ip/email address;
              * opt_expect_sender is used instead
@@ -1232,7 +1265,7 @@ static int setup_verification_ctx(OSSL_CMP_CTX *ctx)
     return 1;
 }
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
 /*
  * set up ssl_ctx for the OSSL_CMP_CTX based on options from config file/CLI.
  * Returns pointer on success, NULL on error
@@ -1405,7 +1438,7 @@ static int setup_protection_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
         CMP_err("must give -ref if no -cert and no -subject given");
         return 0;
     }
-    if (!opt_secret && ((opt_cert == NULL) != (opt_key == NULL))) {
+    if (opt_secret == NULL && ((opt_cert == NULL) != (opt_key == NULL))) {
         CMP_err("must give both -cert and -key options or neither");
         return 0;
     }
@@ -1509,9 +1542,9 @@ static int setup_protection_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
 }
 
 /*
- * set up IR/CR/KUR/CertConf/RR specific parts of the OSSL_CMP_CTX
- * based on options from config file/CLI.
- * Returns pointer on success, NULL on error
+ * Set up IR/CR/P10CR/KUR/CertConf/RR/GENM specific parts of the OSSL_CMP_CTX
+ * based on options from CLI and/or config file.
+ * Returns 1 on success, 0 on error
  */
 static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
 {
@@ -1524,6 +1557,8 @@ static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
             && opt_cmd != CMP_RR && opt_cmd != CMP_GENM)
         CMP_warn("no -subject given; no -csr or -oldcert or -cert available for fallback");
 
+    if (!set_name(opt_issuer, OSSL_CMP_CTX_set1_issuer, ctx, "issuer"))
+        return 0;
     if (opt_cmd == CMP_IR || opt_cmd == CMP_CR || opt_cmd == CMP_KUR) {
         if (opt_newkey == NULL
             && opt_key == NULL && opt_csr == NULL && opt_oldcert == NULL) {
@@ -1548,8 +1583,7 @@ static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
             CMP_err("-certout not given, nowhere to save newly enrolled certificate");
             return 0;
         }
-        if (!set_name(opt_subject, OSSL_CMP_CTX_set1_subjectName, ctx, "subject")
-                || !set_name(opt_issuer, OSSL_CMP_CTX_set1_issuer, ctx, "issuer"))
+        if (!set_name(opt_subject, OSSL_CMP_CTX_set1_subjectName, ctx, "subject"))
             return 0;
     } else {
         const char *msg = "option is ignored for commands other than 'ir', 'cr', and 'kur'";
@@ -1563,8 +1597,8 @@ static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
                 CMP_warn1("-subject %s since sender is taken from -ref or -cert", msg);
             }
         }
-        if (opt_issuer != NULL)
-            CMP_warn1("-issuer %s", msg);
+        if (opt_issuer != NULL && opt_cmd != CMP_RR)
+            CMP_warn1("-issuer %s and 'rr'", msg);
         if (opt_reqexts != NULL)
             CMP_warn1("-reqexts %s", msg);
         if (opt_san_nodefault)
@@ -1598,12 +1632,44 @@ static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
                       opt_subject, ref_cert != NULL ? ref_cert : opt_csr);
     }
     if (opt_cmd == CMP_RR) {
-        if (opt_oldcert == NULL && opt_csr == NULL) {
-            CMP_err("missing -oldcert for certificate to be revoked and no -csr given");
-            return 0;
+        if (opt_issuer == NULL && opt_serial == NULL) {
+            if (opt_oldcert == NULL && opt_csr == NULL) {
+                CMP_err("missing -oldcert or -issuer and -serial for certificate to be revoked and no -csr given");
+                return 0;
+            }
+            if (opt_oldcert != NULL && opt_csr != NULL)
+                CMP_warn("ignoring -csr since certificate to be revoked is given");
+        } else {
+#define OSSL_CMP_RR_MSG "since -issuer and -serial is given for command 'rr'"
+            if (opt_issuer == NULL || opt_serial == NULL) {
+                CMP_err("Must give both -issuer and -serial options or neither");
+                return 0;
+            }
+            if (opt_oldcert != NULL)
+                CMP_warn("Ignoring -oldcert " OSSL_CMP_RR_MSG);
+            if (opt_csr != NULL)
+                CMP_warn("Ignoring -csr " OSSL_CMP_RR_MSG);
+        }
+        if (opt_serial != NULL) {
+            ASN1_INTEGER *sno;
+
+            if ((sno = s2i_ASN1_INTEGER(NULL, opt_serial)) == NULL) {
+                CMP_err1("cannot read serial number: '%s'", opt_serial);
+                return 0;
+            }
+            if (!OSSL_CMP_CTX_set1_serialNumber(ctx, sno)) {
+                ASN1_INTEGER_free(sno);
+                CMP_err("out of memory");
+                return 0;
+            }
+            ASN1_INTEGER_free(sno);
         }
-        if (opt_oldcert != NULL && opt_csr != NULL)
-            CMP_warn("ignoring -csr since certificate to be revoked is given");
+        if (opt_revreason > CRL_REASON_NONE)
+            (void)OSSL_CMP_CTX_set_option(ctx, OSSL_CMP_OPT_REVOCATION_REASON,
+                                          opt_revreason);
+    } else {
+        if (opt_serial != NULL)
+            CMP_warn("Ignoring -serial for command other than 'rr'");
     }
     if (opt_cmd == CMP_P10CR && opt_csr == NULL) {
         CMP_err("missing PKCS#10 CSR for p10cr");
@@ -1750,28 +1816,16 @@ static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
         if (opt_cmd == CMP_GENM) {
             CMP_warn("-oldcert option is ignored for 'genm' command");
         } else {
-            X509 *oldcert = load_cert_pwd(opt_oldcert, opt_keypass,
-                                          opt_cmd == CMP_KUR ?
-                                          "certificate to be updated" :
-                                          opt_cmd == CMP_RR ?
-                                          "certificate to be revoked" :
-                                          "reference certificate (oldcert)");
-            /* opt_keypass needed if opt_oldcert is an encrypted PKCS#12 file */
-
-            if (oldcert == NULL)
+            if (!setup_cert(ctx, opt_oldcert, opt_keypass,
+                            /* needed if opt_oldcert is encrypted PKCS12 file */
+                            opt_cmd == CMP_KUR ? "certificate to be updated" :
+                            opt_cmd == CMP_RR ? "certificate to be revoked" :
+                            "reference certificate (oldcert)",
+                            (add_X509_fn_t)OSSL_CMP_CTX_set1_oldCert))
                 return 0;
-            if (!OSSL_CMP_CTX_set1_oldCert(ctx, oldcert)) {
-                X509_free(oldcert);
-                CMP_err("out of memory");
-                return 0;
-            }
-            X509_free(oldcert);
         }
     }
     cleanse(opt_keypass);
-    if (opt_revreason > CRL_REASON_NONE)
-        (void)OSSL_CMP_CTX_set_option(ctx, OSSL_CMP_OPT_REVOCATION_REASON,
-                                      opt_revreason);
 
     return 1;
 
@@ -1854,7 +1908,7 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
 {
     int ret = 0;
     char *host = NULL, *port = NULL, *path = NULL, *used_path = opt_path;
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     int portnum, use_ssl;
     static char server_port[32] = { '\0' };
     const char *proxy_host = NULL;
@@ -1863,7 +1917,7 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
     char proxy_buf[200] = "";
 
     if (!opt_use_mock_srv && opt_rspin == NULL) { /* note: -port is not given */
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
         if (opt_server == NULL) {
             CMP_err("missing -server or -use_mock_srv or -rspin option");
             goto err;
@@ -1873,7 +1927,7 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
         goto err;
 #endif
     }
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     if (opt_server == NULL) {
         if (opt_proxy != NULL)
             CMP_warn("ignoring -proxy option since -server is not given");
@@ -1936,6 +1990,18 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
             goto err;
         }
     }
+    if (opt_cmd != CMP_GENM || opt_infotype != NID_id_it_rootCaCert) {
+        const char *msg = "option is ignored unless -cmd 'genm' and -infotype rootCaCert is given";
+
+        if (opt_oldwithold != NULL)
+            CMP_warn1("-oldwithold %s", msg);
+        if (opt_newwithnew != NULL)
+            CMP_warn1("-newwithnew %s", msg);
+        if (opt_newwithold != NULL)
+            CMP_warn1("-newwithold %s", msg);
+        if (opt_oldwithnew != NULL)
+            CMP_warn1("-oldwithnew %s", msg);
+    }
 
     if (!setup_verification_ctx(ctx))
         goto err;
@@ -1967,7 +2033,7 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
             || opt_rspin != NULL || opt_rspout != NULL || opt_use_mock_srv)
         (void)OSSL_CMP_CTX_set_transfer_cb(ctx, read_write_req_resp);
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     if (opt_tls_used) {
         APP_HTTP_TLS_INFO *info;
 
@@ -2174,7 +2240,7 @@ static const char *prev_item(const char *opt, const char *end)
     beg = end;
     while (beg > opt) {
         --beg;
-        if (beg[0] == ',' || isspace(beg[0])) {
+        if (beg[0] == ',' || isspace(_UC(beg[0]))) {
             ++beg;
             break;
         }
@@ -2189,7 +2255,7 @@ static const char *prev_item(const char *opt, const char *end)
     opt_item[len] = '\0';
     while (beg > opt) {
         --beg;
-        if (beg[0] != ',' && !isspace(beg[0])) {
+        if (beg[0] != ',' && !isspace(_UC(beg[0]))) {
             ++beg;
             break;
         }
@@ -2404,7 +2470,7 @@ static int get_opts(int argc, char **argv)
             if (!set_verbosity(opt_int_arg()))
                 goto opthelp;
             break;
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
         case OPT_SERVER:
             opt_server = opt_str();
             break;
@@ -2434,7 +2500,7 @@ static int get_opts(int argc, char **argv)
         case OPT_TOTAL_TIMEOUT:
             opt_total_timeout = opt_int_arg();
             break;
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
         case OPT_TLS_USED:
             opt_tls_used = 1;
             break;
@@ -2516,6 +2582,18 @@ static int get_opts(int argc, char **argv)
         case OPT_CACERTSOUT:
             opt_cacertsout = opt_str();
             break;
+        case OPT_OLDWITHOLD:
+            opt_oldwithold = opt_str();
+            break;
+        case OPT_NEWWITHNEW:
+            opt_newwithnew = opt_str();
+            break;
+        case OPT_NEWWITHOLD:
+            opt_newwithold = opt_str();
+            break;
+        case OPT_OLDWITHNEW:
+            opt_oldwithnew = opt_str();
+            break;
 
         case OPT_V_CASES:
             if (!opt_verify(o, vpm))
@@ -2540,9 +2618,6 @@ static int get_opts(int argc, char **argv)
         case OPT_SUBJECT:
             opt_subject = opt_str();
             break;
-        case OPT_ISSUER:
-            opt_issuer = opt_str();
-            break;
         case OPT_DAYS:
             opt_days = opt_int_arg();
             break;
@@ -2602,6 +2677,12 @@ static int get_opts(int argc, char **argv)
                 goto opthelp;
             }
             break;
+        case OPT_ISSUER:
+            opt_issuer = opt_str();
+            break;
+        case OPT_SERIAL:
+            opt_serial = opt_arg();
+            break;
         case OPT_CERTFORM:
             opt_certform_s = opt_str();
             break;
@@ -2650,7 +2731,7 @@ static int get_opts(int argc, char **argv)
             opt_use_mock_srv = 1;
             break;
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
         case OPT_PORT:
             opt_port = opt_str();
             break;
@@ -2691,6 +2772,15 @@ static int get_opts(int argc, char **argv)
         case OPT_RSP_CAPUBS:
             opt_rsp_capubs = opt_str();
             break;
+        case OPT_RSP_NEWWITHNEW:
+            opt_rsp_newwithnew = opt_str();
+            break;
+        case OPT_RSP_NEWWITHOLD:
+            opt_rsp_newwithold = opt_str();
+            break;
+        case OPT_RSP_OLDWITHNEW:
+            opt_rsp_oldwithnew = opt_str();
+            break;
         case OPT_POLL_COUNT:
             opt_poll_count = opt_int_arg();
             break;
@@ -2739,7 +2829,7 @@ static int get_opts(int argc, char **argv)
     return 1;
 }
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
 static int cmp_server(OSSL_CMP_CTX *srv_cmp_ctx)
 {
     BIO *acbio;
@@ -2827,7 +2917,7 @@ static void print_status(void)
         OSSL_CMP_CTX_snprint_PKIStatus(cmp_ctx, buf, OSSL_CMP_PKISI_BUFLEN);
     const char *from = "", *server = "";
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     if (opt_server != NULL) {
         from = " from ";
         server = opt_server;
@@ -2857,18 +2947,61 @@ static int do_genm(OSSL_CMP_CTX *ctx)
             return 0;
         }
 
-        if (!OSSL_CMP_get_caCerts(ctx, &cacerts))
+        if (!OSSL_CMP_get1_caCerts(ctx, &cacerts))
             return 0;
 
         /* could check authorization of sender/origin at this point */
         if (cacerts == NULL) {
             CMP_warn("no CA certificates provided by server");
         } else if (save_free_certs(cacerts, opt_cacertsout, "CA") < 0) {
-            CMP_err1("Failed to store CA certficates from genp in %s",
+            CMP_err1("Failed to store CA certificates from genp in %s",
                      opt_cacertsout);
             return 0;
         }
         return 1;
+    } else if (opt_infotype == NID_id_it_rootCaCert) {
+        X509 *oldwithold = NULL;
+        X509 *newwithnew = NULL;
+        X509 *newwithold = NULL;
+        X509 *oldwithnew = NULL;
+        int res = 0;
+
+        if (opt_newwithnew == NULL) {
+            CMP_err("Missing -newwithnew option for -infotype rootCaCert");
+            return 0;
+        }
+        if (opt_oldwithold == NULL) {
+            CMP_warn("No -oldwithold given, will use all certs given with -trusted as trust anchors for verifying the newWithNew cert");
+        } else {
+            oldwithold = load_cert_pwd(opt_oldwithold, opt_otherpass,
+                                       "OldWithOld cert for genm with -infotype rootCaCert");
+            if (oldwithold == NULL)
+                goto end_upd;
+        }
+        if (!OSSL_CMP_get1_rootCaKeyUpdate(ctx, oldwithold, &newwithnew,
+                                           &newwithold, &oldwithnew))
+            goto end_upd;
+        /* At this point might check authorization of response sender/origin */
+
+        if (newwithnew == NULL)
+            CMP_info("no root CA certificate update available");
+        else if (oldwithold == NULL && oldwithnew != NULL)
+            CMP_warn("oldWithNew certificate received in genp for verifying oldWithOld, but oldWithOld was not provided");
+
+        if (save_cert_or_delete(newwithnew, opt_newwithnew,
+                                "NewWithNew cert from genp")
+            && save_cert_or_delete(newwithold, opt_newwithold,
+                                   "NewWithOld cert from genp")
+            && save_cert_or_delete(oldwithnew, opt_oldwithnew,
+                                   "OldWithNew cert from genp"))
+            res = 1;
+
+        X509_free(newwithnew);
+        X509_free(newwithold);
+        X509_free(oldwithnew);
+    end_upd:
+        X509_free(oldwithold);
+        return res;
     } else {
         OSSL_CMP_ITAV *req;
         STACK_OF(OSSL_CMP_ITAV) *itavs;
@@ -3006,7 +3139,7 @@ int cmp_main(int argc, char **argv)
         goto err;
     }
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     if (opt_tls_cert == NULL && opt_tls_key == NULL && opt_tls_keypass == NULL
             && opt_tls_extra == NULL && opt_tls_trusted == NULL
             && opt_tls_host == NULL) {
@@ -3040,7 +3173,7 @@ int cmp_main(int argc, char **argv)
 #endif
 
     if (opt_use_mock_srv
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
         || opt_port != NULL
 #endif
         ) {
@@ -3057,7 +3190,7 @@ int cmp_main(int argc, char **argv)
         OSSL_CMP_CTX_set_log_verbosity(srv_cmp_ctx, opt_verbosity);
     }
 
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     if (opt_tls_used && (opt_use_mock_srv || opt_server == NULL)) {
         CMP_warn("ignoring -tls_used option since -use_mock_srv is given or -server is not given");
         opt_tls_used = 0;
@@ -3145,7 +3278,7 @@ int cmp_main(int argc, char **argv)
     cleanse(opt_keypass);
     cleanse(opt_newkeypass);
     cleanse(opt_otherpass);
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
     cleanse(opt_tls_keypass);
 #endif
     cleanse(opt_secret);
@@ -3156,7 +3289,7 @@ int cmp_main(int argc, char **argv)
         OSSL_CMP_CTX_print_errors(cmp_ctx);
 
     if (cmp_ctx != NULL) {
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
         APP_HTTP_TLS_INFO *info = OSSL_CMP_CTX_get_http_cb_arg(cmp_ctx);
 
         (void)OSSL_CMP_CTX_set_http_cb_arg(cmp_ctx, NULL);
@@ -3165,7 +3298,7 @@ int cmp_main(int argc, char **argv)
         X509_STORE_free(OSSL_CMP_CTX_get_certConf_cb_arg(cmp_ctx));
         /* cannot free info already here, as it may be used indirectly by: */
         OSSL_CMP_CTX_free(cmp_ctx);
-#ifndef OPENSSL_NO_SOCK
+#if !defined(OPENSSL_NO_SOCK) && !defined(OPENSSL_NO_HTTP)
         if (info != NULL) {
             OPENSSL_free((char *)info->server);
             OPENSSL_free((char *)info->port);