gboolean janus_dtls_retry(gpointer stack) { janus_dtls_srtp *dtls = (janus_dtls_srtp *)stack; if(dtls == NULL) return FALSE; janus_ice_component *component = (janus_ice_component *)dtls->component; if(component == NULL) return FALSE; janus_ice_stream *stream = component->stream; if(!stream) return FALSE; janus_ice_handle *handle = stream->handle; if(!handle) return FALSE; if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)) return FALSE; if(dtls->dtls_state == JANUS_DTLS_STATE_CONNECTED) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS already set up, disabling retransmission timer!\n", handle->handle_id); if(component->source != NULL) { g_source_destroy(component->source); g_source_unref(component->source); component->source = NULL; } return FALSE; } struct timeval timeout = {0}; DTLSv1_get_timeout(dtls->ssl, &timeout); guint64 timeout_value = timeout.tv_sec*1000 + timeout.tv_usec/1000; JANUS_LOG(LOG_HUGE, "[%"SCNu64"] DTLSv1_get_timeout: %"SCNu64"\n", handle->handle_id, timeout_value); if(timeout_value == 0) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS timeout on component %d of stream %d, retransmitting\n", handle->handle_id, component->component_id, stream->stream_id); DTLSv1_handle_timeout(dtls->ssl); janus_dtls_fd_bridge(dtls); } return TRUE; }
void janus_dtls_srtp_send_alert(janus_dtls_srtp *dtls) { /* Send alert */ if(dtls != NULL && dtls->ssl != NULL) { SSL_shutdown(dtls->ssl); janus_dtls_fd_bridge(dtls); } }
void janus_dtls_srtp_handshake(janus_dtls_srtp *dtls) { if(dtls == NULL || dtls->ssl == NULL) return; if(dtls->dtls_state == JANUS_DTLS_STATE_CREATED) dtls->dtls_state = JANUS_DTLS_STATE_TRYING; SSL_do_handshake(dtls->ssl); janus_dtls_fd_bridge(dtls); }
void janus_dtls_srtp_send_alert(janus_dtls_srtp *dtls) { /* Send alert */ janus_refcount_increase(&dtls->ref); if(dtls != NULL && dtls->ssl != NULL) { SSL_shutdown(dtls->ssl); janus_dtls_fd_bridge(dtls); } janus_refcount_decrease(&dtls->ref); }
int janus_dtls_send_sctp_data(janus_dtls_srtp *dtls, char *buf, int len) { if(dtls == NULL || buf == NULL || len < 1) return -1; int res = SSL_write(dtls->ssl, buf, len); if(res <= 0) { unsigned long err = SSL_get_error(dtls->ssl, res); JANUS_LOG(LOG_ERR, "Error sending data: %s\n", ERR_reason_error_string(err)); } else { janus_dtls_fd_bridge(dtls); } return res; }
gboolean janus_dtls_retry(gpointer stack) { janus_dtls_srtp *dtls = (janus_dtls_srtp *)stack; if(dtls == NULL) return FALSE; janus_ice_component *component = (janus_ice_component *)dtls->component; if(component == NULL) return FALSE; janus_ice_stream *stream = component->stream; if(!stream) goto stoptimer; janus_ice_handle *handle = stream->handle; if(!handle) goto stoptimer; if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)) goto stoptimer; if(dtls->dtls_state == JANUS_DTLS_STATE_CONNECTED) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS already set up, disabling retransmission timer!\n", handle->handle_id); goto stoptimer; } if(janus_get_monotonic_time() - dtls->dtls_started >= 20*G_USEC_PER_SEC) { /* FIXME Should we really give up after 20 seconds waiting for DTLS? */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] DTLS taking too much time for component %d in stream %d...\n", handle->handle_id, component->component_id, stream->stream_id); janus_ice_webrtc_hangup(handle, "DTLS timeout"); goto stoptimer; } struct timeval timeout = {0}; DTLSv1_get_timeout(dtls->ssl, &timeout); guint64 timeout_value = timeout.tv_sec*1000 + timeout.tv_usec/1000; JANUS_LOG(LOG_HUGE, "[%"SCNu64"] DTLSv1_get_timeout: %"SCNu64"\n", handle->handle_id, timeout_value); if(timeout_value == 0) { dtls->retransmissions++; JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS timeout on component %d of stream %d, retransmitting\n", handle->handle_id, component->component_id, stream->stream_id); /* Notify event handlers */ janus_dtls_notify_state_change(dtls); /* Retransmit the packet */ DTLSv1_handle_timeout(dtls->ssl); janus_dtls_fd_bridge(dtls); } return TRUE; stoptimer: if(component->dtlsrt_source != NULL) { g_source_destroy(component->dtlsrt_source); g_source_unref(component->dtlsrt_source); component->dtlsrt_source = NULL; } return FALSE; }
void janus_dtls_srtp_handshake(janus_dtls_srtp *dtls) { if(dtls == NULL || dtls->ssl == NULL) return; if(dtls->dtls_state == JANUS_DTLS_STATE_CREATED) { /* Starting the handshake now: enforce the role */ dtls->dtls_started = janus_get_monotonic_time(); if(dtls->dtls_role == JANUS_DTLS_ROLE_CLIENT) { SSL_set_connect_state(dtls->ssl); } else { SSL_set_accept_state(dtls->ssl); } dtls->dtls_state = JANUS_DTLS_STATE_TRYING; } SSL_do_handshake(dtls->ssl); janus_dtls_fd_bridge(dtls); /* Notify event handlers */ janus_dtls_notify_state_change(dtls); }
void janus_dtls_srtp_incoming_msg(janus_dtls_srtp *dtls, char *buf, uint16_t len) { if(dtls == NULL) { JANUS_LOG(LOG_ERR, "No DTLS-SRTP stack, no incoming message...\n"); return; } janus_ice_component *component = (janus_ice_component *)dtls->component; if(component == NULL) { JANUS_LOG(LOG_ERR, "No component, no DTLS...\n"); return; } janus_ice_stream *stream = component->stream; if(!stream) { JANUS_LOG(LOG_ERR, "No stream, no DTLS...\n"); return; } janus_ice_handle *handle = stream->handle; if(!handle || !handle->agent) { JANUS_LOG(LOG_ERR, "No handle/agent, no DTLS...\n"); return; } if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Alert already triggered, clearing up...\n", handle->handle_id); return; } if(!dtls->ssl || !dtls->read_bio) { JANUS_LOG(LOG_ERR, "[%"SCNu64"] No DTLS stuff for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); return; } janus_dtls_fd_bridge(dtls); int written = BIO_write(dtls->read_bio, buf, len); JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Written %d of those bytes on the read BIO...\n", handle->handle_id, written); janus_dtls_fd_bridge(dtls); /* Try to read data */ char data[1500]; /* FIXME */ memset(&data, 0, 1500); int read = SSL_read(dtls->ssl, &data, 1500); JANUS_LOG(LOG_HUGE, "[%"SCNu64"] ... and read %d of them from SSL...\n", handle->handle_id, read); if(read < 0) { unsigned long err = SSL_get_error(dtls->ssl, read); if(err == SSL_ERROR_SSL) { /* Ops, something went wrong with the DTLS handshake */ char error[200]; ERR_error_string_n(ERR_get_error(), error, 200); JANUS_LOG(LOG_ERR, "[%"SCNu64"] Handshake error: %s\n", handle->handle_id, error); return; } } janus_dtls_fd_bridge(dtls); if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP) || janus_is_stopping()) { /* DTLS alert triggered, we should end it here */ JANUS_LOG(LOG_VERB, "[%"SCNu64"] Forced to stop it here...\n", handle->handle_id); return; } if(!SSL_is_init_finished(dtls->ssl)) { /* Nothing else to do for now */ JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Initialization not finished yet...\n", handle->handle_id); return; } if(dtls->ready) { /* There's data to be read? */ JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Any data available?\n", handle->handle_id); #ifdef HAVE_SCTP if(dtls->sctp != NULL && read > 0) { JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Sending data (%d bytes) to the SCTP stack...\n", handle->handle_id, read); janus_sctp_data_from_dtls(dtls->sctp, data, read); } #else if(read > 0) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Data available but Data Channels support disabled...\n", handle->handle_id); } #endif } else { JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS established, yay!\n", handle->handle_id); /* Check the remote fingerprint */ X509 *rcert = SSL_get_peer_certificate(dtls->ssl); if(!rcert) { JANUS_LOG(LOG_ERR, "[%"SCNu64"] No remote certificate??\n", handle->handle_id); } else { unsigned int rsize; unsigned char rfingerprint[EVP_MAX_MD_SIZE]; char remote_fingerprint[160]; char *rfp = (char *)&remote_fingerprint; if(handle->remote_hashing && !strcasecmp(handle->remote_hashing, "sha-1")) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-1 fingerprint of remote certificate...\n", handle->handle_id); X509_digest(rcert, EVP_sha1(), (unsigned char *)rfingerprint, &rsize); } else { JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-256 fingerprint of remote certificate...\n", handle->handle_id); X509_digest(rcert, EVP_sha256(), (unsigned char *)rfingerprint, &rsize); } X509_free(rcert); rcert = NULL; unsigned int i = 0; for(i = 0; i < rsize; i++) { g_snprintf(rfp, 4, "%.2X:", rfingerprint[i]); rfp += 3; } *(rfp-1) = 0; JANUS_LOG(LOG_VERB, "[%"SCNu64"] Remote fingerprint (%s) of the client is %s\n", handle->handle_id, handle->remote_hashing ? handle->remote_hashing : "sha-256", remote_fingerprint); if(!strcasecmp(remote_fingerprint, handle->remote_fingerprint ? handle->remote_fingerprint : "(none)")) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint is a match!\n", handle->handle_id); dtls->dtls_state = JANUS_DTLS_STATE_CONNECTED; dtls->dtls_connected = janus_get_monotonic_time(); } else { /* FIXME NOT a match! MITM? */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Fingerprint is NOT a match! got %s, expected %s\n", handle->handle_id, remote_fingerprint, handle->remote_fingerprint); dtls->dtls_state = JANUS_DTLS_STATE_FAILED; goto done; } if(dtls->dtls_state == JANUS_DTLS_STATE_CONNECTED) { if(component->stream_id == handle->audio_id || component->stream_id == handle->video_id) { /* Complete with SRTP setup */ unsigned char material[SRTP_MASTER_LENGTH*2]; unsigned char *local_key, *local_salt, *remote_key, *remote_salt; /* Export keying material for SRTP */ if (!SSL_export_keying_material(dtls->ssl, material, SRTP_MASTER_LENGTH*2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) { /* Oops... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, couldn't extract SRTP keying material for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); goto done; } /* Key derivation (http://tools.ietf.org/html/rfc5764#section-4.2) */ if(dtls->dtls_role == JANUS_DTLS_ROLE_CLIENT) { local_key = material; remote_key = local_key + SRTP_MASTER_KEY_LENGTH; local_salt = remote_key + SRTP_MASTER_KEY_LENGTH; remote_salt = local_salt + SRTP_MASTER_SALT_LENGTH; } else { remote_key = material; local_key = remote_key + SRTP_MASTER_KEY_LENGTH; remote_salt = local_key + SRTP_MASTER_KEY_LENGTH; local_salt = remote_salt + SRTP_MASTER_SALT_LENGTH; } /* Build master keys and set SRTP policies */ /* Remote (inbound) */ crypto_policy_set_rtp_default(&(dtls->remote_policy.rtp)); crypto_policy_set_rtcp_default(&(dtls->remote_policy.rtcp)); dtls->remote_policy.ssrc.type = ssrc_any_inbound; unsigned char remote_policy_key[SRTP_MASTER_LENGTH]; dtls->remote_policy.key = (unsigned char *)&remote_policy_key; memcpy(dtls->remote_policy.key, remote_key, SRTP_MASTER_KEY_LENGTH); memcpy(dtls->remote_policy.key + SRTP_MASTER_KEY_LENGTH, remote_salt, SRTP_MASTER_SALT_LENGTH); #if HAS_DTLS_WINDOW_SIZE dtls->remote_policy.window_size = 128; dtls->remote_policy.allow_repeat_tx = 0; #endif dtls->remote_policy.next = NULL; /* Local (outbound) */ crypto_policy_set_rtp_default(&(dtls->local_policy.rtp)); crypto_policy_set_rtcp_default(&(dtls->local_policy.rtcp)); dtls->local_policy.ssrc.type = ssrc_any_outbound; unsigned char local_policy_key[SRTP_MASTER_LENGTH]; dtls->local_policy.key = (unsigned char *)&local_policy_key; memcpy(dtls->local_policy.key, local_key, SRTP_MASTER_KEY_LENGTH); memcpy(dtls->local_policy.key + SRTP_MASTER_KEY_LENGTH, local_salt, SRTP_MASTER_SALT_LENGTH); #if HAS_DTLS_WINDOW_SIZE dtls->local_policy.window_size = 128; dtls->local_policy.allow_repeat_tx = 0; #endif dtls->local_policy.next = NULL; /* Create SRTP sessions */ err_status_t res = srtp_create(&(dtls->srtp_in), &(dtls->remote_policy)); if(res != err_status_ok) { /* Something went wrong... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating inbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); JANUS_LOG(LOG_ERR, "[%"SCNu64"] -- %d (%s)\n", handle->handle_id, res, janus_get_srtp_error(res)); goto done; } JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created inbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id); res = srtp_create(&(dtls->srtp_out), &(dtls->local_policy)); if(res != err_status_ok) { /* Something went wrong... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating outbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); JANUS_LOG(LOG_ERR, "[%"SCNu64"] -- %d (%s)\n", handle->handle_id, res, janus_get_srtp_error(res)); goto done; } dtls->srtp_valid = 1; JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created outbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id); } #ifdef HAVE_SCTP if(component->stream_id == handle->data_id || (janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS))) { /* FIXME Create SCTP association as well (5000 should be dynamic, from the SDP...) */ dtls->sctp = janus_sctp_association_create(dtls, handle->handle_id, 5000); if(dtls->sctp != NULL) { /* FIXME We need to start it in a thread, though, since it has blocking accept/connect stuff */ GError *error = NULL; g_thread_try_new("DTLS-SCTP", janus_dtls_sctp_setup_thread, dtls, &error); if(error != NULL) { /* Something went wrong... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Got error %d (%s) trying to launch the DTLS-SCTP thread...\n", handle->handle_id, error->code, error->message ? error->message : "??"); } dtls->srtp_valid = 1; } } #endif dtls->ready = 1; } done: if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT) && dtls->srtp_valid) { /* Handshake successfully completed */ janus_ice_dtls_handshake_done(handle, component); } else { /* Something went wrong in either DTLS or SRTP... tell the plugin about it */ janus_dtls_callback(dtls->ssl, SSL_CB_ALERT, 0); janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING); } } } }
void janus_dtls_srtp_incoming_msg(janus_dtls_srtp *dtls, char *buf, uint16_t len) { if(dtls == NULL) { JANUS_LOG(LOG_ERR, "No DTLS-SRTP stack, no incoming message...\n"); return; } janus_ice_component *component = (janus_ice_component *)dtls->component; if(component == NULL) { JANUS_LOG(LOG_ERR, "No component, no DTLS...\n"); return; } janus_ice_stream *stream = component->stream; if(!stream) { JANUS_LOG(LOG_ERR, "No stream, no DTLS...\n"); return; } janus_ice_handle *handle = stream->handle; if(!handle || !handle->agent) { JANUS_LOG(LOG_ERR, "No handle/agent, no DTLS...\n"); return; } if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) { JANUS_LOG(LOG_ERR, "Alert already received, clearing up...\n"); return; } if(!dtls->ssl || !dtls->read_bio) { JANUS_LOG(LOG_ERR, "[%"SCNu64"] No DTLS stuff for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); return; } /* We just got a message, can we get rid of the last sent message? */ if(dtls->dtls_last_msg != NULL) { g_free(dtls->dtls_last_msg); dtls->dtls_last_msg = NULL; dtls->dtls_last_len = 0; } janus_dtls_fd_bridge(dtls); int written = BIO_write(dtls->read_bio, buf, len); JANUS_LOG(LOG_HUGE, " Written %d of those bytes on the read BIO...\n", written); janus_dtls_fd_bridge(dtls); int read = SSL_read(dtls->ssl, buf, len); JANUS_LOG(LOG_HUGE, " ...and read %d of them from SSL...\n", read); janus_dtls_fd_bridge(dtls); if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP) || janus_is_stopping()) { /* DTLS alert received, we should end it here */ JANUS_LOG(LOG_VERB, "[%"SCNu64"] Forced to stop it here...\n", handle->handle_id); return; } if(SSL_is_init_finished(dtls->ssl)) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS established, yay!\n", handle->handle_id); /* Check the remote fingerprint */ X509 *rcert = SSL_get_peer_certificate(dtls->ssl); if(!rcert) { JANUS_LOG(LOG_ERR, "[%"SCNu64"] No remote certificate??\n", handle->handle_id); } else { unsigned int rsize; unsigned char rfingerprint[EVP_MAX_MD_SIZE]; char remote_fingerprint[160]; char *rfp = (char *)&remote_fingerprint; if(handle->remote_hashing && !strcasecmp(handle->remote_hashing, "sha-1")) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-1 fingerprint of remote certificate...\n", handle->handle_id); X509_digest(rcert, EVP_sha1(), (unsigned char *)rfingerprint, &rsize); } else { JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-256 fingerprint of remote certificate...\n", handle->handle_id); X509_digest(rcert, EVP_sha256(), (unsigned char *)rfingerprint, &rsize); } X509_free(rcert); rcert = NULL; int i = 0; for(i = 0; i < rsize; i++) { sprintf(rfp, "%.2X:", rfingerprint[i]); rfp += 3; } *(rfp-1) = 0; JANUS_LOG(LOG_VERB, "[%"SCNu64"] Remote fingerprint (%s) of the client is %s\n", handle->handle_id, handle->remote_hashing ? handle->remote_hashing : "sha-256", remote_fingerprint); if(!strcasecmp(remote_fingerprint, handle->remote_fingerprint ? handle->remote_fingerprint : "(none)")) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint is a match!\n", handle->handle_id); dtls->dtls_state = JANUS_DTLS_STATE_CONNECTED; } else { /* FIXME NOT a match! MITM? */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Fingerprint is NOT a match! expected %s\n", handle->handle_id, handle->remote_fingerprint); dtls->dtls_state = JANUS_DTLS_STATE_FAILED; goto done; } if(dtls->dtls_state == JANUS_DTLS_STATE_CONNECTED) { /* Complete with SRTP setup */ unsigned char material[SRTP_MASTER_LENGTH*2]; unsigned char *local_key, *local_salt, *remote_key, *remote_salt; /* Export keying material for SRTP */ if (!SSL_export_keying_material(dtls->ssl, material, SRTP_MASTER_LENGTH*2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) { /* Oops... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, couldn't extract SRTP keying material for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); goto done; } /* Key derivation (http://tools.ietf.org/html/rfc5764#section-4.2) */ if(dtls->dtls_role == JANUS_DTLS_ROLE_CLIENT) { local_key = material; remote_key = local_key + SRTP_MASTER_KEY_LENGTH; local_salt = remote_key + SRTP_MASTER_KEY_LENGTH; remote_salt = local_salt + SRTP_MASTER_SALT_LENGTH; } else { remote_key = material; local_key = remote_key + SRTP_MASTER_KEY_LENGTH; remote_salt = local_key + SRTP_MASTER_KEY_LENGTH; local_salt = remote_salt + SRTP_MASTER_SALT_LENGTH; } /* Build master keys and set SRTP policies */ /* Remote (inbound) */ crypto_policy_set_rtp_default(&(dtls->remote_policy.rtp)); crypto_policy_set_rtcp_default(&(dtls->remote_policy.rtcp)); dtls->remote_policy.ssrc.type = ssrc_any_inbound; dtls->remote_policy.key = calloc(SRTP_MASTER_LENGTH+8, sizeof(char)); if(dtls->remote_policy.key == NULL) { JANUS_LOG(LOG_FATAL, "Memory error!\n"); goto done; } memcpy(dtls->remote_policy.key, remote_key, SRTP_MASTER_KEY_LENGTH); memcpy(dtls->remote_policy.key + SRTP_MASTER_KEY_LENGTH, remote_salt, SRTP_MASTER_SALT_LENGTH); dtls->remote_policy.window_size = 128; dtls->remote_policy.allow_repeat_tx = 0; dtls->remote_policy.next = NULL; /* Local (outbound) */ crypto_policy_set_rtp_default(&(dtls->local_policy.rtp)); crypto_policy_set_rtcp_default(&(dtls->local_policy.rtcp)); dtls->local_policy.ssrc.type = ssrc_any_outbound; dtls->local_policy.key = calloc(SRTP_MASTER_LENGTH+8, sizeof(char)); if(dtls->local_policy.key == NULL) { JANUS_LOG(LOG_FATAL, "Memory error!\n"); goto done; } memcpy(dtls->local_policy.key, local_key, SRTP_MASTER_KEY_LENGTH); memcpy(dtls->local_policy.key + SRTP_MASTER_KEY_LENGTH, local_salt, SRTP_MASTER_SALT_LENGTH); dtls->local_policy.window_size = 128; dtls->local_policy.allow_repeat_tx = 0; dtls->local_policy.next = NULL; /* Create SRTP sessions */ err_status_t res = srtp_create(&(dtls->srtp_in), &(dtls->remote_policy)); if(res != err_status_ok) { /* Something went wrong... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating inbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); JANUS_LOG(LOG_ERR, "[%"SCNu64"] -- %d (%s)\n", handle->handle_id, res, janus_get_srtp_error(res)); goto done; } JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created inbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id); res = srtp_create(&(dtls->srtp_out), &(dtls->local_policy)); if(res != err_status_ok) { /* Something went wrong... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating outbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); JANUS_LOG(LOG_ERR, "[%"SCNu64"] -- %d (%s)\n", handle->handle_id, res, janus_get_srtp_error(res)); goto done; } dtls->srtp_valid = 1; JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created outbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id); } done: if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT) && dtls->srtp_valid) { /* Handshake successfully completed */ janus_ice_dtls_handshake_done(handle, component); } else { /* Something went wrong in either DTLS or SRTP... tell the plugin about it */ janus_dtls_callback(dtls->ssl, SSL_CB_ALERT, 0); } } } }
void janus_dtls_srtp_incoming_msg(janus_dtls_srtp *dtls, char *buf, uint16_t len) { if(dtls == NULL) { JANUS_LOG(LOG_ERR, "No DTLS-SRTP stack, no incoming message...\n"); return; } janus_ice_component *component = (janus_ice_component *)dtls->component; if(component == NULL) { JANUS_LOG(LOG_ERR, "No component, no DTLS...\n"); return; } janus_ice_stream *stream = component->stream; if(!stream) { JANUS_LOG(LOG_ERR, "No stream, no DTLS...\n"); return; } janus_ice_handle *handle = stream->handle; if(!handle || !handle->agent) { JANUS_LOG(LOG_ERR, "No handle/agent, no DTLS...\n"); return; } if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Alert already triggered, clearing up...\n", handle->handle_id); return; } if(!dtls->ssl || !dtls->read_bio) { JANUS_LOG(LOG_ERR, "[%"SCNu64"] No DTLS stuff for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); return; } if(dtls->dtls_started == 0) { /* Handshake not started yet: maybe we're still waiting for the answer and the DTLS role? */ return; } janus_dtls_fd_bridge(dtls); int written = BIO_write(dtls->read_bio, buf, len); if(written != len) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Only written %d/%d of those bytes on the read BIO...\n", handle->handle_id, written, len); } else { JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Written %d bytes on the read BIO...\n", handle->handle_id, written); } janus_dtls_fd_bridge(dtls); /* Try to read data */ char data[1500]; /* FIXME */ memset(&data, 0, 1500); int read = SSL_read(dtls->ssl, &data, 1500); JANUS_LOG(LOG_HUGE, "[%"SCNu64"] ... and read %d of them from SSL...\n", handle->handle_id, read); if(read < 0) { unsigned long err = SSL_get_error(dtls->ssl, read); if(err == SSL_ERROR_SSL) { /* Ops, something went wrong with the DTLS handshake */ char error[200]; ERR_error_string_n(ERR_get_error(), error, 200); JANUS_LOG(LOG_ERR, "[%"SCNu64"] Handshake error: %s\n", handle->handle_id, error); return; } } janus_dtls_fd_bridge(dtls); if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP) || janus_is_stopping()) { /* DTLS alert triggered, we should end it here */ JANUS_LOG(LOG_VERB, "[%"SCNu64"] Forced to stop it here...\n", handle->handle_id); return; } if(!SSL_is_init_finished(dtls->ssl)) { /* Nothing else to do for now */ JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Initialization not finished yet...\n", handle->handle_id); return; } if(dtls->ready) { /* There's data to be read? */ JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Any data available?\n", handle->handle_id); #ifdef HAVE_SCTP if(dtls->sctp != NULL && read > 0) { JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Sending data (%d bytes) to the SCTP stack...\n", handle->handle_id, read); janus_sctp_data_from_dtls(dtls->sctp, data, read); } #else if(read > 0) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Data available but Data Channels support disabled...\n", handle->handle_id); } #endif } else { JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS established, yay!\n", handle->handle_id); /* Check the remote fingerprint */ X509 *rcert = SSL_get_peer_certificate(dtls->ssl); if(!rcert) { JANUS_LOG(LOG_ERR, "[%"SCNu64"] No remote certificate?? (%s)\n", handle->handle_id, ERR_reason_error_string(ERR_get_error())); } else { unsigned int rsize; unsigned char rfingerprint[EVP_MAX_MD_SIZE]; char remote_fingerprint[160]; char *rfp = (char *)&remote_fingerprint; if(stream->remote_hashing && !strcasecmp(stream->remote_hashing, "sha-1")) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-1 fingerprint of remote certificate...\n", handle->handle_id); X509_digest(rcert, EVP_sha1(), (unsigned char *)rfingerprint, &rsize); } else { JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-256 fingerprint of remote certificate...\n", handle->handle_id); X509_digest(rcert, EVP_sha256(), (unsigned char *)rfingerprint, &rsize); } X509_free(rcert); rcert = NULL; unsigned int i = 0; for(i = 0; i < rsize; i++) { g_snprintf(rfp, 4, "%.2X:", rfingerprint[i]); rfp += 3; } *(rfp-1) = 0; JANUS_LOG(LOG_VERB, "[%"SCNu64"] Remote fingerprint (%s) of the client is %s\n", handle->handle_id, stream->remote_hashing ? stream->remote_hashing : "sha-256", remote_fingerprint); if(!strcasecmp(remote_fingerprint, stream->remote_fingerprint ? stream->remote_fingerprint : "(none)")) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint is a match!\n", handle->handle_id); dtls->dtls_state = JANUS_DTLS_STATE_CONNECTED; dtls->dtls_connected = janus_get_monotonic_time(); /* Notify event handlers */ janus_dtls_notify_state_change(dtls); } else { /* FIXME NOT a match! MITM? */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Fingerprint is NOT a match! got %s, expected %s\n", handle->handle_id, remote_fingerprint, stream->remote_fingerprint); dtls->dtls_state = JANUS_DTLS_STATE_FAILED; /* Notify event handlers */ janus_dtls_notify_state_change(dtls); goto done; } if(dtls->dtls_state == JANUS_DTLS_STATE_CONNECTED) { /* Which SRTP profile is being negotiated? */ SRTP_PROTECTION_PROFILE *srtp_profile = SSL_get_selected_srtp_profile(dtls->ssl); if(srtp_profile == NULL) { /* Should never happen, but just in case... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] No SRTP profile selected...\n", handle->handle_id); dtls->dtls_state = JANUS_DTLS_STATE_FAILED; /* Notify event handlers */ janus_dtls_notify_state_change(dtls); goto done; } JANUS_LOG(LOG_VERB, "[%"SCNu64"] %s\n", handle->handle_id, srtp_profile->name); int key_length = 0, salt_length = 0, master_length = 0; switch(srtp_profile->id) { case SRTP_AES128_CM_SHA1_80: case SRTP_AES128_CM_SHA1_32: key_length = SRTP_MASTER_KEY_LENGTH; salt_length = SRTP_MASTER_SALT_LENGTH; master_length = SRTP_MASTER_LENGTH; break; #ifdef HAVE_SRTP_AESGCM case SRTP_AEAD_AES_256_GCM: key_length = SRTP_AESGCM256_MASTER_KEY_LENGTH; salt_length = SRTP_AESGCM256_MASTER_SALT_LENGTH; master_length = SRTP_AESGCM256_MASTER_LENGTH; break; case SRTP_AEAD_AES_128_GCM: key_length = SRTP_AESGCM128_MASTER_KEY_LENGTH; salt_length = SRTP_AESGCM128_MASTER_SALT_LENGTH; master_length = SRTP_AESGCM128_MASTER_LENGTH; break; #endif default: /* Will never happen? */ JANUS_LOG(LOG_WARN, "[%"SCNu64"] Unsupported SRTP profile %lu\n", handle->handle_id, srtp_profile->id); break; } JANUS_LOG(LOG_VERB, "[%"SCNu64"] Key/Salt/Master: %d/%d/%d\n", handle->handle_id, master_length, key_length, salt_length); /* Complete with SRTP setup */ unsigned char material[master_length*2]; unsigned char *local_key, *local_salt, *remote_key, *remote_salt; /* Export keying material for SRTP */ if(!SSL_export_keying_material(dtls->ssl, material, master_length*2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) { /* Oops... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, couldn't extract SRTP keying material for component %d in stream %d?? (%s)\n", handle->handle_id, component->component_id, stream->stream_id, ERR_reason_error_string(ERR_get_error())); goto done; } /* Key derivation (http://tools.ietf.org/html/rfc5764#section-4.2) */ if(dtls->dtls_role == JANUS_DTLS_ROLE_CLIENT) { local_key = material; remote_key = local_key + key_length; local_salt = remote_key + key_length; remote_salt = local_salt + salt_length; } else { remote_key = material; local_key = remote_key + key_length; remote_salt = local_key + key_length; local_salt = remote_salt + salt_length; } /* Build master keys and set SRTP policies */ /* Remote (inbound) */ switch(srtp_profile->id) { case SRTP_AES128_CM_SHA1_80: srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->remote_policy.rtp)); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->remote_policy.rtcp)); break; case SRTP_AES128_CM_SHA1_32: srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&(dtls->remote_policy.rtp)); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->remote_policy.rtcp)); break; #ifdef HAVE_SRTP_AESGCM case SRTP_AEAD_AES_256_GCM: srtp_crypto_policy_set_aes_gcm_256_16_auth(&(dtls->remote_policy.rtp)); srtp_crypto_policy_set_aes_gcm_256_16_auth(&(dtls->remote_policy.rtcp)); break; case SRTP_AEAD_AES_128_GCM: srtp_crypto_policy_set_aes_gcm_128_16_auth(&(dtls->remote_policy.rtp)); srtp_crypto_policy_set_aes_gcm_128_16_auth(&(dtls->remote_policy.rtcp)); break; #endif default: /* Will never happen? */ JANUS_LOG(LOG_WARN, "[%"SCNu64"] Unsupported SRTP profile %s\n", handle->handle_id, srtp_profile->name); break; } dtls->remote_policy.ssrc.type = ssrc_any_inbound; unsigned char remote_policy_key[master_length]; dtls->remote_policy.key = (unsigned char *)&remote_policy_key; memcpy(dtls->remote_policy.key, remote_key, key_length); memcpy(dtls->remote_policy.key + key_length, remote_salt, salt_length); #if HAS_DTLS_WINDOW_SIZE dtls->remote_policy.window_size = 128; dtls->remote_policy.allow_repeat_tx = 0; #endif dtls->remote_policy.next = NULL; /* Local (outbound) */ switch(srtp_profile->id) { case SRTP_AES128_CM_SHA1_80: srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->local_policy.rtp)); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->local_policy.rtcp)); break; case SRTP_AES128_CM_SHA1_32: srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&(dtls->local_policy.rtp)); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->local_policy.rtcp)); break; #ifdef HAVE_SRTP_AESGCM case SRTP_AEAD_AES_256_GCM: srtp_crypto_policy_set_aes_gcm_256_16_auth(&(dtls->local_policy.rtp)); srtp_crypto_policy_set_aes_gcm_256_16_auth(&(dtls->local_policy.rtcp)); break; case SRTP_AEAD_AES_128_GCM: srtp_crypto_policy_set_aes_gcm_128_16_auth(&(dtls->local_policy.rtp)); srtp_crypto_policy_set_aes_gcm_128_16_auth(&(dtls->local_policy.rtcp)); break; #endif default: /* Will never happen? */ JANUS_LOG(LOG_WARN, "[%"SCNu64"] Unsupported SRTP profile %s\n", handle->handle_id, srtp_profile->name); break; } dtls->local_policy.ssrc.type = ssrc_any_outbound; unsigned char local_policy_key[master_length]; dtls->local_policy.key = (unsigned char *)&local_policy_key; memcpy(dtls->local_policy.key, local_key, key_length); memcpy(dtls->local_policy.key + key_length, local_salt, salt_length); #if HAS_DTLS_WINDOW_SIZE dtls->local_policy.window_size = 128; dtls->local_policy.allow_repeat_tx = 0; #endif dtls->local_policy.next = NULL; /* Create SRTP sessions */ srtp_err_status_t res = srtp_create(&(dtls->srtp_in), &(dtls->remote_policy)); if(res != srtp_err_status_ok) { /* Something went wrong... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating inbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); JANUS_LOG(LOG_ERR, "[%"SCNu64"] -- %d (%s)\n", handle->handle_id, res, janus_srtp_error_str(res)); goto done; } JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created inbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id); res = srtp_create(&(dtls->srtp_out), &(dtls->local_policy)); if(res != srtp_err_status_ok) { /* Something went wrong... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating outbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); JANUS_LOG(LOG_ERR, "[%"SCNu64"] -- %d (%s)\n", handle->handle_id, res, janus_srtp_error_str(res)); goto done; } dtls->srtp_profile = srtp_profile->id; dtls->srtp_valid = 1; JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created outbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id); #ifdef HAVE_SCTP if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS)) { /* Create SCTP association as well */ janus_dtls_srtp_create_sctp(dtls); } #endif dtls->ready = 1; } done: if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT) && dtls->srtp_valid) { /* Handshake successfully completed */ janus_ice_dtls_handshake_done(handle, component); } else { /* Something went wrong in either DTLS or SRTP... tell the plugin about it */ janus_dtls_callback(dtls->ssl, SSL_CB_ALERT, 0); janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING); } } } }