você está aqui: Home  → Arquivo de Mensagens Programação Shell Linux: Inscrições Abertas

Expect

Colaboração: Rodolfo Broco Manin

Data de Publicação: 26 de Novembro de 2001

O Expect (http://expect.nist.gov/) é uma ferramenta para automatizacao de aplicacoes interativas. Ele implementa uma linguagem de scripting similar ao tcl, porem com comandos adicionais para interagir com os aplicativos "no lugar do usuario", i.e.: "ler" prompts e mensagens de certos programas, tomar decisoes baseadas nelas e "digitar" os comandos necessarios.

Encontrei esse utilitario quando estava desenvolvendo uma pagina para troca de senha via web, e precisei de um metodo para fazer a troca nao-interativa da senha do usuario. Passwd, chpass & cia nao funcionam com pipes (eles utilizam a funcao getpass(), que só le a senha da entrada padrao se /dev/tty nao estiver disponivel).

Estou enviando em attach o script que fiz (estah sendo chamado via popen(), de uma pagina experimental com php). Ele le da entrada padrao o login, a senha atual e a nova senha do usuario (nessa ordem), efetua um telnet para a maquina local e executa o passwd. O script troca a senha de contas expiradas tambem (quando o proprio login chama o passwd), e tem a vantagem de ser "suid-free" :). Se alguem quiser usar, é só dar uma revisada, para compatibiliza-lo com as mensagens do passwd, o prompt do shell do sistema, etc.

#!/usr/local/bin/expect -f
##############################################################################
#                   Script para troca nao-interativa de senhas               #
#                         Rodolfo Broco Manin - 14/09/2001                   #
#----------------------------------------------------------------------------#
# Codigos de erro:                                                           #
#  - Referentes ao login inicial via telnet:                                 #
#    110 - Login ou Senha incorreta                                          #
#    198 - Timeout                                                           #
#    199 - Finalizacao inesperada                                            #
#  - Referentes aa troca de senha                                            #
#    210 - Senha nova invalida                                               #
#    220 - Impossivel trocar a senha (erro generico)                         #
#    298 - Timeout                                                           #
#    299 - Finalizacao inesperada                                            #
##############################################################################

# --- Variaveis configuraveis ---
# Linha de comando para o 'telnet'
set telnet_cmd "/usr/bin/telnet"

# Linha de comando para o 'passwd'
set passwd_cmd "/usr/bin/passwd"

# Host a ser contactado pelo 'telnet' (em geral, o proprio 'localhost')
set login_host "localhost"
# -------------------------------


# --- Parametros da sessao ---
set timeout 10

# Parece que funciona melhor assim...
set stty_init -nottycopy

# Soh para garantir
set env(TERM) vt100

# Nao mostra ao usuario as respostas dos comandos
log_user 0

# Exibe mensagem de ajuda, caso o usuario informe algum parametro na linha de comandos
if [ expr $argc > 0 ] then {
   send_user "\n$argv0: Script para troca nao-interativa de senhas.\
             \n   Execute este script sem parametros, e infrome o login do usuario,\
             \n   sua senha atual e uma nova senha (nessa ordem, via entrada padrao).\n\n"
   exit
}

# Obtem os dados da conta do usuario da entrada padrao
gets stdin login
gets stdin old_password
gets stdin new_password

# Inicia a sessao com um cabecalho informativo, proprio para ser redirecionado
# para um arquivo de log
set timestr [clock format [clock seconds] -format "%d/%m/%Y - %T (%Z)"]
send_user "\n----------= Iniciando sessao para troca de senha  =----------\n"
send_user " * Usuario: $login   -----   $timestr\n\n"

send_user "Iniciando sessao telnet (host: $login_host)...\n"

spawn $telnet_cmd $login_host

# Um breve delay para aguardar pela conexao
sleep 2

# Efetua o login
expect {
   "ogin:" {
   send_user " * Enviando login...\n"
   send "$login\r"

   # Sessao finalizada por timeout (ex.: servidor NIS ou NFS fora do ar)
   } timeout {
      send_user " - Timeout durante a conexao.\n"
      exit 198

   # Sessao abortada inesperadamente (ex.: expect e telnet nao conseguiram se entender)
   } eof {
      send_user " - Finalizacao inesperada durante a conexao.\n"
      exit 199
   }
}


# Finalizacao do login / interacao com o 'passwd'
expect {
   # Espera prompt para senha de login
   "Password:" {
      send_user " * Enviando senha...\n"
      send "$old_password\r"
      exp_continue

   # Erros diversos que impedem a troca de senha (ex.: senha nova vazia)
   # (deve estar antes das linhas que testas os prompts do shell e do 'passwd)
   } "unchanged" {
      send_user " - Nao foi possivel efetuar a troca de senha.\n"
      exit 220

   # Informa a senha antiga, solicitada apos a execucao do 'passwd'
   # ou logo apos o login, caso a senha esteja expirada
   } "Old" {
      send_user "\nEfetuando troca de senha:\n * Informando a senha antiga...\n"
      send "$old_password\r"
      exp_continue

   # Trata respostas do sistema a senhas novas invalidas
   # (deve estar antes da linha que testa o prompt 'New password')
   } "Please" {
      send_user " - Senha nova invalida.\n"
      exit 210

   # Informa a senha nova
   } -r "New|Retype" {
      send_user " * Informando a senha nova / Confirmacao...\n"
      send "$new_password\r"
      exp_continue

   # Concluiu a troca de senha
   # (deve estar antes da linha que testa o prompt do shell)
   } "done" {
      send_user " + Troca de senha efetuada!\n"
      exit 0

   # Executa 'passwd' caso obtenha-se o prompt do shell
   } "$login@" {
      send_user " + Login concluido.\n"
      send "$passwd_cmd\r"
      exp_continue

   # Testa login invalido
   } "ogin incorrect" {
      send_user " - Login ou Senha atual incorreta.\n"
      exit 110

   # Timeout e eof em geral nao acontecem aqui, mas...
   } timeout {
      send_user " - Timeout durante a troca de senha.\n"
      exit 298

   } eof {
      send_user " - Finalizacao inesperada durante a troca de senha.\n"
      exit 299
   }
}


Veja a relação completa dos artigos de Rodolfo Broco Manin