diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | README.pam | 10 | ||||
-rw-r--r-- | configure.ac | 20 | ||||
-rw-r--r-- | pam/Makefile.am | 7 | ||||
-rw-r--r-- | pam/pam_dconf.c | 187 | ||||
-rw-r--r-- | 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 |