Annotation of loncom/lcnfson, revision 1.4

1.1       harris41    1: #!/usr/bin/perl
                      2: 
                      3: use strict;
                      4: 
1.3       harris41    5: # This script is a setuid script (chmod 6755; chown root:root).
                      6: # It enables nfs/portmap services for a specific user at
                      7: # a specific ip address.
                      8: 
                      9: # Exit codes.  0=ok.  Higher than 0 means something went wrong.
1.2       harris41   10: # Usage within code
                     11: #
                     12: # $exitcode=system("/home/httpd/perl/lcuseradd","NAME","IPADDRESS")/256;
                     13: # print "uh-oh" if $exitcode;
                     14: 
1.1       harris41   15: # Security
                     16: $ENV{'PATH'}=""; # Nullify path information.
                     17: $ENV{'BASH_ENV'}=""; # Nullify shell environment information.
                     18: 
                     19: # Do not print error messages if there are command-line arguments
                     20: my $noprint=0;
                     21: if (@ARGV) {
                     22:     $noprint=1;
                     23: }
                     24: 
                     25: # Read in /etc/passwd, and make sure this process is running from user=www
                     26: open (IN, "</etc/passwd");
                     27: my @lines=<IN>;
                     28: close IN;
                     29: my $wwwid;
                     30: for my $l (@lines) {
                     31:     chop $l;
                     32:     my @F=split(/\:/,$l);
                     33:     if ($F[0] eq 'www') {$wwwid=$F[2];}
                     34: }
                     35: if ($wwwid!=$<) {
                     36:     print("User ID mismatch.  This program must be run as user 'www'\n") unless $noprint;
                     37:     exit 1;
                     38: }
                     39: &disable_root_capability;
                     40: 
                     41: # Handle case of another lcnfs process
                     42: unless (&try_to_lock("/tmp/lock_lcnfs")) {
                     43:     print "Error. Too many other simultaneous nfs change requests being made.\n" unless $noprint;
                     44:     exit 4;
                     45: }
1.2       harris41   46: # Gather input.  Should be 2 values (user name, numeric ip address).
1.1       harris41   47: my @input;
                     48: if (@ARGV==3) {
                     49:     @input=@ARGV;
                     50: }
                     51: elsif (@ARGV) {
1.2       harris41   52:     print("Error. This program needs 2 command-line arguments (username, numeric ip address).\n") unless $noprint;
                     53:     unlink('/tmp/lock_lcnfs');
1.1       harris41   54:     exit 2;
                     55: }
                     56: else {
                     57:     @input=<>;
1.2       harris41   58:     if (@input!=2) {
                     59: 	print("Error. Two lines should be entered into standard input.\n") unless $noprint;
                     60: 	unlink('/tmp/lock_lcnfs');
1.1       harris41   61: 	exit 3;
                     62:     }
                     63:     map {chop} @input;
                     64: }
                     65: 
                     66: my ($username,$ipaddress)=@input;
                     67: $username=~/^(\w+)$/;
                     68: my $safeusername=$1;
                     69: if ($username ne $safeusername) {
                     70:     print "Error. The user name specified has invalid characters.\n";
1.2       harris41   71:     unlink('/tmp/lock_lcnfs');
1.1       harris41   72:     exit 9;
                     73: }
                     74: 
                     75: # Read in /etc/passwd, and make sure this process is running from user=www
                     76: open (IN, "</etc/passwd");
                     77: my @lines=<IN>;
                     78: close IN;
                     79: my $uid;
                     80: my $gid;
                     81: for my $l (@lines) {
                     82:     chop $l;
                     83:     my @F=split(/\:/,$l);
                     84:     if ($F[0] eq $safeusername) {$uid=$F[2]; $gid=$F[3];}
                     85: }
                     86: 
                     87: $ipaddress=~/^([\w|\.]*)$/;
                     88: my $safeipaddress=$1;
                     89: if ($ipaddress ne $safeipaddress) {
                     90:     print "Error. The IP address must be numeric and of the form ##.##.##.##.\n";
1.2       harris41   91:     unlink('/tmp/lock_lcnfs');
1.1       harris41   92:     exit 8;
                     93: }
                     94: 
                     95: &enable_root_capability;
                     96: # Make sure nfs is running, if not, start it
                     97: my $status=`/etc/rc.d/init.d/nfs status`;
                     98: if ($status=~/is stopped/) {
                     99:     system('/etc/rc.d/init.d/nfs start','start');
                    100: }
                    101: 
                    102: # Add entry to /etc/exports
                    103: my $exports=`/bin/cat /etc/exports`; $exports="\n$exports";
1.2       harris41  104: my $entry="/home/$safeusername     $safeipaddress(rw,all_squash,anonuid=$uid,anongid=$gid)\n";
1.1       harris41  105: if ($exports=~/\n\/home\/$safeusername\s+$safeipaddress\(rw,all_squash,anonuid=$uid,anongid=$gid\)/) {
                    106:     print "Error. /etc/exports already has this entry enabled.\n";
1.2       harris41  107:     unlink('/tmp/lock_lcnfs');
1.1       harris41  108:     exit 7;
                    109: }
1.2       harris41  110: open (OUT,">>/etc/exports");
1.1       harris41  111: print OUT $entry;
                    112: close OUT;
                    113: 
                    114: # Resynchronize /etc/exports file
                    115: system('/usr/sbin/exportfs','-r');
                    116: 
                    117: # Add entry /etc/hosts.allow
                    118: my $hostsallow=`/bin/cat /etc/hosts.allow`;
1.2       harris41  119: my $entry="# $safeusername\nportmap: $safeipaddress\n";
                    120: if ($hostsallow=~/\n\# $safeusername\s*\nportmap: $safeipaddress\n/) {
1.1       harris41  121:     print "Error. /etc/hosts already has this entry enabled.\n";
1.2       harris41  122:     unlink('/tmp/lock_lcnfs');
1.1       harris41  123:     exit 6;
                    124: }
                    125: open (OUT,">>/etc/hosts.allow");
                    126: print OUT $entry;
                    127: close OUT;
1.2       harris41  128: 
                    129: &disable_root_capability;
                    130: unlink('/tmp/lock_lcnfs');
                    131: exit 0;
1.1       harris41  132: 
                    133: # ----------------------------------------------------------- have setuid script run as root
                    134: sub enable_root_capability {
                    135:     if ($wwwid==$>) {
                    136: 	($<,$>)=($>,$<);
                    137: 	($(,$))=($),$();
                    138:     }
                    139:     else {
                    140: 	# root capability is already enabled
                    141:     }
                    142:     return $>;
                    143: }
                    144: 
                    145: # ----------------------------------------------------------- have setuid script run as www
                    146: sub disable_root_capability {
                    147:     if ($wwwid==$<) {
                    148: 	($<,$>)=($>,$<);
                    149: 	($(,$))=($),$();
                    150:     }
                    151:     else {
                    152: 	# root capability is already disabled
                    153:     }
                    154: }
                    155: 
                    156: # ----------------------------------- make sure that another lcnfs process isn't running
                    157: sub try_to_lock {
                    158:     my ($lockfile)=@_;
                    159:     my $currentpid;
                    160:     my $lastpid;
                    161:     # Do not manipulate lock file as root
                    162:     if ($>==0) {
                    163: 	return 0;
                    164:     }
                    165:     # Try to generate lock file.
                    166:     # Wait 3 seconds.  If same process id is in
                    167:     # lock file, then assume lock file is stale, and
                    168:     # go ahead.  If process id's fluctuate, try
                    169:     # for a maximum of 10 times.
                    170:     for (0..10) {
                    171: 	if (-e $lockfile) {
                    172: 	    open(LOCK,"<$lockfile");
                    173: 	    $currentpid=<LOCK>;
                    174: 	    close LOCK;
                    175: 	    if ($currentpid==$lastpid) {
                    176: 		last;
                    177: 	    }
                    178: 	    sleep 3;
                    179: 	    $lastpid=$currentpid;
                    180: 	}
                    181: 	else {
                    182: 	    last;
                    183: 	}
                    184: 	if ($_==10) {
                    185: 	    return 0;
                    186: 	}
                    187:     }
                    188:     open(LOCK,">$lockfile");
                    189:     print LOCK $$;
                    190:     close LOCK;
                    191:     return 1;
                    192: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>