]> granicus.if.org Git - linux-pam/blob - modules/pam_mkhomedir/pam_mkhomedir.c
ec05993dfa1e2be8e11d6326d2392eccd045c95e
[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_pwdb.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_pwdb.so
19    session    required   pam_mkhomedir.so skel=/etc/skel/ umask=0022
20    session    required   pam_pwdb.so
21    session    optional   pam_lastlog.so
22    password   required   pam_pwdb.so   
23    
24    Released under the GNU LGPL version 2 or later
25    Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999
26    Structure taken from pam_lastlogin by Andrew Morgan 
27      <morgan@parc.power.net> 1996
28  */
29
30 /* I want snprintf dammit */
31 #define _GNU_SOURCE 1
32 #include <stdarg.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <pwd.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <dirent.h>
42
43 /*
44  * here, we make a definition for the externally accessible function
45  * in this file (this definition is required for static a module
46  * but strongly encouraged generally) it is used to instruct the
47  * modules include file to define the function prototypes.
48  */
49
50 #define PAM_SM_SESSION
51
52 #include <security/pam_modules.h>
53 #include <security/_pam_macros.h>
54
55 /* argument parsing */
56 #define MKHOMEDIR_DEBUG      020        /* keep quiet about things */
57 #define MKHOMEDIR_QUIET      040        /* keep quiet about things */
58
59 static unsigned int UMask = 0022;
60 static char SkelDir[BUFSIZ] = "/etc/skel";
61
62 /* some syslogging */
63 static void _log_err(int err, const char *format, ...)
64 {
65     va_list args;
66
67     va_start(args, format);
68     openlog("PAM-mkhomedir", LOG_CONS|LOG_PID, LOG_AUTH);
69     vsyslog(err, format, args);
70     va_end(args);
71     closelog();
72 }
73
74 static int _pam_parse(int flags, int argc, const char **argv)
75 {
76    int ctrl = 0;
77
78    /* does the appliction require quiet? */
79    if ((flags & PAM_SILENT) == PAM_SILENT)
80       ctrl |= MKHOMEDIR_QUIET;
81
82    /* step through arguments */
83    for (; argc-- > 0; ++argv)
84    {
85       if (!strcmp(*argv, "silent"))
86       {
87          ctrl |= MKHOMEDIR_QUIET;
88       } 
89       else if (!strncmp(*argv,"umask=",6))
90          UMask = strtol(*argv+6,0,0);
91       else if (!strncmp(*argv,"skel=",5))
92          strcpy(SkelDir,*argv+5);
93       else
94       {
95          _log_err(LOG_ERR, "unknown option; %s", *argv);
96       }
97    }
98
99    D(("ctrl = %o", ctrl));
100    return ctrl;
101 }
102
103 /* This common function is used to send a message to the applications 
104    conversion function. Our only use is to ask the application to print 
105    an informative message that we are creating a home directory */
106 static int converse(pam_handle_t * pamh, int ctrl, int nargs
107                     ,struct pam_message **message
108                     ,struct pam_response **response)
109 {
110    int retval;
111    struct pam_conv *conv;
112
113    D(("begin to converse"));
114
115    retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
116    if (retval == PAM_SUCCESS)
117    {
118
119       retval = conv->conv(nargs, (const struct pam_message **) message
120                           ,response, conv->appdata_ptr);
121
122       D(("returned from application's conversation function"));
123
124       if (retval != PAM_SUCCESS && (ctrl & MKHOMEDIR_DEBUG))
125       {
126          _log_err(LOG_DEBUG, "conversation failure [%s]"
127                   ,pam_strerror(pamh, retval));
128       }
129
130    }
131    else
132    {
133       _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
134                ,pam_strerror(pamh, retval));
135    }
136
137    D(("ready to return from module conversation"));
138
139    return retval;               /* propagate error status */
140 }
141
142 /* Ask the application to display a short text string for us. */
143 static int make_remark(pam_handle_t * pamh, int ctrl, const char *remark)
144 {
145    int retval;
146
147    if ((ctrl & MKHOMEDIR_QUIET) != MKHOMEDIR_QUIET)
148    {
149       struct pam_message msg[1], *mesg[1];
150       struct pam_response *resp = NULL;
151
152       mesg[0] = &msg[0];
153       msg[0].msg_style = PAM_TEXT_INFO;
154       msg[0].msg = remark;
155
156       retval = converse(pamh, ctrl, 1, mesg, &resp);
157
158       msg[0].msg = NULL;
159       if (resp)
160       {
161          _pam_drop_reply(resp, 1);
162       }
163    }
164    else
165    {
166       D(("keeping quiet"));
167       retval = PAM_SUCCESS;
168    }
169
170    D(("returning %s", pam_strerror(pamh, retval)));
171    return retval;
172 }
173
174 /* Do the actual work of creating a home dir */
175 static int create_homedir(pam_handle_t * pamh, int ctrl,
176                          const struct passwd *pwd)
177 {
178    char *remark;
179    DIR *D;
180    struct dirent *Dir;
181    
182    /* Some scratch space */
183    remark = malloc(BUFSIZ);
184    if (remark == NULL)
185    {
186       D(("no memory for last login remark"));
187       return PAM_BUF_ERR;
188    }
189
190    /* Mention what is happening, if the notification fails that is OK */
191    if (snprintf(remark,BUFSIZ,"Creating home directory '%s'.",
192             pwd->pw_dir) == -1)
193       return PAM_PERM_DENIED;
194    
195    make_remark(pamh, ctrl, remark);
196
197    /* Crete the home directory */
198    if (mkdir(pwd->pw_dir,0700) != 0)
199    {
200       free(remark);
201       _log_err(LOG_DEBUG, "unable to create home directory %s",pwd->pw_dir);
202       return PAM_PERM_DENIED;
203    }   
204    if (chmod(pwd->pw_dir,0777 & (~UMask)) != 0 ||
205        chown(pwd->pw_dir,pwd->pw_uid,pwd->pw_gid) != 0)
206    {
207       free(remark);
208       _log_err(LOG_DEBUG, "unable to chance perms on home directory %s",pwd->pw_dir);
209       return PAM_PERM_DENIED;
210    }   
211    
212    /* See if we need to copy the skel dir over. */
213    if (SkelDir[0] == 0)
214    {
215       free(remark);
216       return PAM_SUCCESS;
217    }
218
219    /* Scan the directory */
220    D = opendir(SkelDir);
221    if (D == 0)
222    {
223       free(remark);
224       _log_err(LOG_DEBUG, "unable to read directory %s",SkelDir);
225       return PAM_PERM_DENIED;
226    }
227    
228    for (Dir = readdir(D); Dir != 0; Dir = readdir(D))
229    {  
230       int SrcFd;
231       int DestFd;
232       int Res;
233       struct stat St;
234       
235       /* Skip some files.. */
236       if (strcmp(Dir->d_name,".") == 0 ||
237           strcmp(Dir->d_name,"..") == 0)
238          continue;
239       
240       /* Check if it is a directory */
241       snprintf(remark,BUFSIZ,"%s/%s",SkelDir,Dir->d_name);
242       if (stat(remark,&St) != 0)
243         continue;
244       if (S_ISDIR(St.st_mode))
245       {
246         snprintf(remark,BUFSIZ,"%s/%s",pwd->pw_dir,Dir->d_name);
247         if (mkdir(remark,(St.st_mode | 0222) & (~UMask)) != 0 ||
248             chmod(remark,(St.st_mode | 0222) & (~UMask)) != 0 ||
249             chown(remark,pwd->pw_uid,pwd->pw_gid) != 0)
250         {
251            free(remark);
252            _log_err(LOG_DEBUG, "unable to change perms on copy %s",remark);
253            return PAM_PERM_DENIED;
254         }
255         continue;
256       }
257
258       /* Open the source file */
259       if ((SrcFd = open(remark,O_RDONLY)) < 0 || fstat(SrcFd,&St) != 0)
260       {
261          free(remark);
262          _log_err(LOG_DEBUG, "unable to open src file %s",remark);
263          return PAM_PERM_DENIED;
264       }
265       stat(remark,&St);
266       
267       /* Open the dest file */
268       snprintf(remark,BUFSIZ,"%s/%s",pwd->pw_dir,Dir->d_name);
269       if ((DestFd = open(remark,O_WRONLY | O_TRUNC | O_CREAT,0600)) < 0)
270       {
271          close(SrcFd);
272          free(remark);
273          _log_err(LOG_DEBUG, "unable to open dest file %s",remark);
274          return PAM_PERM_DENIED;
275       }
276
277       /* Set the proper ownership and permissions for the module. We make
278          the file a+w and then mask it with the set mask. This preseves
279          execute bits */
280       if (fchmod(DestFd,(St.st_mode | 0222) & (~UMask)) != 0 ||
281           fchown(DestFd,pwd->pw_uid,pwd->pw_gid) != 0)
282       {
283          free(remark);
284          _log_err(LOG_DEBUG, "unable to chang perms on copy %s",remark);
285          return PAM_PERM_DENIED;
286       }   
287       
288       /* Copy the file */
289       do
290       {
291          Res = read(SrcFd,remark,BUFSIZ);
292          if (Res < 0 || write(DestFd,remark,Res) != Res)
293          {
294             close(SrcFd);
295             close(DestFd);
296             free(remark);
297             _log_err(LOG_DEBUG, "unable to perform IO");
298             return PAM_PERM_DENIED;
299          }
300       }
301       while (Res != 0);
302       close(SrcFd);
303       close(DestFd);
304    }
305    
306    free(remark);
307    return PAM_SUCCESS;
308 }
309
310 /* --- authentication management functions (only) --- */
311
312 PAM_EXTERN
313 int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc
314                         ,const char **argv)
315 {
316    int retval, ctrl;
317    const char *user;
318    const struct passwd *pwd;
319    struct stat St;
320       
321    /* Parse the flag values */
322    ctrl = _pam_parse(flags, argc, argv);
323
324    /* Determine the user name so we can get the home directory */
325    retval = pam_get_item(pamh, PAM_USER, (const void **) &user);
326    if (retval != PAM_SUCCESS || user == NULL || *user == '\0')
327    {
328       _log_err(LOG_NOTICE, "user unknown");
329       return PAM_USER_UNKNOWN;
330    }
331
332    /* Get the password entry */
333    pwd = getpwnam(user);
334    if (pwd == NULL)
335    {
336       D(("couldn't identify user %s", user));
337       return PAM_CRED_INSUFFICIENT;
338    }
339
340    /* Stat the home directory, if something exists then we assume it is
341       correct and return a success*/
342    if (stat(pwd->pw_dir,&St) == 0)
343       return PAM_SUCCESS;
344
345    return create_homedir(pamh,ctrl,pwd);
346 }
347
348 /* Ignore */
349 PAM_EXTERN 
350 int pam_sm_close_session(pam_handle_t * pamh, int flags, int argc
351                          ,const char **argv)
352 {
353    return PAM_SUCCESS;
354 }
355
356 #ifdef PAM_STATIC
357
358 /* static module data */
359 struct pam_module _pam_mkhomedir_modstruct =
360 {
361    "pam_mkhomedir",
362    NULL,
363    NULL,
364    NULL,
365    pam_sm_open_session,
366    pam_sm_close_session,
367    NULL,
368 };
369
370 #endif