ssld: Add new certfp_methods spki_sha256 and spki_sha512

These operate on the SubjectPublicKeyInfo of the certificate, which does
change unless the private key is changed. This allows the fingerprint to
stay constant even if the certificate is reissued.

(The same fingerprint is also used by DANE)
This commit is contained in:
Simon Arlott 2016-04-23 22:51:05 +01:00
parent 9e98a842e3
commit cf430c1a40
No known key found for this signature in database
GPG key ID: C8975F2043CA5D24
8 changed files with 137 additions and 29 deletions

View file

@ -1405,8 +1405,12 @@ general {
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;

View file

@ -1668,14 +1668,18 @@ conf_set_general_certfp_method(void *data)
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);
}
}

View file

@ -813,7 +813,7 @@ set_default_conf(void)
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)

View file

@ -102,9 +102,13 @@ void rb_note(rb_fde_t *, const char *);
#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

View file

@ -26,10 +26,12 @@
#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>
@ -603,6 +605,7 @@ rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
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;
@ -626,15 +629,19 @@ rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
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;
@ -642,13 +649,51 @@ rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
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;

View file

@ -27,6 +27,7 @@
#include <rb_lib.h>
#include <commio-int.h>
#include <commio-ssl.h>
#include <stdbool.h>
#ifdef HAVE_MBEDTLS
@ -545,18 +546,23 @@ rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
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;
@ -572,13 +578,37 @@ rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
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;
}

View file

@ -706,28 +706,49 @@ rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
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;
}

View file

@ -152,7 +152,7 @@ static void conn_plain_read_shutdown_cb(rb_fde_t *fd, void *data);
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