diff --git a/doc/example-config/conf.d/10-ssl.conf b/doc/example-config/conf.d/10-ssl.conf index 4dbff044aaa..317526d8fa7 100644 --- a/doc/example-config/conf.d/10-ssl.conf +++ b/doc/example-config/conf.d/10-ssl.conf @@ -47,6 +47,11 @@ ssl_key = ssl_set.ca = NULL; conn->ssl_set.cert.key_password = NULL; conn->ssl_set.cert_username_field = NULL; + conn->ssl_set.cert_username_cea = FALSE; conn->ssl_set.crypto_device = NULL; if (set->ssl_set != NULL) { diff --git a/src/lib-master/master-service-ssl-settings.c b/src/lib-master/master-service-ssl-settings.c index fcc06169e81..3f967efbc42 100644 --- a/src/lib-master/master-service-ssl-settings.c +++ b/src/lib-master/master-service-ssl-settings.c @@ -27,6 +27,7 @@ static const struct setting_define master_service_ssl_setting_defines[] = { DEF(STR, ssl_curve_list), DEF(STR, ssl_min_protocol), DEF(STR, ssl_cert_username_field), + DEF(BOOL, ssl_cert_username_cea), DEF(STR, ssl_crypto_device), DEF(BOOL, ssl_verify_client_cert), DEF(BOOL, ssl_client_require_valid_cert), @@ -50,6 +51,7 @@ static const struct master_service_ssl_settings master_service_ssl_default_setti .ssl_curve_list = "", .ssl_min_protocol = "TLSv1.2", .ssl_cert_username_field = "commonName", + .ssl_cert_username_cea = FALSE, .ssl_crypto_device = "", .ssl_verify_client_cert = FALSE, .ssl_client_require_valid_cert = TRUE, @@ -178,6 +180,7 @@ static void master_service_ssl_common_settings_to_iostream_set( set_r->crypto_device = p_strdup(pool, ssl_set->ssl_crypto_device); set_r->cert_username_field = p_strdup(pool, ssl_set->ssl_cert_username_field); + set_r->cert_username_cea = ssl_set->ssl_cert_username_cea; set_r->verbose = ssl_set->verbose_ssl; set_r->verbose_invalid_cert = ssl_set->verbose_ssl; diff --git a/src/lib-master/master-service-ssl-settings.h b/src/lib-master/master-service-ssl-settings.h index a1347df6ffe..a3567bbfe3b 100644 --- a/src/lib-master/master-service-ssl-settings.h +++ b/src/lib-master/master-service-ssl-settings.h @@ -25,6 +25,7 @@ struct master_service_ssl_settings { bool ssl_require_crl; bool verbose_ssl; bool ssl_prefer_server_ciphers; + bool ssl_cert_username_cea; /* These are derived from ssl_options, not set directly */ struct { diff --git a/src/lib-master/master-service-ssl.c b/src/lib-master/master-service-ssl.c index 8e9c90bf538..95870fb9124 100644 --- a/src/lib-master/master-service-ssl.c +++ b/src/lib-master/master-service-ssl.c @@ -77,6 +77,7 @@ void master_service_ssl_ctx_init(struct master_service *service) ssl_set.dh = server_set->ssl_dh; ssl_set.cert.key_password = server_set->ssl_key_password; ssl_set.cert_username_field = set->ssl_cert_username_field; + ssl_set.cert_username_cea = set->ssl_cert_username_cea; if (server_set->ssl_alt_cert != NULL && *server_set->ssl_alt_cert != '\0') { ssl_set.alt_cert.cert = server_set->ssl_alt_cert; diff --git a/src/lib-ssl-iostream/iostream-openssl.c b/src/lib-ssl-iostream/iostream-openssl.c index d53134902f6..d290fdeacd5 100644 --- a/src/lib-ssl-iostream/iostream-openssl.c +++ b/src/lib-ssl-iostream/iostream-openssl.c @@ -268,6 +268,7 @@ openssl_iostream_set(struct ssl_iostream *ssl_io, set->verbose_invalid_cert || event_want_debug(ssl_io->event); ssl_io->allow_invalid_cert = set->allow_invalid_cert; + ssl_io->username_cea = set->cert_username_cea; return 0; } @@ -811,6 +812,86 @@ openssl_iostream_get_peer_name(struct ssl_iostream *ssl_io) #endif i_assert(x509 != NULL); + if (ssl_io->username_cea) { + + ASN1_INTEGER *serialNumber; + X509_NAME *issuer; + BIGNUM *bn; + char *decimal; + BIO *bio; + + bio = BIO_new(BIO_s_mem()); + if (!bio) { + name = NULL; + goto end; + } + + BIO_puts(bio, "{ serialNumber "); + + serialNumber = X509_get_serialNumber(x509); + if (!serialNumber) { + BIO_free(bio); + name = NULL; + goto end; + } + + bn = ASN1_INTEGER_to_BN(serialNumber, NULL); + if (!bn) { + BIO_free(bio); + name = NULL; + goto end; + } + + decimal = BN_bn2dec(bn); + if (!decimal) { + BIO_free(bio); + BN_free(bn); + name = NULL; + goto end; + } + + BIO_puts(bio, decimal); + + OPENSSL_free(decimal); + BN_free(bn); + + BIO_puts(bio, ", issuer rdnSequence:\""); + + issuer = X509_get_issuer_name(x509); + if (!issuer) { + BIO_free(bio); + name = NULL; + goto end; + } + + X509_NAME_print_ex(bio, issuer, 0, XN_FLAG_RFC2253); + + BIO_puts(bio, "\" }"); + + len = BIO_pending(bio); + if (len < 0) { + name = NULL; + } + else { + name = t_malloc0(len + 1); + if (BIO_read(bio, name, len) != len) { + name = NULL; + } + else if (strlen(name) != (size_t)len) { + /* NUL characters in name. Someone's trying to fake + being another user? Don't allow it. */ + name = NULL; + } + else { + name[len] = 0; + } + } + + BIO_free(bio); + + goto end; + } + len = X509_NAME_get_text_by_NID(X509_get_subject_name(x509), ssl_io->username_nid, NULL, 0); if (len < 0) @@ -827,6 +908,8 @@ openssl_iostream_get_peer_name(struct ssl_iostream *ssl_io) name = NULL; } } + +end: X509_free(x509); return name; diff --git a/src/lib-ssl-iostream/iostream-openssl.h b/src/lib-ssl-iostream/iostream-openssl.h index 065ab47564b..a137716fca4 100644 --- a/src/lib-ssl-iostream/iostream-openssl.h +++ b/src/lib-ssl-iostream/iostream-openssl.h @@ -26,6 +26,7 @@ struct ssl_iostream_context { int username_nid; bool client_ctx:1; + bool username_cea:1; }; struct ssl_iostream { @@ -68,6 +69,7 @@ struct ssl_iostream { bool ostream_flush_waiting_input:1; bool closed:1; bool destroyed:1; + bool username_cea:1; }; extern int dovecot_ssl_extdata_index; diff --git a/src/lib-ssl-iostream/iostream-ssl.h b/src/lib-ssl-iostream/iostream-ssl.h index cf0e0737de4..24b23b04ad4 100644 --- a/src/lib-ssl-iostream/iostream-ssl.h +++ b/src/lib-ssl-iostream/iostream-ssl.h @@ -34,6 +34,7 @@ struct ssl_iostream_settings { bool prefer_server_ciphers; /* both */ bool compression; /* context-only */ bool tickets; /* context-only */ + bool cert_username_cea; /* both */ }; /* Load SSL module */