Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / contrib / libpam / modules / pam_mkhomedir / pam_mkhomedir.c
CommitLineData
984263bc
MD
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 $
1de703da 29 * $DragonFly: src/contrib/libpam/modules/pam_mkhomedir/Attic/pam_mkhomedir.c,v 1.2 2003/06/17 04:24:03 dillon Exp $
984263bc
MD
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
61static unsigned int UMask = 0022;
62static char SkelDir[BUFSIZ] = "/etc/skel";
63
64/* some syslogging */
65static 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
76static 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 */
108static 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. */
145static 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 */
177static 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
314PAM_EXTERN
315int 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 */
351PAM_EXTERN
352int 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 */
361struct 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