summaryrefslogtreecommitdiff
path: root/pam/pam_dconf.c
diff options
context:
space:
mode:
Diffstat (limited to 'pam/pam_dconf.c')
-rw-r--r--pam/pam_dconf.c187
1 files changed, 187 insertions, 0 deletions
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;
+}