From 7358692c598bc99d5b38da84d2060fe006ea07d1 Mon Sep 17 00:00:00 2001 From: Abhishek Mishra Date: Fri, 14 Aug 2026 17:21:23 +0000 Subject: [PATCH] system/sudo: add setuid privilege helper Install a UNIX-style setuid-root sudo app: the kernel raises euid on exec, userspace verifies the caller with passwd_verify(), then setresuid/setgroups and execvp() run the command. Ostest covers setuid exec after a hard credential drop. Signed-off-by: Abhishek Mishra --- builtin/exec_builtin.c | 5 + examples/hello/CMakeLists.txt | 36 +++- examples/hello/Kconfig | 10 + examples/hello/Makefile | 6 + nshlib/nsh_fileapps.c | 8 + nshlib/nsh_identity.c | 35 +++- system/sudo/CMakeLists.txt | 41 +++++ system/sudo/Kconfig | 47 +++++ system/sudo/Make.defs | 25 +++ system/sudo/Makefile | 38 ++++ system/sudo/sudo_main.c | 336 ++++++++++++++++++++++++++++++++++ testing/ostest/multiuser.c | 75 ++++++++ 12 files changed, 643 insertions(+), 19 deletions(-) create mode 100644 system/sudo/CMakeLists.txt create mode 100644 system/sudo/Kconfig create mode 100644 system/sudo/Make.defs create mode 100644 system/sudo/Makefile create mode 100644 system/sudo/sudo_main.c diff --git a/builtin/exec_builtin.c b/builtin/exec_builtin.c index b87133243..f6edf1a5f 100644 --- a/builtin/exec_builtin.c +++ b/builtin/exec_builtin.c @@ -209,6 +209,11 @@ int exec_builtin(FAR const char *appname, FAR char * const *argv, /* Load and execute the application. */ ret = posix_spawn(&pid, builtin->name, &file_actions, &attr, argv, NULL); + if (ret == EACCES) + { + goto errout_with_actions; + } + if (ret != 0 && builtin->main != NULL) #endif { diff --git a/examples/hello/CMakeLists.txt b/examples/hello/CMakeLists.txt index 94fefd612..1fb3f2f4b 100644 --- a/examples/hello/CMakeLists.txt +++ b/examples/hello/CMakeLists.txt @@ -21,13 +21,31 @@ # ############################################################################## if(CONFIG_EXAMPLES_HELLO) - nuttx_add_application( - NAME - ${CONFIG_EXAMPLES_HELLO_PROGNAME} - SRCS - hello_main.c - STACKSIZE - ${CONFIG_EXAMPLES_HELLO_STACKSIZE} - PRIORITY - ${CONFIG_EXAMPLES_HELLO_PRIORITY}) + if(CONFIG_EXAMPLES_HELLO_RESTRICTED) + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_HELLO_PROGNAME} + SRCS + hello_main.c + STACKSIZE + ${CONFIG_EXAMPLES_HELLO_STACKSIZE} + PRIORITY + ${CONFIG_EXAMPLES_HELLO_PRIORITY} + UID + 0 + GID + 0 + MODE + 484) + else() + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_HELLO_PROGNAME} + SRCS + hello_main.c + STACKSIZE + ${CONFIG_EXAMPLES_HELLO_STACKSIZE} + PRIORITY + ${CONFIG_EXAMPLES_HELLO_PRIORITY}) + endif() endif() diff --git a/examples/hello/Kconfig b/examples/hello/Kconfig index a35bddc23..685b143ca 100644 --- a/examples/hello/Kconfig +++ b/examples/hello/Kconfig @@ -26,4 +26,14 @@ config EXAMPLES_HELLO_STACKSIZE int "Hello stack size" default DEFAULT_TASK_STACKSIZE +config EXAMPLES_HELLO_RESTRICTED + bool "Install hello as owner-execute only (0744)" + default n + depends on SCHED_USER_IDENTITY + ---help--- + Set the installed mode to ``-rwxr--r--`` owned by root so a + non-root user cannot exec ``/bin/hello``. Used with + ``CONFIG_SYSTEM_SUDO`` to show ``sudo /bin/hello`` restoring + access. + endif diff --git a/examples/hello/Makefile b/examples/hello/Makefile index e8bcc4985..8c1311094 100644 --- a/examples/hello/Makefile +++ b/examples/hello/Makefile @@ -29,6 +29,12 @@ PRIORITY = $(CONFIG_EXAMPLES_HELLO_PRIORITY) STACKSIZE = $(CONFIG_EXAMPLES_HELLO_STACKSIZE) MODULE = $(CONFIG_EXAMPLES_HELLO) +ifeq ($(CONFIG_EXAMPLES_HELLO_RESTRICTED),y) +UID = 0 +GID = 0 +MODE = 0744 +endif + # Hello, World! Example MAINSRC = hello_main.c diff --git a/nshlib/nsh_fileapps.c b/nshlib/nsh_fileapps.c index 66f41e36f..1d6cfe69d 100644 --- a/nshlib/nsh_fileapps.c +++ b/nshlib/nsh_fileapps.c @@ -253,6 +253,14 @@ int nsh_fileapp(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, */ ret = posix_spawnp(&pid, cmd, &file_actions, &attr, argv, environ); + if (ret == EACCES) + { + nsh_error(vtbl, "nsh: %s: Permission denied\n", cmd); + rc = 1; + ret = OK; + goto errout_with_actions; + } + if (ret == OK) { /* The application was successfully started with pre-emption disabled. diff --git a/nshlib/nsh_identity.c b/nshlib/nsh_identity.c index 5027975c5..98204d408 100644 --- a/nshlib/nsh_identity.c +++ b/nshlib/nsh_identity.c @@ -178,10 +178,14 @@ static bool nsh_verify_credentials(FAR const char *username, * Name: nsh_switch_credentials * * Description: - * Switch the session to the given UID/GID. NSH starts with real UID/GID - * zero; file permission checks use the effective identity. When the real - * UID is still zero, only the effective UID/GID are changed so that a - * later 'su' can regain root via seteuid(0) after password verification. + * Switch the session to the given UID/GID. + * + * When the real UID is still zero and the target is not root, set the + * real and effective IDs to the target and keep saved-root (suid/sgid + * 0). File DAC then uses the unprivileged effective ID, while setuid + * helpers such as sudo still see the real UID of the invoking user + * after S_ISUID raises the effective UID to 0. A later ``su root`` + * can restore root from the saved IDs after password verification. * ****************************************************************************/ @@ -189,15 +193,25 @@ static int nsh_switch_credentials(uid_t uid, gid_t gid) { if (getuid() == 0) { - if (geteuid() != 0 || getegid() != 0) + if (geteuid() != 0) { - if (seteuid(0) != 0 || setegid(0) != 0) + if (seteuid(0) != 0) { return -errno; } } - if (seteuid(uid) != 0 || setegid(gid) != 0) + if (uid == 0) + { + if (setresgid(0, 0, 0) != 0 || setresuid(0, 0, 0) != 0) + { + return -errno; + } + + return OK; + } + + if (setresgid(gid, gid, 0) != 0 || setresuid(uid, uid, 0) != 0) { return -errno; } @@ -205,7 +219,7 @@ static int nsh_switch_credentials(uid_t uid, gid_t gid) return OK; } - if (setuid(uid) != 0 || setgid(gid) != 0) + if (setresgid(gid, gid, gid) != 0 || setresuid(uid, uid, uid) != 0) { return -errno; } @@ -222,8 +236,9 @@ static int nsh_switch_credentials(uid_t uid, gid_t gid) * * Description: * Look up 'username' in the passwd database and set the calling task's - * session identity. When NSH still has real UID zero, only the effective - * UID/GID are updated so that 'su' can switch users later. + * session identity. When switching from real UID zero to a non-root + * user, real and effective IDs become that user and saved-root is kept + * so ``su root`` can restore privileges after authentication. * * Input Parameters: * username - Login name to assume diff --git a/system/sudo/CMakeLists.txt b/system/sudo/CMakeLists.txt new file mode 100644 index 000000000..0d11931dc --- /dev/null +++ b/system/sudo/CMakeLists.txt @@ -0,0 +1,41 @@ +# ############################################################################## +# apps/system/sudo/CMakeLists.txt +# +# 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. +# +# ############################################################################## + +if(CONFIG_SYSTEM_SUDO) + nuttx_add_application( + NAME + sudo + PRIORITY + 100 + STACKSIZE + ${CONFIG_DEFAULT_TASK_STACKSIZE} + MODULE + ${CONFIG_SYSTEM_SUDO} + UID + 0 + GID + 0 + MODE + 35309 + SRCS + sudo_main.c) +endif() diff --git a/system/sudo/Kconfig b/system/sudo/Kconfig new file mode 100644 index 000000000..a64cdea5f --- /dev/null +++ b/system/sudo/Kconfig @@ -0,0 +1,47 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config SYSTEM_SUDO + tristate "sudo (setuid privilege helper)" + default n + depends on SCHED_USER_IDENTITY + depends on LIBC_PASSWD_FILE + depends on FSUTILS_PASSWD + depends on LIBC_EXECFUNCS + ---help--- + Install a Linux-style setuid-root ``sudo`` program. The kernel + raises the effective UID to zero when the ELF is loaded (see + ``nx_uid`` / ``nx_mode`` in Application.mk). ``sudo`` verifies + the invoking user's password with ``passwd_verify()``, becomes + fully root via ``setresuid()`` / ``setresgid()``, then + ``execvp()``s the requested command. + + Requires ``CONFIG_LIBC_EXECFUNCS`` so NSH (or ``posix_spawn``) + loads the application ELF instead of calling its entry point + directly from the builtin registry. + + Non-root callers must appear in the sudoers file + (``SYSTEM_SUDO_SUDOERS_PATH``) or in + ``SYSTEM_SUDO_ALLOWED_USERS``. Real UID 0 is always allowed. + +if SYSTEM_SUDO + +config SYSTEM_SUDO_SUDOERS_PATH + string "Path to sudoers allowlist" + default "/etc/sudoers" + ---help--- + Simple allowlist, one username per line. Lines starting with + ``#`` are comments. A Linux-style first field is also accepted + (``user ALL=(ALL) ALL``). Generate this file in ROMFS with + ``CONFIG_BOARD_ETC_ROMFS_PASSWD_EXTRA_ENABLE``. + +config SYSTEM_SUDO_ALLOWED_USERS + string "Compile-time sudoers usernames" + default "" + ---help--- + Optional comma-separated extra allowlist compiled into sudo. + Use this when /etc/sudoers is not present. Example: ``user``. + +endif # SYSTEM_SUDO diff --git a/system/sudo/Make.defs b/system/sudo/Make.defs new file mode 100644 index 000000000..09a90f66f --- /dev/null +++ b/system/sudo/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/system/sudo/Make.defs +# +# 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. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_SUDO),) +CONFIGURED_APPS += $(APPDIR)/system/sudo +endif diff --git a/system/sudo/Makefile b/system/sudo/Makefile new file mode 100644 index 000000000..6e22818fe --- /dev/null +++ b/system/sudo/Makefile @@ -0,0 +1,38 @@ +############################################################################ +# apps/system/sudo/Makefile +# +# 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. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = sudo +PRIORITY = 100 +STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) +MODULE = $(CONFIG_SYSTEM_SUDO) + +MAINSRC = sudo_main.c + +# Set-user-ID root executable (Linux-style sudo): S_IFREG | S_ISUID | +# S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH +UID = 0 +GID = 0 +MODE = 35309 + +include $(APPDIR)/Application.mk diff --git a/system/sudo/sudo_main.c b/system/sudo/sudo_main.c new file mode 100644 index 000000000..53d96856f --- /dev/null +++ b/system/sudo/sudo_main.c @@ -0,0 +1,336 @@ +/**************************************************************************** + * apps/system/sudo/sudo_main.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 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SUDO_PROBE_ARG "--probe" +#define SUDO_MAX_PASSWORD 256 +#define SUDO_SUDOERS_LINE 128 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sudo_read_password + ****************************************************************************/ + +static int sudo_read_password(FAR char *password, size_t buflen) +{ + struct termios saved; + struct termios cfg; + ssize_t nread; + int errcode = 0; + bool restore = false; + + if (isatty(STDIN_FILENO) && tcgetattr(STDIN_FILENO, &saved) == 0) + { + cfg = saved; + cfg.c_lflag &= (tcflag_t)~ECHO; + if (tcsetattr(STDIN_FILENO, TCSANOW, &cfg) == 0) + { + restore = true; + } + } + + password[0] = '\0'; + nread = read(STDIN_FILENO, password, buflen - 1); + if (nread < 0) + { + errcode = errno; + } + + if (restore) + { + tcsetattr(STDIN_FILENO, TCSANOW, &saved); + } + + if (nread < 0) + { + return -errcode; + } + + if (nread == 0) + { + return -ENODATA; + } + + password[nread] = '\0'; + + if (password[nread - 1] == '\n') + { + password[nread - 1] = '\0'; + } + + fputc('\n', stderr); + return 0; +} + +/**************************************************************************** + * Name: sudo_token_eq + ****************************************************************************/ + +static bool sudo_token_eq(FAR const char *name, FAR const char *tok, + size_t toklen) +{ + return strncmp(name, tok, toklen) == 0 && name[toklen] == '\0'; +} + +/**************************************************************************** + * Name: sudo_name_in_csv + ****************************************************************************/ + +static bool sudo_name_in_csv(FAR const char *name, FAR const char *list) +{ + FAR const char *p = list; + FAR const char *start; + + if (name == NULL || list == NULL) + { + return false; + } + + while (*p != '\0') + { + while (*p == ',' || isspace((unsigned char)*p)) + { + p++; + } + + if (*p == '\0') + { + break; + } + + start = p; + while (*p != '\0' && *p != ',' && !isspace((unsigned char)*p)) + { + p++; + } + + if (sudo_token_eq(name, start, p - start)) + { + return true; + } + } + + return false; +} + +/**************************************************************************** + * Name: sudo_name_in_file + ****************************************************************************/ + +static bool sudo_name_in_file(FAR const char *name, FAR const char *path) +{ + FAR FILE *fp; + char line[SUDO_SUDOERS_LINE]; + FAR char *tok; + FAR char *end; + + fp = fopen(path, "r"); + if (fp == NULL) + { + return false; + } + + while (fgets(line, sizeof(line), fp) != NULL) + { + tok = line; + while (*tok != '\0' && isspace((unsigned char)*tok)) + { + tok++; + } + + if (*tok == '\0' || *tok == '#') + { + continue; + } + + end = tok; + while (*end != '\0' && !isspace((unsigned char)*end)) + { + end++; + } + + if (sudo_token_eq(name, tok, end - tok)) + { + fclose(fp); + return true; + } + } + + fclose(fp); + return false; +} + +/**************************************************************************** + * Name: sudo_user_allowed + * + * Description: + * Real UID 0 may always run sudo. Other users must appear in the sudoers + * file and/or CONFIG_SYSTEM_SUDO_ALLOWED_USERS. + * + ****************************************************************************/ + +static bool sudo_user_allowed(FAR const char *name, uid_t ruid) +{ + if (ruid == 0) + { + return true; + } + + if (sudo_name_in_csv(name, CONFIG_SYSTEM_SUDO_ALLOWED_USERS)) + { + return true; + } + + return sudo_name_in_file(name, CONFIG_SYSTEM_SUDO_SUDOERS_PATH); +} + +/**************************************************************************** + * Name: sudo_lookup_invoker + ****************************************************************************/ + +static int sudo_lookup_invoker(FAR struct passwd *result, + FAR char *buf, size_t buflen) +{ + FAR struct passwd *found; + int ret; + + ret = getpwuid_r(getuid(), result, buf, buflen, &found); + if (ret != 0) + { + return -ret; + } + + if (found == NULL) + { + return -ENOENT; + } + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * sudo_main + * + * Description: + * Linux-style setuid-root helper: the kernel raises effective UID to the + * file owner on exec (see nx_uid/nx_mode in the application build). + * This program checks the sudoers allowlist, verifies the invoking user's + * password, becomes fully root with setresuid/setresgid, then execvp()s + * the requested command. + * + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct passwd invoker; + char pwbuf[CONFIG_LIBC_PASSWD_LINESIZE]; + char password[SUDO_MAX_PASSWORD + 1]; + int ret; + + if (geteuid() != 0) + { + fprintf(stderr, "sudo: effective uid is not 0\n"); + return 1; + } + + if (argc >= 2 && strcmp(argv[1], SUDO_PROBE_ARG) == 0) + { + printf("ruid=%d euid=%d\n", getuid(), geteuid()); + return geteuid() == 0 && getuid() != 0 ? 0 : 1; + } + + if (argc < 2) + { + fprintf(stderr, "usage: sudo [args...]\n"); + return 1; + } + + ret = sudo_lookup_invoker(&invoker, pwbuf, sizeof(pwbuf)); + if (ret < 0) + { + fprintf(stderr, "sudo: cannot resolve invoking user: %d\n", -ret); + return 1; + } + + if (!sudo_user_allowed(invoker.pw_name, getuid())) + { + fprintf(stderr, "sudo: %s is not in the sudoers file\n", + invoker.pw_name); + return 1; + } + + fprintf(stderr, "[sudo] password for %s: ", invoker.pw_name); + + ret = sudo_read_password(password, sizeof(password)); + if (ret < 0) + { + fprintf(stderr, "sudo: password read failed: %d\n", -ret); + return 1; + } + + ret = passwd_verify(invoker.pw_name, password); + explicit_bzero(password, sizeof(password)); + if (!PASSWORD_VERIFY_MATCH(ret)) + { + fprintf(stderr, "sudo: authentication failure\n"); + return 1; + } + + if (setresuid(0, 0, 0) != 0 || setresgid(0, 0, 0) != 0) + { + fprintf(stderr, "sudo: cannot set root identity: %d\n", errno); + return 1; + } + + initgroups("root", 0); + execvp(argv[1], &argv[1]); + fprintf(stderr, "sudo: exec failed: %d\n", errno); + return 1; +} diff --git a/testing/ostest/multiuser.c b/testing/ostest/multiuser.c index e9a55721c..cb9d5fba4 100644 --- a/testing/ostest/multiuser.c +++ b/testing/ostest/multiuser.c @@ -40,6 +40,10 @@ #include #include +#if defined(CONFIG_LIBC_EXECFUNCS) && defined(CONFIG_SYSTEM_SUDO) +# include +#endif + #if !defined(CONFIG_DISABLE_MQUEUE) # include #endif @@ -1325,6 +1329,69 @@ static int multiuser_passwd_test(FAR struct mu_ctx_s *ctx) #endif /* CONFIG_LIBC_PASSWD_FILE && CONFIG_TESTING_OSTEST_MULTIUSER */ +#if defined(CONFIG_LIBC_EXECFUNCS) && defined(CONFIG_SYSTEM_SUDO) && \ + defined(CONFIG_SCHED_WAITPID) && !defined(CONFIG_BUILD_KERNEL) + +static int multiuser_sudo_setuid_test(FAR struct mu_ctx_s *ctx) +{ + posix_spawnattr_t attr; + FAR char * const spawn_argv[] = + { + (FAR char *)"sudo", (FAR char *)"--probe", NULL + }; + + pid_t pid; + int status; + int ret; + + printf("multiuser: setuid sudo exec after hard credential drop\n"); + + mu_restore_root(ctx); + ret = setuid(MU_UID1); + if (mu_expect_ok(ctx, "setuid(1000) before sudo exec", ret) != 0) + { + return ctx->failures; + } + + mu_check_eq(ctx, "parent euid before sudo", geteuid(), MU_UID1); + + ret = posix_spawnattr_init(&attr); + if (mu_expect_ok(ctx, "posix_spawnattr_init", ret) != 0) + { + return ctx->failures; + } + + ret = posix_spawn(&pid, "sudo", NULL, &attr, spawn_argv, NULL); + posix_spawnattr_destroy(&attr); + if (mu_expect_ok(ctx, "posix_spawn(sudo --probe)", ret) != 0) + { + mu_restore_root(ctx); + return ctx->failures; + } + + if (waitpid(pid, &status, 0) != pid) + { + mu_fail(ctx, "waitpid(sudo) errno=%d", errno); + mu_restore_root(ctx); + return ctx->failures; + } + + if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) + { + mu_fail(ctx, "sudo --probe status=%d", status); + } + else + { + mu_pass("sudo --probe after hard drop"); + } + + mu_check_eq(ctx, "parent euid after sudo", geteuid(), MU_UID1); + mu_restore_root(ctx); + return ctx->failures; +} + +#endif /* CONFIG_LIBC_EXECFUNCS && CONFIG_SYSTEM_SUDO && CONFIG_SCHED_WAITPID */ + #endif /* CONFIG_SCHED_USER_IDENTITY */ /**************************************************************************** @@ -1389,6 +1456,14 @@ int multiuser_test(void) "(need LIBC_PASSWD_FILE and TESTING_OSTEST_MULTIUSER)\n"); #endif +#if defined(CONFIG_LIBC_EXECFUNCS) && defined(CONFIG_SYSTEM_SUDO) && \ + defined(CONFIG_SCHED_WAITPID) && !defined(CONFIG_BUILD_KERNEL) + multiuser_sudo_setuid_test(&ctx); +#else + printf("multiuser: skipping setuid sudo exec test " + "(need LIBC_EXECFUNCS, SYSTEM_SUDO, SCHED_WAITPID)\n"); +#endif + mu_restore_root(&ctx); printf("multiuser_test: %d failure(s)\n", ctx.failures);