diff --git a/fs/inode/fs_inode.c b/fs/inode/fs_inode.c index b957a8ccecd..32dee8ff432 100644 --- a/fs/inode/fs_inode.c +++ b/fs/inode/fs_inode.c @@ -24,8 +24,13 @@ * Included Files ****************************************************************************/ +#include +#include +#include + #include #include +#include #include "inode/inode.h" @@ -114,3 +119,127 @@ void inode_runlock(void) { up_read(&g_inode_lock); } + +/**************************************************************************** + * Name: inode_checkperm + * + * Description: + * Validate that 'inode' can be opened with the access described by + * 'oflags'. Two sequential checks are performed: + * + * 1. Operation-support check (all inode types, unconditional): + * Verifies the driver exposes the read/write entry points required by + * 'oflags'. Returns -ENXIO when ops are NULL and -EACCES when the + * required entry point is absent. Pseudo-directory inodes + * (INODE_IS_PSEUDODIR) are exempted from this step. + * + * 2. UNIX permission check (pseudo-filesystem inodes only): + * Compares effective uid/gid against i_mode owner/group/other bits. + * Mountpoint inodes and kernel threads are unconditionally exempted. + * Requires CONFIG_PSEUDOFS_ATTRIBUTES and CONFIG_SCHED_USER_IDENTITY; + * when either option is disabled this step is a no-op. + * + * Input Parameters: + * inode - The inode to check + * oflags - Open flags (O_RDONLY / O_WRONLY / O_RDWR) + * + * Returned Value: + * Zero (OK) on success. Negated errno on failure: + * -ENXIO ops pointer is NULL + * -EACCES required operation not supported, or permission denied + * + ****************************************************************************/ + +int inode_checkperm(FAR struct inode *inode, int oflags) +{ +#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY) + FAR struct tcb_s *rtcb; + mode_t perm; + uid_t uid; + gid_t gid; +#endif + FAR const struct file_operations *ops; + + /* === Step 1: operation-support check === */ + + /* Pseudo-directories carry no ops and are always accessible */ + + if (INODE_IS_PSEUDODIR(inode)) + { + return OK; + } + + ops = inode->u.i_ops; + if (ops == NULL) + { + return -ENXIO; + } + + if (((oflags & O_RDOK) != 0 && + !ops->readv && !ops->read && !ops->ioctl) || + ((oflags & O_WROK) != 0 && + !ops->writev && !ops->write && !ops->ioctl)) + { + return -EACCES; + } + +#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY) + + /* === Step 2: UNIX permission check (pseudo-filesystem inodes only) === */ + + /* Mountpoints delegate permission enforcement to the underlying + * filesystem + */ + + if (INODE_IS_MOUNTPT(inode)) + { + return OK; + } + + /* Kernel threads are always granted access */ + + rtcb = nxsched_self(); + if ((rtcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL) + { + return OK; + } + + /* Use effective credentials */ + + DEBUGASSERT(rtcb->group != NULL); + uid = rtcb->group->tg_euid; + gid = rtcb->group->tg_egid; + + /* Select the applicable permission-bit triplet */ + + if (uid == inode->i_owner) + { + perm = (inode->i_mode >> 6) & 7; + } + else if (gid == inode->i_group) + { + perm = (inode->i_mode >> 3) & 7; + } + else + { + perm = inode->i_mode & 7; + } + + /* Bit 2 (value 4) = read permission */ + + if (((oflags & O_RDOK) != 0) && ((perm & 4) == 0)) + { + return -EACCES; + } + + /* Bit 1 (value 2) = write permission */ + + if (((oflags & O_WROK) != 0) && ((perm & 2) == 0)) + { + return -EACCES; + } + +#endif /* CONFIG_PSEUDOFS_ATTRIBUTES && CONFIG_SCHED_USER_IDENTITY */ + + return OK; +} diff --git a/fs/inode/inode.h b/fs/inode/inode.h index 638959ff8fd..8eccad2d367 100644 --- a/fs/inode/inode.h +++ b/fs/inode/inode.h @@ -418,6 +418,36 @@ void inode_addref(FAR struct inode *inode); void inode_release(FAR struct inode *inode); +/**************************************************************************** + * Name: inode_checkperm + * + * Description: + * Validate that 'inode' can be opened with the access described by + * 'oflags'. Two sequential checks are performed: + * + * 1. Operation-support check (all inode types): + * Ensures the driver exposes the read/write entry points required by + * 'oflags'. Pseudo-directory inodes are exempted. + * + * 2. UNIX permission check (pseudo-filesystem inodes only): + * Compares effective uid/gid against i_mode owner/group/other bits. + * Mountpoint inodes and kernel threads are unconditionally exempted. + * Active only when CONFIG_PSEUDOFS_ATTRIBUTES and + * CONFIG_SCHED_USER_IDENTITY are both enabled. + * + * Input Parameters: + * inode - The inode to check + * oflags - Open flags (O_RDONLY / O_WRONLY / O_RDWR) + * + * Returned Value: + * Zero (OK) on success. Negated errno on failure: + * -ENXIO ops pointer is NULL + * -EACCES required operation not supported, or permission denied + * + ****************************************************************************/ + +int inode_checkperm(FAR struct inode *inode, int oflags); + /**************************************************************************** * Name: foreach_inode * diff --git a/fs/vfs/fs_open.c b/fs/vfs/fs_open.c index 65b8a344b0c..4131d14b36e 100644 --- a/fs/vfs/fs_open.c +++ b/fs/vfs/fs_open.c @@ -48,54 +48,6 @@ * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: inode_checkflags - * - * Description: - * Check if the access described by 'oflags' is supported on 'inode' - * - * inode_checkflags() is an internal NuttX interface and should not be - * called from applications. - * - * Input Parameters: - * inode - The inode to check - * oflags - open flags. - * - * Returned Value: - * Zero (OK) is returned on success. On failure, a negated errno value is - * returned. - * - ****************************************************************************/ - -static int inode_checkflags(FAR struct inode *inode, int oflags) -{ - FAR const struct file_operations *ops = inode->u.i_ops; - - if (INODE_IS_PSEUDODIR(inode)) - { - return OK; - } - - if (ops == NULL) - { - return -ENXIO; - } - - if (((oflags & O_RDOK) != 0 && !ops->readv && !ops->read && !ops->ioctl) || - ((oflags & O_WROK) != 0 && !ops->writev && !ops->write && !ops->ioctl)) - { - return -EACCES; - } - else - { - return OK; - } -} - -/**************************************************************************** - * Name: file_vopen - ****************************************************************************/ - /**************************************************************************** * Name: file_vopen * @@ -218,9 +170,9 @@ static int file_vopen(FAR struct file *filep, FAR const char *path, } #endif - /* Make sure that the inode supports the requested access */ + /* Validate operation support and pseudo-filesystem permissions */ - ret = inode_checkflags(inode, oflags); + ret = inode_checkperm(inode, oflags); if (ret < 0) { goto errout_with_inode;