From 1a6f340964e38658222d05be6a49f98473d226b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20B=C3=A1rta?= Date: Fri, 15 Jul 2022 09:48:08 +0200 Subject: [PATCH] Merge our current version of the code with the OSS libsecutils. (reflects securityUtilities b009f9d5) API changes: - functions for checking only against local CRLs added - more generic functions for calculating/checking ICV added - some functions moved to appropriate module (to util.h) Internal changes: - loading of credentials (.p12 containers): - containers with **empty password** are **rejected** if a nonempty password is provided as parameter (i.e. the empty password can no longer be used to circumvent UTA-protection) - containers that don't have private keys encrypted with the same algorithm that is used by "storage" functions (e.g. `CREDENTIALS_save_dv`) are **rejected** - default log level changed to `LOG_WARNING` (should have minimal impact as most logs state the level explicitly) Other changes: - some includes fixed - various compiler warnings fixed --- include/secutils/certstatus/certstatus.h | 9 + include/secutils/certstatus/crl_mgmt.h | 3 +- include/secutils/certstatus/crls.h | 2 + include/secutils/credentials/cert.h | 42 +-- include/secutils/credentials/store.h | 17 ++ include/secutils/credentials/verify.h | 6 +- include/secutils/storage/files_dv.h | 2 + include/secutils/storage/files_icv.h | 12 + include/secutils/util/log.h | 2 +- include/secutils/util/util.h | 69 ++++- src/certstatus/certstatus.c | 168 +++++++++++- src/certstatus/crls.c | 2 +- src/certstatus/ocsp.c | 2 +- src/config/config.c | 11 +- src/config/config_update.c | 44 ++- src/config/opt.c | 2 +- src/connections/conn.c | 6 +- src/connections/http.c | 2 +- src/connections/tls.c | 4 +- src/credentials/cert.c | 208 +------------- src/credentials/credentials.c | 2 +- src/credentials/key.c | 2 +- src/credentials/store.c | 72 +++-- src/credentials/trusted.c | 2 +- src/credentials/verify.c | 5 +- src/crypto/crypto.c | 2 +- src/storage/files.c | 194 +++++++++++-- src/storage/files_dv.c | 2 +- src/storage/files_icv.c | 149 +++++++--- src/storage/uta_api.c | 2 +- src/util/extensions.c | 2 +- src/util/log.c | 4 +- src/util/util.c | 333 +++++++++++++++++++++-- 33 files changed, 979 insertions(+), 405 deletions(-) diff --git a/include/secutils/certstatus/certstatus.h b/include/secutils/certstatus/certstatus.h index eb7948d..c365586 100644 --- a/include/secutils/certstatus/certstatus.h +++ b/include/secutils/certstatus/certstatus.h @@ -17,6 +17,7 @@ #define SECUTILS_CERTSTATUS_H_ #include + #include "../util/log.h" #if OPENSSL_VERSION_NUMBER < 0x10101000L @@ -132,4 +133,12 @@ bool check_cert_revocation(X509_STORE_CTX* ctx, OPTIONAL OCSP_RESPONSE* resp); */ int check_revocation_any_method(X509_STORE_CTX* ctx); +/* + * Check revocation status on certs in ctx->chain. As a generalization of + * check_revocation() in crypto/x509/x509_vfy.c, considers local CRLs only. + * To be used as a callback function to be past to + * X509_STORE_set_check_revocation() + */ +int check_revocation_local_only_method(X509_STORE_CTX* ctx); + #endif /* SECUTILS_CERTSTATUS_H_ */ diff --git a/include/secutils/certstatus/crl_mgmt.h b/include/secutils/certstatus/crl_mgmt.h index ccf488b..5a59c18 100644 --- a/include/secutils/certstatus/crl_mgmt.h +++ b/include/secutils/certstatus/crl_mgmt.h @@ -17,7 +17,8 @@ #define SECUTILS_HEADER_CRL_MGMT_H #include -#include + +#include "../basic.h" #ifdef __cplusplus extern "C" { diff --git a/include/secutils/certstatus/crls.h b/include/secutils/certstatus/crls.h index 9b5e5d6..5c2ecd3 100644 --- a/include/secutils/certstatus/crls.h +++ b/include/secutils/certstatus/crls.h @@ -16,6 +16,8 @@ #ifndef SECUTILS_CRLS_H_ #define SECUTILS_CRLS_H_ +#include "../basic.h" + #include /*!***************************************************************************** diff --git a/include/secutils/credentials/cert.h b/include/secutils/credentials/cert.h index 1bf046a..d81adf4 100644 --- a/include/secutils/credentials/cert.h +++ b/include/secutils/credentials/cert.h @@ -26,8 +26,7 @@ #include /* for strcmp, strlen */ #include "../basic.h" -#include "../operators.h" -# include "../util/log.h" +#include "../util/log.h" #include @@ -99,19 +98,6 @@ int CERTS_save(OPTIONAL const STACK_OF(X509) *certs, const char *file, OPTIONAL void CERTS_free(OPTIONAL STACK_OF(X509) *certs); -/*!***************************************************************************** - * @brief parse an X.500 Distinguished Name (DN) - * - * @param dn string to be parsed, format "/type0=value0/type1=value1/type2=..." where characters may be escaped by '\'. - * The NULL-DN may be given as "/" or "". - * @param chtype type of the string, e.g., MBSTRING_ASC, as defined in openssl/asn1.h - * @param multirdn flag whether to allow multi-valued RDNs - * @return ASN.1 representation of the DN, or null on error - *******************************************************************************/ -/* this function is used by the genCMPClient API implementation */ -X509_NAME* UTIL_parse_name(const char* dn, long chtype, bool multirdn); - - /*!***************************************************************************** * @brief log messsage about the given certificate, printing its subject * @@ -177,30 +163,4 @@ bool CERT_check_all(const char *uri, OPTIONAL STACK_OF(X509) *certs, int type_CA OPTIONAL const X509_VERIFY_PARAM *vpm); -/*!***************************************************************************** - * @brief add certificate to given stack, optionally only if not already contained - * - * @param sk stack of certificates - * @param cert certificate to be pushed to the stack - * @param no_duplicate flag governing whether to add cert if it is a duplicate - * @return true on success, else false - *******************************************************************************/ -bool UTIL_sk_X509_add1_cert(STACK_OF(X509) * sk, X509* cert, bool no_duplicate); - - -/*!***************************************************************************** - * @brief add stack of certificates to given stack, - * optionally only if not self-signed and optionally if not already contained - * - * @param sk stack of certificates - * @param certs (optional) stack of certificates to be pushed to the stack - * @param no_self_signed flag governing whether to add self-signed certs - * @param no_duplicates flag governing whether to add cert if it is a duplicate - * @return true on success, else false - *******************************************************************************/ -/* this function is used by the genCMPClient API implementation */ -int UTIL_sk_X509_add1_certs(STACK_OF(X509) * sk, OPTIONAL const STACK_OF(X509) * certs, int no_self_signed, - int no_duplicates); - - #endif /* SECUTILS_CERT_H_ */ diff --git a/include/secutils/credentials/store.h b/include/secutils/credentials/store.h index 5d707c5..472c31d 100644 --- a/include/secutils/credentials/store.h +++ b/include/secutils/credentials/store.h @@ -208,6 +208,7 @@ bool STORE_load_check_dir(X509_STORE** pstore, const char* trust_dir, /*!***************************************************************************** * @brief search for files with CRLs in specified directory and add them to X509_STORE + * @note this function sets check_revocation_any_method() to be used as a callback during CRL validation * * @param pstore pointer to trust store to be augmented with CRLs. * CRL-based status checking will be enabled in it for the full certificate chain. @@ -220,6 +221,22 @@ bool STORE_load_check_dir(X509_STORE** pstore, const char* trust_dir, ******************************************************************************/ bool STORE_load_crl_dir(X509_STORE* pstore, const char* crl_dir, OPTIONAL const char* desc, bool recursive, OPTIONAL uta_ctx* ctx); +/*!***************************************************************************** + * @brief search for files with CRLs in specified directory and add them to X509_STORE + * @note this function sets check_revocation_local_only_method() to be used as a callback during CRL validation + * + * @param pstore pointer to trust store to be augmented with CRLs. + * CRL-based status checking will be enabled in it for the full certificate chain. + * @param crl_dir directory where to search for CRLs + * @param desc description of CRLs to use for error reporting, or null + * @param recursive if true, use recursive search in subdirectories + * @param ctx pointer to UTA context for checking file integrity&authenticity using ICV, or null + * @note at least one valid CRL file must be found in each visited directory + * @return true on success, false on error/failure + ******************************************************************************/ +bool STORE_load_crl_dir_local_only(X509_STORE* pstore, const char* crl_dir, OPTIONAL const char* desc, bool recursive, + OPTIONAL uta_ctx* ctx); + /*!***************************************************************************** * @brief release a trust store * diff --git a/include/secutils/credentials/verify.h b/include/secutils/credentials/verify.h index c3bfbeb..bbf0ad3 100644 --- a/include/secutils/credentials/verify.h +++ b/include/secutils/credentials/verify.h @@ -74,15 +74,13 @@ bool verify_cb_cert(X509_STORE_CTX* store_ctx, X509* cert, int err); /*!***************************************************************************** * @brief attempt to verify certificate * - * @param ctx (optional) pointer to UTA context, unused * @param cert certificate to be verified * @param untrusted (optional) intermediate certs that may be useful for building * the chain of certificates between the cert and the trusted certs in the trust store * @param trust_store pointer to structure containing trusted (root) certs and further verification parameters * @note trust_store may contain CRLs loaded via STORE_load_crl_dir() - * @return < 0 on on verification error, 0 for invalid cert, 1 for vaild cert + * @return < 0 on on verification error, 0 for invalid cert, 1 for valid cert *******************************************************************************/ -int CREDENTIALS_verify_cert(OPTIONAL uta_ctx* ctx, X509* cert, - OPTIONAL const STACK_OF(X509) * untrusted, X509_STORE* trust_store); +int CREDENTIALS_verify_cert(X509* cert, OPTIONAL const STACK_OF(X509) * untrusted, X509_STORE* trust_store); #endif /* SECUTILS_VERIFY_H_ */ diff --git a/include/secutils/storage/files_dv.h b/include/secutils/storage/files_dv.h index bf87542..e00adcc 100644 --- a/include/secutils/storage/files_dv.h +++ b/include/secutils/storage/files_dv.h @@ -25,6 +25,8 @@ #include +#include "../util/util.h" + #include "../storage/uta_api.h" #define MAX_UTA_PASS_LEN (MAX_B64_CHARS_PER_BYTE * TA_OUTLEN + 1) #include "files.h" diff --git a/include/secutils/storage/files_icv.h b/include/secutils/storage/files_icv.h index f361e9b..af50913 100644 --- a/include/secutils/storage/files_icv.h +++ b/include/secutils/storage/files_icv.h @@ -20,6 +20,7 @@ #include "../storage/uta_api.h" #include +#include /*! * @brief (re-)protect integrity of file (of any type that allows appending text) with ICV derived via UTA @@ -126,5 +127,16 @@ bool FILES_store_cert_pem(OPTIONAL uta_ctx* ctx, const X509* cert, const char* f */ bool FILES_store_crl_pem_icv(OPTIONAL uta_ctx* ctx, const X509_CRL* crl, const char* file, OPTIONAL const char* desc); +/*! + * @brief Calculates a file's ICV and, if it is equal to the ICV stored in the file, returns the content without ICV. + * + * @param ctx pointer to UTA context, which typically is part of the libsecutils context + * @param path path to the file, can be relative or absolute + * @note if \p path is relative, it is transformed into absolute path + * @return \c OPENSSL_STRING containing content of the file if ICV matches, otherwise null pointer. + * In case of an error, message is logged and null pointer is returned. + * @warning Returned \c OPENSSL_STRING must be freed using function \c OPENSSL_free(). + */ +OPENSSL_STRING FILE_get_file_content_if_existing_icv_is_valid(uta_ctx* ctx, const char* path); #endif /* SECUTILS_FILES_ICV_H_ */ diff --git a/include/secutils/util/log.h b/include/secutils/util/log.h index 71240b4..93a2ce5 100644 --- a/include/secutils/util/log.h +++ b/include/secutils/util/log.h @@ -71,7 +71,7 @@ void LOG_close(void); * @brief set verbosity level of LOG_default() * @note this may be done before LOG_init() is called * - * @param level the minimal severity of messages to be printed; default: LOG_INFO + * @param level the minimal severity of messages to be printed; default: LOG_WARNING */ /* this function is used by the genCMPClient CLI implementation */ void LOG_set_verbosity(severity level); diff --git a/include/secutils/util/util.h b/include/secutils/util/util.h index 11c82fc..b113aee 100644 --- a/include/secutils/util/util.h +++ b/include/secutils/util/util.h @@ -30,11 +30,12 @@ # include # include "../basic.h" -# include "../operators.h" # include # include +# include "../storage/uta_api.h" + static const char *const UTIL_SECUTILS_NAME = "secutils"; /*!< short name of this library */ static const int UTIL_max_path_len = 512; /*!< max length of file path name */ @@ -195,6 +196,30 @@ typedef u_int64_t uint64_t; STACK_OF(X509) *X509_STORE_get1_all_certs(X509_STORE *store); # endif +/*!***************************************************************************** + * @brief add certificate to given stack, optionally only if not already contained + * + * @param sk stack of certificates + * @param cert certificate to be pushed to the stack + * @param no_duplicate flag governing whether to add cert if it is a duplicate + * @return true on success, else false + *******************************************************************************/ +bool UTIL_sk_X509_add1_cert(STACK_OF(X509) * sk, X509* cert, bool no_duplicate); + +/*!***************************************************************************** + * @brief add stack of certificates to given stack, + * optionally only if not self-signed and optionally if not already contained + * + * @param sk stack of certificates + * @param certs (optional) stack of certificates to be pushed to the stack + * @param no_self_signed flag governing whether to add self-signed certs + * @param no_duplicates flag governing whether to add cert if it is a duplicate + * @return true on success, else false + *******************************************************************************/ +/* this function is used by the genCMPClient API implementation */ +int UTIL_sk_X509_add1_certs(STACK_OF(X509) * sk, OPTIONAL const STACK_OF(X509) * certs, int no_self_signed, + int no_duplicates); + /*!***************************************************************************** * @brief initialize the OpenSSL crypto library * @param version expected OpenSSL version number @@ -262,6 +287,18 @@ void *UTIL_read_file(const char *filename, int *lenp); ******************************************************************************/ bool UTIL_write_file(const char *filename, const void *data, size_t len); +/*!***************************************************************************** + * @brief parse an X.500 Distinguished Name (DN) + * + * @param dn string to be parsed, format "/type0=value0/type1=value1/type2=..." where characters may be escaped by '\'. + * The NULL-DN may be given as "/" or "". + * @param chtype type of the string, e.g., MBSTRING_ASC, as defined in openssl/asn1.h + * @param multirdn flag whether to allow multi-valued RDNs + * @return ASN.1 representation of the DN, or null on error + *******************************************************************************/ +/* this function is used by the genCMPClient API implementation */ +X509_NAME* UTIL_parse_name(const char* dn, long chtype, bool multirdn); + /*!***************************************************************************** * @brief call given function on each file in dir, optionally with recursion * @param fn function to be called on each file. @@ -370,6 +407,7 @@ size_t UTIL_url_encode(const char *source, # define HEX_BITS 4 # define HEX_MASK 0x0f # define MAX_DIGIT 9 +# define ICV_LEN16 16 /*! * @brief The function converts a binary string into a sequence of hex values. @@ -435,4 +473,33 @@ int UTIL_base64_encode_to_buf(const unsigned char *data, int len, unsigned char *UTIL_base64_decode(const char *b64_data, int b64_len, int *decoded_len); +/*! + * @brief derive integrity protection hash for data with given len, using key as DV. + * + * @param ctx pointer to uta context object + * @param data pointer to data from which the ICV will be calculated + * @param data_len size of data from which the ICV will be calculated + * @param key_dv The derivation value for key for which the ICV is calculated + * @param icv_out Pointer to a buffer where the resulting ICV will be stored. This buffer must be at least + * ICV_LEN16 in size. + * @return true if calculating the ICV is successful, false otherwise + */ +bool UTIL_calculate_icv(uta_ctx* ctx, const unsigned char* data, const size_t data_len, const char* key_dv, + unsigned char* icv_out); + +/*! + * @brief implementation of the function UTIL_calculate_icv. + * @note this function was created to avoid code repetition (the same computation is needed in files_icv.c). + * + * @param ctx pointer to uta context object + * @param data pointer to data from which the ICV will be calculated + * @param data_len size of data from which the ICV will be calculated + * @param key_dv The derivation value for key for which the ICV is calculated + * @param mac Pointer to a buffer where the resulting ICV will be stored. This buffer must be at least + * ICV_LEN16 in size. + * @return true if calculating the ICV is successful, false otherwise + */ +bool UTIL_calculate_icv_impl(uta_ctx* ctx, const unsigned char* data, const size_t data_len, const char* key_dv, + unsigned char* mac); + #endif /* SECUTILS_UTIL_H_ */ diff --git a/src/certstatus/certstatus.c b/src/certstatus/certstatus.c index 5ee6c09..df53e37 100644 --- a/src/certstatus/certstatus.c +++ b/src/certstatus/certstatus.c @@ -1,13 +1,13 @@ -/** +/** * @file certstatus.c -* +* * @brief Certificate status checking using CRLs and/or OCSP * * @copyright Copyright (c) Siemens Mobility GmbH, 2021 * * @author David von Oheimb * -* This work is licensed under the terms of the Apache Software License +* This work is licensed under the terms of the Apache Software License * 2.0. See the COPYING file in the top-level directory. * * SPDX-License-Identifier: Apache-2.0 @@ -28,9 +28,9 @@ # include #endif -#include +#include "secutils/operators.h" -static unsigned int num_CDPs(const X509* cert) +static int num_CDPs(const X509* cert) { CRL_DIST_POINTS* cdps = X509_get_ext_d2i(cert, NID_crl_distribution_points, 0, 0); if(cdps is_eq 0) /* maybe there is still a CDP for delta CRLs */ @@ -42,7 +42,7 @@ static unsigned int num_CDPs(const X509* cert) return res; } -static unsigned int num_AIAs(const X509* cert) +static int num_AIAs(const X509* cert) { STACK_OF(OPENSSL_STRING) *aias = X509_get1_ocsp((X509 *)cert); int res = aias not_eq 0 ? sk_OPENSSL_STRING_num(aias) : 0; @@ -416,3 +416,159 @@ int check_revocation_any_method(X509_STORE_CTX* ctx) } return true; } + + +/* + * @brief Function to check certificate revocation of a certificate and its + * issuer at depth determined by X509_STORE_CTX error_depth + * @param ctx pointer to X509 store context + * @return X509_V_OK (0) on success + * X509_V_ERR_ (>0) error code on failure + */ +static int check_local_cert_crls(X509_STORE_CTX* ctx) +{ + if(0 is_eq ctx) + { + LOG(FL_ERR, "null parameter ctx"); + return X509_V_ERR_UNSPECIFIED; + } + + X509_STORE_CTX* tmp_ctx = X509_STORE_CTX_new(); + X509_STORE_CTX_check_revocation_fn check_revocation; + int cert_idx = X509_STORE_CTX_get_error_depth(ctx); + X509* cert = sk_X509_value(X509_STORE_CTX_get0_chain(ctx), cert_idx); + X509* issuer = sk_X509_value(X509_STORE_CTX_get0_chain(ctx), cert_idx+1); + int ssl_ex_idx = SSL_get_ex_data_X509_STORE_CTX_idx(); + SSL* ssl = X509_STORE_CTX_get_ex_data(ctx, ssl_ex_idx); + STACK_OF(X509) *certs; + int ret = X509_V_ERR_UNSPECIFIED; + + /* + * Unfortunately, check_revocation() in crypto/x509/x509_vfy.c is static, + * yet we can get hold of it via X509_STORE_CTX_get_check_revocation(). + */ + if(tmp_ctx is_eq 0 + or not X509_STORE_CTX_init(tmp_ctx, 0, 0, 0) + or ((check_revocation = X509_STORE_CTX_get_check_revocation(tmp_ctx)) is_eq 0)) + { + LOG(FL_ERR, "cannot get pointer to check_revocation()"); + goto err; + } + X509_STORE_CTX_set0_param(tmp_ctx, 0); /* free tmp_ctx->param */ + if(not X509_STORE_CTX_init(tmp_ctx, X509_STORE_CTX_get0_store(ctx), + 0, 0) /* inherits flags etc. of store */ + or not X509_STORE_CTX_set_ex_data(tmp_ctx, ssl_ex_idx, ssl)) + { + LOG(FL_ERR, "cannot set up tmp_ctx"); + goto err; + } + if((certs = sk_X509_new_reserve(0, 2)) is_eq 0 + or (X509_STORE_CTX_set0_verified_chain(tmp_ctx, certs), 0) + or cert is_eq 0 or not sk_X509_push(certs, X509_dup(cert)) + or issuer is_eq 0 or not sk_X509_push(certs, X509_dup(issuer))) + { + LOG(FL_ERR, "cannot set certs in tmp_ctx"); + goto err; + } + + X509_VERIFY_PARAM* tmp_vpm = X509_STORE_CTX_get0_param(tmp_ctx); + X509_VERIFY_PARAM_clear_flags(tmp_vpm, X509_V_FLAG_CRL_CHECK_ALL); + X509_VERIFY_PARAM_set_flags(tmp_vpm, X509_V_FLAG_NONFINAL_CHECK | X509_V_FLAG_NO_CHECK_TIME); + int res = (*check_revocation)(tmp_ctx); /* checks only depth 0 */ + + if(res is_eq 0) + { + ret = X509_STORE_CTX_get_error(tmp_ctx); + } + else + { + ret = X509_V_OK; + } + + err: + X509_STORE_CTX_free(tmp_ctx); + return ret; +} + +static bool crl_time_valid(const X509_CRL* crl, const X509_VERIFY_PARAM* vpm) +{ + time_t check_time, *ptime = 0; + const ASN1_TIME* crl_end_time; + const ASN1_TIME* crl_last_update; + unsigned long flags = X509_VERIFY_PARAM_get_flags((X509_VERIFY_PARAM*)vpm); + + if((flags bitand X509_V_FLAG_USE_CHECK_TIME) not_eq 0) + { + check_time = X509_VERIFY_PARAM_get_time(vpm); + ptime = &check_time; + } + crl_end_time = X509_CRL_get0_nextUpdate(crl); + crl_last_update = X509_CRL_get0_lastUpdate(crl); + + if ((crl_end_time not_eq 0 and X509_cmp_time(crl_end_time, ptime) not_eq 1) or + (crl_last_update not_eq 0 and X509_cmp_time(crl_last_update, ptime) not_eq -1)) + { + return false; + } + + return true; +} + +int check_revocation_local_only_method(X509_STORE_CTX* ctx) +{ + if(0 is_eq ctx) + { + LOG(FL_ERR, "null parameter ctx"); + return 0; + } + + STACK_OF(X509) *chain = X509_STORE_CTX_get0_chain(ctx); + int i = 0; + const int last = sk_X509_num(chain) - 1; + + for(i = 0; i <= last; i++) + { + X509* cert = sk_X509_value(chain, i); + if(i is_eq last and X509_check_issued(cert, cert) is_eq X509_V_OK) + { + LOG_cert(FL_DEBUG, "skipping revocation check for self-issued last", cert); + break; + } + + STACK_OF(X509_CRL)* crls = X509_STORE_CTX_get1_crls(ctx, X509_get_issuer_name(cert)); + if(not crls or 0 is_eq sk_X509_CRL_num(crls) or 0 is_eq sk_X509_CRL_value(crls, 0)) + { + char* issuer = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); + if (0 not_eq issuer) + { + LOG(FL_ERR, "Local CRL for %s certificate %s not found", + (i + 1 is_eq last) ? "root" : "issuing", issuer); + OPENSSL_free(issuer); + } + return 0; + } + + if(true not_eq crl_time_valid(sk_X509_CRL_value(crls, 0), X509_STORE_CTX_get0_param(ctx))) + { + char* issuer = X509_NAME_oneline(X509_CRL_get_issuer(sk_X509_CRL_value(crls, 0)), 0, 0); + if(issuer not_eq 0) + { + LOG(FL_WARN, "CRL issued by %s is not valid in time", issuer); + OPENSSL_free(issuer); + } + } + + sk_X509_CRL_pop_free(crls, X509_CRL_free); + + X509_STORE_CTX_set_error_depth(ctx, i); + int res = check_local_cert_crls(ctx); + + if(X509_V_OK not_eq res) + { + verify_cb_cert(ctx, cert, res); + return 0; + } + chain = X509_STORE_CTX_get0_chain(ctx); /* for some reason need again */ + } + return 1; +} diff --git a/src/certstatus/crls.c b/src/certstatus/crls.c index c9d545d..370e303 100644 --- a/src/certstatus/crls.c +++ b/src/certstatus/crls.c @@ -27,7 +27,7 @@ #include #include -#include +#include "secutils/operators.h" /* adapted from OpenSSL:crypto/x509/t_crl.c */ void UTIL_print_crl(OPTIONAL BIO* bio, OPTIONAL const X509_CRL* crl) diff --git a/src/certstatus/ocsp.c b/src/certstatus/ocsp.c index 5ff03a0..1097551 100644 --- a/src/certstatus/ocsp.c +++ b/src/certstatus/ocsp.c @@ -29,7 +29,7 @@ # include # endif -# include +# include "secutils/operators.h" OCSP_RESPONSE* CONN_load_OCSP_http(const char* url, int timeout, const OCSP_REQUEST* req, diff --git a/src/config/config.c b/src/config/config.c index 6598a13..21b56e5 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -13,6 +13,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include @@ -22,7 +23,7 @@ #include #include -#include +#include "secutils/operators.h" /* adapted from OpenSSL:apps/include/apps.h */ static opt_t vpm_opts[] = { OPT_V_OPTIONS, OPT_END }; @@ -106,12 +107,16 @@ static const char* prev_item(const char* opt, const char* end) { beg--; } - int len = (int)(end - beg); + assert(beg <= end); + size_t len = (size_t)(end - beg); if(len > SECTION_NAME_MAX) { len = SECTION_NAME_MAX; } - strncpy(opt_item, beg, len); + if(len > 0) + { + strncpy(opt_item, beg, len); + } opt_item[len] = '\0'; if(end - beg > SECTION_NAME_MAX) { diff --git a/src/config/config_update.c b/src/config/config_update.c index 0d0d66b..16a5782 100644 --- a/src/config/config_update.c +++ b/src/config/config_update.c @@ -13,11 +13,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include -#include +#include "secutils/operators.h" static void skip_space(char** p) @@ -29,9 +30,9 @@ static void skip_space(char** p) } -static bool copy_line_substring(char* dest, const char* src, int* offset, int max_dest_len) +static bool copy_line_substring(char* dest, const char* src, size_t* offset, size_t max_dest_len) { - int copy_len = strnlen(src, max_dest_len); + size_t copy_len = strnlen(src, max_dest_len); if(*offset + copy_len > max_dest_len) { return false; @@ -51,9 +52,9 @@ static int file_modified; * Keeps any end-of-line comment. * Returns the length of the resulting line, or 0 in case of error. */ -static int refactor_entry(char* src_p, char* dest_p, const char* const key_p, const char* const val_p, int max_dest_len) +static int refactor_entry(char* const src_p, char* dest_p, const char* const key_p, const char* const val_p, size_t max_dest_len) { - int line_len = 0; + size_t line_len = 0; char* pos_p = src_p; if(0 is_eq src_p or 0 is_eq key_p or 0 is_eq val_p) @@ -82,8 +83,9 @@ static int refactor_entry(char* src_p, char* dest_p, const char* const key_p, co skip_space(&pos_p); /* found "key = ", copy in the new value */ - int val_len = strnlen(val_p, max_dest_len); - line_len = pos_p - src_p; + const size_t val_len = strnlen(val_p, max_dest_len); + assert(pos_p >= src_p); + line_len = (size_t)(pos_p - src_p); if(not copy_line_substring(dest_p, val_p, &line_len, max_dest_len)) { goto len_err; @@ -151,11 +153,11 @@ static size_t read_config_until_section(FILE* file_p, const char* file_name, con { size_t file_len = 0; char* pos_p = 0; - int section_name_len = strlen(section_name); + const size_t section_name_len = strlen(section_name); LOG(FL_TRACE, "Reading configuration until section is found or end of file reached"); while(0 not_eq fgets(input_line, c_line_buf_size, file_p)) { - int line_len = strnlen(input_line, c_line_buf_size); + size_t line_len = strnlen(input_line, c_line_buf_size); copy_file_line(file_buffer, input_line, line_len); /* break if line matches "\ *\[\ *section_name\ *\]" */ @@ -199,7 +201,13 @@ static size_t add_to_config_section(size_t file_len, const key_val_section* cons } if(pair->key and not found[i]) { - int line_len = snprintf(updated_line, c_line_buf_size, "%s = %s\n", pair->key, pair->val); + int line_len_aux = snprintf(updated_line, c_line_buf_size, "%s = %s\n", pair->key, pair->val); + if (line_len_aux < 0) + { + LOG(FL_ERR, "Failed to write string"); + return 0; + } + size_t line_len = (size_t)line_len_aux; copy_file_line(file_buffer, updated_line, line_len); LOG(FL_TRACE, "Adding in config file: %s" /*\n*/, updated_line); file_modified = 1; @@ -221,7 +229,7 @@ static size_t update_config_section(FILE* file_p, const char* file_name, size_t LOG(FL_TRACE, "Replacing 'key = value' pairs in the section"); while(0 not_eq fgets(input_line, c_line_buf_size, file_p)) { - int line_len = strnlen(input_line, c_line_buf_size); + size_t line_len = strnlen(input_line, c_line_buf_size); if(line_len > c_line_buf_size - 1) { return 0; @@ -261,7 +269,13 @@ static size_t update_config_section(FILE* file_p, const char* file_name, size_t return 0; } found[i] = 1; - line_len = refactor_entry(input_line, updated_line, pair->key, pair->val, c_line_buf_size - 1); + int line_len_aux = refactor_entry(input_line, updated_line, pair->key, pair->val, c_line_buf_size - 1); + if (line_len_aux < 0) + { + // error already logged + return 0; + } + line_len = (size_t)line_len_aux; copy_file_line(file_buffer, updated_line, line_len); LOG(FL_TRACE, "Updating in config file: %s" /*\n*/, updated_line); } @@ -283,9 +297,9 @@ static size_t update_config_section(FILE* file_p, const char* file_name, size_t /* copy the rest of the file into the buffer */ -static int copy_remaining_config(FILE* file_p, int file_len, char* input_line) +static size_t copy_remaining_config(FILE* file_p, size_t file_len, char* input_line) { - int line_len = strnlen(input_line, c_line_buf_size); /* first line of next section already read */ + size_t line_len = strnlen(input_line, c_line_buf_size); /* first line of next section already read */ copy_file_line(file_buffer, input_line, line_len); while(0 not_eq fgets(input_line, c_line_buf_size, file_p)) @@ -328,7 +342,7 @@ int CONF_update_config(OPTIONAL uta_ctx* ctx, const char* file_name, const key_v return 0; } - char* found = OPENSSL_malloc(key_val_section->count); + char* found = OPENSSL_malloc((size_t)key_val_section->count); if(found is_eq 0) { LOG(FL_ERR, "Cannot update config, out of memory"); diff --git a/src/config/opt.c b/src/config/opt.c index f41a454..ebd39ec 100644 --- a/src/config/opt.c +++ b/src/config/opt.c @@ -19,7 +19,7 @@ #include /* for strtoimax on Linux */ -#include +#include "secutils/operators.h" const char OPT_more_str[] = "-M"; const char OPT_section_str[] = "-S"; diff --git a/src/connections/conn.c b/src/connections/conn.c index 907587f..29b9863 100644 --- a/src/connections/conn.c +++ b/src/connections/conn.c @@ -13,6 +13,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include @@ -21,7 +22,7 @@ # include #endif -#include +#include "secutils/operators.h" static const char* skip_scheme(const char* str) { @@ -120,7 +121,8 @@ char* CONN_get_host(const char* uri) { end = strchr(uri, '/'); } - int len = end not_eq 0 ? end - uri : strlen(uri); + assert(end is_eq 0 or end >= uri); + size_t len = end not_eq 0 ? (size_t)(end - uri) : strlen(uri); str = OPENSSL_strndup(uri, len); if(0 is_eq str) { diff --git a/src/connections/http.c b/src/connections/http.c index 3859e96..5e5bc7f 100644 --- a/src/connections/http.c +++ b/src/connections/http.c @@ -16,7 +16,7 @@ #if !defined(OPENSSL_NO_OCSP) && !defined(OPENSSL_NO_SOCK) # include -# include +# include "secutils/operators.h" # include # include # ifndef SECUTILS_NO_TLS diff --git a/src/connections/tls.c b/src/connections/tls.c index d611c1b..7a0a134 100644 --- a/src/connections/tls.c +++ b/src/connections/tls.c @@ -29,7 +29,7 @@ #include #endif -#include +#include "secutils/operators.h" bool TLS_init(void) { @@ -176,7 +176,7 @@ SSL_CTX* TLS_CTX_new(OPTIONAL SSL_CTX* ssl_ctx, bak_flags = X509_VERIFY_PARAM_get_flags(vpm); /* disable any cert status/revocation checking etc. */ X509_VERIFY_PARAM_clear_flags(vpm, - compl(X509_V_FLAG_USE_CHECK_TIME + compl((unsigned long)X509_V_FLAG_USE_CHECK_TIME bitor X509_V_FLAG_NO_CHECK_TIME)); X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_NONFINAL_CHECK); } diff --git a/src/credentials/cert.c b/src/credentials/cert.c index bc16638..345126e 100644 --- a/src/credentials/cert.c +++ b/src/credentials/cert.c @@ -19,7 +19,7 @@ #include #include -#include +#include "secutils/operators.h" X509 *CERT_load(const char *file, OPTIONAL const char *source, @@ -71,151 +71,6 @@ void CERTS_free(OPTIONAL STACK_OF(X509) *certs) sk_X509_pop_free(certs, X509_free); } -/* - * dn is expected to be in the format "/type0=value0/type1=value1/type2=..." - * where characters may be escaped by '\'. - * The NULL-DN may be given as "/" or "". - */ -/* adapted from OpenSSL:apps/lib/apps.c */ -X509_NAME* UTIL_parse_name(const char* dn, long chtype, bool multirdn) -{ - size_t buflen = strlen(dn) + 1; /* to copy the types and values. - * Due to escaping, the copy can only become shorter */ - char* buf = OPENSSL_malloc(buflen); - size_t max_ne = buflen / (1 + 1) + 1; /* maximum number of name elements */ - const char** ne_types = OPENSSL_malloc(max_ne * sizeof(char*)); - char** ne_values = OPENSSL_malloc(max_ne * sizeof(char*)); - int* mval = OPENSSL_malloc(max_ne * sizeof(int)); - - const char* sp = dn; - char* bp = buf; - int i, ne_num = 0; - - X509_NAME* n = 0; - int nid; - - if(0 is_eq buf or 0 is_eq ne_types or 0 is_eq ne_values or 0 is_eq mval) - { - LOG_err("Malloc error"); - goto error; - } - - /* no multivalued RDN by default */ - mval[ne_num] = 0; - - if(*sp not_eq '\0' and *sp++ not_eq '/') - { /* skip leading '/' */ - LOG(FL_ERR, "DN '%s' does not start with '/'.", dn); - goto error; - } - - while(*sp not_eq '\0') - { - /* collect type */ - ne_types[ne_num] = bp; - /* parse element name */ - while(*sp not_eq '=') - { - if(*sp is_eq '\\') - { /* is there anything to escape in the * type...? */ - if(*++sp not_eq '\0') - { - *bp++ = *sp++; - } - else - { - LOG(FL_ERR, "Escape character at end of DN '%s'", dn); - goto error; - } - } - else if(*sp is_eq '\0') - { - LOG(FL_ERR, "End of string encountered while processing type of DN '%s' element #%d", dn, ne_num); - goto error; - } - else - { - *bp++ = *sp++; - } - } - sp++; - *bp++ = '\0'; - /* parse element value */ - ne_values[ne_num] = bp; - while(*sp not_eq '\0') - { - if(*sp is_eq '\\') - { - if(*++sp not_eq '\0') - { - *bp++ = *sp++; - } - else - { - LOG(FL_ERR, "Escape character at end of DN '%s'", dn); - goto error; - } - } - else if(*sp is_eq '/') - { /* start of next element */ - sp++; - /* no multivalued RDN by default */ - mval[ne_num + 1] = 0; - break; - } - else if(*sp is_eq '+' and multirdn) - { - /* a not escaped + signals a multi-valued RDN */ - sp++; - mval[ne_num + 1] = -1; - break; - } - else - { - *bp++ = *sp++; - } - } - *bp++ = '\0'; - ne_num++; - } - - if(0 is_eq(n = X509_NAME_new())) - { - goto error; - } - - for(i = 0; i < ne_num; i++) - { - if((nid = OBJ_txt2nid(ne_types[i])) is_eq NID_undef) - { - LOG(FL_WARN, "DN '%s' attribute %s has no known NID, skipped", dn, ne_types[i]); - continue; - } - - if(0 is_eq * ne_values[i]) - { - LOG(FL_WARN, "No value provided for DN '%s' attribute %s, skipped", dn, ne_types[i]); - continue; - } - - if(0 is_eq X509_NAME_add_entry_by_NID(n, nid, chtype, (unsigned char*)ne_values[i], -1, -1, mval[i])) - { - ERR_print_errors(bio_err); - LOG(FL_ERR, "Error adding name attribute '/%s=%s'", ne_types[i], ne_values[i]); - X509_NAME_free(n); - n = 0; - goto error; - } - } - -error: - OPENSSL_free(ne_values); - OPENSSL_free(ne_types); - OPENSSL_free(mval); - OPENSSL_free(buf); - return n; -} - void CERT_print(OPTIONAL const X509* cert, OPTIONAL BIO* bio, unsigned long neg_cflags) { @@ -228,7 +83,7 @@ void CERT_print(OPTIONAL const X509* cert, OPTIONAL BIO* bio, unsigned long neg_ unsigned long flags = ASN1_STRFLGS_RFC2253 bitor ASN1_STRFLGS_ESC_QUOTE bitor XN_FLAG_SEP_CPLUS_SPC bitor XN_FLAG_FN_SN; BIO_printf(bio, " Certificate\n"); - X509_print_ex(bio, (X509*)cert, flags, compl X509_FLAG_NO_SUBJECT); + X509_print_ex(bio, (X509*)cert, flags, compl (unsigned long)X509_FLAG_NO_SUBJECT); if(X509_check_issued((X509*)cert, (X509*)cert) is_eq X509_V_OK) { BIO_printf(bio, " self-signed\n"); @@ -236,9 +91,9 @@ void CERT_print(OPTIONAL const X509* cert, OPTIONAL BIO* bio, unsigned long neg_ else { BIO_printf(bio, " "); - X509_print_ex(bio, (X509*)cert, flags, compl X509_FLAG_NO_ISSUER); + X509_print_ex(bio, (X509*)cert, flags, compl (unsigned long)X509_FLAG_NO_ISSUER); } - X509_print_ex(bio, (X509*)cert, flags, compl(X509_FLAG_NO_SERIAL bitor X509_FLAG_NO_VALIDITY)); + X509_print_ex(bio, (X509*)cert, flags, compl((unsigned long)X509_FLAG_NO_SERIAL bitor X509_FLAG_NO_VALIDITY)); if(X509_cmp_current_time(X509_get0_notBefore(cert)) > 0) { BIO_printf(bio, " not yet valid\n"); @@ -362,58 +217,3 @@ bool CERT_check_all(const char *uri, OPTIONAL STACK_OF(X509) *certs, int type_CA && ret; /* Having 'ret' after the '&&', all certs are checked. */ return ret; } - - -/* start of definitions borrowed from OpenSSL:crypto/cmp/cmp_lib.c */ -static int X509_cmp_from_ptrs(const struct x509_st* const* a, const struct x509_st* const* b) -{ - return X509_cmp(*a, *b); -} - -bool UTIL_sk_X509_add1_cert(STACK_OF(X509) * sk, X509* cert, bool no_duplicate) -{ - if(no_duplicate) - { - sk_X509_set_cmp_func(sk, &X509_cmp_from_ptrs); - if(sk_X509_find(sk, cert) >= 0) - { - return 1; - } - } - if(0 is_eq sk_X509_push(sk, cert)) - { - return 0; - } - return X509_up_ref(cert); -} - -int UTIL_sk_X509_add1_certs(STACK_OF(X509) * sk, OPTIONAL const STACK_OF(X509) * certs, int no_self_signed, - int no_duplicates) -{ - int i = 0; - - if(sk is_eq 0) - { - return 0; - } - - if(certs is_eq 0) - { - return 1; - } - const int n = sk_X509_num(certs); - for(i = 0; i < n; i++) - { - X509* cert = sk_X509_value(certs, i); - if((not no_self_signed or X509_check_issued(cert, cert) not_eq X509_V_OK) - and (not UTIL_sk_X509_add1_cert(sk, cert, no_duplicates))) - { - return 0; - } - } - return 1; -} -/* end of definitions borrowed from OpenSSL:crypto/cmp/cmp_lib.c */ - - - diff --git a/src/credentials/credentials.c b/src/credentials/credentials.c index 58fdb64..df3218e 100644 --- a/src/credentials/credentials.c +++ b/src/credentials/credentials.c @@ -27,7 +27,7 @@ #include #include -#include +#include "secutils/operators.h" /* this type is part of the genCMPClient API */ struct credentials diff --git a/src/credentials/key.c b/src/credentials/key.c index c0f2314..d57e4e1 100644 --- a/src/credentials/key.c +++ b/src/credentials/key.c @@ -20,7 +20,7 @@ #include #include -#include +#include "secutils/operators.h" EVP_PKEY* KEY_new(const char* spec) { diff --git a/src/credentials/store.c b/src/credentials/store.c index 328ad07..0911e71 100644 --- a/src/credentials/store.c +++ b/src/credentials/store.c @@ -26,7 +26,7 @@ #include #include -#include +#include "secutils/operators.h" typedef struct STORE_ex_st { @@ -304,8 +304,9 @@ bool STORE_load_check_dir(X509_STORE** pstore, const char* trust_dir, OPTIONAL const char* desc, bool recursive, OPTIONAL X509_VERIFY_PARAM *vpm, OPTIONAL uta_ctx* ctx) { - DIR* p_dir = 0; bool found = false; + int count = -1; + int i; if(0 is_eq pstore or 0 is_eq trust_dir) { @@ -313,16 +314,18 @@ bool STORE_load_check_dir(X509_STORE** pstore, const char* trust_dir, goto err; } - p_dir = opendir(trust_dir); - if(0 is_eq p_dir) + struct dirent** namelist = 0; + count = scandir(trust_dir, &namelist, 0, alphasort); + if(-1 is_eq count) { LOG(FL_ERR, "cannot read directory '%s'", trust_dir); goto err; } - struct dirent* p_dirent = readdir(p_dir); - while(0 not_eq p_dirent) + for(i = 0; i < count; ++i) { + const struct dirent* p_dirent = namelist[i]; + char full_path[UTIL_max_path_len + 1]; snprintf(full_path, sizeof(full_path), "%s/%s", trust_dir, p_dirent->d_name); @@ -330,7 +333,7 @@ bool STORE_load_check_dir(X509_STORE** pstore, const char* trust_dir, memset(&f_stat, 0x00, sizeof(struct stat)); if(-1 is_eq stat(full_path, &f_stat)) { - LOG(FL_INFO, "cannot read status of %s - %s", full_path, strerror(errno)); + LOG(FL_ERR, "cannot read status of %s - %s", full_path, strerror(errno)); } else { @@ -350,7 +353,6 @@ bool STORE_load_check_dir(X509_STORE** pstore, const char* trust_dir, } } } - p_dirent = readdir(p_dir); } if(not found) @@ -361,9 +363,13 @@ bool STORE_load_check_dir(X509_STORE** pstore, const char* trust_dir, desc not_eq 0 ? desc : "trusted certs", ctx not_eq 0 ? "with valid ICV " : "", trust_dir); } err: - if(p_dir not_eq 0) + for(i = 0; i < count; ++i) { - closedir(p_dir); + free(namelist[i]); + } + if (-1 not_eq count) + { + free(namelist); } int cert_num = STORE_certs_num(*pstore); @@ -375,10 +381,13 @@ bool STORE_load_check_dir(X509_STORE** pstore, const char* trust_dir, return found; } -bool STORE_load_crl_dir(X509_STORE* pstore, const char* crl_dir, OPTIONAL const char* desc, bool recursive, OPTIONAL uta_ctx* ctx) + +static bool STORE_load_crl_dir_impl(X509_STORE* pstore, const char* crl_dir, OPTIONAL const char* desc, bool recursive, + OPTIONAL uta_ctx* ctx, int (*callback)(X509_STORE_CTX*)) { - DIR* p_dir = 0; bool found = false; + int count = -1; + int i; if(0 is_eq pstore or 0 is_eq crl_dir) { @@ -386,16 +395,18 @@ bool STORE_load_crl_dir(X509_STORE* pstore, const char* crl_dir, OPTIONAL const goto err; } - p_dir = opendir(crl_dir); - if(0 is_eq p_dir) + struct dirent** namelist = 0; + count = scandir(crl_dir, &namelist, 0, alphasort); + if(-1 is_eq count) { LOG(FL_ERR, "cannot access directory '%s'", crl_dir); goto err; } - struct dirent* p_dirent = readdir(p_dir); - while(0 not_eq p_dirent) + for(i = 0; i < count; ++i) { + const struct dirent* p_dirent = namelist[i]; + char full_path[UTIL_max_path_len + 1]; snprintf(full_path, sizeof(full_path), "%s/%s", crl_dir, p_dirent->d_name); @@ -403,7 +414,7 @@ bool STORE_load_crl_dir(X509_STORE* pstore, const char* crl_dir, OPTIONAL const memset(&f_stat, 0x00, sizeof(struct stat)); if(-1 is_eq stat(full_path, &f_stat)) { - LOG(FL_INFO, "cannot read status of %s - %s", full_path, strerror(errno)); + LOG(FL_ERR, "cannot read status of %s - %s", full_path, strerror(errno)); } else { @@ -430,32 +441,49 @@ bool STORE_load_crl_dir(X509_STORE* pstore, const char* crl_dir, OPTIONAL const } else if(recursive and (f_stat.st_mode bitand S_IFDIR) and (0 not_eq strncmp(p_dirent->d_name, ".", 1))) { - if(not STORE_load_crl_dir(pstore, full_path, desc, recursive, ctx)) + if(not STORE_load_crl_dir_impl(pstore, full_path, desc, recursive, ctx, callback)) { found = false; goto err; } } } - p_dirent = readdir(p_dir); } // set up cert CRL check callback for checking full chain if(found) { X509_VERIFY_PARAM_set_flags(X509_STORE_get0_param(pstore), X509_V_FLAG_STATUS_CHECK_ALL); - X509_STORE_set_check_revocation(pstore, &check_revocation_any_method); + X509_STORE_set_check_revocation(pstore, callback); } err: - if(p_dir not_eq 0) + for(i = 0; i < count; ++i) + { + free(namelist[i]); + } + if (-1 not_eq count) { - closedir(p_dir); + free(namelist); } + return found; } +bool STORE_load_crl_dir(X509_STORE* pstore, const char* crl_dir, OPTIONAL const char* desc, bool recursive, OPTIONAL uta_ctx* ctx) +{ + return STORE_load_crl_dir_impl(pstore, crl_dir, desc, recursive, ctx, &check_revocation_any_method); +} + + +bool STORE_load_crl_dir_local_only(X509_STORE* pstore, const char* crl_dir, OPTIONAL const char* desc, bool recursive, + OPTIONAL uta_ctx* ctx) +{ + return STORE_load_crl_dir_impl(pstore, crl_dir, desc, recursive, ctx, &check_revocation_local_only_method); +} + + bool STORE_set1_host_ip(X509_STORE* ts, const char* name, const char* ip) { if(ts is_eq 0) diff --git a/src/credentials/trusted.c b/src/credentials/trusted.c index ef07eda..d862854 100644 --- a/src/credentials/trusted.c +++ b/src/credentials/trusted.c @@ -20,7 +20,7 @@ #include #include -#include +#include "secutils/operators.h" static const char* config_file(void) { diff --git a/src/credentials/verify.c b/src/credentials/verify.c index fcfb263..d2be773 100644 --- a/src/credentials/verify.c +++ b/src/credentials/verify.c @@ -23,7 +23,7 @@ #include #include -#include +#include "secutils/operators.h" bool STORE_CTX_tls_active(const X509_STORE_CTX* ctx) @@ -183,8 +183,7 @@ bool verify_cb_cert(X509_STORE_CTX* store_ctx, X509* cert, int err) return verify_cb != 0 and (*verify_cb)(0, store_ctx) != 0; } -int CREDENTIALS_verify_cert(OPTIONAL uta_ctx* uta_ctx, X509* cert, - OPTIONAL const STACK_OF(X509) * untrusted_certs, X509_STORE* trust_store) +int CREDENTIALS_verify_cert(X509* cert, OPTIONAL const STACK_OF(X509) * untrusted_certs, X509_STORE* trust_store) { int result = -1; X509_STORE_CTX* store_ctx = 0; diff --git a/src/crypto/crypto.c b/src/crypto/crypto.c index 5c6a8c8..da0b263 100644 --- a/src/crypto/crypto.c +++ b/src/crypto/crypto.c @@ -13,7 +13,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include +#include "secutils/operators.h" #include #include diff --git a/src/storage/files.c b/src/storage/files.c index 771b23f..a034ff9 100644 --- a/src/storage/files.c +++ b/src/storage/files.c @@ -33,7 +33,7 @@ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") #include # include -#include +#include "secutils/operators.h" static file_format_t adjust_format(const char* * file, file_format_t format, bool engine_ok) { @@ -323,6 +323,11 @@ typedef struct pw_cb_data static int password_callback(char* buf, int bufsiz, int verify, void* cb_tmp) { + if(0 == buf || bufsiz <= 0 || 0 == cb_tmp) + { + return -1; + } + int res = 0 * verify; /* make (artificial) use of 'verify' */ const char* password = 0; PW_CB_DATA* cb_data = (PW_CB_DATA*)cb_tmp; @@ -339,11 +344,145 @@ static int password_callback(char* buf, int bufsiz, int verify, void* cb_tmp) { res = bufsiz; } - memcpy(buf, password, res); /* copy password and length(res) into buf */ + memcpy(buf, password, (size_t)res); /* copy password and length(res) into buf */ } return res; /* the size */ } + +static int get_algo_nid(const X509_ALGOR *alg) +{ + int pbenid, aparamtype; + const ASN1_OBJECT *aoid; + const void *aparam; + + X509_ALGOR_get0(&aoid, &aparamtype, &aparam, alg); + + pbenid = OBJ_obj2nid(aoid); + + if(pbenid == NID_pbes2) + { + PBE2PARAM *pbe2 = 0; + int encnid; + if(aparamtype == V_ASN1_SEQUENCE) + { + pbe2 = ASN1_item_unpack(aparam, ASN1_ITEM_rptr(PBE2PARAM)); + } + if(pbe2 == 0) + { + BIO_puts(bio_err, ", "); + return NID_undef; + } + X509_ALGOR_get0(&aoid, &aparamtype, &aparam, pbe2->keyfunc); + pbenid = OBJ_obj2nid(aoid); + X509_ALGOR_get0(&aoid, 0, 0, pbe2->encryption); + encnid = OBJ_obj2nid(aoid); + PBE2PARAM_free(pbe2); + return encnid; + } + + return NID_undef; +} + + +static int bag_get_pkey_algo(const PKCS12_SAFEBAG *bag, const char *pass, int passlen) +{ + // we only support a shrouded keybag + if(PKCS12_SAFEBAG_get_nid(bag) is_eq NID_pkcs8ShroudedKeyBag) + { + const X509_SIG *tp8; + const X509_ALGOR *tp8alg; + + tp8 = PKCS12_SAFEBAG_get0_pkcs8(bag); + X509_SIG_get0(tp8, &tp8alg, 0); + return get_algo_nid(tp8alg); + } + return NID_undef; +} + + +static int bags_get_pkey_algo(const STACK_OF(PKCS12_SAFEBAG) *bags, const char *pass, int passlen, int desired_algo_nid) +{ + int i, algo_nid = NID_undef; + for(i = 0; i < sk_PKCS12_SAFEBAG_num(bags); ++i) + { + int bag_algo_nid = bag_get_pkey_algo(sk_PKCS12_SAFEBAG_value(bags, i), pass, passlen); + if(bag_algo_nid is_eq NID_undef) + { + continue; + } + if(bag_algo_nid not_eq desired_algo_nid) + { + return bag_algo_nid; + } + algo_nid = bag_algo_nid; + } + return algo_nid; +} + + +/* This function determines whether a given PKCS#12 file contains at least one private key that was encrypted using the + desired algorithm. */ +/* Iterates over all keybags in the file but only looks inside Shrouded keybags. Returns 1 if the desired algorithm + and no other was found, 0 otherwise. */ +static int algo_allowed(const PKCS12* p12, const char* pass, int passlen, int desired_algo_nid) +{ + STACK_OF(PKCS7) *auth_safes = 0; + STACK_OF(PKCS12_SAFEBAG) *bags = 0; + int i, bagnid; + int found_desired_algo = 0; + PKCS7 *p7 = 0; + + auth_safes = PKCS12_unpack_authsafes(p12); + + if(0 is_eq auth_safes) + { + return 0; + } + + for(i = 0; i < sk_PKCS7_num(auth_safes); ++i) + { + p7 = sk_PKCS7_value(auth_safes, i); + bagnid = OBJ_obj2nid(p7->type); + if(bagnid == NID_pkcs7_data) + { + bags = PKCS12_unpack_p7data(p7); + } + else if(bagnid == NID_pkcs7_encrypted) + { + bags = PKCS12_unpack_p7encdata(p7, pass, passlen); + } + else + { + continue; + } + if(!bags) + { + sk_PKCS7_pop_free(auth_safes, PKCS7_free); + return 0; + } + + int algo = bags_get_pkey_algo(bags, pass, passlen, desired_algo_nid); + sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); + bags = 0; + + if(algo is_eq NID_undef) + { + continue; + } + if(algo is_eq desired_algo_nid) + { + found_desired_algo = 1; + continue; + } + sk_PKCS7_pop_free(auth_safes, PKCS7_free); + return 0; + } + + sk_PKCS7_pop_free(auth_safes, PKCS7_free); + return found_desired_algo; +} + /* adapted from OpenSSL:apps/lib/apps.c * @warning security note: the `pem_cb` callback must be compliant with * https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings @@ -371,32 +510,37 @@ static int load_pkcs12(BIO* in, OPTIONAL const char* desc, OPTIONAL pem_password /* See if an empty password will do */ if(PKCS12_verify_mac(p12, "", 0) not_eq 0 or PKCS12_verify_mac(p12, 0, 0) not_eq 0) { - LOG(FL_WARN, "Unencrypted PKCS#12 file%s%s", for_str, desc_str); - pass = ""; + LOG(FL_WARN, "Rejecting unencrypted PKCS#12 file%s%s", for_str, desc_str); + return 0; } - else + if(pem_cb is_eq 0) { - if(pem_cb is_eq 0) - { - pem_cb = password_callback; - } - len = pem_cb(tpass, PEM_BUFSIZE, 0, cb_data); - if(len < 0) - { - LOG(FL_ERR, "passphrase callback error for %s", desc not_eq 0 ? desc : "PKCS#12 file"); - goto die; - } - if(len < PEM_BUFSIZE) - { - tpass[len] = 0; - } - if(0 is_eq PKCS12_verify_mac(p12, tpass, len)) - { - LOG(FL_ERR, "mac verify error (wrong password?) in PKCS12 file%s%s", for_str, desc_str); - goto die; - } - pass = tpass; + pem_cb = password_callback; + } + len = pem_cb(tpass, PEM_BUFSIZE, 0, cb_data); + if(len < 0) + { + LOG(FL_ERR, "passphrase callback error for %s", desc not_eq 0 ? desc : "PKCS#12 file"); + goto die; } + if(len < PEM_BUFSIZE) + { + tpass[len] = 0; + } + if(0 is_eq PKCS12_verify_mac(p12, tpass, len)) + { + LOG(FL_ERR, "mac verify error (wrong password?) in PKCS12 file%s%s", for_str, desc_str); + goto die; + } + + pass = tpass; + + if(0 is_eq algo_allowed(p12, pass, len, NID_aes_256_cbc)) + { + LOG(FL_ERR, "Rejecting PKCS#12 file%s%s due to unsupported private key encryption algorithm", for_str, desc_str); + return 0; + } + EVP_PKEY* unused_pkey = 0; X509* unused_cert = 0; ret = PKCS12_parse(p12, pass, pkey is_eq 0 ? &unused_pkey : pkey, cert is_eq 0 ? &unused_cert : cert, ca); diff --git a/src/storage/files_dv.c b/src/storage/files_dv.c index 576cf1e..01f6cba 100644 --- a/src/storage/files_dv.c +++ b/src/storage/files_dv.c @@ -28,7 +28,7 @@ static const char* const DV_SECTION = "dv"; /*! name of DVFILE section with DV s #include #include -#include +#include "secutils/operators.h" /* Get device-specific password (base64 encoded) */ static bool getBase64Password(OPTIONAL uta_ctx* ctx, const unsigned char* dv, char* pw) diff --git a/src/storage/files_icv.c b/src/storage/files_icv.c index 958e755..d2cfa62 100644 --- a/src/storage/files_icv.c +++ b/src/storage/files_icv.c @@ -13,15 +13,16 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include +#include #include #include #include #include -#include -#include +#include "secutils/operators.h" #define ICVLEN 16 @@ -37,43 +38,10 @@ */ static bool calculate_icv_hex(OPTIONAL uta_ctx* ctx, const void* data, size_t len, const char* name, char* buf) { - unsigned char key[TA_OUTLEN]; unsigned char md[SHA256_DIGEST_LENGTH]; - unsigned int md_len; - -/* Derive an ICV key from the trust anchor */ -#ifdef SECUTILS_USE_UTA - bool uta_res = uta_getkey(ctx, (const unsigned char*)name, strnlen(name, UTIL_max_path_len), key, TA_OUTLEN); - - if(not uta_res) - { - LOG(FL_ERR, "Could not get key for '%s' from UTA", name); - return false; - } -#else - if(ctx not_eq 0) - { - LOG(FL_ERR, "UTA not available"); - return false; - } - /* some trivial emulation of trust anchor */ -#if TA_OUTLEN != SHA256_DIGEST_LENGTH -#error Cannot produce KEY with length other than SHA256_DIGEST_LENGTH -#endif - if(0 is_eq SHA256((const unsigned char*)name, strlen(name), key)) - { - LOG(FL_ERR, "ERROR during SHA256 calculation from: %s", name); - return false; - } - unsigned char tmp = key[3]; - key[3] = key[25]; - key[25] = key[10]; - key[10] = tmp; -#endif - if(0 is_eq HMAC(EVP_sha256(), key, TA_OUTLEN, data, len, md, &md_len) or md_len < ICVLEN) + if (false is_eq UTIL_calculate_icv_impl(ctx, data, len, name, md)) { - LOG(FL_ERR, "Could not calculate HMAC used as ICV for '%s'", name); return false; } @@ -135,13 +103,13 @@ static bool protect_or_check_icv(OPTIONAL uta_ctx* ctx, const char* file, const } else { - buf = OPENSSL_malloc(fsize); + buf = OPENSSL_malloc((size_t)fsize); if(buf is_eq 0) { LOG(FL_ERR, "Out of memory reading file '%s'", file); goto err; } - if((size_t)fsize not_eq fread(buf, 1, fsize, f)) + if((size_t)fsize not_eq fread(buf, 1, (size_t)fsize, f)) { LOG(FL_ERR, "Could not read file '%s'", file); goto err; @@ -155,7 +123,7 @@ static bool protect_or_check_icv(OPTIONAL uta_ctx* ctx, const char* file, const { if(found) { - fsize -= ICV_LINE_LEN; /* strip existing ICV */ + fsize -= (long)ICV_LINE_LEN; /* strip existing ICV */ if(fseek(f, fsize, SEEK_SET) is_eq EOF) { LOG(FL_ERR, "Could not strip ICV from file '%s'", file); @@ -171,7 +139,7 @@ static bool protect_or_check_icv(OPTIONAL uta_ctx* ctx, const char* file, const goto err; } } - if(not calculate_icv_hex(ctx, buf, fsize - (protect ? 0 : ICV_LINE_LEN), location, icv_hex)) + if(not calculate_icv_hex(ctx, buf, (size_t)(fsize - (protect ? 0 : (long)ICV_LINE_LEN)), location, icv_hex)) { LOG(FL_ERR, "Could not calculate ICV for file '%s'", file); goto err; @@ -289,3 +257,104 @@ bool FILES_store_crl_pem_icv(OPTIONAL uta_ctx* ctx, const X509_CRL* crl, const c } return true; } + +static inline long get_file_size(FILE* f) +{ + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + return fsize; +} + +OPENSSL_STRING FILE_get_file_content_if_existing_icv_is_valid(uta_ctx* ctx, const char* path) +{ + if(0 is_eq ctx) + { + LOG(FL_ERR, "No context"); + return 0; + } + + if(0 is_eq path) + { + LOG(FL_ERR, "No path to ICV file"); + return 0; + } + + char absolute_path[PATH_MAX]; + if(0 is_eq realpath(path, absolute_path)) + { + LOG(FL_ERR, "Could not resolve absolute path from: %s", path); + return 0; + } + + // open file + FILE* file = fopen(absolute_path, "rb"); + if(0 is_eq file) + { + LOG(FL_ERR, "Could not open file '%s'", absolute_path); + return 0; + } + + const long file_size = get_file_size(file); + + if(file_size < 0) + { + LOG(FL_ERR, "Could not get size of file '%s'", absolute_path); + } + else if(0 is_eq file_size) + { + LOG(FL_ERR, "File '%s' is empty", absolute_path); + } + else + { + OPENSSL_STRING content = OPENSSL_malloc((size_t)file_size + 1); + if(0 is_eq content) + { + LOG(FL_ERR, "Out of memory reading file '%s'", absolute_path); + goto error; + } + if((size_t)file_size not_eq fread(content, sizeof *content, (size_t)file_size, file)) + { + LOG(FL_ERR, "Could not read file '%s'", absolute_path); + goto error; + } + content[file_size] = '\0'; + + const long icv_tag_start_index = file_size - (long)ICV_LINE_LEN; + const char* icv_tag_start = content + icv_tag_start_index; + const char* icv_hex_start = content + file_size - ICV_HEX_LEN - 1; + + const bool found = (file_size >= ICV_LINE_LEN) and (0 is_eq strncmp(icv_tag_start, ICV_TAG, strlen(ICV_TAG))); + if(false is_eq found) + { + LOG(FL_ERR, "Could not find ICV at end of file '%s'", absolute_path); + goto error; + } + + // read original ICV + char original_icv_hex[ICV_HEX_LEN + 1] = {'\0'}; + strncpy(original_icv_hex, icv_hex_start, ICV_HEX_LEN); + + // calculate ICV + char icv_hex[ICV_HEX_LEN + 1] = {'\0'}; + if(not calculate_icv_hex(ctx, content, (size_t)icv_tag_start_index, absolute_path, icv_hex)) + { + LOG(FL_ERR, "Could not calculate ICV for file '%s'", absolute_path); + goto error; + } + + // if ICVs are equal then return whole file content without ICV + if(0 is_eq strncmp(original_icv_hex, icv_hex, ICV_HEX_LEN)) + { + fclose(file); + // "remove" ICV part in string content + content[icv_tag_start_index] = '\0'; + return content; + } + error: + OPENSSL_free(content); + } + + fclose(file); + return 0; +} diff --git a/src/storage/uta_api.c b/src/storage/uta_api.c index e2a910f..fc6c064 100644 --- a/src/storage/uta_api.c +++ b/src/storage/uta_api.c @@ -21,7 +21,7 @@ #include #include -#include +#include "secutils/operators.h" #if DVLEN not_eq UTA_LEN_DV_V1 #error DVLEN not_eq UTA_LEN_DV_V1 /* mismatch with uta.h */ diff --git a/src/util/extensions.c b/src/util/extensions.c index 4b5453b..14d2730 100644 --- a/src/util/extensions.c +++ b/src/util/extensions.c @@ -16,7 +16,7 @@ #include "util/extensions.h" #include "util/log.h" -#include +#include "secutils/operators.h" X509_EXTENSIONS* EXTENSIONS_new(void) diff --git a/src/util/log.c b/src/util/log.c index f8ff0fa..106c669 100644 --- a/src/util/log.c +++ b/src/util/log.c @@ -16,7 +16,7 @@ #include #include -#include +#include "secutils/operators.h" #include @@ -29,7 +29,7 @@ static LOG_cb_t LOG_fn = 0; /*!< this variable is shared between threads */ static const size_t loc_len = 256; -static severity verbosity = LOG_INFO; +static severity verbosity = LOG_WARNING; BIO* bio_err = 0; BIO* bio_trace = 0; diff --git a/src/util/util.c b/src/util/util.c index 5c1d3c3..2cf0d25 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -13,6 +13,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include @@ -24,7 +25,7 @@ #include -#include +#include "secutils/operators.h" int UTIL_atoint(const char* str) { @@ -42,7 +43,7 @@ int UTIL_atoint(const char* str) const char* UTIL_skip_string(const char* s, OPTIONAL const char* p) { - const int len_s = strlen(s); + const size_t len_s = strlen(s); if(p not_eq 0 and 0 is_eq strncmp(p, s, len_s)) { p += len_s; @@ -102,7 +103,7 @@ void* UTIL_read_file(const char* filename, int* lenp) FILE* fp = 0; struct stat st; unsigned char* contents = 0; - int contents_len = 0; + size_t contents_len = 0; if(stat(filename, &st) < 0) { @@ -117,7 +118,7 @@ void* UTIL_read_file(const char* filename, int* lenp) return 0; } - contents_len = st.st_size; + contents_len = (size_t)st.st_size; contents = OPENSSL_malloc(contents_len + 1); if(contents is_eq 0) { @@ -181,6 +182,7 @@ bool UTIL_write_file(const char* filename, const void* data, size_t len) bool UTIL_iterate_dir(bool (*fn)(const char* file, void* arg), void* arg, const char* path, bool recursive) { bool res = false; + int i; if(0 is_eq fn or 0 is_eq path) { @@ -188,16 +190,18 @@ bool UTIL_iterate_dir(bool (*fn)(const char* file, void* arg), void* arg, const return false; } - DIR* p_dir = opendir(path); - if(0 == p_dir) + struct dirent** namelist = 0; + const int count = scandir(path, &namelist, 0, alphasort); + if(-1 is_eq count) { LOG(FL_ERR, "cannot open directory '%s'", path); return false; } - struct dirent* p_dirent = readdir(p_dir); - while(0 not_eq p_dirent) + for(i = 0; i < count; ++i) { + const struct dirent* p_dirent = namelist[i]; + char full_path[UTIL_max_path_len]; /* constant 2 takes into account end of string and format string content */ snprintf(full_path, sizeof(full_path), "%.*s/%.*s", UTIL_max_path_len - UTIL_max_name_len - 2, path, @@ -207,7 +211,7 @@ bool UTIL_iterate_dir(bool (*fn)(const char* file, void* arg), void* arg, const memset(&f_stat, 0x00, sizeof(f_stat)); if(-1 is_eq stat(full_path, &f_stat)) { - LOG(FL_INFO, "cannot read status of %s - %s", full_path, strerror(errno)); + LOG(FL_WARN, "cannot read status of %s - %s", full_path, strerror(errno)); } else { @@ -226,16 +230,166 @@ bool UTIL_iterate_dir(bool (*fn)(const char* file, void* arg), void* arg, const } } } - p_dirent = readdir(p_dir); } res = true; err: - closedir(p_dir); + for(i = 0; i < count; ++i) + { + free(namelist[i]); + } + free(namelist); + return res; } +/* + * dn is expected to be in the format "/type0=value0/type1=value1/type2=..." + * where characters may be escaped by '\'. + * The NULL-DN may be given as "/" or "". + */ +/* adapted from OpenSSL:apps/lib/apps.c */ +X509_NAME* UTIL_parse_name(const char* dn, long chtype, bool multirdn) +{ + size_t buflen = strlen(dn) + 1; /* to copy the types and values. + * Due to escaping, the copy can only become shorter */ + char* buf = OPENSSL_malloc(buflen); + size_t max_ne = buflen / (1 + 1) + 1; /* maximum number of name elements */ + const char** ne_types = OPENSSL_malloc(max_ne * sizeof(char*)); + char** ne_values = OPENSSL_malloc(max_ne * sizeof(char*)); + int* mval = OPENSSL_malloc(max_ne * sizeof(int)); + + const char* sp = dn; + char* bp = buf; + int i, ne_num = 0; + + X509_NAME* n = 0; + int nid; + + if(0 is_eq buf or 0 is_eq ne_types or 0 is_eq ne_values or 0 is_eq mval) + { + LOG_err("Malloc error"); + goto error; + } + + /* no multivalued RDN by default */ + mval[ne_num] = 0; + + if(*sp not_eq '\0' and *sp++ not_eq '/') + { /* skip leading '/' */ + LOG(FL_ERR, "DN '%s' does not start with '/'.", dn); + goto error; + } + + while(*sp not_eq '\0') + { + /* collect type */ + ne_types[ne_num] = bp; + /* parse element name */ + while(*sp not_eq '=') + { + if(*sp is_eq '\\') + { /* is there anything to escape in the * type...? */ + if(*++sp not_eq '\0') + { + *bp++ = *sp++; + } + else + { + LOG(FL_ERR, "Escape character at end of DN '%s'", dn); + goto error; + } + } + else if(*sp is_eq '\0') + { + LOG(FL_ERR, "End of string encountered while processing type of DN '%s' element #%d", dn, ne_num); + goto error; + } + else + { + *bp++ = *sp++; + } + } + sp++; + *bp++ = '\0'; + /* parse element value */ + ne_values[ne_num] = bp; + while(*sp not_eq '\0') + { + if(*sp is_eq '\\') + { + if(*++sp not_eq '\0') + { + *bp++ = *sp++; + } + else + { + LOG(FL_ERR, "Escape character at end of DN '%s'", dn); + goto error; + } + } + else if(*sp is_eq '/') + { /* start of next element */ + sp++; + /* no multivalued RDN by default */ + mval[ne_num + 1] = 0; + break; + } + else if(*sp is_eq '+' and multirdn) + { + /* a not escaped + signals a multi-valued RDN */ + sp++; + mval[ne_num + 1] = -1; + break; + } + else + { + *bp++ = *sp++; + } + } + *bp++ = '\0'; + ne_num++; + } + + if(0 is_eq(n = X509_NAME_new())) + { + goto error; + } + + for(i = 0; i < ne_num; i++) + { + if((nid = OBJ_txt2nid(ne_types[i])) is_eq NID_undef) + { + LOG(FL_WARN, "DN '%s' attribute %s has no known NID, skipped", dn, ne_types[i]); + continue; + } + + if(0 is_eq * ne_values[i]) + { + LOG(FL_WARN, "No value provided for DN '%s' attribute %s, skipped", dn, ne_types[i]); + continue; + } + + if(0 is_eq X509_NAME_add_entry_by_NID(n, nid, chtype, (unsigned char*)ne_values[i], -1, -1, mval[i])) + { + ERR_print_errors(bio_err); + LOG(FL_ERR, "Error adding name attribute '/%s=%s'", ne_types[i], ne_values[i]); + X509_NAME_free(n); + n = 0; + goto error; + } + } + +error: + OPENSSL_free(ne_values); + OPENSSL_free(ne_types); + OPENSSL_free(mval); + OPENSSL_free(buf); + return n; +} + + void UTIL_erase_mem(void* dst, size_t len) { if(dst not_eq 0) @@ -325,6 +479,59 @@ int ASN1_TIME_compare(const ASN1_TIME *a, const ASN1_TIME *b) } #endif + +/* start of definitions borrowed from OpenSSL:crypto/cmp/cmp_lib.c */ +static int X509_cmp_from_ptrs(const struct x509_st* const* a, const struct x509_st* const* b) +{ + return X509_cmp(*a, *b); +} + +bool UTIL_sk_X509_add1_cert(STACK_OF(X509) * sk, X509* cert, bool no_duplicate) +{ + if(no_duplicate) + { + sk_X509_set_cmp_func(sk, &X509_cmp_from_ptrs); + if(sk_X509_find(sk, cert) >= 0) + { + return 1; + } + } + if(0 is_eq sk_X509_push(sk, cert)) + { + return 0; + } + return X509_up_ref(cert); +} + +int UTIL_sk_X509_add1_certs(STACK_OF(X509) * sk, OPTIONAL const STACK_OF(X509) * certs, int no_self_signed, + int no_duplicates) +{ + int i = 0; + + if(sk is_eq 0) + { + return 0; + } + + if(certs is_eq 0) + { + return 1; + } + const int n = sk_X509_num(certs); + for(i = 0; i < n; i++) + { + X509* cert = sk_X509_value(certs, i); + if((not no_self_signed or X509_check_issued(cert, cert) not_eq X509_V_OK) + and (not UTIL_sk_X509_add1_cert(sk, cert, no_duplicates))) + { + return 0; + } + } + return 1; +} +/* end of definitions borrowed from OpenSSL:crypto/cmp/cmp_lib.c */ + + /* Copy a NUL-terminated string from the given source * into an optional destination buffer, or calculate how large it needs to be. * Copy at most destination_len bytes including the terminating NUL. @@ -525,18 +732,18 @@ bool UTIL_hex_to_bytes(const char** in_p, unsigned char* out, unsigned int num_o num_out *= HEX_CHARS_PER_BYTE; for(i = 0; i < num_out; i++) { - char c = *((*in_p)++); + int c = *((*in_p)++); if(('0' <= c) and (c <= '9')) { - v = c - '0'; + v = (unsigned int)(c - '0'); } else if(('A' <= c) and (c <= 'F')) { - v = (c - 'A') + (MAX_DIGIT + 1); + v = (unsigned int)(c - 'A') + (MAX_DIGIT + 1); } else if(('a' <= c) and (c <= 'f')) { - v = (c - 'a') + (MAX_DIGIT + 1); + v = (unsigned int)(c - 'a') + (MAX_DIGIT + 1); } else { @@ -571,18 +778,16 @@ int UTIL_base64_encode_to_buf(const unsigned char* data, int len, char* buf, int BIO_write(bio_mem, data, len); (void)BIO_flush(bio_mem); BIO_get_mem_ptr(bio_mem, &bptr); - int encoded_len = bptr->length; + size_t encoded_len = bptr->length; if(encoded_len < buf_size) { memcpy(buf, bptr->data, encoded_len); buf[encoded_len] = '\0'; - } - else - { - encoded_len = -1 - 1; + BIO_free_all(bio_mem); + return encoded_len; } BIO_free_all(bio_mem); - return encoded_len; + return -2; } @@ -612,6 +817,7 @@ unsigned char* UTIL_base64_decode(const char* b64_data, int b64_len, int* decode { (*decoded_len)--; } + assert(*decoded_len >= 0); /* Create a base64 filter */ BIO* bio_b64 = BIO_new(BIO_f_base64()); @@ -638,7 +844,7 @@ unsigned char* UTIL_base64_decode(const char* b64_data, int b64_len, int* decode BIO_push(bio_b64, bio_mem); /* Execute the base 64 decoding and store the output in the decoded_data memory++*/ - unsigned char* decoded_data = OPENSSL_malloc(*decoded_len + 1); + unsigned char* decoded_data = OPENSSL_malloc((size_t)(*decoded_len) + 1); if(decoded_data is_eq 0) { LOG_err("Failure allocate memory for base64 decoding"); @@ -660,3 +866,86 @@ unsigned char* UTIL_base64_decode(const char* b64_data, int b64_len, int* decode BIO_free_all(bio_b64); return decoded_data; } + + +bool UTIL_calculate_icv_impl(uta_ctx* ctx, const unsigned char* data, const size_t data_len, const char* key_dv, + unsigned char* mac) +{ + unsigned char dk[TA_OUTLEN]; + unsigned int md_len; + +/* Derive an ICV key from the trust anchor */ +#ifdef SECUTILS_USE_UTA + const bool uta_res = + uta_getkey(ctx, (const unsigned char*)key_dv, strnlen(key_dv, UTIL_max_path_len), dk, TA_OUTLEN); + if(not uta_res) + { + LOG(FL_ERR, "Could not get key for '%s' from UTA", key_dv); + return false; + } +#else /* SECUTILS_USE_UTA */ + if(ctx not_eq 0) + { + LOG(FL_ERR, "UTA not available"); + return false; + } + /* trivial emulation of trust anchor */ +#if TA_OUTLEN != SHA256_DIGEST_LENGTH +#error Cannot produce KEY with length other than SHA256_DIGEST_LENGTH +#endif + if(0 is_eq SHA256((const unsigned char*)key_dv, strlen(key_dv), dk)) + { + LOG(FL_ERR, "ERROR during SHA256 calculation from: %s", key_dv); + return false; + } + unsigned char tmp = dk[3]; + dk[3] = dk[25]; + dk[25] = dk[10]; + dk[10] = tmp; +#endif /* SECUTILS_USE_UTA */ + + if(0 is_eq HMAC(EVP_sha256(), dk, TA_OUTLEN, data, data_len, mac, &md_len) or md_len < ICV_LEN16) + { + LOG(FL_ERR, "Could not calculate HMAC used as ICV for '%s'", key_dv); + return false; + } + return true; +} + + +bool UTIL_calculate_icv(uta_ctx* ctx, const unsigned char* data, const size_t data_len, const char* key_dv, + unsigned char* icv_out) +{ + unsigned char mac[SHA256_DIGEST_LENGTH]; + + if (0 is_eq ctx) + { + LOG(FL_ERR, "No context"); + return false; + } + + if (0 is_eq data) + { + LOG(FL_ERR, "No data to calculate ICV from"); + return false; + } + + if (0 is_eq key_dv) + { + LOG(FL_ERR, "No key_dv to calculate ICV for"); + return false; + } + + if (0 is_eq icv_out) + { + LOG(FL_ERR, "Invalid output buffer"); + return false; + } + + if (false is_eq UTIL_calculate_icv_impl(ctx, data, data_len, key_dv, mac)) + { + return false; + } + memcpy(icv_out, mac, ICV_LEN16); + return true; +}