diff --git a/sflphone-client-gnome/src/config/accountconfigdialog.c b/sflphone-client-gnome/src/config/accountconfigdialog.c index 5fa496fe3d725e9ee018af1c04ee625a85bc821c..fbe634efa1f7b244a2217eb259f93fa98f60ab25 100644 --- a/sflphone-client-gnome/src/config/accountconfigdialog.c +++ b/sflphone-client-gnome/src/config/accountconfigdialog.c @@ -730,7 +730,7 @@ GtkWidget * create_security_tab(account_t **a) keyExchangeCombo = gtk_combo_box_new_text(); gtk_label_set_mnemonic_widget (GTK_LABEL (label), keyExchangeCombo); gtk_combo_box_append_text(GTK_COMBO_BOX(keyExchangeCombo), "ZRTP"); - //gtk_combo_box_append_text(GTK_COMBO_BOX(keyExchangeCombo), "SDES"); + gtk_combo_box_append_text(GTK_COMBO_BOX(keyExchangeCombo), "SDES"); gtk_combo_box_append_text(GTK_COMBO_BOX(keyExchangeCombo), _("Disabled")); advancedZrtpButton = gtk_button_new_from_stock(GTK_STOCK_PREFERENCES); @@ -738,13 +738,17 @@ GtkWidget * create_security_tab(account_t **a) if (g_strcmp0(curSRTPEnabled, "false") == 0) { - gtk_combo_box_set_active(GTK_COMBO_BOX(keyExchangeCombo), 1); + gtk_combo_box_set_active(GTK_COMBO_BOX(keyExchangeCombo), 2); gtk_widget_set_sensitive(GTK_WIDGET(advancedZrtpButton), FALSE); } else { if (strcmp(curKeyExchange, ZRTP) == 0) { gtk_combo_box_set_active(GTK_COMBO_BOX(keyExchangeCombo),0); - } else { - gtk_combo_box_set_active(GTK_COMBO_BOX(keyExchangeCombo), 1); + } + else if (strcmp(curKeyExchange, SDES) == 0) { + gtk_combo_box_set_active(GTK_COMBO_BOX(keyExchangeCombo),1); + } + else { + gtk_combo_box_set_active(GTK_COMBO_BOX(keyExchangeCombo), 2); gtk_widget_set_sensitive(GTK_WIDGET(advancedZrtpButton), FALSE); } } @@ -1157,7 +1161,12 @@ show_account_window (account_t * a) if (g_strcasecmp(keyExchange, "ZRTP") == 0) { g_hash_table_replace(currentAccount->properties, g_strdup(ACCOUNT_SRTP_ENABLED), g_strdup("true")); g_hash_table_replace(currentAccount->properties, g_strdup(ACCOUNT_KEY_EXCHANGE), g_strdup(ZRTP)); - } else { + } + else if(g_strcasecmp(keyExchange, "SDES") == 0) { + g_hash_table_replace(currentAccount->properties, g_strdup(ACCOUNT_SRTP_ENABLED), g_strdup("true")); + g_hash_table_replace(currentAccount->properties, g_strdup(ACCOUNT_KEY_EXCHANGE), g_strdup(SDES)); + } + else { g_hash_table_replace(currentAccount->properties, g_strdup(ACCOUNT_SRTP_ENABLED), g_strdup("false")); } diff --git a/sflphone-client-gnome/src/config/preferencesdialog.c b/sflphone-client-gnome/src/config/preferencesdialog.c index 791e4aaea6c2042c3f9bb4e6f7e27f558fe26e41..1a8bf8b9deb1eb56471808a463383005a464a7e9 100644 --- a/sflphone-client-gnome/src/config/preferencesdialog.c +++ b/sflphone-client-gnome/src/config/preferencesdialog.c @@ -120,10 +120,16 @@ static void key_exchange_changed_cb(GtkWidget *widget, gpointer data) { DEBUG("Key exchange changed"); if (g_strcasecmp(gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget)), (gchar *) "ZRTP") == 0) { - gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE); - g_hash_table_replace(directIpCallsProperties, g_strdup(ACCOUNT_SRTP_ENABLED), g_strdup("true")); - g_hash_table_replace(directIpCallsProperties, g_strdup(ACCOUNT_KEY_EXCHANGE), g_strdup(ZRTP)); - } else { + gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE); + g_hash_table_replace(directIpCallsProperties, g_strdup(ACCOUNT_SRTP_ENABLED), g_strdup("true")); + g_hash_table_replace(directIpCallsProperties, g_strdup(ACCOUNT_KEY_EXCHANGE), g_strdup(ZRTP)); + } + else if (g_strcasecmp(gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget)), (gchar *) "SDES") == 0) { + gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE); + g_hash_table_replace(directIpCallsProperties, g_strdup(ACCOUNT_SRTP_ENABLED), g_strdup("true")); + g_hash_table_replace(directIpCallsProperties, g_strdup(ACCOUNT_KEY_EXCHANGE), g_strdup(SDES)); + } + else { gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE); DEBUG("Setting key exchange %s to %s\n", ACCOUNT_KEY_EXCHANGE, KEY_EXCHANGE_NONE); g_hash_table_replace(directIpCallsProperties, g_strdup(ACCOUNT_SRTP_ENABLED), g_strdup("false")); @@ -326,16 +332,20 @@ GtkWidget* create_direct_ip_calls_tab() keyExchangeCombo = gtk_combo_box_new_text(); gtk_label_set_mnemonic_widget (GTK_LABEL (label), keyExchangeCombo); gtk_combo_box_append_text(GTK_COMBO_BOX(keyExchangeCombo), "ZRTP"); - //gtk_combo_box_append_text(GTK_COMBO_BOX(keyExchangeCombo), "SDES"); + gtk_combo_box_append_text(GTK_COMBO_BOX(keyExchangeCombo), "SDES"); gtk_combo_box_append_text(GTK_COMBO_BOX(keyExchangeCombo), _("Disabled")); advancedZrtpButton = gtk_button_new_from_stock(GTK_STOCK_PREFERENCES); g_signal_connect(G_OBJECT(advancedZrtpButton), "clicked", G_CALLBACK(show_advanced_zrtp_options_cb), directIpCallsProperties); if (g_strcasecmp(curKeyExchange, ZRTP) == 0) { - gtk_combo_box_set_active(GTK_COMBO_BOX(keyExchangeCombo),0); - } else { - gtk_combo_box_set_active(GTK_COMBO_BOX(keyExchangeCombo), 1); + gtk_combo_box_set_active(GTK_COMBO_BOX(keyExchangeCombo),0); + } + else if(g_strcasecmp(curKeyExchange, SDES) == 0) { + gtk_combo_box_set_active(GTK_COMBO_BOX(keyExchangeCombo),1); + } + else { + gtk_combo_box_set_active(GTK_COMBO_BOX(keyExchangeCombo), 2); gtk_widget_set_sensitive(GTK_WIDGET(advancedZrtpButton), FALSE); } diff --git a/sflphone-client-gnome/src/sflphone_const.h b/sflphone-client-gnome/src/sflphone_const.h index d8e7870b9efa49f0ee12d2a4b77e181a634851b9..52e786ade3e73803fe23fcf3943435c3fe7b7851 100644 --- a/sflphone-client-gnome/src/sflphone_const.h +++ b/sflphone-client-gnome/src/sflphone_const.h @@ -51,9 +51,9 @@ #define UNUSED __attribute__((__unused__)) #define ACCOUNT_TYPE "Account.type" -#define ACCOUNT_ALIAS "Account.alias" -#define ACCOUNT_ENABLED "Account.enable" -#define ACCOUNT_MAILBOX "Account.mailbox" +#define ACCOUNT_ALIAS "Account.alias" +#define ACCOUNT_ENABLED "Account.enable" +#define ACCOUNT_MAILBOX "Account.mailbox" #define ACCOUNT_RESOLVE_ONCE "Account.resolveOnce" #define ACCOUNT_REGISTRATION_EXPIRE "Account.expire" #define ACCOUNT_SIP_STUN_SERVER "STUN.server" @@ -71,7 +71,7 @@ #define ACCOUNT_DISPLAY_SAS_ONCE "ZRTP.displaySasOnce" #define KEY_EXCHANGE_NONE "0" #define ZRTP "1" -#define SDES_TLS "2" +#define SDES "2" #define TLS_ENABLE "TLS.enable" #define TLS_PORT "TLS.port" diff --git a/sflphone-common/configure.ac b/sflphone-common/configure.ac index 41bfe9221b850eb4ac988e88fd260feb978e5365..fe7cdfcc927b1878cab573e780f0984c19be50c1 100644 --- a/sflphone-common/configure.ac +++ b/sflphone-common/configure.ac @@ -284,7 +284,7 @@ fi xml_CFLAGS= xml_LIBS=-lexpat - AC_SUBST(xml_CFLAGS) +AC_SUBST(xml_CFLAGS) AC_SUBST(xml_LIBS) AC_CHECK_LIB([pthread], pthread_create, @@ -295,6 +295,22 @@ AC_CHECK_LIB([pthread], pthread_create, AC_MSG_ERROR([You need the POSIX Thread library (pthreads)]) fi + +AC_CHECK_LIB([pcre], pcre_free, + [AC_CHECK_HEADERS(pcre.h, have_pcre=true, have_pcre=false)], + have_pcre=false) + + if test "$have_pcre" = "false"; then +AC_MSG_ERROR([You need the Perl-Compatible Regular Expressions library (pcre)]) + fi + +PCRE_LIBS=-lpcre +PCRE_CFLAGS= +AC_SUBST(PCRE_LIBS) +AC_SUBST(PCRE_CFLAGS) + + + # For the tools/, we need libdbus-c++ for the "build" architecture as well AM_CONDITIONAL(CROSS_COMPILING, test "$cross_compiling" = "yes") diff --git a/sflphone-common/src/Makefile.am b/sflphone-common/src/Makefile.am index 0d2b2da9cc12f78d4bf4898061de9359e26a5da6..10d240d25e40a2b237f61cb2dae6d7420bd2de44 100644 --- a/sflphone-common/src/Makefile.am +++ b/sflphone-common/src/Makefile.am @@ -62,7 +62,7 @@ noinst_LTLIBRARIES = libsflphone.la noinst_HEADERS = \ conference.h \ - voiplink.h \ + voiplink.h \ managerimpl.h \ manager.h \ global.h \ diff --git a/sflphone-common/src/audio/audiortp/AudioRtpFactory.cpp b/sflphone-common/src/audio/audiortp/AudioRtpFactory.cpp index 318bf8c727e9bda938b106b1cf82000eced3c4a6..d9f88f1b55a5dbdcbd7d7c9fcf35a7aa5fda20e8 100644 --- a/sflphone-common/src/audio/audiortp/AudioRtpFactory.cpp +++ b/sflphone-common/src/audio/audiortp/AudioRtpFactory.cpp @@ -20,11 +20,13 @@ #include "AudioRtpFactory.h" #include "AudioZrtpSession.h" +#include "AudioSrtpSession.h" #include "AudioSymmetricRtpSession.h" #include "manager.h" #include "account.h" #include "sip/sipcall.h" +#include "sip/SdesNegotiator.h" #include <assert.h> @@ -106,6 +108,12 @@ void AudioRtpFactory::initAudioRtpSession (SIPCall * ca) case Sdes: + _rtpSession = new AudioSrtpSession (&Manager::instance(), ca); + _rtpSessionType = Sdes; + + ca->getLocalSDP()->set_srtp_crypto(static_cast<AudioSrtpSession *> (_rtpSession)->getLocalCryptoInfo()); + break; + default: throw UnsupportedRtpSessionType(); } @@ -125,6 +133,9 @@ void AudioRtpFactory::start (void) switch (_rtpSessionType) { case Sdes: + if (static_cast<AudioSrtpSession *> (_rtpSession)->startRtpThread() != 0) { + throw AudioRtpFactoryException ("Failed to start AudioSRtpSession thread"); + } break; case Symmetric: @@ -160,6 +171,8 @@ void AudioRtpFactory::stop (void) switch (_rtpSessionType) { case Sdes: + delete static_cast<AudioSrtpSession *> (_rtpSession); + break; case Symmetric: delete static_cast<AudioSymmetricRtpSession *> (_rtpSession); @@ -177,6 +190,29 @@ void AudioRtpFactory::stop (void) } } +void AudioRtpFactory::updateDestinationIpAddress (void) +{ + _debug ("Updating IP address"); + if (_rtpSession == NULL) { + throw AudioRtpFactoryException ("_rtpSession was null when trying to update IP address"); + } + + switch (_rtpSessionType) { + + case Sdes: + static_cast<AudioSrtpSession *> (_rtpSession)->updateDestinationIpAddress(); + break; + + case Symmetric: + static_cast<AudioSymmetricRtpSession *> (_rtpSession)->updateDestinationIpAddress(); + break; + + case Zrtp: + static_cast<AudioZrtpSession *> (_rtpSession)->updateDestinationIpAddress(); + break; + } +} + sfl::AudioZrtpSession * AudioRtpFactory::getAudioZrtpSession() { if ( (_rtpSessionType == Zrtp) && (_rtpSessionType != NULL)) { @@ -185,4 +221,16 @@ sfl::AudioZrtpSession * AudioRtpFactory::getAudioZrtpSession() throw AudioRtpFactoryException(); } } + + void AudioRtpFactory::setRemoteCryptoInfo(sfl::SdesNegotiator& nego) +{ + if ( (_rtpSessionType != NULL) && (_rtpSessionType == Sdes)) { + static_cast<AudioSrtpSession *> (_rtpSession)->setRemoteCryptoInfo(nego); + } + else { + throw AudioRtpFactoryException(); + } +} } + + diff --git a/sflphone-common/src/audio/audiortp/AudioRtpFactory.h b/sflphone-common/src/audio/audiortp/AudioRtpFactory.h index 0eefcbfc8264339055a0cb98520fd95d1f7d9879..664bf3ae2cda5b2ae39804f6bfbcfc7b757413a7 100644 --- a/sflphone-common/src/audio/audiortp/AudioRtpFactory.h +++ b/sflphone-common/src/audio/audiortp/AudioRtpFactory.h @@ -22,14 +22,20 @@ #include <stdexcept> #include <cc++/thread.h> +#include "sip/SdesNegotiator.h" + +class SdesNegotiator; class SIPCall; + namespace sfl { class AudioZrtpSession; + class AudioSrtpSession; } namespace sfl { class AudioZrtpSession; + class AudioSrtpSession; // Possible kind of rtp session typedef enum RtpMethod { @@ -76,6 +82,12 @@ namespace sfl { * @param None */ void stop(); + + /** + * Update current RTP destination address with one stored in call + * @param None + */ + void updateDestinationIpAddress (void); /** * @param None @@ -83,13 +95,28 @@ namespace sfl { * file. initAudioRtpSession must have been called prior to that. */ inline void * getAudioRtpSession(void) { return _rtpSession; } + + /** + * @param None + * @return The internal audio rtp session type + * Symmetric = 0 + * Zrtp = 1 + * Sdes = 2 + */ + inline RtpMethod getAudioRtpType(void) { return _rtpSessionType; } /** * Get the current AudioZrtpSession. Throws an AudioRtpFactoryException * if the current rtp thread is null, or if it's not of the correct type. * @return The current AudioZrtpSession thread. */ - sfl::AudioZrtpSession * getAudioZrtpSession(); + sfl::AudioZrtpSession * getAudioZrtpSession(); + + /** + * Set remote cryptographic info. Should be called after negotiation in SDP + * offer/answer session. + */ + void setRemoteCryptoInfo(sfl::SdesNegotiator& nego); private: void * _rtpSession; diff --git a/sflphone-common/src/audio/audiortp/AudioRtpSession.h b/sflphone-common/src/audio/audiortp/AudioRtpSession.h index e1d8b536cb87083deb612096b4f2ab2e4d0c1442..d168cb8d750fef943960ef2e3b1c7999002a4bc3 100644 --- a/sflphone-common/src/audio/audiortp/AudioRtpSession.h +++ b/sflphone-common/src/audio/audiortp/AudioRtpSession.h @@ -66,7 +66,12 @@ namespace sfl { virtual void run (); int startRtpThread(); - + + /** + * Used mostly when receiving a reinvite + */ + void updateDestinationIpAddress(void); + private: void initBuffers(void); @@ -98,6 +103,16 @@ namespace sfl { // it amounts to the same as doing // start() with no semaphore at all. ost::Semaphore * _mainloopSemaphore; + + // Main destination address for this rtp session. + // Stored in case or reINVITE, which may require to forget + // this destination and update a new one. + ost::InetHostAddress _remote_ip; + + // Main destination port for this rtp session. + // Stored in case reINVITE, which may require to forget + // this destination and update a new one + unsigned short _remote_port; AudioCodec * _audiocodec; @@ -292,20 +307,36 @@ namespace sfl { } _debug ("Setting IP address for the RTP session\n"); - - ost::InetHostAddress remote_ip (_ca->getLocalSDP()->get_remote_ip().c_str()); + + // Store remote ip in case we would need to forget current destination + _remote_ip = ost::InetHostAddress(_ca->getLocalSDP()->get_remote_ip().c_str()); _debug ("Init audio RTP session: remote ip %s\n", _ca->getLocalSDP()->get_remote_ip().data()); - if (!remote_ip) { + if (!_remote_ip) { _debug ("Target IP address [%s] is not correct!\n", _ca->getLocalSDP()->get_remote_ip().data()); return; } - if (! static_cast<D*>(this)->addDestination (remote_ip, (unsigned short) _ca->getLocalSDP()->get_remote_audio_port())) { + // Store remote port in case we would need to forget current destination + _remote_port = (unsigned short) _ca->getLocalSDP()->get_remote_audio_port(); + + if (! static_cast<D*>(this)->addDestination (_remote_ip, _remote_port)) { _debug ("Can't add destination to session!\n"); return; } } + + template <typename D> + void AudioRtpSession<D>::updateDestinationIpAddress(void) + { + // Destination address are stored in a list in ccrtp + // This method clear off this entry + static_cast<D*>(this)->forgetDestination(_remote_ip, _remote_port); + + // new destination is stored in call + // we just need to recall this method + setDestinationIpAddress(); + } template <typename D> int AudioRtpSession<D>::processDataEncode(void) diff --git a/sflphone-common/src/audio/audiortp/AudioSrtpSession.cpp b/sflphone-common/src/audio/audiortp/AudioSrtpSession.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4715587ff03b8e153f56aee346dd689468e0b440 --- /dev/null +++ b/sflphone-common/src/audio/audiortp/AudioSrtpSession.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com> + * Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "AudioSrtpSession.h" +#include "user_cfg.h" + +#include "sip/sipcall.h" + +#include <openssl/sha.h> +#include <openssl/hmac.h> +#include <openssl/evp.h> +#include <openssl/bio.h> +#include <openssl/buffer.h> +#include <openssl/rand.h> + + +#include <cstdio> +#include <cstring> +#include <cerrno> + +static uint8 mk[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; + +static uint8 ms[] = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d }; + + +namespace sfl +{ + +AudioSrtpSession::AudioSrtpSession (ManagerImpl * manager, SIPCall * sipcall) : + ost::SymmetricRTPSession (ost::InetHostAddress (sipcall->getLocalIp().c_str()), sipcall->getLocalAudioPort()), + _localCryptoSuite(0), + _remoteCryptoSuite(0), + AudioRtpSession<AudioSrtpSession> (manager, sipcall) +{ + + // Initialize local Crypto context + initializeLocalMasterKey(); + initializeLocalMasterSalt(); + initializeLocalCryptoContext(); + + // Set local crypto context in ccrtp + _localCryptoCtx->deriveSrtpKeys(0); + + setOutQueueCryptoContext(_localCryptoCtx); +} + + +std::string AudioSrtpSession::getLocalCryptoInfo() { + + _debug("Get Cryptographic info from this rtp session"); + + // @TODO we should return a vector containing supported + // cryptographic context tagged 1, 2, 3... + std::string tag = "1"; + + std::string crypto_suite = sfl::CryptoSuites[_localCryptoSuite].name; + + // srtp keys formated as the following as the following + // inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 + std::string srtp_keys = "inline:"; + srtp_keys += getBase64ConcatenatedKeys(); + srtp_keys.append("|2^20|1:32"); + + // generate crypto attribute + std::string crypto = tag.append(" "); + crypto += crypto_suite.append(" "); + crypto += srtp_keys; + + _debug("%s", crypto.c_str()); + + return crypto; +} + + +void AudioSrtpSession::setRemoteCryptoInfo(sfl::SdesNegotiator& nego) { + + _debug("Set remote Cryptographic info for Srtp session"); + + // decode keys + unBase64ConcatenatedKeys(nego.getKeyInfo()); + + // init crypto content int Srtp session + initializeRemoteCryptoContext(); + setInQueueCryptoContext(_remoteCryptoCtx); +} + + +void AudioSrtpSession::initializeLocalMasterKey(void) +{ + + // @TODO key may have different length depending on cipher suite + _localMasterKeyLength = sfl::CryptoSuites[_localCryptoSuite].masterKeyLength / 8; + + // Allocate memory for key + unsigned char *random_key = new unsigned char[_localMasterKeyLength]; + + // Generate ryptographically strong pseudo-random bytes + int err; + if((err = RAND_bytes(random_key, _localMasterKeyLength)) != 1) + _debug("Error occured while generating cryptographically strong pseudo-random key"); + + memcpy(_localMasterKey, random_key, _localMasterKeyLength); + + printf("Local Master: "); + for(int i = 0; i < _localMasterKeyLength; i++){ + printf("%d", _localMasterKey[i]); + } + printf("\n"); + + return; +} + + +void AudioSrtpSession::initializeLocalMasterSalt(void) +{ + + // @TODO key may have different length depending on cipher suite + _localMasterSaltLength = sfl::CryptoSuites[_localCryptoSuite].masterSaltLength / 8; + + // Allocate memory for key + unsigned char *random_key = new unsigned char[_localMasterSaltLength]; + + // Generate ryptographically strong pseudo-random bytes + int err; + if((err = RAND_bytes(random_key, _localMasterSaltLength)) != 1) + _debug("Error occured while generating cryptographically strong pseudo-random key"); + + memcpy(_localMasterSalt, random_key, _localMasterSaltLength); + + return; + +} + + +std::string AudioSrtpSession::getBase64ConcatenatedKeys() +{ + + // compute concatenated master and salt length + int concatLength = _localMasterKeyLength + _localMasterSaltLength; + + uint8 concatKeys[concatLength]; + + // concatenate keys + memcpy((void*)concatKeys, (void*)_localMasterKey, _localMasterKeyLength); + memcpy((void*)(concatKeys + _localMasterKeyLength), (void*)_localMasterSalt, _localMasterSaltLength); + + // encode concatenated keys in base64 + char *output = encodeBase64((unsigned char*)concatKeys, concatLength); + + // init string containing encoded data + std::string keys(output); + + free(output); + + return keys; +} + + +void AudioSrtpSession::unBase64ConcatenatedKeys(std::string base64keys) +{ + + + _remoteMasterKeyLength = sfl::CryptoSuites[1].masterKeyLength / 8; + _remoteMasterSaltLength = sfl::CryptoSuites[1].masterSaltLength / 8; + + // length of decoded data data + int length; + + // pointer to binary data + char *dataptr = (char*)base64keys.data(); + + // decode concatenated binary keys + char *output = decodeBase64((unsigned char*)dataptr, strlen(dataptr), &length); + + // copy master and slt respectively + memcpy((void*)_remoteMasterKey, (void*)output, _remoteMasterKeyLength); + memcpy((void*)_remoteMasterSalt, (void*)(output + _remoteMasterKeyLength), _remoteMasterSaltLength); + + free(output); +} + + +void AudioSrtpSession::initializeRemoteCryptoContext(void) +{ + + _remoteCryptoCtx = new ost::CryptoContext(0x0, + 0, // roc, + 0L, // keydr, + SrtpEncryptionAESCM, // encryption algo + SrtpAuthenticationSha1Hmac, // authtication algo + _remoteMasterKey, // Master Key + _remoteMasterKeyLength, // Master Key length + _remoteMasterSalt, // Master Salt + _remoteMasterSaltLength, // Master Salt length + 128 / 8, // encryption keyl + 160 / 8, // authentication key len + 112 / 8, // session salt len + 80 / 8); // authentication tag len + + +} + +void AudioSrtpSession::initializeLocalCryptoContext(void) +{ + + _localCryptoCtx = new ost::CryptoContext(OutgoingDataQueue::getLocalSSRC(), + 0, // roc, + 0L, // keydr, + SrtpEncryptionAESCM, // encryption algo + SrtpAuthenticationSha1Hmac, // authtication algo + _localMasterKey, // Master Key + _localMasterKeyLength, // Master Key length + _localMasterSalt, // Master Salt + _localMasterSaltLength, // Master Salt length + 128 / 8, // encryption keyl + 160 / 8, // authentication key len + 112 / 8, // session salt len + 80 / 8); // authentication tag len + + +} + + +char* AudioSrtpSession::encodeBase64(unsigned char *input, int length) +{ + BIO *b64, *bmem; + BUF_MEM *bptr ; + + char *buffer = (char *)malloc(2*length); + memset(buffer, 0, 2*length); + + // init decoder + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + + // init internal buffer + bmem = BIO_new(BIO_s_mem()); + + // create decoder chain + b64 = BIO_push(b64, bmem); + + BIO_write(b64, input, length); + BIO_flush(b64); + + // get pointer to data + BIO_get_mem_ptr(b64, &bptr); + + // copy result in output buffer (-1 since we do not want the EOF character) + strncpy(buffer, (char*)(bptr->data), bptr->length); + + BIO_free_all(bmem); + + return buffer; +} + +char* AudioSrtpSession::decodeBase64(unsigned char *input, int length, int *length_out) +{ + BIO *b64, *bmem; + + char *buffer = (char *)malloc(length); + memset(buffer, 0, length); + + // init decoder and read-only BIO buffer + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + + // init internal buffer + bmem = BIO_new_mem_buf(input, length); + + // create encoder chain + bmem = BIO_push(b64, bmem); + + *length_out = BIO_read(bmem, buffer, length); + + BIO_free_all(bmem); + + return buffer; + +} + +} diff --git a/sflphone-common/src/audio/audiortp/AudioSrtpSession.h b/sflphone-common/src/audio/audiortp/AudioSrtpSession.h new file mode 100644 index 0000000000000000000000000000000000000000..7e94a62baedf636d46dc21f8ee47513ec54e9aa8 --- /dev/null +++ b/sflphone-common/src/audio/audiortp/AudioSrtpSession.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com> + * Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __SFL_AUDIO_SRTP_SESSION_H__ +#define __SFL_AUDIO_SRTP_SESSION_H__ + +#include "AudioRtpSession.h" +#include "sip/SdesNegotiator.h" + +#include <ccrtp/CryptoContext.h> + +class SdesNegotiator; +class ManagerImpl; +class SIPCall; + +/* + Table from RFC 4568 6.2. Crypto-Suites, which define key parameters for supported + cipher suite + + +---------------------+-------------+--------------+---------------+ + | |AES_CM_128_ | AES_CM_128_ | F8_128_ | + | |HMAC_SHA1_80 | HMAC_SHA1_32 | HMAC_SHA1_80 | + +---------------------+-------------+--------------+---------------+ + | Master key length | 128 bits | 128 bits | 128 bits | + | Master salt length | 112 bits | 112 bits | 112 bits | + | SRTP lifetime | 2^48 packets| 2^48 packets | 2^48 packets | + | SRTCP lifetime | 2^31 packets| 2^31 packets | 2^31 packets | + | Cipher | AES Counter | AES Counter | AES F8 Mode | + | | Mode | Mode | | + | Encryption key | 128 bits | 128 bits | 128 bits | + | MAC | HMAC-SHA1 | HMAC-SHA1 | HMAC-SHA1 | + | SRTP auth. tag | 80 bits | 32 bits | 80 bits | + | SRTCP auth. tag | 80 bits | 80 bits | 80 bits | + | SRTP auth. key len. | 160 bits | 160 bits | 160 bits | + | SRTCP auth. key len.| 160 bits | 160 bits | 160 bits | + +---------------------+-------------+--------------+---------------+ +*/ + + +namespace sfl { + + class SrtpException: public std::exception + { + virtual const char* what() const throw() + { + return "ZRTP ZID initialization failed."; + } + }; + + class AudioSrtpSession : public ost::SymmetricRTPSession, public AudioRtpSession<AudioSrtpSession> + { + public: + + AudioSrtpSession(ManagerImpl * manager, SIPCall * sipcall); + + std::string getLocalCryptoInfo(void); + + void setRemoteCryptoInfo(sfl::SdesNegotiator& nego); + + private: + + void initializeLocalMasterKey(void); + + void initializeLocalMasterSalt(void); + + void initializeRemoteCryptoContext(void); + + void initializeLocalCryptoContext(void); + + std::string getBase64ConcatenatedKeys(); + + void unBase64ConcatenatedKeys(std::string base64keys); + + char* encodeBase64(unsigned char *input, int length); + + char* decodeBase64(unsigned char *input, int length, int *length_out); + + /** Default local crypto suite is AES_CM_128_HMAC_SHA1_80*/ + int _localCryptoSuite; + + /** Remote crypto suite is initialized at AES_CM_128_HMAC_SHA1_80*/ + int _remoteCryptoSuite; + + uint8 _localMasterKey[16]; + + /** local master key length in byte */ + int _localMasterKeyLength; + + uint8 _localMasterSalt[14]; + + /** local master salt length in byte */ + int _localMasterSaltLength; + + uint8 _remoteMasterKey[16]; + + /** remote master key length in byte */ + int _remoteMasterKeyLength; + + uint8 _remoteMasterSalt[14]; + + /** remote master salt length in byte */ + int _remoteMasterSaltLength; + + ost::CryptoContext* _remoteCryptoCtx; + + ost::CryptoContext* _localCryptoCtx; + }; + +} + +#endif // __AUDIO_SRTP_SESSION_H__ diff --git a/sflphone-common/src/audio/audiortp/Makefile.am b/sflphone-common/src/audio/audiortp/Makefile.am index 502a17b9002aa0e0799d5219962d46cb05741f0c..d2ab3aaec714d9065241752748284fe4ea59a0c2 100644 --- a/sflphone-common/src/audio/audiortp/Makefile.am +++ b/sflphone-common/src/audio/audiortp/Makefile.am @@ -5,12 +5,14 @@ noinst_LTLIBRARIES = libaudiortp.la libaudiortp_la_SOURCES = \ AudioRtpFactory.cpp \ AudioZrtpSession.cpp \ - ZrtpSessionCallback.cpp + ZrtpSessionCallback.cpp \ + AudioSrtpSession.cpp noinst_HEADERS = \ AudioRtpFactory.h \ AudioRtpSession.h \ AudioSymmetricRtpSession.h \ AudioZrtpSession.h \ - ZrtpSessionCallback.h + ZrtpSessionCallback.h \ + AudioSrtpSession.h diff --git a/sflphone-common/src/hooks/urlhook.cpp b/sflphone-common/src/hooks/urlhook.cpp index b50aa1febbf33ecaefc5d681f0d7330ffda035d8..3538602fb773a926d74162f49e9b8e6ff8942fe5 100644 --- a/sflphone-common/src/hooks/urlhook.cpp +++ b/sflphone-common/src/hooks/urlhook.cpp @@ -19,6 +19,7 @@ #include "urlhook.h" #include <iostream> +#include <vector> UrlHook::UrlHook () { } @@ -28,9 +29,33 @@ int UrlHook::addAction (std::string field_value, std::string command) { std::string command_bg; + std::string temp; + std::vector <std::string> args; + size_t pos; + unsigned int i; + + /* Escape the '&' char to not discard $_GET parameters in the URL - #2659 */ + while ( (pos = field_value.find ("&", 0)) != std::string::npos) { + temp = field_value.substr (0, pos); + field_value.erase (0, pos + 1); + args.push_back (temp); + std::cout << temp << " " << std::endl; + } + + command_bg = command + " "; + + pos = args.size (); + for (i=0; i<pos; i++) { + // Escape the "&" + command_bg += args[i] + "\\&"; + } + + // Retrieve the last argument + command_bg += field_value; /* Execute the command in the background to not block the application */ - command_bg = command + " " + field_value + "&" ; + command_bg += "&"; + /* Execute a system call */ return RUN_COMMAND (command_bg.c_str()); diff --git a/sflphone-common/src/managerimpl.cpp b/sflphone-common/src/managerimpl.cpp index 5fb236892e964303f8d13c537e3081d50662bd6d..1c87bb03c2dbaea8ae62578450df48cca7a4d6b6 100644 --- a/sflphone-common/src/managerimpl.cpp +++ b/sflphone-common/src/managerimpl.cpp @@ -3887,6 +3887,7 @@ ManagerImpl::addAccount (const std::map< std::string, std::string >& details) if (accountType == "SIP") { newAccount = AccountCreator::createAccount (AccountCreator::SIP_ACCOUNT, newAccountID); + newAccount->setVoIPLink(); } else if (accountType == "IAX") { newAccount = AccountCreator::createAccount (AccountCreator::IAX_ACCOUNT, newAccountID); } else { diff --git a/sflphone-common/src/sip/Makefile.am b/sflphone-common/src/sip/Makefile.am index 778494b0b6874fdf3e723ee7838905830cfe2cbf..8cb4829edc3797aa8e107d2a2b8af0cdf87393ec 100644 --- a/sflphone-common/src/sip/Makefile.am +++ b/sflphone-common/src/sip/Makefile.am @@ -3,17 +3,23 @@ include $(top_srcdir)/globals.mak noinst_LTLIBRARIES = libsiplink.la libsiplink_la_SOURCES = \ + Pattern.cpp \ + SdesNegotiator.cpp \ sdp.cpp \ sdpmedia.cpp \ sipaccount.cpp \ sipcall.cpp \ - sipvoiplink.cpp + sipvoiplink.cpp noinst_HEADERS = \ + Pattern.h \ + SdesNegotiator.h \ sdp.h \ sdpmedia.h \ sipaccount.h \ sipcall.h \ sipvoiplink.h +libsiplink_la_CXXFLAGS = \ + @PCRE_LIBS@ \ No newline at end of file diff --git a/sflphone-common/src/util/Pattern.cpp b/sflphone-common/src/sip/Pattern.cpp similarity index 86% rename from sflphone-common/src/util/Pattern.cpp rename to sflphone-common/src/sip/Pattern.cpp index 95186c5d689692cc30c1cf78d00e06e5aa66ebdb..71e093d13c96384877e71ca9ff65d721313a7e79 100644 --- a/sflphone-common/src/util/Pattern.cpp +++ b/sflphone-common/src/sip/Pattern.cpp @@ -23,8 +23,7 @@ namespace sfl { -namespace util { -Pattern::Pattern (const std::string& pattern, const std::string& options = "") : +Pattern::Pattern (const std::string& pattern, const std::string& options) : _pattern (pattern), _ovector (NULL), _ovectorSize (0), @@ -32,6 +31,8 @@ Pattern::Pattern (const std::string& pattern, const std::string& options = "") : _count (0), _options (0) { + + // printf("Pattern constructor called for %s!\n", pattern.c_str()); // Set offsets _offset[0] = _offset[1] = 0; @@ -125,7 +126,7 @@ std::vector<std::string> Pattern::groups (void) while (stringList[i] != NULL) { matchedSubstrings.push_back (stringList[i]); - printf ("Substr: <start>%s<end>", stringList[i]); + // printf ("Substr: <start>%s<end>", stringList[i]); i++; } @@ -138,6 +139,8 @@ std::string Pattern::group (int groupNumber) { const char * stringPtr; + // printf("_subject.substr : %s\n", _subject.substr (_offset[0]).c_str()); + int rc = pcre_get_substring ( _subject.substr (_offset[0]).c_str(), _ovector, @@ -178,16 +181,22 @@ std::string Pattern::group (const std::string& groupName) groupName.c_str(), &stringPtr); + // printf(" _count : %i\n", _count); + // printf("stringPtr : %s\n", stringPtr); + if (rc < 0) { switch (rc) { case PCRE_ERROR_NOSUBSTRING: + // printf("Pattern::PCRE_ERROR_NOSUBSTRING\n"); throw std::out_of_range ("Invalid group reference."); case PCRE_ERROR_NOMEMORY: + // printf("Pattern::PCRE_ERROR_NOMEMORY\n"); throw match_error ("Memory exhausted."); default: + // printf("Pattern::default match error\n"); throw match_error ("Failed to get named substring."); } } @@ -241,13 +250,14 @@ size_t Pattern::end (void) const bool Pattern::matches (void) throw (match_error) { - matches (_subject); + return matches (_subject); } bool Pattern::matches (const std::string& subject) throw (match_error) { - //printf("Current offset: %d, old offset: %d", _offset[1], _offset[0]); - //printf("Trying <start>%s<end>", subject.substr(_offset[1]).c_str()); + // printf("Pattern::matches\n"); + // printf(" Current offset: %d, old offset: %d", _offset[1], _offset[0]); + // printf(" Trying <start>%s<end>\n", subject.substr(_offset[1]).c_str()); // Try to find a match for this pattern int rc = pcre_exec ( @@ -264,7 +274,7 @@ bool Pattern::matches (const std::string& subject) throw (match_error) if (rc < 0) { _offset[0] = _offset[1] = 0; - //printf("Matching failed with %d", rc); + // printf(" Matching failed with %d\n", rc); return false; } @@ -275,7 +285,7 @@ bool Pattern::matches (const std::string& subject) throw (match_error) _offset[1] = _ovector[1] + _offset[0]; } - //printf("Matching succeeded with %d to %d", (int) start(), (int) end()); + // printf(" Matching succeeded with %d to %d\n", (int) start(), (int) end()); // Matching succeded but not enough space. if (rc == 0) { @@ -285,7 +295,8 @@ bool Pattern::matches (const std::string& subject) throw (match_error) // Matching succeeded. Keep the number of substrings for // subsequent calls to group(). - _count = rc; + // printf("_count: %i = %i\n", _count, rc); + _count = rc; return true; } @@ -301,14 +312,15 @@ std::vector<std::string> Pattern::split (void) tokenStart = start(); substringSplitted.push_back (_subject.substr (tokenEnd + 1, tokenStart - tokenEnd - 1)); + // printf("split: %s\n", _subject.substr (tokenEnd + 1, + // tokenStart - tokenEnd - 1).c_str()); tokenEnd = end(); } - substringSplitted.push_back (_subject.substr (tokenEnd + 1, + substringSplitted.push_back (_subject.substr (tokenEnd + 1, tokenStart - tokenEnd - 1)); - tokenStart - tokenEnd - 1)); return substringSplitted; } } -} + diff --git a/sflphone-common/src/sip/Pattern.h b/sflphone-common/src/sip/Pattern.h new file mode 100644 index 0000000000000000000000000000000000000000..b07c92acddbfd4466710c83fb6ce7e40d38e1383 --- /dev/null +++ b/sflphone-common/src/sip/Pattern.h @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __SFL_PATTERN_H__ +#define __SFL_PATTERN_H__ + +#include <stdexcept> +#include <string> +#include <vector> +#include <pcre.h> + +namespace sfl { + + /** + * Exception object that is thrown when + * an error occured while compiling the + * regular expression. + */ + class compile_error : public std::invalid_argument + { + public: + explicit compile_error(const std::string& error) : + std::invalid_argument(error) {} + }; + + /** + * Exception object that is thrown when + * an error occured while mathing a + * pattern to an expression. + */ + class match_error : public std::invalid_argument + { + public: + match_error(const std::string& error) : + std::invalid_argument(error) {} + }; + + /** + * This class implements in its way + * some of the libpcre library. + */ + + class Pattern { + + public: + + /** + * Constructor for a regular expression + * pattern evaluator/matcher. + * + * @param pattern + * The regular expression to + * be used for this instance. + */ + + Pattern(const std::string& pattern, + const std::string& options = ""); + + /** + * Destructor. Pcre pattern gets freed + * here. + */ + ~Pattern(); + + /** + * Assignment operator overloading. + * Set the regular expression + * to be used on subject strings + * and compile the regular expression + * from that string. + * + * @param pattern The new pattern + */ + void operator=(const std::string& pattern) { + _pattern = pattern; + compile(); + } + + void operator=(const char * pattern) { + _pattern = pattern; + compile(); + } + + /** + * Compile the regular expression + * from the pattern that was set for + * this object. + */ + void compile(void); + + /** + * Get the currently set regular expression + * that is used on subject strings + * + * @return The currently set pattern + */ + inline std::string getPattern(void) { return _pattern; } + + /** + * << operator overload. Sets the the subject + * for latter use on the >> operator. + * + * @param subject + * The expression to be evaluated + * by the pattern. + * + */ + void operator<<(const std::string& subject) { + _subject = subject; + } + + /** + * Get the start position of the overall match. + * + * @return the start position of the overall match. + */ + size_t start(void) const; + + /** + * Get the start position of the specified match. + * + * @param groupNumber The capturing group number. + * + * @return the start position of the specified match. + */ + size_t start(unsigned int groupNumber) const; + + /** + * Get the start position of the specified match. + * + * @param groupName The capturing group name. + * + * @return the start position of the specified match. + */ + size_t start(const std::string& groupName) const; + + /** + * Get the end position of the overall match. + * + * @return the end position of the overall match. + */ + size_t end(void) const; + + /** + * Get the end position of the specified match. + * + * @param groupNumber The capturing group number. + * + * @return the end position of the specified match. + */ + size_t end(unsigned int groupNumber) const; + + /** + * Get the end position of the specified match. + * + * @param groupName The capturing group name. + * + * @return the end position of the specified match. + */ + size_t end(const std::string& groupName) const; + + /** + * Get the number of capturing groups in the + * compiled regex. + * + * @return The number of capture groups. + * + * @pre The regular expression should have been + * compiled prior to the execution of this method. + */ + unsigned int getCaptureGroupCount(void); + + /** + * Get the substring matched in a capturing + * group (named or unnamed). + * + * This methods only performs a basic lookup + * inside its internal substring table. Thus, + * matches() should have been called prior to + * this method in order to obtain the desired + * output. + * + * @param groupName The name of the group + * + * @return the substring matched by the + * regular expression designated + * the group name. + */ + std::string group(const std::string& groupName); + + /** + * Get the substring matched in a named group. + * + * This methods only performs a basic lookup + * inside its internal substring table. Thus, + * matches() should have been called prior to + * this method in order to obtain the desired + * output. + * + * @param groupNumber The number of the group. + * + * @return the substring matched by the + * regular expression designated + * the group number. + */ + std::string group(int groupNumber); + + /** + * Similar to python's MatchObject.groups. Get all + * the substrings matched by the capture groups defined + * in the pattern. The complete (implicit) capture group + * is not returned : ie only groups from 1 up to the number + * of groups in the pattern are returned. + * + * @return A vector of stings that were matched by some + * capturing group in the pattern. + * + * @pre The regular expression should have been + * compiled prior to the execution of this method. + */ + std::vector<std::string> groups(void); + + /** + * Try to match the compiled pattern with a + * subject. + * + * @param subject Subject to be matched + * by the pattern. + * + * @return true If the subject matches the pattern, + * false otherwise. + * + * @pre The regular expression should have been + * compiled prior to the execution of this method. + * + * @post The internal substring table will be updated + * with the new matches. Therefore, subsequent + * calls to group may return different results. + */ + bool matches(const std::string& subject) throw(match_error); + + /** + * Try to match the compiled pattern with the implicit + * subject. + * + * @return true If the subject matches the pattern, + * false otherwise. + * + * @pre The regular expression should have been + * compiled prior to the execution of this method. + * + * @post The internal substring table will be updated + * with the new matches. Therefore, subsequent + * calls to group may return different results. + */ + bool matches(void) throw(match_error); + + /** + * Split the subject into a list of substrings. + * + * @return A vector of substrings. + * + * @pre The regular expression should have been + * compiled prior to the execution of this method. + * + * @post The internal subject won't be affected by this + * by this operation. In other words: subject_before = + * subject_after. + */ + std::vector<std::string> split(void); // throw(match_error); + + private: + /** + * The regular expression that represents that pattern. + */ + std::string _pattern; + + /** + * The optional subject string. + */ + std::string _subject; + + /** + * PCRE struct that + * contains the compiled regular + * expression + */ + pcre * _re; + + /** + * The internal output vector used by PCRE. + */ + int * _ovector; + + /** + * The size of the _ovector + */ + int _ovectorSize; + + /** + * Current offset in the _ovector; + */ + + int _offset[2]; + + /** + * The number of substrings matched after calling + * pcre_exec. + */ + int _count; + + /** + * PCRE options for this pattern. + */ + int _options; + + /** + * String representation of the options. + */ + std::string _optionsDescription; + }; +} + + +#endif diff --git a/sflphone-common/src/sip/SdesNegotiator.cpp b/sflphone-common/src/sip/SdesNegotiator.cpp index 74442abafee1aa5ca51b69cfec6e85c31f308b73..e8618e3ab8d80f0bfb9b13fc5205cda5c847c869 100644 --- a/sflphone-common/src/sip/SdesNegotiator.cpp +++ b/sflphone-common/src/sip/SdesNegotiator.cpp @@ -18,14 +18,15 @@ #include "SdesNegotiator.h" -#include "util/Pattern.h" +#include "Pattern.h" +#include <cstdio> #include <iostream> #include <sstream> #include <algorithm> #include <stdexcept> -using namespace sfl::util; +using namespace sfl; struct CryptoAttribute { std::string tag; @@ -58,9 +59,12 @@ void SdesNegotiator::parse (void) * sessionParamPattern; try { + + // used to match white space (which are used as separator) generalSyntaxPattern = new Pattern ("[\x20\x09]+", "g"); - tagPattern = new Pattern ("^a=crypto:(?P<tag>[0-9]{1,9})"); + tagPattern = new Pattern ("^a=crypto:(?P<tag>[0-9]{1,9})", "g"); + // tagPattern = new Pattern ("[0-9]"); cryptoSuitePattern = new Pattern ( "(?P<cryptoSuite>AES_CM_128_HMAC_SHA1_80|" \ @@ -81,7 +85,7 @@ void SdesNegotiator::parse (void) "UNENCRYPTED_SRTCP|" \ "UNAUTHENTICATED_SRTP|" \ "FEC_ORDER=(?P<fecOrder>FEC_SRTP|SRTP_FEC)|" \ - "FEC_KEY=(?P<fecKey>" + keyParamsPattern.getPattern() + ")|" \ + "FEC_KEY=(?P<fecKey>" + keyParamsPattern->getPattern() + ")|" \ "WSH=(?P<wsh>[0-9]{1,2})|" \ "(?<!\\-)[[:graph:]]+))*", "g"); // srtp-session-extension @@ -89,10 +93,12 @@ void SdesNegotiator::parse (void) throw parse_error ("A compile exception occured on a pattern."); } + // Take each line from the vector // and parse its content + std::vector<std::string>::iterator iter; for (iter = _remoteAttribute.begin(); iter != _remoteAttribute.end(); iter++) { @@ -101,12 +107,13 @@ void SdesNegotiator::parse (void) // Split the line into its component // that we will analyze further down. - - generalSyntaxPattern << (*iter); - std::vector<std::string> sdesLine; - + std::vector<std::string> sdesLine; + + *generalSyntaxPattern << (*iter); + + try { - sdesLine = generalSyntaxPattern.split(); + sdesLine = generalSyntaxPattern->split(); if (sdesLine.size() < 3) { throw parse_error ("Missing components in SDES line"); @@ -114,13 +121,16 @@ void SdesNegotiator::parse (void) } catch (match_error& exception) { throw parse_error ("Error while analyzing the SDES line."); } + // Check if the attribute starts with a=crypto // and get the tag for this line - tagPattern << sdesLine.at (0); + *tagPattern << sdesLine.at (0); + + tagPattern->matches(); try { - std::string tag = tagPattern.group ("tag"); + std::string tag = tagPattern->group ("tag"); std::cout << "tag = " << tag << std::endl; } catch (match_error& exception) { throw parse_error ("Error while parsing the tag field"); @@ -128,40 +138,37 @@ void SdesNegotiator::parse (void) // Check if the crypto suite is valid and retreive // its value. - cryptoSuitePattern << sdesLine.at (1); + *cryptoSuitePattern << sdesLine.at (1); + + cryptoSuitePattern->matches(); try { - std::string cryptoSuite - cryptoSuite = cryptoSuitePattern.group ("cryptoSuite"); - std::cout << "crypto-suite = " << cryptoSuite << std::endl; + _cryptoSuite = cryptoSuitePattern->group ("cryptoSuite"); + std::cout << "crypto-suite = " << _cryptoSuite << std::endl; } catch (match_error& exception) { throw parse_error ("Error while parsing the crypto-suite field"); } // Parse one or more key-params field. - keyParamsPattern << sdesLine.at (2); + *keyParamsPattern << sdesLine.at (2); try { - while (keyParamsPattern.matches()) { - std::string srtpKeyMethod; - srtpKeyMethod = keyParamsMatched.group ("srtpKeyMethod"); - std::cout << "srtp-key-method = " << srtpKeyMethod << std::endl; - - std::string srtpKeyInfo; - srtpKeyInfo = keyParamsPattern.group ("srtpKeyInfo"); - std::cout << "srtp-key-info = " << srtpKeyInfo << std::endl; - - std::string lifetime; - lifetime = keyParamsPattern.group ("lifetime"); - std::cout << "lifetime = " << lifetime << std::endl; - - std::string mkiValue - mkiValue = keyParamsPattern.group ("mkiValue"); - std::cout << "mkiValue = " << mkiValue << std::endl; - - std::string mkiLength; - mkiLength = keyParamsPattern.group ("mkiLength"); - std::cout << "mkiLength = " << mkiLength << std::endl; + while (keyParamsPattern->matches()) { + + _srtpKeyMethod = keyParamsPattern->group ("srtpKeyMethod"); + std::cout << "srtp-key-method = " << _srtpKeyMethod << std::endl; + + _srtpKeyInfo = keyParamsPattern->group ("srtpKeyInfo"); + std::cout << "srtp-key-info = " << _srtpKeyInfo << std::endl; + + _lifetime = keyParamsPattern->group ("lifetime"); + std::cout << "lifetime = " << _lifetime << std::endl; + + _mkiValue = keyParamsPattern->group ("mkiValue"); + std::cout << "mkiValue = " << _mkiValue << std::endl; + + _mkiLength = keyParamsPattern->group ("mkiLength"); + std::cout << "mkiLength = " << _mkiLength << std::endl; } } catch (match_error& exception) { throw parse_error ("Error while parsing the key-params field"); @@ -192,4 +199,6 @@ void SdesNegotiator::parse (void) bool SdesNegotiator::negotiate (void) { parse(); + + return true; } diff --git a/sflphone-common/src/sip/SdesNegotiator.h b/sflphone-common/src/sip/SdesNegotiator.h index 2dded5b1a7582002646dc07451ca8e8bb40fb107..08e32090ef5e64b5731f2fab2ace701135414fc0 100644 --- a/sflphone-common/src/sip/SdesNegotiator.h +++ b/sflphone-common/src/sip/SdesNegotiator.h @@ -97,9 +97,38 @@ namespace sfl { SdesNegotiator(const std::vector<CryptoSuiteDefinition>& localCapabilites, const std::vector<std::string>& remoteAttribute); ~SdesNegotiator() { }; - public: bool negotiate(void); + /** + * Return crypto suite after negotiation + */ + std::string getCryptoSuite(void) { return _cryptoSuite; } + + /** + * Return key method after negotiation (most likely inline:) + */ + std::string getKeyMethod(void) { return _srtpKeyMethod; } + + /** + * Return crypto suite after negotiation + */ + std::string getKeyInfo(void) { return _srtpKeyInfo; } + + /** + * Return key lifetime after negotiation + */ + std::string getLifeTime(void) { return _lifetime; } + + /** + * Return mki value after negotiation + */ + std::string getMkiValue(void) { return _mkiValue; } + + /** + * Return mki length after negotiation + */ + std::string getMkiLength(void) { return _mkiLength; } + private: /** * A vector list containing the remote attributes. @@ -107,12 +136,41 @@ namespace sfl { * prefered method is then chosen from that list. */ std::vector<std::string> _remoteAttribute; + std::vector<CryptoSuiteDefinition> _localCapabilities; - + /** + * Selected crypto suite after negotiation + */ + std::string _cryptoSuite; - private: + /** + * Selected key method after negotiation (most likely inline:) + */ + std::string _srtpKeyMethod; + + /** + * Selected crypto suite after negotiation + */ + std::string _srtpKeyInfo; + + /** + * Selected key lifetime after negotiation + */ + std::string _lifetime; + + /** + * Selected mki value after negotiation + */ + std::string _mkiValue; + + /** + * Selected mki length after negotiation + */ + std::string _mkiLength; + void parse(void); + CryptoAttribute * tokenize(const std::string& attributeLine); }; } diff --git a/sflphone-common/src/sip/sdp.cpp b/sflphone-common/src/sip/sdp.cpp index 1d60ce41f79f1d80736263d4af75f3455c996419..a18d82fb4709fff054fffa3abc7025658f3eb98f 100644 --- a/sflphone-common/src/sip/sdp.cpp +++ b/sflphone-common/src/sip/sdp.cpp @@ -32,6 +32,7 @@ static const pj_str_t STR_RTP_AVP = { (char*) "RTP/AVP", 7 }; static const pj_str_t STR_SDP_NAME = { (char*) "sflphone", 8 }; static const pj_str_t STR_SENDRECV = { (char*) "sendrecv", 8 }; static const pj_str_t STR_RTPMAP = { (char*) "rtpmap", 6 }; +static const pj_str_t STR_CRYPTO = { (char*) "crypto", 6 }; Sdp::Sdp (pj_pool_t *pool) @@ -115,8 +116,6 @@ void Sdp::set_media_descriptor_line (sdpMedia *media, pjmedia_sdp_media** p_med) _warn ("No hash specified"); } - sdp_add_sdes_attribute (med); - *p_med = med; } @@ -143,6 +142,12 @@ int Sdp::create_local_offer (CodecOrder selectedCodecs) { sdp_add_timing(); sdp_add_media_description(); + if(!_srtp_crypto.empty()) { + sdp_add_sdes_attribute(_srtp_crypto); + } + + //toString (); + // Validate the sdp session status = pjmedia_sdp_validate (this->_local_offer); @@ -190,8 +195,6 @@ int Sdp::receiving_initial_offer (pjmedia_sdp_session* remote, CodecOrder select pj_status_t status; - _debug ("Receiving initial offer"); - // Create the SDP negociator instance by calling // pjmedia_sdp_neg_create_w_remote_offer with the remote offer, and by providing the local offer ( optional ) @@ -346,31 +349,29 @@ void Sdp::sdp_add_media_description() } -void Sdp::sdp_add_sdes_attribute (pjmedia_sdp_media* media) +void Sdp::sdp_add_sdes_attribute (std::string crypto) { + // temporary buffer used to store crypto attribute char tempbuf[256]; - std::string tag = "1"; - std::string crypto_suite = "AES_CM_128_HMAC_SHA1_32"; - std::string application = "srtp"; - std::string key = "inline:16/14/NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj/2^20/1:32"; - + // the attribute to add to sdp pjmedia_sdp_attr *attribute = (pjmedia_sdp_attr*) pj_pool_zalloc(_pool, sizeof(pjmedia_sdp_attr)); attribute->name = pj_strdup3(_pool, "crypto"); - int len = pj_ansi_snprintf(tempbuf, sizeof(tempbuf), - "%.*s %.*s %.*s", - (int)tag.size(), tag.c_str(), - (int)crypto_suite.size(), crypto_suite.c_str(), - (int)key.size(), key.c_str()); + // _debug("crypto from sdp: %s", crypto.c_str()); + + int len = pj_ansi_snprintf(tempbuf, sizeof(tempbuf), + "%.*s",(int)crypto.size(), crypto.c_str()); + attribute->value.slen = len; attribute->value.ptr = (char*) pj_pool_alloc (_pool, attribute->value.slen+1); pj_memcpy (attribute->value.ptr, tempbuf, attribute->value.slen+1); - if(pjmedia_sdp_media_add_attr(media, attribute) != PJ_SUCCESS) { + // add crypto attribute to sdp session + if(pjmedia_sdp_attr_add(&(_local_offer->attr_count), _local_offer->attr, attribute) != PJ_SUCCESS){ throw sdpException(); } @@ -622,12 +623,8 @@ void Sdp::set_media_transport_info_from_remote_sdp (const pjmedia_sdp_session *r pjmedia_sdp_media *r_media; - pjmedia_sdp_attr *attribute; - this->get_remote_sdp_media_from_offer (remote_sdp, &r_media); - // this->get_remote_sdp_crypto_from_offer() - if (r_media==NULL) { _debug ("SDP Failure: no remote sdp media found in the remote offer"); return; @@ -636,6 +633,7 @@ void Sdp::set_media_transport_info_from_remote_sdp (const pjmedia_sdp_session *r this->set_remote_audio_port_from_sdp (r_media); this->set_remote_ip_from_sdp (remote_sdp); + } void Sdp::get_remote_sdp_media_from_offer (const pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media** r_media) @@ -653,18 +651,45 @@ void Sdp::get_remote_sdp_media_from_offer (const pjmedia_sdp_session* remote_sdp } } -void Sdp::get_remote_sdp_crypto_from_offer (const pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media** r_crypto) +void Sdp::get_remote_sdp_crypto_from_offer (const pjmedia_sdp_session* remote_sdp, CryptoOffer& crypto_offer) { - int count, i; - count = remote_sdp->media_count; - *r_crypto = NULL; + int i; + int attr_count; + pjmedia_sdp_attr *attribute; - for (i = 0; i < count; ++i) { - if (pj_stricmp2 (&remote_sdp->media[i]->desc.media, "crypto") == 0) { - *r_crypto = remote_sdp->media[i]; - return; + // get the number of attribute for this sdp session + attr_count = remote_sdp->attr_count; + + // *r_crypto= pjmedia_sdp_media_find_attr(attribute, &STR_CRYPTO, NULL); + + _debug("****************** Parse for Crypto %i ********************", attr_count); + + CryptoOffer remoteOffer; + + // iterate over all atribute + for (i = 0; i < attr_count; ++i) { + + _debug("%.*s", (int)remote_sdp->attr[i]->name.slen, remote_sdp->attr[i]->name.ptr); + _debug("%.*s", (int)remote_sdp->attr[i]->value.slen, remote_sdp->attr[i]->value.ptr); + + // test if this attribute is a crypto + if (pj_stricmp2 (&remote_sdp->attr[i]->name, "crypto") == 0) { + + attribute = remote_sdp->attr[i]; + + _debug("****************** Found a Crypto ********************"); + std::string attr(attribute->value.ptr, attribute->value.slen); + + // @TODO our parser require the "acrypto:" to be present + std::string full_attr = "a=crypto:"; + full_attr += attr; + + crypto_offer.push_back(full_attr); } } + + _debug("****************** Did not Found any Crypto ********************"); + } diff --git a/sflphone-common/src/sip/sdp.h b/sflphone-common/src/sip/sdp.h index 7d10c4722bf5547fa0a42b184ca4b3e2d32da64a..1e0a40db2b5ac74ac074f36e4303a4366d485c16 100644 --- a/sflphone-common/src/sip/sdp.h +++ b/sflphone-common/src/sip/sdp.h @@ -29,6 +29,7 @@ #include <pjmedia/errno.h> #include <pj/pool.h> #include <pj/assert.h> +#include <vector> #include "audio/codecs/codecDescriptor.h" #include "sdpmedia.h" @@ -43,6 +44,8 @@ class sdpException: public std::exception } }; +typedef std::vector<std::string> CryptoOffer; + class Sdp { public: @@ -101,6 +104,11 @@ class Sdp { * @param hash The hello hash of a rtp session. (Only audio at the moment) */ inline void set_zrtp_hash(const std::string& hash) { _zrtp_hello_hash = hash; _debug("Zrtp hash set with %s\n", hash.c_str()); } + + /* Set the srtp _master_key + * @param mk The Master Key of a srtp session. + */ + inline void set_srtp_crypto(const std::string& mk) { _srtp_crypto = mk; } /* * On building an invite outside a dialog, build the local offer and create the @@ -213,6 +221,8 @@ class Sdp { std::vector<sdpMedia*> get_session_media_list (void) { return _session_media; } + void get_remote_sdp_crypto_from_offer (const pjmedia_sdp_session* remote_sdp, CryptoOffer& crypto_offer); + private: /** Codec Map */ std::vector<sdpMedia*> _local_media_cap; @@ -246,7 +256,10 @@ class Sdp { /** Remote's audio port */ unsigned int _remote_audio_port; - std::string _zrtp_hello_hash; + std::string _zrtp_hello_hash; + + /** "a=crypto" sdes attribute obtained from AudioSrtpSession */ + std::string _srtp_crypto; Sdp(const Sdp&); //No Copy Constructor Sdp& operator=(const Sdp&); //No Assignment Operator @@ -336,15 +349,13 @@ class Sdp { void get_remote_sdp_media_from_offer (const pjmedia_sdp_session* r_sdp, pjmedia_sdp_media** r_media); - void get_remote_sdp_crypto_from_offer (const pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media** r_crypto); - /* * Adds a sdes attribute to the given media section. * * @param media The media to add the srtp attribute to */ - void sdp_add_sdes_attribute(pjmedia_sdp_media* media); + void sdp_add_sdes_attribute(std::string crypto); /* * Adds a zrtp-hash attribute to diff --git a/sflphone-common/src/sip/sipvoiplink.cpp b/sflphone-common/src/sip/sipvoiplink.cpp index 5d78a1f2ee5be34bb6812238c7b5d38ea9dc561f..ae7826fdf947f5905df37d36d0a09d987c96e4e3 100644 --- a/sflphone-common/src/sip/sipvoiplink.cpp +++ b/sflphone-common/src/sip/sipvoiplink.cpp @@ -28,6 +28,7 @@ #include "sipcall.h" #include "sipaccount.h" #include "eventthread.h" +#include "SdesNegotiator.h" #include "dbus/dbusmanager.h" #include "dbus/callmanager.h" @@ -807,11 +808,13 @@ SIPVoIPLink::answer (const CallID& id) local_sdp = call->getLocalSDP(); + /* try { call->getAudioRtp()->initAudioRtpSession (call); } catch (...) { _debug ("Failed to create rtp thread from answer"); } + */ inv_session = call->getInvSession(); @@ -1559,7 +1562,6 @@ bool SIPVoIPLink::new_ip_to_ip_call (const CallID& id, const std::string& to) setCallAudioLocal (call, localAddress); _debug ("toUri received in new_ip_to_ip call %s", to.c_str()); - std::string toUri = account->getToUri (to); call->setPeerNumber (toUri); _debug ("toUri in new_ip_to_ip call %s", toUri.c_str()); @@ -1567,12 +1569,18 @@ bool SIPVoIPLink::new_ip_to_ip_call (const CallID& id, const std::string& to) call->getLocalSDP()->set_ip_address (addrSdp); call->getLocalSDP()->create_initial_offer (account->getActiveCodecs ()); - try { + // Audio Rtp Session must be initialized before creating initial offer in SDP session + // since SDES require crypto attribute. + try { call->getAudioRtp()->initAudioRtpSession (call); } catch (...) { _debug ("! SIP Failure: Unable to create RTP Session in SIPVoIPLink::new_ip_to_ip_call (%s:%d)", __FILE__, __LINE__); } + // Building the local SDP offer + call->getLocalSDP()->set_ip_address (addrSdp); + call->getLocalSDP()->create_initial_offer(); + // If no account already set, use the default one created at pjsip initialization if (account->getAccountTransport() == NULL) { _debug ("No transport for this account, using the default one"); @@ -2948,6 +2956,7 @@ void set_voicemail_info (AccountID account, pjsip_msg_body *body) void SIPVoIPLink::handle_reinvite (SIPCall *call) { + /* // Close the previous RTP session call->getAudioRtp()->stop (); call->setAudioStart (false); @@ -2959,6 +2968,12 @@ void SIPVoIPLink::handle_reinvite (SIPCall *call) } catch (...) { _debug ("! SIP Failure: Unable to create RTP Session (%s:%d)", __FILE__, __LINE__); } + */ + _debug("******************************************"); + _debug("* handle_reinvite *"); + _debug("******************************************"); + + call->getAudioRtp()->updateDestinationIpAddress(); } // This callback is called when the invite session state has changed @@ -3210,6 +3225,30 @@ void call_on_media_update (pjsip_inv_session *inv, pj_status_t status) // Set remote ip / port call->getLocalSDP()->set_media_transport_info_from_remote_sdp (remote_sdp); + // Get the crypto attribute containing srtp's cryptographic context (keys, cipher) + CryptoOffer crypto_offer; + call->getLocalSDP()->get_remote_sdp_crypto_from_offer(remote_sdp, crypto_offer); + + if(!crypto_offer.empty()) { + + _debug("Crypto attribute in SDP: init Srtp session"); + + // init local cryptografic capabilities for negotiation + std::vector<sfl::CryptoSuiteDefinition>localCapabilities; + for(int i = 0; i < 3; i++) { + localCapabilities.push_back(sfl::CryptoSuites[i]); + } + + sfl::SdesNegotiator sdesnego(localCapabilities, crypto_offer); + + if(sdesnego.negotiate()) { + _debug("SDES negociation successfull \n"); + + call->getAudioRtp()->setRemoteCryptoInfo(sdesnego); + } + + } + try { call->setAudioStart (true); call->getAudioRtp()->start(); @@ -3592,6 +3631,12 @@ mod_on_rx_request (pjsip_rx_data *rdata) // We retrieve the remote sdp offer in the rdata struct to begin the negociation call->getLocalSDP()->set_ip_address (addrSdp); + try { + call->getAudioRtp()->initAudioRtpSession (call); + } catch (...) { + _debug ("Failed to create rtp thread from answer"); + } + get_remote_sdp_from_offer (rdata, &r_sdp); status = call->getLocalSDP()->receiving_initial_offer (r_sdp, account->getActiveCodecs ()); diff --git a/sflphone-common/src/util/Pattern.h b/sflphone-common/src/util/Pattern.h deleted file mode 100644 index 676ecbd85ef966cdff2456b45ad03b3102aca18b..0000000000000000000000000000000000000000 --- a/sflphone-common/src/util/Pattern.h +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (C) 2009 Savoir-Faire Linux inc. - * Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#ifndef __SFL_PATTERN_H__ -#define __SFL_PATTERN_H__ - -#include <stdexcept> -#include <string> -#include <vector> -#include <pcre.h> - -namespace sfl { -namespace util { - /** - * Exception object that is thrown when - * an error occured while compiling the - * regular expression. - */ - class compile_error : public std::invalid_argument - { - public: - explicit compile_error(const std::string& error) : - std::invalid_argument(error) {} - }; - - /** - * Exception object that is thrown when - * an error occured while mathing a - * pattern to an expression. - */ - class match_error : public std::invalid_argument - { - public: - match_error(const std::string& error) : - std::invalid_argument(error) {} - }; - - /** - * This class implements in its way - * some of the libpcre library. - */ - - class Pattern { - - public: - - /** - * Constructor for a regular expression - * pattern evaluator/matcher. - * - * @param pattern - * The regular expression to - * be used for this instance. - */ - - Pattern(const std::string& pattern, - const std::string& options); - - /** - * Destructor. Pcre pattern gets freed - * here. - */ - ~Pattern(); - - /** - * Assignment operator overloading. - * Set the regular expression - * to be used on subject strings - * and compile the regular expression - * from that string. - * - * @param pattern The new pattern - */ - void operator=(const std::string& pattern) { - _pattern = pattern; - compile(); - } - - void operator=(const char * pattern) { - _pattern = pattern; - compile(); - } - - /** - * Compile the regular expression - * from the pattern that was set for - * this object. - */ - void compile(void); - - /** - * Get the currently set regular expression - * that is used on subject strings - * - * @return The currently set pattern - */ - inline std::string getPattern(void) { return _pattern; } - - /** - * << operator overload. Sets the the subject - * for latter use on the >> operator. - * - * @param subject - * The expression to be evaluated - * by the pattern. - * - */ - void operator<<(const std::string& subject) { - _subject = subject; - } - - /** - * Get the start position of the overall match. - * - * @return the start position of the overall match. - */ - size_t start(void) const; - - /** - * Get the start position of the specified match. - * - * @param groupNumber The capturing group number. - * - * @return the start position of the specified match. - */ - size_t start(unsigned int groupNumber) const; - - /** - * Get the start position of the specified match. - * - * @param groupName The capturing group name. - * - * @return the start position of the specified match. - */ - size_t start(const std::string& groupName) const; - - /** - * Get the end position of the overall match. - * - * @return the end position of the overall match. - */ - size_t end(void) const; - - /** - * Get the end position of the specified match. - * - * @param groupNumber The capturing group number. - * - * @return the end position of the specified match. - */ - size_t end(unsigned int groupNumber) const; - - /** - * Get the end position of the specified match. - * - * @param groupName The capturing group name. - * - * @return the end position of the specified match. - */ - size_t end(const std::string& groupName) const; - - /** - * Get the number of capturing groups in the - * compiled regex. - * - * @return The number of capture groups. - * - * @pre The regular expression should have been - * compiled prior to the execution of this method. - */ - unsigned int getCaptureGroupCount(void); - - /** - * Get the substring matched in a capturing - * group (named or unnamed). - * - * This methods only performs a basic lookup - * inside its internal substring table. Thus, - * matches() should have been called prior to - * this method in order to obtain the desired - * output. - * - * @param groupName The name of the group - * - * @return the substring matched by the - * regular expression designated - * the group name. - */ - std::string group(const std::string& groupName); - - /** - * Get the substring matched in a named group. - * - * This methods only performs a basic lookup - * inside its internal substring table. Thus, - * matches() should have been called prior to - * this method in order to obtain the desired - * output. - * - * @param groupNumber The number of the group. - * - * @return the substring matched by the - * regular expression designated - * the group number. - */ - std::string group(int groupNumber); - - /** - * Similar to python's MatchObject.groups. Get all - * the substrings matched by the capture groups defined - * in the pattern. The complete (implicit) capture group - * is not returned : ie only groups from 1 up to the number - * of groups in the pattern are returned. - * - * @return A vector of stings that were matched by some - * capturing group in the pattern. - * - * @pre The regular expression should have been - * compiled prior to the execution of this method. - */ - std::vector<std::string> groups(void); - - /** - * Try to match the compiled pattern with a - * subject. - * - * @param subject Subject to be matched - * by the pattern. - * - * @return true If the subject matches the pattern, - * false otherwise. - * - * @pre The regular expression should have been - * compiled prior to the execution of this method. - * - * @post The internal substring table will be updated - * with the new matches. Therefore, subsequent - * calls to group may return different results. - */ - bool matches(const std::string& subject) throw(match_error); - - /** - * Try to match the compiled pattern with the implicit - * subject. - * - * @return true If the subject matches the pattern, - * false otherwise. - * - * @pre The regular expression should have been - * compiled prior to the execution of this method. - * - * @post The internal substring table will be updated - * with the new matches. Therefore, subsequent - * calls to group may return different results. - */ - bool matches(void) throw(match_error); - - /** - * Split the subject into a list of substrings. - * - * @return A vector of substrings. - * - * @pre The regular expression should have been - * compiled prior to the execution of this method. - * - * @post The internal subject won't be affected by this - * by this operation. In other words: subject_before = - * subject_after. - */ - std::vector<std::string> split(void) throw(match_error); - - private: - /** - * The regular expression that represents that pattern. - */ - std::string _pattern; - - /** - * The optional subject string. - */ - std::string _subject; - - /** - * PCRE struct that - * contains the compiled regular - * expression - */ - pcre * _re; - - /** - * The internal output vector used by PCRE. - */ - int * _ovector; - - /** - * The size of the _ovector - */ - int _ovectorSize; - - /** - * Current offset in the _ovector; - */ - - int _offset[2]; - - /** - * The number of substrings matched after calling - * pcre_exec. - */ - int _count; - - /** - * PCRE options for this pattern. - */ - int _options; - - /** - * String representation of the options. - */ - std::string _optionsDescription; - }; -} -} - -#endif diff --git a/sflphone-common/test/Makefile.am b/sflphone-common/test/Makefile.am index 87264309aa0f6008c2e6a5704122de0593a5d097..e3679eb678549f698cf3d25c20c994b7c10a4bdf 100644 --- a/sflphone-common/test/Makefile.am +++ b/sflphone-common/test/Makefile.am @@ -1,28 +1,31 @@ include ../globals.mak -noinst_PROGRAMS = numbercleanerTester pluginmanagerTester hookmanagerTester audiolayerTester historyTester mainbufferTester #rtpTester +noinst_PROGRAMS = numbercleanerTester pluginmanagerTester hookmanagerTester audiolayerTester historyTester mainbufferTester sdesnegotiatorTester #rtpTester OBJECT_FILES= \ + ../src/sflphoned-logger.o \ ../src/sflphoned-managerimpl.o \ + ../src/sflphoned-account.o\ ../src/sflphoned-accountcreator.o \ ../src/sflphoned-call.o \ - ../src/sip/sipcall.o \ - ../src/iax/libiaxlink_la-iaxcall.o \ + ../src/sflphoned-conference.o \ + ../src/sflphoned-eventthread.o \ + ../src/sflphoned-managerimpl_registration.o \ + ../src/sflphoned-numbercleaner.o \ + ../src/sflphoned-observer.o \ ../src/sflphoned-voiplink.o \ - ../src/sip/sipvoiplink.o \ + ../src/sip/libsiplink_la-sipcall.o \ + ../src/iax/libiaxlink_la-iaxcall.o \ + ../src/sip/libsiplink_la-sipvoiplink.o \ ../src/iax/libiaxlink_la-iaxvoiplink.o \ - ../src/sflphoned-account.o \ - ../src/sip/sipaccount.o \ + ../src/sip/libsiplink_la-sipaccount.o \ ../src/iax/libiaxlink_la-iaxaccount.o \ - ../src/sflphoned-eventthread.o \ - ../src/sflphoned-conference.o \ + ../src/sip/libsiplink_la-sdp.o \ + ../src/sip/libsiplink_la-sdpmedia.o \ ../src/plug-in/pluginmanager.o \ ../src/plug-in/audiorecorder/audiorecord.o \ ../src/audio/samplerateconverter.o \ - ../src/sip/sdp.o \ - ../src/sip/sdpmedia.o \ - ../src/sflphoned-numbercleaner.o \ ../src/history/historymanager.o ../ @@ -65,7 +68,7 @@ pluginmanagerTester_LDADD = \ @SAMPLERATE_LIBS@ \ $(PJSIP_LIBS) \ $(OBJECT_FILES) - + hookmanagerTester_SOURCES = \ hookmanagerTest.cpp \ TestMain.cpp @@ -145,3 +148,23 @@ mainbufferTester_LDADD = \ @SAMPLERATE_LIBS@ \ $(PJSIP_LIBS) \ $(OBJECT_FILES) + +sdesnegotiatorTester_SOURCES = \ + sdesnegotiatorTest.h \ + sdesnegotiatorTest.cpp \ + TestMain.cpp + +sdesnegotiatorTester_LDADD = \ + ../src/libsflphone.la \ + $(SFLPHONE_LIBS) $(ZEROCONFLIB) $(LIB_DNSSD) \ + @ALSA_LIBS@ \ + @PULSEAUDIO_LIBS@ \ + @CPPUNIT_LIBS@ \ + @CCEXT2_LIBS@ \ + @CCGNU2_LIBS@ \ + @CCRTP_LIBS@ \ + @ZRTPCPP_LIBS@ \ + @libssl_LIBS@ \ + @SAMPLERATE_LIBS@ \ + $(PJSIP_LIBS) \ + $(OBJECT_FILES) diff --git a/sflphone-common/test/hookmanagerTest.cpp b/sflphone-common/test/hookmanagerTest.cpp index d5b8cb6a80a4a0fcca0e55c7440a0853d443b66f..817533a9277592570a8c2b5db7e5e7c0630dcd62 100644 --- a/sflphone-common/test/hookmanagerTest.cpp +++ b/sflphone-common/test/hookmanagerTest.cpp @@ -38,7 +38,7 @@ void HookManagerTest::testAddAction () int status; - status = urlhook->addAction ("www.google.ca", "x-www-browser"); + status = urlhook->addAction ("http://www.google.ca/?arg1=arg1&arg2=nvls&x=2&y=45&z=1", "x-www-browser"); CPPUNIT_ASSERT (status == 0); } diff --git a/sflphone-common/test/sdesnegotiatorTest.cpp b/sflphone-common/test/sdesnegotiatorTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71b429a5eac0a74d4a011b4fd9bbdc8b84067808 --- /dev/null +++ b/sflphone-common/test/sdesnegotiatorTest.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <sstream> +#include <ccrtp/rtp.h> +#include <assert.h> +#include <string> +#include <cstring> +#include <math.h> +#include <dlfcn.h> +#include <iostream> +#include <sstream> + + +#include "sdesnegotiatorTest.h" + +#include <unistd.h> + + +using std::cout; +using std::endl; + + +void SdesNegotiatorTest::setUp() +{ + + std::string attr("a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwd|2^20|1:32"); + + // std::string attr("a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32"); + + remoteOffer = new std::vector<std::string>(); + remoteOffer->push_back(attr); + + localCapabilities = new std::vector<sfl::CryptoSuiteDefinition>(); + for(int i = 0; i < 3; i++) { + localCapabilities->push_back(sfl::CryptoSuites[i]); + } + + sdesnego = new sfl::SdesNegotiator(*localCapabilities, *remoteOffer); + +} + + +void SdesNegotiatorTest::tearDown() +{ + + delete remoteOffer; + remoteOffer = NULL; + + delete localCapabilities; + localCapabilities = NULL; + + delete sdesnego; + sdesnego = NULL; + +} + +void SdesNegotiatorTest::testTagPattern() +{ + std::string subject = "a=crypto:4"; + + pattern = new sfl::Pattern("^a=crypto:(?P<tag>[0-9]{1,9})"); + *pattern << subject; + + CPPUNIT_ASSERT(pattern->matches()); + CPPUNIT_ASSERT(pattern->group("tag").compare("4") == 0); + + delete pattern; + pattern = NULL; +} + + +void SdesNegotiatorTest::testCryptoSuitePattern() +{ + std::string subject = "AES_CM_128_HMAC_SHA1_80"; + + pattern = new sfl::Pattern("(?P<cryptoSuite>AES_CM_128_HMAC_SHA1_80|" \ + "AES_CM_128_HMAC_SHA1_32|" \ + "F8_128_HMAC_SHA1_80|" \ + "[A-Za-z0-9_]+)"); + *pattern << subject; + + CPPUNIT_ASSERT(pattern->matches()); + CPPUNIT_ASSERT(pattern->group("cryptoSuite").compare("AES_CM_128_HMAC_SHA1_80") == 0); + + delete pattern; + pattern = NULL; +} + + +void SdesNegotiatorTest::testKeyParamsPattern() +{ + + std::string subject = "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32"; + + pattern = new sfl::Pattern("(?P<srtpKeyMethod>inline|[A-Za-z0-9_]+)\\:" \ + "(?P<srtpKeyInfo>[A-Za-z0-9\x2B\x2F\x3D]+)\\|" \ + "2\\^(?P<lifetime>[0-9]+)\\|" \ + "(?P<mkiValue>[0-9]+)\\:" \ + "(?P<mkiLength>[0-9]{1,3})\\;?", "g"); + + *pattern << subject; + + pattern->matches(); + CPPUNIT_ASSERT(pattern->group("srtpKeyMethod").compare("inline:")); + + /* + while (pattern->matches()) { + + std::string _srtpKeyMethod = pattern->group ("srtpKeyMethod"); + std::string _srtpKeyInfo = pattern->group ("srtpKeyInfo"); + std::string _lifetime = pattern->group ("lifetime"); + std::string _mkiValue = pattern->group ("mkiValue"); + std::string _mkiLength = pattern->group ("mkiLength"); + } + + + CPPUNIT_ASSERT(pattern->group("srtpKeyMethod").compare("inline:")); + CPPUNIT_ASSERT(pattern->group("srtpKeyInfo").compare("d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj")); + CPPUNIT_ASSERT(pattern->group("lifetime").compare("20")); + CPPUNIT_ASSERT(pattern->group("mkivalue").compare("1")); + CPPUNIT_ASSERT(pattern->group("mkilength").compare("32")); + */ + + delete pattern; + pattern = NULL; +} + + +void SdesNegotiatorTest::testNegotiation() +{ + CPPUNIT_ASSERT(sdesnego->negotiate()); + CPPUNIT_ASSERT(sdesnego->getCryptoSuite().compare("AES_CM_128_HMAC_SHA1_80") == 0); + CPPUNIT_ASSERT(sdesnego->getKeyMethod().compare("inline") == 0); + CPPUNIT_ASSERT(sdesnego->getKeyInfo().compare("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwd") == 0); + CPPUNIT_ASSERT(sdesnego->getLifeTime().compare("20") == 0); + CPPUNIT_ASSERT(sdesnego->getMkiValue().compare("1") == 0); + CPPUNIT_ASSERT(sdesnego->getMkiLength().compare("32") == 0); +} + + diff --git a/sflphone-common/test/sdesnegotiatorTest.h b/sflphone-common/test/sdesnegotiatorTest.h new file mode 100644 index 0000000000000000000000000000000000000000..4e97a9d6dab6134e88fbfde693844e67a79c61e7 --- /dev/null +++ b/sflphone-common/test/sdesnegotiatorTest.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +// Cppunit import +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCaller.h> +#include <cppunit/TestCase.h> +#include <cppunit/TestSuite.h> + +#include <assert.h> + +#include <stdio.h> +#include <sstream> +#include <ccrtp/rtp.h> + +#include <vector> + +// pjsip import +#include <pjsip.h> +#include <pjlib.h> +#include <pjsip_ua.h> +#include <pjlib-util.h> +#include <pjnath/stun_config.h> + +// Application import +#include "sip/SdesNegotiator.h" +#include "sip/Pattern.h" +// #include "config/config.h" +// #include "user_cfg.h" + + + +/* + * @file sdesnegotiationTest.cpp + * @brief Regroups unitary tests related to the plugin manager. + */ + +#ifndef _SDESNEGOTIATOR_TEST_ +#define _SDESNEGOTIATOR_TEST_ + + + +class SdesNegotiatorTest : public CppUnit::TestCase { + + /* + * Use cppunit library macros to add unit test the factory + */ + CPPUNIT_TEST_SUITE( SdesNegotiatorTest ); + CPPUNIT_TEST( testTagPattern ); + CPPUNIT_TEST( testCryptoSuitePattern ); + CPPUNIT_TEST( testKeyParamsPattern ); + CPPUNIT_TEST( testNegotiation ); + CPPUNIT_TEST_SUITE_END(); + + public: + + SdesNegotiatorTest() : CppUnit::TestCase("Sdes Tests") {} + + /* + * Code factoring - Common resources can be initialized here. + * This method is called by unitcpp before each test + */ + void setUp(); + + /* + * Code factoring - Common resources can be released here. + * This method is called by unitcpp after each test + */ + inline void tearDown(); + + void testTagPattern(); + + void testCryptoSuitePattern(); + + void testKeyParamsPattern(); + + void testNegotiation(); + + private: + + sfl::Pattern *pattern; + + sfl::SdesNegotiator *sdesnego; + + std::vector<std::string> *remoteOffer; + + std::vector<sfl::CryptoSuiteDefinition> *localCapabilities; + +}; + +/* Register our test module */ +CPPUNIT_TEST_SUITE_REGISTRATION( SdesNegotiatorTest ); + +#endif