Code

store, plugin: Let the plugin module determine an objects backends.
[sysdb.git] / src / client / sock.c
index cb506a55565f52cf3400f21d6a29a38b17ebb96c..b1efe58e189700fd46c7b77b46c55278c39a150d 100644 (file)
 #      include "config.h"
 #endif /* HAVE_CONFIG_H */
 
+#include "sysdb.h"
 #include "client/sock.h"
 #include "utils/error.h"
 #include "utils/strbuf.h"
 #include "utils/proto.h"
 #include "utils/os.h"
+#include "utils/ssl.h"
 
 #include <arpa/inet.h>
 
@@ -62,6 +64,11 @@ struct sdb_client {
        int   fd;
        bool  eof;
 
+       /* optional SSL settings */
+       sdb_ssl_options_t ssl_opts;
+       sdb_ssl_client_t *ssl;
+       sdb_ssl_session_t *ssl_session;
+
        ssize_t (*read)(sdb_client_t *, sdb_strbuf_t *, size_t);
        ssize_t (*write)(sdb_client_t *, const void *, size_t);
 };
@@ -70,6 +77,26 @@ struct sdb_client {
  * private helper functions
  */
 
+static ssize_t
+ssl_read(sdb_client_t *client, sdb_strbuf_t *buf, size_t n)
+{
+       char tmp[n];
+       ssize_t ret;
+
+       ret = sdb_ssl_session_read(client->ssl_session, tmp, n);
+       if (ret <= 0)
+               return ret;
+
+       sdb_strbuf_memappend(buf, tmp, ret);
+       return ret;
+} /* ssl_read */
+
+static ssize_t
+ssl_write(sdb_client_t *client, const void *buf, size_t n)
+{
+       return sdb_ssl_session_write(client->ssl_session, buf, n);
+} /* ssl_write */
+
 static ssize_t
 client_read(sdb_client_t *client, sdb_strbuf_t *buf, size_t n)
 {
@@ -90,7 +117,7 @@ connect_unixsock(sdb_client_t *client, const char *address)
        client->fd = socket(AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
        if (client->fd < 0) {
                char errbuf[1024];
-               sdb_log(SDB_LOG_ERR, "Failed to open socket: %s",
+               sdb_log(SDB_LOG_ERR, "client: Failed to open socket: %s",
                                sdb_strerror(errno, errbuf, sizeof(errbuf)));
                return -1;
        }
@@ -102,7 +129,7 @@ connect_unixsock(sdb_client_t *client, const char *address)
        if (connect(client->fd, (struct sockaddr *)&sa, sizeof(sa))) {
                char errbuf[1024];
                sdb_client_close(client);
-               sdb_log(SDB_LOG_ERR, "Failed to connect to '%s': %s",
+               sdb_log(SDB_LOG_ERR, "client: Failed to connect to '%s': %s",
                                sa.sun_path, sdb_strerror(errno, errbuf, sizeof(errbuf)));
                return -1;
        }
@@ -112,11 +139,13 @@ connect_unixsock(sdb_client_t *client, const char *address)
 static int
 connect_tcp(sdb_client_t *client, const char *address)
 {
+       char host[SDB_MAX(strlen("localhost"), (address ? strlen(address) : 0)) + 1];
        struct addrinfo *ai, *ai_list = NULL;
+       char *peer, *tmp;
        int status;
 
        if ((status = sdb_resolve(SDB_NET_TCP, address, &ai_list))) {
-               sdb_log(SDB_LOG_ERR, "Failed to resolve '%s': %s",
+               sdb_log(SDB_LOG_ERR, "client: Failed to resolve '%s': %s",
                                address, gai_strerror(status));
                return -1;
        }
@@ -125,23 +154,58 @@ connect_tcp(sdb_client_t *client, const char *address)
                client->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
                if (client->fd < 0) {
                        char errbuf[1024];
-                       sdb_log(SDB_LOG_ERR, "Failed to open socket: %s",
+                       sdb_log(SDB_LOG_ERR, "client: Failed to open socket: %s",
                                        sdb_strerror(errno, errbuf, sizeof(errbuf)));
                        continue;
                }
 
                if (connect(client->fd, ai->ai_addr, ai->ai_addrlen)) {
-                       char host[1024], port[32], errbuf[1024];
+                       char h[1024], p[32], errbuf[1024];
                        sdb_client_close(client);
-                       getnameinfo(ai->ai_addr, ai->ai_addrlen, host, sizeof(host),
-                                       port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
-                       sdb_log(SDB_LOG_ERR, "Failed to connect to '%s:%s': %s",
-                                       host, port, sdb_strerror(errno, errbuf, sizeof(errbuf)));
+                       getnameinfo(ai->ai_addr, ai->ai_addrlen, h, sizeof(h),
+                                       p, sizeof(p), NI_NUMERICHOST | NI_NUMERICSERV);
+                       sdb_log(SDB_LOG_ERR, "client: Failed to connect to '%s:%s': %s",
+                                       h, p, sdb_strerror(errno, errbuf, sizeof(errbuf)));
                        continue;
                }
                break;
        }
        freeaddrinfo(ai_list);
+
+       if (client->fd < 0)
+               return -1;
+
+       client->ssl = sdb_ssl_client_create(&client->ssl_opts);
+       if (! client->ssl) {
+               sdb_client_close(client);
+               return -1;
+       }
+       client->ssl_session = sdb_ssl_client_connect(client->ssl, client->fd);
+       if (! client->ssl_session) {
+               sdb_client_close(client);
+               return -1;
+       }
+
+       strncpy(host, address, sizeof(host));
+       if ((tmp = strrchr(host, (int)':')))
+               *tmp = '\0';
+       if (! host[0])
+               strncpy(host, "localhost", sizeof(host));
+       peer = sdb_ssl_session_peer(client->ssl_session);
+       if ((! peer) || strcasecmp(peer, host)) {
+               /* TODO: also check alt-name */
+               sdb_log(SDB_LOG_ERR, "client: Failed to connect to '%s': "
+                               "peer name '%s' does not match host address",
+                               address, peer);
+               sdb_client_close(client);
+               if (peer)
+                       free(peer);
+               return -1;
+       }
+       free(peer);
+
+       client->read = ssl_read;
+       client->write = ssl_write;
        return client->fd;
 } /* connect_tcp */
 
@@ -159,20 +223,21 @@ sdb_client_create(const char *address)
 
        client = malloc(sizeof(*client));
        if (! client) {
-               sdb_log(SDB_LOG_ERR, "Out of memory");
+               sdb_log(SDB_LOG_ERR, "client: Out of memory");
                return NULL;
        }
        memset(client, 0, sizeof(*client));
        client->fd = -1;
        client->eof = 1;
 
+       client->ssl = NULL;
        client->read = client_read;
        client->write = client_write;
 
        client->address = strdup(address);
        if (! client->address) {
                sdb_client_destroy(client);
-               sdb_log(SDB_LOG_ERR, "Out of memory");
+               sdb_log(SDB_LOG_ERR, "client: Out of memory");
                return NULL;
        }
 
@@ -191,9 +256,47 @@ sdb_client_destroy(sdb_client_t *client)
                free(client->address);
        client->address = NULL;
 
+       sdb_ssl_free_options(&client->ssl_opts);
+
        free(client);
 } /* sdb_client_destroy */
 
+int
+sdb_client_set_ssl_options(sdb_client_t *client, const sdb_ssl_options_t *opts)
+{
+       int ret = 0;
+
+       if ((! client) || (! opts))
+               return -1;
+
+       sdb_ssl_free_options(&client->ssl_opts);
+
+       if (opts->ca_file) {
+               client->ssl_opts.ca_file = strdup(opts->ca_file);
+               if (! client->ssl_opts.ca_file)
+                       ret = -1;
+       }
+       if (opts->key_file) {
+               client->ssl_opts.key_file = strdup(opts->key_file);
+               if (! client->ssl_opts.key_file)
+                       ret = -1;
+       }
+       if (opts->cert_file) {
+               client->ssl_opts.cert_file = strdup(opts->cert_file);
+               if (! client->ssl_opts.cert_file)
+                       ret = -1;
+       }
+       if (opts->crl_file) {
+               client->ssl_opts.crl_file = strdup(opts->crl_file);
+               if (! client->ssl_opts.crl_file)
+                       ret = -1;
+       }
+
+       if (ret)
+               sdb_ssl_free_options(&client->ssl_opts);
+       return ret;
+} /* sdb_client_set_ssl_options */
+
 int
 sdb_client_connect(sdb_client_t *client, const char *username)
 {
@@ -234,23 +337,23 @@ sdb_client_connect(sdb_client_t *client, const char *username)
        }
 
        if (status < 0) {
-               sdb_log(SDB_LOG_ERR, "%s", sdb_strbuf_string(buf));
+               sdb_log(SDB_LOG_ERR, "client: %s", sdb_strbuf_string(buf));
                sdb_client_close(client);
                sdb_strbuf_destroy(buf);
                return (int)status;
        }
        if (client->eof)
-               sdb_log(SDB_LOG_ERR, "Encountered end-of-file while waiting "
+               sdb_log(SDB_LOG_ERR, "client: Encountered end-of-file while waiting "
                                "for server response");
 
        if (rstatus == SDB_CONNECTION_ERROR) {
-               sdb_log(SDB_LOG_ERR, "Access denied for user '%s': %s",
+               sdb_log(SDB_LOG_ERR, "client: Access denied for user '%s': %s",
                                username, sdb_strbuf_string(buf));
                status = -((int)rstatus);
        }
        else if (rstatus != SDB_CONNECTION_OK) {
-               sdb_log(SDB_LOG_ERR, "Received unsupported authentication request "
-                               "(status %d) during startup", (int)rstatus);
+               sdb_log(SDB_LOG_ERR, "client: Received unsupported authentication "
+                               "request (status %d) during startup", (int)rstatus);
                status = -((int)rstatus);
        }
 
@@ -289,6 +392,15 @@ sdb_client_close(sdb_client_t *client)
        if (! client)
                return;
 
+       if (client->ssl_session) {
+               sdb_ssl_session_destroy(client->ssl_session);
+               client->ssl_session = NULL;
+       }
+       if (client->ssl) {
+               sdb_ssl_client_destroy(client->ssl);
+               client->ssl = NULL;
+       }
+
        close(client->fd);
        client->fd = -1;
        client->eof = 1;
@@ -331,11 +443,11 @@ sdb_client_rpc(sdb_client_t *client,
                if (rcode == SDB_CONNECTION_LOG) {
                        uint32_t prio = 0;
                        if (sdb_proto_unmarshal_int32(SDB_STRBUF_STR(buf), &prio) < 0) {
-                               sdb_log(SDB_LOG_WARNING, "Received a LOG message "
+                               sdb_log(SDB_LOG_WARNING, "client: Received a LOG message "
                                                "with invalid or missing priority");
                                prio = (uint32_t)SDB_LOG_ERR;
                        }
-                       sdb_log((int)prio, "%s", sdb_strbuf_string(buf) + offset);
+                       sdb_log((int)prio, "client: %s", sdb_strbuf_string(buf) + offset);
                        sdb_strbuf_skip(buf, offset, sdb_strbuf_len(buf) - offset);
                        continue;
                }
@@ -384,9 +496,6 @@ sdb_client_recv(sdb_client_t *client,
        while (42) {
                ssize_t status;
 
-               if (sdb_select(client->fd, SDB_SELECTIN))
-                       return -1;
-
                errno = 0;
                status = client->read(client, buf, req);
                if (status < 0) {