[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#634829: libpam-mklocaluser: login fails when used in conjunction with sssd

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
I have no problem with your proposed change, so why not.  Can you test
the new version currently available from



If it works for you, I'll upload it to unstable.

Happy hacking,

best regards


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:
  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:
  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):
      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))
      # 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.")

      # Throw out user, as the log process cached the home directory
      # and need to be restarted.
      return pamh.PAM_TRY_AGAIN
    except Exception, e:

  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)
    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
    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
    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:
    def Message(tag, str):
    def conversation(msg):
  pamh = pam_handler()
  user = sys.argv[1]
  check_and_create_localuser(pamh, user)

Reply to: