I just stumbled across the mod_auth(nz)_external project of yours. A very
good idea!
However, I am surprised about the rationale for the lack of support for
Digest auth, and seemingly also of the intent to ever support it:
| Mod_authnz_external does not work with digest authentication. It is
| unlikely that anyone would actually want to do this. In digest
| authentication, the password is one-way encrypted before it is sent by
| the browser to the http server. It is only possible to check the
| validity of that password, if the password database contains either
| plain text passwords or passwords encrypted by exactly the method
| defined in the digest authentication standard. [...]
| So digest authentication could not be used with most reasonable
| authentication databases (storing plain text passwords is not
| reasonable). [...]
Actually, _I_ am interested in Digest support. I can very well see
storing passwords encrypted according to the Digest standard in a
database just for the benefit of a database's superior manageability
compared to the usual flat files.
What would it take to add Digest support to mod_auth(nz)_external,
accepting the necessary limitations of course?
-Julian
How would digest authentication work in mod_authnz_external? (No, I'm
not going to retro-fit this into mod_auth_external. That would be much
more work.)
First, authenticators would be a bit different. Instead of being given
a login and a password and returning a YES/NO flag, they would be given
a login and a realm, and would return a hashed string. The requirement
for the autheniticator to return a value is new. We'll need to set up
an output pipe when we run the authenticator so mod_auth_external can
read the return string. Luckily there are already hooks in the code to
support this, so is shouldn't be hard to add.
We'll need to devise a bit of protocol for communicating with these new
authenticators. Probably you write two newline-terminated lines to it:
login
realm
If the user/realm is found, the authenticator writes back the hash
string, newline terminated, and exits with status 0. If the user is not
found, it exits with any non-zero status code. Maybe we'll allow it to
write an error message to standard output in that case, which we will
log. So long as we have an output pipe open, we may as well use it.
Obviously the authenticators themselves will have to be quite different.
They'll fall into two categories: (1) those who keep plain text
passwords in the database, and (2) those that keep passwords encrypted
according to the digest authentication standards. The first (greviously
insecure) class will use "login" to look up the password in the
database, then encrypt the string "login:realm:password" and return it.
The second class will use "login" and "realm" to look up the hash string
in the database and just return it unmodified.
Internally to mod_authnz_external we'd need to add a new function
similar to authn_external_check_password() called, probably,
authn_external_get_realm_hash() which takes the same arguments as the
get_realm_hash() function in mod_auth_file.c. The hook where this is to
be tied in is already in mod_authnz_external.c, just ifdefed out. It
currently looks like this:
static const authn_provider authn_external_provider =
{
&authn_external_check_password,
#if 0
&authn_external_get_realm_hash
#else
NULL /* No support for digest authentication at this time */
#endif
};
So the only coding to do is to write authn_external_get_realm_hash(),
which shouldn't be very hard. It will be a little fancier than
authn_external_check_password(), since it needs to read the output
string from the authenticator, but it should otherwise be fairly
similar. Then we need to write some test authenticators, do some
testing, and write the documentation.
Having spent some time figuring out (again) what needs to be done, I'm a
little tempted to just go ahead and do it. But, I still fundamentally
don't believe that anyone would really want this. I just can see the
scenario. I see two alternatives:
(1) Your database has plain text passwords in it. Then you can't
really be serious about security, so why the heck are you fooling
around with digest authentication?
(2) Your database has passwords stored as the MD5 encryption of
"user:realm:password". Then obviously you designed your database
specifically for digest authentication, so why didn't you just
put it in a flat file or dbm file or in one of the other formats
already supported by Apache modules?
I'm not really all that convinced that digest authentication is that
great a solution. It's got some negatives. For one thing, the
"password" file is a much more sensitive object that it usually would
be. Anyone who steals it can immediately impersonate any user with no
extra work. If they stole a traditional password file they'd have to
decrypt the passwords before they could do anyone any good. Really, I
think basic authentication with HTTPS style encryption is a much more
secure solution in every respect.
Hmmm...I suppose the sensitivity of the password file under digest
authentication could be a reason to use mod_auth_external. With
mod_authnz_external it would be possible to make the database unreadable
by apache, thus making it harder to grab via some hack targetted against
apache. So mod_authnz_external could partially cover one of digest
authentication's weaknesses. But HTTPS is still better. Unless you
count the security-by-obscurity aspect, which maybe you should.
So, I guess the other thing that it would take to do this would be for
me to be convinced that this was a useful enough feature to justify the
(admittedly small) extra clutter it would add to the code base, and the
time to implement it.
- Jan