mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
fs/binfmt: Enforce POSIX execute permissions prior to binary load
Adds a pre-load permission check in exec_internal() to verify the calling task has the required execute (x) bits for the target file. Properly evaluates root (euid == 0), owner, group, and other permissions. This ensures POSIX compliance and cleanly rejects unauthorized files with -EACCES before they reach the ELF loader, preventing unnecessary memory allocation and downstream hardware execution faults. Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
This commit is contained in:
parent
2cddf4e638
commit
72192fc8bc
9 changed files with 199 additions and 16 deletions
|
|
@ -41,6 +41,10 @@ list(
|
|||
binfmt_copyactions.c
|
||||
binfmt_dumpmodule.c)
|
||||
|
||||
if(CONFIG_SCHED_USER_IDENTITY)
|
||||
list(APPEND SRCS binfmt_checkexec.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BINFMT_LOADABLE)
|
||||
list(APPEND SRCS binfmt_exit.c)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ CSRCS = binfmt_globals.c binfmt_initialize.c binfmt_register.c binfmt_unregiste
|
|||
CSRCS += binfmt_loadmodule.c binfmt_unloadmodule.c binfmt_execmodule.c
|
||||
CSRCS += binfmt_exec.c binfmt_copyargv.c binfmt_copyactions.c binfmt_dumpmodule.c
|
||||
|
||||
ifeq ($(CONFIG_SCHED_USER_IDENTITY),y)
|
||||
CSRCS += binfmt_checkexec.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_BINFMT_LOADABLE),y)
|
||||
CSRCS += binfmt_exit.c
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -213,6 +213,26 @@ void binfmt_freeactions(FAR const posix_spawn_file_actions_t *copy);
|
|||
# define binfmt_freeactions(copy)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SCHED_USER_IDENTITY
|
||||
/****************************************************************************
|
||||
* Name: binfmt_checkexecperm
|
||||
*
|
||||
* Description:
|
||||
* Verify that the calling task has execute permission on the file
|
||||
* described by 'bin'. The file owner, group, and mode must already be
|
||||
* populated in the binary_s structure.
|
||||
*
|
||||
* Input Parameters:
|
||||
* bin - Load structure with uid, gid, and mode populated
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) on success; -EACCES if execute permission is denied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int binfmt_checkexecperm(FAR struct binary_s *bin);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BUILTIN
|
||||
/****************************************************************************
|
||||
* Name: builtin_initialize
|
||||
|
|
|
|||
100
binfmt/binfmt_checkexec.c
Normal file
100
binfmt/binfmt_checkexec.c
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/****************************************************************************
|
||||
* binfmt/binfmt_checkexec.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 <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
#include <nuttx/binfmt/binfmt.h>
|
||||
|
||||
#include "binfmt.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: binfmt_checkexecperm
|
||||
*
|
||||
* Description:
|
||||
* Verify that the calling task has execute permission on the file
|
||||
* described by 'bin'. The file owner, group, and mode must already be
|
||||
* populated in the binary_s structure before calling this function.
|
||||
*
|
||||
* Input Parameters:
|
||||
* bin - Pointer to the binary descriptor with uid, gid, and mode set.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) on success; -EACCES if execute permission is denied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int binfmt_checkexecperm(FAR struct binary_s *bin)
|
||||
{
|
||||
FAR struct tcb_s *rtcb;
|
||||
mode_t xbits;
|
||||
|
||||
rtcb = nxsched_self();
|
||||
|
||||
if (bin == NULL || rtcb == NULL || rtcb->group == NULL)
|
||||
{
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (rtcb->group->tg_euid == 0)
|
||||
{
|
||||
/* Root can execute any file that has at least one execute bit set */
|
||||
|
||||
if ((bin->mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
|
||||
{
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (rtcb->group->tg_euid == bin->uid)
|
||||
{
|
||||
xbits = S_IXUSR;
|
||||
}
|
||||
else if (rtcb->group->tg_egid == bin->gid)
|
||||
{
|
||||
xbits = S_IXGRP;
|
||||
}
|
||||
else
|
||||
{
|
||||
xbits = S_IXOTH;
|
||||
}
|
||||
|
||||
if ((bin->mode & xbits) == 0)
|
||||
{
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
|
@ -31,7 +31,6 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <nuttx/sched.h>
|
||||
#include <nuttx/binfmt/binfmt.h>
|
||||
|
||||
#include "binfmt.h"
|
||||
|
|
@ -81,11 +80,13 @@ static int exec_internal(FAR const char *filename,
|
|||
FAR const posix_spawnattr_t *attr, bool spawn)
|
||||
{
|
||||
FAR struct binary_s *bin;
|
||||
int pid;
|
||||
int ret;
|
||||
#ifndef CONFIG_BINFMT_LOADABLE
|
||||
struct binary_s sbin;
|
||||
#endif
|
||||
int pid;
|
||||
int ret;
|
||||
|
||||
#ifndef CONFIG_BINFMT_LOADABLE
|
||||
bin = &sbin;
|
||||
memset(bin, 0, sizeof(*bin));
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -134,6 +134,14 @@ static int load_absmodule(FAR struct binary_s *bin, FAR const char *filename,
|
|||
binfmt_dumpmodule(bin);
|
||||
break;
|
||||
}
|
||||
else if (ret == -EACCES)
|
||||
{
|
||||
/* Access explicitly denied -- stop here; do not let a fallback
|
||||
* loader bypass the execute-permission check that already ran.
|
||||
*/
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
#include <nuttx/binfmt/binfmt.h>
|
||||
#include <nuttx/lib/builtin.h>
|
||||
|
||||
#include "binfmt.h"
|
||||
|
||||
#ifdef CONFIG_BUILTIN
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -76,6 +78,9 @@ static int builtin_loadbinary(FAR struct binary_s *binp,
|
|||
FAR const struct builtin_s *builtin;
|
||||
FAR char *name;
|
||||
int index;
|
||||
#ifdef CONFIG_SCHED_USER_IDENTITY
|
||||
int chk;
|
||||
#endif
|
||||
|
||||
binfo("Loading file: %s\n", filename);
|
||||
|
||||
|
|
@ -105,14 +110,21 @@ static int builtin_loadbinary(FAR struct binary_s *binp,
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCHED_USER_IDENTITY
|
||||
binp->uid = builtin->uid;
|
||||
binp->gid = builtin->gid;
|
||||
binp->mode = builtin->mode;
|
||||
|
||||
chk = binfmt_checkexecperm(binp);
|
||||
if (chk < 0)
|
||||
{
|
||||
return chk;
|
||||
}
|
||||
#endif
|
||||
|
||||
binp->entrypt = builtin->main;
|
||||
binp->stacksize = builtin->stacksize;
|
||||
binp->priority = builtin->priority;
|
||||
#ifdef CONFIG_SCHED_USER_IDENTITY
|
||||
binp->uid = builtin->uid;
|
||||
binp->gid = builtin->gid;
|
||||
binp->mode = builtin->mode;
|
||||
#endif
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
|
|
|||
24
binfmt/elf.c
24
binfmt/elf.c
|
|
@ -37,6 +37,8 @@
|
|||
#include <nuttx/binfmt/binfmt.h>
|
||||
#include <nuttx/kmalloc.h>
|
||||
|
||||
#include "binfmt.h"
|
||||
|
||||
#ifdef CONFIG_ELF
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -110,6 +112,20 @@ static int elf_loadbinary(FAR struct binary_s *binp,
|
|||
goto errout_with_init;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCHED_USER_IDENTITY
|
||||
/* Save IDs and mode from file system before loading segments */
|
||||
|
||||
binp->uid = loadinfo.fileuid;
|
||||
binp->gid = loadinfo.filegid;
|
||||
binp->mode = loadinfo.filemode;
|
||||
|
||||
ret = binfmt_checkexecperm(binp);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto errout_with_init;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Load the program binary */
|
||||
|
||||
ret = libelf_load_with_addrenv(&loadinfo);
|
||||
|
|
@ -201,14 +217,6 @@ static int elf_loadbinary(FAR struct binary_s *binp,
|
|||
binp->mod.nfini = loadinfo.nfini;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SCHED_USER_IDENTITY
|
||||
/* Save IDs and mode from file system */
|
||||
|
||||
binp->uid = loadinfo.fileuid;
|
||||
binp->gid = loadinfo.filegid;
|
||||
binp->mode = loadinfo.filemode;
|
||||
#endif
|
||||
|
||||
libelf_dumpentrypt(&loadinfo);
|
||||
#ifdef CONFIG_PIC
|
||||
if (loadinfo.gotindex >= 0)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
|
@ -36,10 +37,13 @@
|
|||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <nuttx/fs/fs.h>
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <nuttx/binfmt/binfmt.h>
|
||||
#include <nuttx/binfmt/nxflat.h>
|
||||
|
||||
#include "binfmt.h"
|
||||
|
||||
#ifdef CONFIG_NXFLAT
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -142,6 +146,9 @@ static int nxflat_loadbinary(FAR struct binary_s *binp,
|
|||
int nexports)
|
||||
{
|
||||
struct nxflat_loadinfo_s loadinfo; /* Contains globals for libnxflat */
|
||||
#ifdef CONFIG_SCHED_USER_IDENTITY
|
||||
struct stat st;
|
||||
#endif
|
||||
int ret;
|
||||
|
||||
binfo("Loading file: %s\n", filename);
|
||||
|
|
@ -156,6 +163,25 @@ static int nxflat_loadbinary(FAR struct binary_s *binp,
|
|||
goto errout;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCHED_USER_IDENTITY
|
||||
ret = file_fstat(&loadinfo.file, &st);
|
||||
if (ret < 0)
|
||||
{
|
||||
berr("Failed to stat NXFLAT program binary: %d\n", ret);
|
||||
goto errout_with_init;
|
||||
}
|
||||
|
||||
binp->uid = st.st_uid;
|
||||
binp->gid = st.st_gid;
|
||||
binp->mode = st.st_mode;
|
||||
|
||||
ret = binfmt_checkexecperm(binp);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto errout_with_init;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Load the program binary */
|
||||
|
||||
ret = nxflat_load(&loadinfo);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue