From 3d0a53cb78ef923132515d38019450f763f1f8ef Mon Sep 17 00:00:00 2001 From: Francesco kurojishi Berni Date: Fri, 19 Aug 2016 14:20:12 +0200 Subject: [PATCH 1/6] Addedd syslog logging fixed authors --- dpam/backends.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dpam/backends.py b/dpam/backends.py index 4ad04fe..546186c 100644 --- a/dpam/backends.py +++ b/dpam/backends.py @@ -3,9 +3,11 @@ from django.conf import settings from django.contrib.auth.models import User from django.contrib.auth.backends import ModelBackend +import syslog class PAMBackend(ModelBackend): def authenticate(self, username=None, password=None): + syslog.syslog('django pam realyy') service = getattr(settings, 'PAM_SERVICE', 'login') if not pam.authenticate(username, password, service=service): return None From ba363d8e376fcf35105291a8f7b61a271e4fc247 Mon Sep 17 00:00:00 2001 From: Francesco kurojishi Berni Date: Fri, 19 Aug 2016 12:27:25 +0200 Subject: [PATCH 2/6] cleaing module --- dpam/backends.py | 17 +++++++++++------ dpam/pam.py | 37 ++++++++++++++++++++----------------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/dpam/backends.py b/dpam/backends.py index 546186c..d4de335 100644 --- a/dpam/backends.py +++ b/dpam/backends.py @@ -1,30 +1,35 @@ -import pam +""" This file manage the authentication and user creation""" +import syslog from django.conf import settings from django.contrib.auth.models import User from django.contrib.auth.backends import ModelBackend -import syslog + +import dpam.pam as pam + class PAMBackend(ModelBackend): + """PAM Auth Backend""" + def authenticate(self, username=None, password=None): - syslog.syslog('django pam realyy') + syslog.syslog('django pam realyy') service = getattr(settings, 'PAM_SERVICE', 'login') if not pam.authenticate(username, password, service=service): return None try: user = User.objects.get(username=username) - except: + except User.DoesNotExist: if not getattr(settings, "PAM_CREATE_USER", True): return None user = User(username=username, password='not stored here') user.set_unusable_password() if getattr(settings, 'PAM_IS_SUPERUSER', False): - user.is_superuser = True + user.is_superuser = True if getattr(settings, 'PAM_IS_STAFF', user.is_superuser): - user.is_staff = True + user.is_staff = True user.save() return user diff --git a/dpam/pam.py b/dpam/pam.py index a799f07..d3284e7 100644 --- a/dpam/pam.py +++ b/dpam/pam.py @@ -24,7 +24,7 @@ STRDUP = LIBC.strdup STRDUP.argstypes = [c_char_p] -STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!! +STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!! # Various constants PAM_PROMPT_ECHO_OFF = 1 @@ -32,51 +32,55 @@ PAM_ERROR_MSG = 3 PAM_TEXT_INFO = 4 + class PamHandle(Structure): """wrapper class for pam_handle_t""" _fields_ = [ - ("handle", c_void_p) - ] + ("handle", c_void_p) + ] def __init__(self): Structure.__init__(self) self.handle = 0 + class PamMessage(Structure): """wrapper class for pam_message structure""" _fields_ = [ - ("msg_style", c_int), - ("msg", c_char_p), - ] + ("msg_style", c_int), + ("msg", c_char_p), + ] def __repr__(self): return "" % (self.msg_style, self.msg) + class PamResponse(Structure): """wrapper class for pam_response structure""" _fields_ = [ - ("resp", c_char_p), - ("resp_retcode", c_int), - ] + ("resp", c_char_p), + ("resp_retcode", c_int), + ] def __repr__(self): return "" % (self.resp_retcode, self.resp) CONV_FUNC = CFUNCTYPE(c_int, - c_int, POINTER(POINTER(PamMessage)), - POINTER(POINTER(PamResponse)), c_void_p) + c_int, POINTER(POINTER(PamMessage)), + POINTER(POINTER(PamResponse)), c_void_p) + class PamConv(Structure): """wrapper class for pam_conv structure""" _fields_ = [ - ("conv", CONV_FUNC), - ("appdata_ptr", c_void_p) - ] + ("conv", CONV_FUNC), + ("appdata_ptr", c_void_p) + ] PAM_START = LIBPAM.pam_start PAM_START.restype = c_int PAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv), - POINTER(PamHandle)] + POINTER(PamHandle)] PAM_AUTHENTICATE = LIBPAM.pam_authenticate PAM_AUTHENTICATE.restype = c_int @@ -91,8 +95,7 @@ class PamConv(Structure): PAM_END.argtypes = [PamHandle, c_int] - -def authenticate(username, password, service='login', enable_acct_mgrt=False): +def authenticate(username, password, service='login', enable_acct_mgrt=False): """Returns True if the given username and password authenticate for the given service. Returns False otherwise From 522d788c66b69b92edd1eea63cdbf7acc711f001 Mon Sep 17 00:00:00 2001 From: Francesco kurojishi Berni Date: Fri, 19 Aug 2016 11:46:11 +0200 Subject: [PATCH 3/6] Added users group managment --- dpam/backends.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dpam/backends.py b/dpam/backends.py index d4de335..2411f84 100644 --- a/dpam/backends.py +++ b/dpam/backends.py @@ -1,9 +1,10 @@ -""" This file manage the authentication and user creation""" +""" Django PAM Authentication Backend""" import syslog from django.conf import settings from django.contrib.auth.models import User from django.contrib.auth.backends import ModelBackend +from django.contrib.auth.models import Group import dpam.pam as pam @@ -31,6 +32,10 @@ def authenticate(self, username=None, password=None): if getattr(settings, 'PAM_IS_STAFF', user.is_superuser): user.is_staff = True + if getattr(settings, 'PAM_USERS_GROUP', None): + group = Group.objects.get(name=settings.PAM_USERS_GROUP) + group.user_set.add(user) + user.save() return user From ffd116f091a0de23fd525337b0b8d7328a9acf1d Mon Sep 17 00:00:00 2001 From: Francesco kurojishi Berni Date: Fri, 19 Aug 2016 14:51:03 +0200 Subject: [PATCH 4/6] Saving before group managment --- dpam/backends.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dpam/backends.py b/dpam/backends.py index 2411f84..3e612e4 100644 --- a/dpam/backends.py +++ b/dpam/backends.py @@ -3,8 +3,8 @@ import syslog from django.conf import settings from django.contrib.auth.models import User -from django.contrib.auth.backends import ModelBackend from django.contrib.auth.models import Group +from django.contrib.auth.backends import ModelBackend import dpam.pam as pam @@ -32,11 +32,13 @@ def authenticate(self, username=None, password=None): if getattr(settings, 'PAM_IS_STAFF', user.is_superuser): user.is_staff = True - if getattr(settings, 'PAM_USERS_GROUP', None): + user.save() + + if getattr(settings, 'PAM_USERS_GROUP', False): group = Group.objects.get(name=settings.PAM_USERS_GROUP) group.user_set.add(user) + group.save() - user.save() return user def get_user(self, user_id): From 98fd6e437c6fe8c48f1fd33172a0829d24a4756e Mon Sep 17 00:00:00 2001 From: Francesco kurojishi Berni Date: Fri, 19 Aug 2016 14:53:48 +0200 Subject: [PATCH 5/6] Group managment documentation --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index 3fca312..ef911c4 100644 --- a/README.rst +++ b/README.rst @@ -19,3 +19,6 @@ object is created. By default this new ``User`` has both ``is_staff`` and If you do not want a ``User`` record to be created automatically, use ``PAM_CREATE_USER=False`` in ``settings.py``. This is useful in situations where you want to use PAM for authentication but not for authorization. + +If you want your new users in a specific group use the setting ``PAM_USERS_GROUP=""``, +this will automatically add them to the group. From f606ec439c643620aa4f1c60ebac6f99a75a6403 Mon Sep 17 00:00:00 2001 From: Francesco Berni Date: Fri, 14 Oct 2016 16:35:59 +0200 Subject: [PATCH 6/6] linux groups management --- dpam/__init__.py | 0 dpam/shadow.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) delete mode 100644 dpam/__init__.py create mode 100644 dpam/shadow.py diff --git a/dpam/__init__.py b/dpam/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/dpam/shadow.py b/dpam/shadow.py new file mode 100644 index 0000000..4c89641 --- /dev/null +++ b/dpam/shadow.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +from os import path +from crypt import crypt +from re import compile as compile_regex + + +def check_auth(user, password): + """Perform authentication against the local systme. + + This function will perform authentication against the local system's + /etc/shadow or /etc/passwd database for a given user and password. + + :param user: The username to perform authentication with + :type user: str + + :param password: The password (plain text) for the given user + :type password: str + + :returns: True if successful, None otherwise. + :rtype: True or None + """ + + salt_pattern = compile_regex(r"\$.*\$.*\$") + passwd = "/etc/shadow" if path.exists("/etc/shadow") else "/etc/passwd" + + with open(passwd, "r") as f: + rows = (line.strip().split(":") for line in f) + records = [row for row in rows if row[0] == user] + + hash = records and records[0][1] + salt = salt_pattern.match(hash).group() + + return crypt(password, salt) == hash