diff options
| author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2014-04-17 22:06:12 +0200 | 
|---|---|---|
| committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2014-04-17 22:06:12 +0200 | 
| commit | 3f35a946ca00996b354a73831ef51aa269e8e623 (patch) | |
| tree | a569ebdf52183b9629221278740bf8f552912846 /src | |
| parent | 80f7b54c85386d171b6b8924e925026d25e4ad47 (diff) | |
| download | dabmux-3f35a946ca00996b354a73831ef51aa269e8e623.tar.gz dabmux-3f35a946ca00996b354a73831ef51aa269e8e623.tar.bz2 dabmux-3f35a946ca00996b354a73831ef51aa269e8e623.zip | |
Add CURVE authentification support for dabInputZMQ
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 10 | ||||
| -rw-r--r-- | src/ParserConfigfile.cpp | 7 | ||||
| -rw-r--r-- | src/dabInputZmq.cpp | 141 | ||||
| -rw-r--r-- | src/dabInputZmq.h | 38 | ||||
| -rw-r--r-- | src/zmqinput-keygen.c | 107 | 
5 files changed, 298 insertions, 5 deletions
| diff --git a/src/Makefile.am b/src/Makefile.am index 921c94f..738168c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,8 +20,6 @@  # You should have received a copy of the GNU General Public License  # along with ODR-DabMux.  If not, see <http://www.gnu.org/licenses/>. -bin_PROGRAMS=odr-dabmux odr-bridgetest -  if IS_GIT_REPO  GITVERSION_FLAGS = -DGITVERSION="\"`git describe`\""  else @@ -32,8 +30,12 @@ FEC_FLAGS   =  FEC_LIBS    =-lfec  if HAVE_ZEROMQ_TEST +bin_PROGRAMS=odr-dabmux odr-bridgetest zmqinput-keygen +  ZMQ_LIBS    =-lzmq  else +bin_PROGRAMS=odr-dabmux odr-bridgetest +  ZMQ_LIBS    =  endif @@ -95,3 +97,7 @@ odr_bridgetest_CFLAGS   =-DBRIDGE_TEST  odr_bridgetest_SOURCES  =bridge.c \                           crc.c crc.h +zmqinput_keygen_SOURCES  = zmqinput-keygen.c +zmqinput_keygen_LDADD    = $(ZMQ_LIBS) +zmqinput_keygen_CFLAGS   = -Wall $(GITVERSION_FLAGS) + diff --git a/src/ParserConfigfile.cpp b/src/ParserConfigfile.cpp index c59f5e5..b477f1d 100644 --- a/src/ParserConfigfile.cpp +++ b/src/ParserConfigfile.cpp @@ -676,6 +676,7 @@ void setup_subchannel_from_ptree(dabSubchannel* subchan,                      " has no zmq-buffer defined!";                  throw runtime_error(ss.str());              } +              try {                  zmqconfig.prebuffering = pt.get<int>("zmq-prebuffering");              } @@ -686,7 +687,11 @@ void setup_subchannel_from_ptree(dabSubchannel* subchan,                  throw runtime_error(ss.str());              } -            zmqconfig.enable_encryption = false; +            zmqconfig.curve_encoder_keyfile = pt.get<string>("encoder-key",""); +            zmqconfig.curve_secret_keyfile = pt.get<string>("secret-key",""); +            zmqconfig.curve_public_keyfile = pt.get<string>("public-key",""); + +            zmqconfig.enable_encryption = pt.get<int>("encryption", 0);              DabInputZmqAAC* inzmq =                  new DabInputZmqAAC(subchanuid, zmqconfig); diff --git a/src/dabInputZmq.cpp b/src/dabInputZmq.cpp index 753d6da..ee38440 100644 --- a/src/dabInputZmq.cpp +++ b/src/dabInputZmq.cpp @@ -67,13 +67,140 @@ using namespace std;  extern StatsServer* global_stats; +int readkey(string& keyfile, char* key) +{ +    int fd = open(keyfile.c_str(), O_RDONLY); +    if (fd < 0) +        return fd; +    int ret = read(fd, key, CURVE_KEYLEN); +    if (ret < 0) +        return ret; +    close(fd); + +    /* It needs to be zero-terminated */ +    key[CURVE_KEYLEN] = '\0'; + +    return 0; +} +  /***** Common functions (MPEG and AAC) ******/ -int DabInputZmqBase::open(const std::string inputUri) +/* If necessary, unbind the socket, then check the keys, + * if they are ok and encryption is required, set the + * keys to the socket, and finally bind the socket + * to the new address + */ +void DabInputZmqBase::rebind()  { +    if (! m_zmq_sock_bound_to.empty()) { +        try { +            m_zmq_sock.unbind(m_zmq_sock_bound_to.c_str()); +        } +        catch (zmq::error_t& err) { +            etiLog.level(warn) << "ZMQ unbind for input " << m_name << " failed"; +        } +    } + +    m_zmq_sock_bound_to = ""; + +    /* Load each key independently */ +    if (! m_config.curve_public_keyfile.empty()) { +        int rc = readkey(m_config.curve_public_keyfile, m_curve_public_key); + +        if (rc < 0) { +            etiLog.level(warn) << "Invalid public key for input " << +                m_name; + +            INVALIDATE_KEY(m_curve_public_key); +        } +    } + +    if (! m_config.curve_secret_keyfile.empty()) { +        int rc = readkey(m_config.curve_secret_keyfile, m_curve_secret_key); + +        if (rc < 0) { +            etiLog.level(warn) << "Invalid secret key for input " << +                m_name; + +            INVALIDATE_KEY(m_curve_secret_key); +        } +    } + +    if (! m_config.curve_encoder_keyfile.empty()) { +        int rc = readkey(m_config.curve_encoder_keyfile, m_curve_encoder_key); + +        if (rc < 0) { +            etiLog.level(warn) << "Invalid encoder key for input " << +                m_name; + +            INVALIDATE_KEY(m_curve_encoder_key); +        } +    } + +    /* If you want encryption, you need to have defined all +     * key files +     */ +    if ( m_config.enable_encryption && +            ( ! (KEY_VALID(m_curve_public_key) && +                 KEY_VALID(m_curve_secret_key) && +                 KEY_VALID(m_curve_encoder_key) ) ) ) { +        throw std::runtime_error("When enabling encryption, all three " +                "keyfiles must be set!"); +    } + +    if (m_config.enable_encryption) { +        try { +            /* We want to check that the encoder is the right one, +             * so the encoder is the CURVE server. +             */ +            m_zmq_sock.setsockopt(ZMQ_CURVE_SERVERKEY, +                    m_curve_encoder_key, CURVE_KEYLEN); +        } +        catch (zmq::error_t& err) { +            std::ostringstream os; +            os << "ZMQ set encoder key for input " << m_name << " failed"; +            throw std::runtime_error(os.str()); +        } + +        try { +            m_zmq_sock.setsockopt(ZMQ_CURVE_PUBLICKEY, +                    m_curve_public_key, CURVE_KEYLEN); +        } +        catch (zmq::error_t& err) { +            std::ostringstream os; +            os << "ZMQ set public key for input " << m_name << " failed"; +            throw std::runtime_error(os.str()); +        } + +        try { +            m_zmq_sock.setsockopt(ZMQ_CURVE_SECRETKEY, +                    m_curve_secret_key, CURVE_KEYLEN); +        } +        catch (zmq::error_t& err) { +            std::ostringstream os; +            os << "ZMQ set secret key for input " << m_name << " failed"; +            throw std::runtime_error(os.str()); +        } +    } +    else { +        try { +            /* This forces the socket to go to the ZMQ_NULL auth +             * mechanism +             */ +            const int no = 0; +            m_zmq_sock.setsockopt(ZMQ_CURVE_SERVER, &no, sizeof(no)); +        } +        catch (zmq::error_t& err) { +            std::ostringstream os; +            os << "ZMQ remove keys for input " << m_name << " failed"; +            throw std::runtime_error(os.str()); +        } + +    } +      // Prepare the ZMQ socket to accept connections      try { -        m_zmq_sock.bind(inputUri.c_str()); +        m_zmq_sock.bind(m_inputUri.c_str());      }      catch (zmq::error_t& err) {          std::ostringstream os; @@ -81,6 +208,8 @@ int DabInputZmqBase::open(const std::string inputUri)          throw std::runtime_error(os.str());      } +    m_zmq_sock_bound_to = m_inputUri; +      try {          m_zmq_sock.setsockopt(ZMQ_SUBSCRIBE, NULL, 0);      } @@ -89,6 +218,14 @@ int DabInputZmqBase::open(const std::string inputUri)          os << "ZMQ set socket options for input " << m_name << " failed";          throw std::runtime_error(os.str());      } +} + +int DabInputZmqBase::open(const std::string inputUri) +{ +    m_inputUri = inputUri; + +    /* Let caller handle exceptions when we open() */ +    rebind();      // We want to appear in the statistics !      global_stats->registerInput(m_name); diff --git a/src/dabInputZmq.h b/src/dabInputZmq.h index 50357f5..871676e 100644 --- a/src/dabInputZmq.h +++ b/src/dabInputZmq.h @@ -76,6 +76,24 @@  // want.  #define INPUT_ZMQ_MAX_BUFFER_SIZE (5*500) // 60s +/* The ZeroMQ Curve key is 40 bytes long in Z85 representation + * + * But we need to store it as zero-terminated string. + */ +#define CURVE_KEYLEN 40 + +/* helper to invalidate a key */ +#define INVALIDATE_KEY(k) memset(k, 0, CURVE_KEYLEN+1) + +/* Verification for key validity */ +#define KEY_VALID(k) (k[0] != '\0') + +/* Read a key from file into key + * + * Returns 0 on success, negative value on failure + */ +int readkey(std::string& keyfile, char* key); +  struct dab_input_zmq_config_t  {      /* The size of the internal buffer, measured in number @@ -116,12 +134,18 @@ class DabInputZmqBase : public DabInputBase, public RemoteControllable {              : RemoteControllable(name),              m_zmq_context(1),              m_zmq_sock(m_zmq_context, ZMQ_SUB), +            m_zmq_sock_bound_to(""),              m_bitrate(0),              m_enable_input(true),              m_config(config),              m_prebuf_current(0) {                  RC_ADD_PARAMETER(enable,                          "If the input is enabled. Set to zero to empty the buffer."); + +                /* Set all keys to zero */ +                INVALIDATE_KEY(m_curve_public_key); +                INVALIDATE_KEY(m_curve_secret_key); +                INVALIDATE_KEY(m_curve_encoder_key);              }          virtual int open(const std::string inputUri); @@ -139,8 +163,15 @@ class DabInputZmqBase : public DabInputBase, public RemoteControllable {      protected:          virtual int readFromSocket(size_t framesize) = 0; +        virtual void rebind(); +          zmq::context_t m_zmq_context;          zmq::socket_t m_zmq_sock; // handle for the zmq socket + +        /* If the socket is bound, this saves the endpoint, +         * otherwise, it's an empty string +         */ +        std::string m_zmq_sock_bound_to;          int m_bitrate;          /* set this to zero to empty the input buffer */ @@ -151,6 +182,13 @@ class DabInputZmqBase : public DabInputBase, public RemoteControllable {          dab_input_zmq_config_t m_config; +        /* Key management, keys need to be zero-terminated */ +        char m_curve_public_key[CURVE_KEYLEN+1]; +        char m_curve_secret_key[CURVE_KEYLEN+1]; +        char m_curve_encoder_key[CURVE_KEYLEN+1]; + +        std::string m_inputUri; +      private:          int m_prebuf_current;  }; diff --git a/src/zmqinput-keygen.c b/src/zmqinput-keygen.c new file mode 100644 index 0000000..0169837 --- /dev/null +++ b/src/zmqinput-keygen.c @@ -0,0 +1,107 @@ +/* Create a key file for the ZMQinput + * and save to file. + * + * Copyright (c) 2014 Matthias P. Braendli + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <zmq_utils.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + + +int main(int argc, char** argv) +{ +    if (argc == 1) { +        fprintf(stderr, "Generate a random key for dabInputZMQ and save it to two files.\n\n"); +        fprintf(stderr, "Usage: %s <name>\n", argv[0]); +        return 1; +    } + +    const char* keyname = argv[1]; + +    if (strlen(keyname) > 2048) { +        fprintf(stderr, "name too long\n"); +        return 1; +    } + +    char pubkeyfile[strlen(keyname) + 10]; +    char seckeyfile[strlen(keyname) + 10]; + +    sprintf(pubkeyfile, "%s.pub", keyname); +    sprintf(seckeyfile, "%s.sec", keyname); + +    char public_key [41]; +    char secret_key [41]; +    int rc = zmq_curve_keypair(public_key, secret_key); +    if (rc != 0) { +        fprintf(stderr, "key generation failed\n"); +    } + +    int fdpub = creat(pubkeyfile, S_IRUSR | S_IWUSR); +    if (fdpub < 0) { +        perror("File creation failed"); +        return 1; +    } + +    int fdsec = creat(seckeyfile, S_IRUSR | S_IWUSR); +    if (fdsec < 0) { +        perror("File creation failed"); +        return 1; +    } + +    int r = write(fdpub, public_key, 41); + +    int ret = 0; + +    if (r < 0) { +        perror("write failed"); +        ret = 1; +    } +    else if (r != 41) { +        fprintf(stderr, "Not enough key data written to file\n"); +        ret = 1; +    } + +    close(fdpub); + +    if (ret == 0) { +        r = write(fdsec, secret_key, 41); + +        if (r < 0) { +            perror("write failed"); +            ret = 1; +        } +        else if (r != 41) { +            fprintf(stderr, "Not enough key data written to file\n"); +            ret = 1; +        } +    } + +    close(fdsec); + +    return ret; +} + | 
