136
120
if res != 0 and throw_on_error:
137
121
raise Exception("Unable to create ivle-auth for %s" % login)
139
# Make sure the file is owned by the web server
141
chown_to_webserver(conf.svn_auth_ivle)
145
def generate_manifest(basedir, targetdir, parent=''):
146
""" From a basedir and a targetdir work out which files are missing or out
147
of date and need to be added/updated and which files are redundant and need
150
parent: This is used for the recursive call to track the relative paths
151
that we have decended.
154
cmp = filecmp.dircmp(basedir, targetdir)
156
# Add all new files and files that have changed
157
to_add = [os.path.join(parent,x) for x in (cmp.left_only + cmp.diff_files)]
159
# Remove files that are redundant
160
to_remove = [os.path.join(parent,x) for x in cmp.right_only]
163
for d in cmp.common_dirs:
164
newbasedir = os.path.join(basedir, d)
165
newtargetdir = os.path.join(targetdir, d)
166
newparent = os.path.join(parent, d)
167
(sadd,sremove) = generate_manifest(newbasedir, newtargetdir, newparent)
171
return (to_add, to_remove)
174
def make_jail(username, uid, force=True, svn_pass=None):
125
def make_jail(username, uid, force=True):
175
126
"""Creates a new user's jail space, in the jail directory as configured in
178
This only creates things within /home - everything else is expected to be
179
part of another UnionFS branch.
129
This expects there to be a "template" directory within the jail root which
130
contains all the files for a sample student jail. It creates the student's
131
directory in the jail root, by making a hard-link copy of every file in the
132
template directory, recursively.
181
134
Returns the path to the user's home directory.
190
143
force: If false, exception if jail already exists for this user.
191
144
If true (default), overwrites it, but preserves home directory.
193
svn_pass: If provided this will be a string, the randomly-generated
194
Subversion password for this user (if you happen to already have it).
195
If not provided, it will be read from the database.
197
146
# MUST run as root or some of this may fail
198
147
if os.getuid() != 0:
199
148
raise Exception("Must run make_jail as root")
150
templatedir = os.path.join(conf.jail_base, 'template')
151
if not os.path.isdir(templatedir):
152
raise Exception("Template jail directory does not exist: " +
201
154
# tempdir is for putting backup homes in
202
tempdir = os.path.join(conf.jail_base, '__temp__')
155
tempdir = os.path.join(conf.jail_base, 'temp')
203
156
if not os.path.exists(tempdir):
204
157
os.makedirs(tempdir)
205
158
elif not os.path.isdir(tempdir):
206
159
os.unlink(tempdir)
207
160
os.mkdir(tempdir)
208
userdir = os.path.join(conf.jail_src_base, username)
161
userdir = os.path.join(conf.jail_base, username)
209
162
homedir = os.path.join(userdir, 'home')
210
userhomedir = os.path.join(homedir, username) # Return value
212
164
if os.path.exists(userdir):
214
166
raise Exception("User's jail already exists")
215
167
# User jail already exists. Blow it away but preserve their home
216
# directory. It should be all that is there anyway, but you never
218
169
# Ignore warnings about the use of tmpnam
219
170
warnings.simplefilter('ignore')
220
171
homebackup = os.tempnam(tempdir)
223
174
# into a directory if it already exists, just fails. Therefore it is
224
175
# not susceptible to tmpnam symlink attack.
225
176
shutil.move(homedir, homebackup)
226
shutil.rmtree(userdir)
228
shutil.move(homebackup, homedir)
178
# Any errors that occur after making the backup will be caught and
179
# the backup will be un-made.
180
# XXX This will still leave the user's jail in an unusable state,
181
# but at least they won't lose their files.
182
shutil.rmtree(userdir)
184
# Hard-link (copy aliasing) the entire tree over
185
linktree(templatedir, userdir)
187
# Set up the user's home directory (restore backup)
188
# First make sure the directory is empty and its parent exists
190
shutil.rmtree(homedir)
193
# XXX If this fails the user's directory will be lost (in the temp
194
# directory). But it shouldn't fail as homedir should not exist.
196
shutil.move(homebackup, homedir)
197
return os.path.join(homedir, username)
230
199
# No user jail exists
200
# Hard-link (copy aliasing) the entire tree over
201
linktree(templatedir, userdir)
231
203
# Set up the user's home directory
232
os.makedirs(userhomedir)
204
userhomedir = os.path.join(homedir, username)
205
os.mkdir(userhomedir)
233
206
# Chown (and set the GID to the same as the UID).
234
207
os.chown(userhomedir, uid, uid)
235
208
# Chmod to rwxr-xr-x (755)
236
209
os.chmod(userhomedir, 0755)
238
# There are 2 special files which need to be generated specific to this
239
# user: /opt/ivle/lib/conf/conf.py and /etc/passwd.
240
# "__" username "__" users are exempt (special)
241
if not (username.startswith("__") and username.endswith("__")):
242
make_conf_py(username, userdir, conf.jail_system, svn_pass)
243
make_etc_passwd(username, userdir, conf.jail_system, uid)
247
def make_conf_py(username, user_jail_dir, staging_dir, svn_pass=None):
249
Creates (overwriting any existing file, and creating directories) a
250
file /opt/ivle/lib/conf/conf.py in a given user's jail.
252
user_jail_dir: User's jail dir, ie. conf.jail_base + username
253
staging_dir: The dir with the staging copy of the jail. (With the
254
template conf.py file).
255
svn_pass: As with make_jail. User's SVN password, but if not supplied,
256
will look up in the DB.
258
template_conf_path = os.path.join(staging_dir,"opt/ivle/lib/conf/conf.py")
259
conf_path = os.path.join(user_jail_dir, "opt/ivle/lib/conf/conf.py")
260
os.makedirs(os.path.dirname(conf_path))
262
# If svn_pass isn't supplied, grab it from the DB
265
svn_pass = dbconn.get_user(username).svn_pass
268
# Read the contents of the template conf file
270
template_conf_file = open(template_conf_path, "r")
271
template_conf_data = template_conf_file.read()
272
template_conf_file.close()
274
# Couldn't open template conf.py for some reason
275
# Just treat it as empty file
276
template_conf_data = ("# Warning: Problem building config script.\n"
277
"# Could not find template conf.py file.\n")
279
conf_file = open(conf_path, "w")
280
conf_file.write(template_conf_data)
281
conf_file.write("\n# The login name for the owner of the jail\n")
282
conf_file.write("login = %s\n" % repr(username))
283
conf_file.write("\n")
284
conf_file.write("# The subversion-only password for the owner of "
286
conf_file.write("svn_pass = %s\n" % repr(svn_pass))
289
# Make this file world-readable
290
# (chmod 644 conf_path)
291
os.chmod(conf_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP
294
def make_etc_passwd(username, user_jail_dir, template_dir, unixid):
296
Creates /etc/passwd in the given user's jail. This will be identical to
297
that in the template jail, except for the added entry for this user.
299
template_passwd_path = os.path.join(template_dir, "etc/passwd")
300
passwd_path = os.path.join(user_jail_dir, "etc/passwd")
301
passwd_dir = os.path.dirname(passwd_path)
302
if not os.path.exists(passwd_dir):
303
os.makedirs(passwd_dir)
304
shutil.copy(template_passwd_path, passwd_path)
305
passwd_file = open(passwd_path, 'a')
306
passwd_file.write('%s:x:%d:%d::/home/%s:/bin/bash'
307
% (username, unixid, unixid, username))
310
212
def linktree(src, dst):
311
213
"""Recursively hard-link a directory tree using os.link().
371
273
if res != 0 and throw_on_error:
372
274
raise Exception("Unable to create local-auth for %s" % kwargs['login'])
374
# Make sure the file is owned by the web server
376
chown_to_webserver(conf.svn_auth_local)
378
def mount_jail(login):
379
# This is where we'll mount to...
380
destdir = os.path.join(conf.jail_base, login)
381
# ... and this is where we'll get the user bits.
382
srcdir = os.path.join(conf.jail_src_base, login)
384
if not os.path.exists(destdir):
386
if os.system('/bin/mount -t aufs -o dirs=%s:%s=ro none %s'
387
% (srcdir, conf.jail_system, destdir)) == 0:
388
logging.info("mounted user %s's jail." % login)
390
logging.error("failed to mount user %s's jail!" % login)
391
except Exception, message:
392
logging.warning(str(message))