<p>Joshua Colp <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/6641">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Joshua Colp: Looks good to me, but someone else must approve; Approved for Submit
Kevin Harwell: Looks good to me, approved
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">dtls: Add support for ephemeral DTLS certificates.<br><br>This mimics the behavior of Chrome and Firefox and creates an ephemeral<br>X.509 certificate for each DTLS session.<br><br>Currently, the only supported key type is ECDSA because of its faster<br>generation time, but other key types can be added in the future as<br>necessary.<br><br>ASTERISK-27395<br><br>Change-Id: I5122e5f4b83c6320cc17407a187fcf491daf30b4<br>---<br>M CHANGES<br>M channels/chan_sip.c<br>M configs/samples/pjsip.conf.sample<br>M configs/samples/sip.conf.sample<br>A contrib/ast-db-manage/config/versions/041c0d3d1857_add_dtls_auto_gen_cert.py<br>M include/asterisk/rtp_engine.h<br>M main/rtp_engine.c<br>M res/res_pjsip.c<br>M res/res_pjsip/pjsip_configuration.c<br>M res/res_rtp_asterisk.c<br>10 files changed, 409 insertions(+), 81 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/CHANGES b/CHANGES<br>index 39b62d0..12fe0fe 100644<br>--- a/CHANGES<br>+++ b/CHANGES<br>@@ -30,6 +30,14 @@<br> --- Functionality changes from Asterisk 15.1.0 to Asterisk 15.2.0 ------------<br> ------------------------------------------------------------------------------<br> <br>+res_rtp_asterisk<br>+------------------<br>+ * The X.509 certificate used for DTLS negotation can now be automatically<br>+ generated. This is supported by res_pjsip by specifying<br>+ "dtls_auto_generate_cert = yes" on a PJSIP endpoint. For chan_sip, you<br>+ would set "dtlsautogeneratecert = yes" either in the [general] section of<br>+ sip.conf or on a specific peer.<br>+<br> res_pjsip<br> ------------------<br> * The "identify_by" on endpoints can now be set to "ip" to restrict an endpoint<br>diff --git a/channels/chan_sip.c b/channels/chan_sip.c<br>index fd780b6..bd68ec0 100644<br>--- a/channels/chan_sip.c<br>+++ b/channels/chan_sip.c<br>@@ -31946,6 +31946,14 @@<br> }<br> }<br> <br>+ /* Validate DTLS configuration */<br>+ if (ast_rtp_dtls_cfg_validate(&peer->dtls_cfg)) {<br>+ sip_unref_peer(peer, "Removing peer due to bad DTLS configuration");<br>+ return NULL;<br>+ }<br>+<br>+ /* SRB */<br>+<br> /* Apply the encryption tag length to the DTLS configuration, in case DTLS is in use */<br> 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);<br> <br>@@ -33145,6 +33153,11 @@<br> }<br> }<br> <br>+ /* Validate DTLS configuration */<br>+ if (ast_rtp_dtls_cfg_validate(&default_dtls_cfg)) {<br>+ return -1;<br>+ }<br>+<br> /* Override global defaults if setting found in general section */<br> ast_copy_flags(&global_flags[0], &setflags[0], mask[0].flags);<br> ast_copy_flags(&global_flags[1], &setflags[1], mask[1].flags);<br>diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample<br>index 800ff0f..302899a 100644<br>--- a/configs/samples/pjsip.conf.sample<br>+++ b/configs/samples/pjsip.conf.sample<br>@@ -746,10 +746,12 @@<br> ; "no")<br> ;dtls_rekey=0 ; Interval at which to renegotiate the TLS session and rekey<br> ; the SRTP session (default: "0")<br>-;dtls_cert_file= ; Path to certificate file to present to peer (default:<br>- ; "")<br>-;dtls_private_key= ; Path to private key for certificate file (default:<br>- ; "")<br>+;dtls_auto_generate_cert= ; Enable ephemeral DTLS certificate generation (default:<br>+ ; "no")<br>+;dtls_cert_file= ; Path to certificate file to present to peer (default:<br>+ ; "")<br>+;dtls_private_key= ; Path to private key for certificate file (default:<br>+ ; "")<br> ;dtls_cipher= ; Cipher to use for DTLS negotiation (default: "")<br> ;dtls_ca_file= ; Path to certificate authority certificate (default: "")<br> ;dtls_ca_path= ; Path to a directory containing certificate authority<br>diff --git a/configs/samples/sip.conf.sample b/configs/samples/sip.conf.sample<br>index 9b52ec0..ace5097 100644<br>--- a/configs/samples/sip.conf.sample<br>+++ b/configs/samples/sip.conf.sample<br>@@ -1340,6 +1340,7 @@<br> ; encryption<br> ; description ; Used to provide a description of the peer in console output<br> ; dtlsenable<br>+; dtlsautogeneratecert<br> ; dtlsverify<br> ; dtlsrekey<br> ; dtlscertfile<br>@@ -1369,6 +1370,7 @@<br> ; ; A value of 'certificate' will perform ONLY certficiate verification<br> ; dtlsrekey = 60 ; Interval at which to renegotiate the TLS session and rekey the SRTP session<br> ; ; If this is not set or the value provided is 0 rekeying will be disabled<br>+; dtlsautogeneratecert = yes ; Enable ephemeral DTLS certificate generation. The default is 'no.'<br> ; dtlscertfile = file ; Path to certificate file to present<br> ; dtlsprivatekey = file ; Path to private key for certificate file<br> ; dtlscipher = <SSL cipher string> ; Cipher to use for TLS negotiation<br>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<br>new file mode 100644<br>index 0000000..2733b35<br>--- /dev/null<br>+++ b/contrib/ast-db-manage/config/versions/041c0d3d1857_add_dtls_auto_gen_cert.py<br>@@ -0,0 +1,33 @@<br>+"""add_dtls_auto_generate_cert<br>+<br>+Revision ID: 041c0d3d1857<br>+Revises: de83fac997e2<br>+Create Date: 2017-10-30 14:28:10.548395<br>+<br>+"""<br>+<br>+# revision identifiers, used by Alembic.<br>+revision = '041c0d3d1857'<br>+down_revision = 'de83fac997e2'<br>+<br>+from alembic import op<br>+import sqlalchemy as sa<br>+from sqlalchemy.dialects.postgresql import ENUM<br>+<br>+YESNO_NAME = 'yesno_values'<br>+YESNO_VALUES = ['yes', 'no']<br>+<br>+def upgrade():<br>+ ############################# Enums ##############################<br>+<br>+ # yesno_values have already been created, so use postgres enum object<br>+ # type to get around "already created" issue - works okay with mysql<br>+ yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)<br>+<br>+ op.add_column('ps_endpoints', sa.Column('dtls_auto_generate_cert', yesno_values))<br>+<br>+<br>+def downgrade():<br>+ if op.get_context().bind.dialect.name == 'mssql':<br>+ op.drop_constraint('ck_ps_endpoints_dtls_auto_generate_cert_yesno_values', 'ps_endpoints')<br>+ op.drop_column('ps_endpoints', 'dtls_auto_generate_cert')<br>diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h<br>index 3ceac84..f9d686a 100644<br>--- a/include/asterisk/rtp_engine.h<br>+++ b/include/asterisk/rtp_engine.h<br>@@ -508,6 +508,7 @@<br> char *cipher; /*!< Cipher to use */<br> char *cafile; /*!< Certificate authority file */<br> char *capath; /*!< Path to certificate authority */<br>+ unsigned int ephemeral_cert:1; /*!< Whether to not to generate an ephemeral certificate - defaults to 0 (off) */<br> };<br> <br> /*! \brief Structure that represents the optional DTLS SRTP support within an RTP engine */<br>@@ -2350,6 +2351,16 @@<br> int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name, const char *value);<br> <br> /*!<br>+ * \brief Validates DTLS related configuration options<br>+ *<br>+ * \param dtls_cfg a DTLS configuration structure<br>+ *<br>+ * \retval 0 if valid<br>+ * \retval -1 if invalid<br>+ */<br>+int ast_rtp_dtls_cfg_validate(struct ast_rtp_dtls_cfg *dtls_cfg);<br>+<br>+/*!<br> * \brief Copy contents of a DTLS configuration structure<br> *<br> * \param src_cfg source DTLS configuration structure<br>diff --git a/main/rtp_engine.c b/main/rtp_engine.c<br>index 226b229..0aed8e9 100644<br>--- a/main/rtp_engine.c<br>+++ b/main/rtp_engine.c<br>@@ -2717,6 +2717,8 @@<br> if (sscanf(value, "%30u", &dtls_cfg->rekey) != 1) {<br> return -1;<br> }<br>+ } else if (!strcasecmp(name, "dtlsautogeneratecert")) {<br>+ dtls_cfg->ephemeral_cert = ast_true(value) ? 1 : 0;<br> } else if (!strcasecmp(name, "dtlscertfile")) {<br> if (!ast_strlen_zero(value) && !ast_file_is_readable(value)) {<br> ast_log(LOG_ERROR, "%s file %s does not exist or is not readable\n", name, value);<br>@@ -2769,6 +2771,25 @@<br> return 0;<br> }<br> <br>+int ast_rtp_dtls_cfg_validate(struct ast_rtp_dtls_cfg *dtls_cfg)<br>+{<br>+ if (dtls_cfg->ephemeral_cert) {<br>+ if (!ast_strlen_zero(dtls_cfg->certfile)) {<br>+ ast_log(LOG_ERROR, "You cannot request automatically generated certificates"<br>+ " (dtls_auto_generate_cert) and also specify a certificate file"<br>+ " (dtls_cert_file) at the same time\n");<br>+ return -1;<br>+ } else if (!ast_strlen_zero(dtls_cfg->pvtfile)<br>+ || !ast_strlen_zero(dtls_cfg->cafile)<br>+ || !ast_strlen_zero(dtls_cfg->capath)) {<br>+ ast_log(LOG_NOTICE, "dtls_pvt_file, dtls_cafile, and dtls_ca_path are"<br>+ " ignored when dtls_auto_generate_cert is enabled\n");<br>+ }<br>+ }<br>+<br>+ return 0;<br>+}<br>+<br> void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rtp_dtls_cfg *dst_cfg)<br> {<br> ast_rtp_dtls_cfg_free(dst_cfg); /* Prevent a double-call leaking memory via ast_strdup */<br>@@ -2778,6 +2799,7 @@<br> dst_cfg->rekey = src_cfg->rekey;<br> dst_cfg->suite = src_cfg->suite;<br> dst_cfg->hash = src_cfg->hash;<br>+ dst_cfg->ephemeral_cert = src_cfg->ephemeral_cert;<br> dst_cfg->certfile = ast_strdup(src_cfg->certfile);<br> dst_cfg->pvtfile = ast_strdup(src_cfg->pvtfile);<br> dst_cfg->cipher = ast_strdup(src_cfg->cipher);<br>diff --git a/res/res_pjsip.c b/res/res_pjsip.c<br>index 7499ded..1b59b28 100644<br>--- a/res/res_pjsip.c<br>+++ b/res/res_pjsip.c<br>@@ -772,6 +772,18 @@<br> If this is not set or the value provided is 0 rekeying will be disabled.<br> </para></description><br> </configOption><br>+ <configOption name="dtls_auto_generate_cert" default="no"><br>+ <synopsis>Whether or not to automatically generate an ephemeral X.509 certificate</synopsis><br>+ <description><br>+ <para><br>+ If enabled, Asterisk will generate an X.509 certificate for each DTLS session.<br>+ This option only applies if <replaceable>media_encryption</replaceable> is set<br>+ to <literal>dtls</literal>. This option will be automatically enabled if<br>+ <literal>webrtc</literal> is enabled and <literal>dtls_cert_file</literal> is<br>+ not specified.<br>+ </para><br>+ </description><br>+ </configOption><br> <configOption name="dtls_cert_file"><br> <synopsis>Path to certificate file to present to peer</synopsis><br> <description><para><br>@@ -1028,6 +1040,7 @@<br> use_received_transport. The following configuration settings also get defaulted<br> as follows:</para><br> <para>media_encryption=dtls</para><br>+ <para>dtls_auto_generate_cert=yes (if dtls_cert_file is not set)</para><br> <para>dtls_verify=fingerprint</para><br> <para>dtls_setup=actpass</para><br> </description><br>diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c<br>index 6db5b38..2b6a2bb 100644<br>--- a/res/res_pjsip/pjsip_configuration.c<br>+++ b/res/res_pjsip/pjsip_configuration.c<br>@@ -991,6 +991,13 @@<br> buf, "%u", endpoint->media.rtp.dtls_cfg.rekey) >=0 ? 0 : -1;<br> }<br> <br>+static int dtlsautogeneratecert_to_str(const void *obj, const intptr_t *args, char **buf)<br>+{<br>+ const struct ast_sip_endpoint *endpoint = obj;<br>+ *buf = ast_strdup(AST_YESNO(endpoint->media.rtp.dtls_cfg.ephemeral_cert));<br>+ return 0;<br>+}<br>+<br> static int dtlscertfile_to_str(const void *obj, const intptr_t *args, char **buf)<br> {<br> const struct ast_sip_endpoint *endpoint = obj;<br>@@ -1353,6 +1360,10 @@<br> return -1;<br> }<br> <br>+ if (ast_rtp_dtls_cfg_validate(&endpoint->media.rtp.dtls_cfg)) {<br>+ return -1;<br>+ }<br>+<br> endpoint->media.topology = ast_stream_topology_create_from_format_cap(endpoint->media.codecs);<br> if (!endpoint->media.topology) {<br> return -1;<br>@@ -1377,9 +1388,8 @@<br> endpoint->media.rtp.dtls_cfg.verify = AST_RTP_DTLS_VERIFY_FINGERPRINT;<br> <br> if (ast_strlen_zero(endpoint->media.rtp.dtls_cfg.certfile)) {<br>- ast_log(LOG_ERROR, "WebRTC can't be enabled on endpoint '%s' - a DTLS cert "<br>- "has not been specified", ast_sorcery_object_get_id(endpoint));<br>- return -1;<br>+ /* If no certificate has been specified, try to automatically create one */<br>+ endpoint->media.rtp.dtls_cfg.ephemeral_cert = 1;<br> }<br> }<br> <br>@@ -1967,6 +1977,7 @@<br> ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_engine", "asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.rtp.engine));<br> ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_verify", "no", dtls_handler, dtlsverify_to_str, NULL, 0, 0);<br> ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_rekey", "0", dtls_handler, dtlsrekey_to_str, NULL, 0, 0);<br>+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_auto_generate_cert", "no", dtls_handler, dtlsautogeneratecert_to_str, NULL, 0, 0);<br> ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cert_file", "", dtls_handler, dtlscertfile_to_str, NULL, 0, 0);<br> ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_private_key", "", dtls_handler, dtlsprivatekey_to_str, NULL, 0, 0);<br> ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cipher", "", dtls_handler, dtlscipher_to_str, NULL, 0, 0);<br>diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c<br>index f5d9134..15ca150 100644<br>--- a/res/res_rtp_asterisk.c<br>+++ b/res/res_rtp_asterisk.c<br>@@ -1593,14 +1593,282 @@<br> return dtls_details_initialize(&rtp->rtcp->dtls, rtp->ssl_ctx, rtp->dtls.dtls_setup);<br> }<br> <br>+static const SSL_METHOD *get_dtls_method(void)<br>+{<br>+#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)<br>+ return DTLSv1_method();<br>+#else<br>+ return DTLS_method();<br>+#endif<br>+}<br>+<br>+struct dtls_cert_info {<br>+ EVP_PKEY *private_key;<br>+ X509 *certificate;<br>+};<br>+<br>+#ifdef HAVE_OPENSSL_EC<br>+<br>+static void configure_dhparams(const struct ast_rtp *rtp, const struct ast_rtp_dtls_cfg *dtls_cfg)<br>+{<br>+ EC_KEY *ecdh;<br>+<br>+ if (!ast_strlen_zero(dtls_cfg->pvtfile)) {<br>+ BIO *bio = BIO_new_file(dtls_cfg->pvtfile, "r");<br>+ if (bio) {<br>+ DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);<br>+ if (dh) {<br>+ if (SSL_CTX_set_tmp_dh(rtp->ssl_ctx, dh)) {<br>+ long options = SSL_OP_CIPHER_SERVER_PREFERENCE |<br>+ SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE;<br>+ options = SSL_CTX_set_options(rtp->ssl_ctx, options);<br>+ ast_verb(2, "DTLS DH initialized, PFS enabled\n");<br>+ }<br>+ DH_free(dh);<br>+ }<br>+ BIO_free(bio);<br>+ }<br>+ }<br>+<br>+ /* enables AES-128 ciphers, to get AES-256 use NID_secp384r1 */<br>+ ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);<br>+ if (ecdh) {<br>+ if (SSL_CTX_set_tmp_ecdh(rtp->ssl_ctx, ecdh)) {<br>+ #ifndef SSL_CTRL_SET_ECDH_AUTO<br>+ #define SSL_CTRL_SET_ECDH_AUTO 94<br>+ #endif<br>+ /* SSL_CTX_set_ecdh_auto(rtp->ssl_ctx, on); requires OpenSSL 1.0.2 which wraps: */<br>+ if (SSL_CTX_ctrl(rtp->ssl_ctx, SSL_CTRL_SET_ECDH_AUTO, 1, NULL)) {<br>+ ast_verb(2, "DTLS ECDH initialized (automatic), faster PFS enabled\n");<br>+ } else {<br>+ ast_verb(2, "DTLS ECDH initialized (secp256r1), faster PFS enabled\n");<br>+ }<br>+ }<br>+ EC_KEY_free(ecdh);<br>+ }<br>+}<br>+<br>+static int create_ephemeral_ec_keypair(EVP_PKEY **keypair)<br>+{<br>+ EC_KEY *eckey = NULL;<br>+ EC_GROUP *group = NULL;<br>+<br>+ group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);<br>+ if (!group) {<br>+ goto error;<br>+ }<br>+<br>+ EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);<br>+ EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);<br>+<br>+ eckey = EC_KEY_new();<br>+ if (!eckey) {<br>+ goto error;<br>+ }<br>+<br>+ if (!EC_KEY_set_group(eckey, group)) {<br>+ goto error;<br>+ }<br>+<br>+ if (!EC_KEY_generate_key(eckey)) {<br>+ goto error;<br>+ }<br>+<br>+ *keypair = EVP_PKEY_new();<br>+ if (!*keypair) {<br>+ goto error;<br>+ }<br>+<br>+ EVP_PKEY_assign_EC_KEY(*keypair, eckey);<br>+ EC_GROUP_free(group);<br>+<br>+ return 0;<br>+<br>+error:<br>+ EC_KEY_free(eckey);<br>+ EC_GROUP_free(group);<br>+<br>+ return -1;<br>+}<br>+<br>+/* From OpenSSL's x509 command */<br>+#define SERIAL_RAND_BITS 159<br>+<br>+static int create_ephemeral_certificate(EVP_PKEY *keypair, X509 **certificate)<br>+{<br>+ X509 *cert = NULL;<br>+ BIGNUM *serial = NULL;<br>+ X509_NAME *name = NULL;<br>+<br>+ cert = X509_new();<br>+ if (!cert) {<br>+ goto error;<br>+ }<br>+<br>+ if (!X509_set_version(cert, 2)) {<br>+ goto error;<br>+ }<br>+<br>+ /* Set the public key */<br>+ X509_set_pubkey(cert, keypair);<br>+<br>+ /* Generate a random serial number */<br>+ if (!(serial = BN_new())<br>+ || !BN_rand(serial, SERIAL_RAND_BITS, -1, 0)<br>+ || !BN_to_ASN1_INTEGER(serial, X509_get_serialNumber(cert))) {<br>+ goto error;<br>+ }<br>+<br>+ /*<br>+ * Validity period - Current Chrome & Firefox make it 31 days starting<br>+ * with yesterday at the current time, so we will do the same.<br>+ */<br>+ if (!X509_time_adj_ex(X509_get_notBefore(cert), -1, 0, NULL)<br>+ || !X509_time_adj_ex(X509_get_notAfter(cert), 30, 0, NULL)) {<br>+ goto error;<br>+ }<br>+<br>+ /* Set the name and issuer */<br>+ if (!(name = X509_get_subject_name(cert))<br>+ || !X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,<br>+ (unsigned char *) "asterisk", -1, -1, 0)<br>+ || !X509_set_issuer_name(cert, name)) {<br>+ goto error;<br>+ }<br>+<br>+ /* Sign it */<br>+ if (!X509_sign(cert, keypair, EVP_sha256())) {<br>+ goto error;<br>+ }<br>+<br>+ *certificate = cert;<br>+<br>+ return 0;<br>+<br>+error:<br>+ BN_free(serial);<br>+ X509_free(cert);<br>+<br>+ return -1;<br>+}<br>+<br>+static int create_certificate_ephemeral(struct ast_rtp_instance *instance,<br>+ const struct ast_rtp_dtls_cfg *dtls_cfg,<br>+ struct dtls_cert_info *cert_info)<br>+{<br>+ /* Make sure these are initialized */<br>+ cert_info->private_key = NULL;<br>+ cert_info->certificate = NULL;<br>+<br>+ if (create_ephemeral_ec_keypair(&cert_info->private_key)) {<br>+ ast_log(LOG_ERROR, "Failed to create ephemeral ECDSA keypair\n");<br>+ goto error;<br>+ }<br>+<br>+ if (create_ephemeral_certificate(cert_info->private_key, &cert_info->certificate)) {<br>+ ast_log(LOG_ERROR, "Failed to create ephemeral X509 certificate\n");<br>+ goto error;<br>+ }<br>+<br>+ return 0;<br>+<br>+ error:<br>+ X509_free(cert_info->certificate);<br>+ EVP_PKEY_free(cert_info->private_key);<br>+<br>+ return -1;<br>+}<br>+<br>+#else<br>+<br>+static void configure_dhparams(const struct ast_rtp *rtp, const struct ast_rtp_dtls_cfg *dtls_cfg)<br>+{<br>+}<br>+<br>+static int create_certificate_ephemeral(struct ast_rtp_instance *instance,<br>+ const struct ast_rtp_dtls_cfg *dtls_cfg,<br>+ struct dtls_cert_info *cert_info)<br>+{<br>+ ast_log(LOG_ERROR, "Your version of OpenSSL does not support ECDSA keys\n");<br>+ return -1;<br>+}<br>+<br>+#endif /* HAVE_OPENSSL_EC */<br>+<br>+static int create_certificate_from_file(struct ast_rtp_instance *instance,<br>+ const struct ast_rtp_dtls_cfg *dtls_cfg,<br>+ struct dtls_cert_info *cert_info)<br>+{<br>+ FILE *fp;<br>+ BIO *certbio = NULL;<br>+ EVP_PKEY *private_key = NULL;<br>+ X509 *cert = NULL;<br>+ char *private_key_file = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile;<br>+<br>+ fp = fopen(private_key_file, "r");<br>+ if (!fp) {<br>+ ast_log(LOG_ERROR, "Failed to read private key from file '%s': %s\n", private_key_file, strerror(errno));<br>+ goto error;<br>+ }<br>+<br>+ if (!PEM_read_PrivateKey(fp, &private_key, NULL, NULL)) {<br>+ ast_log(LOG_ERROR, "Failed to read private key from PEM file '%s'\n", private_key_file);<br>+ fclose(fp);<br>+ goto error;<br>+ }<br>+<br>+ if (fclose(fp)) {<br>+ ast_log(LOG_ERROR, "Failed to close private key file '%s': %s\n", private_key_file, strerror(errno));<br>+ goto error;<br>+ }<br>+<br>+ certbio = BIO_new(BIO_s_file());<br>+ if (!certbio) {<br>+ ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n",<br>+ instance);<br>+ goto error;<br>+ }<br>+<br>+ if (!BIO_read_filename(certbio, dtls_cfg->certfile)<br>+ || !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL))) {<br>+ ast_log(LOG_ERROR, "Failed to read certificate from file '%s'\n", dtls_cfg->certfile);<br>+ goto error;<br>+ }<br>+<br>+ cert_info->private_key = private_key;<br>+ cert_info->certificate = cert;<br>+<br>+ BIO_free_all(certbio);<br>+<br>+ return 0;<br>+<br>+error:<br>+ X509_free(cert);<br>+ BIO_free_all(certbio);<br>+ EVP_PKEY_free(private_key);<br>+<br>+ return -1;<br>+}<br>+<br>+static int load_dtls_certificate(struct ast_rtp_instance *instance,<br>+ const struct ast_rtp_dtls_cfg *dtls_cfg,<br>+ struct dtls_cert_info *cert_info)<br>+{<br>+ if (dtls_cfg->ephemeral_cert) {<br>+ return create_certificate_ephemeral(instance, dtls_cfg, cert_info);<br>+ } else if (!ast_strlen_zero(dtls_cfg->certfile)) {<br>+ return create_certificate_from_file(instance, dtls_cfg, cert_info);<br>+ } else {<br>+ return -1;<br>+ }<br>+}<br>+<br> /*! \pre instance is locked */<br> static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg)<br> {<br> struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br>+ struct dtls_cert_info cert_info = { 0 };<br> int res;<br>-#ifdef HAVE_OPENSSL_EC<br>- EC_KEY *ecdh;<br>-#endif<br> <br> if (!dtls_cfg->enabled) {<br> return 0;<br>@@ -1615,53 +1883,14 @@<br> return 0;<br> }<br> <br>-#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)<br>- rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method());<br>-#else<br>- rtp->ssl_ctx = SSL_CTX_new(DTLS_method());<br>-#endif<br>+ rtp->ssl_ctx = SSL_CTX_new(get_dtls_method());<br> if (!rtp->ssl_ctx) {<br> return -1;<br> }<br> <br> SSL_CTX_set_read_ahead(rtp->ssl_ctx, 1);<br> <br>-#ifdef HAVE_OPENSSL_EC<br>-<br>- if (!ast_strlen_zero(dtls_cfg->pvtfile)) {<br>- BIO *bio = BIO_new_file(dtls_cfg->pvtfile, "r");<br>- if (bio != NULL) {<br>- DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);<br>- if (dh != NULL) {<br>- if (SSL_CTX_set_tmp_dh(rtp->ssl_ctx, dh)) {<br>- long options = SSL_OP_CIPHER_SERVER_PREFERENCE |<br>- SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE;<br>- options = SSL_CTX_set_options(rtp->ssl_ctx, options);<br>- ast_verb(2, "DTLS DH initialized, PFS enabled\n");<br>- }<br>- DH_free(dh);<br>- }<br>- BIO_free(bio);<br>- }<br>- }<br>- /* enables AES-128 ciphers, to get AES-256 use NID_secp384r1 */<br>- ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);<br>- if (ecdh != NULL) {<br>- if (SSL_CTX_set_tmp_ecdh(rtp->ssl_ctx, ecdh)) {<br>- #ifndef SSL_CTRL_SET_ECDH_AUTO<br>- #define SSL_CTRL_SET_ECDH_AUTO 94<br>- #endif<br>- /* SSL_CTX_set_ecdh_auto(rtp->ssl_ctx, on); requires OpenSSL 1.0.2 which wraps: */<br>- if (SSL_CTX_ctrl(rtp->ssl_ctx, SSL_CTRL_SET_ECDH_AUTO, 1, NULL)) {<br>- ast_verb(2, "DTLS ECDH initialized (automatic), faster PFS enabled\n");<br>- } else {<br>- ast_verb(2, "DTLS ECDH initialized (secp256r1), faster PFS enabled\n");<br>- }<br>- }<br>- EC_KEY_free(ecdh);<br>- }<br>-<br>-#endif /* #ifdef HAVE_OPENSSL_EC */<br>+ configure_dhparams(rtp, dtls_cfg);<br> <br> rtp->dtls_verify = dtls_cfg->verify;<br> <br>@@ -1680,25 +1909,22 @@<br> <br> rtp->local_hash = dtls_cfg->hash;<br> <br>- if (!ast_strlen_zero(dtls_cfg->certfile)) {<br>- char *private = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile;<br>- BIO *certbio;<br>- X509 *cert = NULL;<br>+ if (!load_dtls_certificate(instance, dtls_cfg, &cert_info)) {<br> const EVP_MD *type;<br> unsigned int size, i;<br> unsigned char fingerprint[EVP_MAX_MD_SIZE];<br> char *local_fingerprint = rtp->local_fingerprint;<br> <br>- if (!SSL_CTX_use_certificate_file(rtp->ssl_ctx, dtls_cfg->certfile, SSL_FILETYPE_PEM)) {<br>- ast_log(LOG_ERROR, "Specified certificate file '%s' for RTP instance '%p' could not be used\n",<br>- dtls_cfg->certfile, instance);<br>+ if (!SSL_CTX_use_certificate(rtp->ssl_ctx, cert_info.certificate)) {<br>+ ast_log(LOG_ERROR, "Specified certificate for RTP instance '%p' could not be used\n",<br>+ instance);<br> return -1;<br> }<br> <br>- if (!SSL_CTX_use_PrivateKey_file(rtp->ssl_ctx, private, SSL_FILETYPE_PEM) ||<br>- !SSL_CTX_check_private_key(rtp->ssl_ctx)) {<br>- ast_log(LOG_ERROR, "Specified private key file '%s' for RTP instance '%p' could not be used\n",<br>- private, instance);<br>+ if (!SSL_CTX_use_PrivateKey(rtp->ssl_ctx, cert_info.private_key)<br>+ || !SSL_CTX_check_private_key(rtp->ssl_ctx)) {<br>+ ast_log(LOG_ERROR, "Specified private key for RTP instance '%p' could not be used\n",<br>+ instance);<br> return -1;<br> }<br> <br>@@ -1712,22 +1938,9 @@<br> return -1;<br> }<br> <br>- if (!(certbio = BIO_new(BIO_s_file()))) {<br>- ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n",<br>- instance);<br>- return -1;<br>- }<br>-<br>- if (!BIO_read_filename(certbio, dtls_cfg->certfile) ||<br>- !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL)) ||<br>- !X509_digest(cert, type, fingerprint, &size) ||<br>- !size) {<br>- ast_log(LOG_ERROR, "Could not produce fingerprint from certificate '%s' for RTP instance '%p'\n",<br>- dtls_cfg->certfile, instance);<br>- BIO_free_all(certbio);<br>- if (cert) {<br>- X509_free(cert);<br>- }<br>+ if (!X509_digest(cert_info.certificate, type, fingerprint, &size) || !size) {<br>+ ast_log(LOG_ERROR, "Could not produce fingerprint from certificate for RTP instance '%p'\n",<br>+ instance);<br> return -1;<br> }<br> <br>@@ -1736,10 +1949,10 @@<br> local_fingerprint += 3;<br> }<br> <br>- *(local_fingerprint-1) = 0;<br>+ *(local_fingerprint - 1) = 0;<br> <br>- BIO_free_all(certbio);<br>- X509_free(cert);<br>+ EVP_PKEY_free(cert_info.private_key);<br>+ X509_free(cert_info.certificate);<br> }<br> <br> if (!ast_strlen_zero(dtls_cfg->cipher)) {<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/6641">change 6641</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/6641"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I5122e5f4b83c6320cc17407a187fcf491daf30b4 </div>
<div style="display:none"> Gerrit-Change-Number: 6641 </div>
<div style="display:none"> Gerrit-PatchSet: 11 </div>
<div style="display:none"> Gerrit-Owner: Sean Bright <sean.bright@gmail.com> </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins2 </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Sean Bright <sean.bright@gmail.com> </div>