Code

Merged branch 'master' of git://git.tokkee.org/sysdb.
authorSebastian Harl <sh@tokkee.org>
Fri, 12 Dec 2014 07:55:39 +0000 (08:55 +0100)
committerSebastian Harl <sh@tokkee.org>
Fri, 12 Dec 2014 07:57:51 +0000 (08:57 +0100)
13 files changed:
configure.ac
src/Makefile.am
src/client/sock.c
src/frontend/connection.c
src/frontend/session.c
src/include/utils/os.h
src/tools/sysdb/input.h
src/tools/sysdb/main.c
src/utils/os.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 3472b6f527ed55068670db2cd99069e2f40ef0c7..ca558623722049c61fb8808d035aaf1867bdaa37 100644 (file)
@@ -119,7 +119,8 @@ sysdb_SOURCES = tools/sysdb/main.c include/client/sysdb.h \
                tools/sysdb/command.c tools/sysdb/command.h \
                tools/sysdb/input.c tools/sysdb/input.h \
                core/object.c include/core/object.h \
-               utils/llist.c include/utils/llist.h
+               utils/llist.c include/utils/llist.h \
+               utils/os.c include/utils/os.h
 sysdb_CFLAGS = -DBUILD_DATE="\"$$( date --utc '+%F %T' ) (UTC)\"" \
                $(AM_CFLAGS) @READLINE_CFLAGS@
 sysdb_LDADD = libsysdb_scanner.la libsysdbclient.la @READLINE_LIBS@
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 2e6c54776ede89ba35d2f70aacda889df89b7d75..07328dd11a2b0d9aaf96c82c0f920e1d88082070 100644 (file)
@@ -57,6 +57,18 @@ sdb_mkdir_all(const char *pathname, mode_t mode);
 int
 sdb_remove_all(const char *pathname);
 
+/*
+ * sdb_get_current_user:
+ * Returns the name of the current user. The string is allocated dynamically
+ * and has to be freed by the caller.
+ *
+ * Returns:
+ *  - the username on success
+ *  - NULL else
+ */
+char *
+sdb_get_current_user(void);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
index b8501f472383cd454608c19aebcab8688e784c60..ba2b32d8d3553dad528bfda657d7f18f79b20234 100644 (file)
@@ -33,7 +33,7 @@
 
 typedef struct {
        sdb_client_t *client;
-       const char *user;
+       char *user;
 
        sdb_strbuf_t *input;
        size_t tokenizer_pos;
index 6e10e8d9fd87d937a3d668566f9f4fccf7bbcd7d..357ef34827672d0033030eeefe8d85d059f98836 100644 (file)
@@ -37,6 +37,7 @@
 #include "utils/error.h"
 #include "utils/llist.h"
 #include "utils/strbuf.h"
+#include "utils/os.h"
 
 #include <errno.h>
 
 #      define DEFAULT_SOCKET "unix:"LOCALSTATEDIR"/run/sysdbd.sock"
 #endif
 
-static const char *
-get_current_user(void)
-{
-       struct passwd pw_entry;
-       struct passwd *result = NULL;
-
-       uid_t uid;
-
-       /* needs to be static because we return a pointer into this buffer
-        * to the caller */
-       static char buf[1024];
-
-       int status;
-
-       uid = geteuid();
-
-       memset(&pw_entry, 0, sizeof(pw_entry));
-       status = getpwuid_r(uid, &pw_entry, buf, sizeof(buf), &result);
-
-       if (status || (! result)) {
-               char errbuf[1024];
-               sdb_log(SDB_LOG_ERR, "Failed to determine current username: %s",
-                               sdb_strerror(errno, errbuf, sizeof(errbuf)));
-               return NULL;
-       }
-       return result->pw_name;
-} /* get_current_user */
-
 static const char *
 get_homedir(const char *username)
 {
@@ -136,6 +109,7 @@ get_homedir(const char *username)
 static void
 exit_usage(char *name, int status)
 {
+       char *user = sdb_get_current_user();
        printf(
 "Usage: %s <options>\n"
 
@@ -150,7 +124,8 @@ exit_usage(char *name, int status)
 "  -V        display the version number and copyright\n"
 
 "\nSysDB client "SDB_CLIENT_VERSION_STRING SDB_CLIENT_VERSION_EXTRA", "
-PACKAGE_URL"\n", basename(name), get_current_user());
+PACKAGE_URL"\n", basename(name), user);
+       free(user);
        exit(status);
 } /* exit_usage */
 
@@ -288,20 +263,23 @@ main(int argc, char **argv)
 
        if (! host)
                host = DEFAULT_SOCKET;
-       if (! input.user) {
-               input.user = get_current_user();
-               if (! input.user)
-                       exit(1);
-       }
+       if (! input.user)
+               input.user = sdb_get_current_user();
+       else
+               input.user = strdup(input.user);
+       if (! input.user)
+               exit(1);
 
        input.client = sdb_client_create(host);
        if (! input.client) {
                sdb_log(SDB_LOG_ERR, "Failed to create client object");
+               free(input.user);
                exit(1);
        }
        if (sdb_client_connect(input.client, input.user)) {
                sdb_log(SDB_LOG_ERR, "Failed to connect to SysDBd");
                sdb_client_destroy(input.client);
+               free(input.user);
                exit(1);
        }
 
@@ -309,6 +287,7 @@ main(int argc, char **argv)
                int status = execute_commands(input.client, commands);
                sdb_llist_destroy(commands);
                sdb_client_destroy(input.client);
+               free(input.user);
                if ((status != SDB_CONNECTION_OK) && (status != SDB_CONNECTION_DATA))
                        exit(1);
                exit(0);
@@ -332,6 +311,7 @@ main(int argc, char **argv)
                                        hist_file, sdb_strerror(errno, errbuf, sizeof(errbuf)));
                }
        }
+       free(input.user);
 
        input.input = sdb_strbuf_create(2048);
        sdb_input_init(&input);
index 7858825e1c889ad040b028284b9c4725c29a8068..e3f1fe2d42bb61d072940949a7ddee345836d4f6 100644 (file)
@@ -30,6 +30,7 @@
 #endif /* HAVE_CONFIG_H */
 
 #include "utils/os.h"
+#include "utils/error.h"
 
 #include <errno.h>
 
@@ -44,6 +45,7 @@
 #include <unistd.h>
 
 #include <libgen.h>
+#include <pwd.h>
 
 /*
  * public API
@@ -140,5 +142,29 @@ sdb_remove_all(const char *pathname)
        return remove(pathname);
 } /* sdb_remove_all */
 
+char *
+sdb_get_current_user(void)
+{
+       struct passwd pw_entry;
+       struct passwd *result = NULL;
+
+       uid_t uid;
+
+       char buf[1024];
+       int status;
+
+       uid = geteuid();
+       memset(&pw_entry, 0, sizeof(pw_entry));
+       status = getpwuid_r(uid, &pw_entry, buf, sizeof(buf), &result);
+
+       if (status || (! result)) {
+               char errbuf[1024];
+               sdb_log(SDB_LOG_ERR, "Failed to determine current username: %s",
+                               sdb_strerror(errno, errbuf, sizeof(errbuf)));
+               return NULL;
+       }
+       return strdup(result->pw_name);
+} /* sdb_get_current_user */
+
 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */
 
index f8287b45a645adac6798dbb611b092279e070d4f..eb4ccd8814771f058f6a9a357f1b669f5803b5e5 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 2>&1 )" && 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"