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)