Blame SOURCES/pki-core-Verify-Cert-Before-Import.patch

86bca3
From bbc31b8303d34053562c9cb6e2c1132f124bfdb1 Mon Sep 17 00:00:00 2001
86bca3
From: Alexander Scheel <ascheel@redhat.com>
86bca3
Date: Wed, 16 Jan 2019 17:16:09 -0500
86bca3
Subject: [PATCH 1/2] Add validate-then-import certificate utility
86bca3
86bca3
The NSS utility certutil requires certificates to be imported
86bca3
(`certutil -A`) prior to validating their signatures and usage
86bca3
(`certutil -V -e`). PKICertImport avoids this pitfall by handling both
86bca3
import and validation in the same step, so if the validation fails, the
86bca3
certificate is removed. This ensures it is not accidentally used before
86bca3
it is verified.
86bca3
86bca3
Signed-off-by: Alexander Scheel <ascheel@redhat.com>
86bca3
(cherry picked from commit a187cccc269968e310d97eafc99771d2cd5b366e)
86bca3
---
86bca3
 base/util/PKICertImport.bash | 328 +++++++++++++++++++++++++++++++++++++++++++
86bca3
 1 file changed, 328 insertions(+)
86bca3
 create mode 100755 base/util/PKICertImport.bash
86bca3
86bca3
diff --git a/base/util/PKICertImport.bash b/base/util/PKICertImport.bash
86bca3
new file mode 100755
86bca3
index 0000000..d0b54c7
86bca3
--- /dev/null
86bca3
+++ b/base/util/PKICertImport.bash
86bca3
@@ -0,0 +1,328 @@
86bca3
+#!/bin/bash
86bca3
+
86bca3
+# Copyright (C) 2018 Red Hat
86bca3
+
86bca3
+# PKICertImport performs a validate-then-import strategy for importing
86bca3
+# certificates into a NSS DB or HSM Token by wrapping both calls to
86bca3
+# certutil (`certutil -A` and `certutil -V`) such that the certificate
86bca3
+# will be removed if import fails (`certutil -D`). This helps to ensure
86bca3
+# that certificates are not used prior to validation.
86bca3
+function PKICertImport() {
86bca3
+    ## [ overview ] ##
86bca3
+
86bca3
+    # This script has four major sections:
86bca3
+    #
86bca3
+    #   1. Globals -- the definitions of all script-global variables
86bca3
+    #   2. Helper functions -- functions which don't perform key operations
86bca3
+    #   3. Core commands -- functions which interact with the NSS DB via
86bca3
+    #                       certutil
86bca3
+    #   4. Program flow -- main flow of the program
86bca3
+
86bca3
+
86bca3
+    ## [ globals ] ##
86bca3
+
86bca3
+    # Path to certificate; parsed from arguments.
86bca3
+    local CERT_PATH=""
86bca3
+
86bca3
+    # User-given nickname for the certificate.
86bca3
+    local CERT_NICKNAME=""
86bca3
+
86bca3
+    # Whether or not the certificate is in ASCII/PEM format.
86bca3
+    local CERT_ASCII="false"
86bca3
+
86bca3
+    # What trust flags to use when importing the certificate.
86bca3
+    local CERT_TRUST=""
86bca3
+
86bca3
+    # What usage flags to validate the certificate against.
86bca3
+    local CERT_USAGE=""
86bca3
+
86bca3
+    # Location of the original NSS DB.
86bca3
+    local NSSDB=""
86bca3
+
86bca3
+    # Type of the NSSDB.
86bca3
+    local NSSDB_TYPE=""
86bca3
+
86bca3
+    # Location to the NSS DB Password file, if present.
86bca3
+    local NSSDB_PASSWORD=""
86bca3
+
86bca3
+    # Name of the HSM token, if used.
86bca3
+    local HSM_TOKEN=""
86bca3
+
86bca3
+
86bca3
+    ## [ helper functions ] ##
86bca3
+
86bca3
+    # __e prints error messages, prefixing them with "e: " and writing the
86bca3
+    # output to stderr instead of stdout.
86bca3
+    function __e() {
86bca3
+        echo "e:" "$@" 1>&2
86bca3
+    }
86bca3
+
86bca3
+    # __v prints debug messages in verbose mode; these also go to stderr.
86bca3
+    # Messages are only present if the environment variable VERBOSE is set.
86bca3
+    function __v() {
86bca3
+        if [ "x$VERBOSE" != "x" ]; then
86bca3
+            echo "v:" "$@" 1>&2
86bca3
+        fi
86bca3
+    }
86bca3
+
86bca3
+    ## [ core commands ] ##
86bca3
+
86bca3
+    # Parse the command line arguments and set the appropriate global
86bca3
+    # variables. Return status of non-zero indicates a fatal error; help
86bca3
+    # should be displayed. Return status of zero indicates no error and help
86bca3
+    # should not be displayed.
86bca3
+    function _parse_args() {
86bca3
+        # Use a read-and-shift approach to parse both "<option>" and
86bca3
+        # "<option> <value>" forms.
86bca3
+        while (( $# > 0 )); do
86bca3
+            local arg="$1"
86bca3
+            shift
86bca3
+
86bca3
+            # Sorted alphabetically by short option.
86bca3
+            if [ "x$arg" == "x--ascii" ] || [ "x$arg" == "x-a" ]; then
86bca3
+                # If specified, the -a flag is passed when the certificate is
86bca3
+                # imported.
86bca3
+                CERT_ASCII="true"
86bca3
+            elif [ "x$arg" == "x--database" ] || [ "x$arg" == "x-d" ]; then
86bca3
+                # Always required; path to the original NSS DB. Note that this
86bca3
+                # differs from certutil in that we detect the NSSDB type here,
86bca3
+                # versus taking a prefix:path combination.
86bca3
+                NSSDB="$1"
86bca3
+
86bca3
+                if [ -e "$NSSDB/cert8.db" ] && [ ! -e "$NSSDB/cert9.db" ]; then
86bca3
+                    NSSDB_TYPE="dbm:"
86bca3
+                elif [ ! -e "$NSSDB/cert8.db" ] && [ -e "$NSSDB/cert9.db" ]; then
86bca3
+                    NSSDB_TYPE="sql:"
86bca3
+                else
86bca3
+                    __e "Unknown NSS DB type for directory: $NSSDB"
86bca3
+                    __e "Please ensure only one of cert8.db or cert9.db exist"
86bca3
+                    return 1
86bca3
+                fi
86bca3
+
86bca3
+                shift
86bca3
+            elif [ "x$arg" == "x--password" ] || [ "x$arg" == "x-f" ]; then
86bca3
+                # If specified, path to a file containing the NSS DB password.
86bca3
+                NSSDB_PASSWORD="$1"
86bca3
+                shift
86bca3
+            elif [ "x$arg" == "x--hsm" ] || [ "x$arg" == "x-h" ]; then
86bca3
+                # If specified, name of the HSM Token.
86bca3
+                HSM_TOKEN="$1"
86bca3
+                shift
86bca3
+            elif [ "x$arg" == "x--certificate" ] || [ "x$arg" == "x-i" ]; then
86bca3
+                # Always required; path to the original certificate.
86bca3
+                CERT_PATH="$1"
86bca3
+                shift
86bca3
+            elif [ "x$arg" == "x--nickname" ] || [ "x$arg" == "x-n" ]; then
86bca3
+                # Always required; nickname for the certificate.
86bca3
+                CERT_NICKNAME="$1"
86bca3
+                shift
86bca3
+            elif [ "x$arg" == "x--trust" ] || [ "x$arg" == "x-t" ]; then
86bca3
+                # Always required; certificate trust flags.
86bca3
+                CERT_TRUST="$1"
86bca3
+                shift
86bca3
+            elif [ "x$arg" == "x--usage" ] || [ "x$arg" == "x-u" ]; then
86bca3
+                # Always required; certificate usage flags.
86bca3
+                CERT_USAGE="$1"
86bca3
+                shift
86bca3
+            else
86bca3
+                # We print help whenever the return code is 1, so we don't
86bca3
+                # need to explicitly parse a --help flag, but we will get
86bca3
+                # an extraneous but harmless unknown argument message.
86bca3
+                __e "Unknown argument: $arg"
86bca3
+                __e "Check your option syntax; perhaps a prior argument is" \
86bca3
+                     "missing a value?"
86bca3
+                return 1
86bca3
+            fi
86bca3
+        done
86bca3
+
86bca3
+        # Ensure that we've seen the required arguments.
86bca3
+        if [ "x$NSSDB" == "x" ]; then
86bca3
+            __e "Missing NSS Database location: specify --database/-d"
86bca3
+            return 1
86bca3
+        elif [ "x$CERT_PATH" == "x" ]; then
86bca3
+            __e "Missing certificate location: specify --certificate/-i"
86bca3
+            return 1
86bca3
+        elif [ "x$CERT_NICKNAME" == "x" ]; then
86bca3
+            __e "Missing certificate nickname: specify --nickname/-n"
86bca3
+            return 1
86bca3
+        elif [ "x$CERT_TRUST" == "x" ]; then
86bca3
+            __e "Missing certificate trust: specify --trust/-t"
86bca3
+            return 1
86bca3
+        elif [ "x$CERT_USAGE" == "x" ]; then
86bca3
+            __e "Missing certificate usage: specify --usage/-u"
86bca3
+            return 1
86bca3
+        fi
86bca3
+
86bca3
+        # All good to go.
86bca3
+        return 0
86bca3
+    }
86bca3
+
86bca3
+    # Show help and usage information.
86bca3
+    function _print_help() {
86bca3
+        if (( $1 != 0 )); then
86bca3
+            echo ""
86bca3
+        fi
86bca3
+
86bca3
+        echo "Usage: $0 [arguments]"
86bca3
+        echo "$0 validates and imports certificates."
86bca3
+        echo ""
86bca3
+        echo "Requred arguments:"
86bca3
+        echo "--database, -d <path>: path to the NSS DB"
86bca3
+        echo "--certificate, -i <path>: path to the certificate to import"
86bca3
+        echo "--nickname, -n <name>: nickname for the certificate"
86bca3
+        echo "--trust, -t <flags>: trust flags for the certificate"
86bca3
+        echo "--usage, -u <flag>: usage flag to verify the certificate with"
86bca3
+        echo ""
86bca3
+        echo "Optional arguments:"
86bca3
+        echo "--ascii, -a: the certificate is in ASCII encoded"
86bca3
+        echo "--password, -f <path>: password file for the NSS DB"
86bca3
+        echo "--hsm, -h <name>: name of the HSM to use"
86bca3
+        echo ""
86bca3
+        echo "Environment variables:"
86bca3
+        echo "VERBOSE: see certutil commands being run"
86bca3
+        echo ""
86bca3
+        echo "For more information about these options, refer to the" \
86bca3
+             "certutil documentation."
86bca3
+    }
86bca3
+
86bca3
+    # Import a certificate into the NSS DB specified on $1. Errors are fatal;
86bca3
+    # uses exit instead of return.
86bca3
+    function _import_cert() {
86bca3
+        local database="$1"
86bca3
+        local ret=0
86bca3
+        local add_args=("-A")
86bca3
+
86bca3
+        # Use a single import command, setting trust as we import.
86bca3
+        add_args+=("-d" "$NSSDB_TYPE$database")
86bca3
+        add_args+=("-n" "$CERT_NICKNAME")
86bca3
+        if [ "x$NSSDB_PASSWORD" != "x" ]; then
86bca3
+            add_args+=("-f" "$NSSDB_PASSWORD")
86bca3
+        fi
86bca3
+        add_args+=("-i" "$CERT_PATH")
86bca3
+        if [ "$CERT_ASCII" == "true" ]; then
86bca3
+            add_args+=("-a")
86bca3
+        fi
86bca3
+        if [ "x$HSM_TOKEN" != "x" ]; then
86bca3
+            add_args+=("-h" "$HSM_TOKEN")
86bca3
+        fi
86bca3
+        add_args+=("-t" "$CERT_TRUST")
86bca3
+
86bca3
+        # Import the certificate...
86bca3
+        __v certutil "${add_args[@]}"
86bca3
+        certutil "${add_args[@]}"
86bca3
+        ret=$?
86bca3
+        if (( ret != 0 )); then
86bca3
+            __e "certutil returned non-zero value: $ret"
86bca3
+            __e "Unable to import certificate to NSS DB: $NSSDB."
86bca3
+            exit $ret
86bca3
+        fi
86bca3
+    }
86bca3
+
86bca3
+    # Verify the certificate in the NSS DB specified by $1.
86bca3
+    function _verify_cert() {
86bca3
+        local database="$1"
86bca3
+        local ret=0
86bca3
+        local verify_args=("-V")
86bca3
+
86bca3
+        verify_args+=("-d" "$NSSDB_TYPE$database")
86bca3
+        verify_args+=("-n" "$CERT_NICKNAME")
86bca3
+        verify_args+=("-u" "$CERT_USAGE")
86bca3
+        if [ "x$HSM_TOKEN" != "x" ]; then
86bca3
+            verify_args+=("-h" "$HSM_TOKEN")
86bca3
+        fi
86bca3
+        if [ "x$NSSDB_PASSWORD" != "x" ]; then
86bca3
+            verify_args+=("-f" "$NSSDB_PASSWORD")
86bca3
+        fi
86bca3
+
86bca3
+        # Ensures that the signature is checked as well.
86bca3
+        verify_args+=("-e")
86bca3
+
86bca3
+        # Validate the certificate. Note that we have to pattern match on the
86bca3
+        # output of certutil -V; the return code is uncorrelated with the
86bca3
+        # actual result. (It is dependent upon whether or not a HSM is used).
86bca3
+        __v certutil "${verify_args[@]}"
86bca3
+
86bca3
+        local certutil_result=""
86bca3
+        certutil_result="$(certutil "${verify_args[@]}" 2>&1)"
86bca3
+
86bca3
+        grep -q '^certutil: certificate is valid$' <<< "$certutil_result"
86bca3
+        ret=$?
86bca3
+
86bca3
+        if (( ret != 0 )); then
86bca3
+            __e "$certutil_result" 1>&2
86bca3
+        fi
86bca3
+
86bca3
+        return $ret
86bca3
+    }
86bca3
+
86bca3
+    # Remove the certificate from the NSS DB specified by $1. Errors are fatal;
86bca3
+    # uses exit instead of return.
86bca3
+    function _remove_cert() {
86bca3
+        local database="$1"
86bca3
+        local remove_args=("-D")
86bca3
+
86bca3
+        remove_args+=("-d" "$NSSDB_TYPE$database")
86bca3
+        if [ "x$NSSDB_PASSWORD" != "x" ]; then
86bca3
+            remove_args+=("-f" "$NSSDB_PASSWORD")
86bca3
+        fi
86bca3
+
86bca3
+        __v certutil "${remove_args[@]}" "-n" "$CERT_NICKNAME"
86bca3
+        certutil "${remove_args[@]}" "-n" "$CERT_NICKNAME"
86bca3
+        local ret=$?
86bca3
+        if (( ret != 0 )); then
86bca3
+            __e "certutil returned non-zero result: $ret"
86bca3
+            __e "Unable to delete certificate!"
86bca3
+            exit $?
86bca3
+        fi
86bca3
+
86bca3
+        if [ "x$HSM_TOKEN" != "x" ]; then
86bca3
+            # In the event we have a HSM, we also have to remove it from the
86bca3
+            # HSM token.
86bca3
+
86bca3
+            __v certutil "${remove_args[@]}" "-n" "$HSM_TOKEN:$CERT_NICKNAME"
86bca3
+            certutil "${remove_args[@]}" "-n" "$HSM_TOKEN:$CERT_NICKNAME"
86bca3
+            local ret=$?
86bca3
+            if (( ret != 0 )); then
86bca3
+                __e "certutil returned non-zero result: $ret"
86bca3
+                __e "Unable to delete certificate!"
86bca3
+                exit $?
86bca3
+            fi
86bca3
+        fi
86bca3
+    }
86bca3
+
86bca3
+    ## [ program flow ] ##
86bca3
+    local ret=0
86bca3
+
86bca3
+    # The program flow of this script is:
86bca3
+    #
86bca3
+    # - Parse arguments
86bca3
+    #   - [print help if required]
86bca3
+    # - Create working directory
86bca3
+    # - Copy files into the working directory
86bca3
+    # - Import certificate into NSS DB
86bca3
+    # - Validate certificate in NSS DB
86bca3
+    #   - [on failure, remove from NSS DB]
86bca3
+    #
86bca3
+    # Import is handled by _import_cert, verification is handled by
86bca3
+    # _verify_cert, and removal is handled by _remove_cert.
86bca3
+
86bca3
+    _parse_args "$@"
86bca3
+    ret="$?"
86bca3
+    if (( ret != 0 )); then
86bca3
+        _print_help $ret
86bca3
+        exit 1
86bca3
+    fi
86bca3
+
86bca3
+    _import_cert "$NSSDB"
86bca3
+    _verify_cert "$NSSDB"
86bca3
+    ret=$?
86bca3
+
86bca3
+    # Check if the verification failed. If it did, remove it from the NSS DB.
86bca3
+    if (( ret != 0 )); then
86bca3
+        __e "Verification of certificate failed!"
86bca3
+        _remove_cert "$NSSDB"
86bca3
+        exit 1
86bca3
+    fi
86bca3
+}
86bca3
+
86bca3
+PKICertImport "$@"
86bca3
-- 
86bca3
1.8.3.1
86bca3
86bca3
86bca3
From af5461c22cda63a41c9f27df6d119db11bb55f8d Mon Sep 17 00:00:00 2001
86bca3
From: Alexander Scheel <ascheel@redhat.com>
86bca3
Date: Thu, 24 Jan 2019 16:28:42 -0500
86bca3
Subject: [PATCH 2/2] Add PKICertImport to pki
86bca3
86bca3
Signed-off-by: Alexander Scheel <ascheel@redhat.com>
86bca3
86bca3
("cherry picked from commit b85472cd6c33828c5d9bd7248291515687b5ca1f"
86bca3
 with manual changes)
86bca3
---
86bca3
 base/util/CMakeLists.txt | 6 ++++++
86bca3
 1 file changed, 6 insertions(+)
86bca3
86bca3
diff --git a/base/util/CMakeLists.txt b/base/util/CMakeLists.txt
86bca3
index 94e30cc..98dac91 100644
86bca3
--- a/base/util/CMakeLists.txt
86bca3
+++ b/base/util/CMakeLists.txt
86bca3
@@ -2,3 +2,9 @@ project(util NONE)
86bca3
 
86bca3
 add_subdirectory(src)
86bca3
 add_subdirectory(test)
86bca3
+
86bca3
+install(FILES PKICertImport.bash
86bca3
+        DESTINATION ${BIN_INSTALL_DIR}
86bca3
+        RENAME PKICertImport
86bca3
+        PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
86bca3
+                    GROUP_EXECUTE GROUP_READ)
86bca3
-- 
86bca3
1.8.3.1
86bca3