1 /* mkhomedir_helper - helper for pam_mkhomedir module
3 Released under the GNU LGPL version 2 or later
5 Copyright (c) Red Hat, Inc., 2009
6 Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999
7 Structure taken from pam_lastlogin by Andrew Morgan
8 <morgan@parc.power.net> 1996
14 #include <sys/types.h>
26 #include <security/pam_ext.h>
27 #include <security/pam_modutil.h>
29 static unsigned long u_mask = 0022;
30 static char skeldir[BUFSIZ] = "/etc/skel";
33 rec_mkdir(const char *dir, mode_t mode)
36 char *parent = strdup(dir);
41 cp = strrchr(parent, '/');
43 if (cp != NULL && cp != parent)
48 if (stat(parent, &st) == -1 && errno == ENOENT)
49 if (rec_mkdir(parent, mode) != 0)
58 if (mkdir(dir, mode) != 0 && errno != EEXIST)
64 /* Do the actual work of creating a home dir */
66 create_homedir(const struct passwd *pwd,
67 const char *source, const char *dest)
72 int retval = PAM_SESSION_ERR;
74 /* Create the new directory */
75 if (rec_mkdir(dest, 0755) != 0)
77 pam_syslog(NULL, LOG_ERR, "unable to create directory %s: %m", dest);
78 return PAM_PERM_DENIED;
81 /* See if we need to copy the skel dir over. */
82 if ((source == NULL) || (strlen(source) == 0))
88 /* Scan the directory */
92 pam_syslog(NULL, LOG_DEBUG, "unable to read directory %s: %m", source);
93 retval = PAM_PERM_DENIED;
97 for (dent = readdir(d); dent != NULL; dent = readdir(d))
104 char *newsource = NULL, *newdest = NULL;
105 /* track length of buffers */
106 int nslen = 0, ndlen = 0;
107 int slen = strlen(source), dlen = strlen(dest);
109 char newsource[PATH_MAX], newdest[PATH_MAX];
112 /* Skip some files.. */
113 if (strcmp(dent->d_name,".") == 0 ||
114 strcmp(dent->d_name,"..") == 0)
117 /* Determine what kind of file it is. */
119 nslen = slen + strlen(dent->d_name) + 2;
123 retval = PAM_BUF_ERR;
127 if ((newsource = malloc(nslen)) == NULL)
129 retval = PAM_BUF_ERR;
133 sprintf(newsource, "%s/%s", source, dent->d_name);
135 snprintf(newsource, sizeof(newsource), "%s/%s", source, dent->d_name);
138 if (lstat(newsource, &st) != 0)
150 /* We'll need the new file's name. */
152 ndlen = dlen + strlen(dent->d_name)+2;
156 retval = PAM_BUF_ERR;
160 if ((newdest = malloc(ndlen)) == NULL)
163 retval = PAM_BUF_ERR;
167 sprintf (newdest, "%s/%s", dest, dent->d_name);
169 snprintf (newdest, sizeof (newdest), "%s/%s", dest, dent->d_name);
172 /* If it's a directory, recurse. */
173 if (S_ISDIR(st.st_mode))
175 retval = create_homedir(pwd, newsource, newdest);
178 free(newsource); newsource = NULL;
179 free(newdest); newdest = NULL;
182 if (retval != PAM_SUCCESS)
190 /* If it's a symlink, create a new link. */
191 if (S_ISLNK(st.st_mode))
195 char *pointed = NULL;
200 pointed = malloc(size);
201 if (pointed == NULL) {
206 pointedlen = readlink(newsource, pointed, size);
207 if (pointedlen < 0) break;
208 if (pointedlen < size) break;
216 pointed[pointedlen] = 0;
218 char pointed[PATH_MAX];
219 memset(pointed, 0, sizeof(pointed));
221 pointedlen = readlink(newsource, pointed, sizeof(pointed) - 1);
224 if (pointedlen >= 0) {
225 if(symlink(pointed, newdest) == 0)
227 if (lchown(newdest, pwd->pw_uid, pwd->pw_gid) != 0)
229 pam_syslog(NULL, LOG_DEBUG,
230 "unable to change perms on link %s: %m", newdest);
237 return PAM_PERM_DENIED;
245 free(newsource); newsource = NULL;
246 free(newdest); newdest = NULL;
251 /* If it's not a regular file, it's probably not a good idea to create
252 * the new device node, FIFO, or whatever it is. */
253 if (!S_ISREG(st.st_mode))
256 free(newsource); newsource = NULL;
257 free(newdest); newdest = NULL;
262 /* Open the source file */
263 if ((srcfd = open(newsource, O_RDONLY)) < 0 || fstat(srcfd, &st) != 0)
265 pam_syslog(NULL, LOG_DEBUG,
266 "unable to open src file %s: %m", newsource);
270 free(newsource); newsource = NULL;
271 free(newdest); newdest = NULL;
274 return PAM_PERM_DENIED;
276 if (stat(newsource, &st) != 0)
278 pam_syslog(NULL, LOG_DEBUG, "unable to stat src file %s: %m",
284 free(newsource); newsource = NULL;
285 free(newdest); newdest = NULL;
288 return PAM_PERM_DENIED;
291 /* Open the dest file */
292 if ((destfd = open(newdest, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0)
294 pam_syslog(NULL, LOG_DEBUG,
295 "unable to open dest file %s: %m", newdest);
300 free(newsource); newsource = NULL;
301 free(newdest); newdest = NULL;
303 return PAM_PERM_DENIED;
306 /* Set the proper ownership and permissions for the module. We make
307 the file a+w and then mask it with the set mask. This preseves
309 if (fchmod(destfd, (st.st_mode | 0222) & (~u_mask)) != 0 ||
310 fchown(destfd, pwd->pw_uid, pwd->pw_gid) != 0)
312 pam_syslog(NULL, LOG_DEBUG,
313 "unable to change perms on copy %s: %m", newdest);
319 free(newsource); newsource = NULL;
320 free(newdest); newdest = NULL;
323 return PAM_PERM_DENIED;
329 res = pam_modutil_read(srcfd, remark, sizeof(remark));
335 if (pam_modutil_write(destfd, remark, res) == res)
339 /* If we get here, pam_modutil_read returned a -1 or
340 pam_modutil_write returned something unexpected. */
341 pam_syslog(NULL, LOG_DEBUG, "unable to perform IO: %m");
347 free(newsource); newsource = NULL;
348 free(newdest); newdest = NULL;
351 return PAM_PERM_DENIED;
358 free(newsource); newsource = NULL;
359 free(newdest); newdest = NULL;
365 retval = PAM_SUCCESS;
369 if (chmod(dest, 0777 & (~u_mask)) != 0 ||
370 chown(dest, pwd->pw_uid, pwd->pw_gid) != 0)
372 pam_syslog(NULL, LOG_DEBUG,
373 "unable to change perms on directory %s: %m", dest);
374 return PAM_PERM_DENIED;
381 main(int argc, char *argv[])
383 const struct passwd *pwd;
387 fprintf(stderr, "Usage: %s <username> [<umask> [<skeldir>]]\n", argv[0]);
388 return PAM_SESSION_ERR;
391 pwd = getpwnam(argv[1]);
393 pam_syslog(NULL, LOG_ERR, "User unknown.");
394 return PAM_CRED_INSUFFICIENT;
400 u_mask = strtoul(argv[2], &eptr, 0);
401 if (errno != 0 || *eptr != '\0') {
402 pam_syslog(NULL, LOG_ERR, "Bogus umask value %s", argv[2]);
403 return PAM_SESSION_ERR;
408 if (strlen(argv[3]) >= sizeof(skeldir)) {
409 pam_syslog(NULL, LOG_ERR, "Too long skeldir path.");
410 return PAM_SESSION_ERR;
412 strcpy(skeldir, argv[3]);
415 /* Stat the home directory, if something exists then we assume it is
416 correct and return a success */
417 if (stat(pwd->pw_dir, &st) == 0)
420 return create_homedir(pwd, skeldir, pwd->pw_dir);