Imported Upstream version 1.1.6
[platform/upstream/pam.git] / modules / pam_mkhomedir / pam_mkhomedir.c
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 /*
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.
52  */
53
54 #define PAM_SM_SESSION
55
56 #include <security/pam_modules.h>
57 #include <security/_pam_macros.h>
58 #include <security/pam_modutil.h>
59 #include <security/pam_ext.h>
60
61 #define MAX_FD_NO 10000
62
63 /* argument parsing */
64 #define MKHOMEDIR_DEBUG      020        /* be verbose about things */
65 #define MKHOMEDIR_QUIET      040        /* keep quiet about things */
66
67 struct options_t {
68   int ctrl;
69   const char *umask;
70   const char *skeldir;
71 };
72 typedef struct options_t options_t;
73
74 static void
75 _pam_parse (const pam_handle_t *pamh, int flags, int argc, const char **argv,
76             options_t *opt)
77 {
78    opt->ctrl = 0;
79    opt->umask = "0022";
80    opt->skeldir = "/etc/skel";
81
82    /* does the appliction require quiet? */
83    if ((flags & PAM_SILENT) == PAM_SILENT)
84       opt->ctrl |= MKHOMEDIR_QUIET;
85
86    /* step through arguments */
87    for (; argc-- > 0; ++argv)
88    {
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)) {
94          opt->umask = *argv+6;
95       } else if (!strncmp(*argv,"skel=",5)) {
96          opt->skeldir = *argv+5;
97       } else {
98          pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
99       }
100    }
101 }
102
103 /* Do the actual work of creating a home dir */
104 static int
105 create_homedir (pam_handle_t *pamh, options_t *opt,
106                 const struct passwd *pwd)
107 {
108    int retval, child;
109    struct sigaction newsa, oldsa;
110
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);
114
115
116    D(("called."));
117
118    /*
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.
122     */
123    memset(&newsa, '\0', sizeof(newsa));
124    newsa.sa_handler = SIG_DFL;
125    sigaction(SIGCHLD, &newsa, &oldsa);
126
127    if (opt->ctrl & MKHOMEDIR_DEBUG) {
128         pam_syslog(pamh, LOG_DEBUG, "Executing mkhomedir_helper.");
129    }
130
131    /* fork */
132    child = fork();
133    if (child == 0) {
134         int i;
135         struct rlimit rlim;
136         static char *envp[] = { NULL };
137         char *args[] = { NULL, NULL, NULL, NULL, NULL };
138
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++) {
143                 close(i);
144           }
145         }
146
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);
152
153         execve(MKHOMEDIR_HELPER, args, envp);
154
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) {
159         int rc;
160         while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
161         if (rc < 0) {
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;
167         } else {
168           retval = WEXITSTATUS(retval);
169         }
170    } else {
171         D(("fork failed"));
172         pam_syslog(pamh, LOG_ERR, "fork failed: %m");
173         retval = PAM_SYSTEM_ERR;
174    }
175
176    sigaction(SIGCHLD, &oldsa, NULL);   /* restore old signal handler */
177
178    if (opt->ctrl & MKHOMEDIR_DEBUG) {
179         pam_syslog(pamh, LOG_DEBUG, "mkhomedir_helper returned %d", retval);
180    }
181
182    if (retval != PAM_SUCCESS && !(opt->ctrl & MKHOMEDIR_QUIET)) {
183         pam_error(pamh, _("Unable to create and initialize directory '%s'."),
184             pwd->pw_dir);
185    }
186
187    D(("returning %d", retval));
188    return retval;
189 }
190
191 /* --- authentication management functions (only) --- */
192
193 PAM_EXTERN int
194 pam_sm_open_session (pam_handle_t *pamh, int flags, int argc,
195                      const char **argv)
196 {
197    int retval;
198    options_t opt;
199    const void *user;
200    const struct passwd *pwd;
201    struct stat St;
202
203    /* Parse the flag values */
204    _pam_parse(pamh, flags, argc, argv, &opt);
205
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')
209    {
210       pam_syslog(pamh, LOG_NOTICE, "Cannot obtain the user name.");
211       return PAM_USER_UNKNOWN;
212    }
213
214    /* Get the password entry */
215    pwd = pam_modutil_getpwnam (pamh, user);
216    if (pwd == NULL)
217    {
218       pam_syslog(pamh, LOG_NOTICE, "User unknown.");
219       D(("couldn't identify user %s", user));
220       return PAM_CRED_INSUFFICIENT;
221    }
222
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.",
228               pwd->pw_dir);
229       }
230       return PAM_SUCCESS;
231    }
232
233    return create_homedir(pamh, &opt, pwd);
234 }
235
236 /* Ignore */
237 PAM_EXTERN
238 int pam_sm_close_session (pam_handle_t * pamh UNUSED, int flags UNUSED,
239                           int argc UNUSED, const char **argv UNUSED)
240 {
241    return PAM_SUCCESS;
242 }
243
244 #ifdef PAM_STATIC
245
246 /* static module data */
247 struct pam_module _pam_mkhomedir_modstruct =
248 {
249    "pam_mkhomedir",
250    NULL,
251    NULL,
252    NULL,
253    pam_sm_open_session,
254    pam_sm_close_session,
255    NULL,
256 };
257
258 #endif