Added "addhomedir", a utility to create home directories for new users.
authorMichael Spang <mspang@uwaterloo.ca>
Mon, 29 Jan 2007 06:42:42 +0000 (01:42 -0500)
committerMichael Spang <mike@freyr.utgard.net>
Mon, 29 Jan 2007 06:42:42 +0000 (01:42 -0500)
bin/addhomedir [new file with mode: 0755]
debian/control
debian/postinst
debian/postrm
debian/rules
etc/accounts.cf

diff --git a/bin/addhomedir b/bin/addhomedir
new file mode 100755 (executable)
index 0000000..ea5b2b9
--- /dev/null
@@ -0,0 +1,152 @@
+#!/usr/bin/python2.4 --
+"""
+addhomedir - a script to create home directories for new users
+
+For the most part, this script mimics the behavior of adduser(8) when
+creating a home directory. It creates the directory, copies files
+from /etc/skel, sets permissions, and sets quotas.
+"""
+import os, sys, re, pwd, getopt, stat
+
+CONFIG_FILE = '/etc/csc/accounts.cf'
+
+safe_environment = ['LOGNAME', 'USERNAME', 'USER', 'HOME', 'TERM', 'LANG'
+    'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGE', 'LC_MONETARY',
+    'LC_NUMERIC', 'LC_TIME', 'UID', 'GID', 'SSH_CONNECTION', 'SSH_AUTH_SOCK',
+    'SSH_CLIENT']
+
+for key in os.environ.keys():
+    if not key in safe_environment:
+        del os.environ[key]
+
+os.environ['PATH'] = '/usr/sbin:/sbin:/usr/bin:/bin'
+os.umask(0)
+
+for pathent in sys.path[:]:
+    if not pathent.find('/usr') == 0:
+        sys.path.remove(pathent)
+
+from csc.common import conf
+
+usage = "usage: %s [--quiet] username" % sys.argv[0]
+
+try:
+    options, arguments = getopt.gnu_getopt(sys.argv[1:], '', ['quiet'])
+except getopt.GetoptError, e:
+    print usage
+    sys.exit(2)
+
+if len(arguments) != 1:
+    print usage
+    sys.exit(2)
+
+quiet = ('--quiet','') in options
+program = os.path.basename(sys.argv[0])
+username = arguments[0]
+
+def fail(message):
+    print >> sys.stderr, "%s: %s" % (program, message)
+    sys.exit(1)
+
+def debug(message):
+    if not quiet:
+        print message
+
+def warn(message):
+    print >> sys.stderr, "warning: %s" % message
+
+try:
+    cfg = conf.read(CONFIG_FILE)
+except conf.ConfigurationException:
+    fail("could not import configuration from %s" % CONFIG_FILE)
+
+try:
+    string_fields = ['homedir_regex', 'skeleton_dir', 'quota_prototype']
+    integer_fields = ['homedir_min_uid', 'homedir_mode']
+    conf.check_string_fields(CONFIG_FILE, string_fields, cfg)
+    conf.check_integer_fields(CONFIG_FILE, integer_fields, cfg)
+except conf.ConfigurationException, e:
+    fail(e)
+
+if not os.path.isdir(cfg['skeleton_dir']):
+    fail("invalid skeleton dir %s" % cfg['skeleton_dir'])
+
+try:
+    pwent = pwd.getpwnam(username)
+    uid = pwent.pw_uid
+    gid = pwent.pw_gid
+    homedir = pwent.pw_dir
+except KeyError:
+    fail("%s: invalid user" % username)
+    
+if uid < cfg['homedir_min_uid']:
+    fail("uid of account %s is less than homedir_min_uid" % username)
+
+if not re.match(cfg['homedir_regex'], homedir):
+    fail("homedir of account %s does not match homedir_regex" % username)
+
+if os.path.exists(homedir):
+    fail("home directory %s exists" % homedir)
+
+parentdir = os.path.dirname(homedir)
+if not os.path.isdir(parentdir):
+    fail("parent directory %s does not exist" % parentdir)
+
+try:
+    debug("creating %s" % homedir)
+    os.mkdir(homedir, cfg['homedir_mode'])
+    os.chown(homedir, uid, gid)
+
+    # copy files from /etc/skel or similar (location is configurable)
+    skel_files = os.listdir(cfg['skeleton_dir'])
+    for filename in skel_files:
+
+        srcpath = cfg['skeleton_dir'] + '/' + filename
+        destpath = homedir + '/' + filename
+        srcstat = os.lstat(srcpath)
+        perm = srcstat.st_mode & 0777
+        
+        if srcstat.st_uid or srcstat.st_gid:
+            warn("skipping %s due to ownership" % srcpath)
+            continue
+        
+        if stat.S_ISLNK(srcstat.st_mode):
+            linkdest = os.readlink(srcpath)
+            debug("linking %s to %s" % (destpath, linkdest))
+            os.symlink(linkdest, destpath)
+            os.lchown(destpath, uid, gid)
+
+        elif stat.S_ISDIR(srcstat.st_mode):
+            debug("adding directory %s" % destpath)
+            os.mkdir(destpath, perm)
+            os.chown(destpath, uid, gid)
+
+        elif stat.S_ISREG(srcstat.st_mode):
+            debug("adding %s" % destpath)
+            src  = open(srcpath)
+            destfd = os.open(destpath, os.O_CREAT|os.O_EXCL|os.O_WRONLY, perm)
+            dest = os.fdopen(destfd, 'w')
+            dest.write(src.read())
+            src.close()
+            dest.close()
+            os.chown(destpath, uid, gid)
+
+        else:
+            warn("file type of %s not supported" % srcpath)
+
+    # set the user's quota
+    setquota = '/usr/sbin/setquota'
+    if os.access(setquota, os.X_OK):
+        protouser = cfg['quota_prototype']
+        debug("copying quotas from %s to %s" % (protouser, username))
+        args = [ setquota, '-u', '-p', protouser, username, '-a' ]
+        result = os.spawnv(os.P_WAIT, setquota, args)
+        if result:
+            warn("setquota returned error code %d" % result)
+    else:
+        warn("cannot find setquota, not setting quota")
+
+    debug("done!")
+
+except OSError, e:
+    fail(e)
index f5ed12b..b72fc4a 100644 (file)
@@ -8,6 +8,7 @@ Standards-Version: 3.7.2
 Package: csc
 Architecture: any
 Depends: python, python2.4, python2.4-ldap, python2.4-pygresql, krb5-user, less, ${shlibs:Depends}
+Recommends: quota
 Description: Computer Science Club Administrative Utilities
  This package contains the CSC Electronic Office
  and other Computer Science Club administrative
index 5611620..0a28f7b 100644 (file)
@@ -21,6 +21,10 @@ case "$1" in
             dpkg-statoverride --add --update $CEO $OFFICE $SUID /usr/bin/ceo
         fi
 
+        if ! dpkg-statoverride --list /usr/bin/addhomedir > /dev/null; then
+            dpkg-statoverride --add --update root $OFFICE $SUID /usr/bin/addhomedir
+        fi
+
         if [ -f /etc/csc/ldap.cf ] && ! dpkg-statoverride --list /etc/csc/ldap.cf > /dev/null; then
             dpkg-statoverride --add --update $CEO staff 640 /etc/csc/ldap.cf
         fi
index 3908d29..b5f7228 100644 (file)
@@ -7,6 +7,10 @@ case "$1" in
             dpkg-statoverride --remove /usr/bin/ceo || true
         fi
 
+        if dpkg-statoverride --list /usr/bin/addhomedir > /dev/null; then
+            dpkg-statoverride --remove /usr/bin/addhomedir || true
+        fi
+
         if dpkg-statoverride --list /etc/csc/ldap.cf > /dev/null; then
             dpkg-statoverride --remove /etc/csc/ldap.cf || true
         fi
index 443ce29..160348a 100755 (executable)
@@ -7,6 +7,7 @@ build: build-stamp
 build-stamp:
        mkdir build
        $(CC) -DFULL_PATH='"/usr/lib/csc/ceo"' -o build/ceo misc/setuid-prog.c
+       $(CC) -DFULL_PATH='"/usr/lib/csc/addhomedir"' -o build/addhomedir misc/setuid-prog.c
        touch build-stamp
 
 clean:
@@ -27,8 +28,8 @@ install: build
        dh_install etc/* etc/csc/
        dh_install sql/* usr/share/csc/
        
-       dh_install bin/ceo usr/lib/csc/
-       dh_install build/ceo usr/bin/
+       dh_install bin/ceo bin/addhomedir usr/lib/csc/
+       dh_install build/ceo build/addhomedir usr/bin/
        
 binary-arch: build install
        dh_testdir
index 553f947..98ff60b 100644 (file)
@@ -36,9 +36,17 @@ group_min_id = 10000
 group_max_id = 14999
 group_desc = "CSC Group"
 
+### Home Directory Options ###
+
+skeleton_dir = "/etc/skel"
+homedir_mode = 0755
+homedir_min_uid = 10000
+quota_prototype = "ctdalek"
+
 ### Validation Tuning ###
 
 username_regex = "^[a-z][-a-z0-9]*$"
 groupname_regex = "^[a-z][-a-z0-9]*$"
+homedir_regex = "^/users/[^\.]+$"
 min_password_length = 4
 shells_file = "/etc/shells"