summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlberto Ruiz <aruiz@redhat.com>2015-07-14 18:18:44 +0100
committerAlberto Ruiz <aruiz@redhat.com>2015-10-09 13:13:37 +0100
commit6dfbac1e605ee1fafeb4fbfde4c09c9ee2eee359 (patch)
treea479771cde2f9ac69fac0b4a9da3b08b7f17685c
parent7c0beccf286a1dbd396fda7d40b438598a6ce7f4 (diff)
downloaddconf-6dfbac1e605ee1fafeb4fbfde4c09c9ee2eee359.tar.gz
pam: implementation of a pam module to find locate and link a profile for a specific user from XDG_DATA_DIRS onto XDG_RUNTIME_DIR
-rw-r--r--Makefile.am2
-rw-r--r--README.pam10
-rw-r--r--configure.ac20
-rw-r--r--pam/Makefile.am7
-rw-r--r--pam/pam_dconf.c187
-rw-r--r--pam/pam_dconf.h44
6 files changed, 269 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index 83cd492..f6221ce 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,7 @@ include Makefile.gtester
ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = shm gvdb common engine service gdbus gsettings dbus-1 client bin docs tests
+SUBDIRS = shm gvdb common engine service gdbus gsettings dbus-1 client pam bin docs tests
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc
EXTRA_DIST = trim-lcov.py m4
diff --git a/README.pam b/README.pam
new file mode 100644
index 0000000..067f6c2
--- /dev/null
+++ b/README.pam
@@ -0,0 +1,10 @@
+dconf brings an optional pam module that creates a dconf.profile symlink in
+XDG_RUNTIME_DIR pointing to a specific dconf profile for that user. This
+module is affected by changes in XDG_DATA_DIRS which holds the paths where
+the profile files are checked from.
+
+System integrators should make sure that this module should be loaded after
+XDG_RUNTIME_DIR is created and XDG_DATA_DIRS is set. On most modern Linux
+systems this means loading after pam_systemd and pam_env have been loaded.
+Some systems might not use systemd and XDG_RUNTIME_DIR might be set and
+created by some other module.
diff --git a/configure.ac b/configure.ac
index 60f78ba..c31e150 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,6 +62,25 @@ AC_SUBST(dconfincludedir, ${includedir}/dconf)
AC_PATH_PROG(gio_QUERYMODULES, gio-querymodules, no)
+dnl PAM support
+AC_ARG_ENABLE(pam,
+ AC_HELP_STRING([--disable-pam],
+ [Build dconf PAM helper]))
+if test "$enable_pam" != "no"; then
+ AC_CHECK_HEADERS(security/pam_modules.h pam/pam_modules.h, [have_pam=yes; break], have_pam=no)
+ if test "$have_pam" = "no"; then
+ AC_MSG_ERROR(The PAM headers are missing)
+ fi
+fi
+
+AC_ARG_WITH([pam-dir],
+ [AC_HELP_STRING([--with-pam-dir=DIR],
+ [directory to install pam modules in])],
+ [], [with_pam_dir='${libdir}/security'])
+PAM_DEST_DIR="$with_pam_dir"
+AC_SUBST(PAM_DEST_DIR)
+
+dnl gcov support
AC_ARG_ENABLE(gcov,
AC_HELP_STRING([--enable-gcov],
[enable generation of code coverage information]))
@@ -89,6 +108,7 @@ AC_CONFIG_FILES([
service/Makefile
dbus-1/Makefile
bin/Makefile
+ pam/Makefile
tests/Makefile
docs/Makefile
Makefile
diff --git a/pam/Makefile.am b/pam/Makefile.am
new file mode 100644
index 0000000..01d2097
--- /dev/null
+++ b/pam/Makefile.am
@@ -0,0 +1,7 @@
+pamlibdir = $(PAM_DEST_DIR)
+pamlib_PROGRAMS = pam_dconf.so
+
+pam_dconf_so_SOURCES = \
+ pam_dconf.c \
+ pam_dconf.h
+pam_dconf_so_CFLAGS = -fPIC -DPIC -shared -Wall
diff --git a/pam/pam_dconf.c b/pam/pam_dconf.c
new file mode 100644
index 0000000..51ecdbb
--- /dev/null
+++ b/pam/pam_dconf.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright © 2012 Canonical Limited
+ * Copyright © 2015 Red Hat Inc.
+ *
+ * This library 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 of the licence, or (at your option) any later version.
+ *
+ * This library 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 library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Ryan Lortie <desrt@desrt.ca>
+ * Alberto Ruiz <aruiz@redhat.com>
+ */
+
+#include "pam_dconf.h"
+
+static char*
+join_strings (pam_handle_t *pamh,
+ const char *base,
+ const char *suffix,
+ const char *file)
+{
+ char *result;
+
+ result = (char*)malloc (sizeof (char)*(strlen (base) + strlen (suffix) + strlen (file) + 1));
+ if (result == NULL)
+ {
+ pam_syslog (pamh, LOG_ERR, "Could not allocate memory");
+ return NULL;
+ }
+
+ if (sprintf (result, "%s%s%s", base, suffix, file) < 0)
+ {
+ pam_syslog (pamh, LOG_ERR, "There was an error calling sprintf");
+ free (result);
+ return NULL;
+ }
+ return result;
+}
+
+static char*
+username_profile_name (pam_handle_t *pamh)
+{
+ const char *user;
+ int ret;
+
+ ret = pam_get_user (pamh, &user, "");
+ if (ret != PAM_SUCCESS)
+ {
+ pam_syslog (pamh, LOG_ERR, "Could not get username");
+ return NULL;
+ }
+
+ return join_strings (pamh, user, DCONF_PROFILE_SUFFIX, "");
+}
+
+static char*
+find_file_in_dir (pam_handle_t *pamh,
+ const char *basedir,
+ const char *dconfdir,
+ const char *filename)
+{
+ char *file_full_path;
+
+ file_full_path = join_strings (pamh, basedir, dconfdir, filename);
+ if (access (file_full_path, F_OK) != -1)
+ return file_full_path;
+
+ free (file_full_path);
+ return NULL;
+}
+
+static char*
+get_dconf_profile_path (pam_handle_t *pamh)
+{
+ char *dirs = NULL;
+ char *result = NULL;
+ char *filename = NULL;
+ char *dir = NULL;
+
+ /* Find a $USERNAME.profile */
+ filename = username_profile_name (pamh);
+ if (filename == NULL)
+ return NULL;
+
+ /* We search for a profile in the default dconf path first */
+ result = find_file_in_dir (pamh, DCONF_DEFAULT_DATA_DIR, DCONF_PROFILE_DIR, filename);
+ if (result != NULL)
+ goto out;
+
+ if (pam_getenv (pamh, "XDG_DATA_DIRS") != NULL)
+ dirs = strdup (pam_getenv (pamh, "XDG_DATA_DIRS"));
+ else
+ dirs = strdup ("/usr/local/share:/usr/share");
+
+ if (dirs == NULL)
+ {
+ pam_syslog (pamh, LOG_ERR, "Could not allocate memory");
+ goto out;
+ }
+
+ for (dir = strtok (dirs, ":"); dir; dir = strtok (NULL, ":"))
+ {
+ /* empty strings or relative paths are forbidden as per spec */
+ if ((strlen (dir) < 1) || dir[0] != '/')
+ continue;
+
+ /* If we find a candidate we exit the loop */
+ result = find_file_in_dir (pamh, dir, DCONF_PROFILE_DIR, filename);
+ if (result)
+ break;
+ }
+
+ if (result == NULL)
+ pam_syslog (pamh, LOG_DEBUG, "Could not find a dconf profile candidate for this user");
+
+ free (dirs);
+out:
+ free (filename);
+ return result;
+}
+
+PAM_EXTERN int
+pam_sm_open_session (pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv)
+{
+ const char *runtime_dir_path;
+ char *dconf_profile_path;
+ char *symlink_path;
+ bool success = 0;
+
+ runtime_dir_path = pam_getenv (pamh, "XDG_RUNTIME_DIR");
+
+ if (runtime_dir_path == NULL)
+ {
+ pam_syslog (pamh, LOG_NOTICE, "XDG_RUNTIME_DIR has not been set yet. Cannot set up dconf profile.");
+ return PAM_IGNORE;
+ }
+
+ dconf_profile_path = get_dconf_profile_path (pamh);
+ if (dconf_profile_path == NULL)
+ {
+ pam_syslog (pamh, LOG_NOTICE, "Could not find a dconf profile");
+ return PAM_IGNORE;
+ }
+
+ symlink_path = join_strings (pamh,
+ runtime_dir_path,
+ "/",
+ DCONF_PROFILE_LINK);
+ if (symlink_path == NULL)
+ {
+ free (dconf_profile_path);
+ return PAM_IGNORE;
+ }
+
+ unlink (symlink_path);
+ success = symlink (dconf_profile_path, symlink_path) == 0;
+ if (!success)
+ {
+ int saved_errno = errno;
+ pam_syslog (pamh, LOG_NOTICE, "failed to create symlink for dconf profile in XDG_RUNTIME_DIR");
+ pam_syslog (pamh, LOG_NOTICE, strerror (saved_errno));
+ }
+
+ free (dconf_profile_path);
+ free (symlink_path);
+ return success? PAM_SUCCESS : PAM_IGNORE;
+}
+
+PAM_EXTERN int
+pam_sm_close_session (pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv)
+{
+ return PAM_SUCCESS;
+}
diff --git a/pam/pam_dconf.h b/pam/pam_dconf.h
new file mode 100644
index 0000000..7074a36
--- /dev/null
+++ b/pam/pam_dconf.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2015 Red Hat Limited
+ *
+ * This library 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 of the licence, or (at your option) any later version.
+ *
+ * This library 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 library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Alberto Ruiz <aruiz@redhat.com>
+ */
+
+#ifndef __pam_dconf_h__
+#define __pam_dconf_h__
+
+#define PAM_SM_SESSION
+
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#define DCONF_PROFILE_DIR "/dconf/profile/"
+#define DCONF_PROFILE_SUFFIX ".profile"
+#define DCONF_PROFILE_LINK "dconf.profile"
+#define DCONF_DEFAULT_DATA_DIR "/etc"
+
+
+#endif