Annotation of loncom/lcuseradd, revision 1.18
1.1 harris41 1: #!/usr/bin/perl
1.16 harris41 2:
3: # The Learning Online Network with CAPA
1.1 harris41 4: #
1.16 harris41 5: # lcuseradd - LON-CAPA setuid script to coordinate all actions
6: # with adding a user with filesystem privileges (e.g. author)
1.1 harris41 7: #
1.16 harris41 8: # YEAR=2000
9: # 10/27,10/29,10/30 Scott Harrison
1.15 harris41 10: # YEAR=2001
1.16 harris41 11: # 10/21,11/13,11/15 Scott Harrison
12: #
1.18 ! harris41 13: # $Id: lcuseradd,v 1.17 2001/11/15 22:38:12 harris41 Exp $
1.16 harris41 14: ###
1.15 harris41 15:
16: ###############################################################################
17: ## ##
18: ## ORGANIZATION OF THIS PERL SCRIPT ##
19: ## ##
20: ## 1. Description of script ##
21: ## 2. Invoking script (standard input) ##
22: ## 3. Example usage inside another piece of code ##
23: ## 4. Description of functions ##
24: ## 5. Exit codes ##
25: ## 6. Initializations ##
26: ## 7. Make sure this process is running from user=www ##
27: ## 8. Start running script with www permissions ##
28: ## 9. Handle case of another lcpasswd process (locking) ##
29: ## 10. Error-check input, need 3 values (user name, password 1, password 2) ##
30: ## 11. Start running script with root permissions ##
31: ## 12. Add user and make www a member of the user-specific group ##
32: ## 13. Set password ##
33: ## 14. Make final modifications to the user directory ##
34: ## 15. Exit script (unlock) ##
35: ## ##
36: ###############################################################################
1.1 harris41 37:
38: use strict;
39:
1.15 harris41 40: # ------------------------------------------------------- Description of script
41: #
1.1 harris41 42: # This script is a setuid script that should
43: # be run by user 'www'. It creates a /home/USERNAME directory
44: # as well as a /home/USERNAME/public_html directory.
1.15 harris41 45: # It adds a user to the unix system.
1.2 harris41 46: # Passwords are set with lcpasswd.
47: # www becomes a member of this user group.
1.1 harris41 48:
1.15 harris41 49: # -------------- Invoking script (standard input versus command-line arguments)
50: #
51: # Standard input (STDIN) usage
1.1 harris41 52: # First line is USERNAME
53: # Second line is PASSWORD
1.3 harris41 54: # Third line is PASSWORD
1.15 harris41 55: #
56: # Command-line arguments [USERNAME] [PASSWORD] [PASSWORD]
57: # Yes, but be very careful here (don't pass shell commands)
58: # and this is only supported to allow perl-system calls.
59: #
1.7 harris41 60: # Valid passwords must consist of the
61: # ascii characters within the inclusive
62: # range of 0x20 (32) to 0x7E (126).
63: # These characters are:
64: # SPACE and
65: # !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO
66: # PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
1.15 harris41 67: #
1.7 harris41 68: # Valid user names must consist of ascii
69: # characters that are alphabetical characters
70: # (A-Z,a-z), numeric (0-9), or the underscore
71: # mark (_). (Essentially, the perl regex \w).
1.15 harris41 72: # User names must begin with an alphabetical character
73: # (A-Z,a-z).
1.7 harris41 74:
1.15 harris41 75: # ---------------------------------- Example usage inside another piece of code
1.4 harris41 76: # Usage within code
77: #
1.15 harris41 78: # $exitcode=
79: # system("/home/httpd/perl/lcuseradd","NAME","PASSWORD1","PASSWORD2")/256;
1.4 harris41 80: # print "uh-oh" if $exitcode;
81:
1.15 harris41 82: # ---------------------------------------------------- Description of functions
83: # enable_root_capability() : have setuid script run as root
84: # disable_root_capability() : have setuid script run as www
85: # try_to_lock() : make sure that another lcpasswd process isn't running
86:
87: # ------------------------------------------------------------------ Exit codes
1.4 harris41 88: # These are the exit codes.
1.13 harris41 89: # ( (0,"ok"),
90: # (1,"User ID mismatch. This program must be run as user 'www'"),
1.15 harris41 91: # (2,"Error. This program needs 3 command-line arguments (username, ".
92: # "password 1, password 2)."),
1.13 harris41 93: # (3,"Error. Three lines should be entered into standard input."),
1.15 harris41 94: # (4,"Error. Too many other simultaneous password change requests being ".
95: # "made."),
1.13 harris41 96: # (5,"Error. User $username does not exist."),
97: # (6,"Error. Could not make www a member of the group \"$safeusername\"."),
98: # (7,"Error. Root was not successfully enabled.),
1.15 harris41 99: # (8,"Error. Cannot set password."),
1.13 harris41 100: # (9,"Error. The user name specified has invalid characters."),
101: # (10,"Error. A password entry had an invalid character."),
102: # (11,"Error. User already exists.),
1.15 harris41 103: # (12,"Error. Something went wrong with the addition of user ".
104: # "\"$safeusername\"."),
1.13 harris41 105: # (13,"Error. Password mismatch."),
1.4 harris41 106:
1.15 harris41 107: # ------------------------------------------------------------- Initializations
1.1 harris41 108: # Security
1.15 harris41 109: $ENV{'PATH'}='/bin/:/usr/bin:/usr/local/sbin:/home/httpd/perl'; # Nullify path
110: # information
1.16 harris41 111: delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # nullify potential taints
1.2 harris41 112:
1.15 harris41 113: # Do not print error messages.
114: my $noprint=1;
115:
116: # ----------------------------- Make sure this process is running from user=www
117: my $wwwid=getpwnam('www');
118: &disable_root_capability;
119: if ($wwwid!=$>) {
120: print("User ID mismatch. This program must be run as user 'www'\n")
121: unless $noprint;
1.4 harris41 122: exit 1;
123: }
1.15 harris41 124:
125: # ----------------------------------- Start running script with www permissions
1.4 harris41 126: &disable_root_capability;
127:
1.15 harris41 128: # --------------------------- Handle case of another lcpasswd process (locking)
1.4 harris41 129: unless (&try_to_lock("/tmp/lock_lcpasswd")) {
1.15 harris41 130: print "Error. Too many other simultaneous password change requests being ".
131: "made.\n" unless $noprint;
1.4 harris41 132: exit 4;
133: }
134:
1.15 harris41 135: # ------- Error-check input, need 3 values (user name, password 1, password 2).
1.4 harris41 136: my @input;
1.5 harris41 137: if (@ARGV==3) {
1.4 harris41 138: @input=@ARGV;
139: }
140: elsif (@ARGV) {
1.15 harris41 141: print("Error. This program needs 3 command-line arguments (username, ".
142: "password 1, password 2).\n") unless $noprint;
1.4 harris41 143: unlink('/tmp/lock_lcpasswd');
144: exit 2;
145: }
146: else {
147: @input=<>;
1.5 harris41 148: if (@input!=3) {
1.15 harris41 149: print("Error. Three lines should be entered into standard input.\n")
150: unless $noprint;
1.4 harris41 151: unlink('/tmp/lock_lcpasswd');
152: exit 3;
153: }
1.15 harris41 154: map {chomp} @input;
1.4 harris41 155: }
156:
157: my ($username,$password1,$password2)=@input;
158: $username=~/^(\w+)$/;
159: my $safeusername=$1;
1.15 harris41 160: if (($username ne $safeusername) or ($safeusername!~/^[A-Za-z]/)) {
161: print "Error. The user name specified has invalid characters.\n"
162: unless $noprint;
1.8 harris41 163: unlink('/tmp/lock_lcpasswd');
164: exit 9;
165: }
166: my $pbad=0;
1.9 harris41 167: map {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}} (split(//,$password1));
168: map {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}} (split(//,$password2));
1.8 harris41 169: if ($pbad) {
170: print "Error. A password entry had an invalid character.\n";
171: unlink('/tmp/lock_lcpasswd');
172: exit 10;
173: }
1.5 harris41 174:
1.15 harris41 175: # -- Only add user if we can create a brand new home directory (/home/username)
1.7 harris41 176: if (-e "/home/$safeusername") {
177: print "Error. User already exists.\n" unless $noprint;
178: unlink('/tmp/lock_lcpasswd');
1.13 harris41 179: exit 11;
1.7 harris41 180: }
181:
1.15 harris41 182: # -- Only add user if the two password arguments match.
1.5 harris41 183: if ($password1 ne $password2) {
1.6 harris41 184: print "Error. Password mismatch.\n" unless $noprint;
1.5 harris41 185: unlink('/tmp/lock_lcpasswd');
1.13 harris41 186: exit 13;
1.5 harris41 187: }
1.4 harris41 188:
1.15 harris41 189: # ---------------------------------- Start running script with root permissions
1.4 harris41 190: &enable_root_capability;
191:
1.15 harris41 192: # ------------------- Add user and make www a member of the user-specific group
193: # -- Add user
1.5 harris41 194: if (system('/usr/sbin/useradd','-c','LON-CAPA user',$safeusername)) {
1.15 harris41 195: print "Error. Something went wrong with the addition of user ".
196: "\"$safeusername\".\n" unless $noprint;
1.4 harris41 197: unlink('/tmp/lock_lcpasswd');
1.13 harris41 198: exit 12;
1.4 harris41 199: }
1.7 harris41 200:
201: # Make www a member of that user group.
1.17 harris41 202: my $groups=`/usr/bin/groups www` or exit(6);
203: chomp $groups; $groups=~s/^\S+\s+\:\s+//;
204: my @grouplist=split(/\s+/,$groups);
205: my @ugrouplist=grep {!/www|$safeusername/} @grouplist;
206: my $gl=join(',',(@ugrouplist,$safeusername));
207: if (system('/usr/sbin/usermod','-G',$gl,'www')) {
1.15 harris41 208: print "Error. Could not make www a member of the group ".
209: "\"$safeusername\".\n" unless $noprint;
1.5 harris41 210: unlink('/tmp/lock_lcpasswd');
211: exit 6;
212: }
213:
1.15 harris41 214: # ---------------------------------------------------------------- Set password
215: # Set password with lcpasswd (which creates smbpasswd entry).
1.2 harris41 216:
1.15 harris41 217: unlink('/tmp/lock_lcpasswd');
218: &disable_root_capability;
1.16 harris41 219: ($>,$<)=($wwwid,$wwwid);
220: open OUT,"|/home/httpd/perl/lcpasswd";
1.15 harris41 221: print OUT $safeusername;
222: print OUT "\n";
223: print OUT $password1;
224: print OUT "\n";
225: print OUT $password1;
226: print OUT "\n";
227: close OUT;
228: if ($?) {
229: exit 8;
1.8 harris41 230: }
1.18 ! harris41 231: ($>,$<)=($wwwid,0);
1.15 harris41 232: &enable_root_capability;
1.8 harris41 233:
1.15 harris41 234: # ------------------------------ Make final modifications to the user directory
235: # -- Add a public_html file with a stand-in index.html file
1.8 harris41 236:
1.15 harris41 237: # system('/bin/chmod','-R','0660',"/home/$safeusername");
238: system('/bin/chmod','0710',"/home/$safeusername");
1.17 harris41 239: mkdir "/home/$safeusername/public_html",0755;
1.18 ! harris41 240: system('/bin/chmod','02770',"/home/$safeusername/public_html");
1.11 harris41 241: open OUT,">/home/$safeusername/public_html/index.html";
1.8 harris41 242: print OUT<<END;
1.15 harris41 243: <html>
244: <head>
245: <title>$safeusername</title>
246: </head>
247: <body>
248: <h1>$safeusername</h1>
249: <p>
1.8 harris41 250: Learning Online Network
1.15 harris41 251: </p>
252: <p>
1.8 harris41 253: This area provides for:
1.15 harris41 254: </p>
255: <ul>
256: <li>resource construction</li>
257: <li>resource publication</li>
258: <li>record-keeping</li>
1.17 harris41 259: </ul>
260: </body>
261: </html>
1.8 harris41 262: END
263: close OUT;
1.11 harris41 264: system('/bin/chown','-R',"$safeusername:$safeusername","/home/$safeusername");
1.15 harris41 265:
266: # -------------------------------------------------------- Exit script
1.8 harris41 267: &disable_root_capability;
268: exit 0;
1.1 harris41 269:
1.15 harris41 270: # ---------------------------------------------- Have setuid script run as root
1.5 harris41 271: sub enable_root_capability {
272: if ($wwwid==$>) {
273: ($<,$>)=($>,$<);
274: ($(,$))=($),$();
275: }
276: else {
277: # root capability is already enabled
278: }
279: return $>;
280: }
281:
1.15 harris41 282: # ----------------------------------------------- Have setuid script run as www
1.5 harris41 283: sub disable_root_capability {
284: if ($wwwid==$<) {
285: ($<,$>)=($>,$<);
286: ($(,$))=($),$();
287: }
288: else {
289: # root capability is already disabled
290: }
291: }
292:
1.15 harris41 293: # ----------------------- Make sure that another lcpasswd process isn't running
1.5 harris41 294: sub try_to_lock {
295: my ($lockfile)=@_;
296: my $currentpid;
297: my $lastpid;
298: # Do not manipulate lock file as root
299: if ($>==0) {
300: return 0;
301: }
302: # Try to generate lock file.
303: # Wait 3 seconds. If same process id is in
304: # lock file, then assume lock file is stale, and
305: # go ahead. If process id's fluctuate, try
306: # for a maximum of 10 times.
307: for (0..10) {
308: if (-e $lockfile) {
309: open(LOCK,"<$lockfile");
310: $currentpid=<LOCK>;
311: close LOCK;
312: if ($currentpid==$lastpid) {
313: last;
314: }
315: sleep 3;
316: $lastpid=$currentpid;
317: }
318: else {
319: last;
320: }
321: if ($_==10) {
322: return 0;
323: }
324: }
325: open(LOCK,">$lockfile");
326: print LOCK $$;
327: close LOCK;
328: return 1;
329: }
1.16 harris41 330:
331: =head1 NAME
332:
333: lcuseradd - LON-CAPA setuid script to coordinate all actions
334: with adding a user with filesystem privileges (e.g. author)
335:
336: =head1 DESCRIPTION
337:
338: lcuseradd - LON-CAPA setuid script to coordinate all actions
339: with adding a user with filesystem privileges (e.g. author)
340:
341: =head1 README
342:
343: lcuseradd - LON-CAPA setuid script to coordinate all actions
344: with adding a user with filesystem privileges (e.g. author)
345:
346: =head1 PREREQUISITES
347:
348: =head1 COREQUISITES
349:
350: =pod OSNAMES
351:
352: linux
353:
354: =pod SCRIPT CATEGORIES
355:
356: LONCAPA/Administrative
357:
358: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>