|
Attached is a modified version of the python script which works for
me. Essentially I have added some poll() calls to ensure the shell
commands are completed before communicating with the subprocesses. I have also added two lines in the runcmd subroutine to log any error messages to syslog. Hope that's OK with you. I will now do further testing of this. On 26/07/11 11:54, Petter Reinholdtsen wrote: [Wolfgang Schulze-Zachau]Would it be unfair to say we should take the route of least resistance?I have no problem with your proposed change, so why not. Can you test the new version currently available from svn://svn.debian.org/svn/debian-edu/trunk/src/libpam-mklocaluser ? If it works for you, I'll upload it to unstable. Happy hacking, --
best regards Wolfgang Amino | Mob: +44 7554 457 455 | Desk: +44 1954 234 190 | skype: wszachauamino |
#!/usr/bin/env python
#
# Create local user and redirected home directory.
# If the local user logging in have uid >= 1000, create primary group
# and user in /etc/passwd and /etc/group, and create a home directory
# under /home/ if none exist already.
import os
import sys
import pwd
import grp
import subprocess
import shutil
import math
import time
import syslog
def runcmd(pamh, cmd):
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,)
while proc.poll() == None:
pass
result = proc.communicate(input=None)[0]
if result != 0:
syslog.syslog("Command %(command)s failed with %(msg)s" % ( cmd, proc.stderr.read()) )
# print "output: %s" % output
def check_and_create_localuser(pamh, user):
# Location of local users
topdir = "/home"
# Ignore users with uid below this one
minimum_uid = 500
# Create user entries with this shell
shell = '/bin/bash'
# File mode of new home directory
dirmode = 0700
# Last password change, use today
pwlastchange = math.floor(time.time() / (60 * 60 * 24 ))
pwminage = 0
pwmaxage = 99999
pwwarn = 7
# Fetch current user and group info, possibly from LDAP or NIS.
userinfo = pwd.getpwnam(user)
uid = userinfo[2]
gid = userinfo[3]
gecos = userinfo[4]
homedir = userinfo[5]
# Ignore users with uid < 1000
if userinfo[2] < minimum_uid:
return pamh.PAM_SUCCESS
# Ignore users with existing entry in /etc/passwd
cmd = "/bin/grep \"^%s:\" /etc/passwd >/dev/null" % user
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, )
while proc.poll() == None:
pass
result = proc.communicate(input=None)[0]
if proc.returncode == 0:
return pamh.PAM_SUCCESS
if None == homedir:
syslog.syslog("Home directory is not set for user %s" % user)
return pamh.PAM_USER_UNKNOWN
newhomedir = os.path.join(topdir, user)
if not os.path.isdir(homedir) and not os.path.isdir(newhomedir):
try:
groupinfo = grp.getgrgid(gid)
groupname = groupinfo[0]
except KeyError, e:
syslog.syslog("Unknown primary group with gid %d" % gid)
groupname = "[unknown]"
syslog.syslog("Creating local passwd entry uid=%d(%s) gid=%d(%s) gecos='%s' home=%s" % (uid, user, gid, groupname, gecos, newhomedir))
try:
# Add user entry with overridden home directory in /etc/passwd.
# Can not use adduser, as it refuses to add a user if it already
# is visible via NSS.
cmd = "/bin/echo '%s:x:%d:%d:%s:%s:%s' >> /etc/passwd" \
% (user, uid, gid, gecos, newhomedir, shell)
runcmd(pamh, cmd)
# Add shadow entry too.
# XXX Should only add it if it is missing
cmd = "/bin/echo '%s:x:%d:%d:%d:%d:::' >> /etc/shadow" \
% (user, pwlastchange, pwminage, pwmaxage, pwwarn)
runcmd(pamh, cmd)
syslog.syslog("Creating local home directory for user '%s'" % user)
# Copy content of /etc/skel
shutil.copytree("/etc/skel/.", newhomedir, True)
# Change perm of new home dir
os.chmod(newhomedir, dirmode)
# os.chown(newhomedir, uid, gid) - not recursive
runcmd(pamh, "/bin/chown -R %d:%d '%s'" % (uid, gid, newhomedir))
# Flush nscd cache to get rid of original user entry
if os.access("/usr/sbin/nscd", os.X_OK):
runcmd(pamh, "/usr/sbin/nscd -i passwd")
# Hook for adjusting the freshly created home directory
# XXX Should be rewritten in python, I guess
runcmd(pamh, "if [ -d /etc/mklocaluser.d ]; then ORIGHOMEDIR='%s' USER='%s' /bin/run-parts /etc/mklocaluser.d ; fi" % (homedir, user))
# Let the user know what is going on
msg = pamh.Message(pamh.PAM_TEXT_INFO,
"Local user created in /home/, please log in again to start using it.")
pamh.conversation(msg)
# Throw out user, as the log process cached the home directory
# and need to be restarted.
return pamh.PAM_TRY_AGAIN
except Exception, e:
syslog.syslog(e)
pass
return pamh.PAM_SUCCESS
def pam_sm_setcred(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_authenticate(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_acct_mgmt(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_open_session(pamh, flags, argv):
syslog.openlog("pam_mklocaluser", syslog.LOG_PID, syslog.LOG_AUTH)
try:
user = pamh.get_user(None)
except pamh.exception, e:
return e.pam_result
if user == None:
syslog.syslog("No user, ignoring pam-python for mklocaluser")
return pamh.PAM_USER_UNKNOWN
# Only create local users for console logins
try:
if pamh.rhost != None and 0 != len(pamh.rhost):
syslog.syslog("Remote login, ignoring pam-python for mklocaluser")
return pamh.PAM_SUCCESS
except pamh.exception, e:
return e.pam_result
try:
return check_and_create_localuser(pamh, user)
except KeyError, e:
syslog.syslog("Unknown username, should never happen: %s" % e)
return pamh.PAM_USER_UNKNOWN
except Exception, e:
syslog.syslog("Unexpected exception, should never happen: %s" % e)
return pamh.PAM_SYSTEM_ERR
def pam_sm_close_session(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_chauthtok(pamh, flags, argv):
return pamh.PAM_SUCCESS
# Test if the code work. Argument is username to simulate login for.
if __name__ == '__main__':
syslog.openlog("pam_mklocaluser", syslog.LOG_PID, syslog.LOG_AUTH)
class pam_handler:
PAM_SUCCESS = 1
PAM_USER_UNKNOWN = 2
PAM_SYSTEM_ERR = 3
PAM_TRY_AGAIN = 4
PAM_TEXT_INFO = 5
def Message(tag, str):
return
def conversation(msg):
return
pamh = pam_handler()
user = sys.argv[1]
check_and_create_localuser(pamh, user)