diff --git a/CHANGES b/CHANGES index 39b62d05c225437fa51eb6808a1e10a3beb93077..12fe0fe42ac43d5df5a86095d59531b8e691d0b6 100644 --- a/CHANGES +++ b/CHANGES @@ -30,6 +30,14 @@ chan_sip --- Functionality changes from Asterisk 15.1.0 to Asterisk 15.2.0 ------------ ------------------------------------------------------------------------------ +res_rtp_asterisk +------------------ + * The X.509 certificate used for DTLS negotation can now be automatically + generated. This is supported by res_pjsip by specifying + "dtls_auto_generate_cert = yes" on a PJSIP endpoint. For chan_sip, you + would set "dtlsautogeneratecert = yes" either in the [general] section of + sip.conf or on a specific peer. + res_pjsip ------------------ * The "identify_by" on endpoints can now be set to "ip" to restrict an endpoint diff --git a/channels/chan_sip.c b/channels/chan_sip.c index fd780b61f53fb9b8a5a3c327dbcbe16e9e06e678..bd68ec096c835b30c0bd4cc3313fbb4f1848447a 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -31946,6 +31946,14 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v_head } } + /* Validate DTLS configuration */ + if (ast_rtp_dtls_cfg_validate(&peer->dtls_cfg)) { + sip_unref_peer(peer, "Removing peer due to bad DTLS configuration"); + return NULL; + } + + /* SRB */ + /* Apply the encryption tag length to the DTLS configuration, in case DTLS is in use */ peer->dtls_cfg.suite = (ast_test_flag(&peer->flags[2], SIP_PAGE3_SRTP_TAG_32) ? AST_AES_CM_128_HMAC_SHA1_32 : AST_AES_CM_128_HMAC_SHA1_80); @@ -33145,6 +33153,11 @@ static int reload_config(enum channelreloadreason reason) } } + /* Validate DTLS configuration */ + if (ast_rtp_dtls_cfg_validate(&default_dtls_cfg)) { + return -1; + } + /* Override global defaults if setting found in general section */ ast_copy_flags(&global_flags[0], &setflags[0], mask[0].flags); ast_copy_flags(&global_flags[1], &setflags[1], mask[1].flags); diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index 800ff0f444248fc8c8961d937ece17bc3f1fa0a4..302899a1743a6718a0ce83d12dfeb711ad952393 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -746,10 +746,12 @@ ; "no") ;dtls_rekey=0 ; Interval at which to renegotiate the TLS session and rekey ; the SRTP session (default: "0") -;dtls_cert_file= ; Path to certificate file to present to peer (default: - ; "") -;dtls_private_key= ; Path to private key for certificate file (default: - ; "") +;dtls_auto_generate_cert= ; Enable ephemeral DTLS certificate generation (default: + ; "no") +;dtls_cert_file= ; Path to certificate file to present to peer (default: + ; "") +;dtls_private_key= ; Path to private key for certificate file (default: + ; "") ;dtls_cipher= ; Cipher to use for DTLS negotiation (default: "") ;dtls_ca_file= ; Path to certificate authority certificate (default: "") ;dtls_ca_path= ; Path to a directory containing certificate authority diff --git a/configs/samples/sip.conf.sample b/configs/samples/sip.conf.sample index 9b52ec06c0d3245549a12bced7b35284cb180194..ace5097596f0e729e78cc246ea4c4fc7cfbfa41c 100644 --- a/configs/samples/sip.conf.sample +++ b/configs/samples/sip.conf.sample @@ -1340,6 +1340,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; encryption ; description ; Used to provide a description of the peer in console output ; dtlsenable +; dtlsautogeneratecert ; dtlsverify ; dtlsrekey ; dtlscertfile @@ -1369,6 +1370,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; ; A value of 'certificate' will perform ONLY certficiate verification ; dtlsrekey = 60 ; Interval at which to renegotiate the TLS session and rekey the SRTP session ; ; If this is not set or the value provided is 0 rekeying will be disabled +; dtlsautogeneratecert = yes ; Enable ephemeral DTLS certificate generation. The default is 'no.' ; dtlscertfile = file ; Path to certificate file to present ; dtlsprivatekey = file ; Path to private key for certificate file ; dtlscipher = <SSL cipher string> ; Cipher to use for TLS negotiation diff --git a/contrib/ast-db-manage/config/versions/041c0d3d1857_add_dtls_auto_gen_cert.py b/contrib/ast-db-manage/config/versions/041c0d3d1857_add_dtls_auto_gen_cert.py new file mode 100644 index 0000000000000000000000000000000000000000..2733b35cc12bd7f62eb47e8e23c645d7e981ecc1 --- /dev/null +++ b/contrib/ast-db-manage/config/versions/041c0d3d1857_add_dtls_auto_gen_cert.py @@ -0,0 +1,33 @@ +"""add_dtls_auto_generate_cert + +Revision ID: 041c0d3d1857 +Revises: de83fac997e2 +Create Date: 2017-10-30 14:28:10.548395 + +""" + +# revision identifiers, used by Alembic. +revision = '041c0d3d1857' +down_revision = 'de83fac997e2' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import ENUM + +YESNO_NAME = 'yesno_values' +YESNO_VALUES = ['yes', 'no'] + +def upgrade(): + ############################# Enums ############################## + + # yesno_values have already been created, so use postgres enum object + # type to get around "already created" issue - works okay with mysql + yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False) + + op.add_column('ps_endpoints', sa.Column('dtls_auto_generate_cert', yesno_values)) + + +def downgrade(): + if op.get_context().bind.dialect.name == 'mssql': + op.drop_constraint('ck_ps_endpoints_dtls_auto_generate_cert_yesno_values', 'ps_endpoints') + op.drop_column('ps_endpoints', 'dtls_auto_generate_cert') diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h index 3ceac8467e17ee409094736fd21a82fad6e330c1..f9d686aca81011fcd48194983d9d28ffe393dcc5 100644 --- a/include/asterisk/rtp_engine.h +++ b/include/asterisk/rtp_engine.h @@ -508,6 +508,7 @@ struct ast_rtp_dtls_cfg { char *cipher; /*!< Cipher to use */ char *cafile; /*!< Certificate authority file */ char *capath; /*!< Path to certificate authority */ + unsigned int ephemeral_cert:1; /*!< Whether to not to generate an ephemeral certificate - defaults to 0 (off) */ }; /*! \brief Structure that represents the optional DTLS SRTP support within an RTP engine */ @@ -2349,6 +2350,16 @@ struct ast_rtp_engine_dtls *ast_rtp_instance_get_dtls(struct ast_rtp_instance *i */ int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name, const char *value); +/*! + * \brief Validates DTLS related configuration options + * + * \param dtls_cfg a DTLS configuration structure + * + * \retval 0 if valid + * \retval -1 if invalid + */ +int ast_rtp_dtls_cfg_validate(struct ast_rtp_dtls_cfg *dtls_cfg); + /*! * \brief Copy contents of a DTLS configuration structure * diff --git a/main/rtp_engine.c b/main/rtp_engine.c index 226b229f29c972f9c908028718b97a652ebc9e9d..0aed8e97c36c7e185724fe5314fbefd84ae8807e 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -2717,6 +2717,8 @@ int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name, if (sscanf(value, "%30u", &dtls_cfg->rekey) != 1) { return -1; } + } else if (!strcasecmp(name, "dtlsautogeneratecert")) { + dtls_cfg->ephemeral_cert = ast_true(value) ? 1 : 0; } else if (!strcasecmp(name, "dtlscertfile")) { if (!ast_strlen_zero(value) && !ast_file_is_readable(value)) { ast_log(LOG_ERROR, "%s file %s does not exist or is not readable\n", name, value); @@ -2769,6 +2771,25 @@ int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name, return 0; } +int ast_rtp_dtls_cfg_validate(struct ast_rtp_dtls_cfg *dtls_cfg) +{ + if (dtls_cfg->ephemeral_cert) { + if (!ast_strlen_zero(dtls_cfg->certfile)) { + ast_log(LOG_ERROR, "You cannot request automatically generated certificates" + " (dtls_auto_generate_cert) and also specify a certificate file" + " (dtls_cert_file) at the same time\n"); + return -1; + } else if (!ast_strlen_zero(dtls_cfg->pvtfile) + || !ast_strlen_zero(dtls_cfg->cafile) + || !ast_strlen_zero(dtls_cfg->capath)) { + ast_log(LOG_NOTICE, "dtls_pvt_file, dtls_cafile, and dtls_ca_path are" + " ignored when dtls_auto_generate_cert is enabled\n"); + } + } + + return 0; +} + void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rtp_dtls_cfg *dst_cfg) { ast_rtp_dtls_cfg_free(dst_cfg); /* Prevent a double-call leaking memory via ast_strdup */ @@ -2778,6 +2799,7 @@ void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rt dst_cfg->rekey = src_cfg->rekey; dst_cfg->suite = src_cfg->suite; dst_cfg->hash = src_cfg->hash; + dst_cfg->ephemeral_cert = src_cfg->ephemeral_cert; dst_cfg->certfile = ast_strdup(src_cfg->certfile); dst_cfg->pvtfile = ast_strdup(src_cfg->pvtfile); dst_cfg->cipher = ast_strdup(src_cfg->cipher); diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 7499ded3e1ceb7769eb7c692a9618038f91cde76..1b59b28344e4f62fe4baee2bff99aad81cc4a64d 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -772,6 +772,18 @@ If this is not set or the value provided is 0 rekeying will be disabled. </para></description> </configOption> + <configOption name="dtls_auto_generate_cert" default="no"> + <synopsis>Whether or not to automatically generate an ephemeral X.509 certificate</synopsis> + <description> + <para> + If enabled, Asterisk will generate an X.509 certificate for each DTLS session. + This option only applies if <replaceable>media_encryption</replaceable> is set + to <literal>dtls</literal>. This option will be automatically enabled if + <literal>webrtc</literal> is enabled and <literal>dtls_cert_file</literal> is + not specified. + </para> + </description> + </configOption> <configOption name="dtls_cert_file"> <synopsis>Path to certificate file to present to peer</synopsis> <description><para> @@ -1028,6 +1040,7 @@ use_received_transport. The following configuration settings also get defaulted as follows:</para> <para>media_encryption=dtls</para> + <para>dtls_auto_generate_cert=yes (if dtls_cert_file is not set)</para> <para>dtls_verify=fingerprint</para> <para>dtls_setup=actpass</para> </description> diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 6db5b38985ab1c204a3f2b38d2ab49090361f3c2..2b6a2bb2a74575f53ee0df60f8cf23bc08b05af6 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -991,6 +991,13 @@ static int dtlsrekey_to_str(const void *obj, const intptr_t *args, char **buf) buf, "%u", endpoint->media.rtp.dtls_cfg.rekey) >=0 ? 0 : -1; } +static int dtlsautogeneratecert_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + *buf = ast_strdup(AST_YESNO(endpoint->media.rtp.dtls_cfg.ephemeral_cert)); + return 0; +} + static int dtlscertfile_to_str(const void *obj, const intptr_t *args, char **buf) { const struct ast_sip_endpoint *endpoint = obj; @@ -1353,6 +1360,10 @@ static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *o return -1; } + if (ast_rtp_dtls_cfg_validate(&endpoint->media.rtp.dtls_cfg)) { + return -1; + } + endpoint->media.topology = ast_stream_topology_create_from_format_cap(endpoint->media.codecs); if (!endpoint->media.topology) { return -1; @@ -1377,9 +1388,8 @@ static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *o endpoint->media.rtp.dtls_cfg.verify = AST_RTP_DTLS_VERIFY_FINGERPRINT; if (ast_strlen_zero(endpoint->media.rtp.dtls_cfg.certfile)) { - ast_log(LOG_ERROR, "WebRTC can't be enabled on endpoint '%s' - a DTLS cert " - "has not been specified", ast_sorcery_object_get_id(endpoint)); - return -1; + /* If no certificate has been specified, try to automatically create one */ + endpoint->media.rtp.dtls_cfg.ephemeral_cert = 1; } } @@ -1967,6 +1977,7 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_engine", "asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.rtp.engine)); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_verify", "no", dtls_handler, dtlsverify_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_rekey", "0", dtls_handler, dtlsrekey_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_auto_generate_cert", "no", dtls_handler, dtlsautogeneratecert_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cert_file", "", dtls_handler, dtlscertfile_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_private_key", "", dtls_handler, dtlsprivatekey_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cipher", "", dtls_handler, dtlscipher_to_str, NULL, 0, 0); diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index f5d91345832d6eb859c44fcbeac7b152043771b5..15ca1503b1ebe004d27afd6b57c4277581cdd8c4 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)) {