diff --git a/include/libpv/cert.h b/include/libpv/cert.h deleted file mode 100644 index aebe33ba..00000000 --- a/include/libpv/cert.h +++ /dev/null @@ -1,440 +0,0 @@ -/* - * Certificate functions and definitions. - * - * Copyright IBM Corp. 2022 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ -#ifndef LIBPV_CERT_H -#define LIBPV_CERT_H - -#include -#include - -#include "libpv/common.h" - -#define PV_IBM_Z_SUBJECT_COMMON_NAME "International Business Machines Corporation" -#define PV_IBM_Z_SUBJECT_COUNTRY_NAME "US" -#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE "Poughkeepsie" -#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK "Armonk" -#define PV_IBM_Z_SUBJECT_ORGANIZATIONAL_UNIT_NAME_SUFFIX "Key Signing Service" -#define PV_IBM_Z_SUBJECT_ORGANIZATION_NAME "International Business Machines Corporation" -#define PV_IBM_Z_SUBJECT_STATE "New York" -#define PV_IMB_Z_SUBJECT_ENTRY_COUNT 6 - -/* Minimum security level for the keys/certificates used to establish a chain of - * trust (see https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_auth_level.html - * for details). - */ -#define PV_CERTS_SECURITY_LEVEL 2 - -/** pv_cert_init: - * - * Should not be called by user. - * Use pv_init() instead which - * calls this function during creation. - * - * Sets up data structures for caching CRLs. - */ -void pv_cert_init(void); - -/** pv_cert_cleanup: - * - * Should not be called by user. - * Use pv_cleanup() instead which - * calls this function during creation. - * - * Cleans up data structures for caching CRLs. - */ -void pv_cert_cleanup(void); - -#define PV_CERT_ERROR g_quark_from_static_string("pv-cert-error-quark") -typedef enum { - PV_CERT_ERROR_CERT_REVOKED, - PV_CERT_ERROR_CERT_SIGNATURE_INVALID, - PV_CERT_ERROR_CERT_SUBJECT_ISSUER_MISMATCH, - PV_CERT_ERROR_CRL_DOWNLOAD_FAILED, - PV_CERT_ERROR_CRL_SIGNATURE_INVALID, - PV_CERT_ERROR_CRL_SUBJECT_ISSUER_MISMATCH, - PV_CERT_ERROR_FAILED_DOWNLOAD_CRL, - PV_CERT_ERROR_INTERNAL, - PV_CERT_ERROR_INVALID_PARM, - PV_CERT_ERROR_INVALID_SIGNATURE_ALGORITHM, - PV_CERT_ERROR_INVALID_VALIDITY_PERIOD, - PV_CERT_ERROR_LOAD_CRL, - PV_CERT_ERROR_LOAD_DEFAULT_CA, - PV_CERT_ERROR_LOAD_ROOT_CA, - PV_CERT_ERROR_MALFORMED_CERTIFICATE, - PV_CERT_ERROR_MALFORMED_ROOT_CA, - PV_CERT_ERROR_NO_CRL, - PV_CERT_ERROR_NO_CRLDP, - PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY, - PV_CERT_ERROR_NO_ISSUER_IBM_Z_FOUND, - PV_CERT_ERROR_NO_PUBLIC_KEY, - PV_CERT_ERROR_READ_CERTIFICATE, - PV_CERT_ERROR_READ_CRL, - PV_CERT_ERROR_SIGNATURE_ALGORITHM_MISMATCH, - PV_CERT_ERROR_SKID_AKID_MISMATCH, - PV_CERT_ERROR_VERIFICATION_FAILED, - PV_CERT_ERROR_WRONG_CA_USED, -} PvCertErrors; - -/** PvX509WithPath - X509 certificate associated with a path - */ -typedef struct { - X509 *cert; - char *path; -} PvX509WithPath; - -/** pv_x509_with_path_new: - * - * @cert: X509 certificate - * @path: Path of that X509 certificate - * - * Returns: (nullable) (transfer full): new X509 with path - */ -PvX509WithPath *pv_x509_with_path_new(X509 *cert, const char *path); - -/** pv_x509_with_path_free: - * - * Frees the path and the PvX509WithPath; Decreases the refcount of the X509 - */ -void pv_x509_with_path_free(PvX509WithPath *cert); - -typedef STACK_OF(DIST_POINT) STACK_OF_DIST_POINT; -typedef STACK_OF(X509) STACK_OF_X509; -typedef STACK_OF(X509_CRL) STACK_OF_X509_CRL; -typedef GSList PvCertWithPathList; - -typedef struct { - X509 *cert; - STACK_OF_X509_CRL *crls; -} PvX509Pair; - -/** pv_x509_pair_new_take: - * @cert: ptr to X509 - * @crls: ptr to CRLs - * - * Takes a X509 and the associated CRLs and builds a pair. - * Both, *cert and *crls will be NULL afterwards, and owned by the pair. - * - * Returns: (nullable) (transfer full): New PvX509Pair - */ -PvX509Pair *pv_x509_pair_new_take(X509 **cert, STACK_OF_X509_CRL **crls); - -/** pv_x509_pair_free: - * - * Decreases the refcount of the X509 and crls. - * Frees the PvX509Pair. - */ -void pv_x509_pair_free(PvX509Pair *pair); - -void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack); -void STACK_OF_X509_free(STACK_OF_X509 *stack); -void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack); - -/** pv_x509_from_pem_der_data: - * - * @data: GBytes containing the cert in PEM format - * @error: return location for a #GError - * - * Returns: (nullable) (transfer full): X509 cert - */ -X509 *pv_x509_from_pem_der_data(GBytes *data, GError **error); - -/** pv_x509_get_ec_pubkey: - * - * @cert: X509 to extract elliptic curve pubkey from - * @nid: numerical identifier of the expected curve - * @error: return location for a #GError - * - * Returns: (nullable) (transfer full): corresponding pupkey for the given certificate - */ -EVP_PKEY *pv_x509_get_ec_pubkey(X509 *cert, int nid, GError **error); - -/** pv_get_ec_pubkeys: - * - * @certs_with_path: List of PvX509WithPath - * @nid: numerical identifier of the expected curve - * @error: return location for a #GError - * - * Returns: (nullable) (transfer full): List of corresponding public keys for the given certificate - */ -GSList *pv_get_ec_pubkeys(PvCertWithPathList *certs_with_path, int nid, GError **error); - -/* pv_load_certificates: - * - * @cert_paths: list of cert paths. - * @error: return location for a #GError - * - * @cert_paths must contain at least one element, otherwise an error is - * reported. - * - * Returns: (nullable) (transfer full): List of PvX509WithPath corresponding to the given paths - */ -PvCertWithPathList *pv_load_certificates(char **cert_paths, GError **error); - -/* pv_load_first_cert_from_file: - * - * @path: location of the x509 - * @error: return location for a #GError - * - * This function reads in only the first certificate and ignores all other. This - * is only relevant for the PEM file format. For the host-key document and the - * root CA this behavior is expected. - * - * Returns: (nullable) (transfer full): PvX509WithPath corresponding to the given path - */ -X509 *pv_load_first_cert_from_file(const char *path, GError **error); - -/* pv_load_first_crl_from_file: - * - * @path: location of the x509 CRL - * @error: return location for a #GError - * - * This function reads in only the first CRL and ignores all other. This - * is only relevant for the PEM file format. - * - * Returns: (nullable) (transfer full): X509_CRL corresponding to the given path - */ -X509_CRL *pv_load_first_crl_from_file(const char *path, GError **error); - -/** pv_store_setup_crl_download: - * - * @st: X509_STORE - */ -void pv_store_setup_crl_download(X509_STORE *st); - -/** pv_load_first_crl_by_cert: - * @cert: X509 to specify the download location. - * @error: return location for a #GError - * - * This function returns the first X509_CRL found from the CRL distribution - * points specified in @cert. - * - * Returns: (nullable) (transfer full): x509 CRL corresponding to the given X509 - */ -X509_CRL *pv_load_first_crl_by_cert(X509 *cert, GError **error); - -/** pv_try_load_crls_by_certs: - * - * @certs_with_path: List of PvX509WithPath - * - * Returns: (nullable) (transfer full): Stack of CRLs corresponding to the given X509 - */ -STACK_OF_X509_CRL *pv_try_load_crls_by_certs(PvCertWithPathList *certs_with_path); - -/** pv_store_setup: - * - * @root_ca_path: Location of the rootCA or NULL if SystemRoot CA shall be used - * @crl_paths: List of CRL paths or NULL - * @cert_with_crl_paths: List of (untrusted) X509 paths - * @error: return location for a #GError - * - * The untrusted certs need to be verified before actually verifying a Host Key Document. - * - * Returns: (nullable) (transfer full): X509_store with given input data. - * - */ -X509_STORE *pv_store_setup(char *root_ca_path, char **crl_paths, char **cert_with_crl_paths, - GError **error); - -/** pv_get_x509_stack: - * - * x509_with_path_list: list of PvX509WithPath - * - * Returns: (nullable) (transfer full): Stack of X509 corresponding to the given x509 with path - */ -STACK_OF_X509 *pv_get_x509_stack(const GSList *x509_with_path_list); - -/** pv_init_store_ctx: - * - * @ctx: a uninitialized Store CTX - * @trusted: X509_STORE with a trusted rootCA - * @chain: untrusted X509s - * @error: return location for a #GError - * - * Can be called multiple times on the same context if X509_STORE_CTX_cleanup(ctx) - * was called before. - * - * Returns: - * 0 on success - * -1 in failure - */ -int pv_init_store_ctx(X509_STORE_CTX *ctx, X509_STORE *trusted, STACK_OF_X509 *chain, - GError **error) PV_NONNULL(1, 2, 3); - -/** pv_init_store_ctx: - * - * @trusted: X509_STORE with a trusted rootCA - * @chain: untrusted X509s - * @error: return location for a #GError - * - * Returns: (nullable) (transfer full): X509_STORE_CTX setup with the input data - */ -X509_STORE_CTX *pv_create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain, GError **error) - PV_NONNULL(1, 2); -/** pv_remove_ibm_signing_certs: - * - * @certs: Stack of X509s - * - * Returns: (transfer full): - * List of all IBM Z signing key certificates in @certs and remove them - * from the chain. - * Empty stack if no IBM Z signing key is found. - */ -STACK_OF_X509 *pv_remove_ibm_signing_certs(STACK_OF_X509 *certs); - -/** pv_c2b_name: - * - * Workaround to fix the mismatch between issuer name of the - * IBM Z signing CRLs and the IBM Z signing key subject name. - * - * In RFC 5280 the attributes of a (subject/issuer) name is not mandatory - * ordered. The problem is that our certificates are not consistent in the order - * (see https://tools.ietf.org/html/rfc5280#section-4.1.2.4 for details). - * - * This function tries to reorder the name attributes such that - * further OpenSSL calls can work with it. The caller is - * responsible to free the returned value. - */ -X509_NAME *pv_c2b_name(const X509_NAME *name); - -/** pv_verify_host_key: - * - * @host_key: X509 to be verified - * @issuer_pairs: IBM signing key X509+CRLs Pairs used for verification - * @level: Security level. see PV_CERTS_SECURITY_LEVEL - * @error: return location for a #GError - * - * Returns: - * 0 if Host key could be verified with one of the IBM signing keys - * -1 if no IBM signing key could verify the authenticity of the given host key - * - */ -int pv_verify_host_key(X509 *host_key, GSList *issuer_pairs, int verify_flags, int level, - GError **error); - -/** pv_verify_cert: - * - * @ctx: trusted store ctx used for verification - * @cert: X509 to be verified - * @error: return location for a #GError - * - * Cannot be used to verify host keys with IBM signing keys, as IBM signing - * keys are no intermediate CAs. Use pv_verify_host_key() instead. - * - * Returns: - * 0 if @cert could be verified - * -1 if @cert could not be verified - */ -int pv_verify_cert(X509_STORE_CTX *ctx, X509 *cert, GError **error) PV_NONNULL(1, 2); - -/** pv_check_crl_valid_for_cert: - * - * @crl: CRL to be verified - * @cert: Cert that probably issued the given CRL - * @verify_flags: X509 Verification flags (X509_V_FLAG_) - * @error: return location for a #GError - * - * Verify whether a revocation list @crl is valid and is issued by @cert. For - * this multiple steps must be done: - * - * 1. verify issuer of the CRL matches with the suject name of @cert - * 2. verify the validity period of the CRL - * 3. verify the signature of the CRL - * - * Important: This function does not verify whether @cert is allowed to issue a - * CRL. - * - * Returns: - * 0 if @crl is valid and issued by @cert - * -1 otherwise - */ -int pv_verify_crl(X509_CRL *crl, X509 *cert, int verify_flags, GError **error); - -/** pv_check_chain_parameters: - * - * @chain: chain of trust to be validated - * @error: return location for a #GError - * - * Verifies that chain has at least a RootCA ans intermediate CA - * and logs the used ROD CA subject - * - * Returns: - * 0 @chain is valid - * -1 otherwise - */ -int pv_check_chain_parameters(const STACK_OF_X509 *chain, GError **error); - -/** pv_store_set_verify_param: - * - * @store: X509_STORE to set parameters - * @error: return location for a #GError - * - * Returns: - * 0 on success - * -1 on failure - */ -int pv_store_set_verify_param(X509_STORE *store, GError **error); - -/** pv_store_ctx_find_valid_crls: - * - * @ctx: STORE_CTX for searching CRLs - * @cert: X509 to match CRLs aggainst - * @error: return location for a #GError - * - * Returns: (nullable) (transfer full): STACK of CRLs related to given @crl fin @ctx - */ -STACK_OF_X509_CRL *pv_store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert, GError **error) - PV_NONNULL(1, 2); - -/** pv_verify_host_key_doc: - * - * @host_key_certs_with_path: X509s to be verified - * @trusted. X509_STORE with a rusted RootCA - * @untrusted_certs: STACK OF untrusted X509s - * @online: true if CRLs shall be downloaded - * @error: return location for a #GError - * - * Returns: - * 0 if all given HKDs could be verified using the chain of trust. - * -1 otherwise - */ -int pv_verify_host_key_doc(PvCertWithPathList *host_key_certs_with_path, X509_STORE *trusted, - STACK_OF_X509 *untrusted_certs, gboolean online, GError **error) - PV_NONNULL(1, 2, 3); - -/** pv_verify_host_key_docs_by_path: - * - * @host_key_paths: locations of X509 to be verified - * @optional_root_ca_path: rootCA location or NULL if Default shall be used - * @optional_crl_paths: locations of CRLs or NULL - * @untrusted_cert_paths: locations of IntermediateCAs including the IBM signing key - * @online: true if CRLs shall be downloaded - * @error: return location for a #GError - * - * Returns: - * 0 if all given HKDs could be verfied using the chain of trust. - * -1 otherwise - */ -int pv_verify_host_key_docs_by_path(char **host_key_paths, char *optional_root_ca_path, - char **optional_crl_paths, char **untrusted_cert_paths, - gboolean online, GError **error) PV_NONNULL(1, 4); - -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(AUTHORITY_KEYID, AUTHORITY_KEYID_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvX509WithPath, pv_x509_with_path_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_DIST_POINT, STACK_OF_DIST_POINT_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509, STACK_OF_X509_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509_CRL, STACK_OF_X509_CRL_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509, X509_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_CRL, X509_CRL_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_LOOKUP, X509_LOOKUP_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_NAME, X509_NAME_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_VERIFY_PARAM, X509_VERIFY_PARAM_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvX509Pair, pv_x509_pair_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE, X509_STORE_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE_CTX, X509_STORE_CTX_free) - -#endif /* LIBPV_CERT_H */ diff --git a/include/libpv/common.h b/include/libpv/common.h index cda8d72b..a749aef2 100644 --- a/include/libpv/common.h +++ b/include/libpv/common.h @@ -14,22 +14,6 @@ * the glib version is supported */ #include "libpv/glib-helper.h" - #include -#include "libpv/openssl-compat.h" -#include "libpv/macros.h" - -/** pv_init: - * - * Must be called before any libpv call. - */ -int pv_init(void); - -/** pv_cleanup: - * - * Must be called when done with using libpv. - */ -void pv_cleanup(void); - #endif /* LIBPV_COMMON_H */ diff --git a/include/libpv/crypto.h b/include/libpv/crypto.h index 44b253bf..e1b29d83 100644 --- a/include/libpv/crypto.h +++ b/include/libpv/crypto.h @@ -15,6 +15,7 @@ #include #include "libpv/common.h" +#define PV_NONNULL(...) typedef struct pv_cipher_parms { const EVP_CIPHER *cipher; @@ -26,17 +27,6 @@ typedef struct pv_cipher_parms { }; } PvCipherParms; -typedef union { - struct { - uint8_t x[80]; - uint8_t y[80]; - }; - uint8_t data[160]; -} PvEcdhPubKey; -G_STATIC_ASSERT(sizeof(PvEcdhPubKey) == 160); - -typedef GSList PvEvpKeyList; - enum PvCryptoMode { PV_ENCRYPT, PV_DECRYPT, @@ -61,42 +51,6 @@ char *pv_get_openssl_errors(void); */ int pv_BIO_reset(BIO *b); -/** - * pv_generate_rand_data: - * @size: number of generated random bytes using a crypographically secure pseudo random generator - * @error: return location for a #GError - * - * Creates a new #GBytes with @size random bytes using a cryptographically - * secure pseudo random generator. - * - * Returns: (nullable) (transfer full): a new #GBytes, or %NULL in case of an error - */ -GBytes *pv_generate_rand_data(size_t size, GError **error); - -/** - * pv_generate_key: - * @cipher: specifies the OpenSSL cipher for which a cryptographically secure key should be generated - * @error: return location for a #GError - * - * Creates a random key for @cipher using a cryptographically secure pseudo - * random generator. - * - * Returns: (nullable) (transfer full): a new #GBytes, or %NULL in case of an error - */ -GBytes *pv_generate_key(const EVP_CIPHER *cipher, GError **error) PV_NONNULL(1); - -/** - * pv_generate_iv: - * @cipher: specifies the OpenSSL cipher for which a cryptographically secure IV should be generated - * @error: return location for a #GError - * - * Creates a random IV for @cipher using a cryptographically secure pseudo - * random generator. - * - * Returns: (nullable) (transfer full): a new #GBytes, or %NULL in case of an error - */ -GBytes *pv_generate_iv(const EVP_CIPHER *cipher, GError **error) PV_NONNULL(1); - /* Symmetric en/decryption functions */ /** @@ -135,7 +89,7 @@ int64_t pv_gcm_decrypt(GBytes *cipher, GBytes *aad, GBytes *tag, const PvCipherP * @derived_key_len: size of the output key * @key: input key * @salt: salt for the extraction - * @info: infor for the expansion + * @info: info for the expansion * @md: EVP mode of operation * @error: return location for a #GError * @@ -147,54 +101,15 @@ int64_t pv_gcm_decrypt(GBytes *cipher, GBytes *aad, GBytes *tag, const PvCipherP GBytes *pv_hkdf_extract_and_expand(size_t derived_key_len, GBytes *key, GBytes *salt, GBytes *info, const EVP_MD *md, GError **error) PV_NONNULL(2, 3, 4, 5); -/** pv_generate_ec_key: - * - * @nid: Numerical identifier of the curve - * @error: return location for a #GError - * - * Returns: (nullable) (transfer full): new random key based on the given curve - */ -EVP_PKEY *pv_generate_ec_key(int nid, GError **error); - -/** pv_evp_pkey_to_ecdh_pub_key: - * - * @key: input key in EVP_PKEY format - * @error: return location for a #GError - * - * Returns: the public part of the input @key in ECDH format. - */ -PvEcdhPubKey *pv_evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **error) PV_NONNULL(1); - -/** pv_derive_exchange_key: - * @cust: Customer Key - * @host: Host key - * @error: return location for a #GError - * - * Returns: (nullable) (transfer full): Shared Secret of @cust and @host - */ -GBytes *pv_derive_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **error) PV_NONNULL(1, 2); - GQuark pv_crypto_error_quark(void); #define PV_CRYPTO_ERROR pv_crypto_error_quark() typedef enum { - PV_CRYPTO_ERROR_DERIVE, PV_CRYPTO_ERROR_HKDF_FAIL, PV_CRYPTO_ERROR_INTERNAL, - PV_CRYPTO_ERROR_INVALID_KEY_SIZE, - PV_CRYPTO_ERROR_KEYGENERATION, - PV_CRYPTO_ERROR_RANDOMIZATION, - PV_CRYPTO_ERROR_READ_FILE, PV_CRYPTO_ERROR_NO_MATCH_TAG, } PvCryptoErrors; -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_INTEGER, ASN1_INTEGER_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_OCTET_STRING, ASN1_OCTET_STRING_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIGNUM, BN_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BN_CTX, BN_CTX_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EC_GROUP, EC_GROUP_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EC_KEY, EC_KEY_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EC_POINT, EC_POINT_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_CIPHER_CTX, EVP_CIPHER_CTX_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY, EVP_PKEY_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY_CTX, EVP_PKEY_CTX_free) diff --git a/include/libpv/curl.h b/include/libpv/curl.h deleted file mode 100644 index f6089221..00000000 --- a/include/libpv/curl.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Libcurl utils - * - * Copyright IBM Corp. 2022 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ -#ifndef LIBPV_CURL_H -#define LIBPV_CURL_H - -#include - -#include "libpv/common.h" - -#define CRL_DOWNLOAD_TIMEOUT_MS 3000 -#define CRL_DOWNLOAD_MAX_SIZE 0x100000 - -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURL, curl_easy_cleanup) - -/** curl_download: - * @url: URL to specify location of data - * @timeout_ms: time to wait until fail - * @max_size: Maximum size of the downloaded data - * @error: return location for a GError - * - * Returns: (nullable) (transfer full): Downloaded data as #GByteArray - */ -GByteArray *curl_download(const char *url, long timeout_ms, uint max_size, GError **err); - -/** pv_curl_init: - * - * Should not be called by user. - * Use pv_init() instead which - * calls this function during creation. - */ -int pv_curl_init(void); - -/** pv_curl_cleanup: - * - * Should not be called by user. - * Use pv_cleanup() instead which - * calls this function during creation. - */ -void pv_curl_cleanup(void); - -#define PV_CURL_ERROR g_quark_from_static_string("pv-curl-error-quark") -typedef enum { - PV_CURL_ERROR_CURL_INIT_FAILED, - PV_CURL_ERROR_DOWNLOAD_FAILED, -} PvCurlErrors; - -#endif /* LIBPV_CURL_H */ diff --git a/include/libpv/glib-helper.h b/include/libpv/glib-helper.h index 9f90b284..6dfc3a2b 100644 --- a/include/libpv/glib-helper.h +++ b/include/libpv/glib-helper.h @@ -28,8 +28,6 @@ #include #include -#include "libpv/macros.h" - #ifdef __clang__ #define WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(...) \ DO_PRAGMA(clang diagnostic push) \ @@ -40,6 +38,8 @@ #define WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(...) G_DEFINE_AUTOPTR_CLEANUP_FUNC(__VA_ARGS__) #endif +#define DO_PRAGMA(x) _Pragma(#x) + #define pv_wrapped_g_assert(__expr) g_assert(__expr) /** pv_sec_gbytes_new_take: diff --git a/include/libpv/hash.h b/include/libpv/hash.h deleted file mode 100644 index 1656e2c0..00000000 --- a/include/libpv/hash.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Hashing definitions. - * - * Copyright IBM Corp. 2022 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ -#ifndef LIBPV_HASH_H -#define LIBPV_HASH_H - -#include - -#include "libpv/common.h" - -/** pv_digest_ctx_new: - * @md: mode of digest, e.g. #EVP_sha256() - * @error: return location for a #GError - * - * Returns: (nullable) (transfer full): a new #EVP_MD_CTX, or %NULL in case of an error - */ -EVP_MD_CTX *pv_digest_ctx_new(const EVP_MD *md, GError **error); - -/** pv_digest_ctx_update: - * @ctx: EVP_MD_CTX to add data - * @data: #GBytes to add to the context - * @error: return location for a #GError - * - * Adds @data to the digest context. Can be called multiple times. - * - * Returns: 0 in case of success, -1 otherwise. - */ -int pv_digest_ctx_update(EVP_MD_CTX *ctx, GBytes *data, GError **error); - -/** pv_digest_ctx_update_raw: - * @ctx: #EVP_MD_CTX to add data - * @buf: data to add to the context - * @size: size of @buf - * @error: return location for a #GError - * - * Adds @buf to the digest context. Can be called multiple times. - * - * Returns: 0 in case of success, -1 otherwise. - */ -int pv_digest_ctx_update_raw(EVP_MD_CTX *ctx, const uint8_t *buf, size_t size, GError **error); - -/** pv_digest_ctx_finalize: - * @ctx: #EVP_MD_CTX with data to digest - * @error: return location for a #GError - * - * Calculates the digest of all previously added data. Do not use @ctx afterwards. - * - * Returns: (nullable) (transfer full): Digest of all data added before as #GBytes, or NULL in case of error. - */ -GBytes *pv_digest_ctx_finalize(EVP_MD_CTX *ctx, GError **error); - -/** pv_sha256_hash: - * @buf: data for which a sha256 hash sould be calculated - * @size: size of @buf - * @error: return location for a #GError - * - * Shorthand for initializing a sha256-digest ctx, updating, and finalizing. - * - * Returns: (nullable) (transfer full): SHA256 of @buf as #GBytes, or NULL in case of error. - */ -GBytes *pv_sha256_hash(uint8_t *buf, size_t size, GError **error); - -/** pv_hmac_ctx_new: - * @key: key used for the HMAC - * @md: mode of digest, e.g. #EVP_sha512() - * @error: return location for a #GError - * - * Returns: (nullable) (transfer full): New #HMAC_CTX or NULL in case of error - */ -HMAC_CTX *pv_hmac_ctx_new(GBytes *key, const EVP_MD *md, GError **error); - -/** pv_hmac_ctx_update_raw: - * @ctx: #HMAC_CTX to add data - * @buf: data to add to the context - * @size: size of @buf - * @error: return location for a #GError - * - * Adds @buf to the HMAC context. Can be called multiple times. - * - * Returns: 0 in case of success, -1 otherwise. - */ -int pv_hmac_ctx_update_raw(HMAC_CTX *ctx, const void *data, size_t size, GError **error); - -/** pv_hmac_ctx_update: - * @ctx: #HMAC_CTX to add data - * @data: #GBytes to add to the context - * @error: return location for a #GError - * - * Adds @data to the HMAC context. Can be called multiple times. - * - * Returns: 0 in case of success, -1 otherwise. - */ - -int pv_hmac_ctx_update(HMAC_CTX *ctx, GBytes *data, GError **error); - -/** pv_hmac_ctx_finalize: - * @ctx: #HMAC_CTX with data to digest - * @error: return location for a #GError - * - * Calculates the HMAC of all previously added data. Do not use @ctx afterwards. - * - * Returns: (nullable) (transfer full): HMAC of all data added before as #GBytes, or NULL in case of error. - */ -GBytes *pv_hamc_ctx_finalize(HMAC_CTX *ctx, GError **error); - -#define PV_HASH_ERROR g_quark_from_static_string("pv-crypro-error-quark") -typedef enum { - PV_HASH_ERROR_INTERNAL, -} PvHashErrors; - -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_MD_CTX, EVP_MD_CTX_free) -WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(HMAC_CTX, HMAC_CTX_free) - -#endif /* LIBPV_HASH_H */ diff --git a/include/libpv/macros.h b/include/libpv/macros.h deleted file mode 100644 index 73c12dc8..00000000 --- a/include/libpv/macros.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Libpv common macro definitions. - * - * Copyright IBM Corp. 2022 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - * - */ -#ifndef LIBPV_MACROS_H -#define LIBPV_MACROS_H - -#include - -#define PV_NONNULL(...) -#define DO_PRAGMA(x) _Pragma(#x) - -/* Most significant bit */ -#define PV_MSB(idx) ((uint64_t)1 << (63 - (idx))) - -#endif /* LIBPV_MACROS_H */ diff --git a/include/libpv/openssl-compat.h b/include/libpv/openssl-compat.h deleted file mode 100644 index 255f08f9..00000000 --- a/include/libpv/openssl-compat.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * OpenSSL compatibility utils - * - * Copyright IBM Corp. 2021 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ -#ifndef LIBPV_OPENSSL_COMPAT_H -#define LIBPV_OPENSSL_COMPAT_H - -#include -#include -#include - -#if OPENSSL_VERSION_NUMBER >= 0x30000000L -#define pv_X509_STORE_CTX_get_current_cert(ctx) X509_STORE_CTX_get_current_cert(ctx) -#define pv_X509_STORE_CTX_get1_crls(ctx, nm) X509_STORE_CTX_get1_crls((ctx), (nm)) -#define pv_X509_STORE_set_lookup_crls(st, cb) X509_STORE_set_lookup_crls(st, cb) -#elif OPENSSL_VERSION_NUMBER >= 0x10100000L -#define pv_X509_STORE_CTX_get_current_cert(ctx) \ - X509_STORE_CTX_get_current_cert((X509_STORE_CTX *)(ctx)) -#define pv_X509_STORE_CTX_get1_crls(ctx, nm) \ - X509_STORE_CTX_get1_crls((X509_STORE_CTX *)(ctx), (X509_NAME *)(nm)) -#define pv_X509_STORE_set_lookup_crls(st, cb) \ - X509_STORE_set_lookup_crls(st, (X509_STORE_CTX_lookup_crls_fn)(cb)) -#endif - -#endif /* LIBPV_OPENSSL_COMPAT_H */ diff --git a/include/libpv/se-hdr.h b/include/libpv/se-hdr.h deleted file mode 100644 index c8d306ea..00000000 --- a/include/libpv/se-hdr.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * PV/SE header definitions - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ -#ifndef LIBPV_SE_HDR_H -#define LIBPV_SE_HDR_H - -#include "libpv/common.h" - -#include - -#include "boot/psw.h" -#include "libpv/crypto.h" -#include "libpv/macros.h" - -/* Magic number which is used to identify the file containing the PV - * header - */ -#define PV_MAGIC_NUMBER 0x49424d5365634578ULL -#define PV_VERSION_1 0x00000100U - -/* Plaintext control flags */ -/* dumping of the configuration is allowed */ -#define PV_PCF_ALLOW_DUMPING PV_MSB(34) -/* prevent Ultravisor decryption during unpack operation */ -#define PV_PCF_NO_DECRYPTION PV_MSB(35) -/* PCKMO encrypt-DEA/TDEA-key functions allowed */ -#define PV_PCF_PCKMO_DEA_TDEA PV_MSB(56) -/* PCKMO encrypt-AES-key functions allowed */ -#define PV_PCF_PCKMO_AES PV_MSB(57) -/* PCKMO encrypt-ECC-key functions allowed */ -#define PV_PCF_PCKM_ECC PV_MSB(58) - -/* maxima for the PV version 1 */ -#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE -#define PV_V1_PV_HDR_MIN_SIZE \ - (sizeof(struct pv_hdr_head) + sizeof(struct pv_hdr_encrypted) + \ - sizeof(((struct pv_hdr *)0)->tag) + 1 * sizeof(struct pv_hdr_key_slot)) -#define PV_V1_PV_HDR_MAX_SIZE (2 * PAGE_SIZE) - -#define PV_IMAGE_ENCR_KEY_SIZE 64 - -typedef struct pv_hdr_key_slot { - uint8_t digest_key[SHA256_DIGEST_LENGTH]; - uint8_t wrapped_key[32]; - uint8_t tag[16]; -} __packed PvHdrKeySlot; - -typedef struct pv_hdr_opt_item { - uint32_t otype; - uint8_t ibk[32]; - uint8_t data[]; -} __packed PvHdrOptItem; - -/* integrity protected data (by GCM tag), but non-encrypted */ -struct pv_hdr_head { - uint64_t magic; - uint32_t version; - uint32_t phs; - uint8_t iv[12]; - uint32_t res1; - uint64_t nks; - uint64_t sea; - uint64_t nep; - uint64_t pcf; - PvEcdhPubKey cust_pub_key; - uint8_t pld[SHA512_DIGEST_LENGTH]; - uint8_t ald[SHA512_DIGEST_LENGTH]; - uint8_t tld[SHA512_DIGEST_LENGTH]; -} __packed; - -/* Must not have any padding */ -struct pv_hdr_encrypted { - uint8_t cust_comm_key[32]; - uint8_t img_enc_key_1[PV_IMAGE_ENCR_KEY_SIZE / 2]; - uint8_t img_enc_key_2[PV_IMAGE_ENCR_KEY_SIZE / 2]; - struct psw_t psw; - uint64_t scf; - uint32_t noi; - uint32_t res2; -}; -G_STATIC_ASSERT(sizeof(struct pv_hdr_encrypted) == 32 + 32 + 32 + sizeof(struct psw_t) + 8 + 4 + 4); - -typedef struct pv_hdr { - struct pv_hdr_head head; - struct pv_hdr_key_slot *slots; - struct pv_hdr_encrypted *encrypted; - struct pv_hdr_opt_item **optional_items; - uint8_t tag[16]; -} PvHdr; - -#endif /* LIBPV_SE_HDR_H */ diff --git a/libpv/Makefile b/libpv/Makefile index abbf42f3..2170c5dc 100644 --- a/libpv/Makefile +++ b/libpv/Makefile @@ -9,10 +9,7 @@ GLIB2_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags glib-2.0) GLIB2_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs glib-2.0) LIBCRYPTO_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcrypto) LIBCRYPTO_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcrypto) -LIBCURL_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcurl) -LIBCURL_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcurl) -LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) $(LIBCURL_LIBS) - +LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) WARNINGS := -Wall -Wextra -Wshadow \ -Wcast-align -Wwrite-strings -Wmissing-prototypes \ -Wmissing-declarations -Wredundant-decls -Wnested-externs \ @@ -24,18 +21,15 @@ WARNINGS := -Wall -Wextra -Wshadow \ ALL_CFLAGS += -DOPENSSL_API_COMPAT=0x10101000L \ $(GLIB2_CFLAGS) \ $(LIBCRYPTO_CFLAGS) \ - $(LIBCURL_CFLAGS) \ $(WARNINGS) \ $(NULL) BUILD_TARGETS := skip-$(LIB) ifneq (${HAVE_OPENSSL},0) ifneq (${HAVE_GLIB2},0) -ifneq (${HAVE_LIBCURL},0) BUILD_TARGETS := $(LIB) endif endif -endif sources := $(wildcard *.c) objects := $(patsubst %.c,%.o,$(sources)) @@ -82,9 +76,4 @@ skip-$(LIB): "openssl-devel / libssl-dev version >= 1.1.1", \ "HAVE_OPENSSL=0", \ "-I.") - $(call check_dep, \ - "$(LIB)", \ - "curl/curl.h", \ - "libcurl-devel", \ - "HAVE_LIBCURL=0") touch $@ diff --git a/libpv/cert.c b/libpv/cert.c deleted file mode 100644 index 44355e93..00000000 --- a/libpv/cert.c +++ /dev/null @@ -1,1706 +0,0 @@ -/* - * Certificate functions and definitions. - * - * Copyright IBM Corp. 2022 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -/* Must be included before any other header */ -#include "config.h" - -#include - -#include "libpv/cert.h" -#include "libpv/crypto.h" -#include "libpv/curl.h" - -/* Used for the caching of the downloaded CRLs */ -static GHashTable *cached_crls; - -void pv_cert_init(void) -{ - if (!cached_crls) - cached_crls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, - (GDestroyNotify)X509_CRL_free); -} - -void pv_cert_cleanup(void) -{ - g_clear_pointer(&cached_crls, g_hash_table_destroy); -} - -PvX509WithPath *pv_x509_with_path_new(X509 *cert, const char *path) -{ - g_autoptr(PvX509WithPath) ret = g_new(PvX509WithPath, 1); - - g_assert(cert && path); - - if (X509_up_ref(cert) != 1) - g_abort(); - ret->cert = cert; - ret->path = g_strdup(path); - return g_steal_pointer(&ret); -} - -void pv_x509_with_path_free(PvX509WithPath *cert) -{ - if (!cert) - return; - - X509_free(cert->cert); - g_free(cert->path); - g_free(cert); -} - -PvX509Pair *pv_x509_pair_new_take(X509 **cert, STACK_OF_X509_CRL **crls) -{ - g_autoptr(PvX509Pair) ret = g_new0(PvX509Pair, 1); - - g_assert(cert); - g_assert(crls); - - ret->cert = g_steal_pointer(cert); - ret->crls = g_steal_pointer(crls); - return g_steal_pointer(&ret); -} - -void pv_x509_pair_free(PvX509Pair *pair) -{ - if (!pair) - return; - - sk_X509_CRL_pop_free(pair->crls, X509_CRL_free); - X509_free(pair->cert); - g_free(pair); -} - -void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack) -{ - if (!stack) - return; - - sk_DIST_POINT_pop_free(stack, DIST_POINT_free); -} - -void STACK_OF_X509_free(STACK_OF_X509 *stack) -{ - if (!stack) - return; - - sk_X509_pop_free(stack, X509_free); -} - -void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack) -{ - if (!stack) - return; - - sk_X509_CRL_pop_free(stack, X509_CRL_free); -} - -static gboolean certificate_uses_elliptic_curve(EVP_PKEY *key, int nid, GError **error) -{ - g_autoptr(EC_KEY) ec = NULL; - int rc; - - g_assert(key); - - if (EVP_PKEY_id(key) != EVP_PKEY_EC) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INVALID_PARM, _("No EC key found")); - return FALSE; - } - - ec = EVP_PKEY_get1_EC_KEY(key); - if (!ec) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INVALID_PARM, _("No EC key found")); - return FALSE; - } - - if (EC_KEY_check_key(ec) != 1) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INVALID_PARM, _("Invalid EC key")); - return FALSE; - } - - rc = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); - if (rc != nid) { - /* maybe the NID is unset */ - if (rc == 0) { - g_autoptr(EC_GROUP) grp = EC_GROUP_new_by_curve_name(nid); - const EC_POINT *pub = EC_KEY_get0_public_key(ec); - g_autoptr(BN_CTX) ctx = BN_CTX_new(); - - if (EC_POINT_is_on_curve(grp, pub, ctx) != 1) { - g_set_error_literal(error, PV_CERT_ERROR, - PV_CERT_ERROR_INVALID_PARM, - _("Invalid EC curve")); - return FALSE; - } - } else { - /* NID was set but doesn't match with the expected NID - */ - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INVALID_PARM, - _("Wrong NID used: '%d'"), - EC_GROUP_get_curve_name(EC_KEY_get0_group(ec))); - return FALSE; - } - } - return TRUE; -} - -EVP_PKEY *pv_x509_get_ec_pubkey(X509 *cert, int nid, GError **error) -{ - g_autoptr(EVP_PKEY) ret = NULL; - - ret = X509_get_pubkey(cert); - if (!ret) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INVALID_PARM, - _("Failed to get public key from host-key document")); - return NULL; - } - - if (!certificate_uses_elliptic_curve(ret, nid, error)) { - g_prefix_error(error, _("Host-key document does not use an elliptic EC curve")); - return NULL; - } - - return g_steal_pointer(&ret); -} - -GSList *pv_get_ec_pubkeys(PvCertWithPathList *certs_with_path, int nid, GError **error) -{ - g_autoslist(EVP_PKEY) ret = NULL; - - for (GSList *iterator = certs_with_path; iterator; iterator = iterator->next) { - const PvX509WithPath *cert_with_path = iterator->data; - g_autoptr(EVP_PKEY) host_key = NULL; - X509 *cert = cert_with_path->cert; - - host_key = pv_x509_get_ec_pubkey(cert, nid, error); - if (!host_key) - return NULL; - - ret = g_slist_append(ret, g_steal_pointer(&host_key)); - } - - return g_steal_pointer(&ret); -} - -PvCertWithPathList *pv_load_certificates(char **cert_paths, GError **error) -{ - g_autoslist(PvX509WithPath) ret = NULL; - - for (char **iterator = cert_paths; iterator != NULL && *iterator != NULL; iterator++) { - const char *cert_path = *iterator; - g_autoptr(X509) cert = NULL; - - g_assert(cert_path); - - cert = pv_load_first_cert_from_file(cert_path, error); - if (!cert) - return NULL; - - ret = g_slist_append(ret, pv_x509_with_path_new(cert, cert_path)); - } - if (!ret) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_READ_CERTIFICATE, - _("no certificates specified")); - return NULL; - } - - return g_steal_pointer(&ret); -} - -X509 *pv_load_first_cert_from_file(const char *path, GError **error) -{ - g_autoptr(BIO) bio = BIO_new_file(path, "r"); - g_autoptr(X509) cert = NULL; - - if (!bio) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_READ_CERTIFICATE, - _("unable to read certificate: '%s'"), path); - return NULL; - } - - cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); - if (cert) - return g_steal_pointer(&cert); - ERR_clear_error(); - if (pv_BIO_reset(bio) < 0) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_READ_CERTIFICATE, - _("unable to load certificate: '%s'"), path); - return NULL; - } - - /* maybe the certificate is stored in DER format */ - cert = d2i_X509_bio(bio, NULL); - if (cert) - return g_steal_pointer(&cert); - - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_READ_CERTIFICATE, - _("unable to load certificate: '%s'"), path); - return NULL; -} - -static X509_CRL *load_crl_from_bio(BIO *bio) -{ - g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL); - if (crl) - return g_steal_pointer(&crl); - ERR_clear_error(); - if (pv_BIO_reset(bio) < 0) - return NULL; - - /* maybe the CRL is stored in DER format */ - crl = d2i_X509_CRL_bio(bio, NULL); - if (crl) - return g_steal_pointer(&crl); - return NULL; -} - -/* This function reads in only the first CRL and ignores all other. This is only - * relevant for the PEM file format. - */ -X509_CRL *pv_load_first_crl_from_file(const char *path, GError **error) -{ - g_autoptr(BIO) bio = BIO_new_file(path, "r"); - g_autoptr(X509_CRL) crl = NULL; - - if (!bio) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_READ_CRL, - _("unable to read CRL: '%s'"), path); - return NULL; - } - - crl = load_crl_from_bio(bio); - if (!crl) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_READ_CRL, - _("unable to load CRL: '%s'"), path); - return NULL; - } - return g_steal_pointer(&crl); -} - -static char *pv_X509_NAME_oneline(const X509_NAME *name) -{ - g_autoptr(BIO) key_bio = BIO_new(BIO_s_mem()); - g_autofree char *ret = NULL; - char *key = NULL; - long len; - - if (X509_NAME_print_ex(key_bio, name, 0, XN_FLAG_RFC2253) == -1) { - g_autofree char *openssl_err_msg = pv_get_openssl_errors(); - - g_warning(_("Cannot receive X509-NAME from CRL: %s"), openssl_err_msg); - return NULL; - } - - len = BIO_get_mem_data(key_bio, &key); - if (len < 0) { - g_warning(_("Cannot receive X509-NAME from CRL")); - return NULL; - } - - ret = g_malloc0((size_t)len + 1); - memcpy(ret, key, (size_t)len); - return g_steal_pointer(&ret); -} - -static gboolean cache_crl(const X509_NAME *name, X509_CRL *crl) -{ - g_autofree char *key = NULL; - - g_assert(name); - - key = pv_X509_NAME_oneline(name); - if (!key) { - g_warning(_("Cannot receive X509-NAME from CRL")); - return FALSE; - } - if (X509_CRL_up_ref(crl) != 1) - g_abort(); - return g_hash_table_insert(cached_crls, g_steal_pointer(&key), crl); -} - -/* Caller is responsible for free'ing */ -static X509_CRL *lookup_crl(const X509_NAME *name) -{ - g_autoptr(X509_CRL) crl = NULL; - g_autofree char *key = NULL; - - g_assert(name); - - key = pv_X509_NAME_oneline(name); - if (!key) - return NULL; - crl = g_hash_table_lookup(cached_crls, key); - if (crl) { - if (X509_CRL_up_ref(crl) != 1) - g_abort(); - return g_steal_pointer(&crl); - } - return NULL; -} - -/* Returns empty stack if no CRL downloaded. */ -static STACK_OF_X509_CRL *crls_download_cb(const X509_STORE_CTX *ctx, const X509_NAME *nm) -{ - g_autoptr(STACK_OF_X509_CRL) crls = NULL; - g_autoptr(X509_CRL) crl = NULL; - /* must not be free'd */ - X509 *cert = NULL; - - crls = sk_X509_CRL_new_null(); - if (!crls) - g_abort(); - cert = pv_X509_STORE_CTX_get_current_cert(ctx); - if (!cert) - return g_steal_pointer(&crls); - g_assert(X509_NAME_cmp(X509_get_issuer_name(cert), nm) == 0); - crl = lookup_crl(nm); - if (!crl) { - /* ignore error */ - crl = pv_load_first_crl_by_cert(cert, NULL); - if (!crl) - return g_steal_pointer(&crls); - g_assert_true(cache_crl(nm, crl)); - } - if (sk_X509_CRL_push(crls, g_steal_pointer(&crl)) == 0) - g_abort(); - return g_steal_pointer(&crls); -} - -/* Downloaded CRLs have a higher precedence than the CRLs specified on the - * command line. - */ -static STACK_OF_X509_CRL *crls_cb(const X509_STORE_CTX *ctx, const X509_NAME *nm) -{ - g_autoptr(STACK_OF_X509_CRL) crls = crls_download_cb(ctx, nm); - - if (sk_X509_CRL_num(crls) > 0) - return g_steal_pointer(&crls); - return pv_X509_STORE_CTX_get1_crls(ctx, nm); -} - -/* Set up CRL lookup with download support */ -void pv_store_setup_crl_download(X509_STORE *st) -{ - pv_X509_STORE_set_lookup_crls(st, crls_cb); -} - -static X509_CRL *GByteArray_to_X509_CRL(const GByteArray *data) -{ - g_autoptr(X509_CRL) ret = NULL; - g_autoptr(BIO) bio = NULL; - - g_assert(data); - - if (data->len > INT_MAX) - return NULL; - - bio = BIO_new_mem_buf(data->data, (int)data->len); - if (!bio) - g_abort(); - - ret = load_crl_from_bio(bio); - if (!ret) - return NULL; - - return g_steal_pointer(&ret); -} - -static int load_crl_from_web(const char *url, X509_CRL **crl, GError **error) -{ - g_autoptr(X509_CRL) tmp_crl = NULL; - g_autoptr(GByteArray) data = NULL; - g_assert(crl); - - data = curl_download(url, CRL_DOWNLOAD_TIMEOUT_MS, CRL_DOWNLOAD_MAX_SIZE, error); - if (!data) { - g_prefix_error(error, _("unable to download CRL: ")); - return -1; - } - tmp_crl = GByteArray_to_X509_CRL(data); - if (!tmp_crl) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_CRL_DOWNLOAD_FAILED, - _("unable to load CRL from '%s'"), url); - return -1; - } - *crl = g_steal_pointer(&tmp_crl); - return 0; -} - -/* Get the first http[s] URL from a DIST_POINT */ -static const char *get_first_dp_url(DIST_POINT *dp) -{ - GENERAL_NAMES *general_names; - - g_assert(dp); - - if (!dp->distpoint || dp->distpoint->type != 0) - return NULL; - - general_names = dp->distpoint->name.fullname; - for (int i = 0; i < sk_GENERAL_NAME_num(general_names); i++) { - GENERAL_NAME *name = sk_GENERAL_NAME_value(general_names, i); - g_autofree const char *uri_str = NULL; - ASN1_STRING *uri_asn1; - const char *uri_data; - int uri_data_len; - int type; - - uri_asn1 = GENERAL_NAME_get0_value(name, &type); - if (type != GEN_URI) - continue; - uri_data_len = ASN1_STRING_length(uri_asn1); - if (uri_data_len < 0) - continue; - uri_data = (const char *)ASN1_STRING_get0_data(uri_asn1); - /* Make sure that uri_str is null-terminated as in general it - * cannot be assumed that @uri_data is null-terminated. - */ - uri_str = g_strndup(uri_data, (size_t)uri_data_len); - if (g_str_has_prefix(uri_str, "http://")) - return uri_data; - if (g_str_has_prefix(uri_str, "https://")) - return uri_data; - } - return NULL; -} - -/* Download a CRL using the URI specified in the distribution @crldp */ -static X509_CRL *load_crl_by_dist_point(DIST_POINT *crldp, GError **error) -{ - const char *uri = get_first_dp_url(crldp); - g_autoptr(X509_CRL) crl = NULL; - - if (!uri) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL, - _("no valid URL specified in distribution point")); - return NULL; - } - - if (load_crl_from_web(uri, &crl, error) < 0) - return NULL; - - return g_steal_pointer(&crl); -} - -/* This function returns the first X509_CRL found from the CRL distribution - * points specified in @cert. This function could be optimized by filtering - * duplicate certificates and/or filtering duplicated URIs. - */ -X509_CRL *pv_load_first_crl_by_cert(X509 *cert, GError **error) -{ - g_autoptr(STACK_OF_DIST_POINT) crldps = NULL; - g_autoptr(GError) last_error = NULL; - g_autoptr(X509_CRL) ret = NULL; - int dist_points_cnt; - - g_assert(cert); - - crldps = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL); - if (!crldps || sk_DIST_POINT_num(crldps) == 0) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_CRLDP, - _("no distribution point found")); - return NULL; - } - - dist_points_cnt = sk_DIST_POINT_num(crldps); - for (int i = 0; i < dist_points_cnt; i++) { - DIST_POINT *crldp = sk_DIST_POINT_value(crldps, i); - g_assert(crldp); - - g_clear_error(&last_error); - ret = load_crl_by_dist_point(crldp, &last_error); - if (ret) - return g_steal_pointer(&ret); - } - - /* relabel error */ - if (last_error) - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_FAILED_DOWNLOAD_CRL, - "%s", last_error->message); - else - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_FAILED_DOWNLOAD_CRL, - _("failed to download CRL")); - return NULL; -} - -STACK_OF_X509_CRL *pv_try_load_crls_by_certs(GSList *certs_with_path) -{ - g_autoptr(STACK_OF_X509_CRL) ret = sk_X509_CRL_new_null(); - if (!ret) - g_abort(); - - for (GSList *iterator = certs_with_path; iterator; iterator = iterator->next) { - PvX509WithPath *cert_with_path = iterator->data; - X509 *cert = cert_with_path->cert; - g_autoptr(X509_CRL) crl = NULL; - g_assert(cert); - /* ignore error */ - crl = pv_load_first_crl_by_cert(cert, NULL); - if (!crl) - continue; - if (sk_X509_CRL_push(ret, g_steal_pointer(&crl)) == 0) - g_abort(); - } - return g_steal_pointer(&ret); -} - -#define DEFINE_GSLIST_MAP(t2, t1) \ - typedef t1 *(*g_slist_map_func_##t2##_##t1)(const t2 *x, GError **error); \ - G_GNUC_UNUSED static GSList *g_slist_map_##t2##_##t1( \ - const GSList *list, g_slist_map_func_##t2##_##t1 func, GError **error) \ - { \ - g_autoslist(t1) ret = NULL; \ - for (const GSList *iterator = list; iterator; iterator = iterator->next) { \ - const t2 *value = iterator->data; \ - t1 *new_value = NULL; \ - g_assert(value); \ - new_value = func(value, error); \ - if (!new_value) \ - return NULL; \ - ret = g_slist_append(ret, g_steal_pointer(&new_value)); \ - } \ - return g_steal_pointer(&ret); \ - } - -#define DEFINE_GSLIST_TO_STACK(t1) \ - G_GNUC_UNUSED static STACK_OF(t1) * g_slist_to_stack_of_##t1(GSList **list) \ - { \ - g_assert(list); \ - g_autoptr(STACK_OF_##t1) ret = sk_##t1##_new_null(); \ - if (!ret) \ - g_abort(); \ - for (GSList *iterator = *list; iterator; iterator = iterator->next) { \ - if (sk_##t1##_push(ret, g_steal_pointer(&iterator->data)) == 0) \ - g_abort(); \ - } \ - g_clear_pointer(list, g_slist_free); \ - return g_steal_pointer(&ret); \ - } - -DEFINE_GSLIST_MAP(PvX509WithPath, X509) -DEFINE_GSLIST_TO_STACK(X509) - -static X509 *pv_x509_with_path_get_cert(const PvX509WithPath *cert_with_path, - G_GNUC_UNUSED GError **error) -{ - g_autoptr(X509) cert = NULL; - - g_assert(cert_with_path && cert_with_path->cert); - - cert = cert_with_path->cert; - if (X509_up_ref(cert) != 1) - g_abort(); - return g_steal_pointer(&cert); -} - -/* @crl_paths is allowed to be NULL */ -static int load_crls_to_store(X509_STORE *store, char **crl_paths, gboolean err_out_empty_crls, - GError **error) -{ - for (char **iterator = crl_paths; iterator != NULL && *iterator != NULL; iterator++) { - X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); - const char *crl_path = *iterator; - int count; - - g_assert(crl_path); - - if (!lookup) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL, - _("X509 store initialization failed")); - return -1; - } - - /* support *.pem files containing multiple CRLs */ - count = X509_load_crl_file(lookup, crl_path, X509_FILETYPE_PEM); - if (count > 0) - continue; - - count = X509_load_crl_file(lookup, crl_path, X509_FILETYPE_ASN1); - if (count == 1) - continue; - - if (err_out_empty_crls) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_LOAD_CRL, - _("unable to load CRL from: '%s'"), crl_path); - return -1; - } - } - - return 0; -} - -X509_STORE *pv_store_setup(char *root_ca_path, char **crl_paths, char **cert_with_crl_paths, - GError **error) -{ - g_autoptr(X509_STORE) store = X509_STORE_new(); - if (!store) - g_abort(); - - /* if @root_ca_path != NULL use the specified root CA only, otherwise use the - * default root CAs found on the system - */ - if (root_ca_path) { - X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); - int count; - - if (!lookup) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL, - _("X509 store initialization failed")); - return NULL; - } - - /* only the PEM format allows embedded CRLs so we've to - * check for it only here and not in case of ASN1 - */ - count = X509_load_cert_file(lookup, root_ca_path, X509_FILETYPE_PEM); - if (count > 0) { - /* Out of security reasons that it can be easily - * overseen that there are multiple certificates located - * in a PEM-file we raise an error - */ - if (count > 1) { - g_set_error( - error, PV_CERT_ERROR, PV_CERT_ERROR_LOAD_ROOT_CA, - _("multiple certificates in one PEM file is not supported: '%s'"), - root_ca_path); - return NULL; - } - - /* PEM format so it's possible there are CRLs embedded - */ - (void)X509_load_crl_file(lookup, root_ca_path, X509_FILETYPE_PEM); - } else { - /* Maybe the root CA is stored in ASN1 format */ - count = X509_load_cert_file(lookup, root_ca_path, X509_FILETYPE_ASN1); - if (count != 1) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_LOAD_ROOT_CA, - _("failed to load root certificate from '%s'"), - root_ca_path); - return NULL; - } - } - } else { - /* Load certificates into @store from the hardcoded OpenSSL - * default paths - */ - if (X509_STORE_set_default_paths(store) != 1) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_LOAD_DEFAULT_CA, - _("failed to load system root certificates")); - return NULL; - } - } - - /* Error out if a CRL file was provided that has not at least one CRL*/ - if (load_crls_to_store(store, crl_paths, TRUE, error) < 0) - return NULL; - - /* Try to load CRLs from the provided untrusted certificates */ - if (load_crls_to_store(store, cert_with_crl_paths, FALSE, error) < 0) - return NULL; - - return g_steal_pointer(&store); -} - -STACK_OF_X509 *pv_get_x509_stack(const GSList *x509_with_path_list) -{ - g_autoslist(X509) certs = NULL; - g_autoptr(GError) error = NULL; - - certs = g_slist_map_PvX509WithPath_X509(x509_with_path_list, pv_x509_with_path_get_cert, - &error); - g_assert_no_error(error); - return g_slist_to_stack_of_X509(&certs); -} - -int pv_init_store_ctx(X509_STORE_CTX *ctx, X509_STORE *trusted, STACK_OF_X509 *chain, - GError **error) -{ - pv_wrapped_g_assert(ctx); - pv_wrapped_g_assert(trusted); - pv_wrapped_g_assert(chain); - - if (X509_STORE_CTX_init(ctx, trusted, NULL, chain) != 1) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL, - _("X509 store initialization failed: %s"), - X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx))); - return -1; - } - return 0; -} - -X509_STORE_CTX *pv_create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain, GError **error) -{ - g_autoptr(X509_STORE_CTX) ctx = X509_STORE_CTX_new(); - - pv_wrapped_g_assert(trusted); - pv_wrapped_g_assert(chain); - - if (!ctx) - return NULL; - - if (pv_init_store_ctx(ctx, trusted, chain, error) < 0) - return NULL; - - return g_steal_pointer(&ctx); -} - -int pv_store_set_verify_param(X509_STORE *store, GError **error) -{ - g_autoptr(X509_VERIFY_PARAM) param = NULL; - unsigned long flags = X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL | - X509_V_FLAG_TRUSTED_FIRST | X509_V_FLAG_CHECK_SS_SIGNATURE | - X509_V_FLAG_X509_STRICT | X509_V_FLAG_POLICY_CHECK; - - /* Create a X509_VERIFY_PARAM structure, which specifies which checks - * should be done by the certificate verification operation - */ - param = X509_VERIFY_PARAM_new(); - if (!param) - g_abort(); - - /* The maximum depth level of the chain of trust for the verification of - * the IBM Z signing key is 2, i.e. IBM Z signing key -> intermediate CA - * -> root CA - */ - X509_VERIFY_PARAM_set_depth(param, 2); - - /* Set minimum allowed security level to at least 112 bits. */ - X509_VERIFY_PARAM_set_auth_level(param, PV_CERTS_SECURITY_LEVEL); - - /* Set verification purpose to 'Any Purpose' and specify that the - * associated trust setting of the default purpose should be used. - */ - if (X509_VERIFY_PARAM_set_purpose(param, X509_PURPOSE_ANY | X509_TRUST_DEFAULT) != 1) - goto error; - - /* Each certificate from the chain of trust must be checked against a - * CRL to see if it has been revoked. In addition, use trusted - * certificates first mode, check signature of the last certificate, - * strict mode, and verify the policies. - */ - if (X509_VERIFY_PARAM_set_flags(param, flags) != 1) - goto error; - - if (X509_STORE_set1_param(store, param) != 1) - goto error; - - return 0; - -error: - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL, - _("X509 store initialization failed")); - return -1; -} - -static int x509_name_entry_get0_data(X509_NAME_ENTRY *entry, const uint8_t **data, size_t *data_len) -{ - const ASN1_STRING *asn1_str; - int tmp_data_len; - - g_assert(data); - g_assert(data_len); - - asn1_str = X509_NAME_ENTRY_get_data(entry); - if (!asn1_str) - return -1; - - tmp_data_len = ASN1_STRING_length(asn1_str); - if (tmp_data_len < 0) - return -1; - - *data = ASN1_STRING_get0_data(asn1_str); - *data_len = (size_t)tmp_data_len; - return 0; -} - -/* The caller must not free *data! */ -static int x509_name_get0_data_by_NID(X509_NAME *name, int nid, const uint8_t **data, - size_t *data_len) -{ - X509_NAME_ENTRY *entry = NULL; - int lastpos = -1; - - lastpos = X509_NAME_get_index_by_NID(name, nid, lastpos); - if (lastpos == -1) - return -1; - - entry = X509_NAME_get_entry(name, lastpos); - if (!entry) - return -1; - - if (x509_name_entry_get0_data(entry, data, data_len) < 0) - return -1; - - return 0; -} - -/* @y must be a NULL-terminated string */ -static gboolean x509_name_data_by_nid_equal(X509_NAME *name, int nid, const char *y) -{ - const uint8_t *data = NULL; - size_t y_len = strlen(y); - size_t data_len; - - if (x509_name_get0_data_by_NID(name, nid, &data, &data_len) < 0) - return FALSE; - - if (data_len != y_len) - return FALSE; - - return memcmp(data, y, data_len) == 0; -} - -/* Checks whether the subject of @cert is a IBM signing key subject. For this we - * must check that the subject is equal to: 'C = US, ST = New York, L = - * Poughkeepsie or Armonk, O = International Business Machines Corporation, CN = - * International Business Machines Corporation' and the organization unit (OUT) - * must end with the suffix ' Key Signing Service'. - */ -static gboolean has_ibm_signing_subject(X509 *cert) -{ - X509_NAME *subject = X509_get_subject_name(cert); - /* X509_NAME_entry_count is safe to be used with NULL */ - int entry_count = X509_NAME_entry_count(subject); - g_autofree char *data_str = NULL; - const uint8_t *data; - size_t data_len; - - if (entry_count != PV_IMB_Z_SUBJECT_ENTRY_COUNT) - return FALSE; - - if (!x509_name_data_by_nid_equal(subject, NID_countryName, PV_IBM_Z_SUBJECT_COUNTRY_NAME)) - return FALSE; - - if (!x509_name_data_by_nid_equal(subject, NID_stateOrProvinceName, PV_IBM_Z_SUBJECT_STATE)) - return FALSE; - - if (!(x509_name_data_by_nid_equal(subject, NID_localityName, - PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE) || - x509_name_data_by_nid_equal(subject, NID_localityName, - PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK))) - return FALSE; - - if (!x509_name_data_by_nid_equal(subject, NID_organizationName, - PV_IBM_Z_SUBJECT_ORGANIZATION_NAME)) - return FALSE; - - if (!x509_name_data_by_nid_equal(subject, NID_commonName, PV_IBM_Z_SUBJECT_COMMON_NAME)) - return FALSE; - - if (x509_name_get0_data_by_NID(subject, NID_organizationalUnitName, &data, &data_len) < 0) - return FALSE; - - /* Make sure that data_str is null-terminated as in general it cannot be - * assumed that @data is null-terminated. - */ - data_str = g_strndup((const char *)data, data_len); - if (!g_str_has_suffix(data_str, PV_IBM_Z_SUBJECT_ORGANIZATIONAL_UNIT_NAME_SUFFIX)) - return FALSE; - - return TRUE; -} - -/* Return a list of all IBM Z signing key certificates in @certs and remove them - * from the chain. Return empty stack if no IBM Z signing key is found. - */ -STACK_OF_X509 *pv_remove_ibm_signing_certs(STACK_OF_X509 *certs) -{ - g_autoptr(STACK_OF_X509) ret = sk_X509_new_null(); - - for (int i = 0; i < sk_X509_num(certs); i++) { - X509 *cert = sk_X509_value(certs, i); - - g_assert(cert); - - if (!has_ibm_signing_subject(cert)) - continue; - - /* Remove this certificate from the list and change i-- as the - * array has changed - this is not beautiful, but right now the - * easiest solution I came up with. - */ - if (sk_X509_delete(certs, i--) != cert) - g_abort(); - - if (sk_X509_push(ret, g_steal_pointer(&cert)) == 0) - g_abort(); - } - - return g_steal_pointer(&ret); -} - -static X509_NAME *x509_name_reorder_attributes(const X509_NAME *name, const int nids[], - size_t nids_len) -{ - int entry_count = X509_NAME_entry_count(name); - g_autoptr(X509_NAME) ret = NULL; - - if (entry_count < 0) - return NULL; - - if (nids_len != (size_t)entry_count) - return NULL; - - ret = X509_NAME_new(); - if (!ret) - g_abort(); - - for (size_t i = 0; i < nids_len; i++) { - const X509_NAME_ENTRY *entry = NULL; - int nid = nids[i]; - int lastpos = -1; - - lastpos = X509_NAME_get_index_by_NID((X509_NAME *)name, nid, lastpos); - if (lastpos == -1) - return NULL; - - entry = X509_NAME_get_entry(name, lastpos); - if (!entry) - return NULL; - - if (X509_NAME_add_entry(ret, entry, -1, 0) != 1) - return NULL; - } - return g_steal_pointer(&ret); -} - -X509_NAME *pv_c2b_name(const X509_NAME *name) -{ - int nids[] = { NID_countryName, NID_organizationName, NID_organizationalUnitName, - NID_localityName, NID_stateOrProvinceName, NID_commonName }; - g_autoptr(X509_NAME) broken_name = NULL; - - g_assert(name); - - /* Try to reorder the attributes */ - broken_name = x509_name_reorder_attributes(name, nids, G_N_ELEMENTS(nids)); - if (broken_name) - return g_steal_pointer(&broken_name); - return X509_NAME_dup((X509_NAME *)name); -} - -static int security_level_to_bits(int level) -{ - static int security_bits[] = { 0, 80, 112, 128, 192, 256 }; - - g_assert(level > 0 && level < (int)G_N_ELEMENTS(security_bits)); - - return security_bits[level]; -} - -/* returns - * 0 when the certificate is valid, - * -1 when not yet valid, - * 1 when expired - */ -static int check_validity_period(const ASN1_TIME *not_before, const ASN1_TIME *not_after) -{ - if (X509_cmp_current_time(not_before) != -1) - return -1; - - if (X509_cmp_current_time(not_after) != 1) - return 1; - - return 0; -} - -static gboolean own_X509_NAME_ENTRY_equal(const X509_NAME_ENTRY *x, const X509_NAME_ENTRY *y) -{ - const ASN1_OBJECT *x_obj = X509_NAME_ENTRY_get_object(x); - const ASN1_STRING *x_data = X509_NAME_ENTRY_get_data(x); - const ASN1_OBJECT *y_obj = X509_NAME_ENTRY_get_object(y); - const ASN1_STRING *y_data = X509_NAME_ENTRY_get_data(y); - int x_len = ASN1_STRING_length(x_data); - int y_len = ASN1_STRING_length(y_data); - - if (x_len < 0 || x_len != y_len) - return FALSE; - - /* ASN1_STRING_cmp(x_data, y_data) == 0 doesn't work because it also - * compares the type, which is sometimes different. - */ - return OBJ_cmp(x_obj, y_obj) == 0 && - memcmp(ASN1_STRING_get0_data(x_data), ASN1_STRING_get0_data(y_data), - (unsigned long)x_len) == 0; -} - -static gboolean own_X509_NAME_equal(const X509_NAME *x, const X509_NAME *y) -{ - int x_count = X509_NAME_entry_count(x); - int y_count = X509_NAME_entry_count(y); - - if (x != y && (!x || !y)) - return FALSE; - - if (x_count != y_count) - return FALSE; - - for (int i = 0; i < x_count; i++) { - const X509_NAME_ENTRY *entry_i = X509_NAME_get_entry(x, i); - gboolean entry_found = FALSE; - - for (int j = 0; j < y_count; j++) { - const X509_NAME_ENTRY *entry_j = X509_NAME_get_entry(y, j); - - if (own_X509_NAME_ENTRY_equal(entry_i, entry_j)) { - entry_found = TRUE; - break; - } - } - - if (!entry_found) - return FALSE; - } - return TRUE; -} - -/* Verify that the used public key algorithm matches the subject signature - * algorithm - */ -static int check_signature_algo_match(const EVP_PKEY *pkey, const X509 *subject, GError **error) -{ - int pkey_nid; - - if (!pkey) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_PUBLIC_KEY, _("no public key")); - return -1; - } - - if (OBJ_find_sigid_algs(X509_get_signature_nid(subject), NULL, &pkey_nid) != 1) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INVALID_SIGNATURE_ALGORITHM, - _("unsupported signature algorithm")); - return -1; - } - - if (EVP_PKEY_type(pkey_nid) != EVP_PKEY_base_id(pkey)) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_SIGNATURE_ALGORITHM_MISMATCH, - _("signature algorithm mismatch")); - return -1; - } - - return 0; -} - -/* It's almost the same as X509_check_issed from OpenSSL does except that we - * don't check the key usage of the potential issuer. This means we check: - * 1. Check whether the akid(cert) (if available) matches the issuer skid - * 2. Check that the cert algrithm matches the subject algorithm - * 3. Verify the signature of certificate @cert is using the public key of - * @issuer. - */ -static int check_host_key_issued(X509 *cert, X509 *issuer, GError **error) -{ - const X509_NAME *issuer_subject = X509_get_subject_name(issuer); - const X509_NAME *cert_issuer = X509_get_issuer_name(cert); - g_autoptr(AUTHORITY_KEYID) akid = NULL; - - akid = X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL); - if (akid && X509_check_akid(issuer, akid) != X509_V_OK) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_SKID_AKID_MISMATCH, - _("AKID mismatch")); - return -1; - } - - if (check_signature_algo_match(X509_get0_pubkey(issuer), cert, error) < 0) - return -1; - - if (X509_verify(cert, X509_get0_pubkey(issuer)) != 1) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_CERT_SIGNATURE_INVALID, - _("Signature verification failed")); - return -1; - } - - return 0; -} - -static gboolean is_cert_revoked(X509 *cert, X509_CRL *crl) -{ - X509_REVOKED *revoked = NULL; - int rc; - - if (!cert || !crl) - g_abort(); - - rc = X509_CRL_get0_by_serial(crl, &revoked, (ASN1_INTEGER *)X509_get0_serialNumber(cert)); - if (rc == 0) - return FALSE; - - if (revoked) - return TRUE; - - return FALSE; -} - -/* Assumptions are that the issuer_crt and issuer_crl is a trusted IBM Z - * signing certificate/revocation list. This function verifies a host-key - * document. To do so multiple steps are required: - * - * 1. issuer(host_key) == subject(issuer_crt) - * 2. Signature verification - * 3. @host_key must not be expired - * 4. @host_key must not be revoked - */ -int pv_verify_host_key(X509 *host_key, GSList *issuer_pairs, int verify_flags, int level, - GError **error) -{ - const int exp_security_bits = security_level_to_bits(level); - EVP_PKEY *pkey; - gboolean successfully_checked = FALSE; - int pkey_security_bits; - - g_assert(host_key); - pkey = X509_get0_pubkey(host_key); - - if (!pkey) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL, - _("failed to retrieve public key")); - return -1; - } - - /* check key level, if necessary */ - pkey_security_bits = EVP_PKEY_security_bits(pkey); - if (exp_security_bits > 0 && pkey_security_bits < exp_security_bits) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_VERIFICATION_FAILED, - _("not enough bits of security (%d, %d expected)"), pkey_security_bits, - exp_security_bits); - return -1; - } - - if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) { - const ASN1_TIME *last = X509_get0_notBefore(host_key); - const ASN1_TIME *next = X509_get0_notAfter(host_key); - - if (!last || !next || check_validity_period(last, next)) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INVALID_VALIDITY_PERIOD, - _("validity period is not valid")); - return -1; - } - } else { - verify_flags &= ~X509_V_FLAG_NO_CHECK_TIME; - } - - /* Verify that the host_key was issued by a certificate and that it - * wasn't revoked. - */ - for (GSList *iterator = issuer_pairs; iterator; iterator = iterator->next) { - const PvX509Pair *pair = iterator->data; - STACK_OF_X509_CRL *issuer_crls = NULL; - X509 *issuer_cert = NULL; - - g_assert(pair); - - issuer_cert = pair->cert; - issuer_crls = pair->crls; - - g_assert(issuer_cert); - - /* Verify that the issuer(host_key) == subject(issuer_cert) and - * that the signature is valid - */ - if (check_host_key_issued(host_key, issuer_cert, NULL) < 0) - continue; - - /* Check against CRL */ - if (verify_flags & X509_V_FLAG_CRL_CHECK) { - gboolean crl_checked = FALSE; - - verify_flags &= ~X509_V_FLAG_CRL_CHECK; - for (int i = 0; i < sk_X509_CRL_num(issuer_crls); i++) { - X509_CRL *issuer_crl = sk_X509_CRL_value(issuer_crls, i); - - g_assert(issuer_crl); - - if (is_cert_revoked(host_key, issuer_crl)) { - g_set_error(error, PV_CERT_ERROR, - PV_CERT_ERROR_CERT_REVOKED, - _("certificate revoked")); - return -1; - } - - crl_checked = TRUE; - } - - if (!crl_checked) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_CRL, - _("no valid CRL found")); - return -1; - } - successfully_checked = TRUE; - break; - } - } - - if (!successfully_checked) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_ISSUER_IBM_Z_FOUND, - _("no IBM Z signing key that issued this host-key document found")); - return -1; - } - - /* were some unsupported flags specified? */ - g_assert(verify_flags == 0); - return 0; -} - -int pv_verify_cert(X509_STORE_CTX *ctx, X509 *cert, GError **error) -{ - int rc; - - pv_wrapped_g_assert(cert); - pv_wrapped_g_assert(ctx); - - X509_STORE_CTX_set_cert(ctx, cert); - rc = X509_verify_cert(ctx); - if (rc != 1) { - X509 *tmp_cert = NULL; - - tmp_cert = pv_X509_STORE_CTX_get_current_cert(ctx); - if (tmp_cert) { - g_autofree char *subj_name = - pv_X509_NAME_oneline(X509_get_subject_name(tmp_cert)); - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_VERIFICATION_FAILED, - _("failed to verify certificate '%s': %s"), subj_name, - X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx))); - } else { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_VERIFICATION_FAILED, - _("failed to verify certificate: %s"), - X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx))); - } - return -1; - } - return 0; -} - -/* Verify that SKID(issuer) == AKID(crl) */ -static int check_crl_issuer(X509_CRL *crl, X509 *issuer, GError **error) -{ - g_autoptr(AUTHORITY_KEYID) akid = NULL; - - /* If AKID(@crl) is specified it must match with SKID(@issuer) */ - akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, NULL, NULL); - if (akid && X509_check_akid(issuer, akid) != X509_V_OK) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_SKID_AKID_MISMATCH, - _("AKID mismatch")); - return -1; - } - - return 0; -} - -int pv_verify_crl(X509_CRL *crl, X509 *cert, int verify_flags, GError **error) -{ - EVP_PKEY *pkey = X509_get0_pubkey(cert); - - g_assert(crl); - - if (!pkey) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL, - _("failed to retrieve public key from the certificate")); - return -1; - } - - if (check_crl_issuer(crl, cert, error) < 0) - return -1; - - /* verify the validity period of the CRL */ - if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) { - const ASN1_TIME *last = X509_CRL_get0_lastUpdate(crl); - const ASN1_TIME *next = X509_CRL_get0_nextUpdate(crl); - - if (!last || !next || check_validity_period(last, next)) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INVALID_VALIDITY_PERIOD, - _("validity period is not valid")); - return -1; - } - } else { - verify_flags &= ~X509_V_FLAG_NO_CHECK_TIME; - } - - /* verify the signature */ - if (X509_CRL_verify(crl, pkey) != 1) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_CRL_SIGNATURE_INVALID, - _("signature is not valid")); - return -1; - } - g_assert(verify_flags == 0); - return 0; -} - -int pv_check_chain_parameters(const STACK_OF_X509 *chain, GError **error) -{ - const X509_NAME *ca_x509_subject = NULL; - g_autofree char *ca_subject = NULL; - int len = sk_X509_num(chain); - X509 *ca = NULL; - - if (len < 2) { - g_set_error( - error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL, - _("there must be at least one root and one leaf certificate in the chain of trust")); - return -1; - } - - /* get the root certificate of the chain of trust */ - ca = sk_X509_value(chain, len - 1); - if (!ca) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL, - _("no root certificate found")); - return -1; - } - - ca_x509_subject = X509_get_subject_name(ca); - if (!ca_x509_subject) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL, - _("subject of the root CA cannot be retrieved")); - return -1; - } - - ca_subject = pv_X509_NAME_oneline(ca_x509_subject); - if (!ca_subject) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL, - _("subject name of the root CA cannot be retrieved")); - return -1; - } - g_info(_("Root CA used: '%s'"), ca_subject); - - return 0; -} - -/** Replace locality 'Armonk' with 'Pougkeepsie'. If Armonk was not set return - * `NULL`. - */ -static X509_NAME *x509_armonk_locality_fixup(const X509_NAME *name) -{ - g_autoptr(X509_NAME) ret = NULL; - int pos; - - /* Check if ``L=Armonk`` */ - if (!x509_name_data_by_nid_equal((X509_NAME *)name, NID_localityName, - PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK)) - return NULL; - - ret = X509_NAME_dup((X509_NAME *)name); - if (!ret) - g_abort(); - - pos = X509_NAME_get_index_by_NID(ret, NID_localityName, -1); - if (pos == -1) - return NULL; - - X509_NAME_ENTRY_free(X509_NAME_delete_entry(ret, pos)); - - /* Create a new name entry at the same position as before */ - if (X509_NAME_add_entry_by_NID( - ret, NID_localityName, MBSTRING_UTF8, - (const unsigned char *)&PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE, - sizeof(PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE) - 1, pos, 0) != 1) - return NULL; - - return g_steal_pointer(&ret); -} - -/* This function contains work-arounds for some known subject(CRT)<->issuer(CRL) - * issues. - */ -static STACK_OF_X509_CRL *quirk_X509_STORE_ctx_get1_crls(X509_STORE_CTX *ctx, - const X509_NAME *subject, GError **err) -{ - g_autoptr(X509_NAME) fixed_subject = NULL; - g_autoptr(STACK_OF_X509_CRL) ret = NULL; - - ret = pv_X509_STORE_CTX_get1_crls(ctx, subject); - if (ret && sk_X509_CRL_num(ret) > 0) - return g_steal_pointer(&ret); - - /* Workaround to fix the mismatch between issuer name of the * IBM - * signing CRLs and the IBM signing key subject name. Locality name has - * changed from Poughkeepsie to Armonk. - */ - fixed_subject = x509_armonk_locality_fixup(subject); - /* Was the locality replaced? */ - if (fixed_subject) { - X509_NAME *tmp; - - sk_X509_CRL_free(ret); - ret = pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject); - if (ret && sk_X509_CRL_num(ret) > 0) - return g_steal_pointer(&ret); - - /* Workaround to fix the ordering mismatch between issuer name - * of the IBM signing CRLs and the IBM signing key subject name. - */ - tmp = fixed_subject; - fixed_subject = pv_c2b_name(fixed_subject); - X509_NAME_free(tmp); - sk_X509_CRL_free(ret); - ret = pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject); - if (ret && sk_X509_CRL_num(ret) > 0) - return g_steal_pointer(&ret); - X509_NAME_free(fixed_subject); - fixed_subject = NULL; - } - - /* Workaround to fix the ordering mismatch between issuer name of the - * IBM signing CRLs and the IBM signing key subject name. - */ - fixed_subject = pv_c2b_name(subject); - sk_X509_CRL_free(ret); - ret = pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject); - if (ret && sk_X509_CRL_num(ret) > 0) - return g_steal_pointer(&ret); - - g_set_error(err, PV_CERT_ERROR, PV_CERT_ERROR_NO_CRL, _("no CRL found")); - return NULL; -} - -/* Given a certificate @cert try to find valid revocation lists in @ctx. If no - * valid CRL was found NULL is returned. - */ -STACK_OF_X509_CRL *pv_store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert, GError **error) -{ - g_autoptr(STACK_OF_X509_CRL) ret = NULL; - const int verify_flags = 0; - X509_NAME *subject = NULL; - - pv_wrapped_g_assert(ctx); - pv_wrapped_g_assert(cert); - - subject = X509_get_subject_name(cert); - if (!subject) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_MALFORMED_CERTIFICATE, - _("certificate is malformed")); - return NULL; - } - - ret = quirk_X509_STORE_ctx_get1_crls(ctx, subject, error); - if (!ret) - return NULL; - /* Filter out non-valid CRLs for @cert */ - for (int i = 0; i < sk_X509_CRL_num(ret); i++) { - X509_CRL *crl = sk_X509_CRL_value(ret, i); - - g_assert(crl); - - /* If @crl is not valid remove it from the array and log a - * warning. - */ - if (pv_verify_crl(crl, cert, verify_flags, error) < 0) { - g_assert(error); - g_warning(_("CRL is not valid: %s"), (*error)->message); - g_clear_error(error); - - /* Remove this certificate from the list and change i-- as the - * array has changed - this is not beautfiul, but right now the - * easiest solution I came up with - */ - if (sk_X509_CRL_delete(ret, i--) != crl) - g_abort(); - - g_clear_pointer(&crl, X509_CRL_free); - } - } - - if (sk_X509_CRL_num(ret) < 1) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_CRL, _("no valid CRL found")); - return NULL; - } - return g_steal_pointer(&ret); -} - -/* - * Finds the IBM signing key in the stack. - * Error out, if there is not exactly one IBM signing key. - */ -static STACK_OF_X509 *get_ibm_signing_certs(STACK_OF_X509 *certs, GError **error) -{ - g_autoptr(STACK_OF_X509) ibm_signing_certs = NULL; - int ibm_signing_certs_count; - - /* Find all IBM Z signing keys and remove them from the chain as we - * have to verify that they're valid. The last step of the chain of - * trust verification must be done manually, as the IBM Z signing keys - * are not marked as (intermediate) CA and therefore the standard - * `X509_verify_cert` function of OpenSSL cannot be used to verify the - * actual host-key documents. - */ - ibm_signing_certs = pv_remove_ibm_signing_certs(certs); - ibm_signing_certs_count = sk_X509_num(ibm_signing_certs); - if (ibm_signing_certs_count < 1) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY, - _("Specify at least one IBM Z signing key")); - return NULL; - } else if (ibm_signing_certs_count > 1) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY, - _("Specify only one IBM Z signing key")); - return NULL; - } - g_assert(ibm_signing_certs_count == 1); - - return g_steal_pointer(&ibm_signing_certs); -} - -static gboolean download_crls(X509_STORE *trusted, PvCertWithPathList *host_key_certs_with_path, - GError **error) -{ - g_autoptr(STACK_OF_X509_CRL) downloaded_ibm_signing_crls = NULL; - - /* Set up the download routine for the lookup of CRLs. */ - pv_store_setup_crl_download(trusted); - - /* Try to download the CRLs of the IBM Z signing certificates - * specified in the host-key documents. Ignore download errors - * as it's still possible that a CRL is specified via command - * line. - */ - downloaded_ibm_signing_crls = pv_try_load_crls_by_certs(host_key_certs_with_path); - - /* Add the downloaded CRLs to the store so they can be used for - * the verification later. - */ - for (int i = 0; i < sk_X509_CRL_num(downloaded_ibm_signing_crls); i++) { - X509_CRL *crl = sk_X509_CRL_value(downloaded_ibm_signing_crls, i); - - if (X509_STORE_add_crl(trusted, crl) != 1) { - g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL, - _("failed to load CRL")); - return FALSE; - } - } - return TRUE; -} - -gboolean pv_verify_host_key_doc(PvCertWithPathList *host_key_certs_with_path, X509_STORE *trusted, - STACK_OF_X509 *untrusted_certs, gboolean online, GError **error) -{ - g_autoslist(PvX509Pair) ibm_z_pairs = NULL; - g_autoptr(STACK_OF_X509) ibm_signing_certs = NULL; - g_autoptr(X509_STORE_CTX) ctx = NULL; - - pv_wrapped_g_assert(host_key_certs_with_path); - pv_wrapped_g_assert(trusted); - pv_wrapped_g_assert(untrusted_certs); - - if (online && !download_crls(trusted, host_key_certs_with_path, error)) - return -1; - - /* Find all IBM Z signing keys and remove them from the chain as we - * have to verify that they're valid. The last step of the chain of - * trust verification must be done manually, as the IBM Z signing keys - * are not marked as (intermediate) CA and therefore the standard - * `X509_verify_cert` function of OpenSSL cannot be used to verify the - * actual host-key documents. - */ - ibm_signing_certs = get_ibm_signing_certs(untrusted_certs, error); - if (!ibm_signing_certs) - return -1; - - if (pv_store_set_verify_param(trusted, error) < 0) - return -1; - - ctx = pv_create_store_ctx(trusted, untrusted_certs, error); - if (!ctx) - return -1; - /* - * Get all IBM-signing-[key,crls] pairs. - * NOTE: Currently there is only one signing-key allowed - */ - for (int i = 0; i < sk_X509_num(ibm_signing_certs); i++) { - g_autoptr(X509) ibm_signing_cert = sk_X509_pop(ibm_signing_certs); - g_autoptr(STACK_OF_X509_CRL) ibm_signing_crls = NULL; - PvX509Pair *ibm_z_pair = NULL; - - /* - * Get CRLs for the IBM signing cert - */ - ibm_signing_crls = pv_store_ctx_find_valid_crls(ctx, ibm_signing_cert, error); - if (!ibm_signing_crls) { - g_prefix_error(error, _("IBM Z signing key: ")); - return -1; - } - - /* build the pair and add it to the list */ - ibm_z_pair = pv_x509_pair_new_take(&ibm_signing_cert, &ibm_signing_crls); - g_assert(!ibm_signing_cert); - g_assert(!ibm_signing_crls); - ibm_z_pairs = g_slist_append(ibm_z_pairs, ibm_z_pair); - } - - /* Verify host-key documents by using the IBM Z signing - * certificates and the corresponding certificate revocation - * lists. - */ - for (GSList *iterator = host_key_certs_with_path; iterator; iterator = iterator->next) { - PvX509WithPath *host_key_with_path = iterator->data; - const char *host_key_path = host_key_with_path->path; - X509 *host_key = host_key_with_path->cert; - int flags = X509_V_FLAG_CRL_CHECK; - - if (pv_verify_host_key(host_key, ibm_z_pairs, flags, PV_CERTS_SECURITY_LEVEL, - error) < 0) { - g_prefix_error(error, "'%s': ", host_key_path); - return -1; - } - } - - /* Verify that all IBM Z signing keys are trustable. - * For this we must check: - * - * 1. Can a chain of trust be established ending in a root CA - * 2. Is the correct root CA used? It has either to be the - * System CA or the root CA specified via command line. - */ - for (GSList *iterator = ibm_z_pairs; iterator; iterator = iterator->next) { - const PvX509Pair *ibm_z_pair = iterator->data; - - if (pv_verify_cert(ctx, ibm_z_pair->cert, error) < 0) - return -1; - if (pv_check_chain_parameters(X509_STORE_CTX_get0_chain(ctx), error) < 0) - return -1; - /* re-init ctx for the next verification */ - X509_STORE_CTX_cleanup(ctx); - if (pv_init_store_ctx(ctx, trusted, untrusted_certs, error) != 0) - return -1; - } - return 0; -} - -int pv_verify_host_key_docs_by_path(char **host_key_paths, char *optional_root_ca_path, - char **crl_paths, char **untrusted_cert_paths, gboolean online, - GError **error) -{ - g_autoslist(PvX509WithPath) untrusted_certs_with_path = NULL, host_key_certs = NULL; - g_autoptr(STACK_OF_X509) untrusted_certs = NULL; - g_autoptr(X509_STORE) trusted = NULL; - - pv_wrapped_g_assert(host_key_paths); - pv_wrapped_g_assert(untrusted_cert_paths); - - /* Load trusted root CAs of the system if and only if @root_ca_path is - * NULL, otherwise use the root CA specified by @root_ca_path. - */ - trusted = pv_store_setup(optional_root_ca_path, crl_paths, untrusted_cert_paths, error); - if (!trusted) - return -1; - - /* Load all untrusted certificates (e.g. IBM Z signing key and - * intermediate CA) that are required to establish a chain of - * trust starting from the host-key document up to the root CA (if not - * otherwise specified that can be one of the system wide installed - * root CAs, e.g. DigiCert). - */ - untrusted_certs_with_path = pv_load_certificates(untrusted_cert_paths, error); - if (!untrusted_certs_with_path) - return -1; - /* Convert to STACK_OF(X509) */ - untrusted_certs = pv_get_x509_stack(untrusted_certs_with_path); - - host_key_certs = pv_load_certificates(host_key_paths, error); - if (!host_key_certs) - return -1; - - return pv_verify_host_key_doc(host_key_certs, trusted, untrusted_certs, online, error); -} diff --git a/libpv/common.c b/libpv/common.c deleted file mode 100644 index 547d88a0..00000000 --- a/libpv/common.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Libpv common functions. - * - * Copyright IBM Corp. 2022 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - * - */ -/* Must be included before any other header */ -#include "config.h" - -#include "libpv/common.h" -#include "libpv/cert.h" -#include "libpv/curl.h" - -/* setup and tear down */ -int pv_init(void) -{ - static size_t openssl_initalized; - - if (g_once_init_enter(&openssl_initalized)) { - if (OPENSSL_VERSION_NUMBER < 0x1000100fL) - g_assert_not_reached(); -#if OPENSSL_VERSION_NUMBER < 0x10100000L - SSL_library_init(); - SSL_load_error_strings(); -#else - OPENSSL_init_crypto(0, NULL); -#endif - - if (pv_curl_init() != 0) - return -1; - - pv_cert_init(); - g_once_init_leave(&openssl_initalized, 1); - } - return 0; -} - -void pv_cleanup(void) -{ - pv_cert_cleanup(); - pv_curl_cleanup(); -} diff --git a/libpv/crypto.c b/libpv/crypto.c index ae7f1f29..98fdd246 100644 --- a/libpv/crypto.c +++ b/libpv/crypto.c @@ -17,7 +17,6 @@ #include "lib/zt_common.h" #include "libpv/crypto.h" #include "libpv/glib-helper.h" -#include "libpv/hash.h" char *pv_get_openssl_errors(void) { @@ -46,59 +45,6 @@ int pv_BIO_reset(BIO *b) return 1; } -GBytes *pv_generate_rand_data(size_t size, GError **error) -{ - g_autofree uint8_t *data = NULL; - - if (size > INT_MAX) { - g_set_error_literal(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_RANDOMIZATION, - "Too many random data requested. Split it up"); - OPENSSL_clear_free(data, size); - return NULL; - } - - data = g_malloc(size); - if (RAND_bytes(data, (int)size) != 1) { - g_set_error_literal(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_RANDOMIZATION, - "The required amount of random data is not available"); - return NULL; - } - - return pv_sec_gbytes_new_take(g_steal_pointer(&data), size); -} - -GBytes *pv_generate_key(const EVP_CIPHER *cipher, GError **error) -{ - int size; - - pv_wrapped_g_assert(cipher); - - size = EVP_CIPHER_key_length(cipher); - if (size <= 0) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION, - "Unknown cipher"); - return NULL; - } - - return pv_generate_rand_data((guint)size, error); -} - -GBytes *pv_generate_iv(const EVP_CIPHER *cipher, GError **error) -{ - int size; - - pv_wrapped_g_assert(cipher); - - size = EVP_CIPHER_iv_length(cipher); - if (size <= 0) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION, - "Unknown cipher"); - return NULL; - } - - return pv_generate_rand_data((guint)size, error); -} - static int64_t pv_gcm_encrypt_decrypt(GBytes *input, GBytes *aad, const PvCipherParms *parms, GBytes **output, GBytes **tagp, enum PvCryptoMode mode, GError **error) @@ -360,168 +306,6 @@ GBytes *pv_hkdf_extract_and_expand(size_t derived_key_len, GBytes *key, GBytes * return pv_sec_gbytes_new_take(g_steal_pointer(&derived_key), derived_key_len); } -EVP_PKEY *pv_generate_ec_key(int nid, GError **error) -{ - g_autoptr(EVP_PKEY_CTX) ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); - g_autoptr(EVP_PKEY) ret = NULL; - - g_assert(ctx); - - if (EVP_PKEY_keygen_init(ctx) != 1) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION, - _("EC key could not be auto-generated")); - return NULL; - } - - if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) != 1) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION, - _("EC key could not be auto-generated")); - return NULL; - } - - if (EVP_PKEY_keygen(ctx, &ret) != 1) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_KEYGENERATION, - _("EC key could not be auto-generated")); - return NULL; - } - - return g_steal_pointer(&ret); -} - -/* Convert a EVP_PKEY to the key format used in the PV header */ -PvEcdhPubKey *pv_evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **error) -{ - g_autofree PvEcdhPubKey *ret = g_new0(PvEcdhPubKey, 1); - g_autoptr(BIGNUM) pub_x_big = NULL, pub_y_big = NULL; - g_autoptr(EC_KEY) ec_key = NULL; - const EC_POINT *pub_key; - const EC_GROUP *grp; - - pv_wrapped_g_assert(key); - - ec_key = EVP_PKEY_get1_EC_KEY(key); - if (!ec_key) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Key has the wrong type")); - return NULL; - } - - pub_key = EC_KEY_get0_public_key(ec_key); - if (!pub_key) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Failed to get public key")); - return NULL; - } - - grp = EC_KEY_get0_group(ec_key); - if (!grp) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Failed to get EC group")); - return NULL; - } - - pub_x_big = BN_new(); - if (!pub_x_big) - g_abort(); - - pub_y_big = BN_new(); - if (!pub_y_big) - g_abort(); - - if (EC_POINT_get_affine_coordinates_GFp(grp, pub_key, pub_x_big, pub_y_big, NULL) != 1) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Cannot convert key to internal format")); - return NULL; - } - - if (BN_bn2binpad(pub_x_big, ret->x, sizeof(ret->x)) < 0) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Cannot convert key to internal format")); - return NULL; - } - - if (BN_bn2binpad(pub_y_big, ret->y, sizeof(ret->y)) < 0) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Cannot convert key to internal format")); - return NULL; - } - - return g_steal_pointer(&ret); -} - -static GBytes *derive_key(EVP_PKEY *key1, EVP_PKEY *key2, GError **error) -{ - g_autoptr(EVP_PKEY_CTX) ctx = NULL; - uint8_t *data = NULL; - size_t data_size, key_size; - - ctx = EVP_PKEY_CTX_new(key1, NULL); - if (!ctx) - g_abort(); - - if (EVP_PKEY_derive_init(ctx) != 1) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Key derivation failed")); - return NULL; - } - - if (EVP_PKEY_derive_set_peer(ctx, key2) != 1) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, - _("Key derivation failed")); - return NULL; - } - - /* Determine buffer length */ - if (EVP_PKEY_derive(ctx, NULL, &key_size) != 1) { - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE, - _("Key derivation failed")); - return NULL; - } - - data_size = key_size; - data = OPENSSL_malloc(data_size); - if (!data) - g_abort(); - if (EVP_PKEY_derive(ctx, data, &data_size) != 1) { - OPENSSL_clear_free(data, data_size); - g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE, - _("Key derivation failed")); - return NULL; - } - - g_assert(data_size == key_size); - return pv_sec_gbytes_new_take(g_steal_pointer(&data), data_size); -} - -GBytes *pv_derive_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **error) -{ - const guint8 append[] = { 0x00, 0x00, 0x00, 0x01 }; - g_autoptr(GBytes) derived_key = NULL, ret = NULL; - g_autoptr(GByteArray) der_key_ga = NULL; - g_autofree uint8_t *raw = NULL; - size_t raw_len; - - pv_wrapped_g_assert(cust); - pv_wrapped_g_assert(host); - - derived_key = derive_key(cust, host, error); - if (!derived_key) - return NULL; - - der_key_ga = g_bytes_unref_to_array(g_steal_pointer(&derived_key)); - /* ANSI X.9.63-2011: 66 bytes x with leading 7 bits and - * concatenate 32 bit int '1' - */ - der_key_ga = g_byte_array_append(der_key_ga, append, sizeof(append)); - /* free GBytesArray and get underlying data */ - raw_len = der_key_ga->len; - raw = g_byte_array_free(g_steal_pointer(&der_key_ga), FALSE); - - ret = pv_sha256_hash(raw, raw_len, error); - OPENSSL_cleanse(raw, raw_len); - return g_steal_pointer(&ret); -} - GQuark pv_crypto_error_quark(void) { return g_quark_from_static_string("pv-crypto-error-quark"); diff --git a/libpv/curl.c b/libpv/curl.c deleted file mode 100644 index 898f9a8a..00000000 --- a/libpv/curl.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Libcurl utils - * - * Copyright IBM Corp. 2020 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ -/* Must be included before any other header */ -#include "config.h" - -#include -#include - -#include "lib/zt_common.h" -#include "libpv/curl.h" - -struct UserData { - GByteArray *buffer; - uint max_size; -}; - -static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) -{ - g_assert(userdata); - struct UserData *data = (struct UserData *)userdata; - GByteArray *buffer = data->buffer; - uint64_t actual_size; - size_t err; - - g_assert(buffer); - - if (!g_uint64_checked_mul(&actual_size, size, nmemb)) - g_abort(); - - /* Signal an error condition by returning a amount that differs - * from the amount passed to the callback. This results in a - * CURLE_WRITE_ERROR. - */ - err = actual_size + 1; - - if (actual_size > G_MAXUINT) - return err; - - data->buffer = g_byte_array_append(buffer, (uint8_t *)ptr, (uint)actual_size); - if (data->buffer->len > data->max_size) - return err; - - return actual_size; -} - -int pv_curl_init(void) -{ - if (curl_global_init(CURL_GLOBAL_ALL) != 0) - return -1; - return 0; -} - -void pv_curl_cleanup(void) -{ - curl_global_cleanup(); -} - -GByteArray *curl_download(const char *url, long timeout_ms, uint max_size, GError **err) -{ - g_autoptr(GByteArray) ret = NULL; - g_autoptr(CURL) handle = NULL; - g_autofree char *agent = NULL; - struct UserData userdata; - CURLcode rc; - - /* set up curl session */ - handle = curl_easy_init(); - if (!handle) - g_abort(); - - /* follow redirection */ - rc = curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L); - if (rc != CURLE_OK) - goto curl_err; - rc = curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, timeout_ms); - if (rc != CURLE_OK) - goto curl_err; - rc = curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L); - if (rc != CURLE_OK) - goto curl_err; - agent = g_strdup_printf("%s/%s", GETTEXT_PACKAGE, RELEASE_STRING); - rc = curl_easy_setopt(handle, CURLOPT_USERAGENT, agent); - if (rc != CURLE_OK) - goto curl_err; - rc = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback); - if (rc != CURLE_OK) - goto curl_err; - ret = g_byte_array_new(); - userdata.buffer = ret; - userdata.max_size = max_size; - rc = curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&userdata); - if (rc != CURLE_OK) - goto curl_err; - rc = curl_easy_setopt(handle, CURLOPT_URL, url); - if (rc != CURLE_OK) - goto curl_err; - - rc = curl_easy_perform(handle); - if (rc != CURLE_OK) { - g_set_error(err, PV_CURL_ERROR, PV_CURL_ERROR_DOWNLOAD_FAILED, - _("download failed: %s"), curl_easy_strerror(rc)); - return NULL; - } - - return g_steal_pointer(&ret); -curl_err: - g_set_error(err, PV_CURL_ERROR, PV_CURL_ERROR_CURL_INIT_FAILED, - _("cURL initialization failed: %s"), curl_easy_strerror(rc)); - return NULL; -} diff --git a/libpv/hash.c b/libpv/hash.c deleted file mode 100644 index f104960a..00000000 --- a/libpv/hash.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Hashing functions. - - * Copyright IBM Corp. 2022 - * - * s390-tools is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ -/* Must be included before any other header */ -#include "config.h" - -#include "libpv/crypto.h" -#include "libpv/hash.h" - -GBytes *pv_sha256_hash(uint8_t *buf, size_t size, GError **error) -{ - g_autoptr(EVP_MD_CTX) ctx = NULL; - - ctx = pv_digest_ctx_new(EVP_sha256(), error); - if (!ctx) - return NULL; - - if (pv_digest_ctx_update_raw(ctx, buf, size, error) != 0) - return NULL; - - return pv_digest_ctx_finalize(ctx, error); -} - -EVP_MD_CTX *pv_digest_ctx_new(const EVP_MD *md, GError **error) -{ - g_autoptr(EVP_MD_CTX) ctx = EVP_MD_CTX_new(); - - if (!ctx) { - g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL, - _("Hash context generation failed")); - return NULL; - } - - if (EVP_DigestInit_ex(ctx, md, NULL) != 1) { - g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL, - _("EVP_DigestInit_ex failed")); - return NULL; - } - - return g_steal_pointer(&ctx); -} - -int pv_digest_ctx_update_raw(EVP_MD_CTX *ctx, const uint8_t *buf, size_t size, GError **error) -{ - if (!buf || size == 0) - return 0; - - if (EVP_DigestUpdate(ctx, buf, size) != 1) { - g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL, - _("EVP_DigestUpdate failed")); - return -1; - } - return 0; -} - -int pv_digest_ctx_update(EVP_MD_CTX *ctx, GBytes *data, GError **error) -{ - const uint8_t *buf; - size_t buf_size; - - if (!data) - return 0; - buf = g_bytes_get_data((GBytes *)data, &buf_size); - return pv_digest_ctx_update_raw(ctx, buf, buf_size, error); -} - -GBytes *pv_digest_ctx_finalize(EVP_MD_CTX *ctx, GError **error) -{ - int md_size = EVP_MD_size(EVP_MD_CTX_md(ctx)); - g_autofree uint8_t *digest = NULL; - unsigned int digest_size; - - g_assert(md_size > 0); - - digest = g_malloc0((uint)md_size); - if (EVP_DigestFinal_ex(ctx, digest, &digest_size) != 1) { - g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL, - _("EVP_DigestFinal_ex failed")); - return NULL; - } - - g_assert(digest_size == (uint)md_size); - return g_bytes_new_take(g_steal_pointer(&digest), digest_size); -} - -HMAC_CTX *pv_hmac_ctx_new(GBytes *key, const EVP_MD *md, GError **error) -{ - g_autoptr(HMAC_CTX) ctx = HMAC_CTX_new(); - const uint8_t *key_data; - size_t key_size; - - key_data = g_bytes_get_data(key, &key_size); - - if (HMAC_Init_ex(ctx, key_data, (int)key_size, md, NULL) != 1) { - g_autofree char *openssl_err_msg = pv_get_openssl_errors(); - - g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL, - "unable to create HMAC context: %s", openssl_err_msg); - return NULL; - } - return g_steal_pointer(&ctx); -} - -int pv_hmac_ctx_update_raw(HMAC_CTX *ctx, const void *buf, size_t size, GError **error) -{ - if (!buf || size == 0) - return 0; - - if (HMAC_Update(ctx, buf, size) != 1) { - g_autofree char *openssl_err_msg = pv_get_openssl_errors(); - - g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL, - "unable to add data to HMAC context: %s", openssl_err_msg); - return -1; - } - return 0; -} - -int pv_hmac_ctx_update(HMAC_CTX *ctx, GBytes *data, GError **error) -{ - const uint8_t *buf; - size_t buf_size; - - if (!data) - return 0; - buf = g_bytes_get_data((GBytes *)data, &buf_size); - return pv_hmac_ctx_update_raw(ctx, buf, buf_size, error); -} - -GBytes *pv_hamc_ctx_finalize(HMAC_CTX *ctx, GError **error) -{ - int md_size = EVP_MD_size(HMAC_CTX_get_md(ctx)); - g_autofree uint8_t *hmac = NULL; - unsigned int hmac_size = 0; - - g_assert(md_size > 0); - - hmac = g_malloc0((unsigned int)md_size); - - if (HMAC_Final(ctx, hmac, &hmac_size) != 1) { - g_autofree char *openssl_err_msg = pv_get_openssl_errors(); - - g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL, - "unable to calculate HMAC: %s", openssl_err_msg); - return NULL; - } - return g_bytes_new_take(g_steal_pointer(&hmac), hmac_size); -}