Subject: Use of inactive, expire fields in usermgmt
To: None <tech-userlevel@netbsd.org>
From: John Darrow <John.P.Darrow@wheaton.edu>
List: tech-userlevel
Date: 03/30/2000 14:30:24
While attempting to put the usermgmt tools into production on a 1.4.2 box,
I ran across some problems with the expiration feature.  According to the
useradd(8) man page,

     -e expiry-time
             sets the time at which the current password will expire for this
             user.

     -f inactive-secs
             sets the number of seconds after which, if no login has occurred
             for the user during that time, the login will be "locked".

and later
     -e secs-to-expiry
             provides the number of seconds since the epoch (UTC) at which the
             current password change expire. This can be used to implement
             password aging.  A value of 0 can be used to switch off this fea-
             ture.  The default value for this field is 0.  See passwd(5) for
             more details.  This value can be preset for all users by using
             the expire field in the /etc/usermgmt.conf file - it has the for-
             mat: expire <secs-to-expiry>.

In other words, the manual states that "expire" is a _password_ expiration
field, and is in the form of seconds-since-epoch.

However, the code states differently.  First, according to passwd(5) the
format of a master.passwd entry is:

name:password:uid:gid:class:change:expire:gecos:home_dir:shell

where

     The change field is the number of seconds from the epoch, UTC, until the
     password for the account must be changed.  This field may be left empty
     to turn off the password aging feature.  If this is set to ``-1'' then
     the user will be prompted to change their password at the next login.

     The expire field is the number of seconds from the epoch, UTC, until the
     account expires.  This field may be left empty to turn off the account
     aging feature.

(and setting the field to 0 is the equivalent of leaving it empty).

However, the code in user.c writes the following:
        cc = snprintf(buf, sizeof(buf), "%s:%s:%d:%d::%d:%ld:%s:%s:%s\n",
                        login,
                        password,
                        up->u_uid,
                        gid,
                        up->u_inactive,
                        (long) expire,
                        up->u_comment, 
                        home,
                        up->u_shell);

In other words, it uses inactive for the password expiration, and expire
for the _account_ expiration.

Furthermore, the code for expire is inconsistent in and of itself: though
the man page states it's in seconds since the epoch, the program says:

                if (strptime(up->u_expire, "%c", &tm) == (char *) NULL) {  
                        warnx("invalid time format `%s'", optarg);
                } else {
                        expire = mktime(&tm);
                }       

In other words, it wants (from strptime(3)):

      %c    the date and time, using the locale's date and time format.

(in the default locale, this is the same as the output of ctime(3)).
(Note also that the error message is wrong - there's no optarg in this
function, it should be up->u_expire, if anything!)

Now, before I noticed that the expire value is used for the account
expiration instead of password expiration, I had already implemented a
modification which allows the value of expire to be:

UNSET_EXPIRY (currently "Null (unset)"): don't expire, puts 0 in the field.

-1:	force password change upon next login (see passwd(5))

+num:	set expire to be num seconds from now, allowing for relative
	expiration (e.g. "one year from now")

num:	set expire to num seconds from the epoch (absolute expiration)

anything else gets fed through strptime and mktime, as before, to allow
for backward compatibility.

Before I finish the changes and send-pr them, there are issues which need
to be addressed:

1.  NetBSD's master.passwd has no concept of "inactive-secs", in other words,
an amount of time in which, if the account is unused, it is locked.  At
least for the first login, this can be semi-emulated using the password
expiration (if the user doesn't ever log in before their password expires,
then they won't get in after it expires.)  However, this can also catch a
user who does log in, but doesn't change the password.  (What if the user
normally only logs in via ftp, or pop, for example?)

2.  The inconsistent documentation on -e expire.  Should it be account
expiration (as the code uses) or password expiration (as the manual states)?
Both features are desirable.

Code-wise, I could just as easily make -e account expiration and -f password
expiration (as it is now in the code), or vice versa (as the docs lean
toward).  The question is, which way should I do it?  Or should I make
-e password expiration (as documented) and ditch -f completely in favor of
another flag for account expiration?

As another side comment, usermod currently resets both expire and invalid
to the usermgmt.conf default values if they aren't given on the command
line.  It could well be argued that they should be left with whatever is
already in the password file for that user unless the flags are given
explicitly, otherwise if you have a user with non-default values you have
to supply -f and -e flags for them even if you're just trying to use
usermod to change another field (such as gid or home directory)

Comments appreciated.  I'd like to get feedback soon, as I am setting up
some production machines and I would rather not have either local hacks,
or have to later change a bunch of scripts if it is decided to go the other
way with -e/-f than I end up using...

jdarrow

-- 
John Darrow - Senior Technical Specialist               Office: 630/752-5201
Computing Services, Wheaton College, Wheaton, IL 60187  Fax:    630/752-5968
Alphapage: 6303160707@alphapage.airtouch.com            Pager:  630/316-0707
Email:     John.P.Darrow@wheaton.edu