Подтвердить что ты не робот

Проверить, доступен ли каталог для записи по заданному UID?

Мы можем проверить, доступен ли для записи каталог по uid текущего процесса:

if [ -w $directory ] ; then echo 'Eureka!' ; fi

Но может ли кто-нибудь предложить способ проверить, доступен ли для записи каталог другим UID?

Мой сценарий заключается в том, что я администрирую экземпляр MySQL Server и хочу временно изменить местоположение файла журнала медленных запросов. Я могу сделать это, выполнив команду MySQL SET GLOBAL slow_query_log_file='$new_log_filename' а затем отключить и включить ведение журнала запросов, чтобы mysqld начал использовать этот файл.

Но я бы хотел, чтобы мой сценарий проверял, что uid процесса mysqld имеет разрешения на создание этого нового файла журнала. Поэтому я хотел бы сделать что-то вроде (псевдокод):

$ if [ -w-as-mysql-uid 'basename $new_log_filename' ] ; then echo 'Eureka!' ; fi

Но конечно это мнимый тестовый предикат.

Пояснение: я хотел бы получить решение, которое не зависит от su потому что я не могу предположить, что пользователь моего сценария имеет привилегию su.

4b9b3361

Ответ 1

Здесь длинный, окольный способ проверки.

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

Ответ 2

Это может сделать тест:

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

Пояснения:

Существует только один тест (если), нет цикла и нет fork.

+ Нота: как я использовал stat -Lc вместо stat -c, это тоже будет работать для символических ссылок!

Таким образом, условие , если,

  • Я мог бы успешно прочитать статистику $directory и назначить их dirVals,
  • И (
    • (совпадение владельца и присутствует флажок UserWriteable)
    • или флаг. Имеется другая возможность записи.
    • или (Flag GroupWriteabe присутствует И
      • Я мог бы успешно определить список участников $wantedUser до gMember И
      • Строка, созданная путем слияния полей 2 с последним из $gMember, будет соответствовать beginOfSting-Or-something-follow-by-a-space, за которым сразу следует целевая группа (${dirVals[1]}), за которой следует a-space- прослежены по-то-или-endOfString. )

, затем эхо С Новым годом!

Поскольку групповой тест подразумевает вторую fork (И я люблю как можно меньше сократить такие вызовы), это последний тест, который нужно выполнить.

Старый

Просто:

su - mysql -c "test -w '$directory'" && echo yes
yes

или

if su - mysql -s /bin/sh -c "test -w '$directory'" ; then 
    echo 'Eureka!'
  fi

Nota: Предупредите сначала заключить двойные кавычки для создания $directory!

Ответ 3

Вы можете использовать sudo для выполнения теста в script. Например:

sudo -u mysql -H sh -c "if [ -w $directory ] ; then echo 'Eureka' ; fi"

Для этого пользователю, выполняющему script, необходимы привилегии sudo.

Если вы явно нуждаетесь в uid вместо имени пользователя, вы также можете использовать:

sudo -u \#42 -H sh -c "if [ -w $directory ] ; then echo 'Eureka' ; fi"

В этом случае 42 является uid пользователя mysql. При необходимости замените свое значение.

ОБНОВЛЕНИЕ (для поддержки пользователей, не относящихся к Sudo-priviledged)

Чтобы получить bash script для пользователей с изменением без sudu, требовалась возможность suid ( "переключить идентификатор пользователя" ). Это, как указано этим ответом, является ограничением безопасности, которое требует взлома для работы. Проверьте этот блог для примера "как" его обойти (я не тестировал/не пробовал, поэтому не могу подтвердить это успех).

Моя рекомендация, если возможно, заключалась бы в написании script в C, которому дано разрешение suid (попробуйте chmod 4755 file-name). Затем вы можете вызвать setuid(#) из C script, чтобы установить текущий идентификатор пользователя и продолжить выполнение кода из приложения C, или выполнить отдельный bash script, который выполняет любые команды, которые вам нужны/хотеть. Это также довольно хакерский метод, но в отношении альтернатив non-sudo он, вероятно, один из самых простых (на мой взгляд).

Ответ 4

Поскольку мне пришлось внести некоторые изменения в ответ @chepner, чтобы заставить его работать, я размещаю свой ad-hoc script здесь для удобства копирования и вставки. Это незначительный рефакторинг, и у меня есть верный ответ. Я удалю мой, если принятый ответ будет обновлен с этими исправлениями. Я уже оставлял комментарии к этому вопросу, указывая на то, с чем у меня были проблемы.

Я хотел покончить с Bashisms, так что я вообще не использую массивы. Арифметическая оценка (( )) по-прежнему является функцией Bash -only, поэтому я все-таки застрял на 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" "[email protected]" "$f" >&2
done

Без комментариев это даже довольно компактно.

Ответ 5

Одна забавная возможность (но это не bash) состоит в том, чтобы сделать C-программу с флагом suid, принадлежащим mysql.

Шаг 1.

Создайте этот замечательный исходный файл 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;
}

Шаг 2.

Compile:

gcc -Wall -ocaniwrite caniwrite.c

Шаг 3.

Переместите его в любую нужную папку, /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 с таким количеством аргументов, сколько хотите. Если все каталоги (или файлы) доступны для записи, тогда код возврата является истинным, иначе код возврата будет ложным.

Ответ 6

Я написал функцию 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

По крайней мере, из этих тестов метод работает по назначению с учетом права собственности на файл и доступа к группе: -)

Ответ 7

Почему бы просто не сделать что-то простое, как TRY 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

Ответ 8

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!'