Code

frontend: Get the username of the remote peer from a UNIX socket.
authorSebastian Harl <sh@tokkee.org>
Tue, 9 Dec 2014 14:19:47 +0000 (15:19 +0100)
committerSebastian Harl <sh@tokkee.org>
Tue, 9 Dec 2014 14:19:47 +0000 (15:19 +0100)
Then, only allow that user to authenticate against the daemon. That is, use
the same as PostgreSQL's "peer" authentication for all connections.

configure.ac
src/client/sock.c
src/frontend/connection.c
src/frontend/session.c
t/integration/query.sh
t/integration/test_lib.sh
t/unit/frontend/connection_test.c
t/unit/utils/unixsock_test.c

index 088a4b59fc994ad313dbd9540b1fc70901808b6d..baf1137b530197b1689be9106ff4f9751b70377b 100644 (file)
@@ -344,6 +344,46 @@ AC_SUBST([PROFILING_LDFLAGS])
 m4_divert_once([HELP_ENABLE], [
 Build dependencies:])
 
+AC_CHECK_HEADERS([ucred.h])
+dnl On OpenBSD, sys/param.h is required for sys/ucred.h.
+AC_CHECK_HEADERS([sys/ucred.h], [], [],
+               [[ #include <sys/param.h> ]])
+
+AC_CHECK_TYPES([struct ucred],
+               [have_struct_ucred="yes"], [have_struct_ucred="no"],
+               [[
+#include <sys/socket.h>
+#include <sys/param.h>
+#if HAVE_UCRED_H
+#      include <ucred.h>
+#endif
+               ]])
+
+if test "x$have_struct_ucred" != "xyes"; then
+       AC_MSG_CHECKING([for struct ucred when using _GNU_SOURCE])
+       orig_CFLAGS="$CFLAGS"
+       CFLAGS="$CFLAGS -D_GNU_SOURCE"
+       dnl Don't reuse AC_CHECK_HEADERS; for one it'll use the cached value
+       dnl but also, it will print the "checking for" message a second time.
+       AC_COMPILE_IFELSE(
+                       [AC_LANG_PROGRAM(
+                               [[
+#include <sys/socket.h>
+#include <sys/param.h>
+#if HAVE_UCRED_H
+#      include <ucred.h>
+#endif
+                               ]],
+                               [if (sizeof(struct ucred)) return 0;]
+                       )],
+                       [have_struct_ucred="yes"], [have_struct_ucred="no"])
+       CFLAGS="$orig_CFLAGS"
+       if test "x$have_struct_ucred" = "xyes"; then
+               AC_DEFINE([_GNU_SOURCE], 1, [Define to enable GNU features.])
+       fi
+       AC_MSG_RESULT([$have_struct_ucred])
+fi
+
 dnl Testing.
 PKG_CHECK_MODULES([CHECK], [check >= 0.9.4],
                [unit_tests="yes"], [unit_tests="no"])
@@ -482,11 +522,11 @@ if test "x$have_libfacter" = "xyes"; then
        AC_MSG_CHECKING([for facter::facts::collection in -lfacter])
        AC_LINK_IFELSE(
                        [AC_LANG_PROGRAM(
-                               [[[ #include <facter/facts/collection.hpp> ]]],
-                               [[[
+                               [[ #include <facter/facts/collection.hpp> ]],
+                               [[
                                        facter::facts::collection facts;
                                        facts.add_default_facts();
-                               ]]]
+                               ]]
                        )],
                        [TEST_LIBS=$TEST_LIBS -lfacter],
                        [have_libfacter="yes"],
index ef35166bab13162e84156facbd75d83fdd937b85..533e9e31b62c16457f36ae1e844a450251c26c51 100644 (file)
@@ -194,7 +194,8 @@ sdb_client_connect(sdb_client_t *client, const char *username)
                                "for server response");
 
        if (rstatus == SDB_CONNECTION_ERROR) {
-               sdb_log(SDB_LOG_ERR, "Access denied for user '%s'", username);
+               sdb_log(SDB_LOG_ERR, "Access denied for user '%s': %s",
+                               username, sdb_strbuf_string(buf));
                status = -((int)rstatus);
        }
        else if (rstatus != SDB_CONNECTION_OK) {
index e384bafa98297a7cf46c9283dee7b70a9c484dab..b27b3dd28bfbfd0324941d1861bc2bdd5d60a8ca 100644 (file)
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifdef HAVE_CONFIG_H
+#      include "config.h"
+#endif
+
 #include "sysdb.h"
 #include "core/object.h"
 #include "core/plugin.h"
 #include <stdlib.h>
 #include <string.h>
 
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifdef HAVE_UCRED_H
+#      include <ucred.h>
+#endif
+#ifdef HAVE_SYS_UCRED_H
+#      include <sys/ucred.h>
+#endif
+
+#include <pwd.h>
+
 #include <pthread.h>
 
 /*
@@ -59,6 +76,36 @@ static bool          conn_ctx_key_initialized = 0;
 #define CONN_FD_PREFIX "conn#"
 #define CONN_FD_PLACEHOLDER "XXXXXXX"
 
+/* XXX: only supports UNIX sockets so far */
+static char *
+peer(int sockfd)
+{
+       uid_t uid;
+
+       struct passwd pw_entry;
+       struct passwd *result = NULL;
+       char buf[1024];
+
+#ifdef SO_PEERCRED
+       struct ucred cred;
+       socklen_t len = sizeof(cred);
+
+       if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &len)
+                       || (len != sizeof(cred)))
+               return NULL;
+       uid = cred.uid;
+#else /* SO_PEERCRED */
+       errno = ENOSYS;
+       return NULL;
+#endif
+
+       memset(&pw_entry, 0, sizeof(pw_entry));
+       if (getpwuid_r(uid, &pw_entry, buf, sizeof(buf), &result)
+                       || (! result))
+               return NULL;
+       return strdup(result->pw_name);
+} /* peer */
+
 static int
 connection_init(sdb_object_t *obj, va_list ap)
 {
@@ -111,6 +158,16 @@ connection_init(sdb_object_t *obj, va_list ap)
                return -1;
        }
 
+       conn->username = peer(conn->fd);
+       if (! conn->username) {
+               char buf[1024];
+               sdb_log(SDB_LOG_ERR, "frontend: Failed to retrieve peer for "
+                               "connection conn#%i: %s", conn->fd,
+                               sdb_strerror(errno, buf, sizeof(buf)));
+               return -1;
+       }
+       conn->ready = 0;
+
        sdb_log(SDB_LOG_DEBUG, "frontend: Accepted connection on fd=%i",
                        conn->fd);
 
index b0fc7803e2a0e23ab53a05cc6a1c694c4251b824..ab45cdd17c30cb154a7cf051cf940ba11e47e4b0 100644 (file)
 int
 sdb_fe_session_start(sdb_conn_t *conn)
 {
-       const char *username;
+       char username[sdb_strbuf_len(conn->buf) + 1];
+       const char *tmp;
 
-       if ((! conn) || (conn->username))
+       if ((! conn) || (conn->cmd != SDB_CONNECTION_STARTUP))
                return -1;
 
-       if (conn->cmd != SDB_CONNECTION_STARTUP)
-               return -1;
-
-       username = sdb_strbuf_string(conn->buf);
-       if ((! username) || (! conn->cmd_len) || (! *username)) {
+       tmp = sdb_strbuf_string(conn->buf);
+       if ((! tmp) || (! conn->cmd_len) || (! *tmp)) {
                sdb_strbuf_sprintf(conn->errbuf, "Invalid empty username");
                return -1;
        }
+       strncpy(username, tmp, conn->cmd_len);
+       username[conn->cmd_len] = '\0';
 
-       /* XXX: for now, simply accept all connections */
-       conn->username = strndup(username, conn->cmd_len);
        if (! conn->username) {
-               sdb_strbuf_sprintf(conn->errbuf, "Authentication failed");
+               /* We couldn't determine the remote peer when setting up the
+                * connection; TODO: add support for password authentication */
+               sdb_strbuf_sprintf(conn->errbuf, "Password authentication "
+                               "not supported");
                return -1;
        }
+       if (strcmp(conn->username, username)) {
+               sdb_strbuf_sprintf(conn->errbuf, "%s cannot act on behalf of %s",
+                               conn->username, username);
+               return -1;
+       }
+
        sdb_connection_send(conn, SDB_CONNECTION_OK, 0, NULL);
        conn->ready = 1;
        return 0;
index 39522cf9be6c09928952f121b621b6121286e54a..5536c701c9c517eefd3882518e0e2eee1683a88a 100755 (executable)
@@ -48,6 +48,11 @@ run_sysdbd -D -C "$SYSDBD_CONF"
 wait_for_sysdbd
 sleep 3
 
+# Invalid user.
+output="$( run_sysdb_nouser -H "$SOCKET_FILE" \
+  -U $SYSDB_USER-invalid -c 'LIST hosts' 2>&1 )" && exit 1
+echo "$output" | grep -F 'Access denied'
+
 # On parse errors, expect a non-zero exit code.
 output="$( run_sysdb -H "$SOCKET_FILE" -c INVALID )" && exit 1
 echo "$output" | grep "Failed to parse query 'INVALID'"
index 0cc1931770d15bb0b86a46c6a9f6ad23d3b479d1..f6898e4678a9359e7e3b78949e095f8d2f36e9fc 100644 (file)
@@ -50,8 +50,14 @@ SYSDBD_CONF="$TESTDIR/sysdbd.conf"
 SOCKET_FILE="$TESTDIR/sock"
 PLUGIN_DIR="$TESTDIR"
 
+SYSDB_USER="$( id -un )"
+
 function run_sysdb() {
-       $MEMCHECK "$TESTDIR/sysdb" -U mockuser "$@"
+       $MEMCHECK "$TESTDIR/sysdb" -U $SYSDB_USER "$@"
+}
+
+function run_sysdb_nouser() {
+       $MEMCHECK "$TESTDIR/sysdb" "$@"
 }
 
 function run_sysdbd() {
index 77828e6a8c4220d482a1eaaf69c6ec03b142fca0..13ae221a0f509e6f8402c2c5c10e838f4df8b2ef 100644 (file)
 #include "frontend/connection.h"
 #include "frontend/connection-private.h"
 #include "utils/proto.h"
+#include "utils/os.h"
 #include "libsysdb_test.h"
 
 #include "utils/strbuf.h"
 
+#include <assert.h>
 #include <check.h>
 
 #include <stdlib.h>
@@ -48,6 +50,8 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
+static char username[1024];
+
 /*
  * private helper functions
  */
@@ -99,6 +103,9 @@ mock_conn_create(void)
 
        unlink(tmp_file);
 
+       conn->username = strdup(username);
+       assert(conn->username);
+
        conn->cmd = SDB_CONNECTION_IDLE;
        conn->cmd_len = 0;
        return conn;
@@ -174,12 +181,12 @@ connection_startup(sdb_conn_t *conn)
 {
        ssize_t check, expected;
 
-       expected = 2 * sizeof(uint32_t) + strlen("fakeuser");
+       expected = 2 * sizeof(uint32_t) + strlen(username);
        check = sdb_connection_send(conn, SDB_CONNECTION_STARTUP,
-                       (uint32_t)strlen("fakeuser"), "fakeuser");
+                       (uint32_t)strlen(username), username);
        fail_unless(check == expected,
-                       "sdb_connection_send(STARTUP, fakeuser) = %zi; expected: %zi",
-                       check, expected);
+                       "sdb_connection_send(STARTUP, %s) = %zi; expected: %zi",
+                       username, check, expected);
 
        mock_conn_rewind(conn);
        check = sdb_connection_read(conn);
@@ -189,7 +196,8 @@ connection_startup(sdb_conn_t *conn)
 
        fail_unless(sdb_strbuf_len(conn->errbuf) == 0,
                        "sdb_connection_read() left %zu bytes in the error "
-                       "buffer; expected: 0", sdb_strbuf_len(conn->errbuf));
+                       "buffer (%s); expected: 0", sdb_strbuf_len(conn->errbuf),
+                       sdb_strbuf_string(conn->errbuf));
 
        mock_conn_truncate(conn);
 } /* connection_startup */
@@ -244,7 +252,7 @@ START_TEST(test_conn_setup)
                { UINT32_MAX,             NULL,       NULL },
                { SDB_CONNECTION_IDLE,    "fakedata", "Authentication required" },
                { SDB_CONNECTION_PING,    NULL,       "Authentication required" },
-               { SDB_CONNECTION_STARTUP, "fakeuser", NULL },
+               { SDB_CONNECTION_STARTUP, username,   NULL },
                { SDB_CONNECTION_PING,    NULL,       NULL },
                { SDB_CONNECTION_IDLE,    NULL,       "Invalid command 0" },
                { SDB_CONNECTION_PING,    "fakedata", NULL },
@@ -292,7 +300,8 @@ START_TEST(test_conn_setup)
                else
                        fail_unless(sdb_strbuf_len(conn->errbuf) == 0,
                                        "sdb_connection_read() left %zu bytes in the error "
-                                       "buffer; expected: 0", sdb_strbuf_len(conn->errbuf));
+                                       "buffer (%s); expected: 0", sdb_strbuf_len(conn->errbuf),
+                                       sdb_strbuf_string(conn->errbuf));
        }
 
        mock_conn_destroy(conn);
@@ -418,6 +427,11 @@ fe_conn_suite(void)
        Suite *s = suite_create("frontend::connection");
        TCase *tc;
 
+       char *tmp = sdb_get_current_user();
+       assert(tmp);
+       strcpy(username, tmp);
+       free(tmp);
+
        tc = tcase_create("core");
        tcase_add_test(tc, test_conn_accept);
        tcase_add_test(tc, test_conn_setup);
index c28781ea051f815cd739fe3b4df23501904dbd00..82a1daf7b6986a4ae4746dcfd151947a6db39aa5 100644 (file)
@@ -30,7 +30,9 @@
 #endif /* HAVE_CONFIG_H */
 
 /* required for fopencookie support */
-#define _GNU_SOURCE
+#ifndef _GNU_SOURCE
+#      define _GNU_SOURCE
+#endif
 
 #include "utils/unixsock.h"
 #include "libsysdb_test.h"