Fix CMP -days option range checking and test failing with enable-ubsan
[openssl.git] / apps / cmp.c
index 1e4642d466e0a82b3bc3082136311a8e9cf054a9..05fae77d38baa62c1acdceb1bbdc0da5fefc156a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007-2019 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2007-2020 The OpenSSL Project Authors. All Rights Reserved.
  * Copyright Nokia 2007-2019
  * Copyright Siemens AG 2015-2019
  *
@@ -46,157 +46,6 @@ 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 */
@@ -212,13 +61,6 @@ 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,
@@ -314,6 +156,7 @@ static char *opt_tls_host = NULL;
 static int opt_batch = 0;
 static int opt_repeat = 1;
 static char *opt_reqin = NULL;
+static int opt_reqin_new_tid = 0;
 static char *opt_reqout = NULL;
 static char *opt_rspin = NULL;
 static char *opt_rspout = NULL;
@@ -391,7 +234,7 @@ typedef enum OPTION_choice {
     OPT_TLS_EXTRA, OPT_TLS_TRUSTED, OPT_TLS_HOST,
 
     OPT_BATCH, OPT_REPEAT,
-    OPT_REQIN, OPT_REQOUT, OPT_RSPIN, OPT_RSPOUT,
+    OPT_REQIN, OPT_REQIN_NEW_TID, OPT_REQOUT, OPT_RSPIN, OPT_RSPOUT,
 
     OPT_USE_MOCK_SRV, OPT_PORT, OPT_MAX_MSGS,
     OPT_SRV_REF, OPT_SRV_SECRET,
@@ -504,16 +347,16 @@ const OPTIONS cmp_options[] = {
 
     OPT_SECTION("Server authentication"),
     {"trusted", OPT_TRUSTED, 's',
-     "Trusted certs used for CMP server authentication when verifying responses"},
+     "Certificates to trust as chain roots when verifying signed CMP responses"},
     {OPT_MORE_STR, 0, 0, "unless -srvcert is given"},
     {"untrusted", OPT_UNTRUSTED, 's',
-     "Intermediate certs for chain construction verifying CMP/TLS/enrolled certs"},
+     "Intermediate CA certs for chain construction for CMP/TLS/enrolled certs"},
     {"srvcert", OPT_SRVCERT, 's',
-     "Specific CMP server cert to use and trust directly when verifying responses"},
+     "Server cert to pin and trust directly when verifying signed CMP responses"},
     {"recipient", OPT_RECIPIENT, 's',
-     "Distinguished Name (DN) of the recipient to use unless -srvcert is given"},
+     "Distinguished Name (DN) to use as msg recipient; see man page for defaults"},
     {"expect_sender", OPT_EXPECT_SENDER, 's',
-     "DN of expected response sender. Defaults to DN of -srvcert, if provided"},
+     "DN of expected sender of responses. Defaults to subject of -srvcert, if any"},
     {"ignore_keyusage", OPT_IGNORE_KEYUSAGE, '-',
      "Ignore CMP signer cert key usage, else 'digitalSignature' must be allowed"},
     {"unprotected_errors", OPT_UNPROTECTED_ERRORS, '-',
@@ -594,6 +437,8 @@ const OPTIONS cmp_options[] = {
     {"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)"},
+    {"reqin_new_tid", OPT_REQIN_NEW_TID, '-',
+     "Use fresh transactionID for CMP requests read from -reqin"},
     {"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"},
@@ -706,7 +551,8 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */
     {&opt_tls_extra}, {&opt_tls_trusted}, {&opt_tls_host},
 
     {(char **)&opt_batch}, {(char **)&opt_repeat},
-    {&opt_reqin}, {&opt_reqout}, {&opt_rspin}, {&opt_rspout},
+    {&opt_reqin}, {(char **)&opt_reqin_new_tid},
+    {&opt_reqout}, {&opt_rspin}, {&opt_rspout},
 
     {(char **)&opt_use_mock_srv}, {&opt_port}, {(char **)&opt_max_msgs},
     {&opt_srv_ref}, {&opt_srv_secret},
@@ -828,7 +674,7 @@ static EVP_PKEY *load_key_pwd(const char *uri, int format,
                               const char *pass, ENGINE *e, const char *desc)
 {
     char *pass_string = get_passwd(pass, desc);
-    EVP_PKEY *pkey = load_key_preliminary(uri, format, 0, pass_string, e, desc);
+    EVP_PKEY *pkey = load_key(uri, format, 0, pass_string, e, desc);
 
     clear_free(pass_string);
     return pkey;
@@ -1161,26 +1007,17 @@ static OSSL_CMP_MSG *read_write_req_resp(OSSL_CMP_CTX *ctx,
     if (req != NULL && opt_reqout != NULL
             && !write_PKIMESSAGE(req, &opt_reqout))
         goto err;
-    if (opt_reqin != NULL) {
-        if (opt_rspin != NULL) {
-            CMP_warn("-reqin is ignored since -rspin is present");
-        } else {
-            if ((req_new = read_PKIMESSAGE(&opt_reqin)) == NULL)
-                goto err;
-            /*-
-             * The transaction ID in req_new may not be fresh.
-             * In this case the Insta Demo CA correctly complains:
-             * "Transaction id already in use."
-             * The following workaround unfortunately requires re-protection.
-             * See also https://github.com/mpeylo/cmpossl/issues/8
-             */
-#if defined(USE_TRANSACTIONID_WORKAROUND)
-            hdr = OSSL_CMP_MSG_get0_header(req_new);
-            if (!OSSL_CMP_CTX_set1_transactionID(hdr, NULL)
-                    || !ossl_cmp_msg_protect(ctx, req_new))
-                goto err;
-#endif
-        }
+    if (opt_reqin != NULL && opt_rspin == NULL) {
+        if ((req_new = read_PKIMESSAGE(&opt_reqin)) == NULL)
+            goto err;
+        /*-
+         * The transaction ID in req_new read from opt_reqin may not be fresh.
+         * In this case the server may complain "Transaction id already in use."
+         * The following workaround unfortunately requires re-protection.
+         */
+        if (opt_reqin_new_tid
+                && !OSSL_CMP_MSG_update_transactionID(ctx, req_new))
+            goto err;
     }
 
     if (opt_rspin != NULL) {
@@ -1540,7 +1377,8 @@ static OSSL_CMP_SRV_CTX *setup_srv_ctx(ENGINE *e)
     if (opt_srv_cert != NULL) {
         X509 *srv_cert = load_cert_pwd(opt_srv_cert, opt_srv_keypass,
                                        "certificate of the server");
-        if (srv_cert == NULL || !OSSL_CMP_CTX_set1_clCert(ctx, srv_cert)) {
+
+        if (srv_cert == NULL || !OSSL_CMP_CTX_set1_cert(ctx, srv_cert)) {
             X509_free(srv_cert);
             goto err;
         }
@@ -1799,8 +1637,7 @@ static SSL_CTX *setup_ssl_ctx(OSSL_CMP_CTX *ctx, ENGINE *e)
 
         /*
          * Any further certs and any untrusted certs are used for constructing
-         * the client cert chain to be provided along with the TLS client cert
-         * to the TLS server.
+         * the chain to be provided with the TLS client cert to the TLS server.
          */
         if (!SSL_CTX_set0_chain(ssl_ctx, certs)) {
             CMP_err("could not set TLS client cert chain");
@@ -1944,7 +1781,7 @@ static int setup_protection_ctx(OSSL_CMP_CTX *ctx, ENGINE *e)
     }
 
     if (opt_cert != NULL) {
-        X509 *clcert;
+        X509 *cert;
         STACK_OF(X509) *certs = NULL;
         int ok;
 
@@ -1953,14 +1790,14 @@ static int setup_protection_ctx(OSSL_CMP_CTX *ctx, ENGINE *e)
             /* opt_keypass is needed if opt_cert is an encrypted PKCS#12 file */
             goto err;
 
-        clcert = sk_X509_delete(certs, 0);
-        if (clcert == NULL) {
+        cert = sk_X509_delete(certs, 0);
+        if (cert == NULL) {
             CMP_err("no client certificate found");
             sk_X509_pop_free(certs, X509_free);
             goto err;
         }
-        ok = OSSL_CMP_CTX_set1_clCert(ctx, clcert);
-        X509_free(clcert);
+        ok = OSSL_CMP_CTX_set1_cert(ctx, cert);
+        X509_free(cert);
 
         if (ok) {
             /* add any remaining certs to the list of untrusted certs */
@@ -2043,9 +1880,12 @@ static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *e)
         }
     }
 
-    if (opt_days > 0)
-        (void)OSSL_CMP_CTX_set_option(ctx, OSSL_CMP_OPT_VALIDITY_DAYS,
-                                      opt_days);
+    if (opt_days > 0
+            && !OSSL_CMP_CTX_set_option(ctx, OSSL_CMP_OPT_VALIDITY_DAYS,
+                                        opt_days)) {
+        CMP_err("could to set requested cert validity period");
+        goto err;
+    }
 
     if (opt_policies != NULL && opt_policy_oids != NULL) {
         CMP_err("cannot have policies both via -policies and via -policy_oids");
@@ -2259,9 +2099,12 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *e)
         goto oom;
     if (opt_proxy != NULL && !OSSL_CMP_CTX_set1_proxy(ctx, opt_proxy))
         goto oom;
+    if (opt_no_proxy != NULL && !OSSL_CMP_CTX_set1_no_proxy(ctx, opt_no_proxy))
+        goto oom;
     (void)BIO_snprintf(server_buf, sizeof(server_buf), "http%s://%s%s%s/%s",
                        opt_tls_used ? "s" : "", opt_server,
                        server_port == 0 ? "" : ":", server_port_s,
+                       opt_path == NULL ? "" :
                        opt_path[0] == '/' ? opt_path + 1 : opt_path);
 
     if (opt_proxy != NULL)
@@ -2325,6 +2168,10 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *e)
         (void)OSSL_CMP_CTX_set_option(ctx, OSSL_CMP_OPT_TOTAL_TIMEOUT,
                                       opt_total_timeout);
 
+    if (opt_reqin != NULL && opt_rspin != NULL)
+        CMP_warn("-reqin is ignored since -rspin is present");
+    if (opt_reqin_new_tid && opt_reqin == NULL)
+        CMP_warn("-reqin_new_tid is ignored since -reqin is not present");
     if (opt_reqin != NULL || opt_reqout != NULL
             || opt_rspin != NULL || opt_rspout != NULL || opt_use_mock_srv)
         (void)OSSL_CMP_CTX_set_transfer_cb(ctx, read_write_req_resp);
@@ -2379,7 +2226,7 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *e)
     if (!set_name(opt_recipient, OSSL_CMP_CTX_set1_recipient, ctx, "recipient")
             || !set_name(opt_expect_sender, OSSL_CMP_CTX_set1_expected_sender,
                          ctx, "expected sender"))
-        goto oom;
+        goto err;
 
     if (opt_geninfo != NULL && !handle_opt_geninfo(ctx))
         goto err;
@@ -2899,6 +2746,9 @@ static int get_opts(int argc, char **argv)
         case OPT_REQIN:
             opt_reqin = opt_str("reqin");
             break;
+        case OPT_REQIN_NEW_TID:
+            opt_reqin_new_tid = 1;
+            break;
         case OPT_REQOUT:
             opt_reqout = opt_str("reqout");
             break;
@@ -3086,7 +2936,7 @@ int cmp_main(int argc, char **argv)
     }
 
     if (opt_engine != NULL)
-        e = setup_engine_flags(opt_engine, 0 /* not: ENGINE_METHOD_ALL */, 0);
+        e = setup_engine_methods(opt_engine, 0 /* not: ENGINE_METHOD_ALL */, 0);
 
     if (opt_port != NULL) {
         if (opt_use_mock_srv) {
@@ -3132,12 +2982,13 @@ int cmp_main(int argc, char **argv)
         if ((acbio = http_server_init_bio(prog, opt_port)) == NULL)
             goto err;
         while (opt_max_msgs <= 0 || msgs < opt_max_msgs) {
+            char *path = NULL;
             OSSL_CMP_MSG *req = NULL;
             OSSL_CMP_MSG *resp = NULL;
 
             ret = http_server_get_asn1_req(ASN1_ITEM_rptr(OSSL_CMP_MSG),
-                                           (ASN1_VALUE **)&req, &cbio, acbio,
-                                           prog, 0, 0);
+                                           (ASN1_VALUE **)&req, &path,
+                                           &cbio, acbio, prog, 0, 0);
             if (ret == 0)
                 continue;
             if (ret++ == -1)
@@ -3146,17 +2997,32 @@ int cmp_main(int argc, char **argv)
             ret = 0;
             msgs++;
             if (req != NULL) {
+                if (strcmp(path, "") != 0 && strcmp(path, "pkix/") != 0) {
+                    (void)http_server_send_status(cbio, 404, "Not Found");
+                    CMP_err1("Expecting empty path or 'pkix/' but got '%s'",
+                             path);
+                    OPENSSL_free(path);
+                    OSSL_CMP_MSG_free(req);
+                    goto cont;
+                }
+                OPENSSL_free(path);
                 resp = OSSL_CMP_CTX_server_perform(cmp_ctx, req);
                 OSSL_CMP_MSG_free(req);
-                if (resp == NULL)
+                if (resp == NULL) {
+                    (void)http_server_send_status(cbio,
+                                                  500, "Internal Server Error");
                     break; /* treated as fatal error */
+                }
                 ret = http_server_send_asn1_resp(cbio, "application/pkixcmp",
                                                  ASN1_ITEM_rptr(OSSL_CMP_MSG),
                                                  (const ASN1_VALUE *)resp);
                 OSSL_CMP_MSG_free(resp);
                 if (!ret)
                     break; /* treated as fatal error */
+            } else {
+                (void)http_server_send_status(cbio, 400, "Bad Request");
             }
+        cont:
             BIO_free_all(cbio);
             cbio = NULL;
         }