summaryrefslogtreecommitdiff
path: root/res/res_rtp_asterisk.c
diff options
context:
space:
mode:
authorSean Bright <sean.bright@gmail.com>2017-09-29 14:50:17 +0000
committerJoshua Colp <jcolp@digium.com>2017-11-06 08:11:48 -0500
commit04d3785a798e984a5f5d43ec5f124a9b30a58b9e (patch)
tree06ac1ee5a7f04a5edf27e556e0825a31d9dd5c5f /res/res_rtp_asterisk.c
parentbe5b7b2076a577c2a994e752b152c5242fb29ce7 (diff)
dtls: Add support for ephemeral DTLS certificates.
This mimics the behavior of Chrome and Firefox and creates an ephemeral X.509 certificate for each DTLS session. Currently, the only supported key type is ECDSA because of its faster generation time, but other key types can be added in the future as necessary. ASTERISK-27395 Change-Id: I5122e5f4b83c6320cc17407a187fcf491daf30b4
Diffstat (limited to 'res/res_rtp_asterisk.c')
-rw-r--r--res/res_rtp_asterisk.c335
1 files changed, 274 insertions, 61 deletions
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index f5d913458..15ca1503b 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -1593,46 +1593,31 @@ static int dtls_setup_rtcp(struct ast_rtp_instance *instance)
return dtls_details_initialize(&rtp->rtcp->dtls, rtp->ssl_ctx, rtp->dtls.dtls_setup);
}
-/*! \pre instance is locked */
-static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg)
+static const SSL_METHOD *get_dtls_method(void)
{
- struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
- int res;
-#ifdef HAVE_OPENSSL_EC
- EC_KEY *ecdh;
-#endif
-
- if (!dtls_cfg->enabled) {
- return 0;
- }
-
- if (!ast_rtp_engine_srtp_is_registered()) {
- ast_log(LOG_ERROR, "SRTP support module is not loaded or available. Try loading res_srtp.so.\n");
- return -1;
- }
-
- if (rtp->ssl_ctx) {
- return 0;
- }
-
#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
- rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method());
+ return DTLSv1_method();
#else
- rtp->ssl_ctx = SSL_CTX_new(DTLS_method());
+ return DTLS_method();
#endif
- if (!rtp->ssl_ctx) {
- return -1;
- }
+}
- SSL_CTX_set_read_ahead(rtp->ssl_ctx, 1);
+struct dtls_cert_info {
+ EVP_PKEY *private_key;
+ X509 *certificate;
+};
#ifdef HAVE_OPENSSL_EC
+static void configure_dhparams(const struct ast_rtp *rtp, const struct ast_rtp_dtls_cfg *dtls_cfg)
+{
+ EC_KEY *ecdh;
+
if (!ast_strlen_zero(dtls_cfg->pvtfile)) {
BIO *bio = BIO_new_file(dtls_cfg->pvtfile, "r");
- if (bio != NULL) {
+ if (bio) {
DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
- if (dh != NULL) {
+ if (dh) {
if (SSL_CTX_set_tmp_dh(rtp->ssl_ctx, dh)) {
long options = SSL_OP_CIPHER_SERVER_PREFERENCE |
SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE;
@@ -1644,9 +1629,10 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
BIO_free(bio);
}
}
+
/* enables AES-128 ciphers, to get AES-256 use NID_secp384r1 */
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- if (ecdh != NULL) {
+ if (ecdh) {
if (SSL_CTX_set_tmp_ecdh(rtp->ssl_ctx, ecdh)) {
#ifndef SSL_CTRL_SET_ECDH_AUTO
#define SSL_CTRL_SET_ECDH_AUTO 94
@@ -1660,8 +1646,251 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
}
EC_KEY_free(ecdh);
}
+}
+
+static int create_ephemeral_ec_keypair(EVP_PKEY **keypair)
+{
+ EC_KEY *eckey = NULL;
+ EC_GROUP *group = NULL;
+
+ group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+ if (!group) {
+ goto error;
+ }
+
+ EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
+ EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);
+
+ eckey = EC_KEY_new();
+ if (!eckey) {
+ goto error;
+ }
+
+ if (!EC_KEY_set_group(eckey, group)) {
+ goto error;
+ }
+
+ if (!EC_KEY_generate_key(eckey)) {
+ goto error;
+ }
+
+ *keypair = EVP_PKEY_new();
+ if (!*keypair) {
+ goto error;
+ }
+
+ EVP_PKEY_assign_EC_KEY(*keypair, eckey);
+ EC_GROUP_free(group);
+
+ return 0;
+
+error:
+ EC_KEY_free(eckey);
+ EC_GROUP_free(group);
+
+ return -1;
+}
+
+/* From OpenSSL's x509 command */
+#define SERIAL_RAND_BITS 159
+
+static int create_ephemeral_certificate(EVP_PKEY *keypair, X509 **certificate)
+{
+ X509 *cert = NULL;
+ BIGNUM *serial = NULL;
+ X509_NAME *name = NULL;
+
+ cert = X509_new();
+ if (!cert) {
+ goto error;
+ }
+
+ if (!X509_set_version(cert, 2)) {
+ goto error;
+ }
+
+ /* Set the public key */
+ X509_set_pubkey(cert, keypair);
+
+ /* Generate a random serial number */
+ if (!(serial = BN_new())
+ || !BN_rand(serial, SERIAL_RAND_BITS, -1, 0)
+ || !BN_to_ASN1_INTEGER(serial, X509_get_serialNumber(cert))) {
+ goto error;
+ }
+
+ /*
+ * Validity period - Current Chrome & Firefox make it 31 days starting
+ * with yesterday at the current time, so we will do the same.
+ */
+ if (!X509_time_adj_ex(X509_get_notBefore(cert), -1, 0, NULL)
+ || !X509_time_adj_ex(X509_get_notAfter(cert), 30, 0, NULL)) {
+ goto error;
+ }
+
+ /* Set the name and issuer */
+ if (!(name = X509_get_subject_name(cert))
+ || !X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
+ (unsigned char *) "asterisk", -1, -1, 0)
+ || !X509_set_issuer_name(cert, name)) {
+ goto error;
+ }
+
+ /* Sign it */
+ if (!X509_sign(cert, keypair, EVP_sha256())) {
+ goto error;
+ }
+
+ *certificate = cert;
+
+ return 0;
+
+error:
+ BN_free(serial);
+ X509_free(cert);
+
+ return -1;
+}
+
+static int create_certificate_ephemeral(struct ast_rtp_instance *instance,
+ const struct ast_rtp_dtls_cfg *dtls_cfg,
+ struct dtls_cert_info *cert_info)
+{
+ /* Make sure these are initialized */
+ cert_info->private_key = NULL;
+ cert_info->certificate = NULL;
+
+ if (create_ephemeral_ec_keypair(&cert_info->private_key)) {
+ ast_log(LOG_ERROR, "Failed to create ephemeral ECDSA keypair\n");
+ goto error;
+ }
+
+ if (create_ephemeral_certificate(cert_info->private_key, &cert_info->certificate)) {
+ ast_log(LOG_ERROR, "Failed to create ephemeral X509 certificate\n");
+ goto error;
+ }
+
+ return 0;
+
+ error:
+ X509_free(cert_info->certificate);
+ EVP_PKEY_free(cert_info->private_key);
+
+ return -1;
+}
+
+#else
+
+static void configure_dhparams(const struct ast_rtp *rtp, const struct ast_rtp_dtls_cfg *dtls_cfg)
+{
+}
+
+static int create_certificate_ephemeral(struct ast_rtp_instance *instance,
+ const struct ast_rtp_dtls_cfg *dtls_cfg,
+ struct dtls_cert_info *cert_info)
+{
+ ast_log(LOG_ERROR, "Your version of OpenSSL does not support ECDSA keys\n");
+ return -1;
+}
+
+#endif /* HAVE_OPENSSL_EC */
+
+static int create_certificate_from_file(struct ast_rtp_instance *instance,
+ const struct ast_rtp_dtls_cfg *dtls_cfg,
+ struct dtls_cert_info *cert_info)
+{
+ FILE *fp;
+ BIO *certbio = NULL;
+ EVP_PKEY *private_key = NULL;
+ X509 *cert = NULL;
+ char *private_key_file = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile;
+
+ fp = fopen(private_key_file, "r");
+ if (!fp) {
+ ast_log(LOG_ERROR, "Failed to read private key from file '%s': %s\n", private_key_file, strerror(errno));
+ goto error;
+ }
+
+ if (!PEM_read_PrivateKey(fp, &private_key, NULL, NULL)) {
+ ast_log(LOG_ERROR, "Failed to read private key from PEM file '%s'\n", private_key_file);
+ fclose(fp);
+ goto error;
+ }
+
+ if (fclose(fp)) {
+ ast_log(LOG_ERROR, "Failed to close private key file '%s': %s\n", private_key_file, strerror(errno));
+ goto error;
+ }
+
+ certbio = BIO_new(BIO_s_file());
+ if (!certbio) {
+ ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n",
+ instance);
+ goto error;
+ }
+
+ if (!BIO_read_filename(certbio, dtls_cfg->certfile)
+ || !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL))) {
+ ast_log(LOG_ERROR, "Failed to read certificate from file '%s'\n", dtls_cfg->certfile);
+ goto error;
+ }
+
+ cert_info->private_key = private_key;
+ cert_info->certificate = cert;
+
+ BIO_free_all(certbio);
+
+ return 0;
+
+error:
+ X509_free(cert);
+ BIO_free_all(certbio);
+ EVP_PKEY_free(private_key);
+
+ return -1;
+}
+
+static int load_dtls_certificate(struct ast_rtp_instance *instance,
+ const struct ast_rtp_dtls_cfg *dtls_cfg,
+ struct dtls_cert_info *cert_info)
+{
+ if (dtls_cfg->ephemeral_cert) {
+ return create_certificate_ephemeral(instance, dtls_cfg, cert_info);
+ } else if (!ast_strlen_zero(dtls_cfg->certfile)) {
+ return create_certificate_from_file(instance, dtls_cfg, cert_info);
+ } else {
+ return -1;
+ }
+}
-#endif /* #ifdef HAVE_OPENSSL_EC */
+/*! \pre instance is locked */
+static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg)
+{
+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+ struct dtls_cert_info cert_info = { 0 };
+ int res;
+
+ if (!dtls_cfg->enabled) {
+ return 0;
+ }
+
+ if (!ast_rtp_engine_srtp_is_registered()) {
+ ast_log(LOG_ERROR, "SRTP support module is not loaded or available. Try loading res_srtp.so.\n");
+ return -1;
+ }
+
+ if (rtp->ssl_ctx) {
+ return 0;
+ }
+
+ rtp->ssl_ctx = SSL_CTX_new(get_dtls_method());
+ if (!rtp->ssl_ctx) {
+ return -1;
+ }
+
+ SSL_CTX_set_read_ahead(rtp->ssl_ctx, 1);
+
+ configure_dhparams(rtp, dtls_cfg);
rtp->dtls_verify = dtls_cfg->verify;
@@ -1680,25 +1909,22 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
rtp->local_hash = dtls_cfg->hash;
- if (!ast_strlen_zero(dtls_cfg->certfile)) {
- char *private = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile;
- BIO *certbio;
- X509 *cert = NULL;
+ if (!load_dtls_certificate(instance, dtls_cfg, &cert_info)) {
const EVP_MD *type;
unsigned int size, i;
unsigned char fingerprint[EVP_MAX_MD_SIZE];
char *local_fingerprint = rtp->local_fingerprint;
- if (!SSL_CTX_use_certificate_file(rtp->ssl_ctx, dtls_cfg->certfile, SSL_FILETYPE_PEM)) {
- ast_log(LOG_ERROR, "Specified certificate file '%s' for RTP instance '%p' could not be used\n",
- dtls_cfg->certfile, instance);
+ if (!SSL_CTX_use_certificate(rtp->ssl_ctx, cert_info.certificate)) {
+ ast_log(LOG_ERROR, "Specified certificate for RTP instance '%p' could not be used\n",
+ instance);
return -1;
}
- if (!SSL_CTX_use_PrivateKey_file(rtp->ssl_ctx, private, SSL_FILETYPE_PEM) ||
- !SSL_CTX_check_private_key(rtp->ssl_ctx)) {
- ast_log(LOG_ERROR, "Specified private key file '%s' for RTP instance '%p' could not be used\n",
- private, instance);
+ if (!SSL_CTX_use_PrivateKey(rtp->ssl_ctx, cert_info.private_key)
+ || !SSL_CTX_check_private_key(rtp->ssl_ctx)) {
+ ast_log(LOG_ERROR, "Specified private key for RTP instance '%p' could not be used\n",
+ instance);
return -1;
}
@@ -1712,22 +1938,9 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
return -1;
}
- if (!(certbio = BIO_new(BIO_s_file()))) {
- ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n",
- instance);
- return -1;
- }
-
- if (!BIO_read_filename(certbio, dtls_cfg->certfile) ||
- !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL)) ||
- !X509_digest(cert, type, fingerprint, &size) ||
- !size) {
- ast_log(LOG_ERROR, "Could not produce fingerprint from certificate '%s' for RTP instance '%p'\n",
- dtls_cfg->certfile, instance);
- BIO_free_all(certbio);
- if (cert) {
- X509_free(cert);
- }
+ if (!X509_digest(cert_info.certificate, type, fingerprint, &size) || !size) {
+ ast_log(LOG_ERROR, "Could not produce fingerprint from certificate for RTP instance '%p'\n",
+ instance);
return -1;
}
@@ -1736,10 +1949,10 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
local_fingerprint += 3;
}
- *(local_fingerprint-1) = 0;
+ *(local_fingerprint - 1) = 0;
- BIO_free_all(certbio);
- X509_free(cert);
+ EVP_PKEY_free(cert_info.private_key);
+ X509_free(cert_info.certificate);
}
if (!ast_strlen_zero(dtls_cfg->cipher)) {