SUMMARY: Changing Passwords via CGI

From: Ken McKinlay (
Date: Wed Oct 21 1998 - 14:25:38 CDT

>I have just inherited a large group of new UNIX "illiterate" users
>that will be working under a restricted environment. Instead of
>attempting to train/teach these people how to change their password
>and giving them access to the shell, does any one have a CGI script
>that allows one to change their password via the web ?

Sorry it has taken so long but this was item 10 on the to do list and
I had to make sure I could also get a version working for HP-UX 10.2
and for NIS. Included at the bottom is the Perl 5 source for the the
Solaris passwd version. If you want a Solaris NIS or HP-UX 10.2
version let me know.

The program is originally from the Perl Journal, Summer 98 written by
Lincoln D Stein. I have modified it to use the expect module instead
of the chat2 module as he mentioned in the article.

Many thanks to Jeff Putsch <> for pointing me to
the article.

Other suggestions were discarded for various reasons:

- Set the shell for passwd. Unfortunately the users still have to log

- Use Expect. I sort of do in this script by using the expect module.

- Use an interface to the popassd program called wwwpass. Since I
didn't want another daemon to mess around with I didn't use this
avenue. Also the program set found at and

Thanks to:
Rik Schneider <>
Chris Tubutis <>
Robert Rose <>
Mick Morgan <>
Richard Bosire <> <Alistair Gardiner>
Bjorn E. Torsteinsen <>
Jeff Putsch <>

Ken McKinlay
Unix Administrator
Lockheed Martin Canada, Kanata
(613) 599-3280 x861



# preliminaries to satisfy taint checks
$ENV{PATH} = '/bin:/usr/bin';
$ENV{IFS} = '';

# Prevent buffering problems
$| = 1;

use CGI qw/:standard :html3/;

# display the HTML header
print header,
        start_html(-title=>'Change Unix Password',
        h1('Change your Unix password');


TRY: {
    last TRY unless $Q::user;
    my ($rv,$msg) = check_consistency();
    do_error($msg),last TRY unless $rv;

    ($rv,$msg) = set_passwd($Q::user,$Q::old,$Q::new1);
    do_error($msg),last TRY unless $rv;

    print $msg;

create_form() unless $OK;

        a({href=>"$Q::referer" || referer() },"[ EXIT SCRIPT]"),
        a({href=>'/'},'Home page'),

sub check_consistency {
    return (undef,'Please fill in the user name field.') unless $Q::user;
    return (undef,'Please fill in the old password field.') unless $Q::old;
    return (undef,'Please fill in the new password fields.') unless $Q::new1 &&
    return (undef,"New password fields don't match.") unless $Q::new1 eq
    return (undef,"Suspicious user name $Q::user.") unless
    return (undef,'Suspiciously long old password.') unless length($Q::old) <
    return (undef,'Suspiciously long new password.') unless length($Q::new1) <
    my $uid = (getpwnam($Q::user))[2];
    return (undef,"Unknown user name $Q::user.") if $uid eq '';
    return (undef,"Can't use this script to set root password.") if $uid == 0;
    return 1;

sub set_passwd ($$$) {
    use Expect;

    $Expect::Log_Stdout = 0;

    my $TIMEOUT = 2;
    my $PASSWD = "/bin/passwd";
    my $SU = '/bin/su';

    my($user,$old,$new) = @_;

    # spawn the su command using expect
    my $passwd = Expect->spawn("$SU $user -c \"$PASSWD $user\"") || return
(undef,"Couldn't open $SU $user -c $PASSWD");

    # wait for su to prompt for password
    my $rv = $passwd->expect($TIMEOUT,
                        "su: Unknown id: $user");
    $rv == 2 && return (undef,"User $user unknown.");
    $rv || return (undef,"Didn't get su password prompt.");

    # send the password for the user
    print $passwd "$old\r";

    # wait for passwd to prompt for old password
    $rv = $passwd->expect($TIMEOUT,
                        "Old password:",
                        "su: Sorry");
    $rv == 2 && return (undef,"Old password is incorrect.");
    $rv || return (undef,"Didn't get prompt for old password.");

    # print old password
    print $passwd "$old\r";
    $rv = $passwd->expect($TIMEOUT,
                        "New password:",
    $rv == 2 && return (undef,"Old password is incorrect.");
    $rv || return (undef,"Timed out without seeing prompt for new password.");

    # print new password
    print $passwd "$new\r";
    $rv = $passwd->expect($TIMEOUT,
                        "Re-enter new password:",
                        "Please use a longer password.",
                        "Passwords must differ by at least 3 positions",
                        "Password must contain at least two alphabetic
characters and at least one numeric or special character.");
    $rv == 2 && return (undef,"Please use a longer password.");
    $rv == 3 && return (undef,"Passwords must differ by at least 3 positions.");
    $rv == 4 && return (undef,"Password must contain at least two alphabetic
characters and at least one numeric or special character.");
    $rv || return (undef,"Timed out without seeing second prompt for new

    # reconfirm password
    print $passwd "$new\r";
    $rv = $passwd->expect($TIMEOUT,
                        "They don't match; try again.");
    $rv == 1 && return (undef,"Second password doesn't match.");


    return (1,"Password changed successfully for $Q::user.");

sub create_form {
                 th('User name'), td(textfield(-name=>'user')),
                 th('Old password'),td(password_field(-name=>'old'))),
                 th('New password'),td(password_field(-name=>'new1')),
                 th('Confirm new password'),td(password_field(-name=>'new2'))),
                 td({align=>LEFT},submit('Change Password')),

sub do_error ($) {
    print "<CENTER><FONT COLOR='red' SIZE='+1'>";
    print b('Error: '),shift," Password is not changed.";
    print "</FONT></CENTER>";

This archive was generated by hypermail 2.1.2 : Fri Sep 28 2001 - 23:12:51 CDT