[gnome-keyring] [control] Initial implementation of control socket.
- From: Stefan Walter <stefw src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnome-keyring] [control] Initial implementation of control socket.
- Date: Wed, 16 Dec 2009 00:42:09 +0000 (UTC)
commit 955940b6d96c2ab900b2da5c3334b70a4b4388c5
Author: Stef Walter <stef memberwebs com>
Date: Tue Dec 15 02:54:08 2009 +0000
[control] Initial implementation of control socket.
The control socket will be used by the pam module and the daemon
when communicating with itself. Not tested.
configure.in | 1 +
daemon/Makefile.am | 1 +
daemon/control/Makefile.am | 20 ++
daemon/control/gkd-control.c | 428 ++++++++++++++++++++++++++++++++++++++++++
daemon/control/gkd-control.h | 29 +++
egg/egg-unix-credentials.c | 14 ++
egg/egg-unix-credentials.h | 12 +-
7 files changed, 501 insertions(+), 4 deletions(-)
---
diff --git a/configure.in b/configure.in
index 7d562e1..0121c4e 100644
--- a/configure.in
+++ b/configure.in
@@ -550,6 +550,7 @@ AC_OUTPUT([
Makefile
daemon/Makefile
daemon/gnome-keyring-daemon.desktop.in
+daemon/control/Makefile
daemon/data/Makefile
daemon/dbus/Makefile
daemon/keyrings/Makefile
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 2c8201f..73d81ee 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -3,6 +3,7 @@ SUBDIRS = \
ui \
prompt \
keyrings \
+ control \
pkcs11 \
dbus \
data
diff --git a/daemon/control/Makefile.am b/daemon/control/Makefile.am
new file mode 100644
index 0000000..166505b
--- /dev/null
+++ b/daemon/control/Makefile.am
@@ -0,0 +1,20 @@
+
+INCLUDES= \
+ -DPREFIX=\""$(prefix)"\" \
+ -DBINDIR=\""$(bindir)"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ $(GLIB_CFLAGS)
+
+# ------------------------------------------------------------------
+# DAEMON CODE
+
+noinst_LTLIBRARIES = libgkd-control.la
+
+libgkd_control_la_SOURCES = \
+ gkd-control.c gkd-control.h
+
+libgkd_control_la_LIBADD = \
+ $(GLIB_LIBS)
diff --git a/daemon/control/gkd-control.c b/daemon/control/gkd-control.c
new file mode 100644
index 0000000..db22625
--- /dev/null
+++ b/daemon/control/gkd-control.c
@@ -0,0 +1,428 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gkd-control.h"
+
+#include "egg/egg-buffer.h"
+#include "egg/egg-cleanup.h"
+#include "egg/egg-secure-memory.h"
+#include "egg/egg-unix-credentials.h"
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+typedef struct _ControlData {
+ EggBuffer buffer;
+ gsize position;
+} ControlData;
+
+/* All the old op codes, most are no longer used */
+enum {
+ GNOME_KEYRING_OP_LOCK_ALL,
+ GNOME_KEYRING_OP_SET_DEFAULT_KEYRING,
+ GNOME_KEYRING_OP_GET_DEFAULT_KEYRING,
+ GNOME_KEYRING_OP_LIST_KEYRINGS,
+ GNOME_KEYRING_OP_CREATE_KEYRING,
+ GNOME_KEYRING_OP_LOCK_KEYRING,
+ GNOME_KEYRING_OP_UNLOCK_KEYRING,
+ GNOME_KEYRING_OP_DELETE_KEYRING,
+ GNOME_KEYRING_OP_GET_KEYRING_INFO,
+ GNOME_KEYRING_OP_SET_KEYRING_INFO,
+ GNOME_KEYRING_OP_LIST_ITEMS,
+ GNOME_KEYRING_OP_FIND,
+ GNOME_KEYRING_OP_CREATE_ITEM,
+ GNOME_KEYRING_OP_DELETE_ITEM,
+ GNOME_KEYRING_OP_GET_ITEM_INFO,
+ GNOME_KEYRING_OP_SET_ITEM_INFO,
+ GNOME_KEYRING_OP_GET_ITEM_ATTRIBUTES,
+ GNOME_KEYRING_OP_SET_ITEM_ATTRIBUTES,
+ GNOME_KEYRING_OP_GET_ITEM_ACL,
+ GNOME_KEYRING_OP_SET_ITEM_ACL,
+ GNOME_KEYRING_OP_CHANGE_KEYRING_PASSWORD,
+ GNOME_KEYRING_OP_SET_DAEMON_DISPLAY,
+ GNOME_KEYRING_OP_GET_ITEM_INFO_FULL,
+ GNOME_KEYRING_OP_PREPARE_ENVIRONMENT,
+
+ /* Add new ops here */
+
+ GNOME_KEYRING_NUM_OPS
+};
+
+/* All the old result codes */
+enum {
+ GNOME_KEYRING_RESULT_OK,
+ GNOME_KEYRING_RESULT_DENIED,
+ GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON,
+ GNOME_KEYRING_RESULT_ALREADY_UNLOCKED,
+ GNOME_KEYRING_RESULT_NO_SUCH_KEYRING,
+ GNOME_KEYRING_RESULT_BAD_ARGUMENTS,
+ GNOME_KEYRING_RESULT_IO_ERROR,
+ GNOME_KEYRING_RESULT_CANCELLED,
+ GNOME_KEYRING_RESULT_KEYRING_ALREADY_EXISTS,
+ GNOME_KEYRING_RESULT_NO_MATCH
+};
+
+static ControlData*
+control_data_new (void)
+{
+ ControlData *cdata = g_slice_new0 (ControlData);
+ egg_buffer_init_full (&cdata->buffer, 128, egg_secure_realloc);
+ cdata->position = 0;
+ return cdata;
+}
+
+static void
+control_data_free (gpointer data)
+{
+ ControlData *cdata = data;
+ egg_buffer_uninit (&cdata->buffer);
+ g_slice_free (ControlData, cdata);
+}
+
+static guint32
+control_unlock_keyring (EggBuffer *buffer)
+{
+ gchar *name;
+ gchar *master;
+ gsize offset = 8;
+ guint32 res;
+
+ if (!egg_buffer_get_string (buffer, offset, &offset, &name, g_realloc))
+ return GNOME_KEYRING_RESULT_BAD_ARGUMENTS;
+
+ if (!egg_buffer_get_string (buffer, offset, &offset, &master, egg_secure_realloc)) {
+ g_free (name);
+ return GNOME_KEYRING_RESULT_BAD_ARGUMENTS;
+ }
+
+ if (!name || g_str_equal (name, "login")) {
+ // TODO: Perform unlocking */
+ res = GNOME_KEYRING_RESULT_DENIED;
+ } else {
+ g_message ("keyring request not supported");
+ res = GNOME_KEYRING_RESULT_NO_SUCH_KEYRING;
+ }
+
+ egg_secure_strfree (master);
+ g_free (name);
+ return res;
+}
+
+static guint32
+control_change_keyring_password (EggBuffer *buffer)
+{
+ gsize offset = 8;
+ guint32 res;
+ gchar *name;
+ gchar *master;
+ gchar *original;
+
+ if (!egg_buffer_get_string (buffer, offset, &offset, &name, g_realloc))
+ return GNOME_KEYRING_RESULT_BAD_ARGUMENTS;
+
+ if (!egg_buffer_get_string (buffer, offset, &offset, &original, egg_secure_realloc)) {
+ g_free (name);
+ return GNOME_KEYRING_RESULT_BAD_ARGUMENTS;
+ }
+
+ if (!egg_buffer_get_string (buffer, offset, &offset, &master, egg_secure_realloc)) {
+ egg_secure_strfree (original);
+ g_free (name);
+ return GNOME_KEYRING_RESULT_BAD_ARGUMENTS;
+ }
+
+ if (!name || g_str_equal (name, "login")) {
+ // TODO: Perform unlocking */
+ res = GNOME_KEYRING_RESULT_DENIED;
+ } else {
+ g_message ("keyring request not supported");
+ res = GNOME_KEYRING_RESULT_NO_SUCH_KEYRING;
+ }
+
+ egg_secure_strfree (master);
+ egg_secure_strfree (original);
+ g_free (name);
+ return res;
+}
+
+static guint32
+control_prepare_environment (EggBuffer *buffer)
+{
+ gchar **environment;
+ guint32 res;
+ gsize offset = 8;
+
+ if (!egg_buffer_get_stringv (buffer, offset, &offset, &environment, g_realloc))
+ return GNOME_KEYRING_RESULT_BAD_ARGUMENTS;
+
+ /* TODO: Prepare the environment */
+ res = GNOME_KEYRING_RESULT_DENIED;
+
+ g_strfreev (environment);
+ return res;
+}
+
+static gboolean
+control_output (GIOChannel *channel, GIOCondition cond, gpointer user_data)
+{
+ ControlData *cdata = user_data;
+ EggBuffer *buffer = &cdata->buffer;
+ int fd, res;
+
+ fd = g_io_channel_unix_get_fd (channel);
+ g_assert (cdata->position < buffer->len);
+
+ if (cond & G_IO_OUT) {
+ res = write (fd, buffer->buf + cdata->position, buffer->len - cdata->position);
+ if (res <= 0) {
+ if (errno != EAGAIN && errno != EINTR)
+ cdata->position = buffer->len;
+ } else {
+ cdata->position += res;
+ g_assert (cdata->position <= buffer->len);
+ }
+ }
+
+ if (cdata->position == buffer->len)
+ cond |= G_IO_HUP;
+
+ return (cond & G_IO_HUP) == 0;
+}
+
+static void
+control_process (EggBuffer *req, GIOChannel *channel)
+{
+ ControlData *cdata = NULL;
+ guint32 res;
+ guint32 op;
+
+ if (!egg_buffer_get_uint32 (req, 4, NULL, &op)) {
+ g_message ("invalid operation sent to control socket");
+ return;
+ }
+
+ switch (op) {
+ case GNOME_KEYRING_OP_CREATE_KEYRING:
+ case GNOME_KEYRING_OP_UNLOCK_KEYRING:
+ res = control_unlock_keyring (req);
+ cdata = control_data_new ();
+ egg_buffer_add_uint32 (&cdata->buffer, 4);
+ egg_buffer_add_uint32 (&cdata->buffer, res);
+ break;
+ case GNOME_KEYRING_OP_CHANGE_KEYRING_PASSWORD:
+ res = control_change_keyring_password (req);
+ cdata = control_data_new ();
+ egg_buffer_add_uint32 (&cdata->buffer, 4);
+ egg_buffer_add_uint32 (&cdata->buffer, res);
+ break;
+ case GNOME_KEYRING_OP_PREPARE_ENVIRONMENT:
+ res = control_prepare_environment (req);
+ cdata = control_data_new ();
+ egg_buffer_add_uint32 (&cdata->buffer, 8);
+ egg_buffer_add_uint32 (&cdata->buffer, res);
+ egg_buffer_add_uint32 (&cdata->buffer, 0);
+ break;
+ default:
+ g_message ("received unsupported request operation on control socket: %d", (int)op);
+ break;
+ }
+
+ if (cdata) {
+ g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, G_IO_OUT | G_IO_HUP,
+ control_output, cdata, control_data_free);
+ }
+}
+
+static gboolean
+control_input (GIOChannel *channel, GIOCondition cond, gpointer user_data)
+{
+ ControlData *cdata = user_data;
+ EggBuffer *buffer = &cdata->buffer;
+ guint32 packet_size = 0;
+ gboolean finished = FALSE;
+ int fd, res;
+ pid_t pid;
+ uid_t uid;
+
+ fd = g_io_channel_unix_get_fd (channel);
+
+ if (cond & G_IO_IN) {
+
+ /* Time for reading credentials */
+ if (cdata->position == 0) {
+ if (egg_unix_credentials_read (fd, &pid, &uid) < 0) {
+ if (errno != EAGAIN || errno != EINTR)
+ finished = TRUE;
+ } else if (getuid () != uid) {
+ g_warning ("uid mismatch: %u, should be %u\n", uid, getuid ());
+ finished = TRUE;
+ } else {
+ cdata->position = 1;
+ }
+
+ /* Time for reading a packet size */
+ } else if (egg_buffer_length (buffer) < 4) {
+ egg_buffer_reserve (buffer, 4);
+ res = read (fd, buffer->buf + buffer->len, 4 - buffer->len);
+ if (res <= 0) {
+ if (errno != EAGAIN || errno != EINTR)
+ finished = TRUE;
+ } else {
+ buffer->len += res;
+ }
+
+ /* Time for reading the packet */
+ } else {
+ if (!egg_buffer_get_uint32 (buffer, 0, NULL, &packet_size) || packet_size < 4) {
+ g_warning ("invalid packet size from client");
+ finished = TRUE;
+ } else {
+ g_assert (buffer->len < packet_size);
+ egg_buffer_reserve (buffer, packet_size);
+ res = read (fd, buffer->buf + buffer->len, packet_size - buffer->len);
+ if (res <= 0) {
+ if (errno != EAGAIN && errno != EINTR)
+ finished = TRUE;
+ } else {
+ buffer->len += res;
+ g_assert (buffer->len <= packet_size);
+ }
+ }
+ }
+
+ /* Received a full packet, process */
+ if (packet_size && buffer->len == packet_size) {
+ control_process (buffer, channel);
+ finished = TRUE;
+ }
+ }
+
+ if (finished)
+ cond |= G_IO_HUP;
+
+ return (cond & G_IO_HUP) == 0;
+}
+
+static gboolean
+control_accept (GIOChannel *channel, GIOCondition cond, gpointer callback_data)
+{
+ struct sockaddr_un addr;
+ socklen_t addrlen;
+ ControlData *cdata;
+ GIOChannel *new_channel;
+ int fd, new_fd;
+ int val;
+
+ fd = g_io_channel_unix_get_fd (channel);
+
+ addrlen = sizeof (addr);
+ new_fd = accept (fd, (struct sockaddr *) &addr, &addrlen);
+ if (new_fd < 0) {
+ g_warning ("couldn't accept new connection: %s", g_strerror (errno));
+ return TRUE;
+ }
+
+ val = fcntl (new_fd, F_GETFL, 0);
+ if (val < 0) {
+ g_warning ("can't get client fd flags: %s", g_strerror (errno));
+ close (new_fd);
+ return TRUE;
+ }
+
+ if (fcntl (new_fd, F_SETFL, val | O_NONBLOCK) < 0) {
+ g_warning ("can't set client to non-blocking io: %s", g_strerror (errno));
+ close (new_fd);
+ return TRUE;
+ }
+
+ cdata = control_data_new ();
+ new_channel = g_io_channel_unix_new (new_fd);
+ g_io_channel_set_close_on_unref (new_channel, TRUE);
+ g_io_add_watch_full (new_channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP,
+ control_input, cdata, control_data_free);
+ g_io_channel_unref (new_channel);
+
+ return TRUE;
+}
+
+static void
+control_cleanup_channel (gpointer user_data)
+{
+ gchar *path = user_data;
+ unlink (path);
+ g_free (path);
+}
+
+gboolean
+gkd_control_initialize (const gchar *directory)
+{
+ struct sockaddr_un addr;
+ GIOChannel *channel;
+ gchar *path;
+ int sock;
+
+ path = g_strdup_printf ("%s/socket", directory);
+ egg_cleanup_register (control_cleanup_channel, path);
+
+#ifdef WITH_TESTS
+ if (g_getenv ("GNOME_KEYRING_TEST_PATH"))
+ unlink (path);
+#endif
+
+ sock = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ g_warning ("couldn't open socket: %s", g_strerror (errno));
+ return FALSE;
+ }
+
+ memset (&addr, 0, sizeof (addr));
+ addr.sun_family = AF_UNIX;
+ g_strlcpy (addr.sun_path, path, sizeof (addr.sun_path));
+ if (bind (sock, (struct sockaddr*) &addr, sizeof (addr)) < 0) {
+ g_warning ("couldn't bind to socket: %s: %s", path, g_strerror (errno));
+ close (sock);
+ return FALSE;
+ }
+
+ if (listen (sock, 128) < 0) {
+ g_warning ("couldn't listen on socket: %s: %s", path, g_strerror (errno));
+ close (sock);
+ return FALSE;
+ }
+
+ if (!egg_unix_credentials_setup (sock)) {
+ close (sock);
+ return FALSE;
+ }
+
+ channel = g_io_channel_unix_new (sock);
+ g_io_add_watch (channel, G_IO_IN | G_IO_HUP, control_accept, NULL);
+ g_io_channel_set_close_on_unref (channel, TRUE);
+ egg_cleanup_register ((GDestroyNotify)g_io_channel_unref, channel);
+
+ return TRUE;
+}
diff --git a/daemon/control/gkd-control.h b/daemon/control/gkd-control.h
new file mode 100644
index 0000000..09c1aa4
--- /dev/null
+++ b/daemon/control/gkd-control.h
@@ -0,0 +1,29 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GKD_CONTROL_H__
+#define __GKD_CONTROL_H__
+
+#include <glib.h>
+
+gboolean gkd_control_initialize (const gchar *directory);
+
+#endif /* __GKD_CONTROL_H__ */
diff --git a/egg/egg-unix-credentials.c b/egg/egg-unix-credentials.c
index 13b2048..2af0c1b 100644
--- a/egg/egg-unix-credentials.c
+++ b/egg/egg-unix-credentials.c
@@ -208,6 +208,20 @@ again:
return 0;
}
+int
+egg_unix_credentials_setup (int sock)
+{
+ int retval = 0;
+#if defined(LOCAL_CREDS) && !defined(HAVE_CMSGCRED)
+ int val = 1;
+ if (setsockopt (sock, 0, LOCAL_CREDS, &val, sizeof (val)) < 0) {
+ fprintf (stderr, "unable to set LOCAL_CREDS socket option on fd %d\n", fd);
+ retval = -1;
+ }
+#endif
+ return retval;
+}
+
char*
egg_unix_credentials_executable (pid_t pid)
{
diff --git a/egg/egg-unix-credentials.h b/egg/egg-unix-credentials.h
index a389c1e..666e42e 100644
--- a/egg/egg-unix-credentials.h
+++ b/egg/egg-unix-credentials.h
@@ -25,10 +25,14 @@
#include <unistd.h>
-int egg_unix_credentials_read (int sock, pid_t *pid, uid_t *uid);
+int egg_unix_credentials_read (int sock,
+ pid_t *pid,
+ uid_t *uid);
-int egg_unix_credentials_write (int sock);
-
-char* egg_unix_credentials_executable (pid_t pid);
+int egg_unix_credentials_write (int sock);
+
+int egg_unix_credentials_setup (int sock);
+
+char* egg_unix_credentials_executable (pid_t pid);
#endif /*EGGUNIXCREDENTIALS_H_*/
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]