+++ /dev/null
-From b6fef599bf8493480664b766040fa9b0d4b1e335 Mon Sep 17 00:00:00 2001
-From: William Hubbs <w.d.hubbs@gmail.com>
-Date: Fri, 20 Nov 2020 09:15:59 -0600
-Subject: [PATCH] checkpath: fix CVE-2018-21269
-
-This walks the directory path to the file we are going to manipulate to make
-sure that when we create the file and change the ownership and permissions
-we are working on the same file.
-Also, all non-terminal symbolic links must be owned by root. This will
-keep a non-root user from making a symbolic link as described in the
-bug. If root creates the symbolic link, it is assumed to be trusted.
-
-On non-linux platforms, we no longer follow non-terminal symbolic links
-by default. If you need to do that, add the -s option on the checkpath
-command line, but keep in mind that this is not secure.
-
-This fixes #201.
-
-[Patch taken from upstream:
-https://github.com/OpenRC/openrc/commit/b6fef599bf8493480664b766040fa9b0d4b1e335]
-Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>
----
- man/openrc-run.8 | 6 +++
- src/rc/checkpath.c | 103 ++++++++++++++++++++++++++++++++++++++++++---
- 2 files changed, 102 insertions(+), 7 deletions(-)
-
-diff --git a/man/openrc-run.8 b/man/openrc-run.8
-index 1102daaa..ec4b88de 100644
---- a/man/openrc-run.8
-+++ b/man/openrc-run.8
-@@ -461,6 +461,7 @@ Mark the service as inactive.
- .Op Fl p , -pipe
- .Op Fl m , -mode Ar mode
- .Op Fl o , -owner Ar owner
-+.Op Fl s , -symlinks
- .Op Fl W , -writable
- .Op Fl q , -quiet
- .Ar path ...
-@@ -481,6 +482,11 @@ or with names, and are separated by a colon.
- The truncate options (-D and -F) cause the directory or file to be
- cleared of all contents.
- .Pp
-+If -s is not specified on a non-linux platform, checkpath will refuse to
-+allow non-terminal symbolic links to exist in the path. This is for
-+security reasons so that a non-root user can't create a symbolic link to
-+a root-owned file and take ownership of that file.
-+.Pp
- If -W is specified, checkpath checks to see if the first path given on
- the command line is writable. This is different from how the test
- command in the shell works, because it also checks to make sure the file
-diff --git a/src/rc/checkpath.c b/src/rc/checkpath.c
-index 448c9cf8..ff54a892 100644
---- a/src/rc/checkpath.c
-+++ b/src/rc/checkpath.c
-@@ -16,6 +16,7 @@
- * except according to the terms contained in the LICENSE file.
- */
-
-+#define _GNU_SOURCE
- #include <sys/types.h>
- #include <sys/stat.h>
-
-@@ -23,6 +24,7 @@
- #include <fcntl.h>
- #include <getopt.h>
- #include <grp.h>
-+#include <libgen.h>
- #include <pwd.h>
- #include <stdio.h>
- #include <stdlib.h>
-@@ -44,7 +46,7 @@ typedef enum {
-
- const char *applet = NULL;
- const char *extraopts ="path1 [path2] [...]";
--const char *getoptstring = "dDfFpm:o:W" getoptstring_COMMON;
-+const char *getoptstring = "dDfFpm:o:sW" getoptstring_COMMON;
- const struct option longopts[] = {
- { "directory", 0, NULL, 'd'},
- { "directory-truncate", 0, NULL, 'D'},
-@@ -53,6 +55,7 @@ const struct option longopts[] = {
- { "pipe", 0, NULL, 'p'},
- { "mode", 1, NULL, 'm'},
- { "owner", 1, NULL, 'o'},
-+ { "symlinks", 0, NULL, 's'},
- { "writable", 0, NULL, 'W'},
- longopts_COMMON
- };
-@@ -64,15 +67,92 @@ const char * const longopts_help[] = {
- "Create a named pipe (FIFO) if not exists",
- "Mode to check",
- "Owner to check (user:group)",
-+ "follow symbolic links (irrelivent on linux)",
- "Check whether the path is writable or not",
- longopts_help_COMMON
- };
- const char *usagestring = NULL;
-
-+static int get_dirfd(char *path, bool symlinks) {
-+ char *ch;
-+ char *item;
-+ char *linkpath = NULL;
-+ char *path_dupe;
-+ char *str;
-+ int components = 0;
-+ int dirfd;
-+ int flags = 0;
-+ int new_dirfd;
-+ struct stat st;
-+ ssize_t linksize;
-+
-+ if (!path || *path != '/')
-+ eerrorx("%s: empty or relative path", applet);
-+ dirfd = openat(dirfd, "/", O_RDONLY);
-+ if (dirfd == -1)
-+ eerrorx("%s: unable to open the root directory: %s",
-+ applet, strerror(errno));
-+ path_dupe = xstrdup(path);
-+ ch = path_dupe;
-+ while (*ch) {
-+ if (*ch == '/')
-+ components++;
-+ ch++;
-+ }
-+ item = strtok(path_dupe, "/");
-+#ifdef O_PATH
-+ flags |= O_PATH;
-+#endif
-+ if (!symlinks)
-+ flags |= O_NOFOLLOW;
-+ flags |= O_RDONLY;
-+ while (dirfd > 0 && item && components > 1) {
-+ str = xstrdup(linkpath ? linkpath : item);
-+ new_dirfd = openat(dirfd, str, flags);
-+ if (new_dirfd == -1)
-+ eerrorx("%s: %s: could not open %s: %s", applet, path, str,
-+ strerror(errno));
-+ if (fstat(new_dirfd, &st) == -1)
-+ eerrorx("%s: %s: unable to stat %s: %s", applet, path, item,
-+ strerror(errno));
-+ if (S_ISLNK(st.st_mode) ) {
-+ if (st.st_uid != 0)
-+ eerrorx("%s: %s: synbolic link %s not owned by root",
-+ applet, path, str);
-+ linksize = st.st_size+1;
-+ if (linkpath)
-+ free(linkpath);
-+ linkpath = xmalloc(linksize);
-+ memset(linkpath, 0, linksize);
-+ if (readlinkat(new_dirfd, "", linkpath, linksize) != st.st_size)
-+ eerrorx("%s: symbolic link destination changed", applet);
-+ /*
-+ * now follow the symlink.
-+ */
-+ close(new_dirfd);
-+ } else {
-+ close(dirfd);
-+ dirfd = new_dirfd;
-+ free(linkpath);
-+ linkpath = NULL;
-+ item = strtok(NULL, "/");
-+ components--;
-+ }
-+ }
-+ free(path_dupe);
-+ if (linkpath) {
-+ free(linkpath);
-+ linkpath = NULL;
-+ }
-+ return dirfd;
-+}
-+
- static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
-- inode_t type, bool trunc, bool chowner, bool selinux_on)
-+ inode_t type, bool trunc, bool chowner, bool symlinks, bool selinux_on)
- {
- struct stat st;
-+ char *name = NULL;
-+ int dirfd;
- int fd;
- int flags;
- int r;
-@@ -93,14 +173,16 @@ static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
- #endif
- if (trunc)
- flags |= O_TRUNC;
-- readfd = open(path, readflags);
-+ xasprintf(&name, "%s", basename_c(path));
-+ dirfd = get_dirfd(path, symlinks);
-+ readfd = openat(dirfd, name, readflags);
- if (readfd == -1 || (type == inode_file && trunc)) {
- if (type == inode_file) {
- einfo("%s: creating file", path);
- if (!mode) /* 664 */
- mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
- u = umask(0);
-- fd = open(path, flags, mode);
-+ fd = openat(dirfd, name, flags, mode);
- umask(u);
- if (fd == -1) {
- eerror("%s: open: %s", applet, strerror(errno));
-@@ -122,7 +204,7 @@ static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
- strerror (errno));
- return -1;
- }
-- readfd = open(path, readflags);
-+ readfd = openat(dirfd, name, readflags);
- if (readfd == -1) {
- eerror("%s: unable to open directory: %s", applet,
- strerror(errno));
-@@ -140,7 +222,7 @@ static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
- strerror (errno));
- return -1;
- }
-- readfd = open(path, readflags);
-+ readfd = openat(dirfd, name, readflags);
- if (readfd == -1) {
- eerror("%s: unable to open fifo: %s", applet,
- strerror(errno));
-@@ -259,6 +341,7 @@ int main(int argc, char **argv)
- int retval = EXIT_SUCCESS;
- bool trunc = false;
- bool chowner = false;
-+ bool symlinks = false;
- bool writable = false;
- bool selinux_on = false;
-
-@@ -293,6 +376,11 @@ int main(int argc, char **argv)
- eerrorx("%s: owner `%s' not found",
- applet, optarg);
- break;
-+ case 's':
-+#ifndef O_PATH
-+ symlinks = true;
-+#endif
-+ break;
- case 'W':
- writable = true;
- break;
-@@ -320,7 +408,8 @@ int main(int argc, char **argv)
- while (optind < argc) {
- if (writable)
- exit(!is_writable(argv[optind]));
-- if (do_check(argv[optind], uid, gid, mode, type, trunc, chowner, selinux_on))
-+ if (do_check(argv[optind], uid, gid, mode, type, trunc, chowner,
-+ symlinks, selinux_on))
- retval = EXIT_FAILURE;
- optind++;
- }
---
-2.20.1
-