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