]> granicus.if.org Git - linux-pam/blob - modules/pam_mkhomedir/pam_mkhomedir.c
Relevant BUGIDs: 634543
[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"; /* THIS MODULE IS NOT THREAD SAFE */
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          ctrl |= MKHOMEDIR_QUIET;
87       } else if (!strncmp(*argv,"umask=",6)) {
88          UMask = strtol(*argv+6,0,0);
89       } else if (!strncmp(*argv,"skel=",5)) {
90          strncpy(SkelDir,*argv+5,sizeof(SkelDir));
91          SkelDir[sizeof(SkelDir)-1] = '\0';
92       } else {
93          _log_err(LOG_ERR, "unknown option; %s", *argv);
94       }
95    }
96
97    D(("ctrl = %o", ctrl));
98    return ctrl;
99 }
100
101 /* This common function is used to send a message to the applications 
102    conversion function. Our only use is to ask the application to print 
103    an informative message that we are creating a home directory */
104 static int converse(pam_handle_t * pamh, int ctrl, int nargs
105                     ,struct pam_message **message
106                     ,struct pam_response **response)
107 {
108    int retval;
109    struct pam_conv *conv;
110
111    D(("begin to converse"));
112
113    retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
114    if (retval == PAM_SUCCESS)
115    {
116
117       retval = conv->conv(nargs, (const struct pam_message **) message
118                           ,response, conv->appdata_ptr);
119
120       D(("returned from application's conversation function"));
121
122       if (retval != PAM_SUCCESS && (ctrl & MKHOMEDIR_DEBUG))
123       {
124          _log_err(LOG_DEBUG, "conversation failure [%s]"
125                   ,pam_strerror(pamh, retval));
126       }
127
128    }
129    else
130    {
131       _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
132                ,pam_strerror(pamh, retval));
133    }
134
135    D(("ready to return from module conversation"));
136
137    return retval;               /* propagate error status */
138 }
139
140 /* Ask the application to display a short text string for us. */
141 static int make_remark(pam_handle_t * pamh, int ctrl, const char *remark)
142 {
143    int retval;
144
145    if ((ctrl & MKHOMEDIR_QUIET) != MKHOMEDIR_QUIET)
146    {
147       struct pam_message msg[1], *mesg[1];
148       struct pam_response *resp = NULL;
149
150       mesg[0] = &msg[0];
151       msg[0].msg_style = PAM_TEXT_INFO;
152       msg[0].msg = remark;
153
154       retval = converse(pamh, ctrl, 1, mesg, &resp);
155
156       msg[0].msg = NULL;
157       if (resp)
158       {
159          _pam_drop_reply(resp, 1);
160       }
161    }
162    else
163    {
164       D(("keeping quiet"));
165       retval = PAM_SUCCESS;
166    }
167
168    D(("returning %s", pam_strerror(pamh, retval)));
169    return retval;
170 }
171
172 /* Do the actual work of creating a home dir */
173 static int create_homedir(pam_handle_t * pamh, int ctrl,
174                           const struct passwd *pwd,
175                           const char *source, const char *dest)
176 {
177    char remark[BUFSIZ];
178    DIR *D;
179    struct dirent *Dir;
180
181    /* Mention what is happening, if the notification fails that is OK */
182    if (snprintf(remark,sizeof(remark),"Creating directory '%s'.", dest) == -1)
183       return PAM_PERM_DENIED;
184
185    make_remark(pamh, ctrl, remark);
186
187    /* Create the new directory */
188    if (mkdir(dest,0700) != 0)
189    {
190       _log_err(LOG_DEBUG, "unable to create directory %s",dest);
191       return PAM_PERM_DENIED;
192    }   
193    if (chmod(dest,0777 & (~UMask)) != 0 ||
194        chown(dest,pwd->pw_uid,pwd->pw_gid) != 0)
195    {
196       _log_err(LOG_DEBUG, "unable to change perms on directory %s",dest);
197       return PAM_PERM_DENIED;
198    }
199
200    /* See if we need to copy the skel dir over. */
201    if ((source == NULL) || (strlen(source) == 0))
202    {
203       return PAM_SUCCESS;
204    }
205
206    /* Scan the directory */
207    D = opendir(source);
208    if (D == 0)
209    {
210       _log_err(LOG_DEBUG, "unable to read directory %s",source);
211       return PAM_PERM_DENIED;
212    }
213
214    for (Dir = readdir(D); Dir != 0; Dir = readdir(D))
215    {  
216       int SrcFd;
217       int DestFd;
218       int Res;
219       struct stat St;
220       char newsource[PATH_MAX], newdest[PATH_MAX];
221
222       /* Skip some files.. */
223       if (strcmp(Dir->d_name,".") == 0 ||
224           strcmp(Dir->d_name,"..") == 0)
225          continue;
226
227       /* Determine what kind of file it is. */
228       snprintf(newsource,sizeof(newsource),"%s/%s",source,Dir->d_name);
229       if (lstat(newsource,&St) != 0)
230          continue;
231
232       /* We'll need the new file's name. */
233       snprintf(newdest,sizeof(newdest),"%s/%s",dest,Dir->d_name);
234
235       /* If it's a directory, recurse. */
236       if (S_ISDIR(St.st_mode))
237       {
238          create_homedir(pamh, ctrl, pwd, newsource, newdest);
239          continue;
240       }
241
242       /* If it's a symlink, create a new link. */
243       if (S_ISLNK(St.st_mode))
244       {
245          char pointed[PATH_MAX];
246          memset(pointed, 0, sizeof(pointed));
247          if(readlink(newsource, pointed, sizeof(pointed) - 1) != -1)
248          {
249             if(symlink(pointed, newdest) == 0)
250             {
251                if (lchown(newdest,pwd->pw_uid,pwd->pw_gid) != 0)
252                {
253                    _log_err(LOG_DEBUG, "unable to chang perms on link %s",
254                             newdest);
255                    return PAM_PERM_DENIED;
256                }
257             }
258          }
259          continue;
260       }
261
262       /* If it's not a regular file, it's probably not a good idea to create
263        * the new device node, FIFO, or whatever it is. */
264       if (!S_ISREG(St.st_mode))
265       {
266          continue;
267       }
268
269       /* Open the source file */
270       if ((SrcFd = open(newsource,O_RDONLY)) < 0 || fstat(SrcFd,&St) != 0)
271       {
272          _log_err(LOG_DEBUG, "unable to open src file %s",newsource);
273          return PAM_PERM_DENIED;
274       }
275       stat(newsource,&St);
276
277       /* Open the dest file */
278       if ((DestFd = open(newdest,O_WRONLY | O_TRUNC | O_CREAT,0600)) < 0)
279       {
280          close(SrcFd);
281          _log_err(LOG_DEBUG, "unable to open dest file %s",newdest);
282          return PAM_PERM_DENIED;
283       }
284
285       /* Set the proper ownership and permissions for the module. We make
286          the file a+w and then mask it with the set mask. This preseves
287          execute bits */
288       if (fchmod(DestFd,(St.st_mode | 0222) & (~UMask)) != 0 ||
289           fchown(DestFd,pwd->pw_uid,pwd->pw_gid) != 0)
290       {
291          close(SrcFd);
292          close(DestFd);
293          _log_err(LOG_DEBUG, "unable to chang perms on copy %s",newdest);
294          return PAM_PERM_DENIED;
295       }
296
297       /* Copy the file */
298       do
299       {
300          Res = read(SrcFd,remark,sizeof(remark));
301          if (Res < 0 || write(DestFd,remark,Res) != Res)
302          {
303             close(SrcFd);
304             close(DestFd);
305             _log_err(LOG_DEBUG, "unable to perform IO");
306             return PAM_PERM_DENIED;
307          }
308       }
309       while (Res != 0);
310       close(SrcFd);
311       close(DestFd);
312    }
313
314    return PAM_SUCCESS;
315 }
316
317 /* --- authentication management functions (only) --- */
318
319 PAM_EXTERN
320 int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc
321                         ,const char **argv)
322 {
323    int retval, ctrl;
324    const char *user;
325    const struct passwd *pwd;
326    struct stat St;
327       
328    /* Parse the flag values */
329    ctrl = _pam_parse(flags, argc, argv);
330
331    /* Determine the user name so we can get the home directory */
332    retval = pam_get_item(pamh, PAM_USER, (const void **) &user);
333    if (retval != PAM_SUCCESS || user == NULL || *user == '\0')
334    {
335       _log_err(LOG_NOTICE, "user unknown");
336       return PAM_USER_UNKNOWN;
337    }
338
339    /* Get the password entry */
340    pwd = getpwnam(user);
341    if (pwd == NULL)
342    {
343       D(("couldn't identify user %s", user));
344       return PAM_CRED_INSUFFICIENT;
345    }
346
347    /* Stat the home directory, if something exists then we assume it is
348       correct and return a success*/
349    if (stat(pwd->pw_dir,&St) == 0)
350       return PAM_SUCCESS;
351
352    return create_homedir(pamh,ctrl,pwd,SkelDir,pwd->pw_dir);
353 }
354
355 /* Ignore */
356 PAM_EXTERN 
357 int pam_sm_close_session(pam_handle_t * pamh, int flags, int argc
358                          ,const char **argv)
359 {
360    return PAM_SUCCESS;
361 }
362
363 #ifdef PAM_STATIC
364
365 /* static module data */
366 struct pam_module _pam_mkhomedir_modstruct =
367 {
368    "pam_mkhomedir",
369    NULL,
370    NULL,
371    NULL,
372    pam_sm_open_session,
373    pam_sm_close_session,
374    NULL,
375 };
376
377 #endif