Last active
May 23, 2020 10:38
-
-
Save Cybso/2016e4de9a2465cef920 to your computer and use it in GitHub Desktop.
PAM helper script in python to allow migration of user accounts stored in LDAP (with SSHA hashed passwords) to regular unix system users. This script will be executed is pam_unix failed and will try to verify the user password stored in /etc/shadow using SSHA algorithm.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
### | |
# Takes a username from environment variable PAM_USER, a password from STDIN | |
# and tries to verify the shadow's password using LDAP's SSHA algorithm: | |
# | |
# {SSHA}...base64encodedString... | |
# | |
# If used within PAM auth this provides a compatibility layer that allows | |
# to migrate LDAP user into Unix user accounts. When the user's password | |
# has been verified the program will execute 'passwd' to convert the password | |
# into the system's default format. This means this program will make itself | |
# obsolete when every user has logged in at least once. | |
# | |
# This programm will exit with return code 1, except when the password | |
# has been verified. | |
# | |
# To integrate it into PAM, open /etc/pam.d/common-auth and replace the line: | |
# | |
# auth [success=1 default=ignore] pam_unix.so nullok_secure | |
# auth requisite pam_deny.so | |
# | |
# with | |
# | |
# auth [success=2 default=ignore] pam_unix.so nullok_secure | |
# auth [success=1 default=ignore] pam_exec.so expose_authtok /usr/local/sbin/verify_ssha_passwd.py | |
# auth requisite pam_deny.so | |
# | |
# Author: Roland Tapken <[email protected]> | |
# Article: http://rolandtapken.de/blog/2016-03/migrate-user-accounts-openldap-unix-system-user | |
### | |
import os | |
import sys | |
import hashlib | |
import subprocess | |
from base64 import urlsafe_b64decode as decode | |
def checkPassword(challenge_password, password): | |
challenge_bytes = decode(challenge_password[6:]) | |
digest = challenge_bytes[:20] | |
salt = challenge_bytes[20:] | |
hr = hashlib.sha1(password) | |
hr.update(salt) | |
return digest == hr.digest() | |
def updatePassword(username, password): | |
proc=subprocess.Popen(['passwd', '--', username], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
proc.stdin.write(password) | |
proc.stdin.write('\n') | |
proc.stdin.write(password) | |
proc.stdin.flush() | |
proc.communicate() | |
try: | |
user=os.environ.get('PAM_USER') | |
if user: | |
shadow=subprocess.Popen(['getent', 'shadow', '--', user], shell=False, stdout=subprocess.PIPE).stdout.read() | |
if shadow: | |
challenge=shadow.split(':')[1] | |
if challenge and challenge.startswith('{SSHA}'): | |
for password in sys.stdin: | |
# Shell (testing) terminates the string with \n or \r, | |
# pam terminates it with \0. Of course this means you | |
# cannot use \n or \r as last character of a password ;-) | |
password=password.rstrip('\n\r\0') | |
if checkPassword(challenge, password): | |
# Try to update the user's password into the system's default hash | |
try: | |
updatePassword(user, password) | |
except: | |
# Ignore if this is not possible | |
pass | |
os._exit(0) | |
os._exit(1) | |
except: | |
sys.exit(1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment