1: #!/usr/bin/perl
2:
3: # The Learning Online Network with CAPA
4: #
5: # lcuseradd - LON-CAPA setuid script to coordinate all actions
6: # with adding a user with filesystem privileges (e.g. author)
7: #
8: # YEAR=2000
9: # 10/27,10/29,10/30 Scott Harrison
10: # YEAR=2001
11: # 10/21,11/13,11/15 Scott Harrison
12: #
13: # $Id: lcuseradd,v 1.17 2001/11/15 22:38:12 harris41 Exp $
14: ###
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: ###############################################################################
37:
38: use strict;
39:
40: # ------------------------------------------------------- Description of script
41: #
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.
45: # It adds a user to the unix system.
46: # Passwords are set with lcpasswd.
47: # www becomes a member of this user group.
48:
49: # -------------- Invoking script (standard input versus command-line arguments)
50: #
51: # Standard input (STDIN) usage
52: # First line is USERNAME
53: # Second line is PASSWORD
54: # Third line is PASSWORD
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: #
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{|}~
67: #
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).
72: # User names must begin with an alphabetical character
73: # (A-Z,a-z).
74:
75: # ---------------------------------- Example usage inside another piece of code
76: # Usage within code
77: #
78: # $exitcode=
79: # system("/home/httpd/perl/lcuseradd","NAME","PASSWORD1","PASSWORD2")/256;
80: # print "uh-oh" if $exitcode;
81:
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
88: # These are the exit codes.
89: # ( (0,"ok"),
90: # (1,"User ID mismatch. This program must be run as user 'www'"),
91: # (2,"Error. This program needs 3 command-line arguments (username, ".
92: # "password 1, password 2)."),
93: # (3,"Error. Three lines should be entered into standard input."),
94: # (4,"Error. Too many other simultaneous password change requests being ".
95: # "made."),
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.),
99: # (8,"Error. Cannot set password."),
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.),
103: # (12,"Error. Something went wrong with the addition of user ".
104: # "\"$safeusername\"."),
105: # (13,"Error. Password mismatch."),
106:
107: # ------------------------------------------------------------- Initializations
108: # Security
109: $ENV{'PATH'}='/bin/:/usr/bin:/usr/local/sbin:/home/httpd/perl'; # Nullify path
110: # information
111: delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # nullify potential taints
112:
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;
122: exit 1;
123: }
124:
125: # ----------------------------------- Start running script with www permissions
126: &disable_root_capability;
127:
128: # --------------------------- Handle case of another lcpasswd process (locking)
129: unless (&try_to_lock("/tmp/lock_lcpasswd")) {
130: print "Error. Too many other simultaneous password change requests being ".
131: "made.\n" unless $noprint;
132: exit 4;
133: }
134:
135: # ------- Error-check input, need 3 values (user name, password 1, password 2).
136: my @input;
137: if (@ARGV==3) {
138: @input=@ARGV;
139: }
140: elsif (@ARGV) {
141: print("Error. This program needs 3 command-line arguments (username, ".
142: "password 1, password 2).\n") unless $noprint;
143: unlink('/tmp/lock_lcpasswd');
144: exit 2;
145: }
146: else {
147: @input=<>;
148: if (@input!=3) {
149: print("Error. Three lines should be entered into standard input.\n")
150: unless $noprint;
151: unlink('/tmp/lock_lcpasswd');
152: exit 3;
153: }
154: map {chomp} @input;
155: }
156:
157: my ($username,$password1,$password2)=@input;
158: $username=~/^(\w+)$/;
159: my $safeusername=$1;
160: if (($username ne $safeusername) or ($safeusername!~/^[A-Za-z]/)) {
161: print "Error. The user name specified has invalid characters.\n"
162: unless $noprint;
163: unlink('/tmp/lock_lcpasswd');
164: exit 9;
165: }
166: my $pbad=0;
167: map {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}} (split(//,$password1));
168: map {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}} (split(//,$password2));
169: if ($pbad) {
170: print "Error. A password entry had an invalid character.\n";
171: unlink('/tmp/lock_lcpasswd');
172: exit 10;
173: }
174:
175: # -- Only add user if we can create a brand new home directory (/home/username)
176: if (-e "/home/$safeusername") {
177: print "Error. User already exists.\n" unless $noprint;
178: unlink('/tmp/lock_lcpasswd');
179: exit 11;
180: }
181:
182: # -- Only add user if the two password arguments match.
183: if ($password1 ne $password2) {
184: print "Error. Password mismatch.\n" unless $noprint;
185: unlink('/tmp/lock_lcpasswd');
186: exit 13;
187: }
188:
189: # ---------------------------------- Start running script with root permissions
190: &enable_root_capability;
191:
192: # ------------------- Add user and make www a member of the user-specific group
193: # -- Add user
194: if (system('/usr/sbin/useradd','-c','LON-CAPA user',$safeusername)) {
195: print "Error. Something went wrong with the addition of user ".
196: "\"$safeusername\".\n" unless $noprint;
197: unlink('/tmp/lock_lcpasswd');
198: exit 12;
199: }
200:
201: # Make www a member of that user group.
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')) {
208: print "Error. Could not make www a member of the group ".
209: "\"$safeusername\".\n" unless $noprint;
210: unlink('/tmp/lock_lcpasswd');
211: exit 6;
212: }
213:
214: # ---------------------------------------------------------------- Set password
215: # Set password with lcpasswd (which creates smbpasswd entry).
216:
217: unlink('/tmp/lock_lcpasswd');
218: &disable_root_capability;
219: ($>,$<)=($wwwid,$wwwid);
220: open OUT,"|/home/httpd/perl/lcpasswd";
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: ($>,$<)=($wwwid,0);
229: if ($?) {
230: exit 8;
231: }
232: &enable_root_capability;
233:
234: # ------------------------------ Make final modifications to the user directory
235: # -- Add a public_html file with a stand-in index.html file
236:
237: # system('/bin/chmod','-R','0660',"/home/$safeusername");
238: system('/bin/chmod','0710',"/home/$safeusername");
239: mkdir "/home/$safeusername/public_html",0755;
240: system('/bin/chmod','2770',"/home/$safeusername/public_html");
241: open OUT,">/home/$safeusername/public_html/index.html";
242: print OUT<<END;
243: <html>
244: <head>
245: <title>$safeusername</title>
246: </head>
247: <body>
248: <h1>$safeusername</h1>
249: <p>
250: Learning Online Network
251: </p>
252: <p>
253: This area provides for:
254: </p>
255: <ul>
256: <li>resource construction</li>
257: <li>resource publication</li>
258: <li>record-keeping</li>
259: </ul>
260: </body>
261: </html>
262: END
263: close OUT;
264: system('/bin/chown','-R',"$safeusername:$safeusername","/home/$safeusername");
265:
266: # -------------------------------------------------------- Exit script
267: &disable_root_capability;
268: exit 0;
269:
270: # ---------------------------------------------- Have setuid script run as root
271: sub enable_root_capability {
272: if ($wwwid==$>) {
273: ($<,$>)=($>,$<);
274: ($(,$))=($),$();
275: }
276: else {
277: # root capability is already enabled
278: }
279: return $>;
280: }
281:
282: # ----------------------------------------------- Have setuid script run as www
283: sub disable_root_capability {
284: if ($wwwid==$<) {
285: ($<,$>)=($>,$<);
286: ($(,$))=($),$();
287: }
288: else {
289: # root capability is already disabled
290: }
291: }
292:
293: # ----------------------- Make sure that another lcpasswd process isn't running
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: }
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>