X-Git-Url: https://git.tokkee.org/?p=sysdb.git;a=blobdiff_plain;f=src%2Fclient%2Fsock.c;h=b1efe58e189700fd46c7b77b46c55278c39a150d;hp=cd9c8cebe342fca2bdd1865b9e0bae051752aa83;hb=b75718ea9fe4d6c90f1794e517a0712729553c0c;hpb=aafdfe8172431be873180890462703284e0c1856 diff --git a/src/client/sock.c b/src/client/sock.c index cd9c8ce..b1efe58 100644 --- a/src/client/sock.c +++ b/src/client/sock.c @@ -29,11 +29,13 @@ # 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 @@ -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) { @@ -224,41 +327,33 @@ sdb_client_connect(sdb_client_t *client, const char *username) if (! username) username = ""; - status = sdb_client_send(client, SDB_CONNECTION_STARTUP, - (uint32_t)strlen(username), username); - if (status < 0) { - char errbuf[1024]; - sdb_client_close(client); - sdb_log(SDB_LOG_ERR, "Failed to send STARTUP message to server: %s", - sdb_strerror(errno, errbuf, sizeof(errbuf))); - return (int)status; - } - buf = sdb_strbuf_create(64); rstatus = 0; - status = sdb_client_recv(client, &rstatus, buf); - if ((status > 0) && (rstatus == SDB_CONNECTION_OK)) { + status = sdb_client_rpc(client, SDB_CONNECTION_STARTUP, + (uint32_t)strlen(username), username, &rstatus, buf); + if ((status >= 0) && (rstatus == SDB_CONNECTION_OK)) { sdb_strbuf_destroy(buf); return 0; } if (status < 0) { - char errbuf[1024]; - sdb_log(SDB_LOG_ERR, "Failed to receive server response: %s", - sdb_strerror(errno, errbuf, sizeof(errbuf))); + sdb_log(SDB_LOG_ERR, "client: %s", sdb_strbuf_string(buf)); + sdb_client_close(client); + sdb_strbuf_destroy(buf); + return (int)status; } - else if (client->eof) - sdb_log(SDB_LOG_ERR, "Encountered end-of-file while waiting " + if (client->eof) + 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); } @@ -297,11 +392,73 @@ 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; } /* sdb_client_close */ +ssize_t +sdb_client_rpc(sdb_client_t *client, + uint32_t cmd, uint32_t msg_len, const char *msg, + uint32_t *code, sdb_strbuf_t *buf) +{ + uint32_t rcode = 0; + ssize_t status; + + if (! buf) + return -1; + + if (sdb_client_send(client, cmd, msg_len, msg) < 0) { + char errbuf[1024]; + sdb_strbuf_sprintf(buf, "Failed to send %s message to server: %s", + SDB_CONN_MSGTYPE_TO_STRING(cmd), + sdb_strerror(errno, errbuf, sizeof(errbuf))); + if (code) + *code = SDB_CONNECTION_ERROR; + return -1; + } + + while (42) { + size_t offset = sdb_strbuf_len(buf); + + status = sdb_client_recv(client, &rcode, buf); + if (status < 0) { + char errbuf[1024]; + sdb_strbuf_sprintf(buf, "Failed to receive server response: %s", + sdb_strerror(errno, errbuf, sizeof(errbuf))); + if (code) + *code = SDB_CONNECTION_ERROR; + return status; + } + + 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, "client: Received a LOG message " + "with invalid or missing priority"); + prio = (uint32_t)SDB_LOG_ERR; + } + sdb_log((int)prio, "client: %s", sdb_strbuf_string(buf) + offset); + sdb_strbuf_skip(buf, offset, sdb_strbuf_len(buf) - offset); + continue; + } + break; + } + + if (code) + *code = rcode; + return status; +} /* sdb_client_rpc */ + ssize_t sdb_client_send(sdb_client_t *client, uint32_t cmd, uint32_t msg_len, const char *msg) @@ -339,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) {