diff options
-rw-r--r-- | modules/pam_mkhomedir/mkhomedir_helper.c | 149 |
1 files changed, 113 insertions, 36 deletions
diff --git a/modules/pam_mkhomedir/mkhomedir_helper.c b/modules/pam_mkhomedir/mkhomedir_helper.c index fac48a1f..3bbcb0cf 100644 --- a/modules/pam_mkhomedir/mkhomedir_helper.c +++ b/modules/pam_mkhomedir/mkhomedir_helper.c @@ -26,22 +26,75 @@ #include <security/pam_ext.h> #include <security/pam_modutil.h> +struct dir_spec { + int fd; + char *path; +}; + static unsigned long u_mask = 0022; static char skeldir[BUFSIZ] = "/etc/skel"; -static int create_homedir(const struct passwd *, mode_t, const char *, - const char *); +static int create_homedir(struct dir_spec *, const struct passwd *, mode_t, + const char *, const char *); static int -copy_entry(const struct passwd *pwd, mode_t dir_mode, - const char *source, const char *dest, struct dirent *dent) +dir_spec_open(struct dir_spec *spec, const char *path) +{ + spec->path = strdup(path); + if (spec->path == NULL) + goto fail; + + spec->fd = open(spec->path, O_DIRECTORY); + if (spec->fd == -1) + { + free(spec->path); + goto fail; + } + + return 0; +fail: + spec->path = NULL; + spec->fd = -1; + return -1; +} + +static int +dir_spec_at(struct dir_spec *spec, struct dir_spec *parent, const char *path) +{ + if (asprintf(&spec->path, "%s/%s", parent->path, path) < 0) + goto fail; + spec->fd = openat(parent->fd, path, O_DIRECTORY | O_NOFOLLOW); + if (spec->fd == -1) + { + free(spec->path); + goto fail; + } + + return 0; +fail: + spec->path = NULL; + spec->fd = -1; + return -1; +} + +static void +dir_spec_close(struct dir_spec *spec) +{ + if (spec->fd != -1) + close(spec->fd); + free(spec->path); +} + +static int +copy_entry(struct dir_spec *parent, const struct passwd *pwd, mode_t dir_mode, + const char *source, struct dirent *dent) { char remark[BUFSIZ]; int srcfd = -1, destfd = -1; int res; int retval = PAM_SESSION_ERR; struct stat st; - char *newsource = NULL, *newdest = NULL; + char *newsource = NULL; /* Determine what kind of file it is. */ if (asprintf(&newsource, "%s/%s", source, dent->d_name) < 0) @@ -57,18 +110,11 @@ copy_entry(const struct passwd *pwd, mode_t dir_mode, goto go_out; } - /* We'll need the new file's name. */ - if (asprintf(&newdest, "%s/%s", dest, dent->d_name) < 0) - { - pam_syslog(NULL, LOG_CRIT, "asprintf failed for 'newdest'"); - retval = PAM_BUF_ERR; - goto go_out; - } - /* If it's a directory, recurse. */ if (S_ISDIR(st.st_mode)) { - retval = create_homedir(pwd, dir_mode & (~u_mask), newsource, newdest); + retval = create_homedir(parent, pwd, dir_mode & (~u_mask), newsource, + dent->d_name); goto go_out; } @@ -108,23 +154,25 @@ copy_entry(const struct passwd *pwd, mode_t dir_mode, if (pointedlen >= 0) { - if (symlink(pointed, newdest) != 0) + if (symlinkat(pointed, parent->fd, dent->d_name) != 0) { retval = errno == EEXIST ? PAM_SUCCESS : PAM_PERM_DENIED; if (retval != PAM_SUCCESS) - pam_syslog(NULL, LOG_DEBUG, - "unable to create link %s: %m", newdest); + pam_syslog(NULL, LOG_DEBUG, "unable to create link %s/%s: %m", + parent->path, dent->d_name); #ifndef PATH_MAX free(pointed); #endif goto go_out; } - if (lchown(newdest, pwd->pw_uid, pwd->pw_gid) != 0) + if (fchownat(parent->fd, dent->d_name, pwd->pw_uid, pwd->pw_gid, + AT_SYMLINK_NOFOLLOW) != 0) { pam_syslog(NULL, LOG_DEBUG, - "unable to change perms on link %s: %m", newdest); + "unable to change perms on link %s/%s: %m", + parent->path, dent->d_name); #ifndef PATH_MAX free(pointed); #endif @@ -157,12 +205,13 @@ copy_entry(const struct passwd *pwd, mode_t dir_mode, } /* Open the dest file */ - if ((destfd = open(newdest, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) + if ((destfd = openat(parent->fd, dent->d_name, + O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600)) < 0) { retval = errno == EEXIST ? PAM_SUCCESS : PAM_PERM_DENIED; if (retval != PAM_SUCCESS) - pam_syslog(NULL, LOG_DEBUG, - "unable to open dest file %s: %m", newdest); + pam_syslog(NULL, LOG_DEBUG, "unable to open dest file %s/%s: %m", + parent->path, dent->d_name); goto go_out; } @@ -172,8 +221,8 @@ copy_entry(const struct passwd *pwd, mode_t dir_mode, if (fchmod(destfd, (st.st_mode | 0222) & (~u_mask)) != 0 || fchown(destfd, pwd->pw_uid, pwd->pw_gid) != 0) { - pam_syslog(NULL, LOG_DEBUG, - "unable to change perms on copy %s: %m", newdest); + pam_syslog(NULL, LOG_DEBUG, "unable to change perms on copy %s/%s: %m", + parent->path, dent->d_name); retval = PAM_PERM_DENIED; goto go_out; } @@ -200,6 +249,8 @@ copy_entry(const struct passwd *pwd, mode_t dir_mode, } while (res != 0); + retval = PAM_SUCCESS; + go_out: if (srcfd >= 0) close(srcfd); @@ -207,29 +258,36 @@ copy_entry(const struct passwd *pwd, mode_t dir_mode, close(destfd); free(newsource); - free(newdest); return retval; } /* Do the actual work of creating a home dir */ static int -create_homedir(const struct passwd *pwd, mode_t dir_mode, - const char *source, const char *dest) +create_homedir(struct dir_spec *parent, const struct passwd *pwd, + mode_t dir_mode, const char *source, const char *dest) { DIR *d = NULL; struct dirent *dent; + struct dir_spec base; int retval = PAM_SESSION_ERR; /* Create the new directory */ - if (mkdir(dest, 0700)) + if (mkdirat(parent->fd, dest, 0700)) { if (errno == EEXIST) return PAM_SUCCESS; - pam_syslog(NULL, LOG_ERR, "unable to create directory %s: %m", dest); + pam_syslog(NULL, LOG_ERR, "unable to create directory %s/%s: %m", + parent->path, dest); return PAM_PERM_DENIED; } + if (dir_spec_at(&base, parent, dest) < 0) + { + retval = PAM_PERM_DENIED; + goto go_out; + } + /* See if we need to copy the skel dir over. */ if ((source == NULL) || (strlen(source) == 0)) { @@ -253,7 +311,7 @@ create_homedir(const struct passwd *pwd, mode_t dir_mode, strcmp(dent->d_name,"..") == 0) continue; - retval = copy_entry(pwd, dir_mode, source, dest, dent); + retval = copy_entry(&base, pwd, dir_mode, source, dent); if (retval != PAM_SUCCESS) goto go_out; } @@ -264,14 +322,18 @@ create_homedir(const struct passwd *pwd, mode_t dir_mode, if (d != NULL) closedir(d); - if (chmod(dest, dir_mode) != 0 || - chown(dest, pwd->pw_uid, pwd->pw_gid) != 0) + if (fchmodat(parent->fd, dest, dir_mode, AT_SYMLINK_NOFOLLOW) != 0 || + fchownat(parent->fd, dest, pwd->pw_uid, pwd->pw_gid, + AT_SYMLINK_NOFOLLOW) != 0) { pam_syslog(NULL, LOG_DEBUG, - "unable to change perms on directory %s: %m", dest); - return PAM_PERM_DENIED; + "unable to change perms on directory %s/%s: %m", + parent->path, dest); + retval = PAM_PERM_DENIED; } + dir_spec_close(&base); + return retval; } @@ -280,9 +342,24 @@ create_homedir_helper(const struct passwd *_pwd, mode_t home_mode, const char *_skeldir, const char *_homedir) { int retval = PAM_SESSION_ERR; + struct dir_spec base; + char *cp = strrchr(_homedir, '/'); + + *cp = '\0'; + retval = dir_spec_open(&base, cp == _homedir ? "/" : _homedir); + if (retval < 0) + { + pam_syslog(NULL, LOG_DEBUG, + "unable to open parent of home directory %s: %m", _homedir); + retval = PAM_PERM_DENIED; + goto go_out; + } + *cp = '/'; - retval = create_homedir(_pwd, home_mode, _skeldir, _homedir); + retval = create_homedir(&base, _pwd, home_mode, _skeldir, cp + 1); + go_out: + dir_spec_close(&base); return retval; } @@ -363,7 +440,7 @@ main(int argc, char *argv[]) home_mode = 0777 & ~u_mask; if (pwd->pw_dir[0] != '/') { - pam_syslog(NULL, LOG_ERR, "Relative home directory %s", pwd->pw_dir); + pam_syslog(NULL, LOG_ERR, "Relative user home directory %s", pwd->pw_dir); return PAM_SESSION_ERR; } |