TLS: Partially backport the SubjectPublicKeyInfo digesting functionality

This backports the code responsible for SPKI digests from release/4.

It also adjusts doc/reference.conf to note that SPKI digests are now
supported, and how to generate them. It does NOT backport the mkfingerprint
program -- the instructions in reference.conf are sufficient. I am ofcourse
open to anyone else backporting the program, but I don't see the need.
This commit is contained in:
Aaron Jones 2016-11-15 12:30:09 +00:00
parent 0a9598655c
commit 2afd965b21
No known key found for this signature in database
GPG key ID: EC6F86EE9CD840B5
10 changed files with 282 additions and 118 deletions

View file

@ -1316,11 +1316,25 @@ 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. These fingerprints will be prefixed with
* "SPKI:SHA2-256:" or "SPKI:SHA2-512:" depending on the hash type. These fingerprints
* are not supported on servers running charybdis 3.5.3 or earlier.
*
* To generate a fingerprint from a certificate file, run the following:
* $ openssl x509 -outform DER < your.crt | sha1sum (or sha256sum, or sha512sum)
*
* To generate a SPKI SHA-256 fingerprint, run the following:
* $ openssl x509 -pubkey -noout < your.crt | grep -v 'PUBLIC KEY' | base64 -d | \
* sha256sum | awk '{ print $1 }' | sed -r -e 's/^/SPKI:SHA2-256:/'
*
* To generate a SPKI SHA-512 fingerprint, run the following:
* $ openssl x509 -pubkey -noout < your.crt | grep -v 'PUBLIC KEY' | base64 -d | \
* sha512sum | awk '{ print $1 }' | sed -r -e 's/^/SPKI:SHA2-512:/'
*/
certfp_method = sha1;
};

View file

@ -32,7 +32,7 @@ int start_ssldaemon(int count, const char *ssl_cert, const char *ssl_private_key
ssl_ctl_t *start_ssld_accept(rb_fde_t *sslF, rb_fde_t *plainF, uint32_t id);
ssl_ctl_t *start_ssld_connect(rb_fde_t *sslF, rb_fde_t *plainF, uint32_t id);
void start_zlib_session(void *data);
void send_new_ssl_certs(const char *ssl_cert, const char *ssl_private_key, const char *ssl_dh_params, const char *ssl_cipher_list);
void send_new_ssl_certs(const char *ssl_cert, const char *ssl_private_key, const char *ssl_dh_params, const char *ssl_cipher_list, const int method);
void ssld_decrement_clicount(ssl_ctl_t *ctl);
int get_ssld_count(void);

View file

@ -103,9 +103,27 @@ 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
/* Names for certfp */
#define CERTFP_NAME_CERT_SHA1 "sha1"
#define CERTFP_PREFIX_CERT_SHA1 ""
#define CERTFP_NAME_CERT_SHA256 "sha256"
#define CERTFP_PREFIX_CERT_SHA256 ""
#define CERTFP_NAME_CERT_SHA512 "sha512"
#define CERTFP_PREFIX_CERT_SHA512 ""
/* These prefixes are copied from RFC 7218 */
#define CERTFP_NAME_SPKI_SHA256 "spki_sha256"
#define CERTFP_PREFIX_SPKI_SHA256 "SPKI:SHA2-256:"
#define CERTFP_NAME_SPKI_SHA512 "spki_sha512"
#define CERTFP_PREFIX_SPKI_SHA512 "SPKI:SHA2-512:"
#define RB_SSL_CERTFP_LEN_SHA1 20
#define RB_SSL_CERTFP_LEN_SHA256 32

View file

@ -367,6 +367,73 @@ rb_load_file_into_datum_t(const char *const file)
return datum;
}
static int
make_certfp(gnutls_x509_crt_t cert, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
{
int hashlen;
gnutls_digest_algorithm_t md_type;
int spki = 0;
switch(method)
{
case RB_SSL_CERTFP_METH_CERT_SHA1:
hashlen = RB_SSL_CERTFP_LEN_SHA1;
md_type = GNUTLS_DIG_SHA1;
break;
case RB_SSL_CERTFP_METH_SPKI_SHA256:
spki = 1;
case RB_SSL_CERTFP_METH_CERT_SHA256:
hashlen = RB_SSL_CERTFP_LEN_SHA256;
md_type = GNUTLS_DIG_SHA256;
break;
case RB_SSL_CERTFP_METH_SPKI_SHA512:
spki = 1;
case RB_SSL_CERTFP_METH_CERT_SHA512:
hashlen = RB_SSL_CERTFP_LEN_SHA512;
md_type = GNUTLS_DIG_SHA512;
break;
default:
return 0;
}
if(! spki)
{
size_t digest_size = (size_t) hashlen;
if(gnutls_x509_crt_get_fingerprint(cert, md_type, certfp, &digest_size) != 0)
return 0;
return hashlen;
}
gnutls_pubkey_t pubkey;
if(gnutls_pubkey_init(&pubkey) != 0)
return 0;
if(gnutls_pubkey_import_x509(pubkey, cert, 0) != 0)
{
gnutls_pubkey_deinit(pubkey);
return 0;
}
unsigned char derkey[262144]; // Should be big enough to hold any SubjectPublicKeyInfo structure
size_t derkey_len = sizeof derkey;
if(gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, derkey, &derkey_len) != 0)
{
gnutls_pubkey_deinit(pubkey);
return 0;
}
gnutls_pubkey_deinit(pubkey);
if(gnutls_hash_fast(md_type, derkey, derkey_len, certfp) != 0)
return 0;
return hashlen;
}
/*
@ -589,53 +656,29 @@ rb_get_ssl_certfp(rb_fde_t *const F, uint8_t certfp[const RB_SSL_CERTFP_LEN], co
if(F == NULL || F->ssl == NULL)
return 0;
gnutls_digest_algorithm_t md_type;
switch(method)
{
case RB_SSL_CERTFP_METH_SHA1:
md_type = GNUTLS_DIG_SHA1;
break;
case RB_SSL_CERTFP_METH_SHA256:
md_type = GNUTLS_DIG_SHA256;
break;
case RB_SSL_CERTFP_METH_SHA512:
md_type = GNUTLS_DIG_SHA512;
break;
default:
return 0;
}
if(gnutls_certificate_type_get(SSL_P(F)) != GNUTLS_CRT_X509)
return 0;
unsigned int cert_list_size = 0;
const gnutls_datum_t *const cert_list = gnutls_certificate_get_peers(SSL_P(F), &cert_list_size);
if(cert_list == NULL || cert_list_size <= 0)
if(cert_list == NULL || cert_list_size < 1)
return 0;
gnutls_x509_crt_t peer_cert;
if(gnutls_x509_crt_init(&peer_cert) != GNUTLS_E_SUCCESS)
if(gnutls_x509_crt_init(&peer_cert) != 0)
return 0;
if(gnutls_x509_crt_import(peer_cert, &cert_list[0], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS)
if(gnutls_x509_crt_import(peer_cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
{
gnutls_x509_crt_deinit(peer_cert);
return 0;
}
int ret;
size_t hashlen;
if((ret = gnutls_x509_crt_get_fingerprint(peer_cert, md_type, certfp, &hashlen)) != 0)
{
rb_lib_log("%s: gnutls_x509_crt_get_fingerprint: %s", __func__, rb_ssl_strerror(ret));
gnutls_x509_crt_deinit(peer_cert);
return 0;
}
const int len = make_certfp(peer_cert, certfp, method);
gnutls_x509_crt_deinit(peer_cert);
return (int) hashlen;
return len;
}
void

View file

@ -325,6 +325,65 @@ rb_ssl_strerror(const int err)
return errbuf;
}
static int
rb_make_certfp(const mbedtls_x509_crt *const peer_cert, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
{
size_t hashlen = 0;
mbedtls_md_type_t md_type;
int spki = 0;
switch(method)
{
case RB_SSL_CERTFP_METH_CERT_SHA1:
md_type = MBEDTLS_MD_SHA1;
hashlen = RB_SSL_CERTFP_LEN_SHA1;
break;
case RB_SSL_CERTFP_METH_SPKI_SHA256:
spki = 1;
case RB_SSL_CERTFP_METH_CERT_SHA256:
md_type = MBEDTLS_MD_SHA256;
hashlen = RB_SSL_CERTFP_LEN_SHA256;
break;
case RB_SSL_CERTFP_METH_SPKI_SHA512:
spki = 1;
case RB_SSL_CERTFP_METH_CERT_SHA512:
md_type = MBEDTLS_MD_SHA512;
hashlen = RB_SSL_CERTFP_LEN_SHA512;
break;
default:
return 0;
}
const mbedtls_md_info_t *const md_info = mbedtls_md_info_from_type(md_type);
if(md_info == NULL)
return 0;
int ret;
void* data = peer_cert->raw.p;
size_t datalen = peer_cert->raw.len;
if(spki)
{
unsigned char der_pubkey[8192];
if((ret = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)&peer_cert->pk,
der_pubkey, sizeof der_pubkey)) < 0)
{
rb_lib_log("%s: pk_write_pubkey_der: %s", __func__, rb_ssl_strerror(ret));
return 0;
}
data = der_pubkey + (sizeof(der_pubkey) - (size_t)ret);
datalen = (size_t)ret;
}
if((ret = mbedtls_md(md_info, data, datalen, certfp)) != 0)
{
rb_lib_log("%s: mbedtls_md: %s", __func__, rb_ssl_strerror(ret));
return 0;
}
return (int) hashlen;
}
/*
@ -560,43 +619,12 @@ rb_get_ssl_certfp(rb_fde_t *const F, uint8_t certfp[const RB_SSL_CERTFP_LEN], co
if(F == NULL || F->ssl == NULL)
return 0;
mbedtls_md_type_t md_type;
int hashlen;
switch(method)
{
case RB_SSL_CERTFP_METH_SHA1:
md_type = MBEDTLS_MD_SHA1;
hashlen = RB_SSL_CERTFP_LEN_SHA1;
break;
case RB_SSL_CERTFP_METH_SHA256:
md_type = MBEDTLS_MD_SHA256;
hashlen = RB_SSL_CERTFP_LEN_SHA256;
break;
case RB_SSL_CERTFP_METH_SHA512:
md_type = MBEDTLS_MD_SHA512;
hashlen = RB_SSL_CERTFP_LEN_SHA512;
break;
default:
return 0;
}
const mbedtls_x509_crt *const peer_cert = mbedtls_ssl_get_peer_cert(SSL_P(F));
if(peer_cert == NULL)
return 0;
const mbedtls_md_info_t *const md_info = mbedtls_md_info_from_type(md_type);
if(md_info == NULL)
return 0;
int ret;
if((ret = mbedtls_md(md_info, peer_cert->raw.p, peer_cert->raw.len, certfp)) != 0)
{
rb_lib_log("%s: mbedtls_md: %s", __func__, rb_ssl_strerror(ret));
return 0;
}
return hashlen;
return rb_make_certfp(peer_cert, certfp, method);
}
void

View file

@ -245,6 +245,59 @@ rb_ssl_read_or_write(const int r_or_w, rb_fde_t *const F, void *const rbuf, cons
return ret;
}
static int
make_certfp(X509 *const cert, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
{
unsigned int hashlen = 0;
const EVP_MD *md_type = NULL;
const ASN1_ITEM *item = NULL;
void *data = NULL;
switch(method)
{
case RB_SSL_CERTFP_METH_CERT_SHA1:
hashlen = RB_SSL_CERTFP_LEN_SHA1;
md_type = EVP_sha1();
item = ASN1_ITEM_rptr(X509);
data = cert;
break;
case RB_SSL_CERTFP_METH_CERT_SHA256:
hashlen = RB_SSL_CERTFP_LEN_SHA256;
md_type = EVP_sha256();
item = ASN1_ITEM_rptr(X509);
data = cert;
break;
case RB_SSL_CERTFP_METH_CERT_SHA512:
hashlen = RB_SSL_CERTFP_LEN_SHA512;
md_type = EVP_sha512();
item = ASN1_ITEM_rptr(X509);
data = cert;
break;
case RB_SSL_CERTFP_METH_SPKI_SHA256:
hashlen = RB_SSL_CERTFP_LEN_SHA256;
md_type = EVP_sha256();
item = ASN1_ITEM_rptr(X509_PUBKEY);
data = X509_get_X509_PUBKEY(cert);
break;
case RB_SSL_CERTFP_METH_SPKI_SHA512:
hashlen = RB_SSL_CERTFP_LEN_SHA512;
md_type = EVP_sha512();
item = ASN1_ITEM_rptr(X509_PUBKEY);
data = X509_get_X509_PUBKEY(cert);
break;
default:
return 0;
}
if(ASN1_item_digest(item, md_type, data, certfp, &hashlen) != 1)
{
rb_lib_log("%s: ASN1_item_digest: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
return 0;
}
return (int) hashlen;
}
/*
@ -470,31 +523,12 @@ rb_get_ssl_certfp(rb_fde_t *const F, uint8_t certfp[const RB_SSL_CERTFP_LEN], co
if(F == NULL || F->ssl == NULL)
return 0;
const EVP_MD *md_type;
unsigned int hashlen;
switch(method)
{
case RB_SSL_CERTFP_METH_SHA1:
md_type = EVP_sha1();
hashlen = RB_SSL_CERTFP_LEN_SHA1;
break;
case RB_SSL_CERTFP_METH_SHA256:
md_type = EVP_sha256();
hashlen = RB_SSL_CERTFP_LEN_SHA256;
break;
case RB_SSL_CERTFP_METH_SHA512:
md_type = EVP_sha512();
hashlen = RB_SSL_CERTFP_LEN_SHA512;
break;
default:
return 0;
}
X509 *const peer_cert = SSL_get_peer_certificate(SSL_P(F));
if(peer_cert == NULL)
return 0;
int len = 0;
switch(SSL_get_verify_result(SSL_P(F)))
{
case X509_V_OK:
@ -503,16 +537,11 @@ rb_get_ssl_certfp(rb_fde_t *const F, uint8_t certfp[const RB_SSL_CERTFP_LEN], co
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
case X509_V_ERR_CERT_UNTRUSTED:
break;
len = make_certfp(peer_cert, certfp, method);
default:
X509_free(peer_cert);
return 0;
return len;
}
X509_digest(peer_cert, md_type, certfp, &hashlen);
X509_free(peer_cert);
return (int) hashlen;
}
void

View file

@ -1650,15 +1650,19 @@ conf_set_general_certfp_method(void *data)
{
char *method = data;
if (!strcasecmp(method, "sha1"))
ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_SHA1;
else if (!strcasecmp(method, "sha256"))
ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_SHA256;
else if (!strcasecmp(method, "sha512"))
ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_SHA512;
if (!strcasecmp(method, CERTFP_NAME_CERT_SHA1))
ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_CERT_SHA1;
else if (!strcasecmp(method, CERTFP_NAME_CERT_SHA256))
ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_CERT_SHA256;
else if (!strcasecmp(method, CERTFP_NAME_CERT_SHA512))
ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_CERT_SHA512;
else if (!strcasecmp(method, CERTFP_NAME_SPKI_SHA256))
ConfigFileEntry.certfp_method = RB_SSL_CERTFP_METH_SPKI_SHA256;
else if (!strcasecmp(method, CERTFP_NAME_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

@ -822,7 +822,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;
if (!alias_dict)
alias_dict = irc_dictionary_create(strcasecmp);
@ -877,7 +877,7 @@ validate_conf(void)
ircd_ssl_ok = 0;
} else {
ircd_ssl_ok = 1;
send_new_ssl_certs(ServerInfo.ssl_cert, ServerInfo.ssl_private_key, ServerInfo.ssl_dh_params, ServerInfo.ssl_cipher_list);
send_new_ssl_certs(ServerInfo.ssl_cert, ServerInfo.ssl_private_key, ServerInfo.ssl_dh_params, ServerInfo.ssl_cipher_list, ConfigFileEntry.certfp_method);
}
if(ServerInfo.ssld_count > get_ssld_count())

View file

@ -409,24 +409,50 @@ ssl_process_certfp(ssl_ctl_t * ctl, ssl_ctl_buf_t * ctl_buf)
{
struct Client *client_p;
uint32_t fd;
uint32_t certfp_method;
uint32_t len;
uint8_t *certfp;
char *certfp_string;
int i;
const char *method_string;
int method_len;
if(ctl_buf->buflen > 9 + RB_SSL_CERTFP_LEN)
if(ctl_buf->buflen > 13 + RB_SSL_CERTFP_LEN)
return; /* bogus message..drop it.. XXX should warn here */
fd = buf_to_uint32(&ctl_buf->buf[1]);
len = buf_to_uint32(&ctl_buf->buf[5]);
certfp = (uint8_t *)&ctl_buf->buf[9];
certfp_method = buf_to_uint32(&ctl_buf->buf[5]);
len = buf_to_uint32(&ctl_buf->buf[9]);
certfp = (uint8_t *)&ctl_buf->buf[13];
client_p = find_cli_connid_hash(fd);
if(client_p == NULL)
return;
switch (certfp_method) {
case RB_SSL_CERTFP_METH_CERT_SHA1:
method_string = CERTFP_PREFIX_CERT_SHA1;
break;
case RB_SSL_CERTFP_METH_CERT_SHA256:
method_string = CERTFP_PREFIX_CERT_SHA256;
break;
case RB_SSL_CERTFP_METH_CERT_SHA512:
method_string = CERTFP_PREFIX_CERT_SHA512;
break;
case RB_SSL_CERTFP_METH_SPKI_SHA256:
method_string = CERTFP_PREFIX_SPKI_SHA256;
break;
case RB_SSL_CERTFP_METH_SPKI_SHA512:
method_string = CERTFP_PREFIX_SPKI_SHA512;
break;
default:
return;
}
method_len = strlen(method_string);
rb_free(client_p->certfp);
certfp_string = rb_malloc(len * 2 + 1);
for(i = 0; i < len; i++)
rb_snprintf(certfp_string + 2 * i, 3, "%02x",
certfp_string = rb_malloc(method_len + len * 2 + 1);
rb_strlcpy(certfp_string, method_string, method_len + len * 2 + 1);
for(uint32_t i = 0; i < len; i++)
rb_snprintf(certfp_string + method_len + 2 * i, 3, "%02x",
certfp[i]);
client_p->certfp = certfp_string;
}
@ -674,7 +700,7 @@ send_certfp_method(ssl_ctl_t *ctl, int method)
}
void
send_new_ssl_certs(const char *ssl_cert, const char *ssl_private_key, const char *ssl_dh_params, const char *ssl_cipher_list)
send_new_ssl_certs(const char *ssl_cert, const char *ssl_private_key, const char *ssl_dh_params, const char *ssl_cipher_list, const int method)
{
rb_dlink_node *ptr;
if(ssl_cert == NULL)
@ -685,6 +711,7 @@ send_new_ssl_certs(const char *ssl_cert, const char *ssl_private_key, const char
RB_DLINK_FOREACH(ptr, ssl_daemons.head)
{
ssl_ctl_t *ctl = ptr->data;
send_certfp_method(ctl, method);
send_new_ssl_certs_one(ctl, ssl_cert, ssl_private_key, ssl_dh_params, ssl_cipher_list);
}
}

View file

@ -154,7 +154,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 int 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 int ssld_zlib_ok = 1;
#else
@ -688,17 +688,18 @@ ssl_send_cipher(conn_t *conn)
static void
ssl_send_certfp(conn_t *conn)
{
uint8_t buf[9 + RB_SSL_CERTFP_LEN];
uint8_t buf[13 + RB_SSL_CERTFP_LEN];
int len = rb_get_ssl_certfp(conn->mod_fd, &buf[9], certfp_method);
int len = rb_get_ssl_certfp(conn->mod_fd, &buf[13], certfp_method);
if (!len)
return;
lrb_assert(len <= RB_SSL_CERTFP_LEN);
buf[0] = 'F';
uint32_to_buf(&buf[1], conn->id);
uint32_to_buf(&buf[5], len);
mod_cmd_write_queue(conn->ctl, buf, 9 + len);
uint32_to_buf(&buf[5], certfp_method);
uint32_to_buf(&buf[9], len);
mod_cmd_write_queue(conn->ctl, buf, 13 + len);
}
static void