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

发布于 2024-10-21 15:43:57 字数 320 浏览 5 评论 0原文

我正在寻找一种方法让我的 Python 程序通过 PAM 处理身份验证。

我正在使用 http://code.google。 com/p/web2py/source/browse/gluon/contrib/pam.py ,只要我的 Python 程序以 root 身份运行,效果就很好,在我看来,这并不理想。

如何在不需要 root 权限的情况下使用 PAM 进行用户名/密码验证?

I'm looking for a way to let my Python program handle authentication through PAM.

I'm using http://code.google.com/p/web2py/source/browse/gluon/contrib/pam.py for this, which works out great as long as my Python program runs as root which is not ideal in my opinion.

How can I make use of PAM for username/password validation without requiring root privileges?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

假扮的天使 2024-10-28 15:43:57

注意:可能已经过时,请参阅 Donovan Baarda 的评论!

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

:在正常的 PAM 设置中,您不需要 root 权限。最后,这就是 PAM 提供的功能之一:特权分离。

pam_unix 有一种方法可以为您检查密码。似乎 web2py 的 PAM 实现(注意,它来自某个 contrib 子目录...)没有做正确的事情。也许您的 PAM 设置不正确,在没有进一步信息的情况下很难判断;这也很大程度上取决于操作系统和风格/发行版。

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

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


groupadd <用户>;阴影应该可以解决问题。

Note: may be outdated, see comments by Donovan Baarda!

short: use a proper Python PAM implementation, setup PAM properly.

long: In a sane PAM setup, you do not need root privileges. In the end this is one of the things PAM provides, privilege separation.

pam_unix has a way to check the password for you. Seems the PAM implementation of web2py (note, it's from some contrib subdirectory...) is not doing the right thing. Maybe your PAM setup is not correct, which is hard to tell without further information; this also depends heavily on operating system and flavour/distribution.

There are multiple PAM bindings for Python out there (unfortunately nothing in the standard library), use these instead. And for configuration, there are tons of tutorials, find the right one for your system.

old/wrong, don't do this: You do not need to be root, you only need to be able to read /etc/shadow. This file has usually group shadow with read only access. So you simply need to add the user that is running the PAM check in the shadow group.

groupadd <user> shadow should do the trick.

廻憶裏菂餘溫 2024-10-28 15:43:57

我认为 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

I think the pam module is your best choice, but you don't have to embed it into your program directly. You could write a simple service which binds to a port on localhost, or listens on a UNIX domain socket, and fills PAM requests for other processes on the same host. Then have your web2py application connect to it for user/password validation.

For example:

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()

Usage:

% telnet localhost 8317
bob abc123
ok
larry badpass
fail
incomplete
bad
梦屿孤独相伴 2024-10-28 15:43:57

最后我最终使用了 pexpect 并尝试 su - 用户名。
虽然有点慢,但是效果很好。
下面的示例尚未完善,但您会明白其中的想法。

干杯,

杰伊

#!/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')

At the end I ended up using pexpect and trying to su - username.
It's a bit slow, but it works pretty good.
The below example isn't polished but you'll get the idea.

Cheers,

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')
原谅过去的我 2024-10-28 15:43:57

也许 python-pam 可以为你工作。

Maybe python-pam can work for you.

心作怪 2024-10-28 15:43:57

如果您使用常用的系统(unix 风格)登录凭据,则不会。在某些时候,PAM 库必须读取只能由 root 读取的影子文件。但是,如果您使用通过替代方法(例如 LDAP 或数据库)进行身份验证的 PAM 配置文件,则它无需 root 即可工作。

这是我开发自己的框架的原因之一,该框架在不同的用户凭据下运行 URL 路径空间的不同部分。登录部分(仅)可以以 root 身份运行以通过 PAM(系统)进行身份验证,其他路径子树处理程序以不同用户身份运行。

我为此使用 PyPAM 模块。

Not if you use they usual system (unix style) login credentials. At some point the PAM library must read the shadow file which is only readable by root. However, if you use a PAM profile that authenticates with an alternate method, such as LDAP or a database, then it can work without needing root.

This is one reason I developed my own framework that runs different parts of the URL path space under different user credentials. The login part (only) can run as root to authenticate with PAM (system), other path subtree handlers run as different users.

I'm using the PyPAM module for this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文