1

我有一个用户test

当该用户使用chage命令登录时,我设置了密码更改。

chage -E 2012-01-25 -M 30 -d 0 -W 10 -I 5 test

所以当我尝试运行命令时ls

[root@localhost ~]# ssh test@localhost "ls"
WARNING: Your password has expired.
Password change required but no TTY available.
You have new mail in /var/spool/mail/root

然后我尝试连接ssh

[root@localhost ~]# ssh test@localhost
You are required to change your password immediately (root enforced)
Last login: Tue Dec 27 09:55:55 2011 from localhost
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for user test.
Changing password for test.
(current) UNIX password: 

而且我可以为用户设置密码。

如果我尝试将其与paramiko.

In [1]: import paramiko

In [2]: ssh_conn = paramiko.SSHClient()

In [3]: ssh_conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())

In [4]: ssh_conn.load_system_host_keys()

In [5]: ssh_conn.connect('n2001', username='root_acc23', password='test')

In [6]: a = ssh_conn.exec_command('ls')

In [7]: print a[2].read()
WARNING: Your password has expired.
Password change required but no TTY available.

然后我做了一些谷歌并找到了一些解决方案来设置新密码invoke_shell显示我写了一个函数

def chage_password_change(ssh_conn, password, curr_pass):
   '''
   If got error on login then set with interactive mode.
   '''
   interact = ssh_conn.invoke_shell()
   buff = ''
   while not buff.endswith('UNIX password: '):
       resp = interact.recv(9999)
       buff += resp
   interact.send(curr_pass + '\n')

   buff = ''
   while not buff.endswith('New password: '):
       resp = interact.recv(9999)
       buff += resp

   interact.send(password + '\n')

   buff = ''
   while not buff.endswith('Retype new password: '):
       resp = interact.recv(9999)
       buff += resp

   interact.send(password + '\n')


   interact.shutdown(2)
   if interact.exit_status_ready():
       print "EXIT :", interact.recv_exit_status()

   print "Last Password"
   print "LST :", interact.recv(-1)

这在某些情况下是有效的,例如当我们提供带有数字、alpa 和特殊字符组合的正确密码时。

但是当我们输入一些短密码或密码更改发生错误时

[root@localhost ~]# ssh test@localhost
You are required to change your password immediately (root enforced)
Last login: Tue Dec 27 10:41:15 2011 from localhost
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for user test.
Changing password for test.
(current) UNIX password: 
New password: 
Retype new password: 
BAD PASSWORD: it is too short

在这个命令中,我们得到错误BAD PASSWORD: it is too short所以我无法在我的函数中确定。当我这样做时出现此错误,interact.recv(-1)但这是我认为的标准输出。那么有什么方法可以确定这是错误。

我检查了 paramiko 文档,发现Channel该类有一些方法recv_stderr_readyrecv_stderr但该错误并未出现在该数据中。

感谢您提前提供帮助。

4

3 回答 3

3

简单的答案是让您的函数在调用 shell 之前检查密码的长度,如果您知道截止时间是什么。性能也更好。但如果你不知道截止,那将是行不通的。

从您的描述中我不清楚,但如果 BAD PASSWORD 消息从 interact.recv(-1) 返回,那么您知道它发生了,并且可以继续进行。似乎它应该从 std err 或 stdout 返回,因此请检查两者。如果您知道新密码被接受后会返回什么文本,那么您也可以检查它;无论你先得到哪个都会告诉你发生了什么,你的功能可以从那里继续。

于 2011-12-27T18:47:22.957 回答
2

如果您正在寻找过期的密码更改方法,这也可能有效。(蟒蛇3)

import time
from contextlib import closing
import paramiko

def wait_until_channel_endswith(channel, endswith, wait_in_seconds=15):
    """Continues execution if the specified string appears at the end of the channel

    Raises: TimeoutError if string cannot be found on the channel
    """

    timeout = time.time() + wait_in_seconds
    read_buffer = b''
    while not read_buffer.endswith(endswith):
        if channel.recv_ready():
           read_buffer += channel.recv(4096)
        elif time.time() > timeout:
            raise TimeoutError(f"Timeout while waiting for '{endswith}' on the channel")
        else:
            time.sleep(1)

def change_expired_password_over_ssh(host, username, current_password, new_password):
    """Changes expired password over SSH with paramiko"""
    with closing(paramiko.SSHClient()) as ssh_connection:
        ssh_connection.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh_connection.connect(hostname=host, username=username, password=current_password)
        ssh_channel = ssh_connection.invoke_shell()

        wait_until_channel_endswith(ssh_channel, b'UNIX password: ')
        ssh_channel.send(f'{current_password}\n')

        wait_until_channel_endswith(ssh_channel, b'New password: ')
        ssh_channel.send(f'{new_password}\n')

        wait_until_channel_endswith(ssh_channel, b'Retype new password: ')
        ssh_channel.send(f'{new_password}\n')

        wait_until_channel_endswith(ssh_channel, b'all authentication tokens updated successfully.\r\n')

用法:

change_expired_password_over_ssh('192.168.1.1', 'username', 'expired-password', 'new-password')
于 2019-05-22T12:45:58.410 回答
1

以下几行可能有问题,并可能导致一些错误:

while not buff.endswith('Retype new password: '):
      resp = interact.recv(9999)
      buff += resp // this will append the output from the shell 

代码修复:

这样使用会更好

while not buff.endswith('Retype new password: '):
     resp = interact.recv(9999)
     buff = resp

现在循环的每次迭代,都会从 shell 解析当前 \ 更新的输出文本。

问候, 埃尔达德

于 2014-01-02T11:19:56.330 回答