Annotation of loncom/lcuserdel, revision 1.15
1.1 harris41 1: #!/usr/bin/perl
2: #
3: # lcuserdel
4: #
5:
6: use strict;
7:
1.2 harris41 8: # This script is a setuid script (chmod 6755) that should
1.1 harris41 9: # be run by user 'www'. It DOES NOT delete directories.
10: # All it does is remove a user's entries from
11: # /etc/passwd, /etc/groups, and /etc/smbpasswd.
1.5 harris41 12: # It also disables user directory access by making the directory
13: # to be owned by user=www (as opposed to the former "username").
14: # This command only returns an error if it is
15: # invoked incorrectly (by passing bad command-line arguments, etc).
1.1 harris41 16:
1.3 harris41 17: # This script works under the same process control mechanism
18: # as lcuseradd and lcpasswd, to make sure that only one of these
19: # processes is running at any one time on the system.
1.1 harris41 20:
21: # Standard input usage
22: # First line is USERNAME
23:
1.10 harris41 24: # Valid user names must consist of ascii
25: # characters that are alphabetical characters
26: # (A-Z,a-z), numeric (0-9), or the underscore
27: # mark (_). (Essentially, the perl regex \w).
28:
1.1 harris41 29: # Command-line arguments [USERNAME]
30: # Yes, but be very careful here (don't pass shell commands)
31: # and this is only supported to allow perl-system calls.
32:
1.2 harris41 33: # Usage within code
34: #
1.3 harris41 35: # $exitcode=system("/home/httpd/perl/lcuserdel","NAME")/256;
1.2 harris41 36: # print "uh-oh" if $exitcode;
37:
38: # These are the exit codes.
1.9 harris41 39: # ( (0,"ok"),
40: # (1,"User ID mismatch. This program must be run as user 'www'"),
1.11 harris41 41: # (2,"Error. This program needs just 1 command-line argument (username).") )
1.9 harris41 42: # (3,"Error. Only one line should be entered into standard input."),
1.11 harris41 43: # (4,"Error. Too many other simultaneous password change requests being made."),
44: # (5,"Error. The user name specified has invalid characters.") )
1.2 harris41 45:
1.1 harris41 46: # Security
47: $ENV{'PATH'}=""; # Nullify path information.
48: $ENV{'BASH_ENV'}=""; # Nullify shell environment information.
1.2 harris41 49:
50: # Do not print error messages if there are command-line arguments
51: my $noprint=0;
52: if (@ARGV) {
53: $noprint=1;
54: }
55:
1.3 harris41 56: # Read in /etc/passwd, and make sure this process is running from user=www
1.2 harris41 57: open (IN, "</etc/passwd");
58: my @lines=<IN>;
59: close IN;
60: my $wwwid;
61: for my $l (@lines) {
62: chop $l;
63: my @F=split(/\:/,$l);
64: if ($F[0] eq 'www') {$wwwid=$F[2];}
65: }
66: if ($wwwid!=$<) {
67: print("User ID mismatch. This program must be run as user 'www'\n") unless $noprint;
68: exit 1;
69: }
70: &disable_root_capability;
71:
1.3 harris41 72: # Handle case of another lcpasswd process
73: unless (&try_to_lock("/tmp/lock_lcpasswd")) {
74: print "Error. Too many other simultaneous password change requests being made.\n" unless $noprint;
75: exit 4;
76: }
77:
78: # Gather input. Should only be 1 value (user name).
1.2 harris41 79: my @input;
1.3 harris41 80: if (@ARGV==1) {
1.2 harris41 81: @input=@ARGV;
82: }
83: elsif (@ARGV) {
1.3 harris41 84: print("Error. This program needs just 1 command-line argument (username).\n") unless $noprint;
1.9 harris41 85: unlink('/tmp/lock_lcpasswd');
1.2 harris41 86: exit 2;
87: }
88: else {
89: @input=<>;
1.3 harris41 90: if (@input!=1) {
91: print("Error. Only one line should be entered into standard input.\n") unless $noprint;
1.9 harris41 92: unlink('/tmp/lock_lcpasswd');
1.2 harris41 93: exit 3;
94: }
95: map {chop} @input;
96: }
1.4 harris41 97:
98: my ($username)=@input;
99: $username=~/^(\w+)$/;
100: my $safeusername=$1;
1.10 harris41 101: if ($username ne $safeusername) {
102: print "Error. The user name specified has invalid characters.\n";
103: unlink('/tmp/lock_lcpasswd');
1.11 harris41 104: exit 5;
1.10 harris41 105: }
1.4 harris41 106:
1.5 harris41 107: &enable_root_capability;
108:
1.4 harris41 109: # By using the system userdel command:
110: # Remove entry from /etc/passwd if it exists
111: # Remove entry from /etc/groups if it exists
1.8 harris41 112: # I surround with groupdel command to make absolutely sure the group definition disappears.
1.12 harris41 113: system('/usr/sbin/groupdel',$safeusername); # ignore error message
114: system('/usr/sbin/userdel',$safeusername); # ignore error message
115: system('/usr/sbin/groupdel',$safeusername); # ignore error message
1.4 harris41 116:
117: # Remove entry from /etc/smbpasswd if it exists
1.13 foxr 118: # the safest way to do this is with smbpasswd -x
119: # as that's independent of location of the smbpasswd file.
120: #
121: if (-e '/usr/bin/smbpasswd') {
122: ($>,$<) = (0,0); # fool smbpasswd to think this is not setuid.
123: system('/usr/bin/smbpasswd -x '.$safeusername);
1.14 foxr 124: $< = $wwwid;
1.13 foxr 125: }
1.4 harris41 126:
127:
128: # Change ownership on directory from username:username to www:www
129: # This prevents subsequently added users from having access.
130:
1.5 harris41 131: system('/bin/chown','-R','www:www',"/home/$safeusername");
1.3 harris41 132:
133: &disable_root_capability;
134: unlink("/tmp/lock_lcpasswd");
135: exit 0;
136:
137: # ----------------------------------------------------------- have setuid script run as root
138: sub enable_root_capability {
139: if ($wwwid==$>) {
140: ($<,$>)=($>,$<);
141: ($(,$))=($),$();
142: }
143: else {
144: # root capability is already enabled
145: }
146: return $>;
147: }
148:
149: # ----------------------------------------------------------- have setuid script run as www
150: sub disable_root_capability {
151: if ($wwwid==$<) {
152: ($<,$>)=($>,$<);
153: ($(,$))=($),$();
154: }
155: else {
156: # root capability is already disabled
157: }
1.2 harris41 158: }
159:
1.3 harris41 160: # ----------------------------------- make sure that another lcpasswd process isn't running
161: sub try_to_lock {
162: my ($lockfile)=@_;
163: my $currentpid;
164: my $lastpid;
165: # Do not manipulate lock file as root
166: if ($>==0) {
167: return 0;
168: }
169: # Try to generate lock file.
170: # Wait 3 seconds. If same process id is in
171: # lock file, then assume lock file is stale, and
172: # go ahead. If process id's fluctuate, try
173: # for a maximum of 10 times.
174: for (0..10) {
175: if (-e $lockfile) {
176: open(LOCK,"<$lockfile");
177: $currentpid=<LOCK>;
178: close LOCK;
179: if ($currentpid==$lastpid) {
180: last;
181: }
182: sleep 3;
183: $lastpid=$currentpid;
184: }
185: else {
186: last;
187: }
188: if ($_==10) {
189: return 0;
190: }
191: }
192: open(LOCK,">$lockfile");
193: print LOCK $$;
194: close LOCK;
195: return 1;
196: }
1.2 harris41 197:
1.1 harris41 198:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>