#!/usr/bin/perl -wT

=pod

=head1 NAME

changepass.pl - a trivial CGI script for changing passwords

$Ringlet: perl/www/changepass/changepass.pl,v 1.1 2004/06/01 16:21:13 roam Exp $

=head1 DESCRIPTION

Makes an attempt to change a system user's password.

=cut

use strict;

sub get_vars($);
sub change_pass(%);

=pod

=head1 FUNCTIONS

=over 4

=item MAIN

The main routine - examines the parameter string, and if the requireed
parameters are passed in, invokes C<change_pass()>, otherwise invokes
C<display_form()>.

=cut

MAIN:
{
	my ($q, %q);
	
	$q = $ENV{'QUERY_STRING'};
	%q = ();
	%q = get_vars($q) if (defined($q) && $q ne '');
	if (defined($q{'username'}) && defined($q{'password'}) &&
	    defined($q{'submit'})) {
		&change_pass(%q);
	} else {
		&display_form();
	}
	exit(0);
}

=pod

=item get_vars($query_string)

Parses a CGI query string into a hash of var/val pairs.

=cut

sub get_vars($)
{
	my $s = $_[0];
	my %h = ();
	my ($key, $val);

	foreach my $v (split(/&/, $s)) {
		$v =~ s/[+|]/ /g;
		($key, $val) = split(/=/, $v);
		$key =~ s/%([[:xdigit:]]{2})/pack("c",hex($1))/ge;
		$val =~ s/%([[:xdigit:]]{2})/pack("c",hex($1))/ge;
		$h{$key} = $val;
	}
	return %h;
}

=pod

=item display_form()

Display the info entry form.

=cut

sub display_form()
{
	print <<EOF
Content-type: text/html; charset=us-ascii

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
  <title>Password changer</title>
</head>
<body>
  <h1>Password changer</h1>

  <form method="GET">
  <table border="0">
    <tr>
      <td>Username:</td>
      <td><input type="text" name="username"></td>
    </tr>
    <tr>
      <td>Password:</td>
      <td><input type="text" name="password"></td>
    </tr>
    <tr>
      <td><input type="submit" name="submit" value="Change it"></td>
      <td>&nbsp;</td>
    </tr>
  </table>
  </form>
</body>
</html>
EOF
}

=pod

=item change_pass(%data)

Invokes I<sudo(8)> and I<pw(8)> to change the user's password.  Expects
C<$data{'username'}> and C<$data{'password'}> to be defined.

=cut

sub change_pass(%)
{
	my %h = @_;
	my $pid;

	if ($h{'username'} =~ /^([\w\d_.-]+)$/) {
		$h{'username'} = $1;
	} else {
		$h{'username'} = '';
	}
	delete @ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASHENV'};
	if (!defined($pid = open(PW, '|-'))) {
		die("fork(): $!\n");
	} elsif ($pid == 0) {
		exec('/usr/local/bin/sudo', '/usr/sbin/pw', 'usermod', '-n',
		    $h{'username'}, '-h', '0');
		die("exec(): $!\n");
	}
	print PW "$h{password}\n";
	close(PW);
	print <<EOF
Content-type: text/html; charset=us-ascii

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
  <title>Password changer</title>
</head>
<body>
  <h1>Password changer</h1>

  <p>An attempt was made to change the password.  No idea whether we made it
    :)</p>
</html>
EOF
}

=pod

=back

=head1 BUGS

=over 4

=item *

no attempt is made to handle or even detect password change errors;

=item *

no attempt is made to authenticate the user before changing the password;

=item *

the I</usr/local/bin/sudo> and I</usr/sbin/pw> locations are hard-coded;

=item *

I believe Anton wanted I<chfn(1)>, too, but oh well ;)

=back

=cut

