zauth(3)
========

NAME
----
zauth - authentication for ZeroMQ security mechanisms

SYNOPSIS
--------
----
#define CURVE_ALLOW_ANY "*"

//  CZMQ v3 API (for use with zsock, not zsocket, which is deprecated).
//
//  Create new zauth actor instance. This installs authentication on all 
//  zsock sockets. Until you add policies, all incoming NULL connections are
//  allowed (classic ZeroMQ behaviour), and all PLAIN and CURVE connections
//  are denied:
//  
//      zactor_t *auth = zactor_new (zauth, NULL);
//
//  Destroy zauth instance. This removes authentication and allows all
//  connections to pass, without authentication:
//  
//      zactor_destroy (&auth);
//
//  Note that all zauth commands are synchronous, so your application always
//  waits for a signal from the actor after each command.
//
//  Enable verbose logging of commands and activity. Verbose logging can help
//  debug non-trivial authentication policies:
//
//      zstr_send (auth, "VERBOSE");
//      zsock_wait (auth);
//
//  Allow (whitelist) a list of IP addresses. For NULL, all clients from
//  these addresses will be accepted. For PLAIN and CURVE, they will be
//  allowed to continue with authentication. You can call this method
//  multiple times to whitelist more IP addresses. If you whitelist one
//  or more addresses, any non-whitelisted addresses are treated as
//  blacklisted:
//  
//      zstr_sendx (auth, "ALLOW", "127.0.0.1", "127.0.0.2", NULL);
//      zsock_wait (auth);
//  
//  Deny (blacklist) a list of IP addresses. For all security mechanisms,
//  this rejects the connection without any further authentication. Use
//  either a whitelist, or a blacklist, not not both. If you define both
//  a whitelist and a blacklist, only the whitelist takes effect:
//  
//      zstr_sendx (auth, "DENY", "192.168.0.1", "192.168.0.2", NULL);
//      zsock_wait (auth);
//
//  Configure PLAIN authentication using a plain-text password file. You can
//  modify the password file at any time; zauth will reload it automatically
//  if modified externally:
//  
//      zstr_sendx (auth, "PLAIN", filename, NULL);
//      zsock_wait (auth);
//
//  Configure CURVE authentication, using a directory that holds all public
//  client certificates, i.e. their public keys. The certificates must be in
//  zcert_save format. You can add and remove certificates in that directory
//  at any time. To allow all client keys without checking, specify
//  CURVE_ALLOW_ANY for the directory name:
//
//      zstr_sendx (auth, "CURVE", directory, NULL);
//      zsock_wait (auth);
//
//  Configure GSSAPI authentication, using an underlying mechanism (usually
//  Kerberos) to establish a secure context and perform mutual authentication:
//
//      zstr_sendx (auth, "GSSAPI", NULL);
//      zsock_wait (auth);
//
//  This is the zauth constructor as a zactor_fn:
CZMQ_EXPORT void
    zauth (zsock_t *pipe, void *certstore);

//  Selftest
CZMQ_EXPORT void
    zauth_test (bool verbose);
----

DESCRIPTION
-----------

A zauth actor takes over authentication for all incoming connections in
its context. You can whitelist or blacklist peers based on IP address,
and define policies for securing PLAIN, CURVE, and GSSAPI connections.

This class replaces zauth_v2, and is meant for applications that use the
CZMQ v3 API (meaning, zsock).

EXAMPLE
-------
.From zauth_test method
----
//  Create temporary directory for test files
#   define TESTDIR ".test_zauth"
zsys_dir_create (TESTDIR);

//  Check there's no authentication
zsock_t *server = zsock_new (ZMQ_PULL);
assert (server);
zsock_t *client = zsock_new (ZMQ_PUSH);
assert (client);
bool success = s_can_connect (&server, &client, true);
assert (success);

//  Install the authenticator
zactor_t *auth = zactor_new (zauth, NULL);
assert (auth);
if (verbose) {
    zstr_sendx (auth, "VERBOSE", NULL);
    zsock_wait (auth);
}
//  Check there's no authentication on a default NULL server
success = s_can_connect (&server, &client, true);
assert (success);

//  When we set a domain on the server, we switch on authentication
//  for NULL sockets, but with no policies, the client connection
//  will be allowed.
zsock_set_zap_domain (server, "global");
success = s_can_connect (&server, &client, true);
assert (success);

//  Blacklist 127.0.0.1, connection should fail
zsock_set_zap_domain (server, "global");
zstr_sendx (auth, "DENY", "127.0.0.1", NULL);
zsock_wait (auth);
success = s_can_connect (&server, &client, true);
assert (!success);

//  Whitelist our address, which overrides the blacklist
zsock_set_zap_domain (server, "global");
zstr_sendx (auth, "ALLOW", "127.0.0.1", NULL);
zsock_wait (auth);
success = s_can_connect (&server, &client, true);
assert (success);

//  Try PLAIN authentication
zsock_set_plain_server (server, 1);
zsock_set_plain_username (client, "admin");
zsock_set_plain_password (client, "Password");
success = s_can_connect (&server, &client, true);
assert (!success);

FILE *password = fopen (TESTDIR "/password-file", "w");
assert (password);
fprintf (password, "admin=Password\n");
fclose (password);
zsock_set_plain_server (server, 1);
zsock_set_plain_username (client, "admin");
zsock_set_plain_password (client, "Password");
zstr_sendx (auth, "PLAIN", TESTDIR "/password-file", NULL);
zsock_wait (auth);
success = s_can_connect (&server, &client, true);
assert (success);

zsock_set_plain_server (server, 1);
zsock_set_plain_username (client, "admin");
zsock_set_plain_password (client, "Bogus");
success = s_can_connect (&server, &client, true);
assert (!success);

if (zsys_has_curve ()) {
    //  Try CURVE authentication
    //  We'll create two new certificates and save the client public
    //  certificate on disk; in a real case we'd transfer this securely
    //  from the client machine to the server machine.
    zcert_t *server_cert = zcert_new ();
    assert (server_cert);
    zcert_t *client_cert = zcert_new ();
    assert (client_cert);
    const char *server_key = zcert_public_txt (server_cert);

    //  Test without setting-up any authentication
    zcert_apply (server_cert, server);
    zcert_apply (client_cert, client);
    zsock_set_curve_server (server, 1);
    zsock_set_curve_serverkey (client, server_key);
    success = s_can_connect (&server, &client, true);
    assert (!success);

    //  Test CURVE_ALLOW_ANY
    zcert_apply (server_cert, server);
    zcert_apply (client_cert, client);
    zsock_set_curve_server (server, 1);
    zsock_set_curve_serverkey (client, server_key);
    zstr_sendx (auth, "CURVE", CURVE_ALLOW_ANY, NULL);
    zsock_wait (auth);
    success = s_can_connect (&server, &client, true);
    assert (success);

    //  Test full client authentication using certificates
    zcert_set_meta (client_cert, "Hello", "%s", "World!");
    zcert_apply (server_cert, server);
    zcert_apply (client_cert, client);
    zsock_set_curve_server (server, 1);
    zsock_set_curve_serverkey (client, server_key);
    zcert_save_public (client_cert, TESTDIR "/mycert.txt");
    zstr_sendx (auth, "CURVE", TESTDIR, NULL);
    zsock_wait (auth);
    success = s_can_connect (&server, &client, false);
    assert (success);

#if (ZMQ_VERSION >= ZMQ_MAKE_VERSION (4, 1, 0))
    // Test send/recv certificate metadata
    zframe_t *frame = zframe_recv (server);
    assert (frame != NULL);
    const char *meta = zframe_meta (frame, "Hello");
    assert (meta != NULL);
    assert (streq (meta, "World!"));
    zframe_destroy (&frame);
    s_renew_sockets(&server, &client);
#endif

    zcert_destroy (&server_cert);
    zcert_destroy (&client_cert);

    // Test custom zcertstore
    zcertstore_t *certstore = zcertstore_new (NULL);
    zcertstore_set_loader (certstore, s_test_loader, NULL, NULL);
    zactor_destroy(&auth);
    auth = zactor_new (zauth, certstore);
    assert (auth);
    if (verbose) {
        zstr_sendx (auth, "VERBOSE", NULL);
        zsock_wait (auth);
    }

    byte public_key [32] = { 105, 76, 150, 58, 214, 191, 218, 65, 50, 172,
                             131, 188, 247, 211, 136, 170, 227, 26, 57, 170,
                             185, 63, 246, 225, 177, 230, 12, 8, 134, 136,
                             105, 106 };
    byte secret_key [32] = { 245, 217, 172, 73, 106, 28, 195, 17, 218, 132,
                             135, 209, 99, 240, 98, 232, 7, 137, 244, 100,
                             242, 23, 29, 114, 70, 223, 83, 1, 113, 207,
                             132, 149 };
    zcert_t *shared_cert = zcert_new_from (public_key, secret_key);
    assert (shared_cert);
    zcert_apply (shared_cert, server);
    zcert_apply (shared_cert, client);
    zsock_set_curve_server (server, 1);
    zsock_set_curve_serverkey (client, "x?T*N/1Y{8goubv{Ts}#&#f}TXJ//DVe#D2HkoLU");
    success = s_can_connect (&server, &client, true);
    assert (success);
    zcert_destroy (&shared_cert);
}
//  Remove the authenticator and check a normal connection works
zactor_destroy (&auth);
success = s_can_connect (&server, &client, true);
assert (success);

zsock_destroy (&client);
zsock_destroy (&server);

//  Delete all test files
zdir_t *dir = zdir_new (TESTDIR, NULL);
assert (dir);
zdir_remove (dir, true);
zdir_destroy (&dir);
----
