From 71b0389fbb31833d827f5f0fec18880c2f602753 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Thu, 19 May 2022 13:52:22 +0300 Subject: [PATCH 1/2] mkhomedir: add support for pre-CVE-2020-10737 behavior Pre-CVE-2020-10737 behavior was used to allow creating home directories on NFS mounts when non-Kerberos authentication method is in use. This is exactly the case where a race condition addressed by the CVE-2020-10737 fix could have happened. However, there are legit use cases where this setup is needed. Add '-f' option to mkhomedir helper to activate previous behavior. In order to enable it, a change to oddjobd-mkhomedir.conf configuration file is needed by explicitly adding '-f' option to the executable file definition. Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2050079 Signed-off-by: Alexander Bokovoy --- src/mkhomedir.c | 16 +++++++++++++--- src/oddjobd-mkhomedir.conf.5.in | 9 +++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/mkhomedir.c b/src/mkhomedir.c index be85959..ac813a9 100644 --- a/src/mkhomedir.c +++ b/src/mkhomedir.c @@ -53,9 +53,11 @@ static const char *skel; static const char *skel_dir; static struct passwd *pwd; static mode_t override_umask; +static int owner_mkdir_first = 0; #define FLAG_POPULATE (1 << 0) #define FLAG_QUIET (1 << 1) +#define FLAG_OWNER_MKDIR_FIRST (1 << 2) /* Given the path of an item somewhere in the skeleton directory, create as * identical as possible a copy in the destination tree. */ @@ -158,7 +160,7 @@ copy_single_item(const char *source, const struct stat *sb, * target user just yet to avoid potential race conditions * involving symlink attacks when we copy over the skeleton * tree. */ - if (status->level == 0) { + if (status->level == 0 && !owner_mkdir_first) { uid = 0; gid = 0; } @@ -222,6 +224,9 @@ mkhomedir(const char *user, int flags) pwd->pw_dir); return HANDLER_INVALID_INVOCATION; } + if (flags & FLAG_OWNER_MKDIR_FIRST) { + owner_mkdir_first = 1; + } if ((lstat(pwd->pw_dir, &st) == -1) && (errno == ENOENT)) { /* Figure out which location we're using as a * template. */ @@ -237,7 +242,7 @@ mkhomedir(const char *user, int flags) int res = nftw(get_skel_dir(), copy_single_item, 5, FTW_PHYS); /* only now give ownership to the target user */ - if (res == 0) { + if (res == 0 && !owner_mkdir_first) { res = chown(pwd->pw_dir, pwd->pw_uid, pwd->pw_gid); } @@ -317,8 +322,11 @@ main(int argc, char **argv) umask(override_umask); skel_dir = "/etc/skel"; - while ((i = getopt(argc, argv, "nqs:u:")) != -1) { + while ((i = getopt(argc, argv, "nqfs:u:")) != -1) { switch (i) { + case 'f': + flags |= FLAG_OWNER_MKDIR_FIRST; + break; case 'n': flags &= ~FLAG_POPULATE; break; @@ -339,6 +347,8 @@ main(int argc, char **argv) break; default: fprintf(stderr, "Valid options:\n" + "-f\tCreate home directory initially owned by user, " + "not root. See man page for security issues.\n" "-n\tDo not populate home directories, " "just create them.\n" "-q\tDo not print messages when creating " diff --git a/src/oddjobd-mkhomedir.conf.5.in b/src/oddjobd-mkhomedir.conf.5.in index d7a2429..6e35ad5 100644 --- a/src/oddjobd-mkhomedir.conf.5.in +++ b/src/oddjobd-mkhomedir.conf.5.in @@ -10,6 +10,15 @@ directory. The mkhomedir helper itself accepts these options: .TP +-f +Restore behavior before CVE-2020-10737 was fixed: create the home directory +with user's ownership directly rather than create it as a root and only after +populating it change to the user's ownership. The former behavior is insecure +but may be used to allow creation of NFS-mounted home directories when +non-Kerberos authentication is in use. It is prone for a race condition that +could be exploited in the NFS-mounted home directories use case. To avoid +CVE-2020-10737, do not use \fB-f\fR option in production environments. +.TP -q Refrain from outputting the usual "Creating home directory..." message when it creates a home directory. -- 2.38.1 From b800e25258353dbb1a88506123c21ac3298fd2d0 Mon Sep 17 00:00:00 2001 From: Carlos Santos Date: Tue, 18 Oct 2022 08:59:16 -0300 Subject: [PATCH 2/2] Always set the home directory permissions according to HOME_MODE Currently the home directory permissions are set by taking the /etc/skel mode and masking it with HOME_MODE: override_umask = 0777 & ~get_umask(&configured_umask, "HOME_MODE"); stat(skel, &sb); /* performed by nftw() */ oddjob_selinux_mkdir(newpath, sb->st_mode & ~override_umask, uid, gid); The problem is that when HOME_MODE is more permissive than /etc/skel, the masking will not produce the desired result, e.g. skel_mode = 0755 HOME_MODE = 0775 override_umask = 0777 & ~HOME_MODE /* 0002 */ mode = skel_mode & ~override_umask /* 0755 & 0775 = 0755 */ In order to fix the problem, always use 0777 & ~override_umask for the top home directory. Signed-off-by: Carlos Santos Fixes: https://pagure.io/oddjob/issue/17 --- src/mkhomedir.c | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/mkhomedir.c b/src/mkhomedir.c index ac813a9..932918f 100644 --- a/src/mkhomedir.c +++ b/src/mkhomedir.c @@ -67,6 +67,7 @@ copy_single_item(const char *source, const struct stat *sb, { uid_t uid = pwd->pw_uid; gid_t gid = pwd->pw_gid; + mode_t mode = sb->st_mode & ~override_umask; int sfd, dfd, i, res; char target[PATH_MAX + 1], newpath[PATH_MAX + 1]; unsigned char buf[BUFSIZ]; @@ -112,8 +113,7 @@ copy_single_item(const char *source, const struct stat *sb, oddjob_set_selinux_file_creation_context(newpath, sb->st_mode | S_IFREG); - dfd = open(newpath, O_WRONLY | O_CREAT | O_EXCL, - sb->st_mode & ~override_umask); + dfd = open(newpath, O_WRONLY | O_CREAT | O_EXCL, mode); if (dfd != -1) { while ((i = read(sfd, buf, sizeof(buf))) > 0) { retry_write(dfd, buf, i); @@ -156,20 +156,22 @@ copy_single_item(const char *source, const struct stat *sb, } return 0; case FTW_D: - /* It's the home directory itself. Don't give it to the - * target user just yet to avoid potential race conditions - * involving symlink attacks when we copy over the skeleton - * tree. */ - if (status->level == 0 && !owner_mkdir_first) { - uid = 0; - gid = 0; - } - /* It's a directory. Make one with the same name and * permissions, but owned by the target user. */ - res = oddjob_selinux_mkdir(newpath, - sb->st_mode & ~override_umask, - uid, gid); + if (status->level == 0) { + /* It's the home directory itself. Use the configured + * (or overriden) mode, not the source mode & umask. */ + mode = 0777 & ~override_umask; + + /* Don't give it to the target user just yet to avoid + * potential race conditions involving symlink attacks + * when we copy over the skeleton tree. */ + if (!owner_mkdir_first) { + uid = 0; + gid = 0; + } + } + res = oddjob_selinux_mkdir(newpath, mode, uid, gid); /* on unexpected errors, or if the home directory itself * suddenly already exists, abort the copy operation. */ @@ -248,12 +250,8 @@ mkhomedir(const char *user, int flags) return res; } else { - if (stat(skel, &st) != 0) { - st.st_mode = S_IRWXU; - } if ((oddjob_selinux_mkdir(pwd->pw_dir, - st.st_mode & - ~override_umask, + 0777 & ~override_umask, pwd->pw_uid, pwd->pw_gid) != 0) && (errno != EEXIST)) { @@ -269,11 +267,11 @@ mkhomedir(const char *user, int flags) } static mode_t -get_umask(int *configured, const char *variable) +get_umask(int *configured, const char *variable, mode_t default_value) { FILE *fp; char buf[BUFSIZ], *p, *end; - mode_t mask = umask(0777); + mode_t mask = default_value; long tmp; size_t vlen = strlen(variable); @@ -315,11 +313,10 @@ main(int argc, char **argv) openlog(PACKAGE "-mkhomedir", LOG_PID, LOG_DAEMON); /* Unlike UMASK, HOME_MODE is the file mode, so needs to be reverted */ - override_umask = 0777 & ~get_umask(&configured_umask, "HOME_MODE"); + override_umask = 0777 & ~get_umask(&configured_umask, "HOME_MODE", 0); if (configured_umask == 0) { - override_umask = get_umask(&configured_umask, "UMASK"); + override_umask = get_umask(&configured_umask, "UMASK", 022); } - umask(override_umask); skel_dir = "/etc/skel"; while ((i = getopt(argc, argv, "nqfs:u:")) != -1) { -- 2.38.1