Browse Source

Add config file support, create users on the fly, update README

Skylar Ittner 5 years ago
  1. 32
  2. 49


@ -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 `` and supply the Portal API URL and a valid API key.
Edit `` and supply the Portal API URL and a valid API key.
Copy the provided `` to `/lib/security`:
Copy the provided `` to `/lib/security`:
sudo cp /lib/security
sudo cp /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
[success=end default=ignore]
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
auth [success=2 default=ignore]
auth [success=2 default=ignore]
and should be put just before (or after, according to your needs) the
other authentication methods.
@ -36,13 +51,14 @@ Some explanations:
2. "" is the name of the shared object that will be called by pam
3. "" is the script in python that we provide
3. "" 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] nullok_secure
auth [success=1 default=ignore]
auth [success=1 default=ignore]
session required skel=/etc/skel/ umask=0022
auth requisite
auth required

49 →

@ -3,7 +3,7 @@
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 =
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 =, data=req)
@ -39,7 +54,7 @@ def totp_check(user, pamh):
resp =, 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):
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 =, data=req)
if resp.json()['status'] == "OK":
return totp_check(user, pamh)
if totp_check(user, pamh):
try_adduser(user, password)
return True
return False
return False
def pam_sm_authenticate(pamh, flags, argv):
# the basic idea for getting the password via pam.conversation comes from
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
return pamh.PAM_AUTH_ERR