mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-30 10:33:13 +00:00
Merge "dtls: Add support for ephemeral DTLS certificates."
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);
|
||||||
|
@@ -773,6 +773,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>
|
||||||
@@ -1029,6 +1041,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