mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-30 18:40:46 +00:00
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
This commit is contained in:
8
CHANGES
8
CHANGES
@@ -30,6 +30,14 @@ chan_sip
|
|||||||
--- Functionality changes from Asterisk 15.1.0 to Asterisk 15.2.0 ------------
|
--- 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
|
res_pjsip
|
||||||
------------------
|
------------------
|
||||||
* The "identify_by" on endpoints can now be set to "ip" to restrict an endpoint
|
* The "identify_by" on endpoints can now be set to "ip" to restrict an endpoint
|
||||||
|
@@ -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 */
|
/* 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);
|
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 */
|
/* 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[0], &setflags[0], mask[0].flags);
|
||||||
ast_copy_flags(&global_flags[1], &setflags[1], mask[1].flags);
|
ast_copy_flags(&global_flags[1], &setflags[1], mask[1].flags);
|
||||||
|
@@ -746,10 +746,12 @@
|
|||||||
; "no")
|
; "no")
|
||||||
;dtls_rekey=0 ; Interval at which to renegotiate the TLS session and rekey
|
;dtls_rekey=0 ; Interval at which to renegotiate the TLS session and rekey
|
||||||
; the SRTP session (default: "0")
|
; the SRTP session (default: "0")
|
||||||
;dtls_cert_file= ; Path to certificate file to present to peer (default:
|
;dtls_auto_generate_cert= ; Enable ephemeral DTLS certificate generation (default:
|
||||||
; "")
|
; "no")
|
||||||
;dtls_private_key= ; Path to private key for certificate file (default:
|
;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_cipher= ; Cipher to use for DTLS negotiation (default: "")
|
||||||
;dtls_ca_file= ; Path to certificate authority certificate (default: "")
|
;dtls_ca_file= ; Path to certificate authority certificate (default: "")
|
||||||
;dtls_ca_path= ; Path to a directory containing certificate authority
|
;dtls_ca_path= ; Path to a directory containing certificate authority
|
||||||
|
@@ -1340,6 +1340,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
|
|||||||
; encryption
|
; encryption
|
||||||
; description ; Used to provide a description of the peer in console output
|
; description ; Used to provide a description of the peer in console output
|
||||||
; dtlsenable
|
; dtlsenable
|
||||||
|
; dtlsautogeneratecert
|
||||||
; dtlsverify
|
; dtlsverify
|
||||||
; dtlsrekey
|
; dtlsrekey
|
||||||
; dtlscertfile
|
; dtlscertfile
|
||||||
@@ -1369,6 +1370,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
|
|||||||
; ; A value of 'certificate' will perform ONLY certficiate verification
|
; ; A value of 'certificate' will perform ONLY certficiate verification
|
||||||
; dtlsrekey = 60 ; Interval at which to renegotiate the TLS session and rekey the SRTP session
|
; 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
|
; ; 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
|
; dtlscertfile = file ; Path to certificate file to present
|
||||||
; dtlsprivatekey = file ; Path to private key for certificate file
|
; dtlsprivatekey = file ; Path to private key for certificate file
|
||||||
; dtlscipher = <SSL cipher string> ; Cipher to use for TLS negotiation
|
; dtlscipher = <SSL cipher string> ; Cipher to use for TLS negotiation
|
||||||
|
@@ -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')
|
@@ -508,6 +508,7 @@ struct ast_rtp_dtls_cfg {
|
|||||||
char *cipher; /*!< Cipher to use */
|
char *cipher; /*!< Cipher to use */
|
||||||
char *cafile; /*!< Certificate authority file */
|
char *cafile; /*!< Certificate authority file */
|
||||||
char *capath; /*!< Path to certificate authority */
|
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 */
|
/*! \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);
|
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
|
* \brief Copy contents of a DTLS configuration structure
|
||||||
*
|
*
|
||||||
|
@@ -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) {
|
if (sscanf(value, "%30u", &dtls_cfg->rekey) != 1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
} else if (!strcasecmp(name, "dtlsautogeneratecert")) {
|
||||||
|
dtls_cfg->ephemeral_cert = ast_true(value) ? 1 : 0;
|
||||||
} else if (!strcasecmp(name, "dtlscertfile")) {
|
} else if (!strcasecmp(name, "dtlscertfile")) {
|
||||||
if (!ast_strlen_zero(value) && !ast_file_is_readable(value)) {
|
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);
|
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;
|
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)
|
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 */
|
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->rekey = src_cfg->rekey;
|
||||||
dst_cfg->suite = src_cfg->suite;
|
dst_cfg->suite = src_cfg->suite;
|
||||||
dst_cfg->hash = src_cfg->hash;
|
dst_cfg->hash = src_cfg->hash;
|
||||||
|
dst_cfg->ephemeral_cert = src_cfg->ephemeral_cert;
|
||||||
dst_cfg->certfile = ast_strdup(src_cfg->certfile);
|
dst_cfg->certfile = ast_strdup(src_cfg->certfile);
|
||||||
dst_cfg->pvtfile = ast_strdup(src_cfg->pvtfile);
|
dst_cfg->pvtfile = ast_strdup(src_cfg->pvtfile);
|
||||||
dst_cfg->cipher = ast_strdup(src_cfg->cipher);
|
dst_cfg->cipher = ast_strdup(src_cfg->cipher);
|
||||||
|
@@ -772,6 +772,18 @@
|
|||||||
If this is not set or the value provided is 0 rekeying will be disabled.
|
If this is not set or the value provided is 0 rekeying will be disabled.
|
||||||
</para></description>
|
</para></description>
|
||||||
</configOption>
|
</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">
|
<configOption name="dtls_cert_file">
|
||||||
<synopsis>Path to certificate file to present to peer</synopsis>
|
<synopsis>Path to certificate file to present to peer</synopsis>
|
||||||
<description><para>
|
<description><para>
|
||||||
@@ -1028,6 +1040,7 @@
|
|||||||
use_received_transport. The following configuration settings also get defaulted
|
use_received_transport. The following configuration settings also get defaulted
|
||||||
as follows:</para>
|
as follows:</para>
|
||||||
<para>media_encryption=dtls</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_verify=fingerprint</para>
|
||||||
<para>dtls_setup=actpass</para>
|
<para>dtls_setup=actpass</para>
|
||||||
</description>
|
</description>
|
||||||
|
@@ -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;
|
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)
|
static int dtlscertfile_to_str(const void *obj, const intptr_t *args, char **buf)
|
||||||
{
|
{
|
||||||
const struct ast_sip_endpoint *endpoint = obj;
|
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;
|
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);
|
endpoint->media.topology = ast_stream_topology_create_from_format_cap(endpoint->media.codecs);
|
||||||
if (!endpoint->media.topology) {
|
if (!endpoint->media.topology) {
|
||||||
return -1;
|
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;
|
endpoint->media.rtp.dtls_cfg.verify = AST_RTP_DTLS_VERIFY_FINGERPRINT;
|
||||||
|
|
||||||
if (ast_strlen_zero(endpoint->media.rtp.dtls_cfg.certfile)) {
|
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 "
|
/* If no certificate has been specified, try to automatically create one */
|
||||||
"has not been specified", ast_sorcery_object_get_id(endpoint));
|
endpoint->media.rtp.dtls_cfg.ephemeral_cert = 1;
|
||||||
return -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(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_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_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_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_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);
|
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cipher", "", dtls_handler, dtlscipher_to_str, NULL, 0, 0);
|
||||||
|
@@ -1593,14 +1593,282 @@ static int dtls_setup_rtcp(struct ast_rtp_instance *instance)
|
|||||||
return dtls_details_initialize(&rtp->rtcp->dtls, rtp->ssl_ctx, rtp->dtls.dtls_setup);
|
return dtls_details_initialize(&rtp->rtcp->dtls, rtp->ssl_ctx, rtp->dtls.dtls_setup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const SSL_METHOD *get_dtls_method(void)
|
||||||
|
{
|
||||||
|
#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
|
||||||
|
return DTLSv1_method();
|
||||||
|
#else
|
||||||
|
return DTLS_method();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, 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;
|
||||||
|
options = SSL_CTX_set_options(rtp->ssl_ctx, options);
|
||||||
|
ast_verb(2, "DTLS DH initialized, PFS enabled\n");
|
||||||
|
}
|
||||||
|
DH_free(dh);
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
if (SSL_CTX_set_tmp_ecdh(rtp->ssl_ctx, ecdh)) {
|
||||||
|
#ifndef SSL_CTRL_SET_ECDH_AUTO
|
||||||
|
#define SSL_CTRL_SET_ECDH_AUTO 94
|
||||||
|
#endif
|
||||||
|
/* SSL_CTX_set_ecdh_auto(rtp->ssl_ctx, on); requires OpenSSL 1.0.2 which wraps: */
|
||||||
|
if (SSL_CTX_ctrl(rtp->ssl_ctx, SSL_CTRL_SET_ECDH_AUTO, 1, NULL)) {
|
||||||
|
ast_verb(2, "DTLS ECDH initialized (automatic), faster PFS enabled\n");
|
||||||
|
} else {
|
||||||
|
ast_verb(2, "DTLS ECDH initialized (secp256r1), faster PFS enabled\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*! \pre instance is locked */
|
/*! \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 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 ast_rtp *rtp = ast_rtp_instance_get_data(instance);
|
||||||
|
struct dtls_cert_info cert_info = { 0 };
|
||||||
int res;
|
int res;
|
||||||
#ifdef HAVE_OPENSSL_EC
|
|
||||||
EC_KEY *ecdh;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!dtls_cfg->enabled) {
|
if (!dtls_cfg->enabled) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1615,53 +1883,14 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
|
rtp->ssl_ctx = SSL_CTX_new(get_dtls_method());
|
||||||
rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method());
|
|
||||||
#else
|
|
||||||
rtp->ssl_ctx = SSL_CTX_new(DTLS_method());
|
|
||||||
#endif
|
|
||||||
if (!rtp->ssl_ctx) {
|
if (!rtp->ssl_ctx) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
SSL_CTX_set_read_ahead(rtp->ssl_ctx, 1);
|
SSL_CTX_set_read_ahead(rtp->ssl_ctx, 1);
|
||||||
|
|
||||||
#ifdef HAVE_OPENSSL_EC
|
configure_dhparams(rtp, dtls_cfg);
|
||||||
|
|
||||||
if (!ast_strlen_zero(dtls_cfg->pvtfile)) {
|
|
||||||
BIO *bio = BIO_new_file(dtls_cfg->pvtfile, "r");
|
|
||||||
if (bio != NULL) {
|
|
||||||
DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
|
|
||||||
if (dh != NULL) {
|
|
||||||
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;
|
|
||||||
options = SSL_CTX_set_options(rtp->ssl_ctx, options);
|
|
||||||
ast_verb(2, "DTLS DH initialized, PFS enabled\n");
|
|
||||||
}
|
|
||||||
DH_free(dh);
|
|
||||||
}
|
|
||||||
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 (SSL_CTX_set_tmp_ecdh(rtp->ssl_ctx, ecdh)) {
|
|
||||||
#ifndef SSL_CTRL_SET_ECDH_AUTO
|
|
||||||
#define SSL_CTRL_SET_ECDH_AUTO 94
|
|
||||||
#endif
|
|
||||||
/* SSL_CTX_set_ecdh_auto(rtp->ssl_ctx, on); requires OpenSSL 1.0.2 which wraps: */
|
|
||||||
if (SSL_CTX_ctrl(rtp->ssl_ctx, SSL_CTRL_SET_ECDH_AUTO, 1, NULL)) {
|
|
||||||
ast_verb(2, "DTLS ECDH initialized (automatic), faster PFS enabled\n");
|
|
||||||
} else {
|
|
||||||
ast_verb(2, "DTLS ECDH initialized (secp256r1), faster PFS enabled\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EC_KEY_free(ecdh);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* #ifdef HAVE_OPENSSL_EC */
|
|
||||||
|
|
||||||
rtp->dtls_verify = dtls_cfg->verify;
|
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;
|
rtp->local_hash = dtls_cfg->hash;
|
||||||
|
|
||||||
if (!ast_strlen_zero(dtls_cfg->certfile)) {
|
if (!load_dtls_certificate(instance, dtls_cfg, &cert_info)) {
|
||||||
char *private = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile;
|
|
||||||
BIO *certbio;
|
|
||||||
X509 *cert = NULL;
|
|
||||||
const EVP_MD *type;
|
const EVP_MD *type;
|
||||||
unsigned int size, i;
|
unsigned int size, i;
|
||||||
unsigned char fingerprint[EVP_MAX_MD_SIZE];
|
unsigned char fingerprint[EVP_MAX_MD_SIZE];
|
||||||
char *local_fingerprint = rtp->local_fingerprint;
|
char *local_fingerprint = rtp->local_fingerprint;
|
||||||
|
|
||||||
if (!SSL_CTX_use_certificate_file(rtp->ssl_ctx, dtls_cfg->certfile, SSL_FILETYPE_PEM)) {
|
if (!SSL_CTX_use_certificate(rtp->ssl_ctx, cert_info.certificate)) {
|
||||||
ast_log(LOG_ERROR, "Specified certificate file '%s' for RTP instance '%p' could not be used\n",
|
ast_log(LOG_ERROR, "Specified certificate for RTP instance '%p' could not be used\n",
|
||||||
dtls_cfg->certfile, instance);
|
instance);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SSL_CTX_use_PrivateKey_file(rtp->ssl_ctx, private, SSL_FILETYPE_PEM) ||
|
if (!SSL_CTX_use_PrivateKey(rtp->ssl_ctx, cert_info.private_key)
|
||||||
!SSL_CTX_check_private_key(rtp->ssl_ctx)) {
|
|| !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",
|
ast_log(LOG_ERROR, "Specified private key for RTP instance '%p' could not be used\n",
|
||||||
private, instance);
|
instance);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1712,22 +1938,9 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(certbio = BIO_new(BIO_s_file()))) {
|
if (!X509_digest(cert_info.certificate, type, fingerprint, &size) || !size) {
|
||||||
ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n",
|
ast_log(LOG_ERROR, "Could not produce fingerprint from certificate for RTP instance '%p'\n",
|
||||||
instance);
|
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);
|
|
||||||
}
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1736,10 +1949,10 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
|
|||||||
local_fingerprint += 3;
|
local_fingerprint += 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
*(local_fingerprint-1) = 0;
|
*(local_fingerprint - 1) = 0;
|
||||||
|
|
||||||
BIO_free_all(certbio);
|
EVP_PKEY_free(cert_info.private_key);
|
||||||
X509_free(cert);
|
X509_free(cert_info.certificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ast_strlen_zero(dtls_cfg->cipher)) {
|
if (!ast_strlen_zero(dtls_cfg->cipher)) {
|
||||||
|
Reference in New Issue
Block a user