[comp.lang.perl] CPWI - Strange side effects in perl program!

rcpt@tuegate.tue.nl (Piet Tutelaers) (06/09/90)

Last week I tried to do my programming in Perl instead of Bourne shell.
I like the language and you NEED some practice after all :-). 

I wanted a program that could be installed as the login shell for a user
so that when he logs in he is forced to change his password immediately. 
When he does so he get's back his original shell which is derived from a
copy of the passwd file.  When I tested the perl-program, shown at the
end, I was unpleasantly surprised that the program had a NASTY SIDE
EFFECT.  For the user `rctest' I had put the `/someplace/cpwi' shell in
/etc/passwd, his originally shell saved in /etc/opasswd was the
/bin/csh.  When rctest logged in, and changed his password, he gets the
the default shell (!) AND in the opasswd file this /bin/chs changes to cpwi!

/etc/passwd (before login of rctest):
rctest:hisoldpasswd:UID:GID:Test:/his/home:/someplace/cpwi
/etc/opasswd (before login of rctest):
rctest:hisoldpasswd:UID:GID:Test:/his/home:/bin/csh
/etc/passwd (after login of rctest):
rctest:hisnewpasswd:UID:GID:Test:/his/home:
                                           ^^ Why? 
/etc/opasswd (after login of rctest):
rctest:hisoldpasswd:UID:GID:Test:/his/home:/someplace/cpwi
                                           ^^^^^^^^^^^^^^^ Why? 
The logfile showed:
 9 Jun 18:04.03 ** rctest ** password changed, shell `'.

Am I doing something stupid in my perl program? In the mean time we have
written a Bourne shell script, but I would like to know what can be the
reason for this behaviour. Which perl-expert shines his light on this?

I have to mention the fact that I used `perl -u cpwi' and `undump' to create
a compiled version op cpwi, because I need the group ID facility to
write into the logfile. The version of `chsh' we have on Ultrix3.1 does not
allow a user to change to a shell other than the default or some tolerated
shells (csh). Lastly, show is a stripped version of more(1) which does not
allow shell escapes and jumping into an editor. Perhaps this can be done
also easily in perl?

Piet Tutelaers
rcpt@urc.tue.nl

----------cpwi-----------------------------------------------------
#!/usr/bin/perl

#
# Perl script to force a user to change his password at login time. After
# changing the password the loginshell, being this command, will be
# restored to the value obtained from /etc/oldpasswd. 
#

$LOGFILE="/etc/pwlog";
$OLDPASSWD="/etc/opasswd";
$changed_pw=0;

&message; 			# give some explanation

($user, $encrypt, $UID, $GID, $quota, $comment, $gecos, $home, $shell)
   = getpwuid("$<");

system "/bin/passwd", $user;

($newuser, $newencrypt) = getpwuid("$<");

if ($encrypt ne $newencrypt) {
   $changed_pw=1;
   open(oldpasswd, $OLDPASSWD) || die "Can't open $OLDPASSWD\n";
   while (<oldpasswd>) {
      chop;
      ($USER, $ENCRYPT, $uid, $gid, $GECOS, $HOME, $SHELL) = split(/:/);
      last if ($USER eq $user);
   }
   close(oldpasswd);
   if ("$USER:$uid:$gid:$HOME" eq "$user:$UID:$GID:$home") {
      if ($SHELL eq "/bin/csh") {
         system '/usr/ucb/chsh',$user,$SHELL;
      }
      else {
         system '/usr/ucb/chsh',$user;
      }
   }
}
else {
   system '/bin/echo', '## YOU DID NOT CHANGE YOUR PASSORD! ##';
}

($user, $encrypt, $UID, $GID, $quota, $comment, $gecos, $home, $shell)
   = getpwuid("$<");

@mo = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
($usec,$umin,$uhour,$umday,$umon) = localtime(time);

open(logfile, ">>$LOGFILE") || die "Can't open $LOGFILE\n";
printf logfile "%2d %s %02d:%02d.%02d", $umday, $mo[$umon],$uhour,$umin,$usec;

if ($changed_pw) {
   print logfile " ** $user ** password changed, shell `$shell'.\n";
}
else {
   print logfile " ** $user ** password not changed!\n";
}
close(logfile);

system '/bin/echo', '## YOU WILL NOW BE LOGGED OUT, LOGIN AGAIN. ##';

sub message {
   system 'someplace/show', '/someplace/cpwi.txt';
}

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (06/12/90)

In article <1778@tuegate.tue.nl> rcpt@tuegate.tue.nl (Piet Tutelaers) writes:
: 
: Last week I tried to do my programming in Perl instead of Bourne shell.
: I like the language and you NEED some practice after all :-). 
: 
: I wanted a program that could be installed as the login shell for a user
: so that when he logs in he is forced to change his password immediately. 
: When he does so he get's back his original shell which is derived from a
: copy of the passwd file.  When I tested the perl-program, shown at the
: end, I was unpleasantly surprised that the program had a NASTY SIDE
: EFFECT.  For the user `rctest' I had put the `/someplace/cpwi' shell in
: /etc/passwd, his originally shell saved in /etc/opasswd was the
: /bin/csh.  When rctest logged in, and changed his password, he gets the
: the default shell (!) AND in the opasswd file this /bin/chs changes to cpwi!
: 
: /etc/passwd (before login of rctest):
: rctest:hisoldpasswd:UID:GID:Test:/his/home:/someplace/cpwi
: /etc/opasswd (before login of rctest):
: rctest:hisoldpasswd:UID:GID:Test:/his/home:/bin/csh
: /etc/passwd (after login of rctest):
: rctest:hisnewpasswd:UID:GID:Test:/his/home:
:                                            ^^ Why? 
: /etc/opasswd (after login of rctest):
: rctest:hisoldpasswd:UID:GID:Test:/his/home:/someplace/cpwi
:                                            ^^^^^^^^^^^^^^^ Why? 
: The logfile showed:
:  9 Jun 18:04.03 ** rctest ** password changed, shell `'.
: 
: Am I doing something stupid in my perl program? In the mean time we have
: written a Bourne shell script, but I would like to know what can be the
: reason for this behaviour. Which perl-expert shines his light on this?

It looks kind of like something you're calling from the script is copying
passwd to opasswd.  Also, it seems a bit odd to say

:       if ($SHELL eq "/bin/csh") {
:          system '/usr/ucb/chsh',$user,$SHELL;
:       }
:       else {
:          system '/usr/ucb/chsh',$user;
:       }

Did you mean ne instead of eq?

How does your chsh behave with no shell argument?

Another possible problem on some systems, though apparently not on yours, 
is that you're calling getpwuid($<) both before and after running /bin/passwd
without calling endpwent(), which might cause problems on a system that
caches pw entries.

I've been doing something like this for a long time on our machine, but
I remember the shell they had by encoding it into the name of the replacement
script, rather than trying to keep your opasswd file in sync for everyone,
which seems to me to be next to impossible.  On our system, when accounts
expire, you get shells like /usr/etc/age.{sh,csh,bash,ksh}.  Our passwd
program (written in Perl) then can change them back to the corresponding
real shell.  Someday I'm going to make it generally available.

Larry

rcpt@tuegate.tue.nl (Piet Tutelaers) (06/12/90)

|>It looks kind of like something you're calling from the script is copying
|>passwd to opasswd.  ...............................
You hit the nail on his head! (Attempt to translate the Dutch: `Je sloeg de
spijker op zijn kop'.) This is indeed the explanation for all troubles. 
Sorry that we did not see this ourselfes and suspected your perl.

|>
|>How does your chsh behave with no shell argument?

The chsh without a shell argument returns the default shell (/bin/sh) on
Ultrix3.1 at least.

|>
|>...........................................  On our system, when accounts
|>expire, you get shells like /usr/etc/age.{sh,csh,bash,ksh}.  Our passwd
|>program (written in Perl) then can change them back to the corresponding
|>real shell.  Someday I'm going to make it generally available.

Would be greatly appreciated! I am also looking forward for your book about
perl! 

--- Piet
rcpt@urc.tue.nl