Escrevendo um módulo pam_python: “KeyError: getspnam (): nome não encontrado”

2

Estou usando um módulo pam_python para registrar nomes de usuário e senhas usados em tentativas de SSH.

em /etc/pam.d/sshd , adicionei esta linha:

auth       requisite    pam_python.so /lib64/security/pwreveal.py

Isso é /lib64/security/pwreveal.py :

import crypt, spwd, syslog

def auth_log(msg):
        """Send errors to default auth log"""
        syslog.openlog(facility=syslog.LOG_AUTH)
        syslog.syslog("SSH Attack Logged: " + msg)
        syslog.closelog()

def check_pw(user, password):
        auth_log("User: " + user + " Password: " + password)
        """Check the password matches local unix password on file"""
        # try:
        hashed_pw = spwd.getspnam(user)[1]
        # except KeyError,e:
        #  return False
        return crypt.crypt(password, hashed_pw) == hashed_pw

def pam_sm_authenticate(pamh, flags, argv):
        try:
                user = pamh.get_user()
        except pamh.exception, e:
                return e.pam_result

        if not user:
                return pamh.PAM_USER_UNKNOWN

        try:
                resp = pamh.conversation(pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, 'Password:'))
        except pamh.exception, e:
                return e.pam_result

        if not check_pw(user, resp.resp):
                auth_log("Remote Host: %s (%s:%s)" % (pamh.rhost, user, resp.resp))
                return pamh.PAM_AUTH_ERR

        return pamh.PAM_SUCCESS

def pam_sm_setcred(pamh, flags, argv):
        return pamh.PAM_SUCCESS

def pam_sm_acct_mgmt(pamh, flags, argv):
        return pamh.PAM_SUCCESS

def pam_sm_open_session(pamh, flags, argv):
        return pamh.PAM_SUCCESS

def pam_sm_close_session(pamh, flags, argv):
        return pamh.PAM_SUCCESS

def pam_sm_chauthtok(pamh, flags, argv):
        return pamh.PAM_SUCCESS

Isso funciona até certo ponto, vejo a seguinte saída em /var/log/messages após uma tentativa falha de SSH (neste caso, eu de outro servidor dev local):

Mar  3 14:35:59 localhost sshd: SSH Attack Logged: Remote Host: 192.168.1.7 (root:root123)

Meu problema é que, independentemente de a combinação de nome de usuário / senha estar correta, o script sempre gera o mesmo erro em / var / log / secure e não autentica (assim, o SSH é efetivamente quebrado enquanto estou usando este script python ):

Mar  3 14:50:41 localhost /lib64/security/pwreveal.py[13328]: Traceback (most recent call last):
Mar  3 14:50:41 localhost /lib64/security/pwreveal.py[13328]:  File "/lib64/security/pwreveal.py", line 32, in pam_sm_authenticate
Mar  3 14:50:41 localhost /lib64/security/pwreveal.py[13328]:    if not check_pw(user, resp.resp):
Mar  3 14:50:41 localhost /lib64/security/pwreveal.py[13328]:  File "/lib64/security/pwreveal.py", line 13, in check_pw
Mar  3 14:50:41 localhost /lib64/security/pwreveal.py[13328]:    hashed_pw = spwd.getspnam(user)[1]
Mar  3 14:50:41 localhost /lib64/security/pwreveal.py[13328]: KeyError: getspnam(): name not found

Eu sei que 'spwd' é o banco de dados de senhas shadow e encontrei algumas informações on-line que sugeriam que, nesse caso, "nome não encontrado" foi descrito com mais precisão como "permissão negada". Então, como um teste, certifiquei-me de que o usuário do sshd tivesse acesso de leitura ao / etc / shadow - mas isso não ajudou.

Não tenho certeza se estou pesquisando as linhas certas ou não. Alguma ajuda?

Nota: Eu percebo que o log de senhas SSH não é uma coisa desejável para ter em execução em um servidor. Eu estou fazendo isso em uma caixa de desenvolvimento pessoal que só eu tenho acesso. Este é um projeto 'apenas para diversão'.

Editar - como teste, criei um script python autônomo que simplesmente faz isso:

import spwd

test = spwd.getspnam("myusername")[1]

print test

A execução deste script como root extraiu a senha com hash para a senha 'myusername'. Se intencionalmente teclar errado esse nome de usuário, estou tentando efetivamente procurar um nome de usuário que não exista no arquivo de sombra, recebo este erro:

KeyError: 'getspnam(): name not found'

O mesmo erro exato.

Então, a partir disso, posso supor que quando spwd.getspnam() é executado dentro de pwreveal.py via pam, não é possível localizar o usuário. Embora quando é executado de forma independente em um script separado, pode.

Por que isso seria?

    
por Exbi 03.03.2017 / 16:25

1 resposta

2

Não tenho certeza se recebi a sua pergunta corretamente, mas você está tentando dizer que seu script é rastreado sempre que invocado a partir do script do PAM. Você pode simular o mesmo comportamento ao escrever um usuário inválido (usuário não existente).

Então vamos começar de forma longa. A exceção KeyError diz a você que o usuário não foi encontrado no banco de dados por algum motivo. Em seu segundo exemplo, é óbvio que o usuário não está lá e é um comportamento esperado - é assim que funcionam as exceções no python, não é?

Seu problema original é provavelmente causado pela política do SELinux no CentOS. Processos normais não são capazes de ler /etc/shadow . Ao iniciar o script python sob a pilha de pam, pausá-lo e dar uma olhada nos contextos do processo (usando ps auxfZ ), você verá o contexto real que seu script está usando. Você também provavelmente verá algumas mensagens AVC no /var/log/audit/audit.log (ou ausearch -m AVC ) dizendo qual processo foi proibido de acessar o arquivo shadow .

Então, como sair? A possibilidade do punho é mudar o SELinux temporariamente para permissive para ter certeza que é a causa ( setenfoce 0 ). Então, deve começar o trabalho. Mas você não deve ficar satisfeito com isso e se você quiser fazer isso corretamente, você deve tentar ajustar / escrever uma política para o seu script, mas isso não se encaixará no formato de resposta no Serverfault.

    
por 04.03.2017 / 21:02