/**************************************************************************** * apps/testing/ostest/multiuser.c * * SPDX-License-Identifier: Apache-2.0 * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #if !defined(CONFIG_DISABLE_MQUEUE) # include #endif #if defined(CONFIG_FS_NAMED_SEMAPHORES) # include #endif #if defined(CONFIG_FS_SHMFS) # include #endif #include "ostest.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define MU_UID1 1000 #define MU_GID1 1000 #define MU_UID2 2000 #define MU_GID2 2000 #define MU_GID3 3000 #define MU_PSEUDO_PERM "/ostest_mu_perm" #define MU_PSEUDO_SECRET "/ostest_mu_secret" #define MU_PSEUDO_USER "/ostest_mu_user" #ifndef CONFIG_LIBC_TMPDIR # define CONFIG_LIBC_TMPDIR "/tmp" #endif #define MU_TMPFS_SECRET CONFIG_LIBC_TMPDIR "/ostest_mu_secret" #define MU_CHILD_PRIORITY 100 /**************************************************************************** * Private Types ****************************************************************************/ struct mu_ctx_s { int failures; }; /**************************************************************************** * Private Functions ****************************************************************************/ static void mu_fail(FAR struct mu_ctx_s *ctx, FAR const char *fmt, ...) { va_list ap; ctx->failures++; printf(" FAIL: "); va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); printf("\n"); } static void mu_pass(FAR const char *fmt, ...) { va_list ap; printf(" PASS: "); va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); printf("\n"); } static void mu_check_eq(FAR struct mu_ctx_s *ctx, FAR const char *label, long got, long want) { if (got == want) { mu_pass("%s == %ld", label, want); } else { mu_fail(ctx, "%s: expected %ld, got %ld", label, want, got); } } static int mu_restore_root(FAR struct mu_ctx_s *ctx) { if (seteuid(0) != 0) { mu_fail(ctx, "seteuid(0) restore errno=%d", errno); return -1; } if (setegid(0) != 0) { mu_fail(ctx, "setegid(0) restore errno=%d", errno); return -1; } if (geteuid() != 0 || getegid() != 0) { mu_fail(ctx, "restore root effective credentials failed " "(euid=%d egid=%d)", geteuid(), getegid()); return -1; } return 0; } static int mu_set_effective(FAR struct mu_ctx_s *ctx, uid_t uid, gid_t gid) { /* NuttX grants arbitrary seteuid/setegid only while the effective ID * is 0. With real UID 0 (flat NSH), restore effective root before * switching user, matching nsh_switch_credentials(). */ if (getuid() == 0 && (geteuid() != 0 || getegid() != 0)) { if (seteuid(0) != 0) { mu_fail(ctx, "seteuid(0) before switch errno=%d", errno); return -1; } if (setegid(0) != 0) { mu_fail(ctx, "setegid(0) before switch errno=%d", errno); return -1; } } if (seteuid(uid) != 0) { mu_fail(ctx, "seteuid(%u) errno=%d", uid, errno); return -1; } if (setegid(gid) != 0) { mu_fail(ctx, "setegid(%u) errno=%d", gid, errno); return -1; } if (geteuid() != uid || getegid() != gid) { mu_fail(ctx, "effective credential switch failed " "(have euid=%d egid=%d)", geteuid(), getegid()); return -1; } return 0; } static int mu_expect_ok(FAR struct mu_ctx_s *ctx, FAR const char *op, int ret) { if (ret == 0) { mu_pass("%s", op); return 0; } mu_fail(ctx, "%s (ret=%d errno=%d)", op, ret, errno); return -1; } static int mu_expect_denied(FAR struct mu_ctx_s *ctx, FAR const char *op, int ret) { int err = errno; if (ret != 0 && (err == EPERM || err == EACCES)) { mu_pass("%s denied errno=%d", op, err); return 0; } if (ret == 0) { mu_fail(ctx, "%s succeeded (expected EPERM/EACCES, euid=%d)", op, geteuid()); } else { mu_fail(ctx, "%s (ret=%d errno=%d, expected EPERM/EACCES)", op, ret, err); } return -1; } static int mu_verify_owner(FAR struct mu_ctx_s *ctx, FAR const char *path, uid_t uid, gid_t gid) { struct stat st; if (stat(path, &st) != 0) { mu_fail(ctx, "stat(%s) errno=%d", path, errno); return -1; } if (st.st_uid != uid || st.st_gid != gid) { mu_fail(ctx, "%s owner expected %u:%u got %u:%u", path, uid, gid, st.st_uid, st.st_gid); return -1; } mu_pass("%s owner %u:%u", path, uid, gid); return 0; } static void mu_verify_mode(FAR struct mu_ctx_s *ctx, FAR const char *path, mode_t mode) { struct stat st; if (stat(path, &st) != 0) { mu_fail(ctx, "stat(%s) after create errno=%d", path, errno); } else if ((st.st_mode & 0777) != mode) { mu_fail(ctx, "%s mode expected %04o got %04o", path, mode, st.st_mode & 0777); } else { mu_pass("%s mode %04o", path, mode); } } #if defined(CONFIG_SCHED_USER_IDENTITY) static int multiuser_effective_test(FAR struct mu_ctx_s *ctx) { int ret; printf("multiuser: effective UID/GID switching\n"); mu_check_eq(ctx, "initial uid", getuid(), 0); mu_check_eq(ctx, "initial euid", geteuid(), 0); mu_check_eq(ctx, "initial gid", getgid(), 0); mu_check_eq(ctx, "initial egid", getegid(), 0); ret = seteuid(MU_UID1); if (mu_expect_ok(ctx, "root seteuid(1000)", ret) != 0) { mu_restore_root(ctx); return ctx->failures; } mu_check_eq(ctx, "euid after seteuid(1000)", geteuid(), MU_UID1); ret = seteuid(0); if (mu_expect_ok(ctx, "root seteuid(0) restore", ret) != 0) { mu_restore_root(ctx); return ctx->failures; } mu_check_eq(ctx, "euid restored to 0", geteuid(), 0); ret = setegid(MU_GID2); if (mu_expect_ok(ctx, "root setegid(2000)", ret) != 0) { mu_restore_root(ctx); return ctx->failures; } mu_check_eq(ctx, "egid after setegid(2000)", getegid(), MU_GID2); ret = setegid(0); if (mu_expect_ok(ctx, "root setegid(0) restore", ret) != 0) { mu_restore_root(ctx); return ctx->failures; } mu_check_eq(ctx, "egid restored to 0", getegid(), 0); return ctx->failures; } static int multiuser_resuid_test(FAR struct mu_ctx_s *ctx) { uid_t ruid; uid_t euid; uid_t suid; gid_t rgid; gid_t egid; gid_t sgid; int ret; printf("multiuser: getresuid/getresgid and setreuid/setregid\n"); ret = getresuid(&ruid, &euid, &suid); if (mu_expect_ok(ctx, "getresuid(initial)", ret) != 0) { return ctx->failures; } mu_check_eq(ctx, "initial real uid", ruid, 0); mu_check_eq(ctx, "initial eff uid", euid, 0); mu_check_eq(ctx, "initial saved uid", suid, 0); ret = getresgid(&rgid, &egid, &sgid); if (mu_expect_ok(ctx, "getresgid(initial)", ret) != 0) { return ctx->failures; } mu_check_eq(ctx, "initial real gid", rgid, 0); mu_check_eq(ctx, "initial eff gid", egid, 0); mu_check_eq(ctx, "initial saved gid", sgid, 0); ret = setreuid((uid_t)-1, MU_UID1); if (mu_expect_ok(ctx, "root setreuid(-1,1000)", ret) != 0) { mu_restore_root(ctx); return ctx->failures; } ret = getresuid(&ruid, &euid, &suid); mu_check_eq(ctx, "real uid after setreuid(-1,1000)", ruid, 0); mu_check_eq(ctx, "eff uid after setreuid(-1,1000)", euid, MU_UID1); mu_check_eq(ctx, "saved uid after setreuid(-1,1000)", suid, MU_UID1); ret = setreuid(MU_UID2, (uid_t)-1); mu_expect_denied(ctx, "non-root setreuid(2000,-1)", ret); ret = setreuid((uid_t)-1, 0); if (mu_expect_ok(ctx, "setreuid(-1,0) drop effective", ret) != 0) { mu_restore_root(ctx); return ctx->failures; } ret = setreuid(0, (uid_t)-1); if (mu_expect_ok(ctx, "root setreuid(0,-1) restore", ret) != 0) { mu_restore_root(ctx); return ctx->failures; } ret = getresuid(&ruid, &euid, &suid); mu_check_eq(ctx, "real uid restored", ruid, 0); mu_check_eq(ctx, "eff uid restored", euid, 0); mu_check_eq(ctx, "saved uid restored", suid, 0); ret = setregid((gid_t)-1, MU_GID1); if (mu_expect_ok(ctx, "root setregid(-1,1000)", ret) != 0) { mu_restore_root(ctx); return ctx->failures; } ret = getresgid(&rgid, &egid, &sgid); mu_check_eq(ctx, "real gid after setregid(-1,1000)", rgid, 0); mu_check_eq(ctx, "eff gid after setregid(-1,1000)", egid, MU_GID1); mu_check_eq(ctx, "saved gid after setregid(-1,1000)", sgid, MU_GID1); ret = setregid(MU_GID2, (gid_t)-1); mu_expect_denied(ctx, "non-root setregid(2000,-1)", ret); ret = setregid((gid_t)-1, 0); if (mu_expect_ok(ctx, "setregid(-1,0) drop effective", ret) != 0) { mu_restore_root(ctx); return ctx->failures; } ret = setregid(0, (gid_t)-1); mu_expect_ok(ctx, "root setregid(0,-1) restore", ret); mu_restore_root(ctx); return ctx->failures; } #if defined(CONFIG_SCHED_WAITPID) && !defined(CONFIG_BUILD_KERNEL) static int mu_run_child(FAR struct mu_ctx_s *ctx, FAR const char *name, main_t entry) { pid_t pid; int status; pid = task_create(name, MU_CHILD_PRIORITY, STACKSIZE, entry, NULL); if (pid < 0) { mu_fail(ctx, "task_create(%s) failed", name); return ctx->failures; } if (waitpid(pid, &status, 0) != pid) { mu_fail(ctx, "waitpid(%s) failed errno=%d", name, errno); return ctx->failures; } if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) { mu_fail(ctx, "%s child exited with status=%d", name, status); } else { mu_pass("%s child completed successfully", name); } return ctx->failures; } static int multiuser_resuid_child(int argc, FAR char *argv[]) { struct mu_ctx_s ctx = { 0 }; uid_t ruid; uid_t euid; uid_t suid; int ret; (void)argc; (void)argv; ret = setreuid(MU_UID1, MU_UID1); if (mu_expect_ok(&ctx, "root setreuid(1000,1000)", ret) != 0) { return EXIT_FAILURE; } ret = getresuid(&ruid, &euid, &suid); if (mu_expect_ok(&ctx, "getresuid(after setreuid)", ret) != 0) { return EXIT_FAILURE; } mu_check_eq(&ctx, "real uid after setreuid", ruid, MU_UID1); mu_check_eq(&ctx, "eff uid after setreuid", euid, MU_UID1); mu_check_eq(&ctx, "saved uid after setreuid", suid, MU_UID1); printf("multiuser_resuid_child: %d failure(s)\n", ctx.failures); return ctx.failures == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } static int multiuser_resuid_child_test(FAR struct mu_ctx_s *ctx) { printf("multiuser: setreuid(1000,1000) full assignment (child task)\n"); mu_run_child(ctx, "mu_resuid", multiuser_resuid_child); mu_check_eq(ctx, "parent euid after resuid child", geteuid(), 0); mu_check_eq(ctx, "parent egid after resuid child", getegid(), 0); return ctx->failures; } static int multiuser_suid_child(int argc, FAR char *argv[]) { struct mu_ctx_s ctx = { 0 }; int ret; (void)argc; (void)argv; printf("multiuser_suid_child: saved set-UID/GID semantics\n"); ret = setuid(MU_UID1); if (mu_expect_ok(&ctx, "setuid(1000) as root", ret) != 0) { return EXIT_FAILURE; } mu_check_eq(&ctx, "uid after setuid(1000)", getuid(), MU_UID1); mu_check_eq(&ctx, "euid after setuid(1000)", geteuid(), MU_UID1); ret = seteuid(0); mu_expect_denied(&ctx, "non-root seteuid(0)", ret); mu_check_eq(&ctx, "euid unchanged after denied seteuid(0)", geteuid(), MU_UID1); ret = seteuid(MU_UID1); mu_expect_ok(&ctx, "non-root seteuid(1000)", ret); ret = setegid(MU_GID2); mu_expect_ok(&ctx, "root-group setegid(2000)", ret); mu_check_eq(&ctx, "egid after setegid(2000)", getegid(), MU_GID2); ret = setegid(0); mu_expect_ok(&ctx, "root-group setegid(0) restore", ret); ret = setgid(MU_GID3); mu_expect_ok(&ctx, "setgid(3000)", ret); mu_check_eq(&ctx, "gid after setgid(3000)", getgid(), MU_GID3); mu_check_eq(&ctx, "egid after setgid(3000)", getegid(), MU_GID3); ret = setegid(0); mu_expect_denied(&ctx, "non-root setegid(0)", ret); mu_check_eq(&ctx, "egid unchanged after denied setegid(0)", getegid(), MU_GID3); printf("multiuser_suid_child: %d failure(s)\n", ctx.failures); return ctx.failures == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } static int multiuser_suid_test(FAR struct mu_ctx_s *ctx) { printf("multiuser: saved set-UID/GID semantics (child task)\n"); mu_run_child(ctx, "mu_suid", multiuser_suid_child); mu_check_eq(ctx, "parent euid after child", geteuid(), 0); mu_check_eq(ctx, "parent egid after child", getegid(), 0); return ctx->failures; } #endif /* CONFIG_SCHED_WAITPID && !CONFIG_BUILD_KERNEL */ #if defined(CONFIG_FS_PERMISSION) static int multiuser_open_secret(FAR struct mu_ctx_s *ctx, FAR const char *path, int expect_ok) { int fd; fd = open(path, O_RDONLY); if (expect_ok) { if (fd < 0) { mu_fail(ctx, "open(%s) errno=%d (expected success)", path, errno); return -1; } close(fd); mu_pass("open(%s) allowed", path); return 0; } if (fd >= 0) { close(fd); mu_fail(ctx, "open(%s) succeeded (expected EACCES, euid=%d)", path, geteuid()); return -1; } if (errno != EACCES) { mu_fail(ctx, "open(%s) errno=%d (expected EACCES)", path, errno); return -1; } mu_pass("open(%s) denied with EACCES", path); return 0; } #endif /* CONFIG_FS_PERMISSION */ #if defined(CONFIG_FS_PERMISSION) && defined(CONFIG_PSEUDOFS_ATTRIBUTES) && \ defined(CONFIG_PSEUDOFS_FILE) static int multiuser_pseudofs_test(FAR struct mu_ctx_s *ctx) { int fd; int ret; printf("multiuser: pseudoFS chmod/chown/open permissions\n"); unlink(MU_PSEUDO_PERM); unlink(MU_PSEUDO_SECRET); unlink(MU_PSEUDO_USER); fd = open(MU_PSEUDO_PERM, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { mu_fail(ctx, "create %s errno=%d", MU_PSEUDO_PERM, errno); goto out; } close(fd); mu_verify_owner(ctx, MU_PSEUDO_PERM, 0, 0); ret = chmod(MU_PSEUDO_PERM, 0600); mu_expect_ok(ctx, "root chmod(0600)", ret); if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } ret = chmod(MU_PSEUDO_PERM, 0777); mu_expect_denied(ctx, "non-owner chmod(0777)", ret); ret = chown(MU_PSEUDO_PERM, 0, 0); mu_expect_denied(ctx, "non-root chown(0,0)", ret); if (mu_restore_root(ctx) != 0) { goto out; } ret = chown(MU_PSEUDO_PERM, MU_UID1, MU_GID1); mu_expect_ok(ctx, "root chown to 1000:1000", ret); mu_verify_owner(ctx, MU_PSEUDO_PERM, MU_UID1, MU_GID1); if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } ret = chmod(MU_PSEUDO_PERM, 0777); mu_expect_ok(ctx, "owner chmod(0777)", ret); if (mu_restore_root(ctx) != 0) { goto out; } fd = open(MU_PSEUDO_SECRET, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) { mu_fail(ctx, "create %s errno=%d", MU_PSEUDO_SECRET, errno); goto out; } close(fd); mu_verify_owner(ctx, MU_PSEUDO_SECRET, 0, 0); if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } multiuser_open_secret(ctx, MU_PSEUDO_SECRET, 0); if (mu_restore_root(ctx) != 0) { goto out; } multiuser_open_secret(ctx, MU_PSEUDO_SECRET, 1); if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } fd = open(MU_PSEUDO_USER, O_CREAT | O_WRONLY, 0644); if (fd < 0) { mu_fail(ctx, "create %s as euid=%d errno=%d", MU_PSEUDO_USER, geteuid(), errno); goto out; } close(fd); mu_verify_owner(ctx, MU_PSEUDO_USER, MU_UID1, MU_GID1); if (mu_set_effective(ctx, MU_UID2, MU_GID2) != 0) { goto out; } multiuser_open_secret(ctx, MU_PSEUDO_USER, 1); out: mu_restore_root(ctx); unlink(MU_PSEUDO_PERM); unlink(MU_PSEUDO_SECRET); unlink(MU_PSEUDO_USER); return ctx->failures; } #endif /* FS_PERMISSION && PSEUDOFS_ATTRIBUTES && PSEUDOFS_FILE */ #if defined(CONFIG_FS_PERMISSION) && defined(CONFIG_FS_TMPFS) static int multiuser_tmpfs_test(FAR struct mu_ctx_s *ctx) { int fd; printf("multiuser: tmpfs open permission enforcement\n"); unlink(MU_TMPFS_SECRET); fd = open(MU_TMPFS_SECRET, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) { mu_fail(ctx, "create %s errno=%d", MU_TMPFS_SECRET, errno); goto out; } close(fd); mu_verify_owner(ctx, MU_TMPFS_SECRET, 0, 0); if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } multiuser_open_secret(ctx, MU_TMPFS_SECRET, 0); if (mu_restore_root(ctx) != 0) { goto out; } multiuser_open_secret(ctx, MU_TMPFS_SECRET, 1); out: mu_restore_root(ctx); unlink(MU_TMPFS_SECRET); return ctx->failures; } #endif /* CONFIG_FS_PERMISSION && CONFIG_FS_TMPFS */ #if defined(CONFIG_FS_PERMISSION) && defined(CONFIG_PSEUDOFS_ATTRIBUTES) #if !defined(CONFIG_DISABLE_MQUEUE) # define MU_MQ_NAME "ostest_mu_mq" # define MU_MQ_PATH CONFIG_FS_MQUEUE_VFS_PATH "/" MU_MQ_NAME #endif #if defined(CONFIG_FS_NAMED_SEMAPHORES) # define MU_SEM_NAME "ostest_mu_sem" # define MU_SEM_PATH CONFIG_FS_NAMED_SEMAPHORES_VFS_PATH "/" MU_SEM_NAME #endif #if defined(CONFIG_FS_SHMFS) # define MU_SHM_NAME "ostest_mu_shm" # define MU_SHM_PATH CONFIG_FS_SHMFS_VFS_PATH "/" MU_SHM_NAME #endif #if defined(CONFIG_PIPES) # define MU_FIFO_PATH "/ostest_mu_fifo" #endif #if !defined(CONFIG_DISABLE_MQUEUE) static int multiuser_mqueue_test(FAR struct mu_ctx_s *ctx) { mqd_t mq; struct mq_attr attr; printf("multiuser: message queue ownership and permissions\n"); mu_restore_root(ctx); mq_unlink(MU_MQ_NAME); memset(&attr, 0, sizeof(attr)); attr.mq_maxmsg = 4; attr.mq_msgsize = 64; if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } mq = mq_open(MU_MQ_NAME, O_CREAT | O_RDWR | O_EXCL, 0600, &attr); if (mq == (mqd_t)-1) { mu_fail(ctx, "mq_open(create) errno=%d", errno); goto out; } mq_close(mq); mu_verify_owner(ctx, MU_MQ_PATH, MU_UID1, MU_GID1); mu_verify_mode(ctx, MU_MQ_PATH, 0600); if (mu_set_effective(ctx, MU_UID2, MU_GID2) != 0) { goto out; } mq = mq_open(MU_MQ_NAME, O_RDWR); mu_expect_denied(ctx, "mq_open(other user)", mq == (mqd_t)-1 ? -1 : 0); if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } mq = mq_open(MU_MQ_NAME, O_RDWR); if (mq == (mqd_t)-1) { mu_fail(ctx, "mq_open(owner) errno=%d", errno); } else { mu_pass("mq_open(owner)"); mq_close(mq); } out: mu_restore_root(ctx); mq_unlink(MU_MQ_NAME); return ctx->failures; } #endif /* !CONFIG_DISABLE_MQUEUE */ #if defined(CONFIG_FS_NAMED_SEMAPHORES) static int multiuser_sem_test(FAR struct mu_ctx_s *ctx) { sem_t *sem; printf("multiuser: named semaphore ownership and permissions\n"); mu_restore_root(ctx); sem_unlink(MU_SEM_NAME); if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } sem = sem_open(MU_SEM_NAME, O_CREAT | O_EXCL, 0600, 1); if (sem == SEM_FAILED) { mu_fail(ctx, "sem_open(create) errno=%d", errno); goto out; } sem_close(sem); mu_verify_owner(ctx, MU_SEM_PATH, MU_UID1, MU_GID1); if (mu_set_effective(ctx, MU_UID2, MU_GID2) != 0) { goto out; } sem = sem_open(MU_SEM_NAME, 0); mu_expect_denied(ctx, "sem_open(other user)", sem == SEM_FAILED ? -1 : 0); if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } sem = sem_open(MU_SEM_NAME, 0); if (sem == SEM_FAILED) { mu_fail(ctx, "sem_open(owner) errno=%d", errno); } else { mu_pass("sem_open(owner)"); sem_close(sem); } out: mu_restore_root(ctx); sem_unlink(MU_SEM_NAME); return ctx->failures; } #endif /* CONFIG_FS_NAMED_SEMAPHORES */ #if defined(CONFIG_FS_SHMFS) static int multiuser_shm_test(FAR struct mu_ctx_s *ctx) { int fd; printf("multiuser: shared memory ownership and permissions\n"); mu_restore_root(ctx); shm_unlink(MU_SHM_NAME); if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } fd = shm_open(MU_SHM_NAME, O_CREAT | O_EXCL | O_RDWR, 0600); if (fd < 0) { mu_fail(ctx, "shm_open(create) errno=%d", errno); goto out; } close(fd); mu_verify_owner(ctx, MU_SHM_PATH, MU_UID1, MU_GID1); if (mu_set_effective(ctx, MU_UID2, MU_GID2) != 0) { goto out; } fd = shm_open(MU_SHM_NAME, O_RDWR, 0); mu_expect_denied(ctx, "shm_open(other user)", fd < 0 ? -1 : 0); if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } fd = shm_open(MU_SHM_NAME, O_RDWR, 0); if (fd < 0) { mu_fail(ctx, "shm_open(owner) errno=%d", errno); } else { mu_pass("shm_open(owner)"); close(fd); } out: mu_restore_root(ctx); shm_unlink(MU_SHM_NAME); return ctx->failures; } #endif /* CONFIG_FS_SHMFS */ #if defined(CONFIG_PIPES) static int multiuser_fifo_test(FAR struct mu_ctx_s *ctx) { int fd; printf("multiuser: FIFO ownership and permissions\n"); mu_restore_root(ctx); unlink(MU_FIFO_PATH); if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } if (mkfifo(MU_FIFO_PATH, 0600) != 0) { mu_fail(ctx, "mkfifo errno=%d", errno); goto out; } mu_verify_owner(ctx, MU_FIFO_PATH, MU_UID1, MU_GID1); if (mu_set_effective(ctx, MU_UID2, MU_GID2) != 0) { goto out; } fd = open(MU_FIFO_PATH, O_RDONLY | O_NONBLOCK); mu_expect_denied(ctx, "open fifo (other user)", fd < 0 ? -1 : 0); if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { goto out; } fd = open(MU_FIFO_PATH, O_RDONLY | O_NONBLOCK); if (fd < 0) { mu_fail(ctx, "open fifo (owner) errno=%d", errno); } else { mu_pass("open fifo (owner)"); close(fd); } out: mu_restore_root(ctx); unlink(MU_FIFO_PATH); return ctx->failures; } #endif /* CONFIG_PIPES */ static int multiuser_ipc_test(FAR struct mu_ctx_s *ctx) { #if !defined(CONFIG_DISABLE_MQUEUE) multiuser_mqueue_test(ctx); #endif #if defined(CONFIG_FS_NAMED_SEMAPHORES) multiuser_sem_test(ctx); #endif #if defined(CONFIG_FS_SHMFS) multiuser_shm_test(ctx); #endif #if defined(CONFIG_PIPES) multiuser_fifo_test(ctx); #endif return ctx->failures; } #endif /* CONFIG_FS_PERMISSION && CONFIG_PSEUDOFS_ATTRIBUTES */ #if defined(CONFIG_LIBC_PASSWD_FILE) && defined(CONFIG_TESTING_OSTEST_MULTIUSER) static int multiuser_passwd_install(FAR struct mu_ctx_s *ctx) { int fd; ssize_t nwritten; static const char content[] = "root:x:0:0:/\n" "testuser:x:1000:1000:/home/testuser\n"; fd = open(CONFIG_LIBC_PASSWD_FILEPATH, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { mu_fail(ctx, "create %s errno=%d", CONFIG_LIBC_PASSWD_FILEPATH, errno); return -1; } nwritten = write(fd, content, sizeof(content) - 1); close(fd); if (nwritten != (ssize_t)(sizeof(content) - 1)) { mu_fail(ctx, "write %s errno=%d", CONFIG_LIBC_PASSWD_FILEPATH, errno); unlink(CONFIG_LIBC_PASSWD_FILEPATH); return -1; } mu_pass("installed %s", CONFIG_LIBC_PASSWD_FILEPATH); return 0; } static void multiuser_passwd_remove(void) { unlink(CONFIG_LIBC_PASSWD_FILEPATH); } static int multiuser_passwd_test(FAR struct mu_ctx_s *ctx) { FAR struct passwd *pwd; printf("multiuser: passwd lookup after credential drop\n"); if (multiuser_passwd_install(ctx) != 0) { return ctx->failures; } if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0) { multiuser_passwd_remove(); return ctx->failures; } pwd = getpwnam("root"); if (pwd == NULL) { mu_fail(ctx, "getpwnam(root) failed errno=%d after seteuid(%d)", errno, MU_UID1); } else { mu_pass("getpwnam(root) uid=%u", pwd->pw_uid); } pwd = getpwnam("testuser"); if (pwd == NULL) { mu_fail(ctx, "getpwnam(testuser) failed errno=%d", errno); } else if (pwd->pw_uid != MU_UID1) { mu_fail(ctx, "getpwnam(testuser) uid=%u (expected %u)", pwd->pw_uid, MU_UID1); } else { mu_pass("getpwnam(testuser) uid=%u", pwd->pw_uid); } mu_restore_root(ctx); multiuser_passwd_remove(); return ctx->failures; } #endif /* CONFIG_LIBC_PASSWD_FILE && CONFIG_TESTING_OSTEST_MULTIUSER */ #endif /* CONFIG_SCHED_USER_IDENTITY */ /**************************************************************************** * Public Functions ****************************************************************************/ int multiuser_test(void) { struct mu_ctx_s ctx = { 0 }; #if defined(CONFIG_SCHED_USER_IDENTITY) printf("multiuser_test: start\n"); multiuser_effective_test(&ctx); multiuser_resuid_test(&ctx); #if defined(CONFIG_SCHED_WAITPID) && !defined(CONFIG_BUILD_KERNEL) multiuser_resuid_child_test(&ctx); multiuser_suid_test(&ctx); #else printf("multiuser: skipping saved set-UID/GID child test " "(need CONFIG_SCHED_WAITPID)\n"); #endif #if defined(CONFIG_FS_PERMISSION) && defined(CONFIG_PSEUDOFS_ATTRIBUTES) && \ defined(CONFIG_PSEUDOFS_FILE) multiuser_pseudofs_test(&ctx); #else printf("multiuser: skipping pseudoFS permission tests " "(need FS_PERMISSION, PSEUDOFS_ATTRIBUTES, PSEUDOFS_FILE)\n"); #endif #if defined(CONFIG_FS_PERMISSION) && defined(CONFIG_FS_TMPFS) multiuser_tmpfs_test(&ctx); #else printf("multiuser: skipping tmpfs permission tests " "(need FS_PERMISSION and FS_TMPFS)\n"); #endif #if defined(CONFIG_FS_PERMISSION) && defined(CONFIG_PSEUDOFS_ATTRIBUTES) multiuser_ipc_test(&ctx); #else printf("multiuser: skipping IPC/FIFO permission tests " "(need FS_PERMISSION and PSEUDOFS_ATTRIBUTES)\n"); #endif #if defined(CONFIG_LIBC_PASSWD_FILE) && defined(CONFIG_TESTING_OSTEST_MULTIUSER) multiuser_passwd_test(&ctx); #else printf("multiuser: skipping passwd lookup test " "(need LIBC_PASSWD_FILE and TESTING_OSTEST_MULTIUSER)\n"); #endif mu_restore_root(&ctx); printf("multiuser_test: %d failure(s)\n", ctx.failures); #else printf("multiuser_test: CONFIG_SCHED_USER_IDENTITY disabled\n"); #endif return ctx.failures; }