mount/umount 的 root 检测
mount/umount 命令在应用层做了强制的权限检测,通过赋予普通用户进程 CAP_SYS_ADMIN
capability 不能解决普通用户进程没有权限执行 mount/umount 命令的问题,需要为普通用户添加 sudo 权限。
$ export LIBMOUNT_DEBUG=0xffff
$ umount /media
74375: libmount: don't print memory addresses (SUID executable).
74375: libmount: INIT: library debug mask: 0x100ffff
74375: libmount: INIT: library version: 2.34.0
74375: libmount: INIT: feature: selinux
74375: libmount: INIT: feature: smack
74375: libmount: INIT: feature: btrfs
74375: libmount: INIT: feature: namespaces
74375: libmount: INIT: feature: assert
74375: libmount: INIT: feature: debug
Available "LIBMOUNT_DEBUG=<name>[,...]|<mask>" debug masks:
all [0xffff] : info about all subsystems
cache [0x0004] : paths and tags cache
cxt [0x0200] : library context (handler)
diff [0x0400] : mountinfo changes tracking
fs [0x0040] : FS abstraction
help [0x0001] : this help
locks [0x0010] : mtab and utab locking
loop [0x2000] : loop devices routines
options [0x0008] : mount options parsing
tab [0x0020] : fstab, mtab, mountinfo routines
update [0x0080] : mtab, utab updates
utils [0x0100] : misc library utils
monitor [0x0800] : mount tables monitor
btrfs [0x1000] : btrfs specific routines
74375: libmount: CXT: ----> allocate [RESTRICTED]
74375: libmount: CXT: umount: /media
74375: libmount: CXT: umount: lookup FS for '/media'
...
构建 mount/umount 命令行需要使能 libblkid 与 libmount:
$ git clone https://github.com/karelzak/util-linux.git
$ cd util-linux/
$ git co v2.34
$ ./autogen.sh
$ ./configure CFLAGS="-g -O0" --prefix=/usr --disable-all-programs --enable-libblkid --enable-libmount --enable-mount
$ make
$ cd .libs
$ ls
libblkid.a libblkid.lai libblkid.so.1 libcommon.a libmount.a libmount.lai libmount.so.1 libmount.so.1.1.0T libtcolors.la umount
libblkid.la libblkid.so libblkid.so.1.1.0 libcommon.la libmount.la libmount.so libmount.so.1.1.0 libtcolors.a mount
$ export LD_LIBRARY_PATH=$PWD
$ gdb ./umount
(gdb) set args /media
(gdb) b mnt_context_prepare_umount
Function "mnt_context_prepare_umount" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (mnt_context_prepare_umount) pending.
(gdb) r
Starting program: /home/runsisi/working/cpp/util-linux/.libs/umount /media
[Detaching after fork from child process 76658]
Breakpoint 1, mnt_context_prepare_umount (cxt=0x555555560538) at libmount/src/context_umount.c:877
877 {
虽然 CAP_SYS_ADMIN capability 权限允许调用 mount/umount 系统调用,但是 mount/umount 命令自身会进行 uid 检测,也就是说,必须 root 用户才能执行 mount/umount 操作,以 util-linux v2.34 版本为例。
mount 的权限检测
// libmount/src/context.c
struct libmnt_context *mnt_new_context(void)
{
struct libmnt_context *cxt;
uid_t ruid, euid;
cxt = calloc(1, sizeof(*cxt));
if (!cxt)
return NULL;
// 非 root 用户操作受限
/* if we're really root and aren't running setuid */
cxt->restricted = (uid_t) 0 == ruid && ruid == euid ? 0 : 1;
return cxt;
}
// sys-utils/mount.c
main
if (argc == 2 && !mnt_context_get_source(cxt)
&& !mnt_context_get_target(cxt)) {
// 非 root 用户不允许进行 mount 操作
/*
* D) mount <source> <target>
*/
if (mnt_context_is_restricted(cxt))
exit_non_root(NULL);
mnt_context_set_source(cxt, argv[0]);
mnt_context_set_target(cxt, argv[1]);
}
umount 的权限检测
// libmount/src/context_umount.c
static int evaluate_permissions(struct libmnt_context *cxt)
{
struct libmnt_table *fstab;
unsigned long u_flags = 0;
const char *tgt, *src, *optstr;
int rc = 0, ok = 0;
struct libmnt_fs *fs;
// root 用户直接允许 umount
if (!mnt_context_is_restricted(cxt))
return 0; /* superuser mount */
// 非 root 用户必须要检测 /etc/fstab 中是否定义了 user, users, owner, group 之类的选项
// 选项在如下的文件中进行定义:
// https://github.com/karelzak/util-linux/blob/v2.34/libmount/src/optmap.c
/*
* User mounts have to be in /etc/fstab
*/
rc = mnt_context_get_fstab(cxt, &fstab);
if (rc)
return rc;
tgt = mnt_fs_get_target(cxt->fs);
src = mnt_fs_get_source(cxt->fs);
fs = mnt_table_find_pair(fstab, src, tgt, MNT_ITER_FORWARD);
if (!fs) {
/*
* It's possible that there is /path/file.img in fstab and
* /dev/loop0 in mtab -- then we have to check the relation
* between loopdev and the file.
*/
fs = mnt_table_find_target(fstab, tgt, MNT_ITER_FORWARD);
if (fs) {
struct libmnt_cache *cache = mnt_context_get_cache(cxt);
const char *sp = mnt_fs_get_srcpath(cxt->fs); /* devname from mtab */
const char *dev = sp && cache ? mnt_resolve_path(sp, cache) : sp;
if (!dev || !is_associated_fs(dev, fs))
fs = NULL;
}
if (!fs) {
DBG(CXT, ul_debugobj(cxt,
"umount %s: mtab disagrees with fstab",
tgt));
goto eperm;
}
}
// /etc/fstab 中找到了相关的 mount 条目,接下来检测 user, users, owner, group 之类的选项
/*
* User mounting and unmounting is allowed only if fstab contains one
* of the options `user', `users' or `owner' or `group'.
*
* The option `users' allows arbitrary users to mount and unmount -
* this may be a security risk.
*
* The options `user', `owner' and `group' only allow unmounting by the
* user that mounted (visible in mtab).
*/
optstr = mnt_fs_get_user_options(fs); /* FSTAB mount options! */
if (!optstr)
goto eperm;
if (mnt_optstr_get_flags(optstr, &u_flags,
mnt_get_builtin_optmap(MNT_USERSPACE_MAP)))
goto eperm;
eperm:
// 不满足权限检测要求,返回错误 umount failed: Operation not permitted.
DBG(CXT, ul_debugobj(cxt, "umount is not allowed for you"));
return -EPERM;
}
参考资料
Privilage elevation in Processes
https://nick.readthedocs.io/en/latest/Security/elevated_privilages/
最后修改于 2020-12-04