1 /* PAM Make Home Dir module
3 This module will create a users home directory if it does not exist
4 when the session begins. This allows users to be present in central
5 database (such as nis, kerb or ldap) without using a distributed
6 file system or pre-creating a large number of directories.
8 Here is a sample /etc/pam.d/login file for Debian GNU/Linux
11 auth requisite pam_securetty.so
12 auth sufficient pam_ldap.so
13 auth required pam_unix.so
14 auth optional pam_group.so
15 auth optional pam_mail.so
16 account requisite pam_time.so
17 account sufficient pam_ldap.so
18 account required pam_unix.so
19 session required pam_mkhomedir.so skel=/etc/skel/ umask=0022
20 session required pam_unix.so
21 session optional pam_lastlog.so
22 password required pam_unix.so
24 Released under the GNU LGPL version 2 or later
25 Copyright (c) Red Hat, Inc. 2009
26 Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999
27 Structure taken from pam_lastlogin by Andrew Morgan
28 <morgan@parc.power.net> 1996
33 #include <sys/types.h>
36 #include <sys/resource.h>
48 * here, we make a definition for the externally accessible function
49 * in this file (this definition is required for static a module
50 * but strongly encouraged generally) it is used to instruct the
51 * modules include file to define the function prototypes.
54 #define PAM_SM_SESSION
56 #include <security/pam_modules.h>
57 #include <security/_pam_macros.h>
58 #include <security/pam_modutil.h>
59 #include <security/pam_ext.h>
61 #define MAX_FD_NO 10000
63 /* argument parsing */
64 #define MKHOMEDIR_DEBUG 020 /* be verbose about things */
65 #define MKHOMEDIR_QUIET 040 /* keep quiet about things */
72 typedef struct options_t options_t;
75 _pam_parse (const pam_handle_t *pamh, int flags, int argc, const char **argv,
80 opt->skeldir = "/etc/skel";
82 /* does the appliction require quiet? */
83 if ((flags & PAM_SILENT) == PAM_SILENT)
84 opt->ctrl |= MKHOMEDIR_QUIET;
86 /* step through arguments */
87 for (; argc-- > 0; ++argv)
89 if (!strcmp(*argv, "silent")) {
90 opt->ctrl |= MKHOMEDIR_QUIET;
91 } else if (!strcmp(*argv, "debug")) {
92 opt->ctrl |= MKHOMEDIR_DEBUG;
93 } else if (!strncmp(*argv,"umask=",6)) {
95 } else if (!strncmp(*argv,"skel=",5)) {
96 opt->skeldir = *argv+5;
98 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
103 /* Do the actual work of creating a home dir */
105 create_homedir (pam_handle_t *pamh, options_t *opt,
106 const struct passwd *pwd)
109 struct sigaction newsa, oldsa;
111 /* Mention what is happening, if the notification fails that is OK */
112 if (!(opt->ctrl & MKHOMEDIR_QUIET))
113 pam_info(pamh, _("Creating directory '%s'."), pwd->pw_dir);
119 * This code arranges that the demise of the child does not cause
120 * the application to receive a signal it is not expecting - which
121 * may kill the application or worse.
123 memset(&newsa, '\0', sizeof(newsa));
124 newsa.sa_handler = SIG_DFL;
125 sigaction(SIGCHLD, &newsa, &oldsa);
127 if (opt->ctrl & MKHOMEDIR_DEBUG) {
128 pam_syslog(pamh, LOG_DEBUG, "Executing mkhomedir_helper.");
136 static char *envp[] = { NULL };
137 char *args[] = { NULL, NULL, NULL, NULL, NULL };
139 if (getrlimit(RLIMIT_NOFILE, &rlim)==0) {
140 if (rlim.rlim_max >= MAX_FD_NO)
141 rlim.rlim_max = MAX_FD_NO;
142 for (i=0; i < (int)rlim.rlim_max; i++) {
147 /* exec the mkhomedir helper */
148 args[0] = x_strdup(MKHOMEDIR_HELPER);
149 args[1] = pwd->pw_name;
150 args[2] = x_strdup(opt->umask);
151 args[3] = x_strdup(opt->skeldir);
153 execve(MKHOMEDIR_HELPER, args, envp);
155 /* should not get here: exit with error */
156 D(("helper binary is not available"));
157 _exit(PAM_SYSTEM_ERR);
158 } else if (child > 0) {
160 while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
162 pam_syslog(pamh, LOG_ERR, "waitpid failed: %m");
163 retval = PAM_SYSTEM_ERR;
164 } else if (!WIFEXITED(retval)) {
165 pam_syslog(pamh, LOG_ERR, "mkhomedir_helper abnormal exit: %d", retval);
166 retval = PAM_SYSTEM_ERR;
168 retval = WEXITSTATUS(retval);
172 pam_syslog(pamh, LOG_ERR, "fork failed: %m");
173 retval = PAM_SYSTEM_ERR;
176 sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
178 if (opt->ctrl & MKHOMEDIR_DEBUG) {
179 pam_syslog(pamh, LOG_DEBUG, "mkhomedir_helper returned %d", retval);
182 if (retval != PAM_SUCCESS && !(opt->ctrl & MKHOMEDIR_QUIET)) {
183 pam_error(pamh, _("Unable to create and initialize directory '%s'."),
187 D(("returning %d", retval));
191 /* --- authentication management functions (only) --- */
194 pam_sm_open_session (pam_handle_t *pamh, int flags, int argc,
200 const struct passwd *pwd;
203 /* Parse the flag values */
204 _pam_parse(pamh, flags, argc, argv, &opt);
206 /* Determine the user name so we can get the home directory */
207 retval = pam_get_item(pamh, PAM_USER, &user);
208 if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0')
210 pam_syslog(pamh, LOG_NOTICE, "Cannot obtain the user name.");
211 return PAM_USER_UNKNOWN;
214 /* Get the password entry */
215 pwd = pam_modutil_getpwnam (pamh, user);
218 pam_syslog(pamh, LOG_NOTICE, "User unknown.");
219 D(("couldn't identify user %s", user));
220 return PAM_CRED_INSUFFICIENT;
223 /* Stat the home directory, if something exists then we assume it is
224 correct and return a success*/
225 if (stat(pwd->pw_dir, &St) == 0) {
226 if (opt.ctrl & MKHOMEDIR_DEBUG) {
227 pam_syslog(pamh, LOG_DEBUG, "Home directory %s already exists.",
233 return create_homedir(pamh, &opt, pwd);
238 int pam_sm_close_session (pam_handle_t * pamh UNUSED, int flags UNUSED,
239 int argc UNUSED, const char **argv UNUSED)
246 /* static module data */
247 struct pam_module _pam_mkhomedir_modstruct =
254 pam_sm_close_session,