Problem: strict ZAP protocol adherence is backward incompatible

Solution: add ZMQ_ZAP_ENFORCE_DOMAIN to hide backward incompatible
change and make it disabled by default.
In a future release that breaks API compatibility we can then switch
the default to enabled in order to achieve full RFC compatibility.

Fixes #2762
This commit is contained in:
Luca Boccassi 2017-10-07 18:34:18 +01:00
parent 50bddbaac9
commit b6aee51691
10 changed files with 73 additions and 4 deletions

View File

@ -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

View File

@ -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

View File

@ -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. */

View File

@ -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);

View File

@ -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;

View File

@ -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;
};
}

View File

@ -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 ()

View File

@ -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. */

View File

@ -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");

View File

@ -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