#!/usr/bin/perl
# The Learning Online Network with CAPA
#
# lcpasswd - LON-CAPA setuid script to synchronously change all
# filesystem-related passwords (samba, unix, etc)
#
# YEAR=2000
# 10/27,10/28,10/29,10/30 Scott Harrison
#
# YEAR=2001
# 10/22,10/23,11/13,11/15 Scott Harrison
#
# $Id: lcpasswd,v 1.14 2002/02/14 22:09:14 harris41 Exp $
###
###############################################################################
## ##
## ORGANIZATION OF THIS PERL SCRIPT ##
## ##
## 1. Description of script ##
## 2. Invoking script (standard input only) ##
## 3. Example usage inside another piece of code ##
## 4. Description of functions ##
## 5. Exit codes ##
## ##
###############################################################################
use strict;
# ------------------------------------------------------- Description of script
#
# This script is a setuid script that should
# 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.
# --------------------------------------- Invoking script (standard input only)
#
# Standard input usage
# First line is USERNAME
# Second line is CURRENT 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).
# User names must begin with an alphabetical character
# (A-Z,a-z).
# ---------------------------------------------------- Description of functions
# enable_root_capability() : have setuid script run as root
# disable_root_capability() : have setuid script run as www
# try_to_lock() : make sure that another lcpasswd process isn't running
# ------------------------------------------------------------------ Exit codes
# These are the exit codes.
# ( (0,"ok"),
# (1,"User ID mismatch. This program must be run as user 'www'"),
# (2,"Error. This program needs 3 command-line arguments (username, old ".
# password, new password)."),
# (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 set password."),
# (9,"Error. The user name specified has invalid characters."),
# (10,"Error. A password entry had an invalid character.") )
# ------------------------------------------------------------- Initializations
# Security
$ENV{'PATH'}='/bin:/usr/bin:/usr/local/sbin:/home/httpd/perl'; # Nullify path
# information
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # nullify potential taints
# Do not print error messages
my $noprint=1;
# ----------------------------- Make sure this process is running from user=www
my $wwwid=getpwnam('www');
&disable_root_capability;
if ($wwwid!=$>) {
print("User ID mismatch. This program must be run as user 'www'\n")
unless $noprint;
exit 1;
}
# ----------------------------------- Start running script with www permissions
&disable_root_capability;
# --------------------------- Handle case of another lcpasswd process (locking)
unless (&try_to_lock('/tmp/lock_lcpasswd')) {
print "Error. Too many other simultaneous password change requests being ".
"made.\n" unless $noprint;
exit 4;
}
# ------- Error-check input, need 3 values (user name, password 1, password 2).
my @input;
@input=<>;
if (@input!=3) {
print("Error. Three lines need to be entered into standard input.\n")
unless $noprint;
unlink('/tmp/lock_lcpasswd');
exit 3;
}
foreach (@input) {chomp;}
my ($username,$password1,$password2)=@input;
$username=~/^(\w+)$/;
my $safeusername=$1;
if (($username ne $safeusername) or ($safeusername!~/^[A-Za-z]/)) {
print "Error. The user name specified has invalid characters.\n";
unlink('/tmp/lock_lcpasswd');
exit 9;
}
my $pbad=0;
foreach (split(//,$password1)) {if (($_<32)&&($_>126)){$pbad=1;}}
foreach (split(//,$password2)) {if (($_<32)&&($_>126)){$pbad=1;}}
if ($pbad) {
print "Error. A password entry had an invalid character.\n";
unlink('/tmp/lock_lcpasswd');
exit 10;
}
# -- Only add user if the two password arguments match.
if ($password1 ne $password2) {
print "Error. Password mismatch.\n" unless $noprint;
unlink('/tmp/lock_lcpasswd');
exit 13;
}
# Verify existence of user
unless(getpwnam($safeusername)) {
print "Error. User $username does not exist.\n" unless $noprint;
unlink('/tmp/lock_lcpasswd');
exit 5;
}
&enable_root_capability;
($>,$<)=(0,0);
open OUT,"|pwchange $safeusername";
print OUT $password1;
print OUT "\n";
close OUT;
($>,$<)=(0,500);
if ($?) {
exit 8;
}
my $userid=getpwnam($safeusername);
if (-e '/usr/bin/smbpasswd') {
($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid
# environment
unless (-e '/etc/smbpasswd') {
open (OUT,'>/etc/smbpasswd'); close OUT;
}
my $smbexist=0;
open (IN, '</etc/smbpasswd');
my @lines=<IN>;
close IN;
for my $l (@lines) {
chop $l;
my @F=split(/\:/,$l);
if ($F[0] eq $username) {$smbexist=1;}
}
unless ($smbexist) {
open(OUT,'>>/etc/smbpasswd');
print OUT join(':',($safeusername,$userid,
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXX'.
'XXXXXXXXXXXXXXXXXX','','/home/'.$safeusername,
'/bin/bash')) . "\n";
close OUT;
}
open(OUT,"|/usr/bin/smbpasswd -s $safeusername>/dev/null") or
die('cannot run smbpasswd');
print OUT $password2; print OUT "\n";
print OUT $password2; print OUT "\n";
close OUT;
$<=$wwwid; # unfool the program
}
&disable_root_capability;
unlink('/tmp/lock_lcpasswd');
exit 0;
# ---------------------------------------------- have setuid script run as root
sub enable_root_capability {
if ($wwwid==$>) {
($<,$>)=($>,$<);
($(,$))=($),$();
}
else {
# root capability is already enabled
}
return $>;
}
# ----------------------------------------------- have setuid script run as www
sub disable_root_capability {
if ($wwwid==$<) {
($<,$>)=($>,$<);
($(,$))=($),$();
}
else {
# root capability is already disabled
}
}
# ----------------------- make sure that another lcpasswd process isn't running
sub try_to_lock {
my ($lockfile)=@_;
my $currentpid;
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) {
if (-e $lockfile) {
open(LOCK,"<$lockfile");
$currentpid=<LOCK>;
close LOCK;
if ($currentpid==$lastpid) {
last;
}
sleep 3;
$lastpid=$currentpid;
}
else {
last;
}
if ($_==10) {
return 0;
}
}
open(LOCK,">$lockfile");
print LOCK $$;
close LOCK;
return 1;
}
=head1 NAME
lcpasswd - LON-CAPA setuid script to synchronously change all
filesystem-related passwords (samba, unix, etc)
=head1 DESCRIPTION
LON-CAPA setuid script to synchronously change all
filesystem-related passwords (samba, unix, etc)
=head1 README
LON-CAPA setuid script to synchronously change all
filesystem-related passwords (samba, unix, etc)
=head1 PREREQUISITES
=head1 COREQUISITES
=pod OSNAMES
linux
=pod SCRIPT CATEGORIES
LONCAPA/Administrative
=cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>