zdtm: Implement different per-thread credentials testcase

Submitted by Vitaly Ostrosablin on Sept. 12, 2017, 10:44 a.m.

Details

Message ID 20170912104435.3738-1-vostrosablin@virtuozzo.com
State Accepted
Series "zdtm: Implement different per-thread credentials testcase"
Headers show

Commit Message

Vitaly Ostrosablin Sept. 12, 2017, 10:44 a.m.
As requested, implement a test with two threads that have mismatching,
non-root credentials, like Apache does.

Signed-off-by: Vitaly Ostrosablin <vostrosablin@virtuozzo.com>
---
 test/zdtm/static/Makefile                   |   3 +
 test/zdtm/static/thread_different_uid_gid.c | 177 ++++++++++++++++++++++++++++
 2 files changed, 180 insertions(+)
 create mode 100644 test/zdtm/static/thread_different_uid_gid.c

Patch hide | download patch | download mbox

diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile
index 55f7b896d..9a8a87c6c 100644
--- a/test/zdtm/static/Makefile
+++ b/test/zdtm/static/Makefile
@@ -172,6 +172,7 @@  TST_NOFILE	:=				\
 		cr_veth				\
 		sock_peercred			\
 		s390x_mmap_high			\
+		thread_different_uid_gid	\
 #		jobctl00			\
 
 include ../Makefile.inc
@@ -468,6 +469,8 @@  shm-unaligned:		CFLAGS += -DZDTM_SHM_UNALIGNED
 
 s390x_regs_check:	LDFLAGS += -pthread
 
+thread_different_uid_gid:	LDLIBS += -pthread -lcap
+
 $(LIB):	force
 	$(Q) $(MAKE) -C $(LIBDIR)
 
diff --git a/test/zdtm/static/thread_different_uid_gid.c b/test/zdtm/static/thread_different_uid_gid.c
new file mode 100644
index 000000000..1951668fb
--- /dev/null
+++ b/test/zdtm/static/thread_different_uid_gid.c
@@ -0,0 +1,177 @@ 
+/*
+ * Check that we can dump a process with threads having mismatching UID/GID
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <grp.h>
+#include <pwd.h>
+#include <syscall.h>
+
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <pthread.h>
+
+#include "zdtmtst.h"
+
+#define exit_group(code)	\
+	syscall(__NR_exit_group, code)
+
+const char *test_doc	= "Acquire UID/GID setting caps, create thread and drop thread to non-root by changing UID/GID\n";
+const char *test_author	= "Vitaly Ostrosablin <vostrosablin@virtuozzo.com>";
+
+unsigned int gid;
+unsigned int uid;
+pthread_mutex_t mutex  = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t  cond   = PTHREAD_COND_INITIALIZER;
+
+int done = 0;
+
+void *chg_uid_gid(void *arg)
+{
+	int ret;
+	cap_t mycaps;
+	cap_t newcaps;
+	test_msg("Aux thread runs as UID: %d; GID: %d\n", getuid(), getgid());
+	newcaps = cap_from_text("cap_setgid,cap_setuid=+eip");
+	if (!newcaps)
+	{
+		pr_perror("Failed to get capability struct\n");
+		exit(1);
+	}
+	ret = cap_set_proc(newcaps);
+	if (ret) {
+		pr_perror("Failed to set capabilities for the process\n");
+		exit(1);
+	}
+	mycaps = cap_get_proc();
+	if (!mycaps) {
+		pr_perror("Failed to get child thread capabilities\n");
+		exit_group(2);
+	}
+	test_msg("Child capabilities: %s\n", cap_to_text(mycaps, NULL));
+	test_msg("Changing UID/GID in child thread to %d:%d\n", uid, gid);
+	ret = syscall(SYS_setresgid, gid, gid, gid);
+	if (ret >= 0) {
+		syscall(SYS_setresuid, uid, uid, uid);
+	}
+	if (ret < 0) {
+		pr_perror("Failed to change UID/GID\n");
+		exit_group(2);
+	}
+	gid = getgid();
+	uid = getuid();
+	test_msg("Now aux thread runs as UID: %d; GID: %d\n", uid, gid);
+	test_msg("Child thread is waiting for main thread's signal\n");
+	pthread_mutex_lock(&mutex);
+	while (!done)
+	{
+		pthread_cond_wait(&cond, &mutex);
+	}
+	pthread_mutex_unlock(&mutex);
+
+	test_msg("Child thread returns\n");
+	return NULL;
+}
+
+int main(int argc, char **argv)
+{
+
+	int ret;
+	cap_t newcaps;
+	struct group *group;
+	struct passwd *user;
+	pthread_t diff_cred_thread;
+	test_init(argc, argv);
+	int maingroup;
+	int mainuser;
+
+	if (getuid() != 0) {
+		fail("Test is expected to be run with root privileges\n");
+		exit(1);
+	}
+
+	test_daemon();
+	test_msg("Test daemonized\n");
+
+	test_msg("Acquiring CAP_SETGID and CAP_SETUID...\n");
+	newcaps = cap_from_text("cap_setgid,cap_setuid=+eip");
+	if (!newcaps)
+	{
+		pr_perror("Failed to get capability struct\n");
+		exit(1);
+	}
+	ret = cap_set_proc(newcaps);
+	if (ret) {
+		pr_perror("Failed to set capabilities for the process\n");
+		exit(1);
+	}
+	ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+	if (ret) {
+		pr_perror("Unable to set KEEPCAPS\n");
+		exit(1);
+	}
+
+	test_msg("Main thread runs as UID: %d; GID: %d\n", getuid(), getgid());
+	group = getgrnam("nogroup");
+	group = (group) ? group : getgrnam("nobody");
+	if (!group) {
+		pr_perror("Failed to get nogroup/nobody GID\n");
+		exit(1);
+	}
+	user = getpwnam("nobody");
+	if (!user) {
+		pr_perror("Failed to get nobody UID\n");
+		exit(1);
+	}
+	gid = group->gr_gid;
+	uid = user->pw_uid;
+	group = getgrnam("mail");
+	if (!group) {
+		pr_perror("Failed to get mail GID\n");
+		exit(1);
+	}
+	user = getpwnam("mail");
+	if (!user) {
+		pr_perror("Failed to get mail UID\n");
+		exit(1);
+	}
+	maingroup = group->gr_gid;
+	mainuser = user->pw_uid;
+
+	test_msg("Creating thread with different UID/GID\n");
+	ret = pthread_create(&diff_cred_thread, NULL, &chg_uid_gid, NULL);
+	sleep(5);
+	test_msg("Relinquishing root privileges\n");
+	ret = syscall(SYS_setresgid, maingroup, maingroup, maingroup);
+	if (ret >= 0) {
+		ret = syscall(SYS_setresuid, mainuser, mainuser, mainuser);
+	}
+	if (ret < 0) {
+		pr_perror("Failed to drop privileges\n");
+		exit(1);
+	}
+	test_msg("Now main thread runs as UID: %d; GID: %d\n", getuid(), getgid());
+	if (gid == getgid() || uid == getuid()) {
+		pr_perror("Thread credentials match\n");
+		exit(1);
+	}
+	test_msg("Main thread is waiting for signal\n");
+
+	test_waitsig();
+
+	if (gid == getgid() || uid == getuid()) {
+		pr_perror("Thread credentials match after restore\n");
+		exit(1);
+	}
+	pthread_mutex_lock(&mutex);
+	done = 1;
+	pthread_cond_signal(&cond);
+	pthread_mutex_unlock(&mutex);
+	pthread_join(diff_cred_thread, NULL);
+	test_msg("Threads joined\n");
+	pass();
+	return 0;
+}