Hi Steve,
On Tuesday 09 August 2005 20:08, SteveC wrote: > I think that I might have spotted what's causing my problem: > > This Microsoft technet article > > http://www.microsoft.com/technet/prodtechnol/windowsserver2003/library/Tech >Ref/717b450c-f4a0-4cc9-86f4-cc0633aae5f9.mspx > > explains how Microsoft pre-computes and saves certain MD5 hashes based > on combinations of account names (UPN and SamAccountName, DNS names, > fixed strings, and paswords -- in various combinations) [See Digest > Hashes section in the body]. These pre-computed hashes mean that AD > doesn't need to hold passwords in a reversible encryption format.[This > is a new feature in W2k3 -- W2k needs the reversible encryption > apparently if digest authentication is going to be used.] > > In my environment, I have (don't ask my to justify why all the following > non-standard settings!): > - My UPN is [EMAIL PROTECTED] (where BASE is some string, i.e. not a real > fqdn) > - my SamAccountName is name_2 > - the forest root domain is ROOT.BASE > - the AD domain I belong to is XXXX.ROOT.BASE > - the flat domain name is XXXX > > So, given all the above, when the 'A1' hash is calculated the 'realm' > used by DIGEST_MD5 is the one supplied by the domain controller which is > XXXX.ROOT.BASE. If this is the case, then the only entry from the table > in the above technet article that can ever work is if I provide the > SamAccountname -- then row 2 of the table (SamAccountName + > DNSDomainaname + password) will match a pre-computed hash. > > Does that make sense? I don't know if it makes sense, but it seems to match the results of my tests ;-) As long als the server provided realm is used the samAccountname is to be used as the value of the user callback. > If that is the case, then if the SASL & DIGEST_MD5 modules provided a > callback to enable the realm to be overridden, a user could provide one > of the Microsoft 'standard' values (in the case of a perverse > configuration like mine!) and things would work with the 'preferred' > UPN. [It still doesn't solve my auth-int/auth-conf follow-on > requirements however!] I have extended the patch to Authen::SASL 2.09. It now - corrects the un-escaping behaviour when reading the challenge - checks for required fields (according to the RFC) - allows for qop not to be sent froj the server (according to the RFC) - corrects the digest-uri bug - adds a callback for the realm Graham, please add this patch to the Authen-SASL SVN (alternatively you may give my pmarschall account write access to the Authen-SASL SVN in addition to the perl-ldap SVN so that I can do it myself) Peter -- Peter Marschall eMail: [EMAIL PROTECTED]
--- lib/Authen/SASL/Perl/DIGEST_MD5.pm +++ lib/Authen/SASL/Perl/DIGEST_MD5.pm 2005-08-07 18:41:14.000000000 +0200 @@ -43,16 +43,24 @@ while($challenge =~ s/^(?:\s*,)?\s*(\w+)=("([^\\"]+|\\.)*"|[^,]+)\s*//) { my ($k, $v) = ($1,$2); if ($v =~ /^"(.*)"$/s) { - ($v = $1) =~ s/\\//g; + ($v = $1) =~ s/\\(.)/$1/g; } $sparams{$k} = $v; } + # TODO: detect multiple occurrence of fields: fail when forbidden return $self->set_error("Bad challenge: '$challenge'") if length $challenge; - return $self->set_error("Server does not support auth (qop = $sparams{'qop'})") - unless grep { /^auth$/ } split(/,/, $sparams{'qop'}); + # qop in server challenge is optional: if not there "auth" is assumed + return $self->set_error("Server does not support auth (qop = $sparams{qop})") + if ($sparams{qop} && ! grep { /^auth$/ } split(/,/, $sparams{qop})); + + # check required fields in server challenge: nonce, algorithm + foreach (qw/nonce algorithm/) { + return $self->set_error("Server did not provide required field $_ in challenge") + if (!defined $sparams{$_}); + } my %response = ( nonce => $sparams{'nonce'}, @@ -61,11 +69,13 @@ nonce => $sparams{'nonce'}, cnonce => md5_hex($CNONCE || join (":", $$, time, rand)), 'digest-uri' => $self->service . '/' . $self->host, - qop => 'auth', + qop => 'auth', # we currently support 'auth' only + # calc how often the server nonce has been seen; server expects "00000001" nc => sprintf("%08d", ++$self->{nonce}{$sparams{'nonce'}}), charset => $sparams{'charset'}, ); + # let caller-provided fields override defaults: authorization ID, service name, realm my $authzid = $self->_call('authname'); if (defined $authzid) { $response{authzid} = $authzid; @@ -73,8 +83,14 @@ my $serv_name = $self->_call('serv'); if (defined $serv_name) { - $response{'digest_uri'} .= '/' . $serv_name; + $response{'digest-uri'} .= '/' . $serv_name; + } + + my $realm = $self->_call('realm'); + if (defined $realm) { + $response{realm} = $realm; } + # else TODO: use one of the realms provided by the server my $password = $self->_call('pass');