Merge branch 'master' of ssh://down/oryx/aox
[aox:aox.git] / sasl / cram-md5.cpp
1 // Copyright Oryx Mail Systems GmbH. All enquiries to info@oryx.com, please.
2
3 #include "cram-md5.h"
4
5 #include "estring.h"
6 #include "entropy.h"
7 #include "configuration.h"
8 #include "user.h"
9 #include "md5.h"
10
11 #include <time.h>
12
13
14 /*! \class CramMD5 cram-md5.h
15     Implements CRAM-MD5 authentication (RFC 2195)
16
17     We issue a challenge, and expect the client to respond with username
18     and the HMAC-MD5 digest of the challenge keyed with a shared secret.
19     We accept the request only if the digest matches our re-computation
20     based on the stored secret from the database.
21 */
22
23
24 /*! Creates a cram-md5 SASL authentication object on behalf of \a c */
25
26 CramMD5::CramMD5( EventHandler *c )
27     : SaslMechanism( c, SaslMechanism::CramMD5 )
28 {
29 }
30
31
32 EString CramMD5::challenge()
33 {
34     EString hn( Configuration::hostname() );
35     EString random( Entropy::asString( 12 ).e64() );
36
37     if ( hn.isEmpty() || hn.find( '.' ) < 0 )
38         hn = "oryx.invalid";
39
40     challengeSent = "<" + random + "@" + hn + ">";
41
42     return challengeSent;
43 }
44
45
46 void CramMD5::parseResponse( const EString &s )
47 {
48     uint i = s.length();
49     while ( i > 0 && s[i] != ' ' )
50         i--;
51
52     if ( i == 0 ) {
53         log( "Syntax error in client response (no space)" );
54         setState( Failed );
55         return;
56     }
57
58     setLogin( s.mid( 0, i ) );
59     setSecret( s.mid( i+1 ).lower() );
60     setState( Authenticating );
61     execute();
62 }
63
64
65 void CramMD5::verify()
66 {
67     if ( Configuration::toggle( Configuration::AuthAnonymous ) &&
68          user() && user()->login() == "anonymous" )
69         setState( Succeeded );
70     else if ( secret().utf8() ==
71               MD5::HMAC( storedSecret().utf8(), challengeSent ).hex() )
72         setState( Succeeded );
73     else
74         setState( Failed );
75 }
76
77
78 void CramMD5::setChallenge( const EString &s )
79 {
80     challengeSent = s;
81 }