diff options
Diffstat (limited to 'res/res_rtp_asterisk.c')
-rw-r--r-- | res/res_rtp_asterisk.c | 335 |
1 files changed, 274 insertions, 61 deletions
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index f5d913458..15ca1503b 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -1593,46 +1593,31 @@ static int dtls_setup_rtcp(struct ast_rtp_instance *instance) return dtls_details_initialize(&rtp->rtcp->dtls, rtp->ssl_ctx, rtp->dtls.dtls_setup); } -/*! \pre instance is locked */ -static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg) +static const SSL_METHOD *get_dtls_method(void) { - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - int res; -#ifdef HAVE_OPENSSL_EC - EC_KEY *ecdh; -#endif - - if (!dtls_cfg->enabled) { - return 0; - } - - if (!ast_rtp_engine_srtp_is_registered()) { - ast_log(LOG_ERROR, "SRTP support module is not loaded or available. Try loading res_srtp.so.\n"); - return -1; - } - - if (rtp->ssl_ctx) { - return 0; - } - #if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) - rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method()); + return DTLSv1_method(); #else - rtp->ssl_ctx = SSL_CTX_new(DTLS_method()); + return DTLS_method(); #endif - if (!rtp->ssl_ctx) { - return -1; - } +} - SSL_CTX_set_read_ahead(rtp->ssl_ctx, 1); +struct dtls_cert_info { + EVP_PKEY *private_key; + X509 *certificate; +}; #ifdef HAVE_OPENSSL_EC +static void configure_dhparams(const struct ast_rtp *rtp, const struct ast_rtp_dtls_cfg *dtls_cfg) +{ + EC_KEY *ecdh; + if (!ast_strlen_zero(dtls_cfg->pvtfile)) { BIO *bio = BIO_new_file(dtls_cfg->pvtfile, "r"); - if (bio != NULL) { + if (bio) { DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); - if (dh != NULL) { + if (dh) { if (SSL_CTX_set_tmp_dh(rtp->ssl_ctx, dh)) { long options = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE; @@ -1644,9 +1629,10 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con BIO_free(bio); } } + /* enables AES-128 ciphers, to get AES-256 use NID_secp384r1 */ ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - if (ecdh != NULL) { + if (ecdh) { if (SSL_CTX_set_tmp_ecdh(rtp->ssl_ctx, ecdh)) { #ifndef SSL_CTRL_SET_ECDH_AUTO #define SSL_CTRL_SET_ECDH_AUTO 94 @@ -1660,8 +1646,251 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con } EC_KEY_free(ecdh); } +} + +static int create_ephemeral_ec_keypair(EVP_PKEY **keypair) +{ + EC_KEY *eckey = NULL; + EC_GROUP *group = NULL; + + group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + if (!group) { + goto error; + } + + EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); + EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED); + + eckey = EC_KEY_new(); + if (!eckey) { + goto error; + } + + if (!EC_KEY_set_group(eckey, group)) { + goto error; + } + + if (!EC_KEY_generate_key(eckey)) { + goto error; + } + + *keypair = EVP_PKEY_new(); + if (!*keypair) { + goto error; + } + + EVP_PKEY_assign_EC_KEY(*keypair, eckey); + EC_GROUP_free(group); + + return 0; + +error: + EC_KEY_free(eckey); + EC_GROUP_free(group); + + return -1; +} + +/* From OpenSSL's x509 command */ +#define SERIAL_RAND_BITS 159 + +static int create_ephemeral_certificate(EVP_PKEY *keypair, X509 **certificate) +{ + X509 *cert = NULL; + BIGNUM *serial = NULL; + X509_NAME *name = NULL; + + cert = X509_new(); + if (!cert) { + goto error; + } + + if (!X509_set_version(cert, 2)) { + goto error; + } + + /* Set the public key */ + X509_set_pubkey(cert, keypair); + + /* Generate a random serial number */ + if (!(serial = BN_new()) + || !BN_rand(serial, SERIAL_RAND_BITS, -1, 0) + || !BN_to_ASN1_INTEGER(serial, X509_get_serialNumber(cert))) { + goto error; + } + + /* + * Validity period - Current Chrome & Firefox make it 31 days starting + * with yesterday at the current time, so we will do the same. + */ + if (!X509_time_adj_ex(X509_get_notBefore(cert), -1, 0, NULL) + || !X509_time_adj_ex(X509_get_notAfter(cert), 30, 0, NULL)) { + goto error; + } + + /* Set the name and issuer */ + if (!(name = X509_get_subject_name(cert)) + || !X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC, + (unsigned char *) "asterisk", -1, -1, 0) + || !X509_set_issuer_name(cert, name)) { + goto error; + } + + /* Sign it */ + if (!X509_sign(cert, keypair, EVP_sha256())) { + goto error; + } + + *certificate = cert; + + return 0; + +error: + BN_free(serial); + X509_free(cert); + + return -1; +} + +static int create_certificate_ephemeral(struct ast_rtp_instance *instance, + const struct ast_rtp_dtls_cfg *dtls_cfg, + struct dtls_cert_info *cert_info) +{ + /* Make sure these are initialized */ + cert_info->private_key = NULL; + cert_info->certificate = NULL; + + if (create_ephemeral_ec_keypair(&cert_info->private_key)) { + ast_log(LOG_ERROR, "Failed to create ephemeral ECDSA keypair\n"); + goto error; + } + + if (create_ephemeral_certificate(cert_info->private_key, &cert_info->certificate)) { + ast_log(LOG_ERROR, "Failed to create ephemeral X509 certificate\n"); + goto error; + } + + return 0; + + error: + X509_free(cert_info->certificate); + EVP_PKEY_free(cert_info->private_key); + + return -1; +} + +#else + +static void configure_dhparams(const struct ast_rtp *rtp, const struct ast_rtp_dtls_cfg *dtls_cfg) +{ +} + +static int create_certificate_ephemeral(struct ast_rtp_instance *instance, + const struct ast_rtp_dtls_cfg *dtls_cfg, + struct dtls_cert_info *cert_info) +{ + ast_log(LOG_ERROR, "Your version of OpenSSL does not support ECDSA keys\n"); + return -1; +} + +#endif /* HAVE_OPENSSL_EC */ + +static int create_certificate_from_file(struct ast_rtp_instance *instance, + const struct ast_rtp_dtls_cfg *dtls_cfg, + struct dtls_cert_info *cert_info) +{ + FILE *fp; + BIO *certbio = NULL; + EVP_PKEY *private_key = NULL; + X509 *cert = NULL; + char *private_key_file = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile; + + fp = fopen(private_key_file, "r"); + if (!fp) { + ast_log(LOG_ERROR, "Failed to read private key from file '%s': %s\n", private_key_file, strerror(errno)); + goto error; + } + + if (!PEM_read_PrivateKey(fp, &private_key, NULL, NULL)) { + ast_log(LOG_ERROR, "Failed to read private key from PEM file '%s'\n", private_key_file); + fclose(fp); + goto error; + } + + if (fclose(fp)) { + ast_log(LOG_ERROR, "Failed to close private key file '%s': %s\n", private_key_file, strerror(errno)); + goto error; + } + + certbio = BIO_new(BIO_s_file()); + if (!certbio) { + ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n", + instance); + goto error; + } + + if (!BIO_read_filename(certbio, dtls_cfg->certfile) + || !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL))) { + ast_log(LOG_ERROR, "Failed to read certificate from file '%s'\n", dtls_cfg->certfile); + goto error; + } + + cert_info->private_key = private_key; + cert_info->certificate = cert; + + BIO_free_all(certbio); + + return 0; + +error: + X509_free(cert); + BIO_free_all(certbio); + EVP_PKEY_free(private_key); + + return -1; +} + +static int load_dtls_certificate(struct ast_rtp_instance *instance, + const struct ast_rtp_dtls_cfg *dtls_cfg, + struct dtls_cert_info *cert_info) +{ + if (dtls_cfg->ephemeral_cert) { + return create_certificate_ephemeral(instance, dtls_cfg, cert_info); + } else if (!ast_strlen_zero(dtls_cfg->certfile)) { + return create_certificate_from_file(instance, dtls_cfg, cert_info); + } else { + return -1; + } +} -#endif /* #ifdef HAVE_OPENSSL_EC */ +/*! \pre instance is locked */ +static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct dtls_cert_info cert_info = { 0 }; + int res; + + if (!dtls_cfg->enabled) { + return 0; + } + + if (!ast_rtp_engine_srtp_is_registered()) { + ast_log(LOG_ERROR, "SRTP support module is not loaded or available. Try loading res_srtp.so.\n"); + return -1; + } + + if (rtp->ssl_ctx) { + return 0; + } + + rtp->ssl_ctx = SSL_CTX_new(get_dtls_method()); + if (!rtp->ssl_ctx) { + return -1; + } + + SSL_CTX_set_read_ahead(rtp->ssl_ctx, 1); + + configure_dhparams(rtp, dtls_cfg); rtp->dtls_verify = dtls_cfg->verify; @@ -1680,25 +1909,22 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con rtp->local_hash = dtls_cfg->hash; - if (!ast_strlen_zero(dtls_cfg->certfile)) { - char *private = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile; - BIO *certbio; - X509 *cert = NULL; + if (!load_dtls_certificate(instance, dtls_cfg, &cert_info)) { const EVP_MD *type; unsigned int size, i; unsigned char fingerprint[EVP_MAX_MD_SIZE]; char *local_fingerprint = rtp->local_fingerprint; - if (!SSL_CTX_use_certificate_file(rtp->ssl_ctx, dtls_cfg->certfile, SSL_FILETYPE_PEM)) { - ast_log(LOG_ERROR, "Specified certificate file '%s' for RTP instance '%p' could not be used\n", - dtls_cfg->certfile, instance); + if (!SSL_CTX_use_certificate(rtp->ssl_ctx, cert_info.certificate)) { + ast_log(LOG_ERROR, "Specified certificate for RTP instance '%p' could not be used\n", + instance); return -1; } - if (!SSL_CTX_use_PrivateKey_file(rtp->ssl_ctx, private, SSL_FILETYPE_PEM) || - !SSL_CTX_check_private_key(rtp->ssl_ctx)) { - ast_log(LOG_ERROR, "Specified private key file '%s' for RTP instance '%p' could not be used\n", - private, instance); + if (!SSL_CTX_use_PrivateKey(rtp->ssl_ctx, cert_info.private_key) + || !SSL_CTX_check_private_key(rtp->ssl_ctx)) { + ast_log(LOG_ERROR, "Specified private key for RTP instance '%p' could not be used\n", + instance); return -1; } @@ -1712,22 +1938,9 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con return -1; } - if (!(certbio = BIO_new(BIO_s_file()))) { - ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n", - instance); - return -1; - } - - if (!BIO_read_filename(certbio, dtls_cfg->certfile) || - !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL)) || - !X509_digest(cert, type, fingerprint, &size) || - !size) { - ast_log(LOG_ERROR, "Could not produce fingerprint from certificate '%s' for RTP instance '%p'\n", - dtls_cfg->certfile, instance); - BIO_free_all(certbio); - if (cert) { - X509_free(cert); - } + if (!X509_digest(cert_info.certificate, type, fingerprint, &size) || !size) { + ast_log(LOG_ERROR, "Could not produce fingerprint from certificate for RTP instance '%p'\n", + instance); return -1; } @@ -1736,10 +1949,10 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con local_fingerprint += 3; } - *(local_fingerprint-1) = 0; + *(local_fingerprint - 1) = 0; - BIO_free_all(certbio); - X509_free(cert); + EVP_PKEY_free(cert_info.private_key); + X509_free(cert_info.certificate); } if (!ast_strlen_zero(dtls_cfg->cipher)) { |