Bash测试目录是否可由给定的UID写入?

Bil*_*win 45 directory bash file-permissions

我们可以测试目录是否可由当前进程的uid写入:

if [ -w $directory ] ; then echo 'Eureka!' ; fi
Run Code Online (Sandbox Code Playgroud)

但是,有人可以建议一种方法来测试目录是否可由其他 uid 写入?

我的方案是我正在管理MySQL Server实例,我想暂时更改慢查询日志文件的位置.我可以通过执行MySQL命令SET GLOBAL slow_query_log_file='$new_log_filename',然后禁用和启用查询日志记录来mysqld启动使用该文件.

但我希望我的脚本检查mysqld进程的uid 是否有权创建新的日志文件.所以我想做一些像(伪代码)的东西:

$ if [ -w-as-mysql-uid `basename $new_log_filename` ] ; then echo 'Eureka!' ; fi
Run Code Online (Sandbox Code Playgroud)

但当然这是一个想象的测试谓词.

澄清: 我想要一个不依赖的解决方案,su因为我无法假设我的脚本用户具有su特权.

che*_*ner 28

这是一种漫长而迂回的检查方式.

USER=johndoe
DIR=/path/to/somewhere

# Use -L to get information about the target of a symlink,
# not the link itself, as pointed out in the comments
INFO=( $(stat -L -c "%a %G %U" "$DIR") )
PERM=${INFO[0]}
GROUP=${INFO[1]}
OWNER=${INFO[2]}

ACCESS=no
if (( ($PERM & 0002) != 0 )); then
    # Everyone has write access
    ACCESS=yes
elif (( ($PERM & 0020) != 0 )); then
    # Some group has write access.
    # Is user in that group?
    gs=( $(groups $USER) )
    for g in "${gs[@]}"; do
        if [[ $GROUP == $g ]]; then
            ACCESS=yes
            break
        fi
    done
elif (( ($PERM & 0200) != 0 )); then
    # The owner has write access.
    # Does the user own the file?
    [[ $USER == $OWNER ]] && ACCESS=yes
fi
Run Code Online (Sandbox Code Playgroud)

  • 此外,我碰到了[``意外的令牌`&':条件二元运算符预期``](http://stackoverflow.com/questions/14318451/how-to-use-bitwise-operators-in-if-statements) (5认同)
  • 关于符号链接的警告,因为它们可能是`lrwxrwxrwx`而目标不是. (3认同)
  • 我发现我必须改变`stat`选项的顺序,`stat -Lc"fmt"`pro`statual -cL`. (3认同)

F. *_*uri 9

那可以做测试:

if read -a dirVals < <(stat -Lc "%U %G %A" $directory) && (
    ( [ "$dirVals" == "$wantedUser" ] && [ "${dirVals[2]:2:1}" == "w" ] ) ||
    ( [ "${dirVals[2]:8:1}" == "w" ] ) ||
    ( [ "${dirVals[2]:5:1}" == "w" ] && (
        gMember=($(groups $wantedUser)) &&
        [[ "${gMember[*]:2}" =~ ^(.* |)${dirVals[1]}( .*|)$ ]]
    ) ) )
  then
    echo 'Happy new year!!!'
  fi
Run Code Online (Sandbox Code Playgroud)

说明:

只有一个测试(if),没有循环,没有fork.

+ Nota:因为我使用stat -Lc 而不是stat -c,这也适用于符号链接!

所以条件是, 如果,

  • 我可以成功读取统计数据$directory并将其分配给dirVals,
  • 而且(
    • (所有者匹配标记UserWriteable存在)
    • 其他可写的标志
    • (Flag GroupWriteabe存在 AND
      • 我可以成功地将成员列表$wantedUser分配给gMember AND
      • 通过合并字段2到最后的字符串构建的字符串$gMember将匹配beginOfSting-Or-something-follow-a-space,紧接着是target的group(${dirVals[1]}),紧接着是a-space-follow-something-Or-endOfString.)

然后呼应新年快乐!

由于小组的测试意味着第二个分支(我希望尽可能减少这样的调用),这是最后一个要做的测试.

:

只是:

su - mysql -c "test -w '$directory'" && echo yes
yes
Run Code Online (Sandbox Code Playgroud)

要么:

if su - mysql -s /bin/sh -c "test -w '$directory'" ; then 
    echo 'Eureka!'
  fi
Run Code Online (Sandbox Code Playgroud)

Nota:警告首先用双引号括起来进行$directory开发!


new*_*rey 7

您可以使用sudo在脚本中执行测试.例如:

sudo -u mysql -H sh -c "if [ -w $directory ] ; then echo 'Eureka' ; fi"
Run Code Online (Sandbox Code Playgroud)

为此,执行脚本的用户sudo当然需要特权.

如果您明确需要uid而不是用户名,您还可以使用:

sudo -u \#42 -H sh -c "if [ -w $directory ] ; then echo 'Eureka' ; fi"
Run Code Online (Sandbox Code Playgroud)

在这种情况下,42mysql用户的uid .如果需要,替换您自己的价值.

更新(以支持非sudo特权用户)
要获得一个bash脚本来更改用户而不sudu需要能力suid("切换用户ID").正如这个答案所指出的,是一个安全限制,需要黑客来解决.查看这个博客,了解"如何"解决它的一个例子(我没有测试/尝试过,所以我无法确认它是否成功).

如果可能的话,我的建议是在C中编写一个脚本,该脚本具有suid(try chmod 4755 file-name)的权限.然后,您可以setuid(#)从C脚本调用以设置当前用户的id,并从C应用程序继续执行代码,或让它执行单独的bash脚本,该脚本运行您需要/想要的任何命令.这也是一个非常糟糕的方法,但就非sudo替代方案而言,它可能是最简单的(在我看来).


tri*_*eee 5

因为我必须对@chepner的答案进行一些更改才能使其正常工作,所以我在这里发布了我的临时脚本,以便于复制和粘贴。这只是一个较小的重构,我赞成chepner的回答。如果接受的答案已通过这些修复程序更新,我将删除我的。我已经对该答案发表了评论,指出了我遇到的麻烦。

我想消除Bashisms,所以这就是为什么我根本不使用数组的原因。在((算术评估))仍然是一个猛砸特有的功能,所以我毕竟停留在猛砸。

for f; do
    set -- $(stat -Lc "0%a %G %U" "$f")
    (("$1" & 0002)) && continue
    if (("$1" & 0020)); then
        case " "$(groups "$USER")" " in *" "$2" "*) continue ;; esac
    elif (("$1" & 0200)); then
        [ "$3" = "$USER" ] && continue
    fi
    echo "$0: Wrong permissions" "$@" "$f" >&2
done
Run Code Online (Sandbox Code Playgroud)

没有评论,这甚至相当紧凑。