SUMMARY: Changing Passwords via CGI

From: Ken McKinlay (kmckinla@kan.lmcda.lmco.com)
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 <putsch@unitrode.com> 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
in.

- 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
ftp://ray.ucs.sfu.ca/Public/Account_Management/management.tar and
ftp://ray.ucs.sfu.ca/Public/Account_Management/lib.tar.

Thanks to:
Rik Schneider <rik@netasset.com>
Chris Tubutis <chris@tci.com>
Robert Rose <Robert.Rose@ag.gov.au>
Mick Morgan <sun-managers@open.gov.uk>
Richard Bosire <bosire@ns1.africaonline.co.ke>
agardine@lsil.com <Alistair Gardiner>
Bjorn E. Torsteinsen <Bjorn.Torsteinsen@nfh.uit.no>
Jeff Putsch <putsch@unitrode.com>

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

------------------

#!/usr/local/bin/perl5

# 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',
                -bgcolor=>'white'),
        h1('Change your Unix password');

import_names('Q');

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;
    $OK++;
}

create_form() unless $OK;

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

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 &&
$Q::new2;
    return (undef,"New password fields don't match.") unless $Q::new1 eq
$Q::new2;
    return (undef,"Suspicious user name $Q::user.") unless
$Q::user=~/^\w{3,8}$/;
    return (undef,'Suspiciously long old password.') unless length($Q::old) <
30;
    return (undef,'Suspiciously long new password.') unless length($Q::new1) <
30;
    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,
                        "Password:",
                        "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:",
                        "Sorry.");
    $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
password.");

    # 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.");

    $passwd->hard_close();

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

sub create_form {
    print
        start_form,
        table(
              TR({align=>RIGHT},
                 th('User name'), td(textfield(-name=>'user')),
                 th('Old password'),td(password_field(-name=>'old'))),
              TR({align=>RIGHT},
                 th('New password'),td(password_field(-name=>'new1')),
                 th('Confirm new password'),td(password_field(-name=>'new2'))),
              ),
        p,
        hidden(-name=>'referer',-value=>referer()),
        table(
              TR(
                 td({align=>LEFT},submit('Change Password')),
                 td({align=>RIGHT},reset)),
              ),
        end_form;
}

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