X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=modules%2Fpam_mkhomedir%2Fpam_mkhomedir.c;h=c9220897508e821e83fba5ab4c0e1792131b7841;hb=b0ec5d1e472a0cd74972bfe9575dcf6a3d0cad1c;hp=ec05993dfa1e2be8e11d6326d2392eccd045c95e;hpb=ea488580c42e8918445a945484de3c8a5addc761;p=linux-pam diff --git a/modules/pam_mkhomedir/pam_mkhomedir.c b/modules/pam_mkhomedir/pam_mkhomedir.c index ec05993d..c9220897 100644 --- a/modules/pam_mkhomedir/pam_mkhomedir.c +++ b/modules/pam_mkhomedir/pam_mkhomedir.c @@ -4,41 +4,45 @@ 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_pwdb.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_pwdb.so + account required pam_unix.so session required pam_mkhomedir.so skel=/etc/skel/ umask=0022 - session required pam_pwdb.so + session required pam_unix.so session optional pam_lastlog.so - password required pam_pwdb.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 Feb 1999 - Structure taken from pam_lastlogin by Andrew Morgan + Structure taken from pam_lastlogin by Andrew Morgan 1996 */ -/* I want snprintf dammit */ -#define _GNU_SOURCE 1 -#include +#include "config.h" + #include #include -#include +#include +#include +#include #include #include +#include #include #include #include -#include +#include +#include /* * here, we make a definition for the externally accessible function @@ -51,304 +55,181 @@ #include #include +#include +#include /* argument parsing */ -#define MKHOMEDIR_DEBUG 020 /* keep quiet about things */ +#define MKHOMEDIR_DEBUG 020 /* be verbose about things */ #define MKHOMEDIR_QUIET 040 /* keep quiet about things */ -static unsigned int UMask = 0022; -static char SkelDir[BUFSIZ] = "/etc/skel"; - -/* some syslogging */ -static void _log_err(int err, const char *format, ...) -{ - va_list args; - - va_start(args, format); - openlog("PAM-mkhomedir", LOG_CONS|LOG_PID, LOG_AUTH); - vsyslog(err, format, args); - va_end(args); - closelog(); -} +struct options_t { + int ctrl; + const char *umask; + const char *skeldir; +}; +typedef struct options_t options_t; -static int _pam_parse(int flags, int argc, const char **argv) +static void +_pam_parse (const pam_handle_t *pamh, int flags, int argc, const char **argv, + options_t *opt) { - int ctrl = 0; + opt->ctrl = 0; + opt->umask = "0022"; + opt->skeldir = "/etc/skel"; /* does the appliction require quiet? */ if ((flags & PAM_SILENT) == PAM_SILENT) - ctrl |= MKHOMEDIR_QUIET; + opt->ctrl |= MKHOMEDIR_QUIET; /* step through arguments */ for (; argc-- > 0; ++argv) { - if (!strcmp(*argv, "silent")) - { - ctrl |= MKHOMEDIR_QUIET; - } - else if (!strncmp(*argv,"umask=",6)) - UMask = strtol(*argv+6,0,0); - else if (!strncmp(*argv,"skel=",5)) - strcpy(SkelDir,*argv+5); - else - { - _log_err(LOG_ERR, "unknown option; %s", *argv); + if (!strcmp(*argv, "silent")) { + opt->ctrl |= MKHOMEDIR_QUIET; + } else if (!strcmp(*argv, "debug")) { + opt->ctrl |= MKHOMEDIR_DEBUG; + } else if (!strncmp(*argv,"umask=",6)) { + opt->umask = *argv+6; + } else if (!strncmp(*argv,"skel=",5)) { + opt->skeldir = *argv+5; + } else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); } } - - D(("ctrl = %o", ctrl)); - return ctrl; } -/* This common function is used to send a message to the applications - conversion function. Our only use is to ask the application to print - an informative message that we are creating a home directory */ -static int converse(pam_handle_t * pamh, int ctrl, int nargs - ,struct pam_message **message - ,struct pam_response **response) +/* 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; - struct pam_conv *conv; + int retval, child; + struct sigaction newsa, oldsa; - D(("begin to converse")); - - retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv); - if (retval == PAM_SUCCESS) - { + /* Mention what is happening, if the notification fails that is OK */ + if (!(opt->ctrl & MKHOMEDIR_QUIET)) + pam_info(pamh, _("Creating directory '%s'."), dir); - retval = conv->conv(nargs, (const struct pam_message **) message - ,response, conv->appdata_ptr); - D(("returned from application's conversation function")); + D(("called.")); - if (retval != PAM_SUCCESS && (ctrl & MKHOMEDIR_DEBUG)) - { - _log_err(LOG_DEBUG, "conversation failure [%s]" - ,pam_strerror(pamh, retval)); - } + /* + * 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."); } - else - { - _log_err(LOG_ERR, "couldn't obtain coversation function [%s]" - ,pam_strerror(pamh, retval)); - } - - D(("ready to return from module conversation")); - return retval; /* propagate error status */ -} - -/* Ask the application to display a short text string for us. */ -static int make_remark(pam_handle_t * pamh, int ctrl, const char *remark) -{ - int retval; - - if ((ctrl & MKHOMEDIR_QUIET) != MKHOMEDIR_QUIET) - { - struct pam_message msg[1], *mesg[1]; - struct pam_response *resp = NULL; - - mesg[0] = &msg[0]; - msg[0].msg_style = PAM_TEXT_INFO; - msg[0].msg = remark; - - retval = converse(pamh, ctrl, 1, mesg, &resp); - - msg[0].msg = NULL; - if (resp) - { - _pam_drop_reply(resp, 1); - } - } - else - { - D(("keeping quiet")); - retval = PAM_SUCCESS; + /* 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; + + execve(MKHOMEDIR_HELPER, (char *const *) args, envp); + + /* 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; } - D(("returning %s", pam_strerror(pamh, retval))); - return retval; -} + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ -/* Do the actual work of creating a home dir */ -static int create_homedir(pam_handle_t * pamh, int ctrl, - const struct passwd *pwd) -{ - char *remark; - DIR *D; - struct dirent *Dir; - - /* Some scratch space */ - remark = malloc(BUFSIZ); - if (remark == NULL) - { - D(("no memory for last login remark")); - return PAM_BUF_ERR; + if (opt->ctrl & MKHOMEDIR_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "mkhomedir_helper returned %d", retval); } - /* Mention what is happening, if the notification fails that is OK */ - if (snprintf(remark,BUFSIZ,"Creating home directory '%s'.", - pwd->pw_dir) == -1) - return PAM_PERM_DENIED; - - make_remark(pamh, ctrl, remark); - - /* Crete the home directory */ - if (mkdir(pwd->pw_dir,0700) != 0) - { - free(remark); - _log_err(LOG_DEBUG, "unable to create home directory %s",pwd->pw_dir); - return PAM_PERM_DENIED; - } - if (chmod(pwd->pw_dir,0777 & (~UMask)) != 0 || - chown(pwd->pw_dir,pwd->pw_uid,pwd->pw_gid) != 0) - { - free(remark); - _log_err(LOG_DEBUG, "unable to chance perms on home directory %s",pwd->pw_dir); - return PAM_PERM_DENIED; - } - - /* See if we need to copy the skel dir over. */ - if (SkelDir[0] == 0) - { - free(remark); - return PAM_SUCCESS; - } - - /* Scan the directory */ - D = opendir(SkelDir); - if (D == 0) - { - free(remark); - _log_err(LOG_DEBUG, "unable to read directory %s",SkelDir); - return PAM_PERM_DENIED; + if (retval != PAM_SUCCESS && !(opt->ctrl & MKHOMEDIR_QUIET)) { + pam_error(pamh, _("Unable to create and initialize directory '%s'."), + dir); } - - for (Dir = readdir(D); Dir != 0; Dir = readdir(D)) - { - int SrcFd; - int DestFd; - int Res; - struct stat St; - - /* Skip some files.. */ - if (strcmp(Dir->d_name,".") == 0 || - strcmp(Dir->d_name,"..") == 0) - continue; - - /* Check if it is a directory */ - snprintf(remark,BUFSIZ,"%s/%s",SkelDir,Dir->d_name); - if (stat(remark,&St) != 0) - continue; - if (S_ISDIR(St.st_mode)) - { - snprintf(remark,BUFSIZ,"%s/%s",pwd->pw_dir,Dir->d_name); - if (mkdir(remark,(St.st_mode | 0222) & (~UMask)) != 0 || - chmod(remark,(St.st_mode | 0222) & (~UMask)) != 0 || - chown(remark,pwd->pw_uid,pwd->pw_gid) != 0) - { - free(remark); - _log_err(LOG_DEBUG, "unable to change perms on copy %s",remark); - return PAM_PERM_DENIED; - } - continue; - } - - /* Open the source file */ - if ((SrcFd = open(remark,O_RDONLY)) < 0 || fstat(SrcFd,&St) != 0) - { - free(remark); - _log_err(LOG_DEBUG, "unable to open src file %s",remark); - return PAM_PERM_DENIED; - } - stat(remark,&St); - - /* Open the dest file */ - snprintf(remark,BUFSIZ,"%s/%s",pwd->pw_dir,Dir->d_name); - if ((DestFd = open(remark,O_WRONLY | O_TRUNC | O_CREAT,0600)) < 0) - { - close(SrcFd); - free(remark); - _log_err(LOG_DEBUG, "unable to open dest file %s",remark); - return PAM_PERM_DENIED; - } - /* Set the proper ownership and permissions for the module. We make - the file a+w and then mask it with the set mask. This preseves - execute bits */ - if (fchmod(DestFd,(St.st_mode | 0222) & (~UMask)) != 0 || - fchown(DestFd,pwd->pw_uid,pwd->pw_gid) != 0) - { - free(remark); - _log_err(LOG_DEBUG, "unable to chang perms on copy %s",remark); - return PAM_PERM_DENIED; - } - - /* Copy the file */ - do - { - Res = read(SrcFd,remark,BUFSIZ); - if (Res < 0 || write(DestFd,remark,Res) != Res) - { - close(SrcFd); - close(DestFd); - free(remark); - _log_err(LOG_DEBUG, "unable to perform IO"); - return PAM_PERM_DENIED; - } - } - while (Res != 0); - close(SrcFd); - close(DestFd); - } - - free(remark); - return PAM_SUCCESS; + D(("returning %d", retval)); + return retval; } /* --- authentication management functions (only) --- */ -PAM_EXTERN -int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc - ,const char **argv) +PAM_EXTERN int +pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, + const char **argv) { - int retval, ctrl; - const char *user; + int retval; + options_t opt; + const void *user; const struct passwd *pwd; struct stat St; - + /* Parse the flag values */ - ctrl = _pam_parse(flags, argc, argv); + _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, (const void **) &user); - if (retval != PAM_SUCCESS || user == NULL || *user == '\0') + retval = pam_get_item(pamh, PAM_USER, &user); + if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0') { - _log_err(LOG_NOTICE, "user unknown"); + pam_syslog(pamh, LOG_NOTICE, "Cannot obtain the user name."); return PAM_USER_UNKNOWN; } /* Get the password entry */ - pwd = getpwnam(user); + 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_CRED_INSUFFICIENT; } /* 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 (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,ctrl,pwd); + return create_homedir(pamh, &opt, user, pwd->pw_dir); } /* Ignore */ -PAM_EXTERN -int pam_sm_close_session(pam_handle_t * pamh, int flags, int argc - ,const char **argv) +PAM_EXTERN +int pam_sm_close_session (pam_handle_t * pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) { return PAM_SUCCESS; }