Update of setuid cleanup code.
[mspang/pyceo.git] / bin / addhomedir
1 #!/usr/bin/python2.4 --
2 """
3 addhomedir - a script to create home directories for new users
4
5 For the most part, this script mimics the behavior of adduser(8) when
6 creating a home directory. It creates the directory, copies files
7 from /etc/skel, sets permissions, and sets quotas.
8 """
9 import os, sys, re, pwd, getopt, stat
10
11 CONFIG_FILE = '/etc/csc/accounts.cf'
12
13 safe_environment = ['LOGNAME', 'USERNAME', 'USER', 'HOME', 'TERM', 'LANG'
14     'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY',
15     'LC_NUMERIC', 'LC_TIME', 'UID', 'GID', 'SSH_CONNECTION', 'SSH_AUTH_SOCK',
16     'SSH_CLIENT']
17
18 for key in os.environ.keys():
19     if key not in safe_environment:
20         del os.environ[key]
21
22 os.environ['PATH'] = '/usr/sbin:/sbin:/usr/bin:/bin'
23 os.umask(0)
24
25 try:
26     os.setreuid(0, 0)
27     os.setregid(0, 0)
28 except OSError:
29     print "You must be root to use this command."
30     sys.exit(1)
31
32 for pathent in sys.path[:]:
33     if not pathent.find('/usr') == 0:
34         sys.path.remove(pathent)
35
36 from csc.common import conf
37
38 usage = "usage: %s [--quiet] username" % sys.argv[0]
39
40 try:
41     options, arguments = getopt.gnu_getopt(sys.argv[1:], '', ['quiet'])
42 except getopt.GetoptError, e:
43     print usage
44     sys.exit(2)
45
46 if len(arguments) != 1:
47     print usage
48     sys.exit(2)
49
50 quiet = ('--quiet','') in options
51 program = os.path.basename(sys.argv[0])
52 username = arguments[0]
53
54 def fail(message):
55     print >> sys.stderr, "%s: %s" % (program, message)
56     sys.exit(1)
57
58 def debug(message):
59     if not quiet:
60         print message
61
62 def warn(message):
63     print >> sys.stderr, "warning: %s" % message
64
65 try:
66     cfg = conf.read(CONFIG_FILE)
67 except conf.ConfigurationException:
68     fail("could not import configuration from %s" % CONFIG_FILE)
69
70 try:
71     string_fields = ['homedir_regex', 'skeleton_dir', 'quota_prototype']
72     integer_fields = ['homedir_min_uid', 'homedir_mode']
73     conf.check_string_fields(CONFIG_FILE, string_fields, cfg)
74     conf.check_integer_fields(CONFIG_FILE, integer_fields, cfg)
75 except conf.ConfigurationException, e:
76     fail(e)
77
78 if not os.path.isdir(cfg['skeleton_dir']):
79     fail("invalid skeleton dir %s" % cfg['skeleton_dir'])
80
81 try:
82     pwent = pwd.getpwnam(username)
83     uid = pwent.pw_uid
84     gid = pwent.pw_gid
85     homedir = pwent.pw_dir
86 except KeyError:
87     fail("%s: invalid user" % username)
88     
89 if uid < cfg['homedir_min_uid']:
90     fail("uid of account %s is less than homedir_min_uid" % username)
91
92 if not re.match(cfg['homedir_regex'], homedir):
93     fail("homedir of account %s does not match homedir_regex" % username)
94
95 if os.path.exists(homedir):
96     fail("home directory %s exists" % homedir)
97
98 parentdir = os.path.dirname(homedir)
99 if not os.path.isdir(parentdir):
100     fail("parent directory %s does not exist" % parentdir)
101
102 try:
103     debug("creating %s" % homedir)
104     os.mkdir(homedir, cfg['homedir_mode'])
105     os.chown(homedir, uid, gid)
106
107     # copy files from /etc/skel or similar (location is configurable)
108     skel_files = os.listdir(cfg['skeleton_dir'])
109     for filename in skel_files:
110
111         srcpath = cfg['skeleton_dir'] + '/' + filename
112         destpath = homedir + '/' + filename
113         srcstat = os.lstat(srcpath)
114         perm = srcstat.st_mode & 0777
115         
116         if srcstat.st_uid or srcstat.st_gid:
117             warn("skipping %s due to ownership" % srcpath)
118             continue
119         
120         if stat.S_ISLNK(srcstat.st_mode):
121             linkdest = os.readlink(srcpath)
122             debug("linking %s to %s" % (destpath, linkdest))
123             os.symlink(linkdest, destpath)
124             os.lchown(destpath, uid, gid)
125
126         elif stat.S_ISDIR(srcstat.st_mode):
127             debug("adding directory %s" % destpath)
128             os.mkdir(destpath, perm)
129             os.chown(destpath, uid, gid)
130
131         elif stat.S_ISREG(srcstat.st_mode):
132             debug("adding %s" % destpath)
133             src  = open(srcpath)
134             destfd = os.open(destpath, os.O_CREAT|os.O_EXCL|os.O_WRONLY, perm)
135             dest = os.fdopen(destfd, 'w')
136             dest.write(src.read())
137             src.close()
138             dest.close()
139             os.chown(destpath, uid, gid)
140
141         else:
142             warn("file type of %s not supported" % srcpath)
143
144     # set the user's quota
145     setquota = '/usr/sbin/setquota'
146     if os.access(setquota, os.X_OK):
147         protouser = cfg['quota_prototype']
148         debug("copying quotas from %s to %s" % (protouser, username))
149         args = [ setquota, '-u', '-p', protouser, username, '-a' ]
150         result = os.spawnv(os.P_WAIT, setquota, args)
151         if result:
152             warn("setquota returned error code %d" % result)
153     else:
154         warn("cannot find setquota, not setting quota")
155
156     debug("done!")
157
158 except OSError, e:
159     fail(e)