Как создать условный ввод PAM

Для bash это небольшая хитрость (хотя и задокументированная): попытаться использовать typeset , чтобы удалите атрибут «массив»:

$ typeset +a BASH_VERSINFO
bash: typeset: BASH_VERSINFO: cannot destroy array variables in this way
echo $?
1

(Вы не можете сделать это в zsh , он позволяет преобразовать массив в скаляр, в bash это явно запрещено.)

Итак:

 typeset +A myvariable 2>/dev/null || echo is assoc-array
 typeset +a myvariable 2>/dev/null || echo is array

Или в функции, отмечая предостережения в конце:

function typeof() {
    local _myvar="$1"
    if ! typeset -p $_myvar 2>/dev/null ; then
        echo no-such
    elif ! typeset -g +A  $_myvar 2>/dev/null ; then
        echo is-assoc-array
    elif ! typeset -g +a  $_myvar 2>/dev/null; then
        echo is-array
    else
        echo scalar
    fi
}

Обратите внимание на использование typeset -g (bash-4.2 или новее), это требуется внутри функции, чтобы typeset (син. declare ) не работает как local и уничтожает значение, которое вы пытаетесь проверить. Это также не обрабатывает "переменные" типы функций, вы можете добавить еще один тест ветвления, используя typeset -f , если необходимо.


Другой (почти полный) вариант - использовать это:

    ${!name[*]}
          If name is an array variable, expands to  the  list
          of  array indices (keys) assigned in name.  If name
          is not an array, expands to 0 if name  is  set  and
          null  otherwise.   When @ is used and the expansion
          appears within double quotes, each key expands to a
          separate word.

Однако есть одна небольшая проблема: массив с единственным нижним индексом 0 соответствует двум из вышеперечисленных условий. Это то, на что также ссылается mikeserv, bash действительно не имеет резкого различия, и в некоторых из них (если вы проверите журнал изменений) можно винить ksh и совместимость с тем, как $ {name [*]} или $ {name [@]} работают с не-массивом.

Итак, частичное решение:

if [[ ${!BASH_VERSINFO[*]} == '' ]]; then
    echo no-such
elif [[ ${!BASH_VERSINFO[*]} == '0' ]]; then 
    echo not-array
elif [[ ${!BASH_VERSINFO[*]} != '0' ]]; 
    echo is-array    
fi

Я использовал в прошлом вариант этого:

while read _line; do
   if [[ $_line =~ ^"declare -a" ]]; then 
     ...
   fi 
done < <( declare -p )

это тоже требует подоболочки.

Еще один, возможно, полезный метод - это compgen :

compgen -A arrayvar

Это перечислит все индексированные массивы, однако ассоциативные массивы не обрабатываются специально (вплоть до bash-4.4) и отображаются как обычные переменные ( compgen -A variable )

7
01.07.2018, 16:31
2 ответа

Ну, оказывается, я на самом деле идиот, модуль pam_exec.soпрекрасно подходит для создания условий PAM.

Тим Смит был прав, оценив, что оба теста в моем скрипте /etc/security/deny-ssh-user.shНИКОГДА не устанавливали для переменной SSH_SESSIONзначение true. Я не принял это во внимание, потому что скрипт работает в обычной оболочке, но контекст среды удаляется при выполнении pam_exec.so.

В итоге я переписал сценарий, чтобы использовать утилиту last, как в его примере, однако мне пришлось изменить некоторые из них, потому что переключатели для lastотличаются от Arch Linux до RedHat.

Вот исправленный скрипт в /etc/security/deny -ssh -user.sh:

#!/bin/bash
# Returns 1 if the user is logged in through SSH
# Returns 0 if the user is not logged in through SSH
SSH_SESSION=false

function isSshSession {
    local terminal="${1}"
    if $(/usr/bin/last -i | 
        /usr/bin/grep "${terminal}" |
        /usr/bin/grep 'still logged in' |
        /usr/bin/awk '{print $3}' |
        /usr/bin/grep -q --invert-match '0\.0\.0\.0'); then
        echo true
    else
        echo false
    fi
}

function stripTerminal {
    local terminal="${1}"

    # PAM_TTY is in the form /dev/pts/X
    # Last utility displays TTY in the form pts/x
    # Returns the first five characters stripped from TTY
    echo "${terminal:5}"
}

lastTerminal=$( stripTerminal "${PAM_TTY}")
SSH_SESSION=$(isSshSession "${lastTerminal}")

if "${SSH_SESSION}"; then
    exit 1
else
    exit 0
fi

Содержимое /etc/pam.d/sudo

....
auth    [success=ok default=1]    pam_exec.so /etc/security/deny-ssh-user.sh
auth    sufficient    pam_module_to_skip.so
....
3
27.01.2020, 20:17

Вот решение, которое мне подходит. Мой/etc/pam.d/sudo:

#%PAM-1.0
auth            [success=1]     pam_exec.so    /tmp/test-pam
auth            required        pam_deny.so
auth            include         system-auth
account         include         system-auth
session         include         system-auth

И/tmp/test-pam:

#! /bin/bash
/bin/last -i -p now ${PAM_TTY#/dev/} | \
    /bin/awk 'NR==1 { if ($3 != "0.0.0.0") exit 9; exit 0; }'

У меня такое поведение:

$ sudo date
[sudo] password for jdoe:
Thu Jun 28 23:51:58 MDT 2018
$ ssh localhost
Last login: Thu Jun 28 23:40:23 2018 from ::1
valli$ sudo date
/tmp/test-pam failed: exit code 9
[sudo] password for jdoe:
sudo: PAM authentication error: System error
valli$

Первая строка, добавленная к строке pam.d/sudoпо умолчанию, вызывает pam_execи, в случае успеха, пропускает следующую запись. Вторая строка просто безоговорочно запрещает доступ.

В /tmp/test-pamя вызываю last, чтобы получить IP-адрес, связанный с TTY, с которого был вызван pam. ${PAM_TTY#/dev/}удаляет /dev/из начала значения, потому что lastне распознает полный путь к устройству. Флаг -iзаставляет lastпоказывать либо IP-адрес, либо заполнитель 0.0.0.0, если IP-адрес отсутствует; по умолчанию он показывает информационную строку, которую гораздо сложнее проверить. Вот почему я использовал lastвместо whoили w; у них нет аналогичного варианта. Опция -p nowне является строго необходимой, так как мы увидим, что awkпроверяет только первую строку вывода, но ограничивает lastотображением только тех пользователей, которые в данный момент вошли в систему.

Команда awkпросто проверяет первую строку,и если третье поле не 0.0.0.0, оно завершается с ошибкой. Поскольку это последняя команда в /tmp/test-pam, код выхода awk становится кодом выхода для скрипта.

В моей системе ни один из тестов, которые вы пробовали в deny-ssh-user.sh, не работал. Если вы поместите env > /tmp/test-pam.logв начало вашего сценария, вы увидите, что среда была удалена, поэтому ни одна из ваших переменных SSH _FOO не будет установлена. А $PPID может указывать на любое количество процессов. Например, запустите perl -e 'system("sudo cat /etc/passwd")'и убедитесь, что $PPID относится к процессу perl.

Это Arch Linux, ядро ​​4.16.11-1-ARCH, если это имеет значение. Я не думаю, что это должно, хотя.

5
27.01.2020, 20:17

Теги

Похожие вопросы