diff -urN -x _darcs -x .svn -x '*~' upstream/build_tools/menuselect-deps.in local/build_tools/menuselect-deps.in --- upstream/build_tools/menuselect-deps.in 2007-06-30 17:21:43.000000000 +0200 +++ local/build_tools/menuselect-deps.in 2007-06-30 17:52:36.000000000 +0200 @@ -11,6 +11,10 @@ IXJUSER=@PBX_IXJUSER@ KDE=@PBX_KDE@ LTDL=@PBX_LTDL@ +MUTIL=@PBX_MUTIL@ +MNETUTIL=@PBX_MNETUTIL@ +MCRYPTO=@PBX_MCRYPTO@ +MIKEY=@PBX_MIKEY@ NBS=@PBX_NBS@ NETSNMP=@PBX_NETSNMP@ NEWT=@PBX_NEWT@ @@ -26,6 +30,7 @@ SPEEX=@PBX_SPEEX@ SQLITE=@PBX_SQLITE@ SQLITE3=@PBX_SQLITE3@ +SRTP=@PBX_SRTP@ SSL=@PBX_OPENSSL@ CRYPTO=@PBX_CRYPTO@ TONEZONE=@PBX_TONEZONE@ diff -urN -x _darcs -x .svn -x '*~' upstream/channels/chan_sip.c local/channels/chan_sip.c --- upstream/channels/chan_sip.c 2007-09-03 20:59:27.000000000 +0200 +++ local/channels/chan_sip.c 2007-09-04 11:08:38.000000000 +0200 @@ -145,6 +145,10 @@ #include "asterisk/version.h" #include "asterisk/event.h" +#include "sip_srtp.h" +#include "sdp_crypto.h" +#include "sdp_mikey.h" + #ifndef FALSE #define FALSE 0 #endif @@ -207,7 +211,7 @@ #define SIP_MAX_HEADERS 64 /*!< Max amount of SIP headers to read */ #define SIP_MAX_LINES 64 /*!< Max amount of lines in SIP attachment (like SDP) */ -#define SIP_MAX_PACKET 4096 /*!< Also from RFC 3261 (2543), should sub headers tho */ +#define SIP_MAX_PACKET 8192 /*!< Also from RFC 3261 (2543), should sub headers tho */ #define INITIAL_CSEQ 101 /*!< our initial sip sequence number */ @@ -1121,6 +1125,7 @@ before strolling to the Grokyzpå (A bit unsure of this, please correct if you know more) */ + struct sip_srtp *srtp; /*!< Structure for Secure RTP session data */ }; /*! Max entires in the history list for a sip_pvt */ @@ -1780,6 +1785,11 @@ static int sip_get_codec(struct ast_channel *chan); static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect); +/*----- SRTP interface functions */ +static int setup_srtp(struct sip_pvt *p); +static int process_crypto(struct sip_pvt *p, const char *a); +static int process_mikey(struct sip_pvt *p, const char *a); + /*------ T38 Support --------- */ static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt, int reinvite); static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans); @@ -3527,6 +3537,53 @@ } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) { /* We're replacing a call. */ p->options->replaces = ast_var_value(current); + } else if (!strcasecmp(ast_var_name(current), "SIPSRTP")) { + ast_log(LOG_NOTICE, "SIPSRTP\n"); + + if (!p->srtp) { + if (setup_srtp(p) < 0) { + ast_log(LOG_WARNING, "SRTP setup failed\n"); + return -1; + } + } + + if (!strcasecmp(ast_var_value(current), "optional")) { + ast_set_flag(p->srtp, SRTP_ENCR_OPTIONAL); + } + } else if (!strcasecmp(ast_var_name(current), "SIPSRTP_CRYPTO")) { + ast_log(LOG_NOTICE, "SIPSRTP_CRYPTO\n"); + + if (!p->srtp) { + if (setup_srtp(p) < 0) { + ast_log(LOG_WARNING, "SRTP setup failed\n"); + return -1; + } + } + + if (!strcasecmp(ast_var_value(current), "enable")) { + ast_set_flag(p->srtp, SRTP_CRYPTO_ENABLE); + } else if (!strcasecmp(ast_var_value(current), "disable")) { + ast_clear_flag(p->srtp, SRTP_CRYPTO_ENABLE); + } else { + ast_log(LOG_WARNING,"Invalid SIPSRTP_CRYPTO value (%s), enable or disable expected\n", ast_var_value(current)); + } + } else if (!strcasecmp(ast_var_name(current), "SIPSRTP_MIKEY")) { + ast_log(LOG_NOTICE, "SIPSRTP_MIKEY\n"); + + if (!p->srtp) { + if (setup_srtp(p) < 0) { + ast_log(LOG_WARNING, "SRTP setup failed\n"); + return -1; + } + } + + if (!strcasecmp(ast_var_value(current), "enable")) { + ast_set_flag(p->srtp, SRTP_MIKEY_ENABLE); + } else if (!strcasecmp(ast_var_value(current), "disable")) { + ast_clear_flag(p->srtp, SRTP_MIKEY_ENABLE); + } else { + ast_log(LOG_WARNING,"Invalid SIPSRTP_MIKEY value (%s), enable or disable expected\n", ast_var_value(current)); + } } else if (!strcasecmp(ast_var_name(current), "T38CALL")) { p->t38.state = T38_LOCAL_DIRECT; ast_debug(1,"T38State change to %d on channel %s\n", p->t38.state, ast->name); @@ -3704,6 +3761,10 @@ ast_variables_destroy(p->chanvars); p->chanvars = NULL; } + if (p->srtp) { + sip_srtp_destroy(p->srtp); + p->srtp = NULL; + } ast_mutex_destroy(&p->pvt_lock); ast_string_field_free_pools(p); @@ -5529,7 +5590,9 @@ int newnoncodeccapability; int numberofmediastreams = 0; int debug = sip_debug_test_pvt(p); - + int secure_audio = FALSE; + int secure_video = FALSE; + int found_rtpmap_codecs[32]; int last_rtpmap_codec=0; @@ -5601,10 +5664,21 @@ int audio = FALSE; int video = FALSE; int text = FALSE; + char protocol[5] = ""; numberofports = 1; - if ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) || - (sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1)) { + if ((sscanf(m, "audio %d/%d RTP/%4s %n", &x, &numberofports, protocol, &len) == 3) || + (sscanf(m, "audio %d RTP/%4s %n", &x, protocol, &len) == 2)) { + if (!strcmp(protocol, "SAVP")) + secure_audio = 1; + else if (strcmp(protocol, "AVP")) { + ast_log(LOG_WARNING, "Unknown SDP media protocol in offer: %s\n", protocol); + continue; + } + if (len < 0) { + ast_log(LOG_WARNING, "Unknown SDP media type in offer: %s\n", m); + continue; + } audio = TRUE; numberofmediastreams++; /* Found audio stream in this media definition */ @@ -5619,8 +5693,18 @@ ast_verbose("Found RTP audio format %d\n", codec); ast_rtp_set_m_type(newaudiortp, codec); } - } else if ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) || - (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1)) { + } else if ((sscanf(m, "video %d/%d RTP/%4s %n", &x, &numberofports, protocol, &len) == 3) || + (sscanf(m, "video %d RTP/%4s %n", &x, protocol, &len) == 2)) { + if (!strcmp(protocol, "SAVP")) + secure_video = 1; + else if (strcmp(protocol, "AVP")) { + ast_log(LOG_WARNING, "Unknown SDP media protocol for video in offer: %s\n", protocol); + continue; + } + if (len < 0) { + ast_log(LOG_WARNING, "Unknown SDP media type for video in offer: %s\n", m); + continue; + } video = TRUE; p->novideo = FALSE; numberofmediastreams++; @@ -5790,11 +5874,6 @@ if (debug) ast_verbose("Got unsupported a:maxprate in SDP offer \n"); breakout = TRUE; - } else if (!strncasecmp(a, "crypto:", (size_t) 7)) { - /* SRTP stuff, not yet supported */ - if (debug) - ast_verbose("Got unsupported a:crypto in SDP offer \n"); - breakout = TRUE; } if (breakout) /* We have a match, skip to next header */ continue; @@ -5803,6 +5882,12 @@ if (sendonly == -1) sendonly = 1; continue; + } else if (!strncasecmp(a, "crypto:", (size_t) 7)) { + process_crypto(p, a); + continue; + } else if (!strncasecmp(a, "key-mgmt:mikey ", (size_t) 15)) { + process_mikey(p, a); + continue; } else if (!strcasecmp(a, "inactive")) { if (sendonly == -1) sendonly = 2; @@ -5862,6 +5947,11 @@ } } } + + if (secure_audio && !(p->srtp && (ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK) || ast_test_flag(p->srtp, SRTP_MIKEY_OFFER_OK)))) { + ast_log(LOG_WARNING, "Can't provide secure audio requested in SDP offer\n"); + return -2; + } if (udptlportno != -1) { int found = 0, x; @@ -5963,6 +6053,11 @@ ast_debug(3, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : ""); } + if (secure_video && !(p->srtp && (ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK) || ast_test_flag(p->srtp, SRTP_MIKEY_OFFER_OK)))) { + ast_log(LOG_WARNING, "Can't provide secure video requested in SDP offer\n"); + return -2; + } + /* Now gather all of the codecs that we are asked for: */ ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability); ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability); @@ -7063,12 +7158,17 @@ struct ast_str *a_audio = ast_str_alloca(1024); /* Attributes for audio */ struct ast_str *a_video = ast_str_alloca(1024); /* Attributes for video */ struct ast_str *a_text = ast_str_alloca(1024); /* Attributes for text */ + const char *a_crypto = NULL; + const char *a_mikey = NULL; int x; int capability; + const char *protocol = NULL; + struct sip_srtp *srtp = p->srtp; int needvideo = FALSE; int needtext = FALSE; int debug = sip_debug_test_pvt(p); + const char a_encr_optional[] = "a=encryption:optional\r\n"; int min_audio_packet_size = 0; int min_video_packet_size = 0; int min_text_packet_size = 0; @@ -7116,13 +7216,49 @@ /* Get our media addresses */ get_our_media_address(p, needvideo, &sin, &vsin, &tsin, &dest, &vdest); + /* Set encryption properties */ + if (srtp) { + if (srtp->mikey) { + a_mikey = sdp_mikey_attrib(srtp->mikey); + } else if (srtp->crypto) { + a_crypto = sdp_crypto_attrib(srtp->crypto); + } else { + if (ast_test_flag(p->srtp, SRTP_CRYPTO_ENABLE)) { + srtp->crypto = sdp_crypto_setup(); + + if (srtp->crypto && (sdp_crypto_offer(srtp->crypto) >= 0)) { + a_crypto = sdp_crypto_attrib(srtp->crypto); + } + } + + if (ast_test_flag(p->srtp, SRTP_MIKEY_ENABLE)) { + srtp->mikey = sdp_mikey_setup(p->peersecret, p->rtp); + if (srtp->mikey && (sdp_mikey_offer(srtp->mikey, p->rtp) >= 0)) { + a_mikey = sdp_mikey_attrib(srtp->mikey); + } + } + } + + if (!a_crypto && !a_mikey) { + ast_log(LOG_WARNING, "No SRTP key management enabled (MIKEY or CRYPTO)\n"); + } + } + + if ((a_crypto || a_mikey) && !ast_test_flag(srtp, SRTP_ENCR_OPTIONAL)) { + protocol = "SAVP"; + } else { + protocol = "AVP"; + } + + + if (debug) ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(sin.sin_port)); /* Ok, we need video. Let's add what we need for video and set codecs. Video is handled differently than audio since we can not transcode. */ if (needvideo) { - ast_str_append(&m_video, 0, "m=video %d RTP/AVP", ntohs(vdest.sin_port)); + ast_str_append(&m_video, 0, "m=video %d RTP/%s", ntohs(vdest.sin_port), protocol); /* Build max bitrate string */ if (p->maxcallbitrate) @@ -7171,7 +7307,7 @@ snprintf(owner, sizeof(owner), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(dest.sin_addr)); snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr)); - ast_str_append(&m_audio, 0, "m=audio %d RTP/AVP", ntohs(dest.sin_port)); + ast_str_append(&m_audio, 0, "m=audio %d RTP/%s", ntohs(dest.sin_port), protocol); if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR) hold = "a=recvonly\r\n"; @@ -7278,6 +7414,16 @@ if (needtext) /* only if text response is appropriate */ len += m_text->used + a_text->used + strlen(hold); + if (a_crypto) { + len += strlen(a_crypto); + if (ast_test_flag(srtp, SRTP_ENCR_OPTIONAL)) { + len += strlen(a_encr_optional); + } + } + + if (a_mikey) + len += strlen(a_mikey); + add_header(resp, "Content-Type", "application/sdp"); add_header_contentLength(resp, len); add_line(resp, version); @@ -7287,8 +7433,17 @@ if (needvideo) /* only if video response is appropriate */ add_line(resp, bandwidth); add_line(resp, stime); + if (a_mikey) { + add_line(resp, a_mikey); + } add_line(resp, m_audio->str); add_line(resp, a_audio->str); + if (a_crypto) { + add_line(resp, a_crypto); + if (ast_test_flag(srtp, SRTP_ENCR_OPTIONAL)) { + add_line(resp, a_encr_optional); + } + } add_line(resp, hold); if (needvideo) { /* only if video response is appropriate */ add_line(resp, m_video->str); @@ -18732,6 +18887,77 @@ ); } +/* + * SRTP + */ + +static int setup_srtp(struct sip_pvt *p) +{ + if (!ast_srtp_is_registered()) { + ast_log(LOG_ERROR, "No SRTP module loaded, can't setup SRTP session.\n"); + return -1; + } + + p->srtp = sip_srtp_alloc(); /* Allocate SRTP data structure */ + if (!p->srtp) + return -1; + + return 0; +} + +static int process_crypto(struct sip_pvt *p, const char *a) +{ + if (!p->srtp) { + if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + ast_log(LOG_WARNING, "Ignoring unexpected crypto attribute in SDP answer\n"); + return -1; + } + + if (setup_srtp(p) < 0) + return -1; + } + + if (!p->srtp->crypto) + p->srtp->crypto = sdp_crypto_setup(); + + if (!p->srtp->crypto) + return -1; + + if (sdp_crypto_process(p->srtp->crypto, a, p->rtp) < 0) + return -1; + + ast_set_flag(p->srtp, SRTP_CRYPTO_OFFER_OK); + return 0; +} + +static int process_mikey(struct sip_pvt *p, const char *a) +{ + if (!p->srtp) { + if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + ast_log(LOG_WARNING, "Ignoring unexpected mikey attribute in SDP answer\n"); + return -1; + } + + if (setup_srtp(p) < 0) { + ast_log(LOG_WARNING, "Can't setup crypto\n"); + return -1; + } + } + if (!p->srtp->mikey) { + p->srtp->mikey = sdp_mikey_setup(p->peersecret, p->rtp); + if (!p->srtp->mikey) { + ast_log(LOG_WARNING, "Can't setup MIKEY\n"); + return -1; + } + } + + if (sdp_mikey_process(p->srtp->mikey, a + 15, p->rtp) < 0) + return -1; + + ast_set_flag(p->srtp, SRTP_MIKEY_OFFER_OK); + return -1; +} + /*! \brief Reload module */ static int sip_do_reload(enum channelreloadreason reason) { @@ -18850,6 +19076,7 @@ static int load_module(void) { ast_verbose("SIP channel loading...\n"); + sdp_mikey_init(); ASTOBJ_CONTAINER_INIT(&userl); /* User object list */ ASTOBJ_CONTAINER_INIT(&peerl); /* Peer object list */ ASTOBJ_CONTAINER_INIT(®l); /* Registry object list */ @@ -18993,6 +19220,8 @@ close(sipsock); sched_context_destroy(sched); + sdp_mikey_uninit(); + return 0; } diff -urN -x _darcs -x .svn -x '*~' upstream/channels/Makefile local/channels/Makefile --- upstream/channels/Makefile 2007-07-13 17:20:09.000000000 +0200 +++ local/channels/Makefile 2007-07-14 17:09:47.000000000 +0200 @@ -103,6 +103,8 @@ $(CMD_PREFIX) $(CXX) $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) -o $@ $< h323/libchanh323.a $(CHANH323LIB) -L$(PWLIBDIR)/lib $(PTLIB) -L$(OPENH323DIR)/lib $(H323LIB) -L/usr/lib -lcrypto -lssl -lexpat endif +chan_sip.so: sip_srtp.o sdp_crypto.o sdp_mikey.o + chan_misdn.o: ASTCFLAGS+=-Imisdn misdn_config.o: ASTCFLAGS+=-Imisdn diff -urN -x _darcs -x .svn -x '*~' upstream/channels/sdp_crypto.c local/channels/sdp_crypto.c --- upstream/channels/sdp_crypto.c 1970-01-01 01:00:00.000000000 +0100 +++ local/channels/sdp_crypto.c 2007-03-26 21:41:55.000000000 +0200 @@ -0,0 +1,309 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file sdp_crypto.c + * + * \brief SDP Security descriptions + * + * Specified in RFC 4568 + * + * \author Mikael Magnusson + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: $") + +#include "asterisk/options.h" +#include "sdp_crypto.h" + +#define SRTP_MASTER_LEN 30 +#define SRTP_MASTERKEY_LEN 16 +#define SRTP_MASTERSALT_LEN (SRTP_MASTER_LEN - SRTP_MASTERKEY_LEN) +#define SRTP_MASTER_LEN64 ((SRTP_MASTER_LEN * 8 + 5) / 6 + 1) + +struct sdp_crypto { + char *a_crypto; + unsigned char local_key[SRTP_MASTER_LEN]; + char local_key64[SRTP_MASTER_LEN64]; +}; + +static int set_crypto_policy(struct ast_srtp_policy *policy, + int suite_val, const unsigned char *master_key, + unsigned long ssrc, int inbound); + + +static struct sdp_crypto *sdp_crypto_alloc(void) +{ + struct sdp_crypto *crypto = malloc(sizeof(*crypto)); + + if (crypto) + memset(crypto, 0, sizeof(*crypto)); + else + ast_log(LOG_ERROR, "Out of memory, can't allocate crypto structure\n"); + + return crypto; +} + +void sdp_crypto_destroy(struct sdp_crypto *crypto) +{ + if (crypto->a_crypto) + free(crypto->a_crypto); + crypto->a_crypto = NULL; + free(crypto); +} + +struct sdp_crypto *sdp_crypto_setup(void) +{ + struct sdp_crypto *p = sdp_crypto_alloc(); + + if (!p) + return NULL; + + if (ast_srtp_get_random(p->local_key, sizeof(p->local_key)) < 0) { + sdp_crypto_destroy(p); + return NULL; + } + + ast_base64encode(p->local_key64, p->local_key, + SRTP_MASTER_LEN, sizeof(p->local_key64)); + + { + /* FIXME mikma, remove block */ + int key_len; + unsigned char remote_key[SRTP_MASTER_LEN]; + + key_len = ast_base64decode(remote_key, p->local_key64, sizeof(remote_key)); + + if (key_len != SRTP_MASTER_LEN) + ast_log(LOG_ERROR, "base64 encode/decode bad len %d != %d\n", key_len, SRTP_MASTER_LEN); + + if (memcmp(remote_key, p->local_key, SRTP_MASTER_LEN)) + ast_log(LOG_ERROR, "base64 encode/decode bad key\n"); + } + + ast_log(LOG_DEBUG, "local_key64 %s len %d\n", p->local_key64, strlen(p->local_key64)); + return p; +} + +static int set_crypto_policy(struct ast_srtp_policy *policy, + int suite_val, const unsigned char *master_key, + unsigned long ssrc, int inbound) +{ + const unsigned char *master_salt = NULL; + + master_salt = master_key + SRTP_MASTERKEY_LEN; + if (ast_srtp_policy_set_master_key(policy, + master_key, SRTP_MASTERKEY_LEN, + master_salt, SRTP_MASTERSALT_LEN) < 0) + return -1; + + + if (ast_srtp_policy_set_suite(policy, suite_val)) { + ast_log(LOG_WARNING, "Could not set remote SRTP suite\n"); + return -1; + } + + ast_srtp_policy_set_ssrc(policy, ssrc, inbound); + + return 0; +} + +static int sdp_crypto_activate(struct sdp_crypto *p, int suite_val, + unsigned char *remote_key, + struct ast_rtp *rtp) +{ + struct ast_srtp_policy *local_policy = NULL; + struct ast_srtp_policy *remote_policy = NULL; + int res = -1; + + if (!p) + return -1; + + local_policy = ast_srtp_policy_alloc(); + if (!local_policy) + goto err; + + remote_policy = ast_srtp_policy_alloc(); + if (!remote_policy) { + goto err; + } + + if (set_crypto_policy(local_policy, suite_val, p->local_key, + ast_rtp_get_ssrc(rtp), 0) < 0) + goto err; + + if (set_crypto_policy(remote_policy, suite_val, remote_key, 0, 1) < 0) + goto err; + +/* FIXME MIKMA */ + if (ast_rtp_add_srtp_policy(rtp, local_policy)) { + ast_log(LOG_WARNING, "Could not set local SRTP policy\n"); + goto err; + } + + if (ast_rtp_add_srtp_policy(rtp, remote_policy)) { + ast_log(LOG_WARNING, "Could not set remote SRTP policy\n"); + goto err; + } + + + if (option_debug > 1) + ast_log(LOG_DEBUG, "SRTP policy activated\n"); + res = 0; + +err: + if (local_policy) + ast_srtp_policy_destroy(local_policy); + + if (remote_policy) + ast_srtp_policy_destroy(remote_policy); + return res; +} + +int sdp_crypto_process(struct sdp_crypto *p, const char *attr, + struct ast_rtp *rtp) +{ + char *str = NULL; + char *name = NULL; + char *tag = NULL; + char *suite = NULL; + char *key_params = NULL; + char *key_param = NULL; + char *session_params = NULL; + char *key_salt = NULL; + char *lifetime = NULL; + int found = 0; + int attr_len = strlen(attr); + int key_len = 0; + unsigned char remote_key[SRTP_MASTER_LEN]; + int suite_val = 0; + + if (!ast_srtp_is_registered()) + return -1; + + /* Crypto already accepted */ +/* if (p && p->a_crypto) */ +/* return -1; */ + + str = ast_strdupa(attr); + + name = strsep(&str, ":"); + tag = strsep(&str, " "); + suite = strsep(&str, " "); + key_params = strsep(&str, " "); + session_params = strsep(&str, " "); + + if (!tag || !suite) { + ast_log(LOG_WARNING, "Unrecognized a=%s", attr); + return -1; + } + + if (session_params) { + ast_log(LOG_WARNING, "Unsupported crypto parameters: %s", + session_params); + return -1; + } + + if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_80")) { + suite_val = AST_AES_CM_128_HMAC_SHA1_80; + } else if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_32")) { + suite_val = AST_AES_CM_128_HMAC_SHA1_32; + } else { + ast_log(LOG_WARNING, "Unsupported crypto suite: %s", + suite); + return -1; + } + + while ((key_param = strsep(&key_params, ";"))) { + char *method = NULL; + char *info = NULL; + + method = strsep(&key_param, ":"); + info = strsep(&key_param, ";"); + + if (!strcmp(method, "inline")) { + key_salt = strsep(&info, "|"); + lifetime = strsep(&info, "|"); + + if (lifetime) { + ast_log(LOG_NOTICE, "Crypto life time unsupported: %s\n", + attr); + continue; + } + +/* if (info || strncmp(lifetime, "2^", 2)) { */ +/* ast_log(LOG_NOTICE, "MKI unsupported: %s\n", */ +/* attr); */ +/* continue; */ +/* } */ + + found = 1; + break; + } + } + + if (!found) { + ast_log(LOG_NOTICE, "SRTP crypto offer not acceptable\n"); + return -1; + } + + key_len = ast_base64decode(remote_key, key_salt, sizeof(remote_key)); + if (key_len != SRTP_MASTER_LEN) { + ast_log(LOG_WARNING, "SRTP sdescriptions key %d != %d\n", + key_len, SRTP_MASTER_LEN); + return -1; + } + + if (sdp_crypto_activate(p, suite_val, remote_key, rtp) < 0) + return -1; + + if (!p->a_crypto) { + free(p->a_crypto); + + p->a_crypto = malloc(attr_len+11); + snprintf(p->a_crypto, attr_len+10, + "a=crypto:%s %s inline:%s\r\n", + tag, suite, p->local_key64); + } + + return 0; +} + +int sdp_crypto_offer(struct sdp_crypto *p) +{ + char crypto_buf[128]; + + /* Crypto offer */ + const char *crypto_suite = "AES_CM_128_HMAC_SHA1_80"; + + if (p->a_crypto) + free(p->a_crypto); + + snprintf(crypto_buf, sizeof(crypto_buf), + "a=crypto:1 %s inline:%s\r\n", + crypto_suite, p->local_key64); + p->a_crypto = strdup(crypto_buf); + + return 0; +} + +const char *sdp_crypto_attrib(struct sdp_crypto *p) +{ + return p->a_crypto; +} diff -urN -x _darcs -x .svn -x '*~' upstream/channels/sdp_crypto.h local/channels/sdp_crypto.h --- upstream/channels/sdp_crypto.h 1970-01-01 01:00:00.000000000 +0100 +++ local/channels/sdp_crypto.h 2007-03-26 21:41:55.000000000 +0200 @@ -0,0 +1,46 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file sdp_crypto.h + * + * \brief SDP Security descriptions + * + * Specified in RFC 4568 + * + * \author Mikael Magnusson + */ + +#ifndef _SDP_CRYPTO_H +#define _SDP_CRYPTO_H + +#include + +struct sdp_crypto; + +struct sdp_crypto *sdp_crypto_setup(void); +void sdp_crypto_destroy(struct sdp_crypto *crypto); + +/* int sdp_crypto_activate(struct sdp_crypto *p, int suite_val, */ +/* unsigned char *remote_key, */ +/* struct ast_rtp *rtp); */ +int sdp_crypto_process(struct sdp_crypto *p, const char *attr, + struct ast_rtp *rtp); +int sdp_crypto_offer(struct sdp_crypto *p); +const char *sdp_crypto_attrib(struct sdp_crypto *p); + +#endif /* _SDP_CRYPTO_H */ diff -urN -x _darcs -x .svn -x '*~' upstream/channels/sdp_mikey.c local/channels/sdp_mikey.c --- upstream/channels/sdp_mikey.c 1970-01-01 01:00:00.000000000 +0100 +++ local/channels/sdp_mikey.c 2007-03-26 21:41:55.000000000 +0200 @@ -0,0 +1,275 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file sdp_mikey.c + * + * \brief SDP MIKEY key management + * + * SDP MIKEY key management + * Specified in RFC 3830 and 4567 + * + * \author Mikael Magnusson + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: $") + +#include "asterisk/options.h" +#include "asterisk/rtp.h" +#include "asterisk/mikey.h" +#include "sdp_mikey.h" + +/* + MIKEY + Specified in: RFC 3830, RFC 4567 +*/ + +/* + TODO disable rtp until keys are available. + TODO sometimes first RTP packet is received before rtp callback + is installed, leads to that ssrc not being activated. + */ + +struct sdp_mikey { + struct mikey *mikey; + char *a_mikey; +}; + +static int sdp_mikey_activate(struct sdp_mikey *p, struct ast_rtp *rtp); + + +int sdp_mikey_init(void) +{ + return 0; +} + +void sdp_mikey_uninit(void) +{ +} + +static struct sdp_mikey *sdp_mikey_alloc(void) +{ + struct ast_mikey_res *res = ast_get_mikey(); + struct sdp_mikey *mikey; + + if (!res) { + ast_log(LOG_ERROR, "res_mikey not loaded\n"); + return NULL; + } + + mikey = malloc(sizeof(*mikey)); + + if (mikey) + memset(mikey, 0, sizeof(*mikey)); + else + ast_log(LOG_ERROR, "Out of memory, can't allocate mikey structure\n"); + + return mikey; +} + +void sdp_mikey_destroy(struct sdp_mikey *p) +{ + struct ast_mikey_res *res = ast_get_mikey(); + + if (p->mikey) + res->mikey_destroy(p->mikey); + p->mikey = NULL; + + if (p->a_mikey) + free(p->a_mikey); + p->a_mikey = NULL; + + free(p); +} + +struct sdp_mikey *sdp_mikey_setup(const char *peersecret, + struct ast_rtp *rtp) +{ + struct ast_mikey_res *res = ast_get_mikey(); + struct sdp_mikey *p = sdp_mikey_alloc(); + + if (!p) + return NULL; + + p->mikey = res->mikey_alloc(); + if (peersecret) { + ast_log(LOG_NOTICE, "Using MIKEY PSK %s\n", peersecret); + res->mikey_set_psk_secret(p->mikey, (unsigned char*)peersecret, + strlen(peersecret)); + } + else { + ast_log(LOG_NOTICE, "Now MIKEY PSK available\n"); + } + + res->mikey_set_ssrc(p->mikey, ast_rtp_get_ssrc(rtp)); + + return p; +} + +int sdp_mikey_process(struct sdp_mikey *p, const char *attr, + struct ast_rtp *rtp) +{ + struct ast_mikey_res *mod = ast_get_mikey(); + char buf[8192] = "a=key-mgmt:mikey "; + size_t prefixlen = strlen(buf); + size_t buflen = sizeof(buf) - prefixlen - 2; + int res; + + if (!p->mikey) { + ast_log(LOG_ERROR, "No MIKEY object\n"); + return -1; + } + + ast_log(LOG_DEBUG, "%s\n", attr); + + res = mod->mikey_process(p->mikey, attr, buf + prefixlen, buflen); + + if (res < 0) { + ast_log(LOG_NOTICE, "Couldn't parse MIKEY offer\n"); + return -1; + } + + if (p->a_mikey) + free(p->a_mikey); + p->a_mikey = NULL; + + if (sdp_mikey_activate(p, rtp) < 0) + return -1; + + if (res > 0) { + /* Parsed offer, built response */ + strcat(buf, "\r\n"); + + p->a_mikey = strdup(buf); + } + return 0; +} + +int sdp_mikey_offer(struct sdp_mikey *p, struct ast_rtp *rtp) +{ + struct ast_mikey_res *mod = ast_get_mikey(); + char buf[8192] = "a=key-mgmt:mikey "; + size_t prefixlen = strlen(buf); + size_t buflen = sizeof(buf) - prefixlen - 2; + int res; + + /* Crypto already accepted */ + if (p && p->a_mikey) + return -1; + + res = mod->mikey_build_offer(p->mikey, buf + prefixlen, buflen, AST_MIKEY_TYPE_DH_HMAC); + + if (res < 0) { + ast_log(LOG_NOTICE, "Couldn't build MIKEY offer\n"); + return -1; + } + + if (sdp_mikey_activate(p, rtp) < 0) + return -1; + + strcat(buf, "\r\n"); + + if (p->a_mikey) + free(p->a_mikey); + + p->a_mikey = strdup(buf); + return 0; +} + +static int cb_no_ctx(struct ast_rtp *rtp, unsigned long ssrc, void *data) +{ + struct ast_mikey_res *mod = ast_get_mikey(); + struct sdp_mikey *p = data; + struct ast_srtp_policy *policy = NULL; + int res = -1; + + ast_log(LOG_DEBUG, "SRTP cb\n"); + + if (!p) { + ast_log(LOG_WARNING, "No pvt\n"); + goto err; + } + + if (!p->mikey) { + ast_log(LOG_WARNING, "No mikey\n"); + goto err; + } + + policy = mod->mikey_create_policy(p->mikey, ssrc); + if (!policy) { + ast_log(LOG_ERROR, "Could not create MIKEY policy\n"); + goto err; + } + + /* was p->rtp */ + if (ast_rtp_add_srtp_policy(rtp, policy)) { + ast_log(LOG_ERROR, "Could not set SRTP policy\n"); + goto err; + } + + res = 0; + +err: + if (policy) + ast_srtp_policy_destroy(policy); + return res; +} + +struct ast_srtp_cb srtp_cb = { + no_ctx: cb_no_ctx +}; + +static int sdp_mikey_activate(struct sdp_mikey *p, struct ast_rtp *rtp) +{ + struct ast_mikey_res *mod = ast_get_mikey(); + struct ast_srtp_policy *policy = NULL; + int res = -1; + + if (!p || !p->mikey) + return -1; + + policy = mod->mikey_create_policy(p->mikey, ast_rtp_get_ssrc(rtp)); + if (!policy) { + ast_log(LOG_ERROR, "Could not create MIKEY policy\n"); + goto err; + } + + if (ast_rtp_add_srtp_policy(rtp, policy)) { + ast_log(LOG_ERROR, "Could not set local SRTP policy\n"); + goto err; + } + + ast_rtp_set_srtp_cb(rtp, &srtp_cb, p); + + if (option_debug > 1) + ast_log(LOG_NOTICE, "SRTP policy activated\n"); + res = 0; + +err: + if (policy) + ast_srtp_policy_destroy(policy); + return res; +} + +const char *sdp_mikey_attrib(struct sdp_mikey *p) +{ + ast_log(LOG_DEBUG, "Return mikey attrib %s\n", p->a_mikey); + + return p->a_mikey; +} diff -urN -x _darcs -x .svn -x '*~' upstream/channels/sdp_mikey.h local/channels/sdp_mikey.h --- upstream/channels/sdp_mikey.h 1970-01-01 01:00:00.000000000 +0100 +++ local/channels/sdp_mikey.h 2007-03-26 21:41:55.000000000 +0200 @@ -0,0 +1,48 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file sdp_mikey.h + * + * \brief SDP MIKEY key management + * + * SDP MIKEY key management + * Specified in RFC 3830 and 4567 + * + * \author Mikael Magnusson + */ + +#ifndef _SDP_MIKEY_H +#define _SDP_MIKEY_H + +#include + +struct sdp_mikey; + +int sdp_mikey_init(void); +void sdp_mikey_uninit(void); + +struct sdp_mikey *sdp_mikey_setup(const char *peersecret, + struct ast_rtp *rtp); +void sdp_mikey_destroy(struct sdp_mikey *p); +/* int sdp_mikey_activate(struct sdp_mikey *p, struct ast_rtp *rtp); */ +int sdp_mikey_offer(struct sdp_mikey *p, struct ast_rtp *rtp); +int sdp_mikey_process(struct sdp_mikey *p, const char *attr, + struct ast_rtp *rtp); +const char *sdp_mikey_attrib(struct sdp_mikey *p); + +#endif /* _SDP_MIKEY_H */ diff -urN -x _darcs -x .svn -x '*~' upstream/channels/sip_srtp.c local/channels/sip_srtp.c --- upstream/channels/sip_srtp.c 1970-01-01 01:00:00.000000000 +0100 +++ local/channels/sip_srtp.c 2007-03-26 21:41:55.000000000 +0200 @@ -0,0 +1,54 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file sip_srtp.c + * + * \brief SIP Secure RTP (SRTP) + * + * Specified in RFC 3711 + * + * \author Mikael Magnusson + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: $") + +#include "sip_srtp.h" + +struct sip_srtp *sip_srtp_alloc(void) +{ + struct sip_srtp *srtp = malloc(sizeof(*srtp)); + + if (srtp) + memset(srtp, 0, sizeof(*srtp)); + else + ast_log(LOG_ERROR, "Out of memory, can't allocate srtp structure\n"); + return srtp; +} + +void sip_srtp_destroy(struct sip_srtp *srtp) +{ + if (srtp->crypto) + sdp_crypto_destroy(srtp->crypto); + srtp->crypto = NULL; + + if (srtp->mikey) + sdp_mikey_destroy(srtp->mikey); + srtp->mikey = NULL; +} diff -urN -x _darcs -x .svn -x '*~' upstream/channels/sip_srtp.h local/channels/sip_srtp.h --- upstream/channels/sip_srtp.h 1970-01-01 01:00:00.000000000 +0100 +++ local/channels/sip_srtp.h 2007-03-26 21:41:55.000000000 +0200 @@ -0,0 +1,69 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file sip_srtp.h + * + * \brief SIP Secure RTP (SRTP) + * + * Specified in RFC 3711 + * + * \author Mikael Magnusson + */ + +#ifndef _SIP_SRTP_H +#define _SIP_SRTP_H + +#include "sdp_crypto.h" +#include "sdp_mikey.h" + +/* SRTP flags */ +#define SRTP_ENCR_OPTIONAL (1<<1) /* SRTP encryption optional */ +#define SRTP_CRYPTO_ENABLE (1<<3) +#define SRTP_MIKEY_ENABLE (1<<4) +#define SRTP_CRYPTO_OFFER_OK (1<<5) +#define SRTP_MIKEY_OFFER_OK (1<<6) + + +/*! \brief structure for secure RTP audio */ +struct sip_srtp { + unsigned int flags; + struct sdp_crypto *crypto; + struct sdp_mikey *mikey; +}; + +/*----- SRTP interface functions */ +struct sip_srtp *sip_srtp_alloc(void); +void sip_srtp_destroy(struct sip_srtp *srtp); + +#if 0 +struct sip_sdp_keymgmt { + int (*init)(void); + void (*uninit)(void); + struct sdp_keymgmt *(*setup)(const char *peersecret, + struct ast_rtp *rtp); + void (*destroy)(struct sdp_keymgmt *p); + int (*offer)(struct sdp_keymgmt *p, struct ast_rtp *rtp); + const char *(*attrib)(struct sdp_keymgmt *p); + /* FIX attr offset */ + int (*process)(struct sdp_keymgmt *p, const char *attr, + struct ast_rtp *rtp); +}; + +#endif + +#endif /* _SIP_SRTP_H */ diff -urN -x _darcs -x .svn -x '*~' upstream/configure.ac local/configure.ac --- upstream/configure.ac 2007-08-23 14:44:14.000000000 +0200 +++ local/configure.ac 2007-08-23 15:30:50.000000000 +0200 @@ -194,6 +194,10 @@ AST_EXT_LIB_SETUP([ISDNNET], [ISDN4Linux Library], [isdnnet]) AST_EXT_LIB_SETUP([KDE], [KDE], [kde]) AST_EXT_LIB_SETUP([LTDL], [libtool], [ltdl]) +AST_EXT_LIB_SETUP([MUTIL], [Minisip Utility Toolkit], [mutil]) +AST_EXT_LIB_SETUP([MNETUTIL], [Minisip Network Utility Toolkit], [mnetutil]) +AST_EXT_LIB_SETUP([MCRYPTO], [Minisip Cryptography Library], [mcrypto]) +AST_EXT_LIB_SETUP([MIKEY], [Minisip Multimedia Internet Keying], [mikey]) AST_EXT_LIB_SETUP([MISDN], [mISDN User Library], [misdn]) AST_EXT_LIB_SETUP([NBS], [Network Broadcast Sound], [nbs]) AST_EXT_LIB_SETUP([NCURSES], [ncurses], [ncurses]) @@ -214,6 +218,7 @@ AST_EXT_LIB_SETUP([SPEEX], [Speex], [speex]) AST_EXT_LIB_SETUP([SQLITE], [SQLite], [sqlite]) AST_EXT_LIB_SETUP([SQLITE3], [SQLite], [sqlite3]) +AST_EXT_LIB_SETUP([SRTP], [Secure RTP], [srtp]) AST_EXT_LIB_SETUP([SUPPSERV], [mISDN Supplemental Services], [suppserv]) AST_EXT_LIB_SETUP([OPENSSL], [OpenSSL Secure Sockets Layer support], [ssl]) AST_EXT_LIB_SETUP([FREETDS], [FreeTDS], [tds]) @@ -734,6 +739,18 @@ AC_SUBST([KDEDIR]) fi +dnl Minisip +AM_MINISIP_CHECK_LIBMUTIL([0.3.1], [PBX_MUTIL=1], [PBX_MUTIL=0]) +AM_MINISIP_CHECK_LIBMNETUTIL([0.3.1], [PBX_MNETUTIL=1], [PBX_MNETUTIL=0]) +AM_MINISIP_CHECK_LIBMCRYPTO([0.3.1], [PBX_MCRYPTO=1], [PBX_MCRYPTO=0]) +AM_MINISIP_CHECK_LIBMIKEY([0.4.1], [PBX_MIKEY=1], [PBX_MIKEY=0]) +AM_MINISIP_CHECK_COMPLETE + +AC_SUBST([PBX_MUTIL]) +AC_SUBST([PBX_MNETUTIL]) +AC_SUBST([PBX_MCRYPTO]) +AC_SUBST([PBX_MIKEY]) + AC_LANG_POP AST_EXT_LIB_CHECK([MISDN], [mISDN], [mISDN_open], [mISDNuser/mISDNlib.h]) @@ -961,6 +978,8 @@ AST_EXT_LIB_CHECK([OSPTK], [osptk], [OSPPCryptoDecrypt], [osp/osp.h], [-lcrypto -lssl]) fi +AST_EXT_LIB_CHECK([SRTP], [srtp], [srtp_init], [srtp/srtp.h]) + AST_EXT_LIB_CHECK([FREETDS], [tds], [tds_version], [tds.h]) if test "${PBX_FREETDS}" != "0"; then diff -urN -x _darcs -x .svn -x '*~' upstream/CREDITS local/CREDITS --- upstream/CREDITS 2007-07-13 17:19:59.000000000 +0200 +++ local/CREDITS 2007-07-14 17:09:47.000000000 +0200 @@ -145,6 +145,9 @@ George Konstantoulakis - Support for Greek in voicemail added by InAccess Networks (work funded by HOL, www.hol.gr) gkon@inaccessnetworks.com +Mikael Magnusson - Provided SRTP support in RTP, and SRTP and MIKEY support in the SIP channel + mikma@users.sourceforge.net + Daniel Nylander - Support for Swedish and Norwegian languages in voicemail. http://www.danielnylander.se/ Stojan Sljivic - An option for maximum number of messsages per mailbox in voicemail. Also an issue with voicemail synchronization has been fixed. GDS Partners www.gdspartners.com . stojan.sljivic@gdspartners.com diff -urN -x _darcs -x .svn -x '*~' upstream/include/asterisk/aes_internal.h local/include/asterisk/aes_internal.h --- upstream/include/asterisk/aes_internal.h 2007-06-10 10:56:24.000000000 +0200 +++ local/include/asterisk/aes_internal.h 2007-06-10 11:07:45.000000000 +0200 @@ -115,6 +115,8 @@ #ifdef AES_ENCRYPT +#define aes_encrypt ast_aes_encrypt + typedef struct { aes_32t ks[KS_LENGTH]; } aes_encrypt_ctx; @@ -140,6 +142,8 @@ #ifdef AES_DECRYPT +#define aes_decrypt ast_aes_decrypt + typedef struct { aes_32t ks[KS_LENGTH]; } aes_decrypt_ctx; diff -urN -x _darcs -x .svn -x '*~' upstream/include/asterisk/autoconfig.h.in local/include/asterisk/autoconfig.h.in --- upstream/include/asterisk/autoconfig.h.in 2007-08-23 14:33:50.000000000 +0200 +++ local/include/asterisk/autoconfig.h.in 2007-08-23 15:30:46.000000000 +0200 @@ -256,6 +256,18 @@ /* Define if your system has the KDE libraries. */ #undef HAVE_LIBKDE +/* Define to 1 if you have the `mcrypto' library (-lmcrypto). */ +#undef HAVE_LIBMCRYPTO + +/* Define to 1 if you have the `mikey' library (-lmikey). */ +#undef HAVE_LIBMIKEY + +/* Define to 1 if you have the `mnetutil' library (-lmnetutil). */ +#undef HAVE_LIBMNETUTIL + +/* Define to 1 if you have the `mutil' library (-lmutil). */ +#undef HAVE_LIBMUTIL + /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H @@ -490,6 +502,12 @@ /* Define to 1 if you have the `sqrt' function. */ #undef HAVE_SQRT +/* Define this to indicate the ${SRTP_DESCRIP} library */ +#undef HAVE_SRTP + +/* Define to indicate the ${SRTP_DESCRIP} library version */ +#undef HAVE_SRTP_VERSION + /* Define this to indicate the ${SS7_DESCRIP} library */ #undef HAVE_SS7 diff -urN -x _darcs -x .svn -x '*~' upstream/include/asterisk/mikey.h local/include/asterisk/mikey.h --- upstream/include/asterisk/mikey.h 1970-01-01 01:00:00.000000000 +0100 +++ local/include/asterisk/mikey.h 2007-01-19 11:55:48.000000000 +0100 @@ -0,0 +1,82 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2005 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file mikey.h + * \brief MIKEY - Multimedia Internet KEYing + * + * Supported modes: + * Pre-shared, Public key, D-H Sign, D-H HMAC and RSA in reverse mode. + * + * MIKEY is defined in RFC 3830, D-H HMAC in 4650 and RSA-R in 4738. + */ + +#ifndef _ASTERISK_MIKEY_H +#define _ASTERISK_MIKEY_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum ast_mikey_type { + AST_MIKEY_TYPE_PSK = 0, + AST_MIKEY_TYPE_PK, + AST_MIKEY_TYPE_DH_SIGN, + AST_MIKEY_TYPE_DH_HMAC, + AST_MIKEY_TYPE_RSA_R, +}; + +struct ast_mikey_res { + int (*mikey_init)(void); + void (*mikey_uninit)(void); +/* int (*mikey_add_global_ca_file)(const char *ca_file); */ +/* int (*mikey_add_global_cert_file)(const char *cert_file, */ +/* const char *key_file); */ + + struct mikey *(*mikey_alloc)(void); + void (*mikey_destroy)(struct mikey *mikey); + void (*mikey_set_ssrc)(struct mikey *mikey, uint32_t ssrc); + void (*mikey_set_psk_secret)(struct mikey *mikey, + const unsigned char *secret, size_t len); + int (*mikey_add_ca_file)(struct mikey *mikey, const char *ca_file); + int (*mikey_add_cert_file)(struct mikey *mikey, const char *cert_file, + const char *key_file); + + int (*mikey_build_offer)(struct mikey *mikey, char *buf, size_t len, + enum ast_mikey_type type); + + int (*mikey_process)(struct mikey *mikey, const char *offer, + char *buf, size_t buflen); + + struct ast_srtp_policy *(*mikey_create_policy)(struct mikey *mikey, + uint32_t ssrc); +}; + +int ast_register_mikey(struct ast_mikey_res *mikey_res); + +int ast_unregister_mikey(struct ast_mikey_res *mikey_res); + +struct ast_mikey_res *ast_get_mikey(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _ASTERISK_MIKEY_H */ diff -urN -x _darcs -x .svn -x '*~' upstream/include/asterisk/rtp.h local/include/asterisk/rtp.h --- upstream/include/asterisk/rtp.h 2007-07-22 22:07:41.000000000 +0200 +++ local/include/asterisk/rtp.h 2007-07-23 09:36:54.000000000 +0200 @@ -97,6 +97,68 @@ double rtt; /*!< Round trip time */ }; +struct ast_srtp; + +struct ast_srtp_policy; + +struct ast_srtp_cb { + int (*no_ctx)(struct ast_rtp *rtp, unsigned long ssrc, void *data); +}; + +struct ast_srtp_res { + int (*create)(struct ast_srtp **srtp, struct ast_rtp *rtp, + struct ast_srtp_policy *policy); + void (*destroy)(struct ast_srtp *srtp); + int (*add_stream)(struct ast_srtp *srtp, struct ast_srtp_policy *policy); + void (*set_cb)(struct ast_srtp *srtp, + const struct ast_srtp_cb *cb, void *data); + int (*unprotect)(struct ast_srtp *srtp, void *buf, int *size); + int (*protect)(struct ast_srtp *srtp, void **buf, int *size); + int (*get_random)(unsigned char *key, size_t len); +}; + +/* Crypto suites */ +enum ast_srtp_suite { + AST_AES_CM_128_HMAC_SHA1_80 = 1, + AST_AES_CM_128_HMAC_SHA1_32 = 2, + AST_F8_128_HMAC_SHA1_80 = 3 +}; + +enum ast_srtp_ealg { + AST_MIKEY_SRTP_EALG_NULL = 0, + AST_MIKEY_SRTP_EALG_AESCM = 1 +}; + +enum ast_srtp_aalg { + AST_MIKEY_SRTP_AALG_NULL = 0, + AST_MIKEY_SRTP_AALG_SHA1HMAC = 1 +}; + +struct ast_srtp_policy_res { + struct ast_srtp_policy *(*alloc)(void); + void (*destroy)(struct ast_srtp_policy *policy); + int (*set_suite)(struct ast_srtp_policy *policy, + enum ast_srtp_suite suite); + int (*set_master_key)(struct ast_srtp_policy *policy, + const unsigned char *key, size_t key_len, + const unsigned char *salt, size_t salt_len); + int (*set_encr_alg)(struct ast_srtp_policy *policy, + enum ast_srtp_ealg ealg); + int (*set_auth_alg)(struct ast_srtp_policy *policy, + enum ast_srtp_aalg aalg); + void (*set_encr_keylen)(struct ast_srtp_policy *policy, int ekeyl); + void (*set_auth_keylen)(struct ast_srtp_policy *policy, int akeyl); + void (*set_srtp_auth_taglen)(struct ast_srtp_policy *policy, int autht); + void (*set_srtp_encr_enable)(struct ast_srtp_policy *policy, int enable); + void (*set_srtcp_encr_enable)(struct ast_srtp_policy *policy, int enable); + void (*set_srtp_auth_enable)(struct ast_srtp_policy *policy, int enable); + void (*set_ssrc)(struct ast_srtp_policy *policy, unsigned long ssrc, + int inbound); +}; + + +#define FLAG_3389_WARNING (1 << 0) + /*! RTP callback structure */ typedef int (*ast_rtp_callback)(struct ast_rtp *rtp, struct ast_frame *f, void *data); @@ -256,6 +318,33 @@ int ast_rtp_reload(void); /*! reload rtp configuration */ void ast_rtp_new_init(struct ast_rtp *rtp); +int ast_rtp_register_srtp(struct ast_srtp_res *srtp_res, struct ast_srtp_policy_res *policy_res); + +int ast_rtp_unregister_srtp(struct ast_srtp_res *srtp_res, struct ast_srtp_policy_res *policy_res); + +int ast_srtp_is_registered(void); + +unsigned int ast_rtp_get_ssrc(struct ast_rtp *rtp); +void ast_rtp_set_srtp_cb(struct ast_rtp *rtp, const struct ast_srtp_cb *cb, + void *data); +int ast_rtp_add_srtp_policy(struct ast_rtp *rtp, struct ast_srtp_policy *policy); +struct ast_srtp_policy *ast_srtp_policy_alloc(void); +int ast_srtp_policy_set_suite(struct ast_srtp_policy *policy, enum ast_srtp_suite suite); +int ast_srtp_policy_set_master_key(struct ast_srtp_policy *policy, const unsigned char *key, size_t key_len, const unsigned char *salt, size_t salt_len); +int ast_srtp_policy_set_encr_alg(struct ast_srtp_policy *policy, enum ast_srtp_ealg ealg); +int ast_srtp_policy_set_auth_alg(struct ast_srtp_policy *policy, enum ast_srtp_aalg aalg); +void ast_srtp_policy_set_encr_keylen(struct ast_srtp_policy *policy, int ekeyl); +void ast_srtp_policy_set_auth_keylen(struct ast_srtp_policy *policy, int akeyl); +void ast_srtp_policy_set_srtp_auth_taglen(struct ast_srtp_policy *policy, int autht); +void ast_srtp_policy_set_srtp_encr_enable(struct ast_srtp_policy *policy, int enable); +void ast_srtp_policy_set_srtcp_encr_enable(struct ast_srtp_policy *policy, int enable); +void ast_srtp_policy_set_srtp_auth_enable(struct ast_srtp_policy *policy, int enable); +void ast_srtp_policy_set_ssrc(struct ast_srtp_policy *policy, unsigned long ssrc, int inbound); + +void ast_srtp_policy_destroy(struct ast_srtp_policy *policy); +int ast_srtp_get_random(unsigned char *key, size_t len); + + /*! Set codec preference */ int ast_rtp_codec_setpref(struct ast_rtp *rtp, struct ast_codec_pref *prefs); diff -urN -x _darcs -x .svn -x '*~' upstream/main/cryptostub.c local/main/cryptostub.c --- upstream/main/cryptostub.c 2007-06-10 10:53:34.000000000 +0200 +++ local/main/cryptostub.c 2007-06-10 11:07:43.000000000 +0200 @@ -30,8 +30,10 @@ #include #include + #include "asterisk/crypto.h" #include "asterisk/logger.h" +#include "asterisk/mikey.h" static struct ast_key *stub_ast_key_get(const char *kname, int ktype) { @@ -69,3 +71,27 @@ build_stub(ast_sign_bin, struct ast_key *key, const char *msg, int msglen, unsigned char *sig); build_stub(ast_encrypt_bin, unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key); build_stub(ast_decrypt_bin, unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key); + + +static struct ast_mikey_res *g_ast_mikey_res; + +int ast_register_mikey(struct ast_mikey_res *mikey_res) +{ + g_ast_mikey_res = mikey_res; + return 0; +} + +int ast_unregister_mikey(struct ast_mikey_res *mikey_res) +{ + if (g_ast_mikey_res == mikey_res) { + g_ast_mikey_res = NULL; + return 0; + } + + return -1; +} + +struct ast_mikey_res *ast_get_mikey(void) +{ + return g_ast_mikey_res; +} diff -urN -x _darcs -x .svn -x '*~' upstream/main/rtp.c local/main/rtp.c --- upstream/main/rtp.c 2007-08-28 21:51:53.000000000 +0200 +++ local/main/rtp.c 2007-08-28 22:12:26.000000000 +0200 @@ -95,6 +95,9 @@ /* Uncomment this to enable more intense native bridging, but note: this is currently buggy */ /* #define P2P_INTENSE */ +struct ast_srtp_res *g_srtp_res; +struct ast_srtp_policy_res *g_policy_res; + /*! * \brief Structure representing a RTP session. * @@ -107,7 +110,6 @@ int code; }; - /*! \brief RTP session description */ struct ast_rtp { int s; @@ -178,6 +180,7 @@ struct ast_rtcp *rtcp; struct ast_codec_pref pref; struct ast_rtp *bridged; /*!< Who we are Packet bridged to */ + struct ast_srtp *srtp; /*!< Secure RTP data */ }; /* Forward declarations */ @@ -1031,6 +1034,224 @@ return f; } +/*! \brief Register SRTP module in res_srtp to rtp core */ +int ast_rtp_register_srtp(struct ast_srtp_res *srtp_res, + struct ast_srtp_policy_res *policy_res) +{ + if (g_srtp_res || g_policy_res) + return -1; + + if (!srtp_res || !policy_res) + return -1; + + g_srtp_res = srtp_res; + g_policy_res = policy_res; + return 0; +} + +int ast_rtp_unregister_srtp(struct ast_srtp_res *srtp_res, + struct ast_srtp_policy_res *policy_res) +{ + g_srtp_res = NULL; + g_policy_res = NULL; + return 0; +} + +/*! \brief Check if there's a SRTP module registred and available */ +int ast_srtp_is_registered(void) +{ + return g_srtp_res && g_policy_res; +} + +unsigned int ast_rtp_get_ssrc(struct ast_rtp *rtp) +{ + return rtp->ssrc; +} + +void ast_rtp_set_srtp_cb(struct ast_rtp *rtp, const struct ast_srtp_cb *cb, + void *data) +{ + if (!g_srtp_res || !rtp->srtp) + return; + + g_srtp_res->set_cb(rtp->srtp, cb, data); +} + +void ast_srtp_policy_set_ssrc(struct ast_srtp_policy *policy, unsigned long ssrc, int inbound) +{ + if (!g_policy_res) + return; + + g_policy_res->set_ssrc(policy, ssrc, inbound); +} + +int ast_rtp_add_srtp_policy(struct ast_rtp *rtp, struct ast_srtp_policy *policy) +{ + int res; + + if (!g_srtp_res) + return -1; + + if (!rtp->srtp) { + res = g_srtp_res->create(&rtp->srtp, rtp, policy); + } else { + res = g_srtp_res->add_stream(rtp->srtp, policy); + } + + return res; +} + +struct ast_srtp_policy *ast_srtp_policy_alloc() +{ + if (!g_policy_res) + return NULL; + + return g_policy_res->alloc(); +} + +void ast_srtp_policy_destroy(struct ast_srtp_policy *policy) +{ + if (!g_policy_res) + return; + + g_policy_res->destroy(policy); +} + +int ast_srtp_policy_set_suite(struct ast_srtp_policy *policy, + enum ast_srtp_suite suite) +{ + if (!g_policy_res) + return -1; + + return g_policy_res->set_suite(policy, suite); +} + +int ast_srtp_policy_set_master_key(struct ast_srtp_policy *policy, + const unsigned char *key, size_t key_len, + const unsigned char *salt, size_t salt_len) +{ + if (!g_policy_res) + return -1; + + return g_policy_res->set_master_key(policy, key, key_len, salt, salt_len); +} + +int ast_srtp_policy_set_encr_alg(struct ast_srtp_policy *policy, + enum ast_srtp_ealg ealg) +{ + if (!g_policy_res) + return -1; + + return g_policy_res->set_encr_alg(policy, ealg); +} + +int ast_srtp_policy_set_auth_alg(struct ast_srtp_policy *policy, + enum ast_srtp_aalg aalg) +{ + if (!g_policy_res) + return -1; + + return g_policy_res->set_auth_alg(policy, aalg); +} + +void ast_srtp_policy_set_encr_keylen(struct ast_srtp_policy *policy, int ekeyl) +{ + if (!g_policy_res) + return; + + return g_policy_res->set_encr_keylen(policy, ekeyl); +} + +void ast_srtp_policy_set_auth_keylen(struct ast_srtp_policy *policy, + int akeyl) +{ + if (!g_policy_res) + return; + + return g_policy_res->set_auth_keylen(policy, akeyl); +} + +void ast_srtp_policy_set_srtp_auth_taglen(struct ast_srtp_policy *policy, + int autht) +{ + if (!g_policy_res) + return; + + return g_policy_res->set_srtp_auth_taglen(policy, autht); +} + +void ast_srtp_policy_set_srtp_encr_enable(struct ast_srtp_policy *policy, + int enable) +{ + if (!g_policy_res) + return; + + return g_policy_res->set_srtp_encr_enable(policy, enable); +} + +void ast_srtp_policy_set_srtcp_encr_enable(struct ast_srtp_policy *policy, + int enable) +{ + if (!g_policy_res) + return; + + return g_policy_res->set_srtcp_encr_enable(policy, enable); +} + +void ast_srtp_policy_set_srtp_auth_enable(struct ast_srtp_policy *policy, + int enable) +{ + if (!g_policy_res) + return; + + return g_policy_res->set_srtp_auth_enable(policy, enable); +} + +int ast_srtp_get_random(unsigned char *key, size_t len) +{ + if (!g_srtp_res) + return -1; + + return g_srtp_res->get_random(key, len); +} + +static int rtp_recvfrom(struct ast_rtp *rtp, void *buf, size_t size, + int flags, struct sockaddr *sa, socklen_t *salen) +{ + int len; + + len = recvfrom(rtp->s, buf, size, flags, sa, salen); + + if (len < 0) + return len; + + if (g_srtp_res && rtp->srtp) { + int res; + + res = g_srtp_res->unprotect(rtp->srtp, buf, &len); + if (res < 0) + return -1; + } + + return len; +} + +static int rtp_sendto(struct ast_rtp *rtp, void *buf, size_t size, + int flags, struct sockaddr *sa, socklen_t salen) +{ + int len = size; + void *temp = buf; + + if (g_srtp_res && rtp->srtp) { + int res = g_srtp_res->protect(rtp->srtp, &temp, &len); + + if (res < 0) + return -1; + } + + return sendto(rtp->s, temp, len, flags, sa, salen); +} + static int rtpread(int *id, int fd, short events, void *cbdata) { struct ast_rtp *rtp = cbdata; @@ -1392,7 +1613,7 @@ len = sizeof(sin); /* Cache where the header will go */ - res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, + res = rtp_recvfrom(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len); rtpheader = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET); @@ -2522,7 +2743,7 @@ for (i = 0; i < 2; i++) { rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration)); - res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); + res = rtp_sendto(rtp, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); if (res < 0) ast_log(LOG_ERROR, "RTP Transmission error to %s:%u: %s\n", ast_inet_ntoa(rtp->them.sin_addr), @@ -2930,7 +3151,7 @@ rtpheader[2] = htonl(rtp->ssrc); data[12] = level; if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { - res = sendto(rtp->s, (void *)rtpheader, hdrlen + 1, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); + res = rtp_sendto(rtp, (void *)rtpheader, hdrlen + 1, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); if (res <0) ast_log(LOG_ERROR, "RTP Comfort Noise Transmission error to %s:%d: %s\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); if (rtp_debug_test_addr(&rtp->them)) @@ -3015,7 +3236,7 @@ put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc)); if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { - res = sendto(rtp->s, (void *)rtpheader, f->datalen + hdrlen, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); + res = rtp_sendto(rtp, (void *)rtpheader, f->datalen + hdrlen, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); if (res <0) { if (!rtp->nat || (rtp->nat && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) { ast_debug(1, "RTP Transmission error of packet %d to %s:%d: %s\n", rtp->seqno, ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); @@ -3698,6 +3919,13 @@ return AST_BRIDGE_FAILED_NOWARN; } + if (p0->srtp || p1->srtp) { + ast_log(LOG_NOTICE, "Cannot native bridge in SRTP.\n"); + ast_channel_unlock(c0); + ast_channel_unlock(c1); + return AST_BRIDGE_FAILED_NOWARN; + } + /* If we need to feed DTMF frames into the core then only do a partial native bridge */ if (ast_test_flag(p0, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) { ast_set_flag(p0, FLAG_P2P_NEED_DTMF); diff -urN -x _darcs -x .svn -x '*~' upstream/makeopts.in local/makeopts.in --- upstream/makeopts.in 2007-06-30 17:31:27.000000000 +0200 +++ local/makeopts.in 2007-06-30 17:47:35.000000000 +0200 @@ -102,6 +102,18 @@ KDE_INCLUDE=@KDE_INCLUDE@ KDE_LIB=@KDE_LIB@ +MUTIL_INCLUDE=@MUTIL_CFLAGS@ +MUTIL_LIB=@MUTIL_LIBS@ + +MNETUTIL_INCLUDE=@MNETUTIL_CFLAGS@ +MNETUTIL_LIB=@MNETUTIL_LIBS@ + +MCRYPTO_INCLUDE=@MCRYPTO_CFLAGS@ +MCRYPTO_LIB=@MCRYPTO_LIBS@ + +MIKEY_INCLUDE=@MIKEY_CFLAGS@ +MIKEY_LIB=@MIKEY_LIBS@ + NBS_INCLUDE=@NBS_INCLUDE@ NBS_LIB=@NBS_LIB@ @@ -147,6 +159,9 @@ SPEEX_INCLUDE=@SPEEX_INCLUDE@ SPEEX_LIB=@SPEEX_LIB@ +SRTP_LIB=@SRTP_LIB@ +SRTP_INCLUDE=@SRTP_INCLUDE@ + SQLITE_INCLUDE=@SQLITE_INCLUDE@ SQLITE_LIB=@SQLITE_LIB@ diff -urN -x _darcs -x .svn -x '*~' upstream/res/Makefile local/res/Makefile --- upstream/res/Makefile 2007-08-23 14:40:12.000000000 +0200 +++ local/res/Makefile 2007-08-23 15:30:48.000000000 +0200 @@ -33,6 +33,10 @@ $(if $(filter res_snmp,$(EMBEDDED_MODS)),modules.link,res_snmp.so): snmp/agent.o +mikey.oo: ASTCFLAGS+=$(MUTIL_INCLUDE) $(MCRYPTO_INCLUDE) $(MIKEY_INCLUDE) +res_mikey.so: res_mikey.o mikey.oo +res_mikey.so: LIBS+=$(MIKEY_LIB) $(MCRYPTO_LIB) $(MUTIL_LIB) + $(if $(filter res_ael_share,$(EMBEDDED_MODS)),modules.link,res_ael_share.so): ael/ael_lex.o ael/ael.tab.o ael/pval.o ael/ael_lex.c: diff -urN -x _darcs -x .svn -x '*~' upstream/res/mikey.cc local/res/mikey.cc --- upstream/res/mikey.cc 1970-01-01 01:00:00.000000000 +0100 +++ local/res/mikey.cc 2007-06-30 21:09:40.000000000 +0200 @@ -0,0 +1,906 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2005 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file mikey.cc + * + * \brief MIKEY - Multimedia InternetKEYing + * + * MIKEY is specified in RFC 3830, 4650 and 4738. + * + * \author Mikael Magnusson + */ + +extern "C" { +#include "asterisk/autoconfig.h" + +#include"asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision:$") +} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include"mikey.h" + +using namespace std; + +#define DEBUG_OUTPUT + +#define MASTER_KEY_LEN 16 +#define MASTER_SALT_LEN 14 + + +// local functions +static int message_to_base64(MRef msg, + char *buf, size_t buflen); +static int string_to_buf(const string &msg, char *buf, size_t buflen); +static void dump_hex(unsigned char *buf, size_t bufsize); +static int getPolicyParamTypeValue(MikeyPayloadSP *policy, uint8_t type, + uint8_t *value); +static void dump_policy(MikeyPayloadSP *sp); +static bool check_policy(MikeyPayloadSP *policy); +static bool check_policies(MRef msg); +static void dump_crypto_params(MRef ka); + + +// local class declarations +class ast_mikey_config: public IMikeyConfig +{ +public: + ast_mikey_config(); + virtual ~ast_mikey_config(); + + void set_psk_secret(const unsigned char *secret, size_t len); + int add_ca_file(const char *ca_file); + int add_cert(const char *cert_file, const char *key_file); + + // IMikeyConfig + virtual const std::string getUri() const{ return uri; } + virtual MRef getSim() const{ return sim; } + virtual size_t getPskLength() const{ return psk_len; } + virtual const byte_t* getPsk() const{ return psk; } + virtual bool isMethodEnabled( int kaType ) const{ return true; } + virtual bool isCertCheckEnabled() const{ return true; } + +private: + string uri; + MRef sim; + byte_t *psk; + size_t psk_len; +}; + + +struct mikey +{ +protected: + int activate(); + int add_ssrc(uint32_t ssrc, struct ast_srtp_policy *policy); + int set_crypto_policy(uint32_t ssrc, struct ast_srtp_policy *policy); + +public: + mikey(); + virtual ~mikey(); + + MRef get_config() { return config; } + + void add_stream(uint32_t ssrc); + int build_offer(char *buf, size_t buflen, int ka_type); + int parse_offer(const char *offer, char *buf, size_t buflen); + struct ast_srtp_policy *create_policy(uint32_t ssrc); + +private: + MRef config; + MRef state; +}; + + +// globals +static MRef g_cert_chain; +static MRef g_ca_db; + +/* + +RFC 3830 + +EALG + +SRTP encr alg | Value +--------------------- +NULL | 0 +AES-CM | 1 +AES-F8 | 2 + +EKEYL - Encryption key length + +AALG + +SRTP auth alg | Value +--------------------- +NULL | 0 +HMAC-SHA-1 | 1 + +AKEYL - Auth key length + +SALTKEYL - master salt key length + +PRF + +SRTP PRF | Value +--------------------- +AES-CM | 0 + +DERRATE - key derivation rate + +SRTP_ENCR_ON_OFF - encryption on/off + +SRTCP_ENCR_ONOFF - SRTCP encryption on/off + +FEC_ORDER + +FEC order | Value | Comments +-------------------------------- +FEC-SRTP | 0 | First FEC, then SRTP + +SRTP_AUTH_ON_OFF - SRTP authentication on/off + +SRTP_AUTH_TAGL - Authentication tag length + +SRTP_PREFIX - SRTP prefix length + +*/ + + +// +// Local functions +// + +static int message_to_base64(MRef msg, char *buf, size_t buflen) +{ + const string base64 = msg->b64Message(); + + if (buflen <= base64.size()) + return -1; + + strncpy(buf, base64.c_str(), buflen); +// printf("message_to_base64 '%s'\n", buf); + return base64.size(); +} + +static int string_to_buf(const string &msg, char *buf, size_t buflen) +{ + if (buflen <= msg.size()) + return -1; + + strncpy(buf, msg.c_str(), buflen); +// printf("message_to_base64 '%s'\n", buf); + return msg.size(); +} + + +#define dump_var(name) printf("Var: %10s = %08x\n", #name, name); + +static void dump_hex(unsigned char *buf, size_t bufsize) +{ + int i; + size_t start_pos = 0; + + while(start_pos < bufsize) { + size_t pos; + + printf("%04x ", start_pos); + + for (i = 0, pos = start_pos; i < 16 && pos < bufsize; i++,pos++) { + printf("%02x ", buf[pos]); + if (i == 7) + printf(" "); + } + + printf(" "); + + for (i = 0, pos = start_pos; i < 16 && pos < bufsize; i++,pos++) { + int c = buf[pos]; + + if (isalnum(c)) + printf("%c", c); + else + printf("."); + } + + printf("\n"); + start_pos += 16; + } +} + +static int getPolicyParamTypeValue(MikeyPayloadSP *policy, uint8_t type, + uint8_t *value) +{ + MikeyPolicyParam *param = policy->getParameterType(type); + + if (param->length != 1) + return -1; + + *value = param->value[0]; + return 0; +} + +static void dump_policy(MikeyPayloadSP *sp) +{ + int i; + + for( i = 0; i < 20; i++ ){ + MikeyPolicyParam* param = sp->getParameterType( i ); + + if( param ){ + if( param->length == 1 ) + cerr << (int)param->type << ": [byte] " << (int)param->value[0] << endl; + else + cerr << (int)param->type << ": [length] " << (int)param->length << endl; + } + } +} + +// Check if offered policy is acceptable. +static bool check_policy(MikeyPayloadSP *policy) +{ + unsigned int i; + + printf("Check Policy %d\n", policy->policy_no); + + dump_policy( policy ); + + if (policy->prot_type != MIKEY_PROTO_SRTP) { +// *error = new MikeyMessage(); +// (*error)->addPayload(new MikeyPayloadERR(MIKEY_ERR_TYPE_INVALID_SP)); + printf("Bad prot type %d\n", policy->prot_type); + return false; + } + + // Predefined default SRTP parameter values + byte_t values[] = { MIKEY_SRTP_EALG_AESCM, MASTER_KEY_LEN, + MIKEY_SRTP_AALG_SHA1HMAC, + 20, MASTER_SALT_LEN, MIKEY_SRTP_PRF_AESCM, 0, 1, 1, + MIKEY_FEC_ORDER_FEC_SRTP, 1, 10, 0}; + + for (i = 0; i < sizeof(values)/sizeof(values[0]); i++) { + getPolicyParamTypeValue(policy, i, &values[i]); + } + + if (values[MIKEY_SRTP_EALG] == MIKEY_SRTP_EALG_AESCM) { + if (values[MIKEY_SRTP_EKEYL] != MASTER_KEY_LEN) { + printf("Bad AES encryption key length\n"); + return false; + } + + if (values[MIKEY_SRTP_SALTKEYL] != MASTER_SALT_LEN) { + printf("Bad salt key length\n"); + return false; + } + } else if (values[MIKEY_SRTP_EALG] == MIKEY_SRTP_EALG_NULL) { + if (values[MIKEY_SRTP_EKEYL] != 0) { + printf("Bad null encryption key length\n"); + return false; + } + } else { + printf("Bad encryption alg\n"); + return false; + } + + if (values[MIKEY_SRTP_AALG] == MIKEY_SRTP_AALG_SHA1HMAC) { + if (values[MIKEY_SRTP_AKEYL] != 20) { + printf("Bad SHA1 auth key length\n"); + return false; + } + + if (values[MIKEY_SRTP_AUTH_TAGL] < 4) { + printf("Bad SHA1 auth tag length\n"); + return false; + } + } else if (values[MIKEY_SRTP_AALG] == MIKEY_SRTP_AALG_NULL) { + if (values[MIKEY_SRTP_AKEYL] != 0) { + printf("Bad NULL auth key length\n"); + return false; + } + + if (values[MIKEY_SRTP_AUTH_TAGL] != 0) { + printf("Bad NULL auth tag length\n"); + return false; + } + } + + if (values[MIKEY_SRTP_PRF] != MIKEY_SRTP_PRF_AESCM) { + printf("Bad prf\n"); + return false; + } + + if (values[MIKEY_SRTP_KEY_DERRATE] != 0) { + printf("Bad key derivation rate\n"); + return false; + } + + if (values[MIKEY_SRTP_FEC_ORDER] != MIKEY_FEC_ORDER_FEC_SRTP) { + printf("Bad fec order\n"); + return false; + } + + if (values[MIKEY_SRTP_PREFIX] != 0) { + printf("Bad prefix length\n"); + return false; + } + + if (values[MIKEY_SRTP_ENCR_ON_OFF] != 1) { + printf("Bad encryption on/off\n"); + return false; + } + + printf("Policy %d ok\n", policy->policy_no); + + return true; +} + +// Check if all offered policies is acceptable. +static bool check_policies(MRef msg) +{ + list >::iterator i; + + for( i = msg->firstPayload(); i != msg->lastPayload(); i++ ) { + MikeyPayloadSP *sp = dynamic_cast(**i); + + if (sp) { + if (!check_policy(sp)) + return false; + } + } + + return true; +} + +static void dump_crypto_params(MRef ka) +{ + uint32_t ssrc = 17; + + cerr << "dump_crypto_params" << endl; + + uint8_t csId = ka->getSrtpCsId(ssrc); + uint32_t roc = ka->getSrtpRoc(ssrc ); + uint8_t policyNo = ka->findpolicyNo(ssrc); + + uint8_t ealg = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_EALG); + uint8_t ekeyl = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_EKEYL); + uint8_t aalg = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_AALG); + uint8_t akeyl = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_AKEYL); + uint8_t skeyl = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_SALTKEYL); + uint8_t prf = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_PRF); + uint8_t keydr = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_KEY_DERRATE); + uint8_t encr = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_ENCR_ON_OFF); + uint8_t cencr = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTCP_ENCR_ON_OFF); + uint8_t fecor = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_FEC_ORDER); + uint8_t auth = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_AUTH_ON_OFF); + uint8_t autht = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_AUTH_TAGL); + uint8_t prefi = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_PREFIX); + + fprintf( stderr, "csId: %i\n", csId ); + dump_var(csId); + dump_var(roc); + dump_var(policyNo); + dump_var(ealg); + dump_var(ekeyl); + dump_var(roc); + dump_var(aalg); + dump_var(akeyl); + dump_var(skeyl); + dump_var(prf); + dump_var(keydr); + dump_var(encr); + dump_var(cencr); + dump_var(fecor); + dump_var(auth); + dump_var(autht); + dump_var(prefi); +} + + +// +// ast_mikey_config +// + +ast_mikey_config::ast_mikey_config():sim(NULL),psk(NULL), psk_len(0) +{ + sim = new SipSimSoft( g_cert_chain, g_ca_db ); +} + +ast_mikey_config::~ast_mikey_config() +{ + sim = NULL; + + if (psk) { + delete psk; + psk = NULL; + } +} + +int ast_mikey_config::add_ca_file(const char *ca_file) +{ + if (!ca_file) + return -1; + + try { + MRef cert_db = getSim()->getCAs(); + + if ( cert_db == g_ca_db) { + cert_db = CertificateSet::create(); + if (!cert_db) + return -1; + getSim()->setCAs( cert_db ); + } + + cert_db->lock(); + cert_db->addFile(ca_file); + cert_db->unlock(); + } catch(CertificateException& e) { + printf("CA exception: %s\n", e.what()); + return -1; + } + + return 0; +} + +int ast_mikey_config::add_cert(const char *cert_file, const char *key_file) +{ + if (!cert_file || !key_file) + return -1; + + try { + MRef cert_chain = getSim()->getCertificateChain(); + + if ( cert_chain == g_cert_chain) { + cert_chain = CertificateChain::create(); + if (!cert_chain) + return -1; + getSim()->setCertificateChain( cert_chain ); + } + + MRef cert = Certificate::load(cert_file, key_file); + cert_chain->lock(); + cert_chain->addCertificate(cert); + cert_chain->unlock(); + } catch(CertificateException& e) { + printf("Certificate exception: %s\n", e.what()); + return -1; + } + + return 0; +} + +void ast_mikey_config::set_psk_secret(const unsigned char *secret, size_t len) +{ + if (psk) + delete psk; + + psk_len = len; + psk = new byte_t[psk_len]; + memcpy(psk, secret, psk_len); +} + + +// +// mikey +// +mikey::mikey(): state(NULL) +{ +// memset(tmp, 0, sizeof(struct mikey)); + + config = new ast_mikey_config(); + state = new Mikey( *config ); +} + +mikey::~mikey() +{ + state = NULL; +} + +void mikey::add_stream(uint32_t ssrc) +{ + state->addSender(ssrc); +} + +int mikey::set_crypto_policy(uint32_t ssrc, struct ast_srtp_policy *policy) +{ + if( !state ) + return -1; + + MRef ka = state->getKeyAgreement(); + if( !ka ) + return -1; + +// uint32_t roc = ka->getSrtpRoc(ssrc); + uint8_t policyNo = ka->findpolicyNo(ssrc); + + uint8_t ealg = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_EALG); + + uint8_t ekeyl = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_EKEYL); + + uint8_t aalg = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_AALG); + + uint8_t akeyl = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_AKEYL); + uint8_t skeyl = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_SALTKEYL); + uint8_t encr = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_ENCR_ON_OFF); + + uint8_t cencr = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTCP_ENCR_ON_OFF); + + uint8_t auth = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_AUTH_ON_OFF); + + uint8_t autht = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, + MIKEY_SRTP_AUTH_TAGL); +// Not used +// uint8_t prf = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, +// MIKEY_SRTP_PRF); +// uint8_t keydr = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, +// MIKEY_SRTP_KEY_DERRATE); +// uint8_t fecor = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, +// MIKEY_SRTP_FEC_ORDER); +// uint8_t prefi = ka->getPolicyParamTypeValue(policyNo, MIKEY_PROTO_SRTP, +// MIKEY_SRTP_PREFIX); + + printf("Policy: %d %d %d %d %d %d %d %d %d\n", + ealg, aalg, ekeyl, skeyl, akeyl, autht, encr, cencr, auth); + + if (ast_srtp_policy_set_encr_alg(policy, (enum ast_srtp_ealg)ealg)) + return -1; + + if (ast_srtp_policy_set_auth_alg(policy, (enum ast_srtp_aalg)aalg)) + return -1; + + ast_srtp_policy_set_encr_keylen(policy, ekeyl + skeyl); + ast_srtp_policy_set_auth_keylen(policy, akeyl); + ast_srtp_policy_set_srtp_auth_taglen(policy, autht); + ast_srtp_policy_set_srtp_encr_enable(policy, encr); + ast_srtp_policy_set_srtcp_encr_enable(policy, cencr); + ast_srtp_policy_set_srtp_auth_enable(policy, auth); + + return 0; +} + +// Generate master key and salt for one ssrc +int mikey::add_ssrc(uint32_t ssrc, struct ast_srtp_policy *policy) +{ + unsigned char key[MASTER_KEY_LEN]; + unsigned char salt[MASTER_SALT_LEN]; + + MRef ka = state->getKeyAgreement(); + uint8_t csId = ka->getSrtpCsId(ssrc); + +#ifdef DEBUG_OUTPUT + cerr << "mikey::add_ssrc: " << ssrc << endl; + cerr << "csId: " << (int)csId << endl; +#endif + + // TODO search streams for ssrc instead? + bool remote = (csId % 2) == 0; + + ka->genTek(csId, key, sizeof(key)); + ka->genSalt(csId, salt, sizeof(salt)); + +#ifdef DEBUG_OUTPUT + cerr << "SSRC: "<< ssrc <<" - TEK: " << binToHex(key, sizeof(key)) << endl; + cerr << "SSRC: "<< ssrc <<" - SALT: " << binToHex(salt, sizeof(salt) )<< endl; +#endif + + if (ast_srtp_policy_set_master_key(policy, key, sizeof(key), + salt, sizeof(salt)) < 0) { + printf("ast_srtp_policy_set_key failed\n"); + return -1; + } + + ast_srtp_policy_set_ssrc(policy, ssrc, remote); + + return 0; +} + +int mikey::activate() +{ + if( !state ){ + return -1; + } + + MRef ka = state->getKeyAgreement(); + if( !ka ) + return -1; + + KeyAgreementDHBase *ka_dha = dynamic_cast(*ka); + + if (ka_dha) { + fprintf(stderr, "computeTgk\n"); + return ka_dha->computeTgk(); + } + + return 0; +} + +int mikey::build_offer(char *buf, size_t buflen, int ka_type) +{ + string offer = state->initiatorCreate( ka_type ); + + if( state->error() ) + return -1; + + static const char prefix[] = "mikey "; + string msg = offer.substr(sizeof(prefix)-1); + + return string_to_buf(msg, buf, buflen); +} + +int mikey::parse_offer(const char *offer, char *buf, size_t buflen) +{ + string mikeyMsg = string("mikey ") + string(offer); + + if (state->error()) { + return -1; + } + + if (state->isInitiator()) { + // Initiator + + if (!state->initiatorAuthenticate(mikeyMsg)) { + printf("Authentication failed\n"); + return -1; + } + + string msg = state->initiatorParse(); + if (msg != ""){ + printf("Parse failed %s\n", msg.c_str()); + return -1; + } + + if (!check_policies(state->getKeyAgreement()->responderData())) { + return -1; + } + + return activate(); + } else { + // Responder + + if (!state->responderAuthenticate(mikeyMsg)) { + return -1; + } + + state->setMikeyOffer(); + + MRef ka = state->getKeyAgreement(); + + if (!check_policies(ka->initiatorData())) { + return -1; + } + + cerr << "Offer:" << endl; + dump_crypto_params(ka); + + string response = state->responderParse(); + + if (state->error()) { + // TODO return error MIKEY response + printf("responderParse failed\n"); + return -1; + } + + if (activate() < 0) + return -1; + + return string_to_buf(response, buf, buflen); + } +} + +struct ast_srtp_policy *mikey::create_policy(uint32_t ssrc) +{ + ast_srtp_policy *policy = ast_srtp_policy_alloc(); + + if (!policy) + return NULL; + + if (set_crypto_policy(ssrc, policy) < 0) { + printf("set_crypto_policy failed\n"); + ast_srtp_policy_destroy(policy); + return NULL; + } + + if (add_ssrc(ssrc, policy) < 0) { + printf("add_ssrc failed\n"); + ast_srtp_policy_destroy(policy); + return NULL; + } + + return policy; +} + + +// +// Exported functions +// + +int mikey_init(void) +{ + libmcryptoInit(); + + g_ca_db = CertificateSet::create(); + g_cert_chain = CertificateChain::create(); + + /* TODO add as conf parameters */ + char ca_name[PATH_MAX]; + char cert_name[PATH_MAX]; + char key_name[PATH_MAX]; + + snprintf(ca_name, sizeof(ca_name), "%s/%s", + ast_config_AST_CONFIG_DIR, "ca-certificates.crt"); + snprintf(cert_name, sizeof(cert_name), "%s/%s", + ast_config_AST_CONFIG_DIR, "asterisk.crt"); + snprintf(key_name, sizeof(key_name), "%s/%s", + ast_config_AST_CONFIG_DIR, "asterisk.key"); + + mikey_add_global_ca_file(ca_name); + mikey_add_global_cert_file(cert_name, key_name); + + return 0; +} + +void mikey_uninit(void) +{ + g_cert_chain = NULL; + g_ca_db = NULL; + + libmcryptoUninit(); +} + +int mikey_add_global_ca_file(const char *ca_file) +{ + if (!ca_file) + return -1; + + try { + g_ca_db->lock(); + g_ca_db->addFile(ca_file); + g_ca_db->unlock(); + } catch(CertificateException& e) { + printf("Certificate exception: %s\n", e.what()); + return -1; + } + + return 0; +} + +int mikey_add_global_cert_file(const char *cert_file, const char *key_file) +{ + if (!cert_file || !key_file) + return -1; + + try { + MRef cert = Certificate::load(cert_file, key_file); + g_cert_chain->lock(); + g_cert_chain->addCertificate(cert); + g_cert_chain->unlock(); + } catch(CertificateException& e) { + printf("Certificate exception: %s\n", e.what()); + return -1; + } + + return 0; +} + +struct mikey *mikey_alloc() +{ + mikey *cls = new mikey(); + return cls; +} + +void mikey_destroy(struct mikey *mikey) +{ + delete mikey; +} + +void mikey_set_ssrc(struct mikey *mikey, uint32_t ssrc) +{ + mikey->add_stream(ssrc); +} + +void mikey_set_psk_secret(struct mikey *mikey, const unsigned char *secret, size_t len) +{ + mikey->get_config()->set_psk_secret(secret, len); +} + +int mikey_add_ca_file(struct mikey *mikey, const char *ca_file) +{ + return mikey->get_config()->add_ca_file(ca_file); +} + +int mikey_add_cert_file(struct mikey *mikey, const char *cert_file, + const char *key_file) +{ + return mikey->get_config()->add_cert(cert_file, key_file); +} + +// Build and return mikey offer in buf +int mikey_build_offer(struct mikey *mikey, char *buf, size_t buflen, + enum ast_mikey_type type) +{ + int ka_type; + + switch (type) { + case AST_MIKEY_TYPE_PSK: + ka_type = KEY_AGREEMENT_TYPE_PSK; + break; + case AST_MIKEY_TYPE_PK: + ka_type = KEY_AGREEMENT_TYPE_PK; + break; + case AST_MIKEY_TYPE_DH_SIGN: + ka_type = KEY_AGREEMENT_TYPE_DH; + break; + case AST_MIKEY_TYPE_DH_HMAC: + ka_type = KEY_AGREEMENT_TYPE_DHHMAC; + break; + case AST_MIKEY_TYPE_RSA_R: + ka_type = KEY_AGREEMENT_TYPE_RSA_R; + break; + default: + fprintf(stderr, "Unsupported MIKEY type"); + return -1; + } + + return mikey->build_offer(buf, buflen, ka_type); +} + +int mikey_process(struct mikey *mikey, const char *offer, + char *buf, size_t buflen) +{ + return mikey->parse_offer(offer, buf, buflen); +} + +// Generate master key and salt for one ssrc and add to rtp policy +struct ast_srtp_policy *mikey_create_policy(struct mikey *mikey, uint32_t ssrc) +{ + return mikey->create_policy(ssrc); +} diff -urN -x _darcs -x .svn -x '*~' upstream/res/mikey.h local/res/mikey.h --- upstream/res/mikey.h 1970-01-01 01:00:00.000000000 +0100 +++ local/res/mikey.h 2007-01-19 11:55:48.000000000 +0100 @@ -0,0 +1,62 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2005 - 2007, Mikael Magnusson + * + * Mikael Magnusson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + * + */ + +/*! \file mikey.h + * + * \brief MIKEY (internal) + * + * MIKEY is specified in RFC 3830, 4650 and 4738. + * + * \author Mikael Magnusson + */ + +#ifndef _MIKEY_H +#define _MIKEY_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int mikey_init(void); +void mikey_uninit(void); +int mikey_add_global_ca_file(const char *ca_file); +int mikey_add_global_cert_file(const char *cert_file, const char *key_file); + + +struct mikey *mikey_alloc(void); +void mikey_destroy(struct mikey *mikey); +void mikey_set_ssrc(struct mikey *mikey, uint32_t ssrc); +void mikey_set_psk_secret(struct mikey *mikey, const unsigned char *secret, size_t len); +int mikey_add_ca_file(struct mikey *mikey, const char *ca_file); +int mikey_add_cert_file(struct mikey *mikey, const char *cert_file, const char *key_file); + +int mikey_build_offer(struct mikey *mikey, char *buf, size_t len, enum ast_mikey_type type); + +int mikey_process(struct mikey *mikey, const char *offer, char *buf, size_t buflen); + +struct ast_srtp_policy *mikey_create_policy(struct mikey *mikey, uint32_t ssrc); + +#ifdef __cplusplus +} +#endif + +#endif /* _MIKEY_H */ diff -urN -x _darcs -x .svn -x '*~' upstream/res/res_mikey.c local/res/res_mikey.c --- upstream/res/res_mikey.c 1970-01-01 01:00:00.000000000 +0100 +++ local/res/res_mikey.c 2007-03-24 15:30:10.000000000 +0100 @@ -0,0 +1,89 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2005 - 2006, Mikael Magnusson + * + * Mikael Magnusson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file res_mikey.c + * + * \brief Multimedia Internet KEYing (MIKEY) + * + * Specified in RFC 3830, 4650 and 4738. + * + * \author Mikael Magnusson + */ + +/*** MODULEINFO + mutil + mnetutil + mcrypto + mikey +***/ + +#include "asterisk/autoconfig.h" +#include"asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: $") + +#include +#include"mikey.h" + +#define DESCRIPTION "Multimedia Internet KEYing (MIKEY)" + +/* extern struct ast_mikey_res g_mikey_res; */ + +struct ast_mikey_res g_mikey_res = { + .mikey_init = mikey_init, + .mikey_uninit = mikey_uninit, +/* .mikey_add_global_ca_file = mikey_add_global_ca_file, */ +/* .mikey_add_global_cert_file = mikey_add_global_cert_file, */ + .mikey_alloc = mikey_alloc, + .mikey_destroy = mikey_destroy, + .mikey_set_ssrc = mikey_set_ssrc, + .mikey_set_psk_secret = mikey_set_psk_secret, +/* .mikey_add_ca_file = mikey_add_ca_file, */ +/* .mikey_add_cert_file = mikey_add_cert_file, */ + .mikey_build_offer = mikey_build_offer, + .mikey_process = mikey_process, + .mikey_create_policy = mikey_create_policy, +}; + + +/* + * Exported functions + */ + +static int load_module(void) +{ + mikey_init(); + return ast_register_mikey(&g_mikey_res); +} + +static int unload_module(void) +{ + int res = ast_unregister_mikey(&g_mikey_res); + mikey_uninit(); + return res; +} + +int usecount(void) +{ + return 1; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, DESCRIPTION, + .load = load_module, + .unload = unload_module, + ); diff -urN -x _darcs -x .svn -x '*~' upstream/res/res_srtp.c local/res/res_srtp.c --- upstream/res/res_srtp.c 1970-01-01 01:00:00.000000000 +0100 +++ local/res/res_srtp.c 2007-03-24 15:30:10.000000000 +0100 @@ -0,0 +1,569 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2005, Mikael Magnusson + * + * Mikael Magnusson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + * + * Builds on libSRTP http://srtp.sourceforge.net + */ + + + +/*! \file res_srtp.c + * + * \brief Secure RTP (SRTP) + * + * Secure RTP (SRTP) + * Specified in RFC 3711. + * + * \author Mikael Magnusson + */ + +/*** MODULEINFO + srtp +***/ + +/* The SIP channel will automatically use sdescriptions if received in a SDP offer, + and res_srtp is loaded. SRTP with sdescriptions key exchange can be activated + in outgoing offers by setting _SIP_SRTP_SDES=1 in extension.conf before executing Dial + + The dial fails if the callee doesn't support SRTP and sdescriptions. + + exten => 2345,1,Set(_SIP_SRTP_SDES=1) + exten => 2345,2,Dial(SIP/1001) + + NOTE: Since chan_sip does not support TLS, this is just a first step + towards a secure channel. At this moment, all key exchange will be sent + in clear text, making it easy to eavesdrop. +*/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: $") + +#include + +#include "asterisk/lock.h" +#include "asterisk/module.h" +#include "asterisk/options.h" +#include "asterisk/rtp.h" + +#define DESCRIPTION "Secure RTP (SRTP)" + +struct ast_srtp { + struct ast_rtp *rtp; + srtp_t session; + const struct ast_srtp_cb *cb; + void *data; + unsigned char buf[8192 + AST_FRIENDLY_OFFSET]; +}; + +struct ast_srtp_policy { + srtp_policy_t sp; +}; + +static int srtpdebug = 1; +static int g_initialized = 0; + +/* Exported functions */ +int usecount(void); + +/* SRTP functions */ +static int res_srtp_create(struct ast_srtp **srtp, + struct ast_rtp *rtp, + struct ast_srtp_policy *policy); +static void res_srtp_destroy(struct ast_srtp *srtp); +static int res_srtp_add_stream(struct ast_srtp *srtp, + struct ast_srtp_policy *policy); + +static int res_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len); +static int res_srtp_protect(struct ast_srtp *srtp, void **buf, int *len); +static int res_srtp_get_random(unsigned char *key, size_t len); +static void res_srtp_set_cb(struct ast_srtp *srtp, + const struct ast_srtp_cb *cb, void *data); + +/* Policy functions */ +static struct ast_srtp_policy *res_srtp_policy_alloc(void); +static void res_srtp_policy_destroy(struct ast_srtp_policy *policy); +static int res_srtp_policy_set_suite(struct ast_srtp_policy *policy, + enum ast_srtp_suite suite); +static int res_srtp_policy_set_master_key(struct ast_srtp_policy *policy, + const unsigned char *key, size_t key_len, + const unsigned char *salt, size_t salt_len); +static int res_srtp_policy_set_encr_alg(struct ast_srtp_policy *policy, + enum ast_srtp_ealg ealg); +static int res_srtp_policy_set_auth_alg(struct ast_srtp_policy *policy, + enum ast_srtp_aalg aalg); +static void res_srtp_policy_set_encr_keylen(struct ast_srtp_policy *policy, + int ekeyl); +static void res_srtp_policy_set_auth_keylen(struct ast_srtp_policy *policy, + int akeyl); +static void res_srtp_policy_set_srtp_auth_taglen(struct ast_srtp_policy *policy, + int autht); +static void res_srtp_policy_set_srtp_encr_enable(struct ast_srtp_policy *policy, + int enable); +static void res_srtp_policy_set_srtcp_encr_enable(struct ast_srtp_policy *policy, + int enable); +static void res_srtp_policy_set_srtp_auth_enable(struct ast_srtp_policy *policy, + int enable); +static void res_srtp_policy_set_ssrc(struct ast_srtp_policy *policy, + unsigned long ssrc, int inbound); + +static struct ast_srtp_res srtp_res = { + .create = res_srtp_create, + .destroy = res_srtp_destroy, + .add_stream = res_srtp_add_stream, + .set_cb = res_srtp_set_cb, + .unprotect = res_srtp_unprotect, + .protect = res_srtp_protect, + .get_random = res_srtp_get_random +}; + +static struct ast_srtp_policy_res policy_res = { + .alloc = res_srtp_policy_alloc, + .destroy = res_srtp_policy_destroy, + .set_suite = res_srtp_policy_set_suite, + .set_master_key = res_srtp_policy_set_master_key, + .set_encr_alg = res_srtp_policy_set_encr_alg, + .set_auth_alg = res_srtp_policy_set_auth_alg, + .set_encr_keylen = res_srtp_policy_set_encr_keylen, + .set_auth_keylen = res_srtp_policy_set_auth_keylen, + .set_srtp_auth_taglen = res_srtp_policy_set_srtp_auth_taglen, + .set_srtp_encr_enable = res_srtp_policy_set_srtp_encr_enable, + .set_srtcp_encr_enable = res_srtp_policy_set_srtcp_encr_enable, + .set_srtp_auth_enable = res_srtp_policy_set_srtp_auth_enable, + .set_ssrc = res_srtp_policy_set_ssrc +}; + +static const char *srtp_errstr(int err) +{ + switch(err) { + case err_status_ok: + return "nothing to report"; + case err_status_fail: + return "unspecified failure"; + case err_status_bad_param: + return "unsupported parameter"; + case err_status_alloc_fail: + return "couldn't allocate memory"; + case err_status_dealloc_fail: + return "couldn't deallocate properly"; + case err_status_init_fail: + return "couldn't initialize"; + case err_status_terminus: + return "can't process as much data as requested"; + case err_status_auth_fail: + return "authentication failure"; + case err_status_cipher_fail: + return "cipher failure"; + case err_status_replay_fail: + return "replay check failed (bad index)"; + case err_status_replay_old: + return "replay check failed (index too old)"; + case err_status_algo_fail: + return "algorithm failed test routine"; + case err_status_no_such_op: + return "unsupported operation"; + case err_status_no_ctx: + return "no appropriate context found"; + case err_status_cant_check: + return "unable to perform desired validation"; + case err_status_key_expired: + return "can't use key any more"; + default: + return "unknown"; + } +} + +static struct ast_srtp *res_srtp_new(void) +{ + struct ast_srtp *srtp = malloc(sizeof(*srtp)); + memset(srtp, 0, sizeof(*srtp)); + return srtp; +} + +/* + struct ast_srtp_policy +*/ +static void srtp_event_cb(srtp_event_data_t *data) +{ + switch (data->event) { + case event_ssrc_collision: { + if (option_debug || srtpdebug) { + ast_log(LOG_DEBUG, "SSRC collision ssrc:%u dir:%d\n", + ntohl(data->stream->ssrc), + data->stream->direction); + } + break; + } + case event_key_soft_limit: { + if (option_debug || srtpdebug) { + ast_log(LOG_DEBUG, "event_key_soft_limit\n"); + } + break; + } + case event_key_hard_limit: { + if (option_debug || srtpdebug) { + ast_log(LOG_DEBUG, "event_key_hard_limit\n"); + } + break; + } + case event_packet_index_limit: { + if (option_debug || srtpdebug) { + ast_log(LOG_DEBUG, "event_packet_index_limit\n"); + } + break; + } + } +} + +static void res_srtp_policy_set_ssrc(struct ast_srtp_policy *policy, + unsigned long ssrc, int inbound) +{ + if (ssrc) { + policy->sp.ssrc.type = ssrc_specific; + policy->sp.ssrc.value = ssrc; + } else { + policy->sp.ssrc.type = + inbound ? ssrc_any_inbound : ssrc_any_outbound; + } +} + +static struct ast_srtp_policy *res_srtp_policy_alloc() +{ + struct ast_srtp_policy *tmp = malloc(sizeof(*tmp)); + + memset(tmp, 0, sizeof(*tmp)); + return tmp; +} + +static void +res_srtp_policy_destroy(struct ast_srtp_policy *policy) +{ + if (policy->sp.key) { + free(policy->sp.key); + policy->sp.key = NULL; + } + free(policy); +} + +static int policy_set_suite(crypto_policy_t *p, enum ast_srtp_suite suite) +{ + switch (suite) { + case AST_AES_CM_128_HMAC_SHA1_80: + p->cipher_type = AES_128_ICM; + p->cipher_key_len = 30; + p->auth_type = HMAC_SHA1; + p->auth_key_len = 20; + p->auth_tag_len = 10; + p->sec_serv = sec_serv_conf_and_auth; + return 0; + + case AST_AES_CM_128_HMAC_SHA1_32: + p->cipher_type = AES_128_ICM; + p->cipher_key_len = 30; + p->auth_type = HMAC_SHA1; + p->auth_key_len = 20; + p->auth_tag_len = 4; + p->sec_serv = sec_serv_conf_and_auth; + return 0; + + default: + ast_log(LOG_ERROR, "Invalid crypto suite: %d\n", suite); + return -1; + } +} + +static int +res_srtp_policy_set_suite(struct ast_srtp_policy *policy, + enum ast_srtp_suite suite) +{ + int res = policy_set_suite(&policy->sp.rtp, suite) | + policy_set_suite(&policy->sp.rtcp, suite); + + return res; +} + +static int +res_srtp_policy_set_master_key(struct ast_srtp_policy *policy, + const unsigned char *key, size_t key_len, + const unsigned char *salt, size_t salt_len) +{ + size_t size = key_len + salt_len; + unsigned char *master_key = NULL; + + if (policy->sp.key) { + free(policy->sp.key); + policy->sp.key = NULL; + } + + master_key = malloc(size); + + memcpy(master_key, key, key_len); + memcpy(master_key + key_len, salt, salt_len); + + policy->sp.key = master_key; + return 0; +} + +static int +res_srtp_policy_set_encr_alg(struct ast_srtp_policy *policy, + enum ast_srtp_ealg ealg) +{ + int type = -1; + + switch (ealg) { + case AST_MIKEY_SRTP_EALG_NULL: + type = NULL_CIPHER; + break; + case AST_MIKEY_SRTP_EALG_AESCM: + type = AES_128_ICM; + break; + default: + return -1; + } + + policy->sp.rtp.cipher_type = type; + policy->sp.rtcp.cipher_type = type; + return 0; +} + +static int +res_srtp_policy_set_auth_alg(struct ast_srtp_policy *policy, + enum ast_srtp_aalg aalg) +{ + int type = -1; + + switch (aalg) { + case AST_MIKEY_SRTP_AALG_NULL: + type = NULL_AUTH; + break; + case AST_MIKEY_SRTP_AALG_SHA1HMAC: + type = HMAC_SHA1; + break; + default: + return -1; + } + + policy->sp.rtp.auth_type = type; + policy->sp.rtcp.auth_type = type; + return 0; +} + +static void +res_srtp_policy_set_encr_keylen(struct ast_srtp_policy *policy, int ekeyl) +{ + policy->sp.rtp.cipher_key_len = ekeyl; + policy->sp.rtcp.cipher_key_len = ekeyl; +} + +static void +res_srtp_policy_set_auth_keylen(struct ast_srtp_policy *policy, int akeyl) +{ + policy->sp.rtp.auth_key_len = akeyl; + policy->sp.rtcp.auth_key_len = akeyl; +} + +static void +res_srtp_policy_set_srtp_auth_taglen(struct ast_srtp_policy *policy, int autht) +{ + policy->sp.rtp.auth_tag_len = autht; + policy->sp.rtcp.auth_tag_len = autht; + +} + +static void +res_srtp_policy_set_srtp_encr_enable(struct ast_srtp_policy *policy, int enable) +{ + int serv = enable ? sec_serv_conf : sec_serv_none; + policy->sp.rtp.sec_serv = + (policy->sp.rtp.sec_serv & ~sec_serv_conf) | serv; +} + +static void +res_srtp_policy_set_srtcp_encr_enable(struct ast_srtp_policy *policy, int enable) +{ + int serv = enable ? sec_serv_conf : sec_serv_none; + policy->sp.rtcp.sec_serv = + (policy->sp.rtcp.sec_serv & ~sec_serv_conf) | serv; +} + +static void +res_srtp_policy_set_srtp_auth_enable(struct ast_srtp_policy *policy, int enable) +{ + int serv = enable ? sec_serv_auth : sec_serv_none; + policy->sp.rtp.sec_serv = + (policy->sp.rtp.sec_serv & ~sec_serv_auth) | serv; +} + + +static int res_srtp_get_random(unsigned char *key, size_t len) +{ + int res = crypto_get_random(key, len); + + return res != err_status_ok ? -1: 0; +} + +static void res_srtp_set_cb(struct ast_srtp *srtp, + const struct ast_srtp_cb *cb, void *data) +{ + if (!srtp) + return; + + srtp->cb = cb; + srtp->data = data; +} + + +/* Vtable functions */ + +static int +res_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len) +{ + int res = 0; + int i; + + for (i = 0; i < 2; i++) { + srtp_hdr_t *header = buf; + + res = srtp_unprotect(srtp->session, buf, len); + if (res != err_status_no_ctx) + break; + + if (srtp->cb && srtp->cb->no_ctx) { + if (srtp->cb->no_ctx(srtp->rtp, ntohl(header->ssrc), srtp->data) < 0) { + break; + } + + } else { + break; + } + } + + if (res != err_status_ok) { + if (option_debug || srtpdebug) { + ast_log(LOG_DEBUG, "SRTP unprotect: %s\n", + srtp_errstr(res)); + } + return -1; + } + + return *len; +} + +static int +res_srtp_protect(struct ast_srtp *srtp, void **buf, int *len) +{ + int res = 0; + + if ((*len + SRTP_MAX_TRAILER_LEN) > sizeof(srtp->buf)) + return -1; + + memcpy(srtp->buf, *buf, *len); + + res = srtp_protect(srtp->session, srtp->buf, len); + + if (res != err_status_ok) { + if (option_debug || srtpdebug) { + ast_log(LOG_DEBUG, "SRTP protect: %s\n", + srtp_errstr(res)); + } + return -1; + } + + *buf = srtp->buf; + return *len; +} + +static int +res_srtp_create(struct ast_srtp **srtp, struct ast_rtp *rtp, + struct ast_srtp_policy *policy) +{ + int res; + struct ast_srtp *temp = res_srtp_new(); + + res = srtp_create(&temp->session, &policy->sp); + if (res != err_status_ok) { + return -1; + } + + temp->rtp = rtp; + *srtp = temp; + + return 0; +} + +static void +res_srtp_destroy(struct ast_srtp *srtp) +{ + if (srtp->session) { + srtp_dealloc(srtp->session); + } + + free(srtp); +} + +static int +res_srtp_add_stream(struct ast_srtp *srtp, struct ast_srtp_policy *policy) +{ + int res; + + res = srtp_add_stream(srtp->session, &policy->sp); + if (res != err_status_ok) + return -1; + + return 0; +} + +static int res_srtp_init(void) +{ + int res; + + if (g_initialized) + return 0; + + res = srtp_init(); + if (res != err_status_ok) + return -1; + + srtp_install_event_handler(srtp_event_cb); + + return ast_rtp_register_srtp(&srtp_res, &policy_res); +} + + +/* + * Exported functions + */ + +static int load_module(void) +{ + return res_srtp_init(); +} + +static int unload_module(void) +{ + return ast_rtp_unregister_srtp(&srtp_res, &policy_res); +} + +int usecount(void) +{ + return 1; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, DESCRIPTION, + .load = load_module, + .unload = unload_module, +/* .reload = reload, */ + );