version 1.2, 2000/10/27 22:02:23
|
version 1.9, 2000/10/30 02:31:45
|
Line 1
|
Line 1
|
#!/usr/bin/perl |
#!/usr/bin/perl |
|
# |
|
# lcpasswd |
|
# |
|
# Scott Harrison |
|
# SH: October 27, 2000 |
|
# SH: October 28, 2000 |
|
|
use strict; |
use strict; |
|
|
# Scott Harrison |
|
# October 27, 2000 |
|
|
|
# This script is a setuid script that should |
# This script is a setuid script that should |
# be run by user 'www'. |
# be run by user 'www'. This script allows |
|
# for synchronous entry of passwords into |
|
# both the /etc/passwd and the /etc/smbpasswd |
|
# files. |
|
|
|
# This script works under the same process control mechanism |
|
# as lcuseradd and lcpasswd, to make sure that only one of these |
|
# processes is running at any one time on the system. |
|
|
# Standard input usage |
# Standard input usage |
# First line is USERNAME |
# First line is USERNAME |
# Second line is CURRENT PASSWORD |
# Second line is CURRENT PASSWORD |
# Third line is NEW PASSWORD |
# Third line is NEW PASSWORD |
|
|
|
# Valid passwords must consist of the |
|
# ascii characters within the inclusive |
|
# range of 0x20 (32) to 0x7E (126). |
|
# These characters are: |
|
# SPACE and |
|
# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO |
|
# PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ |
|
|
|
# Valid user names must consist of ascii |
|
# characters that are alphabetical characters |
|
# (A-Z,a-z), numeric (0-9), or the underscore |
|
# mark (_). (Essentially, the perl regex \w). |
|
|
|
# Command-line arguments |
|
# Yes, but be very careful here (don't pass shell commands) |
|
# and this is only supported to allow perl-system calls. |
|
|
|
# Usage within code |
|
# Note: NEVER run as system("/home/httpd/perl/lcpasswd NAME OLDPWD NEWPWD") |
|
# |
|
# $exitcode=system("/home/httpd/perl/lcpasswd","NAME","OLDPWD","NEWPWD")/256; |
|
# print "uh-oh" if $exitcode; |
|
|
|
# These are the exit codes. |
|
# ( (0,"ok"), |
|
# (1,"User ID mismatch. This program must be run as user 'www'"), |
|
# (2,"Error. This program does not accept command-line arguments."), |
|
# (3,"Error. Three lines need to be entered into standard input."), |
|
# (4,"Error. Too many other simultaneous password change requests being made."), |
|
# (5,"Error. User $username does not exist."), |
|
# (6,"Error. Invalid entry of current password."), |
|
# (7,"Error. Root was not successfully enabled."), |
|
# (8,"Error. Cannot open /etc/passwd.") ) |
|
|
# Security |
# Security |
$ENV{'PATH'}="/bin:/usr/bin"; # Nullify path information except for what smbpasswd needs |
$ENV{'PATH'}='/bin:/usr/bin'; # Nullify path information except for what smbpasswd needs |
$ENV{'BASH_ENV'}=""; # Nullify shell environment information. |
$ENV{'BASH_ENV'}=''; # Nullify shell environment information. |
|
|
|
# Do not print error messages if there are command-line arguments |
|
my $noprint=0; |
|
if (@ARGV) { |
|
$noprint=1; |
|
} |
|
|
open (IN, "</etc/passwd"); |
# Read in /etc/passwd, and make sure this process is running from user=www |
|
open (IN, '</etc/passwd'); |
my @lines=<IN>; |
my @lines=<IN>; |
close IN; |
close IN; |
my $wwwid; |
my $wwwid; |
Line 27 for my $l (@lines) {
|
Line 78 for my $l (@lines) {
|
if ($F[0] eq 'www') {$wwwid=$F[2];} |
if ($F[0] eq 'www') {$wwwid=$F[2];} |
} |
} |
if ($wwwid!=$<) { |
if ($wwwid!=$<) { |
print("User ID mismatch. This program must be run as user 'www'\n"); |
print("User ID mismatch. This program must be run as user 'www'\n") unless $noprint; |
exit 0; |
exit 1; |
} |
} |
&disable_root_capability; |
&disable_root_capability; |
if (@ARGV) { |
|
print("Error. This program does not accept command-line arguments.\n"); |
|
exit 0; |
|
} |
|
|
|
# Gather input from standard input. Should only be 3 lines. |
|
my @input=<>; |
|
if (@input!=3) { |
|
print("Error. Three lines need to be entered into standard input.\n"); |
|
exit 0; |
|
} |
|
|
|
# Handle case of another lcpasswd process |
# Handle case of another lcpasswd process |
unless (&try_to_lock("/tmp/lock_lcpasswd")) { |
unless (&try_to_lock('/tmp/lock_lcpasswd')) { |
print "Error. Too many other simultaneous password change requests being made.\n"; |
print "Error. Too many other simultaneous password change requests being made.\n" unless $noprint; |
exit 0; |
exit 4; |
|
} |
|
|
|
# Gather input. Should only be 3 values. |
|
my @input; |
|
if (@ARGV==3) { |
|
@input=@ARGV; |
|
} |
|
elsif (@ARGV) { |
|
print("Error. This program needs 3 command-line arguments (username, old password, new password).\n") unless $noprint; |
|
unlink('/tmp/lock_lcpasswd'); |
|
exit 2; |
|
} |
|
else { |
|
@input=<>; |
|
if (@input!=3) { |
|
print("Error. Three lines need to be entered into standard input.\n") unless $noprint; |
|
unlink('/tmp/lock_lcpasswd'); |
|
exit 3; |
|
} |
|
map {chop} @input; |
} |
} |
|
|
my ($username,$oldpwd,$newpwd)=map {chop; $_} @input; |
my ($username,$oldpwd,$newpwd)=@input; |
|
$username=~/^(\w+)$/; |
|
my $safeusername=$1; |
|
if ($username ne $safeusername) { |
|
print "Error. The user name specified has invalid characters.\n"; |
|
unlink('/tmp/lock_lcpasswd'); |
|
exit 9; |
|
} |
|
my $pbad=0; |
|
map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$oldpwd)); |
|
map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$newpwd)); |
|
if ($pbad) { |
|
print "Error. A password entry had an invalid character.\n"; |
|
unlink('/tmp/lock_lcpasswd'); |
|
exit 10; |
|
} |
|
|
# Grab the line corresponding to username |
# Grab the line corresponding to username |
my ($userid,$useroldcryptpwd); |
my ($userid,$useroldcryptpwd); |
Line 61 for my $l (@lines) {
|
Line 136 for my $l (@lines) {
|
|
|
# Verify existence of user |
# Verify existence of user |
if (!defined($userid)) { |
if (!defined($userid)) { |
print "Error. User $username does not exist.\n"; |
print "Error. User $username does not exist.\n" unless $noprint; |
exit 0; |
unlink('/tmp/lock_lcpasswd'); |
|
exit 5; |
} |
} |
|
|
# Verify password entry |
# Verify password entry |
if (crypt($oldpwd,$useroldcryptpwd) ne $useroldcryptpwd) { |
if (crypt($oldpwd,$useroldcryptpwd) ne $useroldcryptpwd) { |
print "Error. Invalid entry of current password.\n"; |
print "Error. Invalid entry of current password.\n" unless $noprint; |
exit 0; |
unlink('/tmp/lock_lcpasswd'); |
|
exit 6; |
} |
} |
|
|
# Construct new password entry (random salt) |
# Construct new password entry (random salt) |
my $newcryptpwd=crypt($newpwd,(join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64])); |
my $newcryptpwd=crypt($newpwd,(join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64])); |
$U[1]=$newcryptpwd; |
$U[1]=$newcryptpwd; |
my $userline=join(":",@U); |
my $userline=join(':',@U); |
my $rootid=&enable_root_capability; |
my $rootid=&enable_root_capability; |
if ($rootid!=0) { |
if ($rootid!=0) { |
print "Error. Root was not successfully enabled.\n"; |
print "Error. Root was not successfully enabled.\n" unless $noprint; |
exit 0; |
unlink('/tmp/lock_lcpasswd'); |
|
exit 7; |
} |
} |
open PASSWORDFILE, ">/etc/passwd" or die("Cannot open /etc/passwd!"); |
open PASSWORDFILE, '>/etc/passwd' or (print("Error. Cannot open /etc/passwd.\n") && unlink('/tmp/lock_lcpasswd') && exit(8)); |
for my $l (@lines) { |
for my $l (@lines) { |
@F=split(/\:/,$l); |
@F=split(/\:/,$l); |
if ($F[0] eq $username) {print PASSWORDFILE "$userline\n";} |
if ($F[0] eq $username) {print PASSWORDFILE "$userline\n";} |
else {print PASSWORDFILE "$l\n";} |
else {print PASSWORDFILE "$l\n";} |
} |
} |
close PASSWORDFILE; |
close PASSWORDFILE; |
$username=~/^(\w+)$/; |
|
my $safeusername=$1; |
|
($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid environment |
($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid environment |
unless (-e "/etc/smbpasswd") { |
unless (-e '/etc/smbpasswd') { |
open (OUT,">/etc/smbpasswd"); close OUT; |
open (OUT,'>/etc/smbpasswd'); close OUT; |
} |
} |
my $smbexist=0; |
my $smbexist=0; |
open (IN, "</etc/smbpasswd"); |
open (IN, '</etc/smbpasswd'); |
my @lines=<IN>; |
my @lines=<IN>; |
close IN; |
close IN; |
for my $l (@lines) { |
for my $l (@lines) { |
Line 103 for my $l (@lines) {
|
Line 180 for my $l (@lines) {
|
if ($F[0] eq $username) {$smbexist=1;} |
if ($F[0] eq $username) {$smbexist=1;} |
} |
} |
unless ($smbexist) { |
unless ($smbexist) { |
open(OUT,">>/etc/smbpasswd"); |
open(OUT,'>>/etc/smbpasswd'); |
print OUT join(":",($safeusername,$userid,'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX','','/home/'.$safeusername,'/bin/bash')) . "\n"; |
print OUT join(':',($safeusername,$userid,'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX','','/home/'.$safeusername,'/bin/bash')) . "\n"; |
close OUT; |
close OUT; |
} |
} |
open(OUT,"|/usr/bin/smbpasswd -s $safeusername>/dev/null"); |
open(OUT,"|/usr/bin/smbpasswd -s $safeusername>/dev/null"); |
Line 113 print OUT $newpwd; print OUT "\n";
|
Line 190 print OUT $newpwd; print OUT "\n";
|
close OUT; |
close OUT; |
$<=$wwwid; # unfool the program |
$<=$wwwid; # unfool the program |
&disable_root_capability; |
&disable_root_capability; |
unlink("/tmp/lock_lcpasswd"); |
unlink('/tmp/lock_lcpasswd'); |
|
exit 0; |
|
|
|
# ----------------------------------------------------------- have setuid script run as root |
sub enable_root_capability { |
sub enable_root_capability { |
if ($wwwid==$>) { |
if ($wwwid==$>) { |
($<,$>)=($>,$<); |
($<,$>)=($>,$<); |
Line 126 sub enable_root_capability {
|
Line 205 sub enable_root_capability {
|
return $>; |
return $>; |
} |
} |
|
|
|
# ----------------------------------------------------------- have setuid script run as www |
sub disable_root_capability { |
sub disable_root_capability { |
if ($wwwid==$<) { |
if ($wwwid==$<) { |
($<,$>)=($>,$<); |
($<,$>)=($>,$<); |
Line 136 sub disable_root_capability {
|
Line 216 sub disable_root_capability {
|
} |
} |
} |
} |
|
|
|
# ----------------------------------- make sure that another lcpasswd process isn't running |
sub try_to_lock { |
sub try_to_lock { |
my ($lockfile)=@_; |
my ($lockfile)=@_; |
my $currentpid; |
my $currentpid; |
my $lastpid; |
my $lastpid; |
|
# Do not manipulate lock file as root |
|
if ($>==0) { |
|
return 0; |
|
} |
|
# Try to generate lock file. |
|
# Wait 3 seconds. If same process id is in |
|
# lock file, then assume lock file is stale, and |
|
# go ahead. If process id's fluctuate, try |
|
# for a maximum of 10 times. |
for (0..10) { |
for (0..10) { |
if (-e $lockfile) { |
if (-e $lockfile) { |
open(LOCK,"<$lockfile"); |
open(LOCK,"<$lockfile"); |