Skip to content

Commit fa7f856

Browse files
Abhishekmishra2808xiaoxiang781216
authored andcommitted
fs/vfs: enforce pseudoFS permissions on mutation operations
Add pseudoFS permission enforcement for unlink(), mkdir(), and rename() VFS mutation operations. This change validates parent-directory permissions before modifying pseudoFS inode topology and returns -EACCES for unauthorized operations. The implementation preserves mountpoint filesystem behavior and fixes multiple inode lifetime/search-state issues in the rename path. Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
1 parent 59a66fa commit fa7f856

5 files changed

Lines changed: 197 additions & 79 deletions

File tree

fs/inode/fs_inode.c

Lines changed: 113 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <assert.h>
2828
#include <errno.h>
2929
#include <fcntl.h>
30+
#include <unistd.h>
3031

3132
#include <nuttx/fs/fs.h>
3233
#include <nuttx/rwsem.h>
@@ -48,6 +49,70 @@
4849

4950
static rw_semaphore_t g_inode_lock = RWSEM_INITIALIZER;
5051

52+
/****************************************************************************
53+
* Private Functions
54+
****************************************************************************/
55+
56+
/****************************************************************************
57+
* Name: _inode_checkmode
58+
*
59+
* Description:
60+
* Test effective credentials against 'inode' for 'amode' access.
61+
* Kernel threads always pass.
62+
*
63+
* Returned Value:
64+
* Zero (OK) or -EACCES.
65+
*
66+
****************************************************************************/
67+
68+
#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
69+
static int _inode_checkmode(FAR struct inode *inode, int amode)
70+
{
71+
FAR struct tcb_s *rtcb;
72+
mode_t perm;
73+
uid_t uid;
74+
gid_t gid;
75+
76+
/* Kernel threads are always granted access */
77+
78+
rtcb = nxsched_self();
79+
if ((rtcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL)
80+
{
81+
return OK;
82+
}
83+
84+
/* Use effective credentials */
85+
86+
DEBUGASSERT(rtcb->group != NULL);
87+
uid = rtcb->group->tg_euid;
88+
gid = rtcb->group->tg_egid;
89+
90+
/* Select the applicable permission-bit triplet */
91+
92+
if (uid == inode->i_owner)
93+
{
94+
perm = (inode->i_mode >> 6) & 7;
95+
}
96+
else if (gid == inode->i_group)
97+
{
98+
perm = (inode->i_mode >> 3) & 7;
99+
}
100+
else
101+
{
102+
perm = inode->i_mode & 7;
103+
}
104+
105+
/* Every requested bit must be present in the selected triplet */
106+
107+
if ((amode & perm) != amode)
108+
{
109+
return -EACCES;
110+
}
111+
112+
return OK;
113+
}
114+
#endif /* CONFIG_PSEUDOFS_ATTRIBUTES && CONFIG_SCHED_USER_IDENTITY */
115+
51116
/****************************************************************************
52117
* Public Functions
53118
****************************************************************************/
@@ -124,49 +189,45 @@ void inode_runlock(void)
124189
* Name: inode_checkperm
125190
*
126191
* Description:
127-
* Validate that 'inode' can be opened with the access described by
128-
* 'oflags'. Two sequential checks are performed:
129-
*
130-
* 1. Operation-support check (all inode types, unconditional):
131-
* Verifies the driver exposes the read/write entry points required by
132-
* 'oflags'. Returns -ENXIO when ops are NULL and -EACCES when the
133-
* required entry point is absent. Pseudo-directory inodes
134-
* (INODE_IS_PSEUDODIR) are exempted from this step.
135-
*
136-
* 2. UNIX permission check (pseudo-filesystem inodes only):
137-
* Compares effective uid/gid against i_mode owner/group/other bits.
138-
* Mountpoint inodes and kernel threads are unconditionally exempted.
139-
* Requires CONFIG_PSEUDOFS_ATTRIBUTES and CONFIG_SCHED_USER_IDENTITY;
140-
* when either option is disabled this step is a no-op.
192+
* Validate open access to 'inode' for 'oflags'. Checks driver operation
193+
* support, then pseudo-filesystem mode bits when enabled. Mountpoints
194+
* are exempt from mode checks.
141195
*
142196
* Input Parameters:
143197
* inode - The inode to check
144198
* oflags - Open flags (O_RDONLY / O_WRONLY / O_RDWR)
145199
*
146200
* Returned Value:
147-
* Zero (OK) on success. Negated errno on failure:
148-
* -ENXIO ops pointer is NULL
149-
* -EACCES required operation not supported, or permission denied
201+
* Zero (OK) on success, or a negated errno on failure.
150202
*
151203
****************************************************************************/
152204

153205
int inode_checkperm(FAR struct inode *inode, int oflags)
154206
{
155207
#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
156-
FAR struct tcb_s *rtcb;
157-
mode_t perm;
158-
uid_t uid;
159-
gid_t gid;
208+
int amode = 0;
160209
#endif
161210
FAR const struct file_operations *ops;
162211

163-
/* === Step 1: operation-support check === */
212+
#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
213+
if ((oflags & O_RDOK) != 0)
214+
{
215+
amode |= R_OK;
216+
}
164217

165-
/* Pseudo-directories carry no ops and are always accessible */
218+
if ((oflags & O_WROK) != 0)
219+
{
220+
amode |= W_OK;
221+
}
222+
#endif
166223

167224
if (INODE_IS_PSEUDODIR(inode))
168225
{
226+
#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
227+
return _inode_checkmode(inode, amode);
228+
#else
169229
return OK;
230+
#endif
170231
}
171232

172233
ops = inode->u.i_ops;
@@ -185,61 +246,51 @@ int inode_checkperm(FAR struct inode *inode, int oflags)
185246

186247
#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
187248

188-
/* === Step 2: UNIX permission check (pseudo-filesystem inodes only) === */
189-
190-
/* Mountpoints delegate permission enforcement to the underlying
191-
* filesystem
192-
*/
193-
194249
if (INODE_IS_MOUNTPT(inode))
195250
{
196251
return OK;
197252
}
198253

199-
/* Kernel threads are always granted access */
200-
201-
rtcb = nxsched_self();
202-
if ((rtcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL)
203-
{
204-
return OK;
205-
}
206-
207-
/* Use effective credentials */
254+
return _inode_checkmode(inode, amode);
208255

209-
DEBUGASSERT(rtcb->group != NULL);
210-
uid = rtcb->group->tg_euid;
211-
gid = rtcb->group->tg_egid;
256+
#endif /* CONFIG_PSEUDOFS_ATTRIBUTES && CONFIG_SCHED_USER_IDENTITY */
212257

213-
/* Select the applicable permission-bit triplet */
258+
return OK;
259+
}
214260

215-
if (uid == inode->i_owner)
216-
{
217-
perm = (inode->i_mode >> 6) & 7;
218-
}
219-
else if (gid == inode->i_group)
220-
{
221-
perm = (inode->i_mode >> 3) & 7;
222-
}
223-
else
224-
{
225-
perm = inode->i_mode & 7;
226-
}
261+
/****************************************************************************
262+
* Name: inode_checkdirperm
263+
*
264+
* Description:
265+
* Check parent directory 'dir' for 'amode' access on pseudo-filesystem
266+
* inodes. NULL 'dir' (root) and mountpoints are exempt.
267+
*
268+
* Input Parameters:
269+
* dir - Parent directory inode, or NULL for a root-level path
270+
* amode - Access mode bitmask (R_OK / W_OK / X_OK)
271+
*
272+
* Returned Value:
273+
* Zero (OK) on success, or -EACCES if permission is denied.
274+
*
275+
****************************************************************************/
227276

228-
/* Bit 2 (value 4) = read permission */
277+
int inode_checkdirperm(FAR struct inode *dir, int amode)
278+
{
279+
#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
229280

230-
if (((oflags & O_RDOK) != 0) && ((perm & 4) == 0))
281+
if (dir == NULL)
231282
{
232-
return -EACCES;
283+
return OK;
233284
}
234285

235-
/* Bit 1 (value 2) = write permission */
236-
237-
if (((oflags & O_WROK) != 0) && ((perm & 2) == 0))
286+
if (INODE_IS_MOUNTPT(dir))
238287
{
239-
return -EACCES;
288+
return OK;
240289
}
241290

242-
#endif /* CONFIG_PSEUDOFS_ATTRIBUTES && CONFIG_SCHED_USER_IDENTITY */
291+
return _inode_checkmode(dir, amode);
243292

293+
#else
244294
return OK;
295+
#endif /* CONFIG_PSEUDOFS_ATTRIBUTES && CONFIG_SCHED_USER_IDENTITY */
245296
}

fs/inode/inode.h

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -422,32 +422,39 @@ void inode_release(FAR struct inode *inode);
422422
* Name: inode_checkperm
423423
*
424424
* Description:
425-
* Validate that 'inode' can be opened with the access described by
426-
* 'oflags'. Two sequential checks are performed:
427-
*
428-
* 1. Operation-support check (all inode types):
429-
* Ensures the driver exposes the read/write entry points required by
430-
* 'oflags'. Pseudo-directory inodes are exempted.
431-
*
432-
* 2. UNIX permission check (pseudo-filesystem inodes only):
433-
* Compares effective uid/gid against i_mode owner/group/other bits.
434-
* Mountpoint inodes and kernel threads are unconditionally exempted.
435-
* Active only when CONFIG_PSEUDOFS_ATTRIBUTES and
436-
* CONFIG_SCHED_USER_IDENTITY are both enabled.
425+
* Validate open access to 'inode' for 'oflags'. Checks driver operation
426+
* support, then pseudo-filesystem mode bits when enabled. Mountpoints
427+
* are exempt from mode checks.
437428
*
438429
* Input Parameters:
439430
* inode - The inode to check
440431
* oflags - Open flags (O_RDONLY / O_WRONLY / O_RDWR)
441432
*
442433
* Returned Value:
443-
* Zero (OK) on success. Negated errno on failure:
444-
* -ENXIO ops pointer is NULL
445-
* -EACCES required operation not supported, or permission denied
434+
* Zero (OK) on success, or a negated errno on failure.
446435
*
447436
****************************************************************************/
448437

449438
int inode_checkperm(FAR struct inode *inode, int oflags);
450439

440+
/****************************************************************************
441+
* Name: inode_checkdirperm
442+
*
443+
* Description:
444+
* Check parent directory 'dir' for 'amode' access on pseudo-filesystem
445+
* inodes. NULL 'dir' (root) and mountpoints are exempt.
446+
*
447+
* Input Parameters:
448+
* dir - Parent directory inode, or NULL for a root-level path
449+
* amode - Access mode bitmask (R_OK / W_OK / X_OK)
450+
*
451+
* Returned Value:
452+
* Zero (OK) on success, or -EACCES if permission is denied.
453+
*
454+
****************************************************************************/
455+
456+
int inode_checkdirperm(FAR struct inode *dir, int amode);
457+
451458
/****************************************************************************
452459
* Name: foreach_inode
453460
*

fs/vfs/fs_mkdir.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <stdbool.h>
3232
#include <assert.h>
3333
#include <errno.h>
34+
#include <unistd.h>
3435

3536
#include <nuttx/fs/fs.h>
3637

@@ -136,6 +137,18 @@ int mkdir(const char *pathname, mode_t mode)
136137

137138
else
138139
{
140+
/* Verify write+search permission on the parent directory before
141+
* adding a new name to the pseudo-filesystem tree. POSIX requires
142+
* both W_OK and X_OK to create a directory entry.
143+
*/
144+
145+
ret = inode_checkdirperm(desc.parent, W_OK | X_OK);
146+
if (ret < 0)
147+
{
148+
errcode = -ret;
149+
goto errout_with_search;
150+
}
151+
139152
/* Create an inode in the pseudo-filesystem at this path.
140153
* NOTE that the new inode will be created with a reference
141154
* count of zero.

0 commit comments

Comments
 (0)