Como criar uma entrada PAM condicional

5

Estou testando cenários do PAM no RedHat 7.5

O módulo pam pam_succeed_if.so se parece com o nível mais básico de testes condicionais que o PAM tem a oferecer, e não está atendendo às minhas necessidades. Você só pode criar testes nos campos usuário, uid, gid, shell, home, ruser, rtml, tty e service.

Na minha situação, eu quero testar baseado no campo 'rhost', no entanto, após colocar o módulo em debug, vi que o campo rhost não estava definido.

Meu objetivo é apenas executar um módulo PAM em /etc/pam.d/sudo se o usuário estiver logado localmente na máquina. Se pudermos detectar que o usuário está logado através do SSH, então eu quero pular o módulo. Na verdade, eu tinha três ideias diferentes que achei que funcionariam, mas todas acabaram falhando.

Compartilharei algumas das soluções que acabaram falhando

Entrada condicional usando pam_exec.so

Eu queria adicionar a seguinte entrada pam na parte superior do módulo pam que desejo ignorar de forma condicional:

auth    [success=ok default=1]    pam_exec.so /etc/security/deny-ssh-user.sh

O conteúdo do /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
if [ -n "${SSH_CLIENT}" ] || [ -n "${SSH_TTY}" ] || [ -n "${SSH_CONNECTION}" ]; then
    SSH_SESSION=true
else
    case $(ps -o comm= -p $PPID) in
        sshd|*/sshd) SSH_SESSION=true;;
    esac
fi

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

Eu passei pelo código-fonte do pam_exec.so em link e, surpreendentemente, ele sempre retornará PAM_SUCCESS, independentemente do código de saída do script. E não consigo fazer o script fazer com que o módulo pam_exec retorne PAM_SERVICE_ERR, PAM_SYSTEM_ERR ou PAM_IGNORE.

Entrada condicional usando pam_access.so

Mais uma vez, adiciono a seguinte entrada pam no topo do módulo pam que pretendo saltar condicionalmente

auth    [success=ok perm_denied=1]    pam_access.so    accessfile=/etc/security/ssh-sudo-access.conf noaudit

O conteúdo do /etc/security/ssh-sudo-access.conf

+:ALL:localhost 127.0.0.1 
-:ALL:ALL

Uau, super limpo né? Ele retornará o sucesso se você estiver logado localmente e negar todo o resto. Bem não. Acontece que quando o módulo pam_access.so é colocado em debug, ele não tem conhecimento de hosts remotos, apenas o terminal que está sendo usado. Portanto, o pam_access não pode realmente bloquear o acesso por hosts remotos.

Foi um dia enfurecedor ler literalmente o código-fonte apenas para descobrir que magia negra eu tenho que lançar apenas para poder pular um módulo PAM.

    
por Luke Pafford 29.06.2018 / 01:05

2 respostas

2

Bem, na verdade sou um idiota, o módulo pam_exec.so está perfeitamente bem para criar condicionais do PAM.

Tim Smith estava correto ao avaliar que os dois testes no meu script /etc/security/deny-ssh-user.sh NUNCA estavam definindo a variável SSH_SESSION como true. Eu não levei isso em consideração porque o script funciona em um shell normal, mas o contexto do ambiente é removido quando executado por pam_exec.so .

Acabei de reescrever o script para usar o utilitário last exatamente como seu exemplo, no entanto, tive que alterar alguns deles porque os comutadores de last diferem do Arch Linux para o RedHat.

Aqui está o script revisado em /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

Conteúdo do /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
....
    
por 29.06.2018 / 19:23
4

Aqui está uma solução que funciona para mim. Meu /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

e /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; }'

Eu obtenho este comportamento:

$ 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$

A primeira linha adicionada ao padrão pam.d/sudo chamadas pam_exec e, se for bem-sucedida, ignora a próxima entrada. A segunda linha apenas nega o acesso incondicionalmente.

Em /tmp/test-pam , chamo last para obter o endereço IP associado ao TTY pam de onde foi chamado. ${PAM_TTY#/dev/} remove /dev/ da frente do valor, porque last não reconhece o caminho completo do dispositivo. O -i sinalizador faz last mostrar o endereço IP ou o espaço reservado 0.0.0.0 se não houver endereço IP; Por padrão, ele mostra uma string de informações que é muito mais difícil de verificar. É também por isso que usei last em vez de who ou w ; aqueles que não têm uma opção semelhante. A opção -p now não é estritamente necessária, pois veremos awk apenas verificando a primeira linha de saída, mas restringe last para mostrar apenas os usuários que estão logados atualmente.

O comando awk apenas verifica a primeira linha e, se o terceiro campo não for 0.0.0.0 , sairá com um erro. Como esse é o último comando em /tmp/test-pam , o código de saída do awk se torna o código de saída do script.

No meu sistema, nenhum dos testes que você estava testando no seu deny-ssh-user.sh funcionaria. Se você colocar env > /tmp/test-pam.log no topo do seu script, verá que o ambiente foi removido, portanto, nenhuma das suas variáveis SSH_FOO será configurada. E $ PPID poderia apontar para qualquer número de processos. Por exemplo, execute perl -e 'system("sudo cat /etc/passwd")' e veja que $ PPID se refere ao processo perl .

Este é o Arch Linux, kernel 4.16.11-1-ARCH , caso isso seja importante. Eu não acho que deveria, no entanto.

    
por 29.06.2018 / 07:18

Tags