mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-19 04:28:28 +00:00
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 <mishra.abhishek2808@gmail.com>
This commit is contained in:
parent
84ffa84e24
commit
7358692c59
12 changed files with 643 additions and 19 deletions
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
41
system/sudo/CMakeLists.txt
Normal file
41
system/sudo/CMakeLists.txt
Normal file
|
|
@ -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()
|
||||
47
system/sudo/Kconfig
Normal file
47
system/sudo/Kconfig
Normal file
|
|
@ -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
|
||||
25
system/sudo/Make.defs
Normal file
25
system/sudo/Make.defs
Normal file
|
|
@ -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
|
||||
38
system/sudo/Makefile
Normal file
38
system/sudo/Makefile
Normal file
|
|
@ -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
|
||||
336
system/sudo/sudo_main.c
Normal file
336
system/sudo/sudo_main.c
Normal file
|
|
@ -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 <nuttx/config.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fsutils/passwd.h>
|
||||
|
||||
/****************************************************************************
|
||||
* 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 <command> [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;
|
||||
}
|
||||
|
|
@ -40,6 +40,10 @@
|
|||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(CONFIG_LIBC_EXECFUNCS) && defined(CONFIG_SYSTEM_SUDO)
|
||||
# include <spawn.h>
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_DISABLE_MQUEUE)
|
||||
# include <mqueue.h>
|
||||
#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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue