我们可以测试目录是否可由当前进程的uid写入:
$ if [ -w $directory ] ; then echo 'Eureka!' ; fi
但有人可以建议一种方法来测试目录是否可由某些其他 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
但当然这是一个想象的测试谓词。
澄清:我想要一个不依赖于su
的解决方案,因为我不能假设我的脚本用户具有su权限。
答案 0 :(得分:27)
这是一种漫长而迂回的检查方式。
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
答案 1 :(得分: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
说明:
只有一个测试(如果),无循环和无分支。
+ Nota:因为我使用stat -Lc
代替stat -c
,这也适用于符号链接!
条件是如果,
$directory
的统计信息并将其分配给dirVals
,$wantedUser
的成员列表分配给gMember
AND $gMember
的最后一个字符串构建的字符串将匹配 beginOfSting-Or-something-follow-a-space ,紧接着是目标组({{1}紧随其后的是 a-space-follow-something-Or-endOfString 。 )然后回声新年快乐!
由于小组的测试意味着第二个 fork (并且我喜欢尽可能减少此类调用),这是最后一项测试。
<强>旧强>:
简单地:
${dirVals[1]}
或:
su - mysql -c "test -w '$directory'" && echo yes
yes
Nota:警告首先用双引号括起来if su - mysql -s /bin/sh -c "test -w '$directory'" ; then
echo 'Eureka!'
fi
开发!
答案 2 :(得分:7)
您可以使用sudo
在脚本中执行测试。例如:
sudo -u mysql -H sh -c "if [ -w $directory ] ; then echo 'Eureka' ; fi"
为此,执行脚本的用户当然需要sudo
个权限。
如果您明确需要uid而不是用户名,您还可以使用:
sudo -u \#42 -H sh -c "if [ -w $directory ] ; then echo 'Eureka' ; fi"
在这种情况下,42
是mysql
用户的uid。如果需要,可以替换自己的价值。
更新(支持非sudo-priviledged用户)
要让bash脚本更改 - 没有sudu
的用户将需要能够suid
(“切换用户ID”)。正如this answer所指出的,这是一个安全限制,需要黑客才能解决。检查this blog以获取“如何”解决它的示例(我没有测试/尝试过,所以我无法确认它是否成功)。
如果可能的话,我的建议是在C中编写一个给suid权限的脚本(try chmod 4755 file-name
)。然后,您可以从C脚本调用setuid(#)
来设置当前用户的id,并从C应用程序继续执行代码,或者让它执行一个单独的bash脚本来运行您需要/想要的任何命令。这也是一个非常糟糕的方法,但就非sudo替代方案而言,它可能是最简单的(在我看来)。
答案 3 :(得分:3)
一个有趣的可能性(但它不再是bash)是用mysql拥有的suid标志创建一个C程序。
创建这个精彩的C源文件,并将其命名为caniwrite.c
(抱歉,我总是选择名字):
#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char* argv[]) {
int i;
for(i=1;i<argc;++i) {
if(eaccess(argv[i],W_OK)) {
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
编译:
gcc -Wall -ocaniwrite caniwrite.c
将它移动到你喜欢的任何文件夹中,/usr/local/bin/
是一个不错的选择,更改它的所有权并设置suid标志:(以root身份执行)
# mv -nv caniwrite /usr/local/bin
# chown mysql:mysql /usr/local/bin/caniwrite
# chmod +s /usr/local/bin/caniwrite
完成!
只需将其命名为:
if caniwrite folder1; then
echo "folder1 is writable"
else
echo "folder1 is not writable"
fi
事实上,您可以根据需要使用尽可能多的参数调用caniwrite
。如果 all 目录(或文件)是可写的,则返回码为true,否则返回码为false。
答案 4 :(得分:3)
因为我必须对@Chepner的答案进行一些更改才能使其正常工作,所以我在此处发布我的临时脚本以便于复制和播放。糊。它只是一个轻微的重构,我赞成了chepner的回答。如果使用这些修补程序更新了已接受的答案,我将删除我的。我已经在答案中留下了评论,指出了我遇到的问题。
我想废除Bashisms,这就是为什么我根本不使用数组。 ((
算术评估))
仍然是仅限Bash的功能,所以我毕竟不会使用Bash。
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
没有评论,这甚至相当紧凑。
答案 5 :(得分:2)
我编写了一个函数can_user_write_to_file
,如果用户传递给它,它将返回1
,或者是文件/目录的所有者,或者是对该文件/目录具有写访问权限的组的成员文件/目录。如果不是,则该方法返回0
。
## Method which returns 1 if the user can write to the file or
## directory.
##
## $1 :: user name
## $2 :: file
function can_user_write_to_file() {
if [[ $# -lt 2 || ! -r $2 ]]; then
echo 0
return
fi
local user_id=$(id -u ${1} 2>/dev/null)
local file_owner_id=$(stat -c "%u" $2)
if [[ ${user_id} == ${file_owner_id} ]]; then
echo 1
return
fi
local file_access=$(stat -c "%a" $2)
local file_group_access=${file_access:1:1}
local file_group_name=$(stat -c "%G" $2)
local user_group_list=$(groups $1 2>/dev/null)
if [ ${file_group_access} -ge 6 ]; then
for el in ${user_group_list-nop}; do
if [[ "${el}" == ${file_group_name} ]]; then
echo 1
return
fi
done
fi
echo 0
}
为了测试它,我写了一个小测试函数:
function test_can_user_write_to_file() {
echo "The file is: $(ls -l $2)"
echo "User is:" $(groups $1 2>/dev/null)
echo "User" $1 "can write to" $2 ":" $(can_user_write_to_file $1 $2)
echo ""
}
test_can_user_write_to_file root /etc/fstab
test_can_user_write_to_file invaliduser /etc/motd
test_can_user_write_to_file torstein /home/torstein/.xsession
test_can_user_write_to_file torstein /tmp/file-with-only-group-write-access
至少从这些测试中,该方法按预期工作,考虑文件所有权和组写访问: - )
答案 6 :(得分:2)
为什么不做一些简单的事情,比如在有问题的文件夹上搜索mkdir。它更可靠......
mkdir your_directory/
[[ $? -ne 0 ]] && echo "fatal" || echo "winner winner chicken dinner.."
或?
# -- run the following commands as the_User_ID
sudo su - the_User_ID << BASH
mkdir your_directory/
[[ $? -ne 0 ]] && echo "fatal" || echo "winner winner chicken dinner.."
BASH
答案 7 :(得分:2)
alias wbyu='_(){ local -i FND=0; if [[ $# -eq 2 ]]; then for each in $(groups "$1" | awk "{\$1=\"\";\$2=\"\"; print \$0}"); do (($(find "${2}" \( -perm /220 -o -group "$each" -a -perm /g+w \) 2>/dev/null | wc -l))) && FND=1; done; else echo "Usage: wbyu <user> <file|dir>"; fi; (($FND)) && echo "Eureka!"; }; _'
我把它放入一个别名,它需要两个参数,第一个是用户,第二个是要检查的目录。它查找任何人可写的权限,并循环指定用户的组以检查目录是否在用户组中并且是可写的 - 如果获得命中它设置找到的标志并打印Eureka!在末尾。
IOW:
FND=0
USER=user1
DIR=/tmp/test
for each in $(groups "$USER" | awk '{$1="";$2=""; print $0}'); do
(($(find "$DIR" \( -perm /220 -o -group "$each" -a -perm /g+w \)\
2>/dev/null | wc -l))) && FND=1
done
(($FND)) && echo 'Eureka!'