Skip to content
Snippets Groups Projects
Commit 174c45b3 authored by Adrien Béraud's avatar Adrien Béraud
Browse files

tlsvalidator: add missing certificate details

Change-Id: I5094741acc1b395947bac34321cfc05b3e5328ae
parent acb3a44a
No related branches found
No related tags found
No related merge requests found
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "config.h" #include "config.h"
#endif #endif
#include <opendht/infohash.h> // for toHex
#include <dhtnet/certstore.h> #include <dhtnet/certstore.h>
#include "fileutils.h" #include "fileutils.h"
...@@ -116,7 +117,9 @@ const CallbackMatrix1D<TlsValidator::CertificateDetails, TlsValidator, TlsValida ...@@ -116,7 +117,9 @@ const CallbackMatrix1D<TlsValidator::CertificateDetails, TlsValidator, TlsValida
/* SERIAL_NUMBER */ &TlsValidator::getSerialNumber, /* SERIAL_NUMBER */ &TlsValidator::getSerialNumber,
/* ISSUER */ &TlsValidator::getIssuer, /* ISSUER */ &TlsValidator::getIssuer,
/* SUBJECT_KEY_ALGORITHM */ &TlsValidator::getSubjectKeyAlgorithm, /* SUBJECT_KEY_ALGORITHM */ &TlsValidator::getSubjectKeyAlgorithm,
/* SUBJECT_KEY */ &TlsValidator::getSubjectKey,
/* CN */ &TlsValidator::getCN, /* CN */ &TlsValidator::getCN,
/* UID */ &TlsValidator::getUID,
/* N */ &TlsValidator::getN, /* N */ &TlsValidator::getN,
/* O */ &TlsValidator::getO, /* O */ &TlsValidator::getO,
/* SIGNATURE_ALGORITHM */ &TlsValidator::getSignatureAlgorithm, /* SIGNATURE_ALGORITHM */ &TlsValidator::getSignatureAlgorithm,
...@@ -124,6 +127,10 @@ const CallbackMatrix1D<TlsValidator::CertificateDetails, TlsValidator, TlsValida ...@@ -124,6 +127,10 @@ const CallbackMatrix1D<TlsValidator::CertificateDetails, TlsValidator, TlsValida
/* SHA1_FINGERPRINT */ &TlsValidator::getSha1Fingerprint, /* SHA1_FINGERPRINT */ &TlsValidator::getSha1Fingerprint,
/* PUBLIC_KEY_ID */ &TlsValidator::getPublicKeyId, /* PUBLIC_KEY_ID */ &TlsValidator::getPublicKeyId,
/* ISSUER_DN */ &TlsValidator::getIssuerDN, /* 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 /* NEXT_EXPECTED_UPDATE_DATE */ &TlsValidator::getIssuerDN, // TODO
/* OUTGOING_SERVER */ &TlsValidator::outgoingServer, /* OUTGOING_SERVER */ &TlsValidator::outgoingServer,
/* IS_CA */ &TlsValidator::isCA, /* IS_CA */ &TlsValidator::isCA,
...@@ -189,7 +196,9 @@ const EnumClassNames<TlsValidator::CertificateDetails> TlsValidator::Certificate ...@@ -189,7 +196,9 @@ const EnumClassNames<TlsValidator::CertificateDetails> TlsValidator::Certificate
/* SERIAL_NUMBER */ libjami::Certificate::DetailsNames::SERIAL_NUMBER, /* SERIAL_NUMBER */ libjami::Certificate::DetailsNames::SERIAL_NUMBER,
/* ISSUER */ libjami::Certificate::DetailsNames::ISSUER, /* ISSUER */ libjami::Certificate::DetailsNames::ISSUER,
/* SUBJECT_KEY_ALGORITHM */ libjami::Certificate::DetailsNames::SUBJECT_KEY_ALGORITHM, /* SUBJECT_KEY_ALGORITHM */ libjami::Certificate::DetailsNames::SUBJECT_KEY_ALGORITHM,
/* SUBJECT_KEY */ libjami::Certificate::DetailsNames::SUBJECT_KEY,
/* CN */ libjami::Certificate::DetailsNames::CN, /* CN */ libjami::Certificate::DetailsNames::CN,
/* UID */ libjami::Certificate::DetailsNames::UID,
/* N */ libjami::Certificate::DetailsNames::N, /* N */ libjami::Certificate::DetailsNames::N,
/* O */ libjami::Certificate::DetailsNames::O, /* O */ libjami::Certificate::DetailsNames::O,
/* SIGNATURE_ALGORITHM */ libjami::Certificate::DetailsNames::SIGNATURE_ALGORITHM, /* SIGNATURE_ALGORITHM */ libjami::Certificate::DetailsNames::SIGNATURE_ALGORITHM,
...@@ -197,6 +206,10 @@ const EnumClassNames<TlsValidator::CertificateDetails> TlsValidator::Certificate ...@@ -197,6 +206,10 @@ const EnumClassNames<TlsValidator::CertificateDetails> TlsValidator::Certificate
/* SHA1_FINGERPRINT */ libjami::Certificate::DetailsNames::SHA1_FINGERPRINT, /* SHA1_FINGERPRINT */ libjami::Certificate::DetailsNames::SHA1_FINGERPRINT,
/* PUBLIC_KEY_ID */ libjami::Certificate::DetailsNames::PUBLIC_KEY_ID, /* PUBLIC_KEY_ID */ libjami::Certificate::DetailsNames::PUBLIC_KEY_ID,
/* ISSUER_DN */ libjami::Certificate::DetailsNames::ISSUER_DN, /* 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, /* NEXT_EXPECTED_UPDATE_DATE */ libjami::Certificate::DetailsNames::NEXT_EXPECTED_UPDATE_DATE,
/* OUTGOING_SERVER */ libjami::Certificate::DetailsNames::OUTGOING_SERVER, /* OUTGOING_SERVER */ libjami::Certificate::DetailsNames::OUTGOING_SERVER,
/* IS_CA */ libjami::Certificate::DetailsNames::IS_CA, /* IS_CA */ libjami::Certificate::DetailsNames::IS_CA,
...@@ -311,7 +324,7 @@ TlsValidator::getStringValue(const TlsValidator::CertificateCheck check, ...@@ -311,7 +324,7 @@ TlsValidator::getStringValue(const TlsValidator::CertificateCheck check,
case CheckValues::PASSED: case CheckValues::PASSED:
case CheckValues::FAILED: case CheckValues::FAILED:
case CheckValues::UNSUPPORTED: case CheckValues::UNSUPPORTED:
return CheckValuesNames[result.first]; return std::string(CheckValuesNames[result.first]);
case CheckValues::ISO_DATE: case CheckValues::ISO_DATE:
// TODO validate date // TODO validate date
// return CheckValues::FAILED; // return CheckValues::FAILED;
...@@ -322,7 +335,7 @@ TlsValidator::getStringValue(const TlsValidator::CertificateCheck check, ...@@ -322,7 +335,7 @@ TlsValidator::getStringValue(const TlsValidator::CertificateCheck check,
return result.second; return result.second;
default: default:
// Consider any other case (such as forced int->CheckValues casting) as failed // 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) ...@@ -339,7 +352,7 @@ TlsValidator::isValid(bool verbose)
if (enforcedCheckType[check] == CheckValuesType::BOOLEAN) { if (enforcedCheckType[check] == CheckValuesType::BOOLEAN) {
if (((this->*(checkCallback[check]))()).first == CheckValues::FAILED) { if (((this->*(checkCallback[check]))()).first == CheckValues::FAILED) {
if (verbose) if (verbose)
JAMI_WARN("Check failed: %s", CertificateCheckNames[check]); JAMI_WARNING("Check failed: {}", CertificateCheckNames[check]);
return false; return false;
} }
} }
...@@ -356,11 +369,11 @@ TlsValidator::getSerializedChecks() ...@@ -356,11 +369,11 @@ TlsValidator::getSerializedChecks()
std::map<std::string, std::string> ret; std::map<std::string, std::string> ret;
if (not certificateFound_) { if (not certificateFound_) {
// Instead of checking `certificateFound` everywhere, handle it once // 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()); exist());
} else { } else {
for (const CertificateCheck check : Matrix0D<CertificateCheck>()) for (const CertificateCheck check : Matrix0D<CertificateCheck>())
ret[CertificateCheckNames[check]] = getStringValue(check, ret[std::string(CertificateCheckNames[check])] = getStringValue(check,
(this->*(checkCallback[check]))()); (this->*(checkCallback[check]))());
} }
...@@ -375,7 +388,7 @@ TlsValidator::getSerializedDetails() ...@@ -375,7 +388,7 @@ TlsValidator::getSerializedDetails()
{ {
std::map<std::string, std::string> ret; std::map<std::string, std::string> ret;
if (certificateFound_) { if (certificateFound_) {
for (const CertificateDetails det : Matrix0D<CertificateDetails>()) { for (const CertificateDetails& det : Matrix0D<CertificateDetails>()) {
const CheckResult r = (this->*(getterCallback[det]))(); const CheckResult r = (this->*(getterCallback[det]))();
std::string val; std::string val;
// TODO move this to a fuction // TODO move this to a fuction
...@@ -393,8 +406,8 @@ TlsValidator::getSerializedDetails() ...@@ -393,8 +406,8 @@ TlsValidator::getSerializedDetails()
default: default:
val = r.second; val = r.second;
break; break;
}; }
ret[CertificateDetailsNames[det]] = val; ret[std::string(CertificateDetailsNames[det])] = val;
} }
} }
return ret; return ret;
...@@ -414,21 +427,6 @@ checkError(int err, char* copy_buffer, size_t size) ...@@ -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 * Convert a time_t to an ISO date string
*/ */
...@@ -451,7 +449,7 @@ checkBinaryError(int err, char* copy_buffer, size_t resultSize) ...@@ -451,7 +449,7 @@ checkBinaryError(int err, char* copy_buffer, size_t resultSize)
{ {
if (err == GNUTLS_E_SUCCESS) if (err == GNUTLS_E_SUCCESS)
return TlsValidator::CheckResult(TlsValidator::CheckValues::CUSTOM, return TlsValidator::CheckResult(TlsValidator::CheckValues::CUSTOM,
binaryToHex(reinterpret_cast<uint8_t*>(copy_buffer), dht::toHex(reinterpret_cast<uint8_t*>(copy_buffer),
resultSize)); resultSize));
else else
return TlsValidator::CheckResult(TlsValidator::CheckValues::UNSUPPORTED, ""); return TlsValidator::CheckResult(TlsValidator::CheckValues::UNSUPPORTED, "");
...@@ -1185,8 +1183,9 @@ TlsValidator::getIssuer() ...@@ -1185,8 +1183,9 @@ TlsValidator::getIssuer()
TlsValidator::CheckResult TlsValidator::CheckResult
TlsValidator::getSubjectKeyAlgorithm() TlsValidator::getSubjectKeyAlgorithm()
{ {
unsigned key_length = 0;
gnutls_pk_algorithm_t algo = (gnutls_pk_algorithm_t) 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) if (algo < 0)
return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, ""); return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
...@@ -1196,9 +1195,27 @@ TlsValidator::getSubjectKeyAlgorithm() ...@@ -1196,9 +1195,27 @@ TlsValidator::getSubjectKeyAlgorithm()
if (!name) if (!name)
return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, ""); return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
if (key_length)
return TlsValidator::CheckResult(CheckValues::CUSTOM, fmt::format("{} ({} bits)", name, key_length));
else
return TlsValidator::CheckResult(CheckValues::CUSTOM, name); 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());
}
}
/** /**
* The 'CN' section of a DN (RFC4514) * The 'CN' section of a DN (RFC4514)
*/ */
...@@ -1216,6 +1233,22 @@ TlsValidator::getCN() ...@@ -1216,6 +1233,22 @@ TlsValidator::getCN()
return checkError(err, copy_buffer, resultSize); 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) * The 'N' section of a DN (RFC4514)
*/ */
...@@ -1328,6 +1361,70 @@ TlsValidator::getIssuerDN() ...@@ -1328,6 +1361,70 @@ TlsValidator::getIssuerDN()
return checkError(err, copy_buffer, resultSize); 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 * Get the expiration date
* *
......
...@@ -122,7 +122,9 @@ public: ...@@ -122,7 +122,9 @@ public:
SERIAL_NUMBER, SERIAL_NUMBER,
ISSUER, ISSUER,
SUBJECT_KEY_ALGORITHM, SUBJECT_KEY_ALGORITHM,
SUBJECT_KEY,
CN, CN,
UID,
N, N,
O, O,
SIGNATURE_ALGORITHM, SIGNATURE_ALGORITHM,
...@@ -130,6 +132,10 @@ public: ...@@ -130,6 +132,10 @@ public:
SHA1_FINGERPRINT, SHA1_FINGERPRINT,
PUBLIC_KEY_ID, PUBLIC_KEY_ID,
ISSUER_DN, ISSUER_DN,
ISSUER_CN,
ISSUER_UID,
ISSUER_N,
ISSUER_O,
NEXT_EXPECTED_UPDATE_DATE, NEXT_EXPECTED_UPDATE_DATE,
OUTGOING_SERVER, /** The hostname/outgoing server used for this certificate */ OUTGOING_SERVER, /** The hostname/outgoing server used for this certificate */
IS_CA, IS_CA,
...@@ -233,7 +239,9 @@ public: ...@@ -233,7 +239,9 @@ public:
CheckResult getSerialNumber(); CheckResult getSerialNumber();
CheckResult getIssuer(); CheckResult getIssuer();
CheckResult getSubjectKeyAlgorithm(); CheckResult getSubjectKeyAlgorithm();
CheckResult getSubjectKey();
CheckResult getCN(); CheckResult getCN();
CheckResult getUID();
CheckResult getN(); CheckResult getN();
CheckResult getO(); CheckResult getO();
CheckResult getSignatureAlgorithm(); CheckResult getSignatureAlgorithm();
...@@ -241,6 +249,10 @@ public: ...@@ -241,6 +249,10 @@ public:
CheckResult getSha1Fingerprint(); CheckResult getSha1Fingerprint();
CheckResult getPublicKeyId(); CheckResult getPublicKeyId();
CheckResult getIssuerDN(); CheckResult getIssuerDN();
CheckResult getIssuerCN();
CheckResult getIssuerUID();
CheckResult getIssuerN();
CheckResult getIssuerO();
CheckResult outgoingServer(); CheckResult outgoingServer();
CheckResult isCA(); CheckResult isCA();
......
...@@ -51,7 +51,7 @@ enum_class_size() ...@@ -51,7 +51,7 @@ enum_class_size()
template<class Row, typename Value, typename A = Value> template<class Row, typename Value, typename A = Value>
struct Matrix1D 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) // Row is a built-in type ("int" by default)
Value operator[](Row v); Value operator[](Row v);
...@@ -136,7 +136,7 @@ struct Matrix0D ...@@ -136,7 +136,7 @@ struct Matrix0D
* A helper to type to match serializable string to enum elements * A helper to type to match serializable string to enum elements
*/ */
template<class Row> 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 * 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)>; ...@@ -171,14 +171,14 @@ using CallbackMatrix2D = Matrix2D<Row, Column, void (Class::*)(Args... args)>;
*/ */
template<class Row, typename Value, typename Accessor> 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)) : data_(*std::begin(s))
{ {
static_assert(std::is_enum<Row>(), "Row has to be an enum class"); static_assert(std::is_enum<Row>(), "Row has to be an enum class");
static_assert((int) Row::COUNT__ > 0, "Row need a COUNT__ element"); static_assert((int) Row::COUNT__ > 0, "Row need a COUNT__ element");
// FIXME C++14, use static_assert and make the ctor constexpr // 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"); == enum_class_size<Row>()); //,"Matrix row have to match the enum class size");
} }
......
...@@ -76,7 +76,9 @@ constexpr static char VERSION_NUMBER[] = "VERSION_NUMBER"; ...@@ -76,7 +76,9 @@ constexpr static char VERSION_NUMBER[] = "VERSION_NUMBER";
constexpr static char SERIAL_NUMBER[] = "SERIAL_NUMBER"; constexpr static char SERIAL_NUMBER[] = "SERIAL_NUMBER";
constexpr static char ISSUER[] = "ISSUER"; constexpr static char ISSUER[] = "ISSUER";
constexpr static char SUBJECT_KEY_ALGORITHM[] = "SUBJECT_KEY_ALGORITHM"; constexpr static char SUBJECT_KEY_ALGORITHM[] = "SUBJECT_KEY_ALGORITHM";
constexpr static char SUBJECT_KEY[] = "SUBJECT_KEY";
constexpr static char CN[] = "CN"; constexpr static char CN[] = "CN";
constexpr static char UID[] = "UID";
constexpr static char N[] = "N"; constexpr static char N[] = "N";
constexpr static char O[] = "O"; constexpr static char O[] = "O";
constexpr static char SIGNATURE_ALGORITHM[] = "SIGNATURE_ALGORITHM"; constexpr static char SIGNATURE_ALGORITHM[] = "SIGNATURE_ALGORITHM";
...@@ -84,6 +86,10 @@ constexpr static char MD5_FINGERPRINT[] = "MD5_FINGERPRINT"; ...@@ -84,6 +86,10 @@ constexpr static char MD5_FINGERPRINT[] = "MD5_FINGERPRINT";
constexpr static char SHA1_FINGERPRINT[] = "SHA1_FINGERPRINT"; constexpr static char SHA1_FINGERPRINT[] = "SHA1_FINGERPRINT";
constexpr static char PUBLIC_KEY_ID[] = "PUBLIC_KEY_ID"; constexpr static char PUBLIC_KEY_ID[] = "PUBLIC_KEY_ID";
constexpr static char ISSUER_DN[] = "ISSUER_DN"; 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 NEXT_EXPECTED_UPDATE_DATE[] = "NEXT_EXPECTED_UPDATE_DATE";
constexpr static char OUTGOING_SERVER[] = "OUTGOING_SERVER"; constexpr static char OUTGOING_SERVER[] = "OUTGOING_SERVER";
constexpr static char IS_CA[] = "IS_CA"; constexpr static char IS_CA[] = "IS_CA";
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment