nuttx-apps/testing/ostest/multiuser.c
Abhishek Mishra 917e2e860b testing/ostest: create temp passwd file for multiuser test
Install a minimal passwd file at CONFIG_LIBC_PASSWD_FILEPATH before
getpwnam() checks so sim:ostest does not require a pre-built /etc/passwd.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-07-31 01:14:30 +08:00

1199 lines
27 KiB
C

/****************************************************************************
* 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 <nuttx/config.h>
#include <stdarg.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#if !defined(CONFIG_DISABLE_MQUEUE)
# include <mqueue.h>
#endif
#if defined(CONFIG_FS_NAMED_SEMAPHORES)
# include <semaphore.h>
#endif
#if defined(CONFIG_FS_SHMFS)
# include <sys/mman.h>
#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;
}