#!/perl/bin/perl -T # Change shebang as appropiate # IBU Repair Password Management (irs-pass.pl) ####################################### # Copyright ALARIS Medical Systems 2002 # Version 1.01 17 July 2002 # GNU GPL terms of distribution ####################################### use warnings; # DISABLE this on IIS in production use diagnostics; # DISABLE in production use strict; # use lib '.'; # Taint mode removes this from @INC use 5.6.1; # Start with a recent version of Perl use IRS_Utils; use CGI; # Does all the smarts use CGI::Carp qw/fatalsToBrowser/; # Deal with error messages properly use Digest::SHA1; # To create the SHA1 password hashes use Fcntl qw(:DEFAULT :flock); # Get the File System constants use Net::SMTP; # Send an email if we can use subs qw(load_password_file password_form user_lock make_password_digest save_password_file local_sanity_checks verify_password open_and_die send_email); # ##### # START # ##### cgi_settings; my $q = CGI->new; my %settings = %{get_settings("pass")}; local_sanity_checks $q; my $user = $q->param("REMOTE_USER"); tad $q, "Runtime error: No username specified." unless $user; my $old_password = $q->param("password1"); my $new_password_1 = $q->param("password2"); my $new_password_2 = $q->param("password3"); if ($old_password && $new_password_1 && $new_password_2) { tad $q, "The new passwords are not the same.\n
Please try again." unless $new_password_1 eq $new_password_2; tad $q, "The new password is the same as the old one.\n
Please try again." if $new_password_1 eq $old_password; verify_password $new_password_1, $q; user_lock $q, $settings{write_lock}; my ($password_list, $file_digest) = load_password_file $q, $user, $settings{htpassfile}; my $old_digest = make_password_digest($old_password); open_and_die $q, "Old password does not match the password in the file." unless $old_digest eq $file_digest; my $new_digest = make_password_digest($new_password_1); my @new_password_list = grep !/^$user/, @$password_list; push @new_password_list, ($user . ":{SHA}" . $new_digest . "="); save_password_file $q, $settings{htpassfile}, \@new_password_list; user_lock $q, $settings{write_lock}, 'open'; print $q->header, $q->hr, $q->h1("Password Changed."), $q->hr, $q->p("You should close your browser down and restart it."); $q->hr; send_email $q, $user, $new_password_1, $settings{smtp_server} if $settings{smtp_server}; } else { password_form } clean_exit; # ------------- # Sanity checks # ------------- sub local_sanity_checks { my $q = shift; tad $q, "Runtime error: Can only be run via CGI." unless $q->request_method; tad $q, "The Administrator has locked the password file.\n
Please try again later." if -e $settings{admin_lock}; tad $q, "Configuration error: The password file has not been configured" unless $settings{htpassfile}; tad $q, "Runtime error: The specified password file is missing." unless -e $settings{htpassfile}; tad $q, "Runtime error: The password file is empty." if -z _; tad $q, "Runtime error: Unable to read password file." unless -r _; tad $q, "Runtime error: Unable to write to password file." unless -w _; } # ---------------------- # Load the Password File # ---------------------- sub load_password_file { my $q = shift; my $user = shift; my $file = shift; open PASS_FILE, "<", $file or tad $q, "Runtime error: Unable to open password file."; my @people = ; close PASS_FILE; my @person = grep /^$user/, @people; open_and_die $q, "Runtime error: Syntax error in password file." if @person > 1; open_and_die $q, "User $user is not in the password file.\n
Please contact admin for a username." if @person < 1; my ($name, $digest) = split /}/, $person[0]; chomp $digest; chop $digest; return \@people, $digest; } # ---------------------------------- # Save the Password file out to disk # ---------------------------------- sub save_password_file { my $q = shift; my $filename = shift; my $p_list = shift; my $new_filename = $filename . ".new"; my $old_filename = $filename . ".old"; open PASS_FILE, ">", $new_filename or tad $q, "Runtime error: Unable to write to password file"; flock PASS_FILE, LOCK_EX; foreach (@$p_list) { print PASS_FILE; } print PASS_FILE "\n"; flock PASS_FILE, LOCK_UN; close PASS_FILE; open_and_die $q, "The Administrator has locked the password file.\n
Please try again later." if -e $settings{admin_lock}; rename $filename, $old_filename; rename $new_filename, $filename; } # ------------------------------------- # Verify that a password is good enough # ------------------------------------- sub verify_password { my $password = shift; my $q = shift; tad $q, "Password is not long enough.\n
Please use 6 characters or more." if (length $password) < 6; tad $q, "Password is not strong enough.\n
Please use some chatacters other than just letters and numbers." unless $password =~ /\W/; } # ---------------------------------------- # Make a SHA1 Password Digest for the user # ---------------------------------------- sub make_password_digest{ my $ctx = Digest::SHA1->new; $ctx->add(shift); return $ctx->b64digest; } # ------------------------- # Print out a password form # ------------------------- sub password_form { print $q->header, $q->start_form(-method => 'POST', -name => 'form', -action => '/repair/ibu-repair-passman.html'), $q->table({-class => 'form'}, $q->Tr($q->th("Old Password:"), $q->td($q->password_field(-name => 'password1', -size => '15', -maxlength => '30'))), $q->Tr($q->th("New Password:"), $q->td($q->password_field(-name => 'password2', -size => '15', -maxlength => '30'))), $q->Tr($q->th("New Password:"), $q->td($q->password_field(-name => 'password3', -size => '15', -maxlength => '30'))), $q->Tr($q->td({-class => 'submit', -colspan => '2'},$q->submit(-name => 'submit', -value => 'Change Password')))), $q->hidden(-name => 'cache', -value => 'off'), $q->end_form, } # ------------------------ # Open and close user lock # ------------------------ sub user_lock { my $q = shift; my $file = shift; my $open = shift; if ($open) { unlink $file if -e $file; } else { my $escape; while (-e $file) { tad $q, "The password file has been locked by another user.\n
Please try again shortly." if $escape >= 10; $escape++; sleep (1); } sysopen (LOCK_FILE, $file, O_WRONLY | O_EXCL |O_CREAT) or tad $q, "The password file has been locked by another user.\n
Please try again shortly."; close LOCK_FILE; } } # ----------------------- # Open Lock and Terminate # ----------------------- sub open_and_die { my $q = shift; my $message = shift; user_lock $q, $settings{write_lock}, 'open'; tad $q, $message; } # ------------------------- # Send a confirmation email # ------------------------- sub send_email { my $q = shift; my $user = shift; my $pass = shift; my $server = shift; my $address= lc($user) . "\@alarismed.com"; my $smtp = Net::SMTP->new($server, Timeout => 30); if ($smtp && $smtp->mail($address) && $smtp->to($address) && $smtp->data()) { $smtp->datasend("Subject: IBU Service Management Password Change Confirmation\n" . "To: $address\n\n" . "User: $user\n" . "New Password: $pass\n\n"); $smtp->dataend(); $smtp->quit; } }