Thayne Harbaugh <[EMAIL PROTECTED]> writes:

> /etc/shadow contains userids (which correlate to /etc/passwd),
> encrytped passwords, and password dates/days for how often
> passwords have to and can be changed, when users are warned
> about their passwords needing changing, and when accounts
> are forcibly expired.  "man chage" and "man shadow".

I have tried to get this right, based on the manpages for "shadow" and
"getspnam" on a Solaris box. Some questions I could not find answers
for in the docs:

o What values will I find in the shadowpwd struct if there is no
  expiration date and no limit on password age? My guess is zero.

o What about timezones? Are the absolute "days" values in sp_lstchg
  and sp_expire in GMT or localtime? For now, I'm assuming GMT and if
  that's wrong I hope a few hours off isn't too bad.

I would be grateful if you could review the new code (do_lookup_user()
in unix_user.c) and tell me if I have gotten the shadow stuff right.
The function is included below, the rest of the code is at
http://www.lysator.liu.se/~nisse/lsh/src as usual.

/* This method filters out accounts that are known to be disabled
 * (i.e. root, or shadow style expiration). However, it may still
 * return some disabled accounts.
 *
 * An account that is disabled in /etc/passwd should have a value for
 * the login shell that prevents login; replacing the passwd field
 * only doesn't prevent login using publickey authentication. */
static struct user *
do_lookup_user(struct user_db *s,
               struct lsh_string *name, int free)
{
  CAST(unix_user_db, self, s);
  
  struct passwd *passwd;
  
  name = make_cstring(name, free);
  
  if (!name)
    return NULL;
  
  if ((passwd = getpwnam(name->data))
      /* Check for root login */
      && (passwd->pw_uid || self->allow_root))
    {      
      char *crypted;
  
#if HAVE_GETSPNAM
      /* FIXME: What's the most portable way to test for shadow passwords?
       * A single character in the passwd field should cover most variants. */
      if (passwd->pw_passwd && (strlen(passwd->pw_passwd) == 1))
        {
          struct spwd *shadowpwd;

          /* Current day number since January 1, 1970.
           *
           * FIXME: Which timezone is used in the /etc/shadow file? */
          long now = time(NULL) / (3600 * 24);
          
          if (!(shadowpwd = getspnam(name->data)))
            goto fail;

          /* FIXME: I'm assuming that zero means there's no expiry
           * date. */
          if (shadowpwd->sp_expire && (now > shadowpwd->sp_expire))
            {
              werror("Access denied for user '%pS', account expired.\n", name);
              goto fail;
            }
                             
          /* FIXME: I'm assuming that zero means that there is no
           * restriction on password age. */
          if (shadowpwd->sp_max &&
              (now > (shadowpwd->sp_lstchg +  shadowpwd->sp_max)))
            {
              werror("Access denied for user '%pS', password too old.\n", name);
              goto fail;
            }

          /* FIXME: We could look at sp_warn and figure out if it is
           * appropriate to send an SSH_MSG_USERAUTH_PASSWD_CHANGEREQ
           * message. */

          crypted = shadowpwd->sp_pwdp;
        }
      else
#endif /* HAVE_GETSPNAM */
        crypted = passwd->pw_passwd;
  
      return make_unix_user(name,
                            passwd->pw_uid, passwd->pw_gid,
                            crypted,
                            passwd->pw_dir, passwd->pw_shell);
    }
  else
    {
    fail:
      lsh_string_free(name);
      return NULL;
    }
}

Regards,
/Niels

Reply via email to