Subject: Re: Password Expiration Change
To: Dave Burgess <burgess@cynjut.netins.net>
From: Simon J. Gerraty <sjg@zen.void.oz.au>
List: current-users
Date: 04/02/1995 16:01:22
> In an effort to help make our systems a little more secure, I have made
> a few simple changes to the passwd program, and the local_passwd.c
> function in particular, that 'turn on' six month passwd expiration.

Good idea.  However, I don't believe it is needed (or appropriate) for
passwd to modify the expire field.  That field is not supposed to be
under the user's control.  Ie. sysadmin wants to creat an account
which no matter what will be come un-usable after the expire date.

Anyway, below is a modified version of Dave's patch.  Apart from not
touching the expire field, it uses the SECSPERDAY etc values from
tzfile.h so you can define CHPASS_DAYS=30 to require changes every
month - if you wanted to - 6 months seems ok to me.

I've also patched login to force a password change by invoking
passwd(1) if pw_change == IMEDIATE_CHANGE (1) (unless of course they
used S/Key to authenticate themselves).  This lets you easily add
accounts that need their password changed on first login.

I've also increased the default warning period to 7 days as the
chances of an average user logging in within 2 days of their password
expring seemed rather remote.  Most users would change their password
after the first warning.  Add -DWARN_DAYS=14 to give 2 weeks warning.

Also below is a patch to su(1) to make it warn of ageing passwords too
and only allow root to su to accounts with expired passwords (or
expired accounts).

Enjoy!

I've shar'd them so you can use Dave's patch rather than mine if you
want... 

#!/bin/sh
# This is a shell archive.
# remove everything above the "#!/bin/sh" line
# and feed to /bin/sh
# Use -c option to overwrite existing files
#
# Contents: 
#	passwd.age.diff
#	login.age.diff
#	su.age.diff
#
# packed by: <sjg@zen.void.oz.au> on Sun Apr  2 15:59:46 EST 1995
#
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f passwd.age.diff -a x$1 != x-c ; then
  echo shar: Will not over-write existing file \"passwd.age.diff\"
else
  echo shar: Extracting \"passwd.age.diff\" \(1577 characters\)
  sed 's/^X//' >passwd.age.diff << '!EOF'
X*** passwd/Makefile.orig	Mon Mar 27 15:08:38 1995
X***************
X*** 9,14 ****
X--- 9,16 ----
X  LDADD+= -lrpcsvc -lcrypt
X  CFLAGS+=-I${.CURDIR} -I${.CURDIR}/../../usr.sbin/vipw -DYP
X  
X+ CFLAGS+= -DPASSWD_CHANGE
X+ 
X  .if defined(KERBEROS5)
X  SRCS+= krb5_passwd.c
X  CFLAGS+= -DKERBEROS5
X*** passwd/local_passwd.c.orig	Sun Dec 25 22:29:16 1994
X***************
X*** 42,47 ****
X--- 42,61 ----
X  #include <stdio.h>
X  #include <string.h>
X  
X+ #ifdef  PASSWD_CHANGE
X+ # undef PASSWD_CHANGE
X+ # include <time.h>
X+ # include <tzfile.h>
X+ 
X+ # ifndef CHPASS_DAYS
X+ #   define CHPASS_DAYS 180			/* 6 months */
X+ # endif
X+ 
X+ # define PASSWD_CHANGE  (CHPASS_DAYS * DAYSPERWEEK * SECSPERDAY) + time((time_t *)NULL) 
X+ #else
X+ # define PASSWD_CHANGE 0
X+ #endif
X+   
X  uid_t uid;
X  
X  char *progname = "passwd";
X***************
X*** 74,85 ****
X  	tfd = pw_tmp();
X  
X  	/*
X! 	 * Get the new password.  Reset passwd change time to zero; when
X  	 * classes are implemented, go and get the "offset" value for this
X  	 * class and reset the timer.
X  	 */
X  	pw->pw_passwd = getnewpasswd(pw);
X! 	pw->pw_change = 0;
X  	pw_copy(pfd, tfd, pw);
X  
X  	if (!pw_mkdb())
X--- 88,99 ----
X  	tfd = pw_tmp();
X  
X  	/*
X! 	 * Get the new password.  Set passwd change time to default; when
X  	 * classes are implemented, go and get the "offset" value for this
X  	 * class and reset the timer.
X  	 */
X  	pw->pw_passwd = getnewpasswd(pw);
X! 	pw->pw_change = PASSWD_CHANGE;
X  	pw_copy(pfd, tfd, pw);
X  
X  	if (!pw_mkdb())
!EOF
  if test 1577 -ne `wc -c < passwd.age.diff`; then
    echo shar: \"passwd.age.diff\" unpacked with wrong size!
  fi
  
fi
if test -f login.age.diff -a x$1 != x-c ; then
  echo shar: Will not over-write existing file \"login.age.diff\"
else
  echo shar: Extracting \"login.age.diff\" \(3720 characters\)
  sed 's/^X//' >login.age.diff << '!EOF'
X*** login/login.c.old	Sun Apr  2 13:05:35 1995
X***************
X*** 83,88 ****
X--- 83,89 ----
X  int	 rootterm __P((char *));
X  void	 sigint __P((int));
X  void	 sleepexit __P((int));
X+ void	do_chpass __P((void));
X  char	*stypeof __P((char *));
X  void	 timedout __P((int));
X  int	 pwcheck __P((char *, char *, char *, char *));
X***************
X*** 112,117 ****
X--- 113,128 ----
X  struct	passwd *pwd;
X  int	failures;
X  char	term[64], *envinit[1], *hostname, *username, *tty;
X+ #ifdef SKEY
X+ int	used_skey = 0;
X+ #endif
X+ 
X+ #ifndef IMEDIATE_CHANGE
X+ # define IMEDIATE_CHANGE 1
X+ #endif
X+ #ifndef WARN_DAYS
X+ # define WARN_DAYS 7				/* give 1 week warning */
X+ #endif
X  
X  int
X  main(argc, argv)
X***************
X*** 128,133 ****
X--- 139,145 ----
X  	char *domain, *p, *salt, *ttyn;
X  	char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
X  	char localhost[MAXHOSTNAMELEN];
X+ 	int need_chpass = 0;
X  	
X  	(void)signal(SIGALRM, timedout);
X  	(void)alarm(timeout);
X***************
X*** 349,359 ****
X  	if (pwd->pw_change || pwd->pw_expire)
X  		(void)gettimeofday(&tp, (struct timezone *)NULL);
X  	if (pwd->pw_change)
X! 		if (tp.tv_sec >= pwd->pw_change) {
X  			(void)printf("Sorry -- your password has expired.\n");
X  			sleepexit(1);
X  		} else if (pwd->pw_change - tp.tv_sec <
X! 		    2 * DAYSPERWEEK * SECSPERDAY && !quietlog)
X  			(void)printf("Warning: your password expires on %s",
X  			    ctime(&pwd->pw_change));
X  	if (pwd->pw_expire)
X--- 361,373 ----
X  	if (pwd->pw_change || pwd->pw_expire)
X  		(void)gettimeofday(&tp, (struct timezone *)NULL);
X  	if (pwd->pw_change)
X! 		if (pwd->pw_change == IMEDIATE_CHANGE) {
X! 			need_chpass = 1;
X! 		} else if (tp.tv_sec >= pwd->pw_change) {
X  			(void)printf("Sorry -- your password has expired.\n");
X  			sleepexit(1);
X  		} else if (pwd->pw_change - tp.tv_sec <
X! 		    WARN_DAYS * DAYSPERWEEK * SECSPERDAY && !quietlog)
X  			(void)printf("Warning: your password expires on %s",
X  			    ctime(&pwd->pw_change));
X  	if (pwd->pw_expire)
X***************
X*** 361,367 ****
X  			(void)printf("Sorry -- your account has expired.\n");
X  			sleepexit(1);
X  		} else if (pwd->pw_expire - tp.tv_sec <
X! 		    2 * DAYSPERWEEK * SECSPERDAY && !quietlog)
X  			(void)printf("Warning: your account expires on %s",
X  			    ctime(&pwd->pw_expire));
X  
X--- 375,381 ----
X  			(void)printf("Sorry -- your account has expired.\n");
X  			sleepexit(1);
X  		} else if (pwd->pw_expire - tp.tv_sec <
X! 		    WARN_DAYS * DAYSPERWEEK * SECSPERDAY && !quietlog)
X  			(void)printf("Warning: your account expires on %s",
X  			    ctime(&pwd->pw_expire));
X  
X***************
X*** 457,462 ****
X--- 471,480 ----
X  	else
X  		(void) setuid(pwd->pw_uid);
X  
X+ 	/* wait until we are un-privileged */
X+ 	if (need_chpass)
X+ 		do_chpass();
X+ 	
X  	execlp(pwd->pw_shell, tbuf, 0);
X  	err(1, "%s", pwd->pw_shell);
X  }
X***************
X*** 471,477 ****
X  			fprintf(stderr, "You have no s/key. ");
X  			return 1;
X  		} else {
X! 			return skey_authenticate(user);
X  		}
X  	}
X  #endif
X--- 489,499 ----
X  			fprintf(stderr, "You have no s/key. ");
X  			return 1;
X  		} else {
X! 			int ret = skey_authenticate(user);
X! 
X! 			if (ret == 0)
X! 				used_skey = 1;
X! 			return ret;
X  		}
X  	}
X  #endif
X***************
X*** 675,678 ****
X--- 697,718 ----
X  {
X  	(void)sleep(5);
X  	exit(eval);
X+ }
X+ 
X+ void
X+ do_chpass()
X+ {
X+ #ifdef SKEY
X+ 	/*
X+ 	 * if user logged on using skey, don't make them
X+ 	 * type a password in...
X+ 	 */
X+ 	if (used_skey) {
X+ 		(void)printf("Warning: your password has expired, change it as soon as possible.\n");
X+ 		return;
X+ 	}
X+ #endif
X+ 	(void)printf("Your password has expired, chose a new one.\n");
X+ 	if (system("passwd") != 0)
X+ 		sleepexit(1);
X  }
!EOF
  if test 3720 -ne `wc -c < login.age.diff`; then
    echo shar: \"login.age.diff\" unpacked with wrong size!
  fi
  
fi
if test -f su.age.diff -a x$1 != x-c ; then
  echo shar: Will not over-write existing file \"su.age.diff\"
else
  echo shar: Extracting \"su.age.diff\" \(1780 characters\)
  sed 's/^X//' >su.age.diff << '!EOF'
X*** su/su.c.~1~	Wed May 25 22:46:15 1994
X***************
X*** 51,56 ****
X--- 51,57 ----
X  #include <pwd.h>
X  #include <grp.h>
X  #include <string.h>
X+ #include <tzfile.h>
X  #include <unistd.h>
X  #include <paths.h>
X  
X***************
X*** 66,71 ****
X--- 67,76 ----
X  #define	ARGSTR	"-flm"
X  #endif
X  
X+ #ifndef WARN_DAYS
X+ # define WARN_DAYS 7				/* give 1 week warning */
X+ #endif
X+ 
X  extern char *crypt();
X  int chshell();
X  
X***************
X*** 79,84 ****
X--- 84,90 ----
X  	register struct passwd *pwd;
X  	register char *p, **g;
X  	struct group *gr;
X+ 	struct timeval tp;
X  	uid_t ruid, getuid();
X  	int asme, ch, asthem, fastlogin, prio;
X  	enum { UNSET, YES, NO } iscsh = UNSET;
X***************
X*** 269,274 ****
X--- 275,307 ----
X  	}
X  			
X  	*np = avshell;
X+ 
X+ 	if (pwd->pw_change || pwd->pw_expire)
X+ 		(void)gettimeofday(&tp, (struct timezone *)NULL);
X+ 	if (pwd->pw_change)
X+ 		if (tp.tv_sec >= pwd->pw_change) {
X+ 			(void)printf("%s -- %s's password has expired.\n",
X+ 				     (ruid ? "Sorry" : "Note"),
X+ 				     user);
X+ 			if (ruid != 0)
X+ 				exit(1);
X+ 		} else if (pwd->pw_change - tp.tv_sec <
X+ 		    WARN_DAYS * DAYSPERWEEK * SECSPERDAY)
X+ 			(void)printf("Warning: %s's password expires on %s",
X+ 				     user,
X+ 				     ctime(&pwd->pw_change));
X+ 	if (pwd->pw_expire)
X+ 		if (tp.tv_sec >= pwd->pw_expire) {
X+ 			(void)printf("%s -- %s's account has expired.\n",
X+ 				     (ruid ? "Sorry" : "Note"),
X+ 				     user);
X+ 			if (ruid != 0)
X+ 				exit(1);
X+ 		} else if (pwd->pw_expire - tp.tv_sec <
X+ 		    WARN_DAYS * DAYSPERWEEK * SECSPERDAY)
X+ 			(void)printf("Warning: %s's account expires on %s",
X+ 				     user,
X+ 				     ctime(&pwd->pw_expire));
X  
X  	if (ruid != 0)
X  		syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
!EOF
  if test 1780 -ne `wc -c < su.age.diff`; then
    echo shar: \"su.age.diff\" unpacked with wrong size!
  fi
  
fi
exit 0