diff --git a/README.md b/README.md index 44f883e..80a2856 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,28 @@ shell with root access open while experimenting. Install the package libpam-python: - sudo apt-get install libpam-python + sudo apt install libpam-python -Edit `pam_custom.py` and supply the Portal API URL and a valid API key. +Edit `pam_netsyms.py` and supply the Portal API URL and a valid API key. -Copy the provided `pam_custom.py` to `/lib/security`: +Copy the provided `pam_netsyms.py` to `/lib/security`: - sudo cp pam_custom.py /lib/security + sudo cp pam_netsyms.py /lib/security + +### Recommended Install + +Make a file `/usr/share/pam-configs/netsyms` with the following content: + + Name: Netsyms Business Apps authentication + Default: no + Priority: 256 + Auth-Type: Primary + Auth: + [success=end default=ignore] pam_python.so pam_netsyms.py + +Run `sudo pam-auth-update` and enable it + +### Manual Install Make a backup of the file `/etc/pam.d/common-auth`: @@ -25,7 +40,7 @@ Edit the file `/etc/pam.d/common-auth` introducing a line in which you declare your custom authentication method. It should be something like this: - auth [success=2 default=ignore] pam_python.so pam_custom.py + auth [success=2 default=ignore] pam_python.so pam_netsyms.py and should be put just before (or after, according to your needs) the other authentication methods. @@ -36,13 +51,14 @@ Some explanations: 2. "pam_python.so" is the name of the shared object that will be called by pam -3. "pam_custom.py" is the script in python that we provide +3. "pam_netsyms.py" is the script in python that we provide -### Sample /etc/pam.d/common-auth +#### Sample /etc/pam.d/common-auth This config file will gather the username and password and attempt a normal login. If that fails, PAM will try to process the login via this module. auth [success=2 default=ignore] pam_unix.so nullok_secure - auth [success=1 default=ignore] pam_python.so pam_custom.py + auth [success=1 default=ignore] pam_python.so pam_netsyms.py + session required pam_mkhomedir.so skel=/etc/skel/ umask=0022 auth requisite pam_deny.so auth required pam_permit.so diff --git a/pam_custom.py b/pam_netsyms.py similarity index 67% rename from pam_custom.py rename to pam_netsyms.py index 482906c..b7809c4 100644 --- a/pam_custom.py +++ b/pam_netsyms.py @@ -3,7 +3,7 @@ ''' pam-custom Copyright (C) 2013 Loris Tisisno - Copyright (C) 2017 Netsyms Technologies + Copyright (C) 2017-2018 Netsyms Technologies This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,10 +22,25 @@ import os import requests import json +import pwd +import crypt +import sys -api_url = "http://localhost/accounthub/api.php" +api_url = "http:///accounthub/api.php" api_key = "123" +def load_settings(): + global api_url + global api_key + if os.path.isfile("/etc/netsyms-business/config.json"): + with open("/etc/netsyms-business/config.json") as f: + text = f.read() + data = json.loads(text) + api_url = data['apiurl'] + api_key = data['apikey'] + + + def totp_verify(user, totp): req = {"key": api_key, "action": "verifytotp", "username": user, "code": totp} resp = requests.post(api_url, data=req) @@ -39,7 +54,7 @@ def totp_check(user, pamh): resp = requests.post(api_url, data=req) if resp.json()['status'] == "OK": if resp.json()['otp'] == True: - otpmsg = pamh.Message(pamh.PAM_PROMPT_ECHO_ON, "[AccouhtHub] enter 2-factor auth code for " + user + ": ") + otpmsg = pamh.Message(pamh.PAM_PROMPT_ECHO_ON, "Authentication code: ") rsp = pamh.conversation(otpmsg) otpcode = rsp.resp return totp_verify(user, otpcode) @@ -47,20 +62,32 @@ def totp_check(user, pamh): return True return False -def portal_auth(user, password, pamh): + +def try_adduser(user, password): + try: + pwd.getpwnam(user) + except KeyError: + print('User ' + user + ' does not exist on this machine, creating...') + # stick this in if you want offline auth, but it isn't updated if the pwd changes in AccountHub + # -p $(mkpasswd -m sha-512 \"" + password + "\") + os.system("useradd -s "+ "/bin/bash "+ "-d "+ "/home/" + user + " -m " + user) + + +def accounthub_auth(user, password, pamh): req = {"key": api_key, "action": "auth", "username": user, "password": password} resp = requests.post(api_url, data=req) if resp.json()['status'] == "OK": - return totp_check(user, pamh) + if totp_check(user, pamh): + try_adduser(user, password) + return True + else: + return False else: return False def pam_sm_authenticate(pamh, flags, argv): - -# the basic idea for getting the password via pam.conversation comes from -# http://benctechnicalblog.blogspot.it/2011/05/pam-python-module-for-out-of-band.html - + load_settings() try: user = pamh.get_user(None) if user == None: @@ -69,13 +96,13 @@ def pam_sm_authenticate(pamh, flags, argv): password = pamh.authtok if password == None: ## got no password in authtok - trying through conversation... - passmsg = pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, "[AccountHub] enter password for " + user + ": ") + passmsg = pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, "Password for " + user + ": ") rsp = pamh.conversation(passmsg) password = rsp.resp # so we should at this point have the password either through the # prompt or from previous module - if portal_auth(user, password, pamh): + if accounthub_auth(user, password, pamh): return pamh.PAM_SUCCESS else: return pamh.PAM_AUTH_ERR