summaryrefslogtreecommitdiff
path: root/modules/pam_mkhomedir/pam_mkhomedir.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_mkhomedir/pam_mkhomedir.c')
-rw-r--r--modules/pam_mkhomedir/pam_mkhomedir.c370
1 files changed, 370 insertions, 0 deletions
diff --git a/modules/pam_mkhomedir/pam_mkhomedir.c b/modules/pam_mkhomedir/pam_mkhomedir.c
new file mode 100644
index 00000000..ec05993d
--- /dev/null
+++ b/modules/pam_mkhomedir/pam_mkhomedir.c
@@ -0,0 +1,370 @@
+/* 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_pwdb.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
+ session required pam_mkhomedir.so skel=/etc/skel/ umask=0022
+ session required pam_pwdb.so
+ session optional pam_lastlog.so
+ password required pam_pwdb.so
+
+ Released under the GNU LGPL version 2 or later
+ Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999
+ Structure taken from pam_lastlogin by Andrew Morgan
+ <morgan@parc.power.net> 1996
+ */
+
+/* I want snprintf dammit */
+#define _GNU_SOURCE 1
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_SESSION
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* argument parsing */
+#define MKHOMEDIR_DEBUG 020 /* keep quiet 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();
+}
+
+static int _pam_parse(int flags, int argc, const char **argv)
+{
+ int ctrl = 0;
+
+ /* does the appliction require quiet? */
+ if ((flags & PAM_SILENT) == PAM_SILENT)
+ 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);
+ }
+ }
+
+ 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)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("begin to converse"));
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+ if (retval == PAM_SUCCESS)
+ {
+
+ retval = conv->conv(nargs, (const struct pam_message **) message
+ ,response, conv->appdata_ptr);
+
+ D(("returned from application's conversation function"));
+
+ if (retval != PAM_SUCCESS && (ctrl & MKHOMEDIR_DEBUG))
+ {
+ _log_err(LOG_DEBUG, "conversation failure [%s]"
+ ,pam_strerror(pamh, retval));
+ }
+
+ }
+ 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;
+ }
+
+ D(("returning %s", pam_strerror(pamh, retval)));
+ return retval;
+}
+
+/* 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;
+ }
+
+ /* 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;
+ }
+
+ 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;
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc
+ ,const char **argv)
+{
+ int retval, ctrl;
+ const char *user;
+ const struct passwd *pwd;
+ struct stat St;
+
+ /* Parse the flag values */
+ ctrl = _pam_parse(flags, argc, argv);
+
+ /* 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')
+ {
+ _log_err(LOG_NOTICE, "user unknown");
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* Get the password entry */
+ pwd = getpwnam(user);
+ if (pwd == NULL)
+ {
+ 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)
+ return PAM_SUCCESS;
+
+ return create_homedir(pamh,ctrl,pwd);
+}
+
+/* Ignore */
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t * pamh, int flags, int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+struct pam_module _pam_mkhomedir_modstruct =
+{
+ "pam_mkhomedir",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL,
+};
+
+#endif