[lxc-devel] [lxd/master] seccomp: ensure correct owner on __NR_mknod{at}

brauner on Github lxc-bot at linuxcontainers.org
Thu Jun 27 22:53:43 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 417 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190627/85fb3744/attachment.bin>
-------------- next part --------------
From cd21241f20074f9a4f1200577f6e1e4e7fd791a3 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 27 Jun 2019 23:40:06 +0200
Subject: [PATCH] seccomp: ensure correct owner on __NR_mknod{at}
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Reported-by: Stéphane Graber <stgraber at ubuntu.com>
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/container_lxc.go  |   8 ++++
 lxd/main_forkmknod.go | 100 +++++++++++++++++++++++++-----------------
 lxd/seccomp.go        |  60 ++++++++++++++++++++++++-
 3 files changed, 126 insertions(+), 42 deletions(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index c9ed8937e8..552673091f 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -7578,6 +7578,14 @@ func (c *containerLXC) InsertSeccompUnixDevice(prefix string, m types.Device, pi
 		return err
 	}
 
+	err, uid, gid := taskUidGid(pid)
+	if err != nil {
+		return err
+	}
+
+	m["uid"] = fmt.Sprintf("%d", uid)
+	m["gid"] = fmt.Sprintf("%d", gid)
+
 	prefixPath = strings.TrimPrefix(prefixPath, rootPath)
 	m["path"] = filepath.Join(rootPath, prefixPath, m["path"])
 	paths, err := c.createUnixDevice(prefix, m, true)
diff --git a/lxd/main_forkmknod.go b/lxd/main_forkmknod.go
index 8196df0c2e..f1f9403a84 100644
--- a/lxd/main_forkmknod.go
+++ b/lxd/main_forkmknod.go
@@ -26,66 +26,60 @@ import (
 extern char* advance_arg(bool required);
 extern int dosetns(int pid, char *nstype);
 
-static uid_t get_root_uid(pid_t pid)
+static uid_t get_host_uid(uid_t uid, pid_t pid)
 {
-	char *line = NULL;
-	size_t sz = 0;
-	uid_t nsid, hostid, range;
-	FILE *f;
+        __do_free char *line = NULL;
+        __do_fclose FILE *f = NULL;
+        size_t sz = 0;
 	char path[256];
+        uid_t nsid, hostid, range;
 
 	snprintf(path, sizeof(path), "/proc/%d/uid_map", pid);
 	f = fopen(path, "re");
 	if (!f)
 		return -1;
 
-	while (getline(&line, &sz, f) != -1) {
-		if (sscanf(line, "%u %u %u", &nsid, &hostid, &range) != 3)
-			continue;
+        while (getline(&line, &sz, f) != -1) {
+                if (sscanf(line, "%u %u %u", &nsid, &hostid, &range) != 3)
+                        continue;
 
-		if (nsid == 0)
+                if (nsid <= uid && nsid + range > uid) {
+                        hostid += uid - nsid;
 			return hostid;
-	}
-
-	nsid = -1;
+                }
+        }
 
-found:
-	fclose(f);
-	free(line);
-	return nsid;
+        return -1;
 }
 
-static gid_t get_root_gid(pid_t pid)
+static gid_t get_host_gid(uid_t gid, pid_t pid)
 {
-	char *line = NULL;
-	size_t sz = 0;
-	gid_t nsid, hostid, range;
-	FILE *f;
+        __do_free char *line = NULL;
+        __do_fclose FILE *f = NULL;
+        size_t sz = 0;
 	char path[256];
+        uid_t nsid, hostid, range;
 
 	snprintf(path, sizeof(path), "/proc/%d/gid_map", pid);
 	f = fopen(path, "re");
 	if (!f)
 		return -1;
 
-	while (getline(&line, &sz, f) != -1) {
-		if (sscanf(line, "%u %u %u", &nsid, &hostid, &range) != 3)
-			continue;
+        while (getline(&line, &sz, f) != -1) {
+                if (sscanf(line, "%u %u %u", &nsid, &hostid, &range) != 3)
+                        continue;
 
-		if (nsid == 0)
+                if (nsid <= gid && nsid + range > gid) {
+                        hostid += gid - nsid;
 			return hostid;
-	}
-
-	nsid = -1;
+                }
+        }
 
-found:
-	fclose(f);
-	free(line);
-	return nsid;
+        return -1;
 }
 
 static int chowmknod(const char *path, mode_t mode, dev_t dev,
-			  uid_t uid, gid_t gid)
+		     uid_t uid, gid_t gid)
 {
 	int ret;
 
@@ -93,7 +87,25 @@ static int chowmknod(const char *path, mode_t mode, dev_t dev,
 	if (ret)
 		return -1;
 
-	return chown(path, uid, gid);
+	ret = chown(path, uid, gid);
+	if (ret)
+		(void)unlink(path);
+	return ret;
+}
+
+static int fchowmknodat(int fd, const char *path, mode_t mode, dev_t dev,
+		        uid_t uid, gid_t gid)
+{
+	int ret;
+
+	ret = mknodat(fd, path, mode, dev);
+	if (ret)
+		return -1;
+
+	ret = fchownat(fd, path, uid, gid, 0);
+	if (ret)
+		(void)unlinkat(fd, path, 0);
+	return ret;
 }
 
 static int chdirchroot(pid_t pid)
@@ -170,14 +182,20 @@ void forkmknod()
 	mode = atoi(advance_arg(true));
 	dev = atoi(advance_arg(true));
 	target_host = advance_arg(true);
+	uid = atoi(advance_arg(true));
+	gid = atoi(advance_arg(true));
 
-	uid = get_root_uid(pid);
-	if (uid < 0)
-		fprintf(stderr, "No root uid found (%d)\n", uid);
+	if (uid < 0) {
+		uid = get_host_uid(0, pid);
+		if (uid < 0)
+			fprintf(stderr, "No root uid found (%d)\n", uid);
+	}
 
-	gid = get_root_gid(pid);
-	if (gid < 0)
-		fprintf(stderr, "No root gid found (%d)\n", gid);
+	if (gid < 0) {
+		gid = get_host_gid(0, pid);
+		if (gid < 0)
+			fprintf(stderr, "No root gid found (%d)\n", gid);
+	}
 
 	// dirname() can modify its argument
 	target_host_dup = strdup(target_host);
@@ -216,7 +234,7 @@ void forkmknod()
 
 	// basename() can modify its argument so accessing target_host is
 	// invalid from now on.
-	ret = mknodat(target_fd, basename(target_host), mode, dev);
+	ret = fchowmknodat(target_fd, basename(target_host), mode, dev, uid, gid);
 	if (ret) {
 		if (errno == EEXIST) {
 			fprintf(stderr, "%d", errno);
diff --git a/lxd/seccomp.go b/lxd/seccomp.go
index 9c4d9cf60c..c83f697b3c 100644
--- a/lxd/seccomp.go
+++ b/lxd/seccomp.go
@@ -11,6 +11,7 @@ import (
 	"os"
 	"path"
 	"path/filepath"
+	"regexp"
 	"strconv"
 	"strings"
 	"unsafe"
@@ -445,6 +446,57 @@ func NewSeccompServer(d *Daemon, path string) (*SeccompServer, error) {
 	return &s, nil
 }
 
+func taskUidGid(pid int) (error, int32, int32) {
+	status, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/status", pid))
+	if err != nil {
+		return err, -1, -1
+	}
+
+	reUid := regexp.MustCompile("Uid:\\s*([0-9]*)\\s*([0-9]*)")
+	reGid := regexp.MustCompile("Gid:\\s*([0-9]*)\\s*([0-9]*)")
+	var gid int32
+	var uid int32
+	uidFound := false
+	gidFound := false
+	for _, line := range strings.Split(string(status), "\n") {
+		if uidFound && gidFound {
+			break
+		}
+
+		if !uidFound {
+			m := reUid.FindStringSubmatch(line)
+			if m != nil && len(m) > 2 {
+				// effective uid
+				result, err := strconv.Atoi(m[2])
+				if err != nil {
+					return err, -1, -1
+				}
+
+				uid = int32(result)
+				uidFound = true
+				continue
+			}
+		}
+
+		if !gidFound {
+			m := reGid.FindStringSubmatch(line)
+			if m != nil && len(m) > 2 {
+				// effective gid
+				result, err := strconv.Atoi(m[2])
+				if err != nil {
+					return err, -1, -1
+				}
+
+				gid = int32(result)
+				gidFound = true
+				continue
+			}
+		}
+	}
+
+	return nil, uid, gid
+}
+
 func doMknod(c container, dev types.Device, requestPID int) (error, int) {
 	goErrno := int(-C.EPERM)
 
@@ -460,11 +512,17 @@ func doMknod(c container, dev types.Device, requestPID int) (error, int) {
 		return err, goErrno
 	}
 
+	err, uid, gid := taskUidGid(requestPID)
+	if err != nil {
+		return err, goErrno
+	}
+
 	prefixPath = strings.TrimPrefix(prefixPath, rootPath)
 	dev["hostpath"] = filepath.Join(c.RootfsPath(), rootPath, prefixPath, dev["path"])
 	errnoMsg, err := shared.RunCommand(util.GetExecPath(),
 		"forkmknod", dev["pid"], dev["path"],
-		dev["mode_t"], dev["dev_t"], dev["hostpath"])
+		dev["mode_t"], dev["dev_t"], dev["hostpath"],
+		fmt.Sprintf("%d", uid), fmt.Sprintf("%d", gid))
 	if err != nil {
 		tmp, err2 := strconv.Atoi(errnoMsg)
 		if err2 == nil {


More information about the lxc-devel mailing list