用Python脚本更改Linux用户名或密码

1 投票
1 回答
4620 浏览
提问于 2025-04-29 23:55

我正在写一个Python脚本,用来更改Linux账户的用户名和密码。这是一个更大内部网页系统的一部分,系统会排队处理来自apache2的密码更改请求(因为apache2不能以root身份运行),然后自己更改密码。这个Python脚本显然需要以root身份运行才能更改密码。

更改密码的功能其实很简单:

def chpasswd(user, passwd):
    if os.getuid() != 0:
        syslog.syslog("Error: chpasswd.py must be run as root")
        return

    proc = Popen(
        ['/usr/sbin/chpasswd'], 
        stdin = subprocess.PIPE, 
        stdout = subprocess.PIPE, 
        stderr = subprocess.PIPE
    )
    print "Changing: " + user + ':' + passwd
    out, err = proc.communicate(user + ':' + passwd)
    proc.wait()

    print out

    if proc.returncode != 0:
        print "Error: Return code", proc.returncode, ", stderr: ", out, err
        if out:
            syslog.syslog("stdout: " + out)
        if err:
            syslog.syslog("stderr: " + err)

这些print语句只是用来临时调试的。这个功能运行得很好,没有报错——outerr都没有任何信息;但不知为何,实际的UNIX密码就是没有被更改

调用这个功能的脚本在一个本地绑定的TCP套接字上监听。当它收到一个更改密码的请求(格式为user:password——之后会加密,但现在是明文)时,它会把请求添加到队列中,然后调用chpasswd函数。

所以,典型的用法是:

# telnet localhost 7001
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
jsmith:mynewpassword

当服务器在bash窗口中运行(而不是作为守护进程)时,它会打印出:

# python chpasswd.py
Starting Password Server
New connection from: 127.0.0.1
Changing: jsmith:mynewpassword

最后一条语句,你可以看到的是我在chpasswd函数中的print语句。

但是在执行完这些操作后,当我尝试用新密码登录时,却出现了:

$ su jsmith
Password: 
su: Authentication failure

我这里是不是做错了什么明显的事情?我怀疑是Popen的连接没有真正关闭,或者是user:password这一行文本没有被传输。所以我尝试做了类似这样的事情:

out, err = proc.communicate(user + ':' + passwd + '\x04')

注意最后的\x04字符,表示传输结束。加上这个后,还是没有解决问题——密码依然没有改变。

我是在Debian Wheezy上运行这个,看看这是否有影响。

更新:

进一步调查后,我发现我的chpasswd函数实际上在更改密码——如果我在连接到我的密码服务器之前和之后查看/etc/shadow文件,我会看到不同的哈希值。

只是当我尝试用明文密码进行身份验证时,它却不工作。因此,我的怀疑是,Popen的通信可能在某种程度上添加了额外的字符,或者以某种方式丢失了字符。当然,由于/etc/shadow是一个哈希,我无法确切弄清楚这里发生了什么。

暂无标签

1 个回答

1

这个问题的关键在于,当你在使用telnet输入文本后按下回车键时,它会自动在文本后面加上 "\r\n"。而你的服务器没有去掉这些多余的空白,所以在更改密码时,这些空白就被保留了。

其实,你可以通过在行尾加上结束传输字符(EOT)来让telnet不发送回车和换行符。你可以通过按下Ctrl-D来实现这一点。

例如:

$ telnet localhost 7001
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
jsmith:mynewpassword^DChanging: jsmith:mynewpassword

另外,你也可以将这一行通过管道传输给telnet。

echo -n jsmith:mynewpassword | telnet localhost 7001

当然,你这样做最好只是为了测试,否则新密码会被记录在你的命令历史中。(-n参数可以让echo不打印换行符)

或者,你也可以完全不使用telnet,而是选择使用netcat

echo -n jsmith:mynewpassword | netcat localhost 7001

撰写回答