diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 12:01:37 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 12:01:37 +0000 |
commit | de848d9e9146434817c65d74d1d0313e9d729462 (patch) | |
tree | dcbd0efb229b17f696f7195671f05b354b4f70fc /modules/pam_mkhomedir/pam_mkhomedir.c | |
parent | Initial commit. (diff) | |
download | pam-upstream.tar.xz pam-upstream.zip |
Adding upstream version 1.4.0.upstream/1.4.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/pam_mkhomedir/pam_mkhomedir.c')
-rw-r--r-- | modules/pam_mkhomedir/pam_mkhomedir.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/modules/pam_mkhomedir/pam_mkhomedir.c b/modules/pam_mkhomedir/pam_mkhomedir.c new file mode 100644 index 0000000..cb773e8 --- /dev/null +++ b/modules/pam_mkhomedir/pam_mkhomedir.c @@ -0,0 +1,232 @@ +/* PAM Make Home Dir module + + This module will create a users home directory if it does not exist + when the session begins. This allows users to be present in central + database (such as nis, kerb or ldap) without using a distributed + file system or pre-creating a large number of directories. + + Here is a sample /etc/pam.d/login file for Debian GNU/Linux + 2.1: + + auth requisite pam_securetty.so + auth sufficient pam_ldap.so + auth required pam_unix.so + auth optional pam_group.so + auth optional pam_mail.so + account requisite pam_time.so + account sufficient pam_ldap.so + account required pam_unix.so + session required pam_mkhomedir.so skel=/etc/skel/ umask=0022 + session required pam_unix.so + session optional pam_lastlog.so + password required pam_unix.so + + Released under the GNU LGPL version 2 or later + Copyright (c) Red Hat, Inc. 2009 + Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999 + Structure taken from pam_lastlogin by Andrew Morgan + <morgan@parc.power.net> 1996 + */ + +#include "config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <unistd.h> +#include <pwd.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <signal.h> + +#include <security/pam_modules.h> +#include <security/_pam_macros.h> +#include <security/pam_modutil.h> +#include <security/pam_ext.h> + +#include "pam_cc_compat.h" +#include "pam_inline.h" + +/* argument parsing */ +#define MKHOMEDIR_DEBUG 020 /* be verbose about things */ +#define MKHOMEDIR_QUIET 040 /* keep quiet about things */ + +struct options_t { + int ctrl; + const char *umask; + const char *skeldir; +}; +typedef struct options_t options_t; + +static void +_pam_parse (const pam_handle_t *pamh, int flags, int argc, const char **argv, + options_t *opt) +{ + opt->ctrl = 0; + opt->umask = "0022"; + opt->skeldir = "/etc/skel"; + + /* does the application require quiet? */ + if ((flags & PAM_SILENT) == PAM_SILENT) + opt->ctrl |= MKHOMEDIR_QUIET; + + /* step through arguments */ + for (; argc-- > 0; ++argv) + { + const char *str; + + if (!strcmp(*argv, "silent")) { + opt->ctrl |= MKHOMEDIR_QUIET; + } else if (!strcmp(*argv, "debug")) { + opt->ctrl |= MKHOMEDIR_DEBUG; + } else if ((str = pam_str_skip_prefix(*argv, "umask=")) != NULL) { + opt->umask = str; + } else if ((str = pam_str_skip_prefix(*argv, "skel=")) != NULL) { + opt->skeldir = str; + } else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } +} + +/* Do the actual work of creating a home dir */ +static int +create_homedir (pam_handle_t *pamh, options_t *opt, + const char *user, const char *dir) +{ + int retval, child; + struct sigaction newsa, oldsa; + + /* Mention what is happening, if the notification fails that is OK */ + if (!(opt->ctrl & MKHOMEDIR_QUIET)) + pam_info(pamh, _("Creating directory '%s'."), dir); + + + D(("called.")); + + /* + * This code arranges that the demise of the child does not cause + * the application to receive a signal it is not expecting - which + * may kill the application or worse. + */ + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &newsa, &oldsa); + + if (opt->ctrl & MKHOMEDIR_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "Executing mkhomedir_helper."); + } + + /* fork */ + child = fork(); + if (child == 0) { + static char *envp[] = { NULL }; + const char *args[] = { NULL, NULL, NULL, NULL, NULL }; + + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD) < 0) + _exit(PAM_SYSTEM_ERR); + + /* exec the mkhomedir helper */ + args[0] = MKHOMEDIR_HELPER; + args[1] = user; + args[2] = opt->umask; + args[3] = opt->skeldir; + + DIAG_PUSH_IGNORE_CAST_QUAL; + execve(MKHOMEDIR_HELPER, (char **)args, envp); + DIAG_POP_IGNORE_CAST_QUAL; + + /* should not get here: exit with error */ + D(("helper binary is not available")); + _exit(PAM_SYSTEM_ERR); + } else if (child > 0) { + int rc; + while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR); + if (rc < 0) { + pam_syslog(pamh, LOG_ERR, "waitpid failed: %m"); + retval = PAM_SYSTEM_ERR; + } else if (!WIFEXITED(retval)) { + pam_syslog(pamh, LOG_ERR, "mkhomedir_helper abnormal exit: %d", retval); + retval = PAM_SYSTEM_ERR; + } else { + retval = WEXITSTATUS(retval); + } + } else { + D(("fork failed")); + pam_syslog(pamh, LOG_ERR, "fork failed: %m"); + retval = PAM_SYSTEM_ERR; + } + + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + + if (opt->ctrl & MKHOMEDIR_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "mkhomedir_helper returned %d", retval); + } + + if (retval != PAM_SUCCESS && !(opt->ctrl & MKHOMEDIR_QUIET)) { + pam_error(pamh, _("Unable to create and initialize directory '%s'."), + dir); + } + + D(("returning %d", retval)); + return retval; +} + +/* --- authentication management functions (only) --- */ + +int +pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + int retval; + options_t opt; + const void *user; + const struct passwd *pwd; + struct stat St; + + /* Parse the flag values */ + _pam_parse(pamh, flags, argc, argv, &opt); + + /* Determine the user name so we can get the home directory */ + retval = pam_get_item(pamh, PAM_USER, &user); + if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0') + { + pam_syslog(pamh, LOG_NOTICE, "Cannot obtain the user name."); + return PAM_USER_UNKNOWN; + } + + /* Get the password entry */ + pwd = pam_modutil_getpwnam (pamh, user); + if (pwd == NULL) + { + pam_syslog(pamh, LOG_NOTICE, "User unknown."); + D(("couldn't identify user %s", user)); + return PAM_USER_UNKNOWN; + } + + /* Stat the home directory, if something exists then we assume it is + correct and return a success*/ + if (stat(pwd->pw_dir, &St) == 0) { + if (opt.ctrl & MKHOMEDIR_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "Home directory %s already exists.", + pwd->pw_dir); + } + return PAM_SUCCESS; + } + + return create_homedir(pamh, &opt, user, pwd->pw_dir); +} + +/* Ignore */ +int pam_sm_close_session (pam_handle_t * pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} |