[CRIU] [PATCH 2/2] zdtm: Implement different per-thread credentials testcase
Andrey Vagin
avagin at virtuozzo.com
Mon Oct 16 21:55:20 MSK 2017
On Mon, Oct 02, 2017 at 01:16:19PM +0300, Cyrill Gorcunov wrote:
> From: Vitaly Ostrosablin <vostrosablin at virtuozzo.com>
>
> As requested, implement a test with two threads that have mismatching,
> non-root credentials, like Apache does.
>
> Signed-off-by: Vitaly Ostrosablin <vostrosablin at virtuozzo.com>
> Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
> ---
> test/zdtm/static/Makefile | 3 +
> test/zdtm/static/thread_different_uid_gid.c | 177 +++++++++++++++++++++++++
> test/zdtm/static/thread_different_uid_gid.desc | 1 +
> 3 files changed, 181 insertions(+)
> create mode 100644 test/zdtm/static/thread_different_uid_gid.c
> create mode 100644 test/zdtm/static/thread_different_uid_gid.desc
>
> diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile
> index 74ae02fc666c..0dc2ac48dc18 100644
> --- a/test/zdtm/static/Makefile
> +++ b/test/zdtm/static/Makefile
> @@ -193,6 +193,7 @@ TST_NOFILE := \
> pidns02 \
> pidns03 \
> config_inotify_irmap \
> + thread_different_uid_gid \
> # jobctl00 \
>
> include ../Makefile.inc
> @@ -497,6 +498,8 @@ pidns03: LDFLAGS += -pthread
>
> 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 000000000000..1951668fbdfe
> --- /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 at 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);
> +
Why do you check nothing here?
> + 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");
pr_perror() is used only when you want to print errno.
Here you have to use fail() or pr_err()
> + exit(1);
exit_group?
> + }
You have to check capabilities too. And, pls, set a custom capabilities
for a thread too.
> + 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;
> +}
> diff --git a/test/zdtm/static/thread_different_uid_gid.desc b/test/zdtm/static/thread_different_uid_gid.desc
> new file mode 100644
> index 000000000000..fa2c82d083ee
> --- /dev/null
> +++ b/test/zdtm/static/thread_different_uid_gid.desc
> @@ -0,0 +1 @@
> +{'flavor': 'h', 'flags': 'suid'}
> --
> 2.7.5
>
More information about the CRIU
mailing list