Python中不需要root权限的PAM身份验证

5
我正在寻找一种方法,让我的Python程序通过pam处理身份验证。我正在使用http://code.google.com/p/web2py/source/browse/gluon/contrib/pam.py进行身份验证,只要我的Python程序作为root运行,这个方法就非常好用,但我认为这并不理想。
如何在不需要root权限的情况下利用pam进行用户名/密码验证?
5个回答

8

简短: 使用适当的Python PAM实现,正确设置PAM。

详细: 在合理的PAM设置中,您不需要root特权。最终,这是PAM提供的特权分离之一。

pam_unix有一种方法可以为您检查密码。似乎web2py的PAM实现(注意,它来自某些contrib子目录...)没有做正确的事情。也许您的PAM设置不正确,这需要更多信息才能确定;这也严重依赖于操作系统和版本/分发版。

有多个Python的PAM绑定可供使用(不幸的是标准库中没有),请使用这些绑定。对于配置,有大量的教程,请找到适合您系统的正确教程。

旧/错误的做法,不要这样做: 您不需要成为root用户,您只需要能够读取/etc/shadow。该文件通常具有组shadow,只有读取权限。因此,您只需要将运行PAM检查的用户添加到shadow中即可。

groupadd <user> shadow就可以解决问题了。


1
谢谢,这个方法可行。不知道为什么今天Debian不支持groupadd命令,只好使用sudo usermod -a -G shadow <user>命令。另外需要注意的一点是,这个修改只有在重新登录后才会生效。 - cardamom
@cardamom,+1,谢谢!非常有帮助! - PyTis
1
这在许多应用程序中是_强烈_不建议的,例如,如果您将其用于Apache进程,则不应该授予Apache读取/ etc / shadow的能力,因为如果攻击者获取该文件的内容,会存在危险。 - HunnyBear
1
@HunnyBear:我错了。重新阅读问题,特别是关于pampam_unix手册https://linux.die.net/man/8/pam_unix,我注意到在一个健全的应用程序中,“pam”将获取凭证并在不需要特殊权限或透露信息的情况下正确回答。看起来所提到的`web2py`实现只是做得不好。 - trapicki
是的,我觉得解决 Python 库的这个问题的方法可能有些不太正常,比如需要 sudo pam 的能力之类的东西。 - HunnyBear

4
我认为pam模块是您最好的选择,但您不必直接将其嵌入到程序中。您可以编写一个简单的服务,绑定到本地主机上的端口或监听UNIX域套接字,并为同一主机上的其他进程填充PAM请求。然后,让您的web2py应用程序连接它以进行用户/密码验证。
例如:
import asyncore
import pam
import socket

class Client(asyncore.dispatcher_with_send):

    def __init__(self, sock):
        asyncore.dispatcher_with_send.__init__(self, sock)
        self._buf = ''

    def handle_read(self):
        data = self._buf + self.recv(1024)
        if not data:
            self.close()
            return
        reqs, data = data.rsplit('\r\n', 1)
        self._buf = data
        for req in reqs.split('\r\n'):
            try:
                user, passwd = req.split()
            except:
                self.send('bad\r\n')
            else:
                if pam.authenticate(user, passwd):
                    self.send('ok\r\n')
                else:
                    self.send('fail\r\n')

    def handle_close(self):
        self.close()


class Service(asyncore.dispatcher_with_send):

    def __init__(self, addr):
        asyncore.dispatcher_with_send.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind(addr)
        self.listen(1)

    def handle_accept(self):
        conn, _ = self.accept()
        Client(conn)

def main():
    addr = ('localhost', 8317)
    Service(addr)
    try:
        asyncore.loop()
    except KeyboardInterrupt:
        pass

if __name__ == '__main__':
    main()

使用方法:

% telnet localhost 8317
bob abc123
ok
larry badpass
fail
incomplete
bad

谢谢,虽然我不喜欢这种特定的方法,但它让我开始思考。我现在已经找到了解决方案。请查看答案。 - jay_t

4

最终我使用了pexpect并尝试su -用户名。 它有点慢,但效果还不错。 下面的示例还不够完善,但你会有所了解。

干杯,

Jay

#!/usr/bin/python
import pexpect
def pam(username, password):
        '''Accepts username and password and tried to use PAM for authentication'''
        try:
                child = pexpect.spawn('/bin/su - %s'%(username))
                child.expect('Password:')
                child.sendline(password)
                result=child.expect(['su: Authentication failure',username])
                child.close()
        except Exception as err:
                child.close()
                print ("Error authenticating. Reason: "%(err))
                return False
        if result == 0:
                print ("Authentication failed for user %s."%(username))
                return False
        else:
                print ("Authentication succeeded for user %s."%(username))
                return True

if __name__ == '__main__':
        print pam(username='default',password='chandgeme')

@FoxWilson,你能给我们举个例子,说明你认为最好的清理方法是什么吗? - EminezArtus
1
在生成/bin/su进程的行中,"username"参数(可能是用户指定的)没有经过净化。想象一下,如果用户传递一个用户名"root; rm -rf /",那么应用程序用户可以访问的所有文件都将被删除。pexpect的spawn方法可以接受命令行参数:更好的解决方案可能是pexpect.spawn('/bin/su', ['-', username])。看起来pexpect不处理";"这样的shell元字符,因此这种攻击可能不起作用,但最好还是小心为妙。 - tew
1
@EminezArtus 我再想一下这个问题,你可以用它来做更有趣的事情,比如传递用户名 -c "/bin/rm -rf /home/user/" user - tew

3

2
如果您使用通常的系统(Unix风格)登录凭据,则不行。在某个时刻,PAM库必须读取仅由root可读取的shadow文件。但是,如果您使用认证方法不同的PAM配置文件,例如LDAP或数据库,则可以在不需要root的情况下工作。
这就是我开发自己的框架的原因,该框架在不同的URL路径空间下运行不同部分的用户凭据。只有登录部分可以以root身份运行,以使用PAM(系统)进行身份验证,其他路径子树处理程序则以不同的用户身份运行。
我正在使用PyPAM模块来实现这一点。

链路断开。 - Pacerier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接