[zeromq-dev] ZMTP3.0 authentication protocol bug with ZeroMQ4.0?

Hugo Landau hlandau at devever.net
Thu Oct 2 22:24:14 CEST 2014


I've identified what appear to be two issues; one with how ZeroMQ (v4.0.4)
implements ZMTP/3.0, and the other with the ZMTP/3.0 specification itself.

When a client attempts to connect to a ZeroMQ server, and the client is
configured to use NULL authentication but the server is configured to use PLAIN
authentication, the server will send a READY command and wait for the client to
send HELLO, even though according to ZMTP/3.0 it should close the connection.
The specification doesn't specify exactly when the connection should be
terminated in the event of authentication mismatch but I think it's implied
that it should be done immediately, and there's no need to postpone closure.

What follows is a protocol dump indicating this issue. Client and server source
code are at the end of this message.

    SERVER
    00000000  ff 00 00 00 00 00 00 00  01 7f                   ........ ..
CLIENT
00000000  ff 00 00 00 00 00 00 00  01 7f 03                ........ ...
    SERVER
    0000000A  03 00 50 4c 41 49 4e 00  00 00 00 00 00 00 00 00 ..PLAIN. ........
    0000001A  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ........ ........
    0000002A  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ........ ........
    0000003A  00 00 00 00 00 00                                ......
CLIENT
0000000B  00 4e 55 4c 4c 00 00 00  00 00 00 00 00 00 00 00 .NULL... ........
0000001B  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ........ ........
0000002B  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ........ ........
0000003B  00 00 00 00 00                                   .....
    SERVER
    00000040  04 19 05 52 45 41 44 59  0b 53 6f 63 6b 65 74 2d ...READY .Socket-
    00000050  54 79 70 65 00 00 00 03  52 45 50                Type.... REP
CLIENT
00000040  04 08 05 48 45 4c 4c 4f  00 00                   ...HELLO ..
(connection closed)

The ZMTP/3.0 specification also seems flawed in how it outlines how
authentication errors are to be dealt with. It clearly states that
mechanism-specific authentication errors (i.e., those occurring after mechanism
negotiation) should result in an ERROR command, which ensures that reconnects
are not attempted. But mechanism mismatches just result in the connection being
closed, which is indistinguishable from some lower-level issue (such as a loss
of network connectivity).

The end result of this is that the client keeps trying to connect perpetually,
hammering the server even though connection is impossible due to the mechanism
mismatch. This is what actually happens with ZeroMQ 4.0.4; the client just
keeps hammering the server at approximately half-second intervals.

It would be better if the ZMTP specification made provision for mechanism
mismatches to inhibit reconnection just as mechanism-specific errors do.

The ZMTP/3.0 specification also specifies that an increasing reconnection
interval should be used, but ZMQ_RECONNECT_IVL_MAX is disabled by default.

    /* CLIENT */
    #include <zmq.h>
    #include <stdio.h>
    #include <assert.h>

    int main(int argc, char **argv) {
      void *ctx  = zmq_ctx_new();
      void *req = zmq_socket(ctx, ZMQ_REQ);
      int rc = zmq_connect(req, "tcp://127.0.0.1:1234");
      assert(rc == 0);

      char buf[64] = {};
      zmq_send(req, "Request#1", 9, 0);
      zmq_recv(req, buf, 64, 0);
      printf("RX: %s\n", buf);

      return 0;
    }

    /* SERVER */
    #include <zmq.h>
    #include <stdio.h>
    #include <assert.h>
    #include <czmq.h>
    #include <stdbool.h>

    int main(int argc, char **argv) {
      zctx_t *ctx  = zctx_new();
      assert(ctx);

      zauth_t *auth = zauth_new(ctx);
      assert(auth);
      zauth_set_verbose(auth, true);

      void *resp = zsocket_new(ctx, ZMQ_REP);
      zsocket_set_zap_domain(resp, "a");
      zsocket_set_plain_server(resp, true);
      zauth_configure_plain(auth, "*", "passwords.txt");

      int rc = zmq_bind(resp, "tcp://*:1234");
      assert(rc == 0);

      printf("Ready\n");
      for (;;) {
        char buf[64] = {};
        rc = zmq_recv(resp, buf, 64, 0);
        if (rc < 0)
          break;
        printf("RX: %s\n", buf);
        zmq_send(resp, "(response)", 10, 0);
      }

      return 0;
    }



More information about the zeromq-dev mailing list