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";
32 /* Do the actual work of creating a home dir */
34 create_homedir(const struct passwd *pwd,
35 const char *source, const char *dest)
40 int retval = PAM_SESSION_ERR;
42 /* Create the new directory */
43 if (mkdir(dest, 0700) && errno != EEXIST)
45 pam_syslog(NULL, LOG_ERR, "unable to create directory %s: %m", dest);
46 return PAM_PERM_DENIED;
49 /* See if we need to copy the skel dir over. */
50 if ((source == NULL) || (strlen(source) == 0))
56 /* Scan the directory */
60 pam_syslog(NULL, LOG_DEBUG, "unable to read directory %s: %m", source);
61 retval = PAM_PERM_DENIED;
65 for (dent = readdir(d); dent != NULL; dent = readdir(d))
72 char *newsource = NULL, *newdest = NULL;
73 /* track length of buffers */
74 int nslen = 0, ndlen = 0;
75 int slen = strlen(source), dlen = strlen(dest);
77 char newsource[PATH_MAX], newdest[PATH_MAX];
80 /* Skip some files.. */
81 if (strcmp(dent->d_name,".") == 0 ||
82 strcmp(dent->d_name,"..") == 0)
85 /* Determine what kind of file it is. */
87 nslen = slen + strlen(dent->d_name) + 2;
95 if ((newsource = malloc(nslen)) == NULL)
101 sprintf(newsource, "%s/%s", source, dent->d_name);
103 snprintf(newsource, sizeof(newsource), "%s/%s", source, dent->d_name);
106 if (lstat(newsource, &st) != 0)
118 /* We'll need the new file's name. */
120 ndlen = dlen + strlen(dent->d_name)+2;
124 retval = PAM_BUF_ERR;
128 if ((newdest = malloc(ndlen)) == NULL)
131 retval = PAM_BUF_ERR;
135 sprintf (newdest, "%s/%s", dest, dent->d_name);
137 snprintf (newdest, sizeof (newdest), "%s/%s", dest, dent->d_name);
140 /* If it's a directory, recurse. */
141 if (S_ISDIR(st.st_mode))
143 retval = create_homedir(pwd, newsource, newdest);
146 free(newsource); newsource = NULL;
147 free(newdest); newdest = NULL;
150 if (retval != PAM_SUCCESS)
158 /* If it's a symlink, create a new link. */
159 if (S_ISLNK(st.st_mode))
163 char *pointed = NULL;
168 pointed = malloc(size);
169 if (pointed == NULL) {
174 pointedlen = readlink(newsource, pointed, size);
175 if (pointedlen < 0) break;
176 if (pointedlen < size) break;
184 pointed[pointedlen] = 0;
186 char pointed[PATH_MAX];
187 memset(pointed, 0, sizeof(pointed));
189 pointedlen = readlink(newsource, pointed, sizeof(pointed) - 1);
192 if (pointedlen >= 0) {
193 if(symlink(pointed, newdest) == 0)
195 if (lchown(newdest, pwd->pw_uid, pwd->pw_gid) != 0)
197 pam_syslog(NULL, LOG_DEBUG,
198 "unable to change perms on link %s: %m", newdest);
205 return PAM_PERM_DENIED;
213 free(newsource); newsource = NULL;
214 free(newdest); newdest = NULL;
219 /* If it's not a regular file, it's probably not a good idea to create
220 * the new device node, FIFO, or whatever it is. */
221 if (!S_ISREG(st.st_mode))
224 free(newsource); newsource = NULL;
225 free(newdest); newdest = NULL;
230 /* Open the source file */
231 if ((srcfd = open(newsource, O_RDONLY)) < 0 || fstat(srcfd, &st) != 0)
233 pam_syslog(NULL, LOG_DEBUG,
234 "unable to open src file %s: %m", newsource);
238 free(newsource); newsource = NULL;
239 free(newdest); newdest = NULL;
242 return PAM_PERM_DENIED;
244 if (stat(newsource, &st) != 0)
246 pam_syslog(NULL, LOG_DEBUG, "unable to stat src file %s: %m",
252 free(newsource); newsource = NULL;
253 free(newdest); newdest = NULL;
256 return PAM_PERM_DENIED;
259 /* Open the dest file */
260 if ((destfd = open(newdest, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0)
262 pam_syslog(NULL, LOG_DEBUG,
263 "unable to open dest file %s: %m", newdest);
268 free(newsource); newsource = NULL;
269 free(newdest); newdest = NULL;
271 return PAM_PERM_DENIED;
274 /* Set the proper ownership and permissions for the module. We make
275 the file a+w and then mask it with the set mask. This preseves
277 if (fchmod(destfd, (st.st_mode | 0222) & (~u_mask)) != 0 ||
278 fchown(destfd, pwd->pw_uid, pwd->pw_gid) != 0)
280 pam_syslog(NULL, LOG_DEBUG,
281 "unable to change perms on copy %s: %m", newdest);
287 free(newsource); newsource = NULL;
288 free(newdest); newdest = NULL;
291 return PAM_PERM_DENIED;
297 res = pam_modutil_read(srcfd, remark, sizeof(remark));
303 if (pam_modutil_write(destfd, remark, res) == res)
307 /* If we get here, pam_modutil_read returned a -1 or
308 pam_modutil_write returned something unexpected. */
309 pam_syslog(NULL, LOG_DEBUG, "unable to perform IO: %m");
315 free(newsource); newsource = NULL;
316 free(newdest); newdest = NULL;
319 return PAM_PERM_DENIED;
326 free(newsource); newsource = NULL;
327 free(newdest); newdest = NULL;
333 retval = PAM_SUCCESS;
337 if (chmod(dest, 0777 & (~u_mask)) != 0 ||
338 chown(dest, pwd->pw_uid, pwd->pw_gid) != 0)
340 pam_syslog(NULL, LOG_DEBUG,
341 "unable to change perms on directory %s: %m", dest);
342 return PAM_PERM_DENIED;
349 make_parent_dirs(char *dir, int make)
351 int rc = PAM_SUCCESS;
352 char *cp = strrchr(dir, '/');
355 if (!cp || cp == dir)
359 if (stat(dir, &st) && errno == ENOENT)
360 rc = make_parent_dirs(dir, 1);
363 if (rc != PAM_SUCCESS)
366 if (make && mkdir(dir, 0755) && errno != EEXIST) {
367 pam_syslog(NULL, LOG_ERR, "unable to create directory %s: %m", dir);
368 return PAM_PERM_DENIED;
375 main(int argc, char *argv[])
381 fprintf(stderr, "Usage: %s <username> [<umask> [<skeldir>]]\n", argv[0]);
382 return PAM_SESSION_ERR;
385 pwd = getpwnam(argv[1]);
387 pam_syslog(NULL, LOG_ERR, "User unknown.");
388 return PAM_CRED_INSUFFICIENT;
394 u_mask = strtoul(argv[2], &eptr, 0);
395 if (errno != 0 || *eptr != '\0') {
396 pam_syslog(NULL, LOG_ERR, "Bogus umask value %s", argv[2]);
397 return PAM_SESSION_ERR;
402 if (strlen(argv[3]) >= sizeof(skeldir)) {
403 pam_syslog(NULL, LOG_ERR, "Too long skeldir path.");
404 return PAM_SESSION_ERR;
406 strcpy(skeldir, argv[3]);
409 /* Stat the home directory, if something exists then we assume it is
410 correct and return a success */
411 if (stat(pwd->pw_dir, &st) == 0)
414 if (make_parent_dirs(pwd->pw_dir, 0) != PAM_SUCCESS)
415 return PAM_PERM_DENIED;
417 return create_homedir(pwd, skeldir, pwd->pw_dir);