[dconf/wip/peruserprofile: 1/5] pam: implementation of a pam module to find locate and link a profile for a specific user from XDG_D



commit 6dfbac1e605ee1fafeb4fbfde4c09c9ee2eee359
Author: Alberto Ruiz <aruiz redhat com>
Date:   Tue Jul 14 18:18:44 2015 +0100

    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

 Makefile.am     |    2 +-
 README.pam      |   10 +++
 configure.ac    |   20 ++++++
 pam/Makefile.am |    7 ++
 pam/pam_dconf.c |  187 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 pam/pam_dconf.h |   44 +++++++++++++
 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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]