]> granicus.if.org Git - linux-pam/blob - modules/pam_mkhomedir/pam_mkhomedir.c
Relevant BUGIDs:
[linux-pam] / 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 static char UMask[16] = "0022";
68 static char SkelDir[BUFSIZ] = "/etc/skel"; /* THIS MODULE IS NOT THREAD SAFE */
69
70 static int
71 _pam_parse (const pam_handle_t *pamh, int flags, int argc, const char **argv)
72 {
73    int ctrl = 0;
74
75    /* does the appliction require quiet? */
76    if ((flags & PAM_SILENT) == PAM_SILENT)
77       ctrl |= MKHOMEDIR_QUIET;
78
79    /* step through arguments */
80    for (; argc-- > 0; ++argv)
81    {
82       if (!strcmp(*argv, "silent")) {
83          ctrl |= MKHOMEDIR_QUIET;
84       } else if (!strcmp(*argv, "debug")) {
85          ctrl |= MKHOMEDIR_DEBUG;
86       } else if (!strncmp(*argv,"umask=",6)) {
87          strncpy(SkelDir,*argv+6,sizeof(UMask));
88          UMask[sizeof(UMask)-1] = '\0';
89       } else if (!strncmp(*argv,"skel=",5)) {
90          strncpy(SkelDir,*argv+5,sizeof(SkelDir));
91          SkelDir[sizeof(SkelDir)-1] = '\0';
92       } else {
93          pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
94       }
95    }
96
97    D(("ctrl = %o", ctrl));
98    return ctrl;
99 }
100
101 /* Do the actual work of creating a home dir */
102 static int
103 create_homedir (pam_handle_t *pamh, int ctrl,
104                 const struct passwd *pwd)
105 {
106    int retval, child;
107    struct sigaction newsa, oldsa;
108
109    /* Mention what is happening, if the notification fails that is OK */
110    if (!(ctrl & MKHOMEDIR_QUIET))
111       pam_info(pamh, _("Creating directory '%s'."), pwd->pw_dir);
112
113
114    D(("called."));
115
116    /*
117     * This code arranges that the demise of the child does not cause
118     * the application to receive a signal it is not expecting - which
119     * may kill the application or worse.
120     */
121    memset(&newsa, '\0', sizeof(newsa));
122    newsa.sa_handler = SIG_DFL;
123    sigaction(SIGCHLD, &newsa, &oldsa);
124  
125    if (ctrl & MKHOMEDIR_DEBUG) {
126         pam_syslog(pamh, LOG_DEBUG, "Executing mkhomedir_helper.");
127    }
128
129    /* fork */
130    child = fork();
131    if (child == 0) {
132         int i;
133         struct rlimit rlim;
134         static char *envp[] = { NULL };
135         char *args[] = { NULL, NULL, NULL, NULL, NULL };
136
137         if (getrlimit(RLIMIT_NOFILE, &rlim)==0) {
138           if (rlim.rlim_max >= MAX_FD_NO)
139                 rlim.rlim_max = MAX_FD_NO;
140           for (i=0; i < (int)rlim.rlim_max; i++) {
141                 close(i);
142           }
143         }
144
145         /* exec the mkhomedir helper */
146         args[0] = x_strdup(MKHOMEDIR_HELPER);
147         args[1] = pwd->pw_name;
148         args[2] = UMask;
149         args[3] = SkelDir;
150
151         execve(MKHOMEDIR_HELPER, args, envp);
152
153         /* should not get here: exit with error */
154         D(("helper binary is not available"));
155         exit(PAM_SYSTEM_ERR);
156    } else if (child > 0) {
157         int rc;
158         while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
159         if (rc < 0) {
160           pam_syslog(pamh, LOG_ERR, "waitpid failed: %m");
161           retval = PAM_SYSTEM_ERR;
162         } else {
163           retval = WEXITSTATUS(retval);
164         }
165    } else {
166         D(("fork failed"));
167         pam_syslog(pamh, LOG_ERR, "fork failed: %m");
168         retval = PAM_SYSTEM_ERR;
169    }
170
171    sigaction(SIGCHLD, &oldsa, NULL);   /* restore old signal handler */
172
173    if (ctrl & MKHOMEDIR_DEBUG) {
174         pam_syslog(pamh, LOG_DEBUG, "mkhomedir_helper returned %d", retval);
175    }
176
177    if (retval != PAM_SUCCESS && !(ctrl & MKHOMEDIR_QUIET)) {
178         pam_error(pamh, _("Unable to create and initialize directory '%s'."),
179             pwd->pw_dir);
180    }
181
182    D(("returning %d", retval));
183    return retval;
184 }
185
186 /* --- authentication management functions (only) --- */
187
188 PAM_EXTERN int
189 pam_sm_open_session (pam_handle_t *pamh, int flags, int argc,
190                      const char **argv)
191 {
192    int retval, ctrl;
193    const void *user;
194    const struct passwd *pwd;
195    struct stat St;
196
197    /* Parse the flag values */
198    ctrl = _pam_parse(pamh, flags, argc, argv);
199
200    /* Determine the user name so we can get the home directory */
201    retval = pam_get_item(pamh, PAM_USER, &user);
202    if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0')
203    {
204       pam_syslog(pamh, LOG_NOTICE, "Cannot obtain the user name.");
205       return PAM_USER_UNKNOWN;
206    }
207
208    /* Get the password entry */
209    pwd = pam_modutil_getpwnam (pamh, user);
210    if (pwd == NULL)
211    {
212       pam_syslog(pamh, LOG_NOTICE, "User unknown.");
213       D(("couldn't identify user %s", user));
214       return PAM_CRED_INSUFFICIENT;
215    }
216
217    /* Stat the home directory, if something exists then we assume it is
218       correct and return a success*/
219    if (stat(pwd->pw_dir, &St) == 0) {
220       if (ctrl & MKHOMEDIR_DEBUG) {
221           pam_syslog(pamh, LOG_DEBUG, "Home directory %s already exists.",
222               pwd->pw_dir);
223       }
224       return PAM_SUCCESS;
225    }
226
227    return create_homedir(pamh, ctrl, pwd);
228 }
229
230 /* Ignore */
231 PAM_EXTERN
232 int pam_sm_close_session (pam_handle_t * pamh UNUSED, int flags UNUSED,
233                           int argc UNUSED, const char **argv UNUSED)
234 {
235    return PAM_SUCCESS;
236 }
237
238 #ifdef PAM_STATIC
239
240 /* static module data */
241 struct pam_module _pam_mkhomedir_modstruct =
242 {
243    "pam_mkhomedir",
244    NULL,
245    NULL,
246    NULL,
247    pam_sm_open_session,
248    pam_sm_close_session,
249    NULL,
250 };
251
252 #endif