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)