<p>Jenkins2 <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/6640">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Joshua Colp: Looks good to me, but someone else must approve
  Kevin Harwell: Looks good to me, approved
  Jenkins2: Approved for Submit

</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 199028d..30d3493 100644<br>--- a/CHANGES<br>+++ b/CHANGES<br>@@ -12,6 +12,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 cdd78fd..fa228ce 100644<br>--- a/channels/chan_sip.c<br>+++ b/channels/chan_sip.c<br>@@ -31785,6 +31785,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>@@ -32984,6 +32992,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 cee113a..2c65c3c 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/6640">change 6640</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/6640"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 15 </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: 6640 </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>