[556] in linux-security and linux-alert archive

home help back first fref pref prev next nref lref last post

Password checking - JFH the way forward ?

daemon@ATHENA.MIT.EDU (Ian Jackson)
Wed Jan 10 13:33:32 1996

Date: Wed, 10 Jan 96 14:02 GMT
From: Ian Jackson <ian@chiark.chu.cam.ac.uk>
To: Linux Security list <linux-security@tarsier.cv.nrao.edu>

[Mod: I'd like to ask that all replies to this post go to the author,
Ian Jackson, rather than to the list.  Early-comers to this list will
probably remember the thread on shadow passwords that Would Not Die.
I'd like to avoid that problem this time (though Ian's post is *not* a
rehash of the old advocacy arguments).  Ian, if you could summarize any
good feedback that you get in response to this and post it to the list
as a follow-up, I'm sure that it would be appreciated.  --Jeff.]

0. Introduction

I've been having a (to my mind) rather unsatisfactory email exchange
with the Linux maintainer of the (JFH) shadow password suite, and I
thought I'd try saying some of the things here that I don't seem to be
expressing well there.

It will be obvious to everyone that the current state of the "API" for
password checking is far from ideal.  I use the quotes because there
is not so much an API for password checking as a set of interfaces for
getting the information needed to do the check yourself.

It seems to me that if we are going to change anything we should
consider carefully where we would like to get to.  We should borrow
(or if necessary design) a good API that is easy to use, and then try
to get it used within the Linux community.

1. Idealisation

Designing a good password-checking API is very simple: what the
programmer wants is a function that tells them whether a password is
correct, and the call needs the username that the password is thought
to belong to.  So we end up with a function:
   int status= check_password(char *username, char *password);
This would transparently support long passwords, if we provide a
#define in <pwd.h> which specifies the length that programs should use
for their internal buffer.

Now, funnily enough someone has already implemented this API: SunOS4
has:
  int pwdauth(char *user, char *password);
According to the manpage this returns "0" for "OK".  It doesn't say
what kind of error returns it gives; I'd propose "1" for "wrong
password" and "-1" (with errno) for "system call failure".  I
therefore propose that this API should be implemented for Linux in the
libc in the obvious way.  The code is pretty trivial.  Sun have
implemented pwdauth by having it do an RPC call to a
hopefully-permanently-running daemon which is the cause of many system
failures, but I'm not suggesting we do it that way.  For reasons best
known to Sun, pwdauth doesn't seem to be present in Solaris 2.

The maximum length of a password should probably be _PASSWORD_LEN,
which is also used by getpass(3).  According to the manpage on my
system that's set to 128 at the moment.

2. Backwards compatibility

There is one small problem with the approach above: if we want to use
it to move towards shadow passwords and longer passwords we have to
change and recompile every program that does password checking.

I have devised a scheme that will allow (practially?) all existing
binaries which do password checking to continue to function in the
presence of shadow passwords.  Programs which set passwords will have
to have a minor change made.

The scheme works by replacing crypt() with a host-specific function
that can only be computed by privileged programs; unprivileged ones
have to get the computation done for them.

The replacement version of crypt functions as follows: it looks at the
first character of the salt.  If it is not a "$" then the usual crypt
function is computed.  If it is a "$" then the second character is
used as an index into a table of host-specific magic secret numbers in
/etc (most hosts would only need one such number; the possibility to
have several is there so that the number can be changed without having
to reset all user passwords).  The crypt function then computes
MD5(<magic-string-from-etc>,<password>)[1], discards all but the first
64 bits and returns them base-64-encoded in the usual fashion of
crypt(3).

[1] <password> here is the argument <key> from the crypt call.

Password-changing programs would need to be modified to look up the
most recent (preferably the first) entry from the magic cookies file,
and to set the salt to "$" followed by its index, so that the crypt
function will use the "shadowed" version of crypt.  If they do not do
this then the program will produce non-shadow passwords.

When crypt was running unprivileged it should contact a
permanently-running daemon via a Unix-domain socket and get it to do
the encryption.  This allows unprivileged programs to check passwords,
without giving them access to the shadowing information.  A daemon is
better than a setuid program, because it is easier to write correctly,
easier for the system administrator to turn off and on, and makes it
easier to limit the rate at which enquiries are made.

Note that the shadowing information is in the per-host keys file, not
in the passwd file, whose format and organisation remain unchanged
apart from the change to the way the encrypted password field is used
by crypt.

If crypt fails to open the key file or contact the daemon it could
perhaps call exit, or do a normal crypt and then set the first
character to a *, or something.  Unfortunately there is no way to
indicate to a program that crypt has failed, because a standard
version of crypt can never fail.

3. JFH's shadow suite

I'm told that JFH's shadow suite has had many of its copyright
problems resolved.  However, that doesn't tell us whether it is a good
thing.

Using JFH-style shadow passwords requires every password checking
program to be changed and recompiled to use getspent instead of
getpwent, and to understand the shadow password mechanism.

So, we have to put in the effort required to change all of these
programs, but we still don't end up with a generalised flexible API
which is independent of the cryptographic or other mechanisms used,
and the next time we want to change something it will all happen
again.

It is true that JFH's shadow suite contains password ageing and other
potentially useful features.  To support these it is simple to retain
the getspent interface of JFH's library, but we must mandate that
programs using getspent do *not* assume that they can check the
password using the information they get from getspent.

4. Conclusions

* We should implement pwdauth() ASAP, and start converting programs
  to use it.

* If we want shadowing on systems where not all of the programs have
  been changed to support pwdauth we should use my scheme above.

* We should support JFH's shadow suite only for password ageing and
  similar features; these are minority applications (and have
  debateable merits in security terms) and so should go on the back
  burner.

Ian.

home help back first fref pref prev next nref lref last post