1429 lines · 35.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#define _GNU_SOURCE#include <errno.h>#include <fcntl.h>#include <grp.h>#include <limits.h>#include <sched.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/mount.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/wait.h>#include <unistd.h>#include <linux/unistd.h>#include "kselftest_harness.h" #ifndef FD_NSFS_ROOT#define FD_NSFS_ROOT -10003 /* Root of the nsfs filesystem */#endif TEST(nsfs_net_handle){	struct file_handle *handle;	int mount_id;	int ret;	int fd;	int ns_fd;	struct stat st1, st2; 	/* Drop to unprivileged uid/gid */	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */ 	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);	ASSERT_NE(handle, NULL); 	/* Open a namespace file descriptor */	ns_fd = open("/proc/self/ns/net", O_RDONLY);	ASSERT_GE(ns_fd, 0); 	/* Get handle for the namespace */	handle->handle_bytes = MAX_HANDLE_SZ;	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);	if (ret < 0 && errno == EOPNOTSUPP) {		SKIP(free(handle); close(ns_fd);		     return, "nsfs doesn't support file handles");	}	ASSERT_EQ(ret, 0);	ASSERT_GT(handle->handle_bytes, 0); 	/* Try to open using FD_NSFS_ROOT as unprivileged user */	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {		SKIP(free(handle); close(ns_fd);		     return,			   "open_by_handle_at with FD_NSFS_ROOT not supported");	}	if (fd < 0 && errno == EPERM) {		SKIP(free(handle); close(ns_fd);		     return,			   "Permission denied for unprivileged user (expected)");	}	ASSERT_GE(fd, 0); 	/* Verify we opened the correct namespace */	ASSERT_EQ(fstat(ns_fd, &st1), 0);	ASSERT_EQ(fstat(fd, &st2), 0);	ASSERT_EQ(st1.st_ino, st2.st_ino);	ASSERT_EQ(st1.st_dev, st2.st_dev); 	close(fd);	close(ns_fd);	free(handle);} TEST(nsfs_uts_handle){	struct file_handle *handle;	int mount_id;	int ret;	int fd;	int ns_fd;	struct stat st1, st2; 	/* Drop to unprivileged uid/gid */	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */ 	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);	ASSERT_NE(handle, NULL); 	/* Open UTS namespace file descriptor */	ns_fd = open("/proc/self/ns/uts", O_RDONLY);	ASSERT_GE(ns_fd, 0); 	/* Get handle for the namespace */	handle->handle_bytes = MAX_HANDLE_SZ;	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);	if (ret < 0 && errno == EOPNOTSUPP) {		SKIP(free(handle); close(ns_fd);		     return, "nsfs doesn't support file handles");	}	ASSERT_EQ(ret, 0);	ASSERT_GT(handle->handle_bytes, 0); 	/* Try to open using FD_NSFS_ROOT */	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {		SKIP(free(handle); close(ns_fd);		     return,			   "open_by_handle_at with FD_NSFS_ROOT not supported");	}	ASSERT_GE(fd, 0); 	/* Verify we opened the correct namespace */	ASSERT_EQ(fstat(ns_fd, &st1), 0);	ASSERT_EQ(fstat(fd, &st2), 0);	ASSERT_EQ(st1.st_ino, st2.st_ino);	ASSERT_EQ(st1.st_dev, st2.st_dev); 	close(fd);	close(ns_fd);	free(handle);} TEST(nsfs_ipc_handle){	struct file_handle *handle;	int mount_id;	int ret;	int fd;	int ns_fd;	struct stat st1, st2; 	/* Drop to unprivileged uid/gid */	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */ 	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);	ASSERT_NE(handle, NULL); 	/* Open IPC namespace file descriptor */	ns_fd = open("/proc/self/ns/ipc", O_RDONLY);	ASSERT_GE(ns_fd, 0); 	/* Get handle for the namespace */	handle->handle_bytes = MAX_HANDLE_SZ;	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);	if (ret < 0 && errno == EOPNOTSUPP) {		SKIP(free(handle); close(ns_fd);		     return, "nsfs doesn't support file handles");	}	ASSERT_EQ(ret, 0);	ASSERT_GT(handle->handle_bytes, 0); 	/* Try to open using FD_NSFS_ROOT */	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {		SKIP(free(handle); close(ns_fd);		     return,			   "open_by_handle_at with FD_NSFS_ROOT not supported");	}	ASSERT_GE(fd, 0); 	/* Verify we opened the correct namespace */	ASSERT_EQ(fstat(ns_fd, &st1), 0);	ASSERT_EQ(fstat(fd, &st2), 0);	ASSERT_EQ(st1.st_ino, st2.st_ino);	ASSERT_EQ(st1.st_dev, st2.st_dev); 	close(fd);	close(ns_fd);	free(handle);} TEST(nsfs_pid_handle){	struct file_handle *handle;	int mount_id;	int ret;	int fd;	int ns_fd;	struct stat st1, st2; 	/* Drop to unprivileged uid/gid */	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */ 	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);	ASSERT_NE(handle, NULL); 	/* Open PID namespace file descriptor */	ns_fd = open("/proc/self/ns/pid", O_RDONLY);	ASSERT_GE(ns_fd, 0); 	/* Get handle for the namespace */	handle->handle_bytes = MAX_HANDLE_SZ;	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);	if (ret < 0 && errno == EOPNOTSUPP) {		SKIP(free(handle); close(ns_fd);		     return, "nsfs doesn't support file handles");	}	ASSERT_EQ(ret, 0);	ASSERT_GT(handle->handle_bytes, 0); 	/* Try to open using FD_NSFS_ROOT */	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {		SKIP(free(handle); close(ns_fd);		     return,			   "open_by_handle_at with FD_NSFS_ROOT not supported");	}	ASSERT_GE(fd, 0); 	/* Verify we opened the correct namespace */	ASSERT_EQ(fstat(ns_fd, &st1), 0);	ASSERT_EQ(fstat(fd, &st2), 0);	ASSERT_EQ(st1.st_ino, st2.st_ino);	ASSERT_EQ(st1.st_dev, st2.st_dev); 	close(fd);	close(ns_fd);	free(handle);} TEST(nsfs_mnt_handle){	struct file_handle *handle;	int mount_id;	int ret;	int fd;	int ns_fd;	struct stat st1, st2; 	/* Drop to unprivileged uid/gid */	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */ 	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);	ASSERT_NE(handle, NULL); 	/* Open mount namespace file descriptor */	ns_fd = open("/proc/self/ns/mnt", O_RDONLY);	ASSERT_GE(ns_fd, 0); 	/* Get handle for the namespace */	handle->handle_bytes = MAX_HANDLE_SZ;	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);	if (ret < 0 && errno == EOPNOTSUPP) {		SKIP(free(handle); close(ns_fd);		     return, "nsfs doesn't support file handles");	}	ASSERT_EQ(ret, 0);	ASSERT_GT(handle->handle_bytes, 0); 	/* Try to open using FD_NSFS_ROOT */	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {		SKIP(free(handle); close(ns_fd);		     return,			   "open_by_handle_at with FD_NSFS_ROOT not supported");	}	ASSERT_GE(fd, 0); 	/* Verify we opened the correct namespace */	ASSERT_EQ(fstat(ns_fd, &st1), 0);	ASSERT_EQ(fstat(fd, &st2), 0);	ASSERT_EQ(st1.st_ino, st2.st_ino);	ASSERT_EQ(st1.st_dev, st2.st_dev); 	close(fd);	close(ns_fd);	free(handle);} TEST(nsfs_user_handle){	struct file_handle *handle;	int mount_id;	int ret;	int fd;	int ns_fd;	struct stat st1, st2; 	/* Drop to unprivileged uid/gid */	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */ 	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);	ASSERT_NE(handle, NULL); 	/* Open user namespace file descriptor */	ns_fd = open("/proc/self/ns/user", O_RDONLY);	ASSERT_GE(ns_fd, 0); 	/* Get handle for the namespace */	handle->handle_bytes = MAX_HANDLE_SZ;	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);	if (ret < 0 && errno == EOPNOTSUPP) {		SKIP(free(handle); close(ns_fd);		     return, "nsfs doesn't support file handles");	}	ASSERT_EQ(ret, 0);	ASSERT_GT(handle->handle_bytes, 0); 	/* Try to open using FD_NSFS_ROOT */	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {		SKIP(free(handle); close(ns_fd);		     return,			   "open_by_handle_at with FD_NSFS_ROOT not supported");	}	ASSERT_GE(fd, 0); 	/* Verify we opened the correct namespace */	ASSERT_EQ(fstat(ns_fd, &st1), 0);	ASSERT_EQ(fstat(fd, &st2), 0);	ASSERT_EQ(st1.st_ino, st2.st_ino);	ASSERT_EQ(st1.st_dev, st2.st_dev); 	close(fd);	close(ns_fd);	free(handle);} TEST(nsfs_cgroup_handle){	struct file_handle *handle;	int mount_id;	int ret;	int fd;	int ns_fd;	struct stat st1, st2; 	/* Drop to unprivileged uid/gid */	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */ 	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);	ASSERT_NE(handle, NULL); 	/* Open cgroup namespace file descriptor */	ns_fd = open("/proc/self/ns/cgroup", O_RDONLY);	if (ns_fd < 0) {		SKIP(free(handle); return, "cgroup namespace not available");	} 	/* Get handle for the namespace */	handle->handle_bytes = MAX_HANDLE_SZ;	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);	if (ret < 0 && errno == EOPNOTSUPP) {		SKIP(free(handle); close(ns_fd);		     return, "nsfs doesn't support file handles");	}	ASSERT_EQ(ret, 0);	ASSERT_GT(handle->handle_bytes, 0); 	/* Try to open using FD_NSFS_ROOT */	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {		SKIP(free(handle); close(ns_fd);		     return,			   "open_by_handle_at with FD_NSFS_ROOT not supported");	}	ASSERT_GE(fd, 0); 	/* Verify we opened the correct namespace */	ASSERT_EQ(fstat(ns_fd, &st1), 0);	ASSERT_EQ(fstat(fd, &st2), 0);	ASSERT_EQ(st1.st_ino, st2.st_ino);	ASSERT_EQ(st1.st_dev, st2.st_dev); 	close(fd);	close(ns_fd);	free(handle);} TEST(nsfs_time_handle){	struct file_handle *handle;	int mount_id;	int ret;	int fd;	int ns_fd;	struct stat st1, st2; 	/* Drop to unprivileged uid/gid */	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */ 	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);	ASSERT_NE(handle, NULL); 	/* Open time namespace file descriptor */	ns_fd = open("/proc/self/ns/time", O_RDONLY);	if (ns_fd < 0) {		SKIP(free(handle); return, "time namespace not available");	} 	/* Get handle for the namespace */	handle->handle_bytes = MAX_HANDLE_SZ;	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);	if (ret < 0 && errno == EOPNOTSUPP) {		SKIP(free(handle); close(ns_fd);		     return, "nsfs doesn't support file handles");	}	ASSERT_EQ(ret, 0);	ASSERT_GT(handle->handle_bytes, 0); 	/* Try to open using FD_NSFS_ROOT */	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {		SKIP(free(handle); close(ns_fd);		     return,			   "open_by_handle_at with FD_NSFS_ROOT not supported");	}	ASSERT_GE(fd, 0); 	/* Verify we opened the correct namespace */	ASSERT_EQ(fstat(ns_fd, &st1), 0);	ASSERT_EQ(fstat(fd, &st2), 0);	ASSERT_EQ(st1.st_ino, st2.st_ino);	ASSERT_EQ(st1.st_dev, st2.st_dev); 	close(fd);	close(ns_fd);	free(handle);} TEST(nsfs_user_net_namespace_isolation){	struct file_handle *handle;	int mount_id;	int ret;	int fd;	int ns_fd;	pid_t pid;	int status;	int pipefd[2];	char result; 	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);	ASSERT_NE(handle, NULL); 	/* Create pipe for communication */	ASSERT_EQ(pipe(pipefd), 0); 	/* Get handle for current network namespace */	ns_fd = open("/proc/self/ns/net", O_RDONLY);	ASSERT_GE(ns_fd, 0); 	handle->handle_bytes = MAX_HANDLE_SZ;	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);	if (ret < 0 && errno == EOPNOTSUPP) {		SKIP(free(handle); close(ns_fd); close(pipefd[0]);		     close(pipefd[1]);		     return, "nsfs doesn't support file handles");	}	ASSERT_EQ(ret, 0);	close(ns_fd); 	pid = fork();	ASSERT_GE(pid, 0); 	if (pid == 0) {		/* Child process */		close(pipefd[0]); 		/* First create new user namespace to drop privileges */		ret = unshare(CLONE_NEWUSER);		if (ret < 0) {			write(pipefd[1], "U",			      1); /* Unable to create user namespace */			close(pipefd[1]);			exit(0);		} 		/* Write uid/gid mappings to maintain some capabilities */		int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);		int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);		int setgroups_fd = open("/proc/self/setgroups", O_WRONLY); 		if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {			write(pipefd[1], "M", 1); /* Unable to set mappings */			close(pipefd[1]);			exit(0);		} 		/* Disable setgroups to allow gid mapping */		write(setgroups_fd, "deny", 4);		close(setgroups_fd); 		/* Map current uid/gid to root in the new namespace */		char mapping[64];		snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());		write(uid_map_fd, mapping, strlen(mapping));		close(uid_map_fd); 

Lines 1–500 of 1429.