away_interval = 30;
/* certfp_method: the method that should be used for computing certificate fingerprints.
- * Acceptable options are sha1, sha256 and sha512. Networks running versions of charybdis
- * prior to charybdis 3.5 MUST use sha1 for certfp_method.
+ * Acceptable options are sha1, sha256, spki_sha256, sha512 and spki_sha512. Networks
+ * running versions of charybdis prior to charybdis 3.5 MUST use sha1 for certfp_method.
+ *
+ * The spki_* variants operate on the SubjectPublicKeyInfo of the certificate, which does
+ * not change unless the private key is changed. This allows the fingerprint to stay
+ * constant even if the certificate is reissued.
*/
certfp_method = sha1;
char *method = data;
if (!rb_strcasecmp(method, "sha1"))
- ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_SHA1;
+ ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_CERT_SHA1;
else if (!rb_strcasecmp(method, "sha256"))
- ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_SHA256;
+ ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_CERT_SHA256;
else if (!rb_strcasecmp(method, "sha512"))
- ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_SHA512;
+ ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_CERT_SHA512;
+ else if (!rb_strcasecmp(method, "spki_sha256"))
+ ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_SPKI_SHA256;
+ else if (!rb_strcasecmp(method, "spki_sha512"))
+ ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_SPKI_SHA512;
else
{
- ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_SHA1;
+ ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_CERT_SHA1;
conf_report_error("Ignoring general::certfp_method -- bogus certfp method %s", method);
}
}
ServerInfo.default_max_clients = MAXCONNECTIONS;
ConfigFileEntry.nicklen = NICKLEN;
- ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_SHA1;
+ ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_CERT_SHA1;
ConfigFileEntry.hide_opers_in_whois = 0;
if (!alias_dict)
#define RB_SSL_CERTFP_LEN 64
/* Methods for certfp */
-#define RB_SSL_CERTFP_METH_SHA1 0
-#define RB_SSL_CERTFP_METH_SHA256 1
-#define RB_SSL_CERTFP_METH_SHA512 2
+/* Digest of full X.509 certificate */
+#define RB_SSL_CERTFP_METH_CERT_SHA1 0x0000
+#define RB_SSL_CERTFP_METH_CERT_SHA256 0x0001
+#define RB_SSL_CERTFP_METH_CERT_SHA512 0x0002
+/* Digest of SubjectPublicKeyInfo (RFC 5280), used by DANE (RFC 6698) */
+#define RB_SSL_CERTFP_METH_SPKI_SHA256 0x1001
+#define RB_SSL_CERTFP_METH_SPKI_SHA512 0x1002
#define RB_SSL_CERTFP_LEN_SHA1 20
#define RB_SSL_CERTFP_LEN_SHA256 32
#include <rb_lib.h>
#include <commio-int.h>
#include <commio-ssl.h>
+#include <stdbool.h>
#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
#if (GNUTLS_VERSION_MAJOR < 3)
# include <gcrypt.h>
uint8_t digest[RB_SSL_CERTFP_LEN * 2];
size_t digest_size;
int len;
+ bool spki = false;
if (gnutls_certificate_type_get(SSL_P(F)) != GNUTLS_CRT_X509)
return 0;
switch(method)
{
- case RB_SSL_CERTFP_METH_SHA1:
+ case RB_SSL_CERTFP_METH_CERT_SHA1:
algo = GNUTLS_DIG_SHA1;
len = RB_SSL_CERTFP_LEN_SHA1;
break;
- case RB_SSL_CERTFP_METH_SHA256:
+ case RB_SSL_CERTFP_METH_SPKI_SHA256:
+ spki = true;
+ case RB_SSL_CERTFP_METH_CERT_SHA256:
algo = GNUTLS_DIG_SHA256;
len = RB_SSL_CERTFP_LEN_SHA256;
break;
- case RB_SSL_CERTFP_METH_SHA512:
+ case RB_SSL_CERTFP_METH_SPKI_SHA512:
+ spki = true;
+ case RB_SSL_CERTFP_METH_CERT_SHA512:
algo = GNUTLS_DIG_SHA512;
len = RB_SSL_CERTFP_LEN_SHA512;
break;
return 0;
}
- if (gnutls_x509_crt_get_fingerprint(cert, algo, digest, &digest_size) < 0)
+ if (!spki)
{
- gnutls_x509_crt_deinit(cert);
- return 0;
+ if (gnutls_x509_crt_get_fingerprint(cert, algo, digest, &digest_size) < 0)
+ len = 0;
+ }
+ else
+ {
+ gnutls_pubkey_t pubkey;
+ unsigned char *der_pubkey = NULL;
+ size_t der_pubkey_len = 0;
+
+ if (gnutls_pubkey_init(&pubkey) == GNUTLS_E_SUCCESS)
+ {
+ if (gnutls_pubkey_import_x509(pubkey, cert, 0) == GNUTLS_E_SUCCESS)
+ {
+ if (gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, der_pubkey, &der_pubkey_len) == GNUTLS_E_SHORT_MEMORY_BUFFER)
+ {
+ der_pubkey = rb_malloc(der_pubkey_len);
+
+ if (gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, der_pubkey, &der_pubkey_len) != GNUTLS_E_SUCCESS)
+ {
+ rb_free(der_pubkey);
+ der_pubkey = NULL;
+ }
+ }
+ }
+
+ gnutls_pubkey_deinit(pubkey);
+ }
+
+ if (der_pubkey)
+ {
+ if (gnutls_hash_fast(algo, der_pubkey, der_pubkey_len, digest) != 0)
+ len = 0;
+
+ rb_free(der_pubkey);
+ }
+ else
+ {
+ len = 0;
+ }
}
- memcpy(certfp, digest, len);
+ if (len)
+ memcpy(certfp, digest, len);
gnutls_x509_crt_deinit(cert);
return len;
#include <rb_lib.h>
#include <commio-int.h>
#include <commio-ssl.h>
+#include <stdbool.h>
#ifdef HAVE_MBEDTLS
const mbedtls_md_info_t *md_info;
mbedtls_md_type_t md_type;
int ret;
+ bool spki = false;
switch (method)
{
- case RB_SSL_CERTFP_METH_SHA1:
+ case RB_SSL_CERTFP_METH_CERT_SHA1:
md_type = MBEDTLS_MD_SHA1;
hashlen = RB_SSL_CERTFP_LEN_SHA1;
break;
- case RB_SSL_CERTFP_METH_SHA256:
+ case RB_SSL_CERTFP_METH_SPKI_SHA256:
+ spki = true;
+ case RB_SSL_CERTFP_METH_CERT_SHA256:
md_type = MBEDTLS_MD_SHA256;
hashlen = RB_SSL_CERTFP_LEN_SHA256;
break;
- case RB_SSL_CERTFP_METH_SHA512:
+ case RB_SSL_CERTFP_METH_SPKI_SHA512:
+ spki = true;
+ case RB_SSL_CERTFP_METH_CERT_SHA512:
md_type = MBEDTLS_MD_SHA512;
hashlen = RB_SSL_CERTFP_LEN_SHA512;
break;
if (md_info == NULL)
return 0;
- if ((ret = mbedtls_md(md_info, peer_cert->raw.p, peer_cert->raw.len, hash)) != 0)
+ if (!spki)
{
- rb_lib_log("rb_get_ssl_certfp: unable to get certfp for F: %p, -0x%x", -ret);
- return 0;
+ if ((ret = mbedtls_md(md_info, peer_cert->raw.p, peer_cert->raw.len, hash)) != 0)
+ {
+ rb_lib_log("rb_get_ssl_certfp: unable to get certfp for F: %p, -0x%x", -ret);
+ hashlen = 0;
+ }
+ }
+ else
+ {
+ const size_t der_pubkey_bufsz = 4096;
+ void *der_pubkey = rb_malloc(der_pubkey_bufsz);
+ int der_pubkey_len;
+
+ der_pubkey_len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)&peer_cert->pk, der_pubkey, der_pubkey_bufsz);
+ if (der_pubkey_len < 0)
+ {
+ rb_lib_log("rb_get_ssl_certfp: unable to get pubkey for F: %p, -0x%x", -der_pubkey_len);
+ hashlen = 0;
+ }
+ else if ((ret = mbedtls_md(md_info, der_pubkey+(der_pubkey_bufsz-der_pubkey_len), der_pubkey_len, hash)) != 0)
+ {
+ rb_lib_log("rb_get_ssl_certfp: unable to get certfp for F: %p, -0x%x", -ret);
+ hashlen = 0;
+ }
+
+ rb_free(der_pubkey);
}
- memcpy(certfp, hash, hashlen);
+ if (hashlen)
+ memcpy(certfp, hash, hashlen);
return hashlen;
}
res == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
res == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)
{
+ const ASN1_ITEM *it;
const EVP_MD *evp;
+ void *data;
unsigned int len;
switch(method)
{
- case RB_SSL_CERTFP_METH_SHA1:
+ case RB_SSL_CERTFP_METH_CERT_SHA1:
+ it = ASN1_ITEM_rptr(X509);
evp = EVP_sha1();
+ data = cert;
len = RB_SSL_CERTFP_LEN_SHA1;
break;
- case RB_SSL_CERTFP_METH_SHA256:
+ case RB_SSL_CERTFP_METH_CERT_SHA256:
+ it = ASN1_ITEM_rptr(X509);
evp = EVP_sha256();
+ data = cert;
len = RB_SSL_CERTFP_LEN_SHA256;
break;
- case RB_SSL_CERTFP_METH_SHA512:
+ case RB_SSL_CERTFP_METH_CERT_SHA512:
+ it = ASN1_ITEM_rptr(X509);
evp = EVP_sha512();
+ data = cert;
+ len = RB_SSL_CERTFP_LEN_SHA512;
+ break;
+ case RB_SSL_CERTFP_METH_SPKI_SHA256:
+ it = ASN1_ITEM_rptr(X509_PUBKEY);
+ evp = EVP_sha256();
+ data = X509_get_X509_PUBKEY(cert);
+ len = RB_SSL_CERTFP_LEN_SHA256;
+ break;
+ case RB_SSL_CERTFP_METH_SPKI_SHA512:
+ it = ASN1_ITEM_rptr(X509_PUBKEY);
+ evp = EVP_sha512();
+ data = X509_get_X509_PUBKEY(cert);
len = RB_SSL_CERTFP_LEN_SHA512;
break;
default:
return 0;
}
- X509_digest(cert, evp, certfp, &len);
+ if (ASN1_item_digest(it, evp, data, certfp, &len) != 1)
+ len = 0;
X509_free(cert);
return len;
}
static void mod_cmd_write_queue(mod_ctl_t * ctl, const void *data, size_t len);
static const char *remote_closed = "Remote host closed the connection";
static bool ssld_ssl_ok;
-static int certfp_method = RB_SSL_CERTFP_METH_SHA1;
+static int certfp_method = RB_SSL_CERTFP_METH_CERT_SHA1;
#ifdef HAVE_LIBZ
static bool zlib_ok = true;
#else