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.16 2001/11/15 19:08:00 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: if (system('/usr/sbin/usermod','-G',$safeusername,'www')) {
203: print "Error. Could not make www a member of the group ".
204: "\"$safeusername\".\n" unless $noprint;
205: unlink('/tmp/lock_lcpasswd');
206: exit 6;
207: }
208:
209: # ---------------------------------------------------------------- Set password
210: # Set password with lcpasswd (which creates smbpasswd entry).
211:
212: unlink('/tmp/lock_lcpasswd');
213: &disable_root_capability;
214: ($>,$<)=($wwwid,$wwwid);
215: open OUT,"|/home/httpd/perl/lcpasswd";
216: print OUT $safeusername;
217: print OUT "\n";
218: print OUT $password1;
219: print OUT "\n";
220: print OUT $password1;
221: print OUT "\n";
222: close OUT;
223: ($>,$<)=($wwwid,0);
224: if ($?) {
225: exit 8;
226: }
227: &enable_root_capability;
228:
229: # ------------------------------ Make final modifications to the user directory
230: # -- Add a public_html file with a stand-in index.html file
231:
232: # system('/bin/chmod','-R','0660',"/home/$safeusername");
233: system('/bin/chmod','0710',"/home/$safeusername");
234: mkdir "/home/$safeusername/public_html",2760;
235: open OUT,">/home/$safeusername/public_html/index.html";
236: print OUT<<END;
237: <html>
238: <head>
239: <title>$safeusername</title>
240: </head>
241: <body>
242: <h1>$safeusername</h1>
243: <p>
244: Learning Online Network
245: </p>
246: <p>
247: This area provides for:
248: </p>
249: <ul>
250: <li>resource construction</li>
251: <li>resource publication</li>
252: <li>record-keeping</li>
253: </UL>
254: </BODY>
255: </HTML>
256: END
257: close OUT;
258: system('/bin/chown','-R',"$safeusername:$safeusername","/home/$safeusername");
259:
260: # -------------------------------------------------------- Exit script
261: &disable_root_capability;
262: exit 0;
263:
264: # ---------------------------------------------- Have setuid script run as root
265: sub enable_root_capability {
266: if ($wwwid==$>) {
267: ($<,$>)=($>,$<);
268: ($(,$))=($),$();
269: }
270: else {
271: # root capability is already enabled
272: }
273: return $>;
274: }
275:
276: # ----------------------------------------------- Have setuid script run as www
277: sub disable_root_capability {
278: if ($wwwid==$<) {
279: ($<,$>)=($>,$<);
280: ($(,$))=($),$();
281: }
282: else {
283: # root capability is already disabled
284: }
285: }
286:
287: # ----------------------- Make sure that another lcpasswd process isn't running
288: sub try_to_lock {
289: my ($lockfile)=@_;
290: my $currentpid;
291: my $lastpid;
292: # Do not manipulate lock file as root
293: if ($>==0) {
294: return 0;
295: }
296: # Try to generate lock file.
297: # Wait 3 seconds. If same process id is in
298: # lock file, then assume lock file is stale, and
299: # go ahead. If process id's fluctuate, try
300: # for a maximum of 10 times.
301: for (0..10) {
302: if (-e $lockfile) {
303: open(LOCK,"<$lockfile");
304: $currentpid=<LOCK>;
305: close LOCK;
306: if ($currentpid==$lastpid) {
307: last;
308: }
309: sleep 3;
310: $lastpid=$currentpid;
311: }
312: else {
313: last;
314: }
315: if ($_==10) {
316: return 0;
317: }
318: }
319: open(LOCK,">$lockfile");
320: print LOCK $$;
321: close LOCK;
322: return 1;
323: }
324:
325: =head1 NAME
326:
327: lcuseradd - LON-CAPA setuid script to coordinate all actions
328: with adding a user with filesystem privileges (e.g. author)
329:
330: =head1 DESCRIPTION
331:
332: lcuseradd - LON-CAPA setuid script to coordinate all actions
333: with adding a user with filesystem privileges (e.g. author)
334:
335: =head1 README
336:
337: lcuseradd - LON-CAPA setuid script to coordinate all actions
338: with adding a user with filesystem privileges (e.g. author)
339:
340: =head1 PREREQUISITES
341:
342: =head1 COREQUISITES
343:
344: =pod OSNAMES
345:
346: linux
347:
348: =pod SCRIPT CATEGORIES
349:
350: LONCAPA/Administrative
351:
352: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>