diff --git a/doc/zmq_getsockopt.txt b/doc/zmq_getsockopt.txt index cd0aecc7..ed0f247c 100644 --- a/doc/zmq_getsockopt.txt +++ b/doc/zmq_getsockopt.txt @@ -838,6 +838,18 @@ Default value:: not set Applicable socket types:: all, when using TCP transport +ZMQ_ZAP_ENFORCE_DOMAIN: Retrieve ZAP domain handling mode +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The 'ZMQ_ZAP_ENFORCE_DOMAIN' option shall retrieve the flag that determines +whether a ZAP domain is strictly required or not. + +[horizontal] +Option value type:: int +Option value unit:: 0, 1 +Default value:: 0 +Applicable socket types:: all, when using ZAP + + ZMQ_VMCI_BUFFER_SIZE: Retrieve buffer size of the VMCI socket ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The `ZMQ_VMCI_BUFFER_SIZE` option shall retrieve the size of the underlying diff --git a/doc/zmq_setsockopt.txt b/doc/zmq_setsockopt.txt index 7dfc44a2..4c6f5f64 100644 --- a/doc/zmq_setsockopt.txt +++ b/doc/zmq_setsockopt.txt @@ -1081,7 +1081,9 @@ ZMQ_ZAP_DOMAIN: Set RFC 27 authentication domain ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sets the domain for ZAP (ZMQ RFC 27) authentication. A ZAP domain must be specified to enable authentication. When the ZAP domain is empty, which is -the default, ZAP authentication is disabled. +the default, ZAP authentication is disabled. This is not compatible with +previous versions of libzmq, so it can be controlled by ZMQ_ZAP_ENFORCE_DOMAIN +which for now is disabled by default. See http://rfc.zeromq.org/spec:27 for more details. [horizontal] @@ -1091,6 +1093,22 @@ Default value:: empty Applicable socket types:: all, when using TCP transport +ZMQ_ZAP_ENFORCE_DOMAIN: Set ZAP domain handling to strictly adhere the RFC +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ZAP (ZMQ RFC 27) authentication protocol specifies that a domain must +always be set. Older versions of libzmq did not follow the spec and allowed +an empty domain to be set. +This option can be used to enabled or disable the stricter, backward +incompatible behaviour. For now it is disabled by default, but in a future +version it will be enabled by default. + +[horizontal] +Option value type:: int +Option value unit:: 0, 1 +Default value:: 0 +Applicable socket types:: all, when using ZAP + + ZMQ_TCP_ACCEPT_FILTER: Assign filters to allow new TCP connections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Assign an arbitrary number of filters that will be applied for each new TCP diff --git a/include/zmq.h b/include/zmq.h index 2cde8230..eb092133 100644 --- a/include/zmq.h +++ b/include/zmq.h @@ -570,6 +570,7 @@ ZMQ_EXPORT void zmq_threadclose (void* thread); #define ZMQ_GSSAPI_PRINCIPAL_NAMETYPE 90 #define ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE 91 #define ZMQ_BINDTODEVICE 92 +#define ZMQ_ZAP_ENFORCE_DOMAIN 93 /* DRAFT 0MQ socket events and monitoring */ /* Unspecified system errors during handshake. Event value is an errno. */ diff --git a/src/curve_server.cpp b/src/curve_server.cpp index 23173045..643e887b 100644 --- a/src/curve_server.cpp +++ b/src/curve_server.cpp @@ -390,7 +390,9 @@ int zmq::curve_server_t::process_initiate (msg_t *msg_) rc = crypto_box_beforenm (cn_precom, cn_client, cn_secret); zmq_assert (rc == 0); - if (zap_required ()) { + // Given this is a backward-incompatible change, it's behind a socket + // option disabled by default. + if (zap_required () || !options.zap_enforce_domain) { // Use ZAP protocol (RFC 27) to authenticate the user. rc = session->zap_connect (); if (rc == 0) { @@ -404,6 +406,10 @@ int zmq::curve_server_t::process_initiate (msg_t *msg_) rc = receive_and_process_zap_reply (); if (rc == -1) return -1; + } else if (!options.zap_enforce_domain) { + // This supports the Stonehouse pattern (encryption without + // authentication) in legacy mode (domain set but no handler). + state = sending_ready; } else { session->get_socket ()->event_handshake_failed_no_detail ( session->get_endpoint (), EFAULT); diff --git a/src/options.cpp b/src/options.cpp index 53821404..6aeb14a7 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -89,7 +89,8 @@ zmq::options_t::options_t () : heartbeat_ttl (0), heartbeat_interval (0), heartbeat_timeout (-1), - use_fd (-1) + use_fd (-1), + zap_enforce_domain (false) { memset (curve_public_key, 0, CURVE_KEYSIZE); memset (curve_secret_key, 0, CURVE_KEYSIZE); @@ -628,6 +629,14 @@ int zmq::options_t::setsockopt (int option_, const void *optval_, } break; + case ZMQ_ZAP_ENFORCE_DOMAIN: + if (is_int) { + zap_enforce_domain = (value != 0); + return 0; + } + break; + + default: #if defined (ZMQ_ACT_MILITANT) // There are valid scenarios for probing with unknown socket option @@ -1052,6 +1061,13 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_) } break; + case ZMQ_ZAP_ENFORCE_DOMAIN: + if (is_int) { + *value = zap_enforce_domain; + return 0; + } + break; + default: #if defined (ZMQ_ACT_MILITANT) malformed = false; diff --git a/src/options.hpp b/src/options.hpp index 25972660..d76b112b 100644 --- a/src/options.hpp +++ b/src/options.hpp @@ -242,6 +242,9 @@ namespace zmq // Device to bind the underlying socket to, eg. VRF or interface std::string bound_device; + + // Enforce a non-empty ZAP domain requirement for PLAIN auth + bool zap_enforce_domain; }; } diff --git a/src/plain_server.cpp b/src/plain_server.cpp index 5fabb1ca..e993507d 100644 --- a/src/plain_server.cpp +++ b/src/plain_server.cpp @@ -47,7 +47,10 @@ zmq::plain_server_t::plain_server_t (session_base_t *session_, // Note that there is no point to PLAIN if ZAP is not set up to handle the // username and password, so if ZAP is not configured it is considered a // failure. - zmq_assert (zap_required()); + // Given this is a backward-incompatible change, it's behind a socket + // option disabled by default. + if (options.zap_enforce_domain) + zmq_assert (zap_required()); } zmq::plain_server_t::~plain_server_t () diff --git a/src/zmq_draft.h b/src/zmq_draft.h index cf2b452b..08534956 100644 --- a/src/zmq_draft.h +++ b/src/zmq_draft.h @@ -50,6 +50,7 @@ #define ZMQ_GSSAPI_PRINCIPAL_NAMETYPE 90 #define ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE 91 #define ZMQ_BINDTODEVICE 92 +#define ZMQ_ZAP_ENFORCE_DOMAIN 93 /* DRAFT 0MQ socket events and monitoring */ /* Unspecified system errors during handshake. Event value is an errno. */ diff --git a/tests/test_security_zap.cpp b/tests/test_security_zap.cpp index 383dbd97..31bd0d7c 100644 --- a/tests/test_security_zap.cpp +++ b/tests/test_security_zap.cpp @@ -324,6 +324,7 @@ void test_zap_errors (socket_config_fn server_socket_config_, shutdown_context_and_server_side (ctx, zap_thread, server, server_mon, handler); +#ifdef ZMQ_ZAP_ENFORCE_DOMAIN // no ZAP handler fprintf (stderr, "test_zap_unsuccessful no ZAP handler started\n"); setup_context_and_server_side (&ctx, &handler, &zap_thread, &server, @@ -339,6 +340,7 @@ void test_zap_errors (socket_config_fn server_socket_config_, client_socket_config_, client_socket_config_data_); shutdown_context_and_server_side (ctx, zap_thread, server, server_mon, handler); +#endif // ZAP handler disconnecting on first message fprintf(stderr, "test_zap_unsuccessful ZAP handler disconnects\n"); diff --git a/tests/testutil_security.hpp b/tests/testutil_security.hpp index c3736225..22d96eb1 100644 --- a/tests/testutil_security.hpp +++ b/tests/testutil_security.hpp @@ -114,6 +114,13 @@ void socket_config_curve_server (void *server, void *server_secret) rc = zmq_setsockopt (server, ZMQ_ZAP_DOMAIN, test_zap_domain, strlen (test_zap_domain)); assert (rc == 0); + +#ifdef ZMQ_ZAP_ENFORCE_DOMAIN + int required = 1; + rc = zmq_setsockopt (server, ZMQ_ZAP_ENFORCE_DOMAIN, &required, + sizeof (int)); + assert (rc == 0); +#endif } struct curve_client_data_t