1 /* PAM Make Home Dir module
2
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.
7
8 Here is a sample /etc/pam.d/login file for Debian GNU/Linux
9 2.1:
10
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
23
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
29 */
30
31 #include "config.h"
32
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/time.h>
36 #include <sys/resource.h>
37 #include <sys/wait.h>
38 #include <unistd.h>
39 #include <pwd.h>
40 #include <errno.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <signal.h>
46
47 #include <security/pam_modules.h>
48 #include <security/_pam_macros.h>
49 #include <security/pam_modutil.h>
50 #include <security/pam_ext.h>
51
52 #include "pam_cc_compat.h"
53 #include "pam_inline.h"
54
55 /* argument parsing */
56 #define MKHOMEDIR_DEBUG 020 /* be verbose about things */
57 #define MKHOMEDIR_QUIET 040 /* keep quiet about things */
58
59 #define LOGIN_DEFS "/etc/login.defs"
60 #define UMASK_DEFAULT "0022"
61
62 struct options_t {
63 int ctrl;
64 const char *umask;
65 const char *skeldir;
66 };
67 typedef struct options_t options_t;
68
69 static void
70 _pam_parse (const pam_handle_t *pamh, int flags, int argc, const char **argv,
71 options_t *opt)
72 {
73 opt->ctrl = 0;
74 opt->umask = NULL;
75 opt->skeldir = "/etc/skel";
76
77 /* does the application require quiet? */
78 if ((flags & PAM_SILENT) == PAM_SILENT)
79 opt->ctrl |= MKHOMEDIR_QUIET;
80
81 /* step through arguments */
82 for (; argc-- > 0; ++argv)
83 {
84 const char *str;
85
86 if (!strcmp(*argv, "silent")) {
87 opt->ctrl |= MKHOMEDIR_QUIET;
88 } else if (!strcmp(*argv, "debug")) {
89 opt->ctrl |= MKHOMEDIR_DEBUG;
90 } else if ((str = pam_str_skip_prefix(*argv, "umask=")) != NULL) {
91 opt->umask = str;
92 } else if ((str = pam_str_skip_prefix(*argv, "skel=")) != NULL) {
93 opt->skeldir = str;
94 } else {
95 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
96 }
97 }
98 }
99
100 static char*
101 _pam_conv_str_umask_to_homemode(const char *umask)
102 {
103 unsigned int m = 0;
104 char tmp[5];
105
106 m = 0777 & ~strtoul(umask, NULL, 8);
107 (void) snprintf(tmp, sizeof(tmp), "0%o", m);
108 return strdup(tmp);
109 }
110
111 /* Do the actual work of creating a home dir */
112 static int
113 create_homedir (pam_handle_t *pamh, options_t *opt,
114 const char *user, const char *dir)
115 {
116 int retval, child;
117 struct sigaction newsa, oldsa;
118 char *login_umask = NULL;
119 char *login_homemode = NULL;
120
121 /* Mention what is happening, if the notification fails that is OK */
122 if (!(opt->ctrl & MKHOMEDIR_QUIET))
123 pam_info(pamh, _("Creating directory '%s'."), dir);
124
125
126 D(("called."));
127
128 if (opt->ctrl & MKHOMEDIR_DEBUG) {
129 pam_syslog(pamh, LOG_DEBUG, "Executing mkhomedir_helper.");
130 }
131
132 /* fetch UMASK from /etc/login.defs if not in argv */
133 if (opt->umask == NULL) {
134 login_umask = pam_modutil_search_key(pamh, LOGIN_DEFS, "UMASK");
135 login_homemode = pam_modutil_search_key(pamh, LOGIN_DEFS, "HOME_MODE");
136 if (login_homemode == NULL) {
137 if (login_umask != NULL) {
138 login_homemode = _pam_conv_str_umask_to_homemode(login_umask);
139 } else {
140 login_homemode = _pam_conv_str_umask_to_homemode(UMASK_DEFAULT);
141 }
142 }
143 } else {
144 login_homemode = _pam_conv_str_umask_to_homemode(opt->umask);
145 }
146
147 /*
148 * This code arranges that the demise of the child does not cause
149 * the application to receive a signal it is not expecting - which
150 * may kill the application or worse.
151 */
152 memset(&newsa, '\0', sizeof(newsa));
153 newsa.sa_handler = SIG_DFL;
154 sigaction(SIGCHLD, &newsa, &oldsa);
155
156 /* fork */
157 child = fork();
158 if (child == 0) {
159 static char *envp[] = { NULL };
160 const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
161
162 if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD,
163 PAM_MODUTIL_PIPE_FD,
164 PAM_MODUTIL_PIPE_FD) < 0)
165 _exit(PAM_SYSTEM_ERR);
166
167 /* exec the mkhomedir helper */
168 args[0] = MKHOMEDIR_HELPER;
169 args[1] = user;
170 args[2] = opt->umask ? opt->umask : UMASK_DEFAULT;
171 args[3] = opt->skeldir;
172 args[4] = login_homemode;
173
174 DIAG_PUSH_IGNORE_CAST_QUAL;
175 execve(MKHOMEDIR_HELPER, (char **)args, envp);
176 DIAG_POP_IGNORE_CAST_QUAL;
177
178 /* should not get here: exit with error */
179 D(("helper binary is not available"));
180 _exit(PAM_SYSTEM_ERR);
181 } else if (child > 0) {
182 int rc;
183 while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
184 if (rc < 0) {
185 pam_syslog(pamh, LOG_ERR, "waitpid failed: %m");
186 retval = PAM_SYSTEM_ERR;
187 } else if (!WIFEXITED(retval)) {
188 pam_syslog(pamh, LOG_ERR, "mkhomedir_helper abnormal exit: %d", retval);
189 retval = PAM_SYSTEM_ERR;
190 } else {
191 retval = WEXITSTATUS(retval);
192 }
193 } else {
194 D(("fork failed"));
195 pam_syslog(pamh, LOG_ERR, "fork failed: %m");
196 retval = PAM_SYSTEM_ERR;
197 }
198
199 sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
200
201 if (opt->ctrl & MKHOMEDIR_DEBUG) {
202 pam_syslog(pamh, LOG_DEBUG, "mkhomedir_helper returned %d", retval);
203 }
204
205 if (retval != PAM_SUCCESS && !(opt->ctrl & MKHOMEDIR_QUIET)) {
206 pam_error(pamh, _("Unable to create and initialize directory '%s'."),
207 dir);
208 }
209
210 free(login_umask);
211 free(login_homemode);
212
213 D(("returning %d", retval));
214 return retval;
215 }
216
217 /* --- authentication management functions (only) --- */
218
219 int
220 pam_sm_open_session (pam_handle_t *pamh, int flags, int argc,
221 const char **argv)
222 {
223 int retval;
224 options_t opt;
225 const void *user;
226 const struct passwd *pwd;
227 struct stat St;
228
229 /* Parse the flag values */
230 _pam_parse(pamh, flags, argc, argv, &opt);
231
232 /* Determine the user name so we can get the home directory */
233 retval = pam_get_item(pamh, PAM_USER, &user);
234 if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0')
235 {
236 pam_syslog(pamh, LOG_NOTICE, "Cannot obtain the user name.");
237 return PAM_USER_UNKNOWN;
238 }
239
240 /* Get the password entry */
241 pwd = pam_modutil_getpwnam (pamh, user);
242 if (pwd == NULL)
243 {
244 pam_syslog(pamh, LOG_NOTICE, "User unknown.");
245 D(("couldn't identify user %s", user));
246 return PAM_USER_UNKNOWN;
247 }
248
249 /* Stat the home directory, if something exists then we assume it is
250 correct and return a success*/
251 if (stat(pwd->pw_dir, &St) == 0) {
252 if (opt.ctrl & MKHOMEDIR_DEBUG) {
253 pam_syslog(pamh, LOG_DEBUG, "Home directory %s already exists.",
254 pwd->pw_dir);
255 }
256 return PAM_SUCCESS;
257 }
258
259 return create_homedir(pamh, &opt, user, pwd->pw_dir);
260 }
261
262 /* Ignore */
263 int pam_sm_close_session (pam_handle_t * pamh UNUSED, int flags UNUSED,
264 int argc UNUSED, const char **argv UNUSED)
265 {
266 return PAM_SUCCESS;
267 }