From: Djalal Harouni Date: Mon, 18 Aug 2014 16:59:20 +0000 (+0100) Subject: test: add the metadata tests inside new namespaces X-Git-Tag: upstream/0.20140911.160207utc~26 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8c2db5c76d7b7734a4c600e93527c01fa3cb4df5;p=platform%2Fcore%2Fsystem%2Fkdbus-bus.git test: add the metadata tests inside new namespaces Currently this only handles user namespace. Use it to inspect the dumped metadata/credentials. Signed-off-by: Djalal Harouni --- diff --git a/.gitignore b/.gitignore index e2bdc63..8103db9 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ test/test-kdbus-timeout test/test-kdbus-prio test/test-kdbus-sync test/test-kdbus-policy +test/test-kdbus-metadata-ns diff --git a/test/Makefile b/test/Makefile index af15649..38459a2 100644 --- a/test/Makefile +++ b/test/Makefile @@ -23,7 +23,8 @@ TESTS= \ test-kdbus-timeout \ test-kdbus-sync \ test-kdbus-prio \ - test-kdbus-policy + test-kdbus-policy \ + test-kdbus-metadata-ns all: $(TESTS) diff --git a/test/test-kdbus-metadata-ns.c b/test/test-kdbus-metadata-ns.c new file mode 100644 index 0000000..2ef414e --- /dev/null +++ b/test/test-kdbus-metadata-ns.c @@ -0,0 +1,292 @@ +/* Test metadata in new namespaces */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kdbus-util.h" +#include "kdbus-enum.h" + +/* Return: CHECK_OK, CHECK_ERR or CHECK_SKIP */ +static int __kdbus_clone_userns_test(const char *bus, struct conn *conn) +{ + int efd = -1; + pid_t pid; + int ret; + int status; + unsigned int uid = 65534; + int test_status = CHECK_ERR; + + ret = drop_privileges(uid, uid); + if (ret < 0) + goto out; + + /** + * Since we just dropped privileges, the dumpable flag was just + * cleared which makes the /proc/$clone_child/uid_map to be + * owned by root, hence any userns uid mapping will fail with + * -EPERM since the mapping will be done by uid 65534. + * + * To avoid this set the dumpable flag again which makes procfs + * update the /proc/$clone_child/ inodes owner to 65534. + * + * Using this we will be able write to /proc/$clone_child/uid_map + * as uid 65534 and map the uid 65534 to 0 inside the user + * namespace. + */ + ret = prctl(PR_SET_DUMPABLE, SUID_DUMP_USER); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "error prctl: %d (%m)\n", ret); + goto out; + } + + /* sync with parent */ + efd = eventfd(0, EFD_CLOEXEC); + if (efd < 0) { + ret = -errno; + fprintf(stderr, "error eventfd: %d (%m)\n", ret); + goto out; + } + + pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWUSER, NULL); + if (pid < 0) { + ret = -errno; + fprintf(stderr, "error clone: %d (%m)\n", ret); + + /* Unprivileged can't create user namespace ? */ + if (ret == -EPERM) { + printf("-- CLONE_NEWUSER TEST Failed for uid: %u\n" + "-- Make sure that your kernel do not allow CLONE_NEWUSER for unprivileged users\n", + uid); + test_status = CHECK_SKIP; + } + + goto out; + } + + if (pid == 0) { + struct conn *conn_src; + eventfd_t event_status = 0; + + setbuf(stdout, NULL); + ret = prctl(PR_SET_PDEATHSIG, SIGKILL); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "error prctl: %d (%m)\n", ret); + _exit(CHECK_ERR); + } + + ret = eventfd_read(efd, &event_status); + if (ret < 0 || event_status != 1) + _exit(CHECK_ERR); + + /* ping connection from the new user namespace */ + conn_src = kdbus_hello(bus, 0, NULL, 0); + if (!conn_src) + _exit(CHECK_ERR); + + add_match_empty(conn_src->fd); + ret = msg_send(conn_src, NULL, 0xabcd1234, + 0, 0, 0, conn->id); + if (ret < 0) + _exit(CHECK_ERR); + + close(conn_src->fd); + free(conn_src); + + _exit(CHECK_OK); + } + + ret = userns_map_uid_gid(pid, "0 65534 1", "0 65534 1"); + if (ret < 0) { + /* send error to child */ + eventfd_write(efd, 2); + fprintf(stderr, "error mapping uid/gid in new user namespace\n"); + goto out; + } + + ret = eventfd_write(efd, 1); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "error eventfd_write: %d (%m)\n", ret); + goto out; + } + + ret = waitpid(pid, &status, 0); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "error waitpid: %d (%m)\n", ret); + goto out; + } + + if (WIFEXITED(status)) + test_status = WEXITSTATUS(status); + +out: + if (efd != -1) + close(efd); + + return test_status; +} + +static int kdbus_clone_userns_test(const char *bus, struct conn *conn) +{ + int ret; + pid_t pid; + int status; + int test_status = CHECK_ERR; + + setbuf(stdout, NULL); + printf("STARTING TEST 'chat' in a new user namespace........\n"); + if (geteuid() > 0) { + fprintf(stderr, "geteuid() != 0, %s() needs root\n", + __func__); + test_status = CHECK_SKIP; + goto out; + } + + pid = fork(); + if (pid < 0) { + ret = -errno; + fprintf(stderr, "error fork(): %d (%m)\n", ret); + goto out; + } + + if (pid == 0) { + ret = prctl(PR_SET_PDEATHSIG, SIGKILL); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "error prctl: %d (%m)\n", ret); + _exit(CHECK_ERR); + } + + ret = __kdbus_clone_userns_test(bus, conn); + _exit(ret); + } + + /* Receive in the original (root privileged) user namespace */ + ret = conn_recv(conn); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "error recv: %d (%m)\n", ret); + goto out; + } + + ret = waitpid(pid, &status, 0); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "error waitpid: %d (%m)\n", ret); + goto out; + } + + if (WIFEXITED(status)) + test_status = WEXITSTATUS(status); + +out: + printf("RUNNING TEST 'chat' in a new user namespace........ "); + switch (test_status) { + case CHECK_OK: + printf("OK"); + break; + case CHECK_SKIP: + printf("SKIPPED"); + break; + case CHECK_ERR: + default: + printf("ERROR"); + break; + } + + printf("\n"); + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct { + struct kdbus_cmd_make head; + + /* bloom size item */ + struct { + uint64_t size; + uint64_t type; + struct kdbus_bloom_parameter bloom; + } bs; + + /* name item */ + uint64_t n_size; + uint64_t n_type; + char name[64]; + } bus_make; + int fdc, ret; + char *bus; + struct conn *conn_a; + + printf("-- opening /dev/" KBUILD_MODNAME "/control\n"); + fdc = open("/dev/" KBUILD_MODNAME "/control", O_RDWR|O_CLOEXEC); + if (fdc < 0) { + fprintf(stderr, "--- error %d (%m)\n", fdc); + return EXIT_FAILURE; + } + + memset(&bus_make, 0, sizeof(bus_make)); + bus_make.bs.size = sizeof(bus_make.bs); + bus_make.bs.type = KDBUS_ITEM_BLOOM_PARAMETER; + bus_make.bs.bloom.size = 64; + bus_make.bs.bloom.n_hash = 1; + + snprintf(bus_make.name, sizeof(bus_make.name), "%u-testbus", getuid()); + bus_make.n_type = KDBUS_ITEM_MAKE_NAME; + bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1; + + /* A world readable bus to test user namespace metadata... */ + bus_make.head.flags = KDBUS_MAKE_ACCESS_WORLD; + bus_make.head.size = sizeof(struct kdbus_cmd_make) + + sizeof(bus_make.bs) + + bus_make.n_size; + + printf("-- creating bus '%s'\n", bus_make.name); + ret = ioctl(fdc, KDBUS_CMD_BUS_MAKE, &bus_make); + if (ret) { + fprintf(stderr, "--- error %d (%m)\n", ret); + return EXIT_FAILURE; + } + + if (asprintf(&bus, "/dev/" KBUILD_MODNAME "/%s/bus", bus_make.name) < 0) + return EXIT_FAILURE; + + conn_a = kdbus_hello(bus, 0, NULL, 0); + if (!conn_a) + return EXIT_FAILURE; + + add_match_empty(conn_a->fd); + + kdbus_clone_userns_test(bus, conn_a); + + printf("-- closing bus connections\n"); + close(conn_a->fd); + free(conn_a); + + printf("-- closing bus master\n"); + close(fdc); + free(bus); + + return EXIT_SUCCESS; +}