3

I'm a TA for an introductory Python programming class (all work is done from the terminal), and I'm writing a project submission script. Ideally I want a directory setup like this:

submissions/
   user1/
      project1/
      ...
   user2/
      project1/
      ...
   ...

Ideally, I'd like nothing inside the submissions directory to be readable or writable to any of the users, and I'll provide a submission script that copies all of their work into the right place.

Currently I have a little Perl script that does the submission part and works when I use it, but how can I set up the permissions (or the script) so that the students don't get a permission denied error while running the script (requiring write permissions to the submissions directory), but they don't simultaneously have write permissions?

Kevin
  • 40,767
JeremyKun
  • 314
  • 1
    If you are not totally wedded to your approach, I'd suggest an alternative method, using distributed version control. Have each of your students have a repos in a standard location, like /.../.../studentname. then you can have an automated script that just clones/pulls their submissions wherever you want them to go. Since you are initiating the transaction, you don't have any permission issues, and of course, neither do they. Your script would presumably loop over the list of students, and could be totally automated, run as a cron job for example. – Faheem Mitha Jan 12 '12 at 19:26
  • I'm not totally wedded, but if there is a nice way to do it this way... I think it might be simpler without bringing cron and repos into it. – JeremyKun Jan 12 '12 at 19:35
  • The approach I describe would be simple to set up. The major difficulty would be getting the students to use a DVCS, but if they are programming, they should learn it anyway. Cron is strictly optional. – Faheem Mitha Jan 12 '12 at 19:39
  • right. I'd much rather them be able to run a command like "submit assignment file" and not have to think about versioning. – JeremyKun Jan 12 '12 at 19:42
  • 1
    The only option I see here to avoid permission issues is to set up a program to run with the SUID bit set. For good reason, there are safeguards in place to prevent shell scripts from running as root via the SUID bit. However, you could write a simple C/C++ program to do the work for you, compile it and use the SUID bit to have it run as root and do the required copying. – Sean C. Jan 12 '12 at 21:06

3 Answers3

2

A follow-up to one of the comments (I don't have enough mojo to comment yet): it seems like everyone has shell access to a known host for doing their assignments. So that simplifies things. Here's what you can (and should) do:

  • You should set up a git server (preferably gitolite for this type of environment) and a separate repo for each student. Assuming you've set up the student home directories, you can go ahead & run ssh-keygen for each student account and copy the public keys to your gitolite admin configuration. (EDIT (clarification): root access is not required to install gitolite. Creating student public/private keys requires access to their accounts, of course, but it's just one command that they can run on their own if necessary (ssh-keygen).)
  • Each student only has permissions for their repo, but you have access to read/write to them all.
  • They clone their repo w/ any initial templates and instructions at the beginning of the course; afterwards, they just pull/push new assignments & submissions.
  • Each project (assignment) can be in a separate directory (as you proposed). And you can create the directories yourself & commit/push the changes, and they can simply pull the changes when it's time to start the project. (Yes, you have to do it for each student, but that can be easily scripted.) That way you know the project directories are all named correctly to support automation for grading.
  • Additional perks: You can see who started the project the night before it was due... and who started early & refined as they went. Bonus points for steady commits; minus points for a single commit w/ the "finished" project. You can also (optionally) have people work on teams, and really see who did all the work (each person would have their own commit history.) It's trivial to configure permissions this way in gitolite.

Long story short: there is so much for students to learn, so little time to teach it. Over the past 20 years as a professional developer, I've rarely seen a new hire out of university actually know how to use any type of revision control. All they have is a loose grasp of programming languages x, y & z (and Java), and know nothing of software development. But we can't really blame the students, though, can we? (Ahem...) This is a teaching moment.

michael
  • 842
  • Hey, I learned CVS as an undergraduate (it was the modern thing at the time). Seriously, I agree that a version control system with access control is a good solution here. – Gilles 'SO- stop being evil' Jan 13 '12 at 21:40
  • +1, sounds like a reasonable setup. @Bean, if you make your students learn version control, they'll thank you later, though maybe not at the time. :-) I'd suggest mercurial over git for people with no experience with version control. – Faheem Mitha Jan 17 '12 at 08:25
  • This class is not right place to learn version control. They hardly know the terminal. It's an introductory class. – JeremyKun Jan 18 '12 at 02:02
  • @Bean no need to learn version control; they just have to follow instructions. And that's true in either case. Either they follow instructions for a home-grown "commit script" (/opt/bin/submit_hmwk.sh my_homewk.py); or, follow instructions a (very) popular "commit script" that happens to be called "git commit -a; git push". Which is more difficult? Well, the 2nd option (two commands) is perhaps 2x more difficult than the 1st (one command). Still, the "git" option is infinitely more useful, considering the custom script option has value=0 to the student. But admittedly it'd be fun to write ;-) – michael Jan 20 '12 at 02:36
  • Point taken. In any event, I don't have permissions to download or install anything on the server we're using (shared with some other classes, apparently), and I certainly didn't set up their home directories. So homegrown it is. – JeremyKun Jan 20 '12 at 03:24
2

For completeness, here's the simplest solution. I follow it with my view of why a versioning system is not appropriate.

I ended up enabling the setuid bit on the submission executable (chmod 4755), so that when students ran it, the program ran as me. All copying of files would then transfer ownership to me, and I could make the entire directory inaccessible to the students. This did involve a few hurdles in getting past Perl's added setuid security (untainting the input, untainting the PATH), but it worked out nicely in the end. The final script is around 20 lines.

The reason a versioning system is unacceptable is because this is a first course in programming. The students have never used a terminal, and are confused enough by the idea of ssh-ing into a server and transferring files back and forth. The details of a version control system are for upper-division classes (especially when a student is prone to terminal typos!). And you'd be surprised today how many of those courses do actually require one to learn a version control system (I took four courses that did in my undergraduate). A less significant reason I couldn't do a versioning system is that I couldn't have super-user privileges on the server, and wouldn't ask the professor to set up a versioning system just because I couldn't figure out the stupid setuid bit.

All I want for this course is a simple one-step submission to a private place. The problem was with my execution and lack of knowledge about unix permissions (not knowing about the setuid bit), not the structure of my solution. So Sean C.'s comment was the tip, but I didn't recognize it at first because it seemed like he meant the point was to run the script as root, and I didn't want that to protect the world from my own naivete about unix.

Here is the actual working script, and its permissions. Please point out any security holes you might see.

-rwsr-xr-x 1 260s12ta 260s12ta 600 2012-01-16 23:19 submit

#!/usr/bin/perl

use File::Copy;

$ENV{"PATH"} = "/usr/bin"; # appease the perl-suid security for shell calls

my $username = getlogin() or die "Couldn't access user login: $!\n"; 
my $dir = "/home/260s12ta/labs/$username/";

foreach (@ARGV) {
   if ($_ =~ /([\w-]+\.tar\.gz)/) { # untaint the given filename
      $filename = $1;
   } else {
      die "\nInvalid submission: $_ is not a .tar.gz file.\n";
   }

   print "Submitting $filename... ";
   copy($filename, $dir) or print "Submission failed: $!\n";
   chmod(0600, "$dir/$filename") or print "Submission failed: $!\n"; 
   print "OK!\n";
}
JeremyKun
  • 314
  • If it is not confidential, you could post the script, in case someone sometime wants to do a similar thing. – Faheem Mitha Jan 18 '12 at 06:17
  • At the risk of exposing my novice perl skills? sure why not. – JeremyKun Jan 18 '12 at 14:21
  • Thanks, Bean. Would you care to elaborate on what 'untaint' means? – Faheem Mitha Jan 18 '12 at 15:23
  • This is what the submission script at my school did. There's nothing particularly secret in there, but if you want you can even set it non-world-readable, so students can run it but not see what it does – Michael Mrozek Jan 18 '12 at 20:36
  • For some reason I set it nonreadable and they couldn't run it... But with luck the script is bullet-proof enough that they can read it and not take advantage of it anyway. Also, they are intro programmers, so they certainly don't know Perl. – JeremyKun Jan 19 '12 at 00:06
  • @FaheemMitha In perl, one considers all data that originates from the user to be unclean, or "tainted". In particular, if a user supplies a devious enough command line argument (which I then pass to copy or chmod), then they can potentially take over the running process and gain my privileges indefinitely. If you run a perl with the setuid bit enabled, it enforces some additional constraints, including that all user data must be untainted before it could be used in a potentially harmful way. (ctd...) – JeremyKun Jan 19 '12 at 00:09
  • To untaint a variable containing user data, one must match it against a regular expression (the if statement, the most complex part of the program), and then extract the captured part of the matched expression (here, the variable $1). In other words, this will ensure your user entered data in the way you expected him to. – JeremyKun Jan 19 '12 at 00:10
0

You should be able to use the Sticky bit to allow students to only over-write their own files in the shared directory.

Edit: Doesn't solve the problem since students would be able to read other students work.