From e1f797b0482d7887bd44238bdec28194b0c83628 Mon Sep 17 00:00:00 2001 From: Pieter Hintjens Date: Wed, 15 May 2013 17:54:03 +0200 Subject: [PATCH] Added configuration for PLAIN security * ZMQ_PLAIN_SERVER, ZMQ_PLAIN_USERNAME, ZMQ_PLAIN_PASSWORD options * Man page changes to zmq_setsockopt and zmq_getsockopt * Man pages for ZMQ_NULL, ZMQ_PLAIN, and ZMQ_CURVE * Test program test_security --- .gitignore | 1 + doc/Makefile.am | 5 +- doc/zmq.txt | 17 ++ doc/zmq_curve.txt | 40 +++ doc/zmq_getsockopt.txt | 58 +++- doc/zmq_null.txt | 27 ++ doc/zmq_plain.txt | 37 +++ doc/zmq_setsockopt.txt | 76 ++++- include/zmq.h | 12 + src/options.cpp | 597 ++++++++++++++++++++-------------------- src/options.hpp | 9 +- src/zmq.cpp | 4 +- tests/Makefile.am | 2 + tests/test_security.cpp | 117 ++++++++ 14 files changed, 693 insertions(+), 309 deletions(-) create mode 100644 doc/zmq_curve.txt create mode 100644 doc/zmq_null.txt create mode 100644 doc/zmq_plain.txt create mode 100644 tests/test_security.cpp diff --git a/.gitignore b/.gitignore index 87a432a6..57dc9040 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ tests/test_raw_sock tests/test_disconnect_inproc tests/test_ctx_options tests/test_iov +tests/test_security src/platform.hpp* src/stamp-h1 perf/local_lat diff --git a/doc/Makefile.am b/doc/Makefile.am index 6f41ce2b..026d76b7 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -8,9 +8,10 @@ MAN3 = zmq_bind.3 zmq_unbind.3 zmq_connect.3 zmq_disconnect.3 zmq_close.3 \ zmq_getsockopt.3 zmq_setsockopt.3 \ zmq_socket.3 zmq_socket_monitor.3 zmq_poll.3 \ zmq_errno.3 zmq_strerror.3 zmq_version.3 zmq_proxy.3 \ - zmq_sendmsg.3 zmq_recvmsg.3 zmq_init.3 zmq_term.3 + zmq_sendmsg.3 zmq_recvmsg.3 zmq_init.3 zmq_term.3 -MAN7 = zmq.7 zmq_tcp.7 zmq_pgm.7 zmq_epgm.7 zmq_inproc.7 zmq_ipc.7 +MAN7 = zmq.7 zmq_tcp.7 zmq_pgm.7 zmq_epgm.7 zmq_inproc.7 zmq_ipc.7 \ + zmq_null.7 zmq_plain.7 zmq_curve.7 MAN_DOC = $(MAN1) $(MAN3) $(MAN7) diff --git a/doc/zmq.txt b/doc/zmq.txt index 68fbceea..bbdcfb50 100644 --- a/doc/zmq.txt +++ b/doc/zmq.txt @@ -177,6 +177,23 @@ two sockets, opaquely. A proxy may optionally capture all traffic to a third socket. To start a proxy in an application thread, use linkzmq:zmq_proxy[3]. +Security +~~~~~~~~ +A 0MQ socket can select a security mechanism. Both peers must use the same +security mechanism. + +The following security mechanisms are provided for IPC and TCP connections: + +Null security:: + linkzmq:zmq_null[7] + +Clear-text authentication using username and password:: + linkzmq:zmq_clear[7] + +Secure authentication and encryption:: + linkzmq:zmq_curve[7] + + ERROR HANDLING -------------- The 0MQ library functions handle errors using the standard conventions found on diff --git a/doc/zmq_curve.txt b/doc/zmq_curve.txt new file mode 100644 index 00000000..a2d255b6 --- /dev/null +++ b/doc/zmq_curve.txt @@ -0,0 +1,40 @@ +zmq_curve(7) +============ + + +NAME +---- +zmq_curve - clear-text authentication + + +SYNOPSIS +-------- +The CURVE mechanism defines a mechanism for secure authentication and +confidentiality for communications between a client and a server. CURVE +is intended for use on public networks. The CURVE mechanism is defined +by this document: . + + +SERVER AND CLIENT ROLES +----------------------- +To use CURVE, the server shall set the ZMQ_CURVE_SERVER option, and the +client shall set the ZMQ_CURVE_PUBLICKEY and ZMQ_CURVE_SERVERKEY socket +options. Which peer binds, and which connects, is not relevant. + +NOTE: this isn't implemented and not fully defined. The server keypair +needs to be persistent, and it would be sensible to define a format for +this in CurveZMQ + + +SEE ALSO +-------- +linkzmq:zmq_setsockopt[3] +linkzmq:zmq_null[7] +linkzmq:zmq_plain[7] +linkzmq:zmq[7] + + +AUTHORS +------- +This page was written by the 0MQ community. To make a change please +read the 0MQ Contribution Policy at . diff --git a/doc/zmq_getsockopt.txt b/doc/zmq_getsockopt.txt index a5efc9f3..9d67c818 100644 --- a/doc/zmq_getsockopt.txt +++ b/doc/zmq_getsockopt.txt @@ -431,7 +431,7 @@ a ZMQ DSN. Note that if the TCP host is INADDR_ANY, indicated by a *, then the returned address will be 0.0.0.0 (for IPv4). [horizontal] -Option value type:: character string +Option value type:: NULL-terminated character string Option value unit:: N/A Default value:: NULL Applicable socket types:: all, when binding TCP or IPC transports @@ -451,8 +451,9 @@ Applicable socket types:: all, when using TCP transports. ZMQ_TCP_KEEPALIVE_IDLE: Override TCP_KEEPCNT(or TCP_KEEPALIVE on some OS) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Override 'TCP_KEEPCNT'(or 'TCP_KEEPALIVE' on some OS) socket option(where supported by OS). -The default value of `-1` means to skip any overrides and leave it to OS default. +Override 'TCP_KEEPCNT'(or 'TCP_KEEPALIVE' on some OS) socket option (where +supported by OS). The default value of `-1` means to skip any overrides and +leave it to OS default. [horizontal] Option value type:: int @@ -485,6 +486,57 @@ Default value:: -1 (leave to OS default) Applicable socket types:: all, when using TCP transports. +ZMQ_MECHANISM: Retrieve the current security mechanism +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The 'ZMQ_MECHANISM' option shall retrieve the current security mechanism +for the socket. + +[horizontal] +Option value type:: int +Option value unit:: ZMQ_NULL, ZMQ_PLAIN, or ZMQ_CURVE +Default value:: ZMQ_NULL +Applicable socket types:: all, when using TCP or IPC transports + + +ZMQ_PLAIN_SERVER: Retrieve the PLAIN server role +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Returns the 'ZMQ_PLAIN_SERVER' option, if any, previously set on the socket. + +[horizontal] +Option value type:: int +Option value unit:: 0, 1 +Default value:: int +Applicable socket types:: all, when using TCP or IPC transports + + +ZMQ_PLAIN_USERNAME: Retrieve the last username set +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The 'ZMQ_PLAIN_USERNAME' option shall retrieve the last username set for +the PLAIN security mechanism. The returned value shall be a NULL-terminated +string and MAY be empty. The returned size SHALL include the terminating +null byte. + +[horizontal] +Option value type:: NULL-terminated character string +Option value unit:: N/A +Default value:: null string +Applicable socket types:: all, when using TCP or IPC transports + + +ZMQ_PLAIN_PASSWORD: Retrieve the last password set +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The 'ZMQ_PLAIN_PASSWORD' option shall retrieve the last password set for +the PLAIN security mechanism. The returned value shall be a NULL-terminated +string and MAY be empty. The returned size SHALL include the terminating +null byte. + +[horizontal] +Option value type:: NULL-terminated character string +Option value unit:: N/A +Default value:: null string +Applicable socket types:: all, when using TCP or IPC transports + + RETURN VALUE ------------ The _zmq_getsockopt()_ function shall return zero if successful. Otherwise it diff --git a/doc/zmq_null.txt b/doc/zmq_null.txt new file mode 100644 index 00000000..abb9fff8 --- /dev/null +++ b/doc/zmq_null.txt @@ -0,0 +1,27 @@ +zmq_null(7) +=========== + + +NAME +---- +zmq_null - no security or confidentiality + + +SYNOPSIS +-------- +The NULL mechanism is defined by the ZMTP 3.0 specification: +. This is the default security mechanism +for ZeroMQ sockets. + + +SEE ALSO +-------- +linkzmq:zmq_plain[7] +linkzmq:zmq_curve[7] +linkzmq:zmq[7] + + +AUTHORS +------- +This page was written by the 0MQ community. To make a change please +read the 0MQ Contribution Policy at . diff --git a/doc/zmq_plain.txt b/doc/zmq_plain.txt new file mode 100644 index 00000000..7dd40dc8 --- /dev/null +++ b/doc/zmq_plain.txt @@ -0,0 +1,37 @@ +zmq_plain(7) +============ + + +NAME +---- +zmq_plain - clear-text authentication + + +SYNOPSIS +-------- +The PLAIN mechanism defines a simple username/password mechanism that +lets a server authenticate a client. PLAIN makes no attempt at security +or confidentiality. It is intended for use on internal networks where +security requirements are low. The PLAIN mechanism is defined by this +document: . + + +USAGE +----- +To use PLAIN, the server shall set the ZMQ_PLAIN_SERVER option, and the +client shall set the ZMQ_PLAIN_USERNAME and ZMQ_PLAIN_PASSWORD socket +options. Which peer binds, and which connects, is not relevant. + + +SEE ALSO +-------- +linkzmq:zmq_setsockopt[3] +linkzmq:zmq_null[7] +linkzmq:zmq_curve[7] +linkzmq:zmq[7] + + +AUTHORS +------- +This page was written by the 0MQ community. To make a change please +read the 0MQ Contribution Policy at . diff --git a/doc/zmq_setsockopt.txt b/doc/zmq_setsockopt.txt index 83cd25ef..f62d8423 100644 --- a/doc/zmq_setsockopt.txt +++ b/doc/zmq_setsockopt.txt @@ -437,6 +437,7 @@ Applicable socket types:: ZMQ_XPUB ZMQ_TCP_KEEPALIVE: Override SO_KEEPALIVE socket option ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Override 'SO_KEEPALIVE' socket option(where supported by OS). The default value of `-1` means to skip any overrides and leave it to OS default. @@ -449,8 +450,10 @@ Applicable socket types:: all, when using TCP transports. ZMQ_TCP_KEEPALIVE_IDLE: Override TCP_KEEPCNT(or TCP_KEEPALIVE on some OS) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Override 'TCP_KEEPCNT'(or 'TCP_KEEPALIVE' on some OS) socket option(where supported by OS). -The default value of `-1` means to skip any overrides and leave it to OS default. + +Override 'TCP_KEEPCNT'(or 'TCP_KEEPALIVE' on some OS) socket option (where +supported by OS). The default value of `-1` means to skip any overrides and +leave it to OS default. [horizontal] Option value type:: int @@ -461,8 +464,9 @@ Applicable socket types:: all, when using TCP transports. ZMQ_TCP_KEEPALIVE_CNT: Override TCP_KEEPCNT socket option ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Override 'TCP_KEEPCNT' socket option(where supported by OS). -The default value of `-1` means to skip any overrides and leave it to OS default. + +Override 'TCP_KEEPCNT' socket option(where supported by OS). The default +value of `-1` means to skip any overrides and leave it to OS default. [horizontal] Option value type:: int @@ -473,8 +477,9 @@ Applicable socket types:: all, when using TCP transports. ZMQ_TCP_KEEPALIVE_INTVL: Override TCP_KEEPINTVL socket option ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Override 'TCP_KEEPINTVL' socket option(where supported by OS). -The default value of `-1` means to skip any overrides and leave it to OS default. + +Override 'TCP_KEEPINTVL' socket option(where supported by OS). The default +value of `-1` means to skip any overrides and leave it to OS default. [horizontal] Option value type:: int @@ -485,11 +490,12 @@ Applicable socket types:: all, when using TCP transports. ZMQ_TCP_ACCEPT_FILTER: Assign filters to allow new TCP connections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Assign arbitrary number of filters that will be applied for each new TCP transport -connection on a listening socket. -If no filters applied, then TCP transport allows connections from any ip. -If at least one filter is applied then new connection source ip should be matched. -To clear all filters call zmq_setsockopt(socket, ZMQ_TCP_ACCEPT_FILTER, NULL, 0). + +Assign an arbitrary number of filters that will be applied for each new TCP +transport connection on a listening socket. If no filters are applied, then +the TCP transport allows connections from any IP address. If at least one +filter is applied then new connection source ip should be matched. To clear +all filters call zmq_setsockopt(socket, ZMQ_TCP_ACCEPT_FILTER, NULL, 0). Filter is a null-terminated string with ipv6 or ipv4 CIDR. [horizontal] @@ -499,6 +505,52 @@ Default value:: no filters (allow from all) Applicable socket types:: all listening sockets, when using TCP transports. +ZMQ_PLAIN_SERVER: Set PLAIN server role +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Defines whether the socket will act as server for PLAIN security, see +linkzmq:zmq_plain[3]. A value of '1' means the socket will act as +PLAIN server. A value of '0' means the socket will not act as PLAIN +server, and its security role then depends on other option settings. +Setting this to '0' shall reset the socket security to NULL. + +[horizontal] +Option value type:: int +Option value unit:: 0, 1 +Default value:: 0 +Applicable socket types:: all, when using TCP or IPC transports + + +ZMQ_PLAIN_USERNAME: Set PLAIN security username +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sets the username for outgoing connections over TCP or IPC. If you set this +to a non-null value, the security mechanism used for connections shall be +PLAIN, see linkzmq:zmq_plain[3]. If you set this to a null value, the security +mechanism used for connections shall be NULL, see linkzmq:zmq_null[3]. + +[horizontal] +Option value type:: character string +Option value unit:: N/A +Default value:: not set +Applicable socket types:: all, when using TCP or IPC transports + + +ZMQ_PLAIN_PASSWORD: Set PLAIN security password +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sets the password for outgoing connections over TCP or IPC. If you set this +to a non-null value, the security mechanism used for connections shall be +PLAIN, see linkzmq:zmq_plain[7]. If you set this to a null value, the security +mechanism used for connections shall be NULL, see linkzmq:zmq_null[3]. + +[horizontal] +Option value type:: character string +Option value unit:: N/A +Default value:: not set +Applicable socket types:: all, when using TCP or IPC transports + + RETURN VALUE ------------ The _zmq_setsockopt()_ function shall return zero if successful. Otherwise it @@ -550,6 +602,8 @@ SEE ALSO -------- linkzmq:zmq_getsockopt[3] linkzmq:zmq_socket[3] +linkzmq:zmq_plain[7] +linkzmq:zmq_curve[7] linkzmq:zmq[7] diff --git a/include/zmq.h b/include/zmq.h index de8f7b8b..470f16bb 100644 --- a/include/zmq.h +++ b/include/zmq.h @@ -253,6 +253,13 @@ ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg, int option, int optval); #define ZMQ_XPUB_VERBOSE 40 #define ZMQ_ROUTER_RAW 41 #define ZMQ_IPV6 42 +#define ZMQ_MECHANISM 43 +#define ZMQ_PLAIN_SERVER 44 +#define ZMQ_PLAIN_USERNAME 45 +#define ZMQ_PLAIN_PASSWORD 46 +#define ZMQ_CURVE_SERVER 47 +#define ZMQ_CURVE_PUBLICKEY 48 +#define ZMQ_CURVE_SERVERKEY 49 /* Message options */ #define ZMQ_MORE 1 @@ -261,6 +268,11 @@ ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg, int option, int optval); #define ZMQ_DONTWAIT 1 #define ZMQ_SNDMORE 2 +/* Security mechanisms */ +#define ZMQ_NULL 0 +#define ZMQ_PLAIN 1 +#define ZMQ_CURVE 2 + /* Deprecated aliases */ #define ZMQ_DELAY_ATTACH_ON_CONNECT ZMQ_IMMEDIATE #define ZMQ_NOBLOCK ZMQ_DONTWAIT diff --git a/src/options.cpp b/src/options.cpp index e18f0ab9..1558da86 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -51,6 +51,8 @@ zmq::options_t::options_t () : tcp_keepalive_cnt (-1), tcp_keepalive_idle (-1), tcp_keepalive_intvl (-1), + mechanism (ZMQ_NULL), + plain_server (0), socket_id (0) { } @@ -58,30 +60,29 @@ zmq::options_t::options_t () : int zmq::options_t::setsockopt (int option_, const void *optval_, size_t optvallen_) { - bool valid = true; bool is_int = (optvallen_ == sizeof (int)); int value = is_int? *((int *) optval_): 0; switch (option_) { case ZMQ_SNDHWM: - if (is_int && value >= 0) + if (is_int && value >= 0) { sndhwm = value; - else - valid = false; + return 0; + } break; case ZMQ_RCVHWM: - if (is_int && value >= 0) + if (is_int && value >= 0) { rcvhwm = value; - else - valid = false; + return 0; + } break; case ZMQ_AFFINITY: - if (optvallen_ == sizeof (uint64_t)) + if (optvallen_ == sizeof (uint64_t)) { affinity = *((uint64_t*) optval_); - else - valid = false; + return 0; + } break; case ZMQ_IDENTITY: @@ -92,405 +93,419 @@ int zmq::options_t::setsockopt (int option_, const void *optval_, && *((const unsigned char *) optval_) != 0) { identity_size = optvallen_; memcpy (identity, optval_, identity_size); + return 0; } - else - valid = false; break; case ZMQ_RATE: - if (is_int && value > 0) + if (is_int && value > 0) { rate = value; - else - valid = false; + return 0; + } break; case ZMQ_RECOVERY_IVL: - if (is_int && value >= 0) + if (is_int && value >= 0) { recovery_ivl = value; - else - valid = false; + return 0; + } + break; case ZMQ_SNDBUF: - if (is_int && value >= 0) + if (is_int && value >= 0) { sndbuf = value; - else - valid = false; + return 0; + } break; case ZMQ_RCVBUF: - if (is_int && value >= 0) + if (is_int && value >= 0) { rcvbuf = value; - else - valid = false; + return 0; + } break; case ZMQ_LINGER: - if (is_int && value >= -1) + if (is_int && value >= -1) { linger = value; - else - valid = false; + return 0; + } break; case ZMQ_RECONNECT_IVL: - if (is_int && value >= -1) + if (is_int && value >= -1) { reconnect_ivl = value; - else - valid = false; + return 0; + } break; case ZMQ_RECONNECT_IVL_MAX: - if (is_int && value >= 0) + if (is_int && value >= 0) { reconnect_ivl_max = value; - else - valid = false; + return 0; + } break; case ZMQ_BACKLOG: - if (is_int && value >= 0) + if (is_int && value >= 0) { backlog = value; - else - valid = false; + return 0; + } break; case ZMQ_MAXMSGSIZE: - if (optvallen_ == sizeof (int64_t)) + if (optvallen_ == sizeof (int64_t)) { maxmsgsize = *((int64_t *) optval_); - else - valid = false; + return 0; + } break; case ZMQ_MULTICAST_HOPS: - if (is_int && value > 0) + if (is_int && value > 0) { multicast_hops = value; - else - valid = false; + return 0; + } break; case ZMQ_RCVTIMEO: - if (is_int && value >= -1) + if (is_int && value >= -1) { rcvtimeo = value; - else - valid = false; + return 0; + } break; case ZMQ_SNDTIMEO: - if (is_int && value >= -1) + if (is_int && value >= -1) { sndtimeo = value; - else - valid = false; + return 0; + } break; /* Deprecated in favor of ZMQ_IPV6 */ case ZMQ_IPV4ONLY: - if (is_int && (value == 0 || value == 1)) + if (is_int && (value == 0 || value == 1)) { ipv6 = (value == 0); - else - valid = false; + return 0; + } break; /* To replace the somewhat surprising IPV4ONLY */ case ZMQ_IPV6: - if (is_int && (value == 0 || value == 1)) + if (is_int && (value == 0 || value == 1)) { ipv6 = (value != 0); - else - valid = false; + return 0; + } break; case ZMQ_TCP_KEEPALIVE: - if (is_int && (value >= -1 || value <= 1)) + if (is_int && (value >= -1 || value <= 1)) { tcp_keepalive = value; - else - valid = false; + return 0; + } break; case ZMQ_TCP_KEEPALIVE_CNT: - if (is_int && (value == -1 || value >= 0)) + if (is_int && (value == -1 || value >= 0)) { tcp_keepalive_cnt = value; - else - valid = false; + return 0; + } break; case ZMQ_TCP_KEEPALIVE_IDLE: - if (is_int && (value == -1 || value >= 0)) + if (is_int && (value == -1 || value >= 0)) { tcp_keepalive_idle = value; - else - valid = false; + return 0; + } break; case ZMQ_TCP_KEEPALIVE_INTVL: - if (is_int && (value == -1 || value >= 0)) + if (is_int && (value == -1 || value >= 0)) { tcp_keepalive_intvl = value; - else - valid = false; + return 0; + } break; case ZMQ_IMMEDIATE: - if (is_int && (value == 0 || value == 1)) + if (is_int && (value == 0 || value == 1)) { immediate = value; - else - valid = false; + return 0; + } break; case ZMQ_TCP_ACCEPT_FILTER: - if (optvallen_ == 0 && optval_ == NULL) + if (optvallen_ == 0 && optval_ == NULL) { tcp_accept_filters.clear (); + return 0; + } else - if (optvallen_ < 1 || optvallen_ > 255 || optval_ == NULL || *((const char*) optval_) == 0) - valid = false; - else { + if (optvallen_ > 0 && optvallen_ < 256 && optval_ != NULL && *((const char*) optval_) != 0) { std::string filter_str ((const char *) optval_, optvallen_); tcp_address_mask_t mask; int rc = mask.resolve (filter_str.c_str (), ipv6); - if (rc == 0) + if (rc == 0) { tcp_accept_filters.push_back (mask); - else - valid = false; + return 0; + } + } + break; + + case ZMQ_PLAIN_SERVER: + if (is_int && (value == 0 || value == 1)) { + plain_server = value; + mechanism = value? ZMQ_PLAIN: ZMQ_NULL; + return 0; + } + break; + + case ZMQ_PLAIN_USERNAME: + if (optvallen_ == 0 && optval_ == NULL) { + mechanism = ZMQ_NULL; + return 0; + } + else + if (optvallen_ >= 0 && optvallen_ < 256 && optval_ != NULL) { + plain_username.assign ((const char *) optval_, optvallen_); + plain_server = false; + mechanism = ZMQ_PLAIN; + return 0; + } + break; + + case ZMQ_PLAIN_PASSWORD: + if (optvallen_ == 0 && optval_ == NULL) { + mechanism = ZMQ_NULL; + return 0; + } + else + if (optvallen_ >= 0 && optvallen_ < 256 && optval_ != NULL) { + plain_password.assign ((const char *) optval_, optvallen_); + plain_server = false; + mechanism = ZMQ_PLAIN; + return 0; } break; default: - valid = false; break; } - if (valid) - return 0; - else { - errno = EINVAL; - return -1; - } + errno = EINVAL; + return -1; } int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_) { + bool is_int = (*optvallen_ == sizeof (int)); + int *value = (int *) optval_; + switch (option_) { + case ZMQ_SNDHWM: + if (is_int) { + *value = sndhwm; + return 0; + } + break; - case ZMQ_SNDHWM: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = sndhwm; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_RCVHWM: + if (is_int) { + *value = rcvhwm; + return 0; + } + break; - case ZMQ_RCVHWM: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = rcvhwm; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_AFFINITY: + if (*optvallen_ == sizeof (uint64_t)) { + *((uint64_t *) optval_) = affinity; + return 0; + } + break; - case ZMQ_AFFINITY: - if (*optvallen_ < sizeof (uint64_t)) { - errno = EINVAL; - return -1; - } - *((uint64_t*) optval_) = affinity; - *optvallen_ = sizeof (uint64_t); - return 0; + case ZMQ_IDENTITY: + if (*optvallen_ >= identity_size) { + memcpy (optval_, identity, identity_size); + *optvallen_ = identity_size; + return 0; + } + break; - case ZMQ_IDENTITY: - if (*optvallen_ < identity_size) { - errno = EINVAL; - return -1; - } - memcpy (optval_, identity, identity_size); - *optvallen_ = identity_size; - return 0; + case ZMQ_RATE: + if (is_int) { + *value = rate; + return 0; + } + break; - case ZMQ_RATE: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = rate; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_RECOVERY_IVL: + if (is_int) { + *value = recovery_ivl; + return 0; + } + break; - case ZMQ_RECOVERY_IVL: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = recovery_ivl; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_SNDBUF: + if (is_int) { + *value = sndbuf; + return 0; + } + break; - case ZMQ_SNDBUF: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = sndbuf; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_RCVBUF: + if (is_int) { + *value = rcvbuf; + return 0; + } + break; - case ZMQ_RCVBUF: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = rcvbuf; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_TYPE: + if (is_int) { + *value = type; + return 0; + } + break; - case ZMQ_TYPE: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = type; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_LINGER: + if (is_int) { + *value = linger; + return 0; + } + break; - case ZMQ_LINGER: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = linger; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_RECONNECT_IVL: + if (is_int) { + *value = reconnect_ivl; + return 0; + } + break; - case ZMQ_RECONNECT_IVL: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = reconnect_ivl; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_RECONNECT_IVL_MAX: + if (is_int) { + *value = reconnect_ivl_max; + return 0; + } + break; - case ZMQ_RECONNECT_IVL_MAX: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = reconnect_ivl_max; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_BACKLOG: + if (is_int) { + *value = backlog; + return 0; + } + break; - case ZMQ_BACKLOG: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = backlog; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_MAXMSGSIZE: + if (*optvallen_ == sizeof (int64_t)) { + *((int64_t *) optval_) = maxmsgsize; + *optvallen_ = sizeof (int64_t); + return 0; + } + break; - case ZMQ_MAXMSGSIZE: - if (*optvallen_ < sizeof (int64_t)) { - errno = EINVAL; - return -1; - } - *((int64_t*) optval_) = maxmsgsize; - *optvallen_ = sizeof (int64_t); - return 0; + case ZMQ_MULTICAST_HOPS: + if (is_int) { + *value = multicast_hops; + return 0; + } + break; - case ZMQ_MULTICAST_HOPS: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = multicast_hops; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_RCVTIMEO: + if (is_int) { + *value = rcvtimeo; + return 0; + } + break; - case ZMQ_RCVTIMEO: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = rcvtimeo; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_SNDTIMEO: + if (is_int) { + *value = sndtimeo; + return 0; + } + break; - case ZMQ_SNDTIMEO: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = sndtimeo; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_IPV4ONLY: + if (is_int) { + *value = 1 - ipv6; + return 0; + } + break; + + case ZMQ_IPV6: + if (is_int) { + *value = ipv6; + return 0; + } + break; - case ZMQ_IPV4ONLY: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = 1 - ipv6; - *optvallen_ = sizeof (int); - return 0; + case ZMQ_IMMEDIATE: + if (is_int) { + *value = immediate; + return 0; + } + break; + + case ZMQ_TCP_KEEPALIVE: + if (is_int) { + *value = tcp_keepalive; + return 0; + } + break; + + case ZMQ_TCP_KEEPALIVE_CNT: + if (is_int) { + *value = tcp_keepalive_cnt; + return 0; + } + break; + + case ZMQ_TCP_KEEPALIVE_IDLE: + if (is_int) { + *value = tcp_keepalive_idle; + return 0; + } + break; + + case ZMQ_TCP_KEEPALIVE_INTVL: + if (is_int) { + *value = tcp_keepalive_intvl; + return 0; + } + break; + + case ZMQ_LAST_ENDPOINT: + if (*optvallen_ >= last_endpoint.size () + 1) { + memcpy (optval_, last_endpoint.c_str (), last_endpoint.size () + 1); + *optvallen_ = last_endpoint.size () + 1; + return 0; + } + break; - case ZMQ_IPV6: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = ipv6; - *optvallen_ = sizeof (int); - return 0; - - case ZMQ_IMMEDIATE: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = immediate; - *optvallen_ = sizeof (int); - return 0; - - case ZMQ_TCP_KEEPALIVE: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = tcp_keepalive; - *optvallen_ = sizeof (int); - return 0; - - case ZMQ_TCP_KEEPALIVE_CNT: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = tcp_keepalive_cnt; - *optvallen_ = sizeof (int); - return 0; - - case ZMQ_TCP_KEEPALIVE_IDLE: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = tcp_keepalive_idle; - *optvallen_ = sizeof (int); - return 0; - - case ZMQ_TCP_KEEPALIVE_INTVL: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = tcp_keepalive_intvl; - *optvallen_ = sizeof (int); - return 0; - - case ZMQ_LAST_ENDPOINT: - /* don't allow string which cannot contain the entire message */ - if (*optvallen_ < last_endpoint.size() + 1) { - errno = EINVAL; - return -1; - } - memcpy (optval_, last_endpoint.c_str(), last_endpoint.size()+1); - *optvallen_ = last_endpoint.size()+1; - return 0; + case ZMQ_MECHANISM: + if (is_int) { + *value = mechanism; + return 0; + } + break; + + case ZMQ_PLAIN_SERVER: + if (is_int) { + *value = plain_server; + return 0; + } + break; + + case ZMQ_PLAIN_USERNAME: + if (*optvallen_ >= plain_username.size () + 1) { + memcpy (optval_, plain_username.c_str (), plain_username.size () + 1); + *optvallen_ = plain_username.size () + 1; + return 0; + } + break; + + case ZMQ_PLAIN_PASSWORD: + if (*optvallen_ >= plain_password.size () + 1) { + memcpy (optval_, plain_password.c_str (), plain_password.size () + 1); + *optvallen_ = plain_password.size () + 1; + return 0; + } + break; } - errno = EINVAL; return -1; } diff --git a/src/options.hpp b/src/options.hpp index b7382793..7d2ccf62 100644 --- a/src/options.hpp +++ b/src/options.hpp @@ -123,10 +123,17 @@ namespace zmq typedef std::vector tcp_accept_filters_t; tcp_accept_filters_t tcp_accept_filters; + // Security mechanism for all connections on this socket + int mechanism; + + // Security credentials for PLAIN mechanism + std::string plain_username; + std::string plain_password; + int plain_server; + // ID of the socket. int socket_id; }; - } #endif diff --git a/src/zmq.cpp b/src/zmq.cpp index 6daf7c33..0a9003d8 100644 --- a/src/zmq.cpp +++ b/src/zmq.cpp @@ -376,7 +376,9 @@ int zmq_send (void *s_, const void *buf_, size_t len_, int flags_) return rc; } + // Send multiple messages. +// TODO: this function has no man page // // If flag bit ZMQ_SNDMORE is set the vector is treated as // a single multi-part message, i.e. the last message has @@ -477,7 +479,7 @@ int zmq_recv (void *s_, void *buf_, size_t len_, int flags_) // // The iov_base* buffers of each iovec *a_ filled in by this // function may be freed using free(). -// +// TODO: this function has no man page // int zmq_recviov (void *s_, iovec *a_, size_t *count_, int flags_) { diff --git a/tests/Makefile.am b/tests/Makefile.am index 87911257..276609a8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -21,6 +21,7 @@ noinst_PROGRAMS = test_pair_inproc \ test_raw_sock \ test_disconnect_inproc \ test_ctx_options \ + test_security \ test_iov if !ON_MINGW @@ -49,6 +50,7 @@ test_raw_sock_SOURCES = test_raw_sock.cpp test_disconnect_inproc_SOURCES = test_disconnect_inproc.cpp test_ctx_options_SOURCES = test_ctx_options.cpp test_iov_SOURCES = test_iov.cpp +test_security_SOURCES = test_security.cpp if !ON_MINGW test_shutdown_stress_SOURCES = test_shutdown_stress.cpp test_pair_ipc_SOURCES = test_pair_ipc.cpp testutil.hpp diff --git a/tests/test_security.cpp b/tests/test_security.cpp new file mode 100644 index 00000000..e5380046 --- /dev/null +++ b/tests/test_security.cpp @@ -0,0 +1,117 @@ +/* + Copyright (c) 2007-2013 Contributors as noted in the AUTHORS file + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "../include/zmq.h" +#include +#include +#undef NDEBUG +#include + +int main (void) +{ + void *ctx = zmq_ctx_new (); + assert (ctx); + + // Server socket will accept connections + void *server = zmq_socket (ctx, ZMQ_DEALER); + assert (server); + + // Client socket that will try to connect to server + void *client = zmq_socket (ctx, ZMQ_DEALER); + assert (client); + + // Check NULL security configuration + int rc; + size_t optsize; + int mechanism; + + optsize = sizeof (int); + rc = zmq_getsockopt (client, ZMQ_MECHANISM, &mechanism, &optsize); + assert (rc == 0); + assert (mechanism == ZMQ_NULL); + + optsize = sizeof (int); + rc = zmq_getsockopt (server, ZMQ_MECHANISM, &mechanism, &optsize); + assert (rc == 0); + assert (mechanism == ZMQ_NULL); + + // Check PLAIN security + char username [256]; + optsize = 256; + rc = zmq_getsockopt (client, ZMQ_PLAIN_USERNAME, username, &optsize); + assert (rc == 0); + assert (optsize == 1); // Null string is one byte long + + char password [256]; + optsize = 256; + rc = zmq_getsockopt (client, ZMQ_PLAIN_PASSWORD, password, &optsize); + assert (rc == 0); + assert (optsize == 1); // Null string is one byte long + + strcpy (username, "admin"); + strcpy (password, "password"); + rc = zmq_setsockopt (client, ZMQ_PLAIN_USERNAME, username, strlen (username)); + assert (rc == 0); + rc = zmq_setsockopt (client, ZMQ_PLAIN_PASSWORD, password, strlen (password)); + assert (rc == 0); + + optsize = 256; + rc = zmq_getsockopt (client, ZMQ_PLAIN_USERNAME, username, &optsize); + assert (rc == 0); + assert (optsize == 5 + 1); + optsize = 256; + rc = zmq_getsockopt (client, ZMQ_PLAIN_PASSWORD, password, &optsize); + assert (rc == 0); + assert (optsize == 8 + 1); + + optsize = sizeof (int); + rc = zmq_getsockopt (client, ZMQ_MECHANISM, &mechanism, &optsize); + assert (rc == 0); + assert (mechanism == ZMQ_PLAIN); + + int as_server = 1; + rc = zmq_setsockopt (server, ZMQ_PLAIN_SERVER, &as_server, sizeof (int)); + assert (rc == 0); + + optsize = sizeof (int); + rc = zmq_getsockopt (server, ZMQ_MECHANISM, &mechanism, &optsize); + assert (rc == 0); + assert (mechanism == ZMQ_PLAIN); + + // Check we can switch back to NULL security + rc = zmq_setsockopt (client, ZMQ_PLAIN_USERNAME, NULL, 0); + assert (rc == 0); + rc = zmq_setsockopt (client, ZMQ_PLAIN_PASSWORD, NULL, 0); + assert (rc == 0); + optsize = sizeof (int); + rc = zmq_getsockopt (client, ZMQ_MECHANISM, &mechanism, &optsize); + assert (rc == 0); + assert (mechanism == ZMQ_NULL); + + rc = zmq_close (client); + assert (rc == 0); + + rc = zmq_close (server); + assert (rc == 0); + + rc = zmq_ctx_term (ctx); + assert (rc == 0); + + return 0; +}