httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Marc Slemko <ma...@znep.com>
Subject Re: basic auth broken
Date Sun, 31 Jan 1999 22:02:32 GMT
On Sun, 31 Jan 1999, Rodent of Unusual Size wrote:

> Eric Cholet wrote:
> > 
> > The recent patch to mod_auth in the CVS source tree broke basic auth on
> > my server. My passwords, generated with htpasswd, all start with "$1" but
> > the code thinks they are MD5 encrypted, whereas they really come
> > from crypt(). Therefore authentication fails for valid passwords.
> > 
> > >uname -rs
> > FreeBSD 2.2.7-RELEASE
> 
> Unless you chose DES when you installed FreeBSD, its crypt() *is*
> producing MD5 passwords.  And that it's prefacing them with '$1'
> indicates that's the case.  So it looks as though the ap_MD5Encode()
> routine and FreeBSD's implementation aren't interoperable, which
> is definitely not good, and something to be investigated this
> week.. :-(

Right, FreeBSD does some extra voodoo to make it harder, ie. take longer,
to do the hash so that brute forcing is harder.

First, we can just copy the way that FreeBSD does it.  Obviously, that 
requires that we pull the password checking code out of each mod_auth_*,
which should probably be done anyway.

Second, we could define our own $n$, although we run into the risk of 
conflict.

However, the question is what if some other system uses some other
$1$ that is also incompatible?  It doesn't matter too much for
FreeBSD since, after all, it is the system password file and moving
it between systems is an exceptional event.  I am tempted to think
that it is better just to have a directive that can be used to
explicitly set the non-default style for a platform.  I don't think
that yet though.

The actual code used (from /usr/src/lib/libcrpt/crypt.c; apparently
under the beerware license) is:

        /* If it starts with the magic string, then skip that */
        if(!strncmp(sp,magic,strlen(magic)))
                sp += strlen(magic);

        /* It stops at the first '$', max 8 chars */
        for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++)
                continue;

        /* get the length of the true salt */
        sl = ep - sp;

        MD5Init(&ctx);

        /* The password first, since that is what is most unknown */
        MD5Update(&ctx,pw,strlen(pw));

        /* Then our magic string */
        MD5Update(&ctx,magic,strlen(magic));

        /* Then the raw salt */
        MD5Update(&ctx,sp,sl);

        /* Then just as many characters of the MD5(pw,salt,pw) */
        MD5Init(&ctx1);
        MD5Update(&ctx1,pw,strlen(pw));
        MD5Update(&ctx1,sp,sl);
        MD5Update(&ctx1,pw,strlen(pw));
        MD5Final(final,&ctx1);
        for(pl = strlen(pw); pl > 0; pl -= 16)
                MD5Update(&ctx,final,pl>16 ? 16 : pl);

        /* Don't leave anything around in vm they could use. */
        memset(final,0,sizeof final);

        /* Then something really weird... */
        for (i = strlen(pw); i ; i >>= 1)
                if(i&1)
                    MD5Update(&ctx, final, 1);
                else
                    MD5Update(&ctx, pw, 1);

        /* Now make the output string */
        strcpy(passwd,magic);
        strncat(passwd,sp,sl);
        strcat(passwd,"$");

        MD5Final(final,&ctx);

        /*
         * and now, just to make sure things don't run too fast
         * On a 60 Mhz Pentium this takes 34 msec, so you would
         * need 30 seconds to build a 1000 entry dictionary...
         */
        for(i=0;i<1000;i++) {
                MD5Init(&ctx1);
                if(i & 1)
                        MD5Update(&ctx1,pw,strlen(pw));
                else
                        MD5Update(&ctx1,final,16);

                if(i % 3)
                        MD5Update(&ctx1,sp,sl);

                if(i % 7)
                        MD5Update(&ctx1,pw,strlen(pw));

                if(i & 1)
                        MD5Update(&ctx1,final,16);
                else
                        MD5Update(&ctx1,pw,strlen(pw));
                MD5Final(final,&ctx1);
        }

        p = passwd + strlen(passwd);

        l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4;
        l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4;
        l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4;
        l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4;
        l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4;
        l =                    final[11]                ; to64(p,l,2); p += 2;
        *p = '\0';

        /* Don't leave anything around in vm they could use. */
        memset(final,0,sizeof final);

        return passwd;



Mime
View raw message