Pam authentication in python without root privileges

I am looking for a way to let my python program handle authentication through pam. I use http://code.google.com/p/web2py/source/browse/gluon/contrib/pam.py for this, which works fine as long as my python program runs as root, which is not ideal for my opinion.

How can I use pam to verify the username and password without requiring root privileges?

+4
source share
5 answers

You do not need to be root, you only need to read /etc/shadow . Usually this file has a shadow group with read-only access. Therefore, you just need to add a user who runs the PAM check in the shadow group.

groupadd <user> shadow should do the trick.

+6
source

I think the pam module is your best bet, but you don’t need to directly embed it in your program. You can write a simple service that binds to a port on localhost or listens on a UNIX domain socket and populates PAM requests for other processes on the same host. Then connect the web2py application to it to verify the user / password.

For instance:

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

Using:

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

In the end, I ended up using pexpect and tried to execute su - username. It is a little slow, but it works very well. The following example is not polished, but you get this idea.

Greetings

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') 
+4
source

Not if you use regular login credentials (unix style). At some point, the PAM library should read a shadow file that is read-only. However, if you use a PAM profile that authenticates with an alternative method, such as LDAP or a database, then it can work without the need for root.

This is one of the reasons why I developed my own framework that uses different parts of the URL path space under different user credentials. Part of the login (only) can be performed as root for authentication using PAM (system), other path subtree handlers run as different users.

I am using the PyPAM module.

+3
source

Perhaps python-pam might work for you.

+1
source

Source: https://habr.com/ru/post/1343489/


All Articles