diff --git a/README.md b/README.md index efae754..90bb781 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ make Sample usage is covered in *mumlib_example.cpp* file. Basically, you should extend *mumlib::Callback* class to implement your own handlers. +To use a client certificate, you'll need a PEM certificate and private key without a passphrase. These are assed in the MumlibConfig struct to the Mumlib object constructor. Support for passphrase still needs to be added. + ## Credits 2015 Michał Słomkowski. The code is published under the terms of Lesser General Public License Version 3. @@ -42,4 +44,4 @@ to implement your own handlers. The library contains code from following 3rd party projects: * official Mumble Client: https://github.com/mumble-voip/mumble -* *libmumble*: https://github.com/cornejo/libmumble \ No newline at end of file +* *libmumble*: https://github.com/cornejo/libmumble diff --git a/include/mumlib.hpp b/include/mumlib.hpp index 09e1b90..9350b38 100644 --- a/include/mumlib.hpp +++ b/include/mumlib.hpp @@ -26,6 +26,8 @@ namespace mumlib { int opusEncoderBitrate = DEFAULT_OPUS_ENCODER_BITRATE; int opusSampleRate = DEFAULT_OPUS_SAMPLE_RATE; int opusChannels = DEFAULT_OPUS_NUM_CHANNELS; + std::string cert_file = ""; + std::string privkey_file = ""; // additional fields will be added in the future }; diff --git a/include/mumlib/Transport.hpp b/include/mumlib/Transport.hpp index 346728b..2bc7f6c 100644 --- a/include/mumlib/Transport.hpp +++ b/include/mumlib/Transport.hpp @@ -34,12 +34,26 @@ namespace mumlib { TransportException(string message) : MumlibException(std::move(message)) { } }; + /* This helper is needed because the sslContext and sslSocket are initialized in + * the Transport constructor and there wasn't an easier way of passing these two + * arguments. + * TODO: add support for password callback. + */ + class SslContextHelper : boost::noncopyable { + public: + SslContextHelper(boost::asio::ssl::context &ctx, + std::string cert_file, std::string privkey_file); + ~SslContextHelper() { }; + }; + class Transport : boost::noncopyable { public: Transport(io_service &ioService, ProcessControlMessageFunction processControlMessageFunc, ProcessEncodedAudioPacketFunction processEncodedAudioPacketFunction, - bool noUdp = false); + bool noUdp = false, + std::string cert_file = "", + std::string privkey_file = ""); ~Transport(); @@ -48,6 +62,13 @@ namespace mumlib { string user, string password); + void connect(string host, + int port, + string user, + string password, + string cert_file, + string privkey_file); + void disconnect(); ConnectionState getConnectionState() { @@ -85,6 +106,7 @@ namespace mumlib { CryptState cryptState; ssl::context sslContext; + SslContextHelper sslContextHelper; ssl::stream sslSocket; uint8_t *sslIncomingBuffer; diff --git a/mumlib_example.cpp b/mumlib_example.cpp index 2e874d6..6cebc0f 100644 --- a/mumlib_example.cpp +++ b/mumlib_example.cpp @@ -39,8 +39,8 @@ int main(int argc, char *argv[]) { logger.setPriority(log4cpp::Priority::NOTICE); logger.addAppender(appender1); - if (argc < 3) { - logger.crit("Usage: %s {server} {password}", argv[0]); + if (argc < 3 || argc == 4 || argc > 5) { + logger.crit("Usage: %s {server} {password} [{certfile} {keyfile}]", argv[0]); return 1; } @@ -50,6 +50,10 @@ int main(int argc, char *argv[]) { try { mumlib::MumlibConfiguration conf; conf.opusEncoderBitrate = 32000; + if ( argc > 3 && argc <= 5 ) { + conf.cert_file = argv[3]; + conf.privkey_file = argv[4]; + } mumlib::Mumlib mum(myCallback, conf); myCallback.mum = &mum; mum.connect(argv[1], 64738, "mumlib_example", argv[2]); @@ -61,4 +65,4 @@ int main(int argc, char *argv[]) { std::this_thread::sleep_for(std::chrono::seconds(5)); } } -} \ No newline at end of file +} diff --git a/src/Transport.cpp b/src/Transport.cpp index 83a383a..4af0a91 100644 --- a/src/Transport.cpp +++ b/src/Transport.cpp @@ -29,7 +29,9 @@ mumlib::Transport::Transport( io_service &ioService, mumlib::ProcessControlMessageFunction processMessageFunc, ProcessEncodedAudioPacketFunction processEncodedAudioPacketFunction, - bool noUdp) : + bool noUdp, + std::string cert_file, + std::string privkey_file) : logger(log4cpp::Category::getInstance("mumlib.Transport")), ioService(ioService), processMessageFunction(std::move(processMessageFunc)), @@ -38,6 +40,7 @@ mumlib::Transport::Transport( state(ConnectionState::NOT_CONNECTED), udpSocket(ioService), sslContext(ssl::context::sslv23), + sslContextHelper(sslContext, cert_file, privkey_file), sslSocket(ioService, sslContext), pingTimer(ioService, PING_INTERVAL), asyncBufferPool(static_cast(max(MAX_UDP_LENGTH, MAX_TCP_LENGTH))) { @@ -67,6 +70,7 @@ void mumlib::Transport::connect( sslSocket.set_verify_mode(boost::asio::ssl::verify_peer); + //todo for now it accepts every certificate, move it to callback sslSocket.set_verify_callback([](bool preverified, boost::asio::ssl::verify_context &ctx) { return true; @@ -519,6 +523,16 @@ void mumlib::Transport::throwTransportException(string message) { throw TransportException(std::move(message)); } +mumlib::SslContextHelper::SslContextHelper(ssl::context &ctx, std::string cert_file, std::string privkey_file) { + if ( cert_file.size() > 0 ) { + ctx.use_certificate_file(cert_file, ssl::context::file_format::pem); + } + if ( privkey_file.size() > 0 ) { + ctx.use_private_key_file(privkey_file, ssl::context::file_format::pem); + } +} + + void mumlib::Transport::sendEncodedAudioPacket(uint8_t *buffer, int length) { if (state != ConnectionState::CONNECTED) { logger.warn("Connection not established."); diff --git a/src/mumlib.cpp b/src/mumlib.cpp index 3c85cec..0447b7f 100644 --- a/src/mumlib.cpp +++ b/src/mumlib.cpp @@ -43,12 +43,20 @@ namespace mumlib { externalIoService = false; } - _Mumlib_Private(Callback &callback, io_service &ioService, MumlibConfiguration &configuration) + _Mumlib_Private( + Callback &callback, + io_service &ioService, + MumlibConfiguration &configuration) : callback(callback), ioService(ioService), externalIoService(true), - transport(ioService, boost::bind(&_Mumlib_Private::processIncomingTcpMessage, this, _1, _2, _3), - boost::bind(&_Mumlib_Private::processAudioPacket, this, _1, _2, _3)), + transport( + ioService, + boost::bind(&_Mumlib_Private::processIncomingTcpMessage, this, _1, _2, _3), + boost::bind(&_Mumlib_Private::processAudioPacket, this, _1, _2, _3), + false, + configuration.cert_file, + configuration.privkey_file), audio(configuration.opusSampleRate, configuration.opusEncoderBitrate, configuration.opusChannels) { audio.setOpusEncoderBitrate(configuration.opusEncoderBitrate);