diff --git a/src/connectivity/security/tlsvalidator.cpp b/src/connectivity/security/tlsvalidator.cpp index 09778035bf15747d0725809958e1825e2b2dac7d..93a1484607209f557ba13513ab093309915deac1 100644 --- a/src/connectivity/security/tlsvalidator.cpp +++ b/src/connectivity/security/tlsvalidator.cpp @@ -27,6 +27,7 @@ #include "config.h" #endif +#include <opendht/infohash.h> // for toHex #include <dhtnet/certstore.h> #include "fileutils.h" @@ -116,7 +117,9 @@ const CallbackMatrix1D<TlsValidator::CertificateDetails, TlsValidator, TlsValida /* SERIAL_NUMBER */ &TlsValidator::getSerialNumber, /* ISSUER */ &TlsValidator::getIssuer, /* SUBJECT_KEY_ALGORITHM */ &TlsValidator::getSubjectKeyAlgorithm, + /* SUBJECT_KEY */ &TlsValidator::getSubjectKey, /* CN */ &TlsValidator::getCN, + /* UID */ &TlsValidator::getUID, /* N */ &TlsValidator::getN, /* O */ &TlsValidator::getO, /* SIGNATURE_ALGORITHM */ &TlsValidator::getSignatureAlgorithm, @@ -124,6 +127,10 @@ const CallbackMatrix1D<TlsValidator::CertificateDetails, TlsValidator, TlsValida /* SHA1_FINGERPRINT */ &TlsValidator::getSha1Fingerprint, /* PUBLIC_KEY_ID */ &TlsValidator::getPublicKeyId, /* ISSUER_DN */ &TlsValidator::getIssuerDN, + /* ISSUER_CN */ &TlsValidator::getIssuerCN, + /* ISSUER_UID */ &TlsValidator::getIssuerUID, + /* ISSUER_N */ &TlsValidator::getIssuerN, + /* ISSUER_O */ &TlsValidator::getIssuerO, /* NEXT_EXPECTED_UPDATE_DATE */ &TlsValidator::getIssuerDN, // TODO /* OUTGOING_SERVER */ &TlsValidator::outgoingServer, /* IS_CA */ &TlsValidator::isCA, @@ -189,7 +196,9 @@ const EnumClassNames<TlsValidator::CertificateDetails> TlsValidator::Certificate /* SERIAL_NUMBER */ libjami::Certificate::DetailsNames::SERIAL_NUMBER, /* ISSUER */ libjami::Certificate::DetailsNames::ISSUER, /* SUBJECT_KEY_ALGORITHM */ libjami::Certificate::DetailsNames::SUBJECT_KEY_ALGORITHM, + /* SUBJECT_KEY */ libjami::Certificate::DetailsNames::SUBJECT_KEY, /* CN */ libjami::Certificate::DetailsNames::CN, + /* UID */ libjami::Certificate::DetailsNames::UID, /* N */ libjami::Certificate::DetailsNames::N, /* O */ libjami::Certificate::DetailsNames::O, /* SIGNATURE_ALGORITHM */ libjami::Certificate::DetailsNames::SIGNATURE_ALGORITHM, @@ -197,6 +206,10 @@ const EnumClassNames<TlsValidator::CertificateDetails> TlsValidator::Certificate /* SHA1_FINGERPRINT */ libjami::Certificate::DetailsNames::SHA1_FINGERPRINT, /* PUBLIC_KEY_ID */ libjami::Certificate::DetailsNames::PUBLIC_KEY_ID, /* ISSUER_DN */ libjami::Certificate::DetailsNames::ISSUER_DN, + /* ISSUER_CN */ libjami::Certificate::DetailsNames::ISSUER_CN, + /* ISSUER_UID */ libjami::Certificate::DetailsNames::ISSUER_UID, + /* ISSUER_N */ libjami::Certificate::DetailsNames::ISSUER_N, + /* ISSUER_O */ libjami::Certificate::DetailsNames::ISSUER_O, /* NEXT_EXPECTED_UPDATE_DATE */ libjami::Certificate::DetailsNames::NEXT_EXPECTED_UPDATE_DATE, /* OUTGOING_SERVER */ libjami::Certificate::DetailsNames::OUTGOING_SERVER, /* IS_CA */ libjami::Certificate::DetailsNames::IS_CA, @@ -311,7 +324,7 @@ TlsValidator::getStringValue(const TlsValidator::CertificateCheck check, case CheckValues::PASSED: case CheckValues::FAILED: case CheckValues::UNSUPPORTED: - return CheckValuesNames[result.first]; + return std::string(CheckValuesNames[result.first]); case CheckValues::ISO_DATE: // TODO validate date // return CheckValues::FAILED; @@ -322,7 +335,7 @@ TlsValidator::getStringValue(const TlsValidator::CertificateCheck check, return result.second; default: // Consider any other case (such as forced int->CheckValues casting) as failed - return CheckValuesNames[CheckValues::FAILED]; + return std::string(CheckValuesNames[CheckValues::FAILED]); }; } @@ -339,7 +352,7 @@ TlsValidator::isValid(bool verbose) if (enforcedCheckType[check] == CheckValuesType::BOOLEAN) { if (((this->*(checkCallback[check]))()).first == CheckValues::FAILED) { if (verbose) - JAMI_WARN("Check failed: %s", CertificateCheckNames[check]); + JAMI_WARNING("Check failed: {}", CertificateCheckNames[check]); return false; } } @@ -356,11 +369,11 @@ TlsValidator::getSerializedChecks() std::map<std::string, std::string> ret; if (not certificateFound_) { // Instead of checking `certificateFound` everywhere, handle it once - ret[CertificateCheckNames[CertificateCheck::EXIST]] = getStringValue(CertificateCheck::EXIST, + ret[std::string(CertificateCheckNames[CertificateCheck::EXIST])] = getStringValue(CertificateCheck::EXIST, exist()); } else { for (const CertificateCheck check : Matrix0D<CertificateCheck>()) - ret[CertificateCheckNames[check]] = getStringValue(check, + ret[std::string(CertificateCheckNames[check])] = getStringValue(check, (this->*(checkCallback[check]))()); } @@ -375,7 +388,7 @@ TlsValidator::getSerializedDetails() { std::map<std::string, std::string> ret; if (certificateFound_) { - for (const CertificateDetails det : Matrix0D<CertificateDetails>()) { + for (const CertificateDetails& det : Matrix0D<CertificateDetails>()) { const CheckResult r = (this->*(getterCallback[det]))(); std::string val; // TODO move this to a fuction @@ -393,8 +406,8 @@ TlsValidator::getSerializedDetails() default: val = r.second; break; - }; - ret[CertificateDetailsNames[det]] = val; + } + ret[std::string(CertificateDetailsNames[det])] = val; } } return ret; @@ -414,21 +427,6 @@ checkError(int err, char* copy_buffer, size_t size) : ""); } -/** - * Some fields, such as the binary signature need to be converted to an - * ASCII-hexadecimal representation before being sent to DBus as it will cause the - * process to assert - */ -static std::string -binaryToHex(const uint8_t* input, size_t input_sz) -{ - std::ostringstream ret; - ret << std::hex; - for (size_t i = 0; i < input_sz; i++) - ret << std::setfill('0') << std::setw(2) << (unsigned) input[i]; - return ret.str(); -} - /** * Convert a time_t to an ISO date string */ @@ -451,7 +449,7 @@ checkBinaryError(int err, char* copy_buffer, size_t resultSize) { if (err == GNUTLS_E_SUCCESS) return TlsValidator::CheckResult(TlsValidator::CheckValues::CUSTOM, - binaryToHex(reinterpret_cast<uint8_t*>(copy_buffer), + dht::toHex(reinterpret_cast<uint8_t*>(copy_buffer), resultSize)); else return TlsValidator::CheckResult(TlsValidator::CheckValues::UNSUPPORTED, ""); @@ -1185,8 +1183,9 @@ TlsValidator::getIssuer() TlsValidator::CheckResult TlsValidator::getSubjectKeyAlgorithm() { + unsigned key_length = 0; gnutls_pk_algorithm_t algo = (gnutls_pk_algorithm_t) - gnutls_x509_crt_get_pk_algorithm(x509crt_->cert, nullptr); + gnutls_x509_crt_get_pk_algorithm(x509crt_->cert, &key_length); if (algo < 0) return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, ""); @@ -1196,7 +1195,25 @@ TlsValidator::getSubjectKeyAlgorithm() if (!name) return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, ""); - return TlsValidator::CheckResult(CheckValues::CUSTOM, name); + if (key_length) + return TlsValidator::CheckResult(CheckValues::CUSTOM, fmt::format("{} ({} bits)", name, key_length)); + else + return TlsValidator::CheckResult(CheckValues::CUSTOM, name); +} + +/** + * The subject public key + */ +TlsValidator::CheckResult +TlsValidator::getSubjectKey() +{ + try { + std::vector<uint8_t> data; + x509crt_->getPublicKey().pack(data); + return TlsValidator::CheckResult(CheckValues::CUSTOM, dht::toHex(data)); + } catch (const dht::crypto::CryptoException& e) { + return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, e.what()); + } } /** @@ -1216,6 +1233,22 @@ TlsValidator::getCN() return checkError(err, copy_buffer, resultSize); } +/** + * The 'UID' section of a DN (RFC4514) + */ +TlsValidator::CheckResult +TlsValidator::getUID() +{ + size_t resultSize = sizeof(copy_buffer); + int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert, + GNUTLS_OID_LDAP_UID, + 0, + 0, + copy_buffer, + &resultSize); + return checkError(err, copy_buffer, resultSize); +} + /** * The 'N' section of a DN (RFC4514) */ @@ -1328,6 +1361,70 @@ TlsValidator::getIssuerDN() return checkError(err, copy_buffer, resultSize); } +/** + * If the certificate is not self signed, return the issuer CN + */ +TlsValidator::CheckResult +TlsValidator::getIssuerCN() +{ + size_t resultSize = sizeof(copy_buffer); + int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert, + GNUTLS_OID_X520_COMMON_NAME, + 0, + 0, + copy_buffer, + &resultSize); + return checkError(err, copy_buffer, resultSize); +} + +/** + * If the certificate is not self signed, return the issuer UID + */ +TlsValidator::CheckResult +TlsValidator::getIssuerUID() +{ + size_t resultSize = sizeof(copy_buffer); + int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert, + GNUTLS_OID_LDAP_UID, + 0, + 0, + copy_buffer, + &resultSize); + return checkError(err, copy_buffer, resultSize); +} + +/** + * If the certificate is not self signed, return the issuer N + */ +TlsValidator::CheckResult +TlsValidator::getIssuerN() +{ + size_t resultSize = sizeof(copy_buffer); + int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert, + GNUTLS_OID_X520_NAME, + 0, + 0, + copy_buffer, + &resultSize); + return checkError(err, copy_buffer, resultSize); +} + +/** + * If the certificate is not self signed, return the issuer O + */ +TlsValidator::CheckResult +TlsValidator::getIssuerO() +{ + size_t resultSize = sizeof(copy_buffer); + int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert, + GNUTLS_OID_X520_ORGANIZATION_NAME, + 0, + 0, + copy_buffer, + &resultSize); + return checkError(err, copy_buffer, resultSize); +} + /** * Get the expiration date * diff --git a/src/connectivity/security/tlsvalidator.h b/src/connectivity/security/tlsvalidator.h index 7a1bfd06566ecddd036c5c4b33737b17d3c567e7..f2c89414feed896f2f2be540c1b089f412fb7272 100644 --- a/src/connectivity/security/tlsvalidator.h +++ b/src/connectivity/security/tlsvalidator.h @@ -122,7 +122,9 @@ public: SERIAL_NUMBER, ISSUER, SUBJECT_KEY_ALGORITHM, + SUBJECT_KEY, CN, + UID, N, O, SIGNATURE_ALGORITHM, @@ -130,6 +132,10 @@ public: SHA1_FINGERPRINT, PUBLIC_KEY_ID, ISSUER_DN, + ISSUER_CN, + ISSUER_UID, + ISSUER_N, + ISSUER_O, NEXT_EXPECTED_UPDATE_DATE, OUTGOING_SERVER, /** The hostname/outgoing server used for this certificate */ IS_CA, @@ -233,7 +239,9 @@ public: CheckResult getSerialNumber(); CheckResult getIssuer(); CheckResult getSubjectKeyAlgorithm(); + CheckResult getSubjectKey(); CheckResult getCN(); + CheckResult getUID(); CheckResult getN(); CheckResult getO(); CheckResult getSignatureAlgorithm(); @@ -241,6 +249,10 @@ public: CheckResult getSha1Fingerprint(); CheckResult getPublicKeyId(); CheckResult getIssuerDN(); + CheckResult getIssuerCN(); + CheckResult getIssuerUID(); + CheckResult getIssuerN(); + CheckResult getIssuerO(); CheckResult outgoingServer(); CheckResult isCA(); diff --git a/src/enumclass_utils.h b/src/enumclass_utils.h index 76418bf7cc8bf69ea589c69cc85691dce34ae4b5..02fc4ac60a9f6e96d91518351332170cdd2bca09 100644 --- a/src/enumclass_utils.h +++ b/src/enumclass_utils.h @@ -51,7 +51,7 @@ enum_class_size() template<class Row, typename Value, typename A = Value> struct Matrix1D { - Matrix1D(std::initializer_list<std::initializer_list<Value>> s); + constexpr Matrix1D(std::initializer_list<std::initializer_list<Value>> s); // Row is a built-in type ("int" by default) Value operator[](Row v); @@ -136,7 +136,7 @@ struct Matrix0D * A helper to type to match serializable string to enum elements */ template<class Row> -using EnumClassNames = Matrix1D<Row, const char*>; +using EnumClassNames = Matrix1D<Row, std::string_view>; /** * Create a matrix type with 2 enum class dimensions M[I,J] = V @@ -171,14 +171,14 @@ using CallbackMatrix2D = Matrix2D<Row, Column, void (Class::*)(Args... args)>; */ template<class Row, typename Value, typename Accessor> -Matrix1D<Row, Value, Accessor>::Matrix1D(std::initializer_list<std::initializer_list<Value>> s) +constexpr Matrix1D<Row, Value, Accessor>::Matrix1D(std::initializer_list<std::initializer_list<Value>> s) : data_(*std::begin(s)) { static_assert(std::is_enum<Row>(), "Row has to be an enum class"); static_assert((int) Row::COUNT__ > 0, "Row need a COUNT__ element"); // FIXME C++14, use static_assert and make the ctor constexpr - assert(std::begin(s)->size() + assert(s.begin()->size() == enum_class_size<Row>()); //,"Matrix row have to match the enum class size"); } diff --git a/src/jami/security_const.h b/src/jami/security_const.h index 28a52ee2dfd7b35123468f3c292194172bf1730f..618aa8ec6a60034028cec4711acfab54a864e55a 100644 --- a/src/jami/security_const.h +++ b/src/jami/security_const.h @@ -76,7 +76,9 @@ constexpr static char VERSION_NUMBER[] = "VERSION_NUMBER"; constexpr static char SERIAL_NUMBER[] = "SERIAL_NUMBER"; constexpr static char ISSUER[] = "ISSUER"; constexpr static char SUBJECT_KEY_ALGORITHM[] = "SUBJECT_KEY_ALGORITHM"; +constexpr static char SUBJECT_KEY[] = "SUBJECT_KEY"; constexpr static char CN[] = "CN"; +constexpr static char UID[] = "UID"; constexpr static char N[] = "N"; constexpr static char O[] = "O"; constexpr static char SIGNATURE_ALGORITHM[] = "SIGNATURE_ALGORITHM"; @@ -84,6 +86,10 @@ constexpr static char MD5_FINGERPRINT[] = "MD5_FINGERPRINT"; constexpr static char SHA1_FINGERPRINT[] = "SHA1_FINGERPRINT"; constexpr static char PUBLIC_KEY_ID[] = "PUBLIC_KEY_ID"; constexpr static char ISSUER_DN[] = "ISSUER_DN"; +constexpr static char ISSUER_CN[] = "ISSUER_CN"; +constexpr static char ISSUER_UID[] = "ISSUER_UID"; +constexpr static char ISSUER_N[] = "ISSUER_N"; +constexpr static char ISSUER_O[] = "ISSUER_O"; constexpr static char NEXT_EXPECTED_UPDATE_DATE[] = "NEXT_EXPECTED_UPDATE_DATE"; constexpr static char OUTGOING_SERVER[] = "OUTGOING_SERVER"; constexpr static char IS_CA[] = "IS_CA";