From: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
To: ltp@lists.linux.it
Subject: [LTP] [PATCH] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS
Date: Fri, 14 Jun 2019 18:32:27 +0800 [thread overview]
Message-ID: <5D0377BB.1060605@cn.fujitsu.com> (raw)
In-Reply-To: <20190523115236.GD30616@rei.lan>
Hi Cyril
Sorry for the late reply.
> Hi!
>> +#define IPC_ENV_VAR "LTP_IPC_PATH"
>> +
>> +static void check_no_new_privs(int val)
>> +{
>> + TEST(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0));
>> + if (TST_RET == val)
>> + tst_res(TPASS,
>> + "prctl(PR_GET_NO_NEW_PRIVS) got %d "
>> + "when no_new_privs was %d", val, val);
>> + else
>> + tst_res(TFAIL | TTERRNO,
>> + "prctl(PR_GET_NO_NEW_PRIVS) expected %d got %ld",
>> + val, TST_RET);
>> + return;
> This return is useless.
OK. I will remove it and add check for /proc/[pid]/status for NoNewPrivs.
>> +}
>> +
>> +static void do_prctl(void)
>> +{
>> + char path[4096];
>> + char ipc_env_var[1024];
>> + char *const argv[] = {"prctl06_execve", "parent process", NULL};
>> + char *const childargv[] = {"prctl06_execve", "child process", NULL};
>> + char *const envp[] = {"LTP_TEST_ENV_VAR=test", ipc_env_var, NULL };
>> + cap_t caps = cap_init();
>> + cap_value_t capList = CAP_SETGID;
>> + unsigned int num_caps = 1;
>> + int childpid;
>> +
>> + cap_set_flag(caps, CAP_EFFECTIVE, num_caps,&capList, CAP_SET);
>> + cap_set_flag(caps, CAP_INHERITABLE, num_caps,&capList, CAP_SET);
>> + cap_set_flag(caps, CAP_PERMITTED, num_caps,&capList, CAP_SET);
>> +
>> + if (cap_set_proc(caps))
>> + tst_brk(TFAIL | TERRNO,
>> + "cap_set_flag(CAP_SETGID) failed");
> You cannot use tst_brk with TFAIL, the best you can do here is to use
> tst_ret(TFAIL ... ) then return; as you do below.
>
I got it.
>> + tst_res(TINFO, "cap_set_flag(CAP_SETGID) succeeded");
>> +
>> + check_no_new_privs(0);
>> +
>> + TEST(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
>> + if (TST_RET == -1) {
>> + tst_res(TFAIL | TTERRNO, "prctl(PR_SET_NO_NEW_PRIVS) failed");
>> + return;
>> + }
>> + tst_res(TPASS, "prctl(PR_SET_NO_NEW_PRIVS) succeeded");
>> +
>> + check_no_new_privs(1);
>> +
>> + if (tst_get_path("prctl06_execve", path, sizeof(path)))
>> + tst_brk(TCONF, "Couldn't find prctl_execve in $PATH");
> If the path to the binary is in $PATH you don't have to execute the
> binary by an absolute path, passing it's name to execve() should
> suffice.
>
It sounds reasonable. I will pass binary_name directly.
>> + sprintf(ipc_env_var, IPC_ENV_VAR "=%s", getenv(IPC_ENV_VAR));
>> + childpid = SAFE_FORK();
>> + if (childpid == 0) {
>> + check_no_new_privs(1);
> Maybe we can pass a "child" string here and "parent"
> string in the cases above so that we can print if the
> check was done in child/parent in the tst_res() inside
> of this function.
>
Ok. Pass a "child" or "parent" string can make it more clear for user.
>> + execve(path, childargv, envp);
>> + tst_brk(TFAIL | TERRNO,
>> + "child process failed to execute prctl_execve");
>> +
>> + } else {
>> + tst_reap_children();
>> + execve(path, argv, envp);
>> + tst_brk(TFAIL | TERRNO,
>> + "parent process failed to execute prctl_execve");
>> + }
>> + cap_free(caps);
>> + return;
>> +}
>> +
>> +static void verify_prctl(void)
>> +{
>> + int pid;
>> +
>> + pid = SAFE_FORK();
>> + if (pid == 0) {
>> + do_prctl();
>> + exit(0);
>> + }
>> + tst_reap_children();
>> + return;
>> +}
>> +
>> +static struct tst_test test = {
>> + .test_all = verify_prctl,
>> + .forks_child = 1,
>> + .needs_root = 1,
>> + .child_needs_reinit = 1,
>> +};
>> diff --git a/testcases/kernel/syscalls/prctl/prctl06_execve.c b/testcases/kernel/syscalls/prctl/prctl06_execve.c
>> new file mode 100644
>> index 000000000..84c28551c
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/prctl/prctl06_execve.c
>> @@ -0,0 +1,46 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved.
>> + * Author: Yang Xu<xuyang2018.jy@cn.fujitsu.com>
>> + *
>> + * dummy program which is used by prctl06 testcase
>> + */
>> +#define TST_NO_DEFAULT_MAIN
>> +#include<stdlib.h>
>> +#include<sys/types.h>
>> +#include<unistd.h>
>> +#include<errno.h>
>> +#include<pwd.h>
>> +#include "tst_test.h"
>> +
>> +int main(int argc, char **argv)
>> +{
>> + struct passwd *pw;
>> + uid_t nobody_uid;
>> + gid_t nobody_gid;
>> +
>> + tst_reinit();
>> + if (argc != 2)
>> + tst_brk(TFAIL, "argc is %d, expected 2", argc);
>> +
>> + pw = SAFE_GETPWNAM("nobody");
>> + nobody_uid = pw->pw_uid;
>> + nobody_gid = pw->pw_gid;
>> + /*positive check*/
>> + TEST(setgid(nobody_gid));
>> + if (TST_RET == -1)
>> + tst_res(TFAIL | TTERRNO,
>> + "%s setgid(%d) isn't permmit", argv[1], nobody_gid);
>> + else
>> + tst_res(TPASS, "%s setgid(%d) succeed expectedly",
>> + argv[1], nobody_gid);
>> + /*negative check*/
>> + TEST(setuid(nobody_uid));
>> + if (TST_RET == -1)
>> + tst_res(TPASS | TTERRNO,
>> + "%s setuid(%d) isn't permmit", argv[1], nobody_uid);
>> + else
>> + tst_res(TFAIL, " %s setuid(%d) succeed unexpectedly",
>> + argv[1], nobody_gid);
>> + return 0;
>> +}
> I do not think that this is actually testing the prctl in question. Here
> we actually check that capabilities were inherited over fork() + exec().
>
> As far as I understand the PR_SET_NO_NEW_PRIVS it has been designed
> expecially to avoid misuse of capabilities associated with a particular
> file.
>
> So the check would have to do:
>
> 1. copy the prctl06_execve binary to a $PWD
> 2. chmod it with S_ISUID, S_ISGID
> 3. the prct06_execve would be executed under user/group nobody
> 4. the prct06_execve itself would check if it gained root priviledges
>
I have seen documention about PR_SET_NO_NEW_PRIVS. I think your way is right. But I have reset this capabilities
and only keep CAP_SETGID, so when I set no_new_privs to 1, prctl06_execve inherits CAP_SETGID but not gain CAP_SETUID.
> And we can do the same for capablities as well.
>
> I guess that we would have to format a device and mount it with support
> for capabilities for the test, since as far as I can tell you cannot
> usually do neither of set-usr-id or add capabilites to files in /tmp/.
Yes. In /tmp, these make no sense. I will follow by your advise.
Thanks
Yang Xu
next prev parent reply other threads:[~2019-06-14 10:32 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-05-09 12:21 [LTP] [PATCH] syscalls/prctl06.c: New test for prctl() with PR_{SET, GET}_NO_NEW_PRIVS Yang Xu
2019-05-22 10:18 ` xuyang
2019-05-23 11:52 ` Cyril Hrubis
2019-06-14 10:32 ` Yang Xu [this message]
2019-07-09 10:53 ` Cyril Hrubis
2019-07-05 22:48 ` [LTP] [PATCH RESEND] " Yang Xu
2019-07-10 10:52 ` Cyril Hrubis
2019-07-11 7:57 ` Yang Xu
2019-07-11 11:34 ` Cyril Hrubis
2019-07-12 4:34 ` Yang Xu
2019-07-12 4:53 ` [LTP] [PATCH v3] syscalls/prctl06: " Yang Xu
2019-07-15 15:49 ` Cyril Hrubis
2019-07-16 5:32 ` Yang Xu
2019-07-10 9:42 ` [LTP] [PATCH] syscalls/prctl06.c: " Yang Xu
2019-06-19 10:58 ` [LTP] [PATCH v2] " Yang Xu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5D0377BB.1060605@cn.fujitsu.com \
--to=xuyang2018.jy@cn.fujitsu.com \
--cc=ltp@lists.linux.it \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.