Included Oliver Tappe's DIGEST-MD5 support and fixes to the CRAM-MD5 mechanism.

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11176 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Nathan Whitehorn 2005-01-31 23:15:51 +00:00
parent bf8c5a0440
commit e4b4e8f554
2 changed files with 186 additions and 15 deletions

View File

@ -11,6 +11,7 @@
#include <Entry.h>
#include <Path.h>
#include <cctype>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
@ -24,6 +25,8 @@
#include <crypt.h>
#include <unistd.h>
#include <map>
#include "smtp.h"
#ifndef USESSL
#include "md5.h"
@ -127,10 +130,96 @@ MD5HexHmac(char *hexdigest,
{
unsigned char digest[16];
int i;
unsigned char c;
MD5Hmac(digest, text, text_len, key, key_len);
for (i = 0; i < 16; i++)
sprintf(hexdigest + 2 * i, "%02x", digest[i]);
for (i = 0; i < 16; i++) {
c = digest[i];
*hexdigest++ = (c > 0x9F ? 'a'-10 : '0')+(c>>4);
*hexdigest++ = ((c&0x0F) > 9 ? 'a'-10 : '0')+(c&0x0F);
}
*hexdigest = '\0';
}
/*
** Function: MD5Sum
** generates an MD5-sum from the given string
*/
void
MD5Sum (char* sum, unsigned char *text, int text_len) {
MD5_CTX context;
MD5_Init(&context);
MD5_Update(&context, text, text_len);
MD5_Final((unsigned char*)sum, &context);
sum[16] = '\0';
}
/*
** Function: MD5Digest
** generates an MD5-digest from the given string
*/
void MD5Digest (char* hexdigest, unsigned char *text, int text_len) {
int i;
unsigned char digest[17];
unsigned char c;
MD5Sum((char*)digest, text, text_len);
for (i = 0; i < 16; i++) {
c = digest[i];
*hexdigest++ = (c > 0x9F ? 'a'-10 : '0')+(c>>4);
*hexdigest++ = ((c&0x0F) > 9 ? 'a'-10 : '0')+(c&0x0F);
}
*hexdigest = '\0';
}
/*
** Function: SplitChallengeIntoMap
** splits a challenge-string into the given map (see RFC-2831)
*/
// :
static bool
SplitChallengeIntoMap(BString str, map<BString,BString>& m)
{
m.clear();
const char* key;
const char* val;
char* s = (char*)str.String();
while(*s != 0) {
while(isspace(*s))
s++;
key = s;
while(isalpha(*s))
s++;
if (*s != '=')
return false;
*s++ = '\0';
while(isspace(*s))
s++;
if (*s=='"') {
val = ++s;
while(*s!='"') {
if (*s == 0)
return false;
s++;
}
*s++ = '\0';
} else {
val = s;
while(*s!=0 && *s!=',' && !isspace(*s))
s++;
if (*s != 0)
*s++ = '\0';
}
m[key] = val;
while(isspace(*s))
s++;
if (*s != ',')
return false;
s++;
}
return true;
}
@ -372,8 +461,10 @@ SMTPProtocol::Open(const char *address, int port, bool esmtp)
fAuthType |= PLAIN;
if(::strstr(p, "CRAM-MD5"))
fAuthType |= CRAM_MD5;
if(::strstr(p, "DIGEST-MD5"))
if(::strstr(p, "DIGEST-MD5")) {
fAuthType |= DIGEST_MD5;
fServerName = address;
}
}
}
return B_OK;
@ -465,16 +556,92 @@ SMTPProtocol::Login(const char *_login, const char *password)
int32 loginlen = ::strlen(login);
int32 passlen = ::strlen(password);
if (fAuthType & DIGEST_MD5) {
//******* DIGEST-MD5 Authentication ( tested. works fine [with Cyrus SASL] )
// this implements only the subpart of DIGEST-MD5 which is
// required for authentication to SMTP-servers. Integrity-
// and confidentiality-protection are not implemented, as
// they are provided by the use of OpenSSL.
SendCommand("AUTH DIGEST-MD5"CRLF);
const char *res = fLog.String();
if (strncmp(res, "334", 3) != 0)
return B_ERROR;
int32 baselen = ::strlen(&res[4]);
char *base = new char[baselen+1];
baselen = ::decode_base64(base, &res[4], baselen);
base[baselen] = '\0';
D(bug("base: %s\n", base));
map<BString,BString> challengeMap;
SplitChallengeIntoMap(base, challengeMap);
BString rawResponse = BString("username=") << '"' << login << '"';
rawResponse << ",realm=" << '"' << challengeMap["realm"] << '"';
rawResponse << ",nonce=" << '"' << challengeMap["nonce"] << '"';
rawResponse << ",nc=00000001";
char temp[33];
for( int i=0; i<32; ++i)
temp[i] = 1+(rand()%254);
temp[33] = '\0';
BString rawCnonce(temp);
BString cnonce;
char* cnoncePtr = cnonce.LockBuffer(rawCnonce.Length()*2);
baselen = ::encode_base64(cnoncePtr, rawCnonce.String(), rawCnonce.Length(), true /* headerMode */);
cnoncePtr[baselen] = '\0';
cnonce.UnlockBuffer(baselen);
rawResponse << ",cnonce=" << '"' << cnonce << '"';
rawResponse << ",qop=auth";
BString digestUriValue = BString("smtp/") << fServerName;
rawResponse << ",digest-uri=" << '"' << digestUriValue << '"';
char sum[17], hex_digest2[33];
BString a1,a2,kd;
BString t1 = BString(login) << ":"
<< challengeMap["realm"] << ":"
<< password;
MD5Sum(sum, (unsigned char*)t1.String(), t1.Length());
a1 << sum << ":" << challengeMap["nonce"] << ":" << cnonce;
MD5Digest(hex_digest, (unsigned char*)a1.String(), a1.Length());
a2 << "AUTHENTICATE:" << digestUriValue;
MD5Digest(hex_digest2, (unsigned char*)a2.String(), a2.Length());
kd << hex_digest << ':' << challengeMap["nonce"]
<< ":" << "00000001" << ':' << cnonce << ':' << "auth"
<< ':' << hex_digest2;
MD5Digest(hex_digest, (unsigned char*)kd.String(), kd.Length());
rawResponse << ",response=" << hex_digest;
rawResponse << ",charset=utf-8";
BString postResponse;
char *resp = postResponse.LockBuffer(rawResponse.Length() * 2 + 10);
baselen = ::encode_base64(resp, rawResponse.String(), rawResponse.Length(), true /* headerMode */);
resp[baselen] = 0;
postResponse.UnlockBuffer();
postResponse.Append(CRLF);
SendCommand(postResponse.String());
res = fLog.String();
if (atol(res) >= 500)
return B_ERROR;
// actually, we are supposed to check the rspauth sent back
// by the SMTP-server, but that isn't strictly required,
// so we skip that for now.
SendCommand(CRLF); // finish off authentication
res = fLog.String();
if (atol(res) < 500)
return B_OK;
}
if (fAuthType & CRAM_MD5) {
//******* CRAM-MD5 Authentication ( not tested yet.)
//******* CRAM-MD5 Authentication ( tested. works fine [with Cyrus SASL] )
SendCommand("AUTH CRAM-MD5"CRLF);
const char *res = fLog.String();
if (strncmp(res, "334", 3) != 0)
return B_ERROR;
char *base = new char[::strlen(&res[4])+1];
int32 baselen = ::strlen(base);
baselen = ::decode_base64(base, base, baselen);
int32 baselen = ::strlen(&res[4]);
char *base = new char[baselen+1];
baselen = ::decode_base64(base, &res[4], baselen);
base[baselen] = '\0';
D(bug("base: %s\n", base));
@ -501,10 +668,6 @@ SMTPProtocol::Login(const char *_login, const char *password)
if (atol(res) < 500)
return B_OK;
}
if (fAuthType & DIGEST_MD5) {
//******* DIGEST-MD5 Authentication ( not written yet..)
fLog = "DIGEST-MD5 Authentication is not supported";
}
if (fAuthType & LOGIN) {
//******* LOGIN Authentication ( tested. works fine)
ssize_t encodedsize; // required by our base64 implementation
@ -540,13 +703,20 @@ SMTPProtocol::Login(const char *_login, const char *password)
return B_OK;
}
if (fAuthType & PLAIN) {
//******* PLAIN Authentication ( not tested yet.)
//******* PLAIN Authentication ( tested. works fine [with Cyrus SASL] )
// format is:
// authenticateID + \0 + username + \0 + password
// (where authenticateID is always empty !?!)
BString preResponse, postResponse;
char *stringPntr;
ssize_t encodedLength;
stringPntr = preResponse.LockBuffer(loginlen * 2 + passlen + 3);
sprintf (stringPntr, "%s%c%s%c%s", login, 0, login, 0, password);
preResponse.UnlockBuffer(loginlen * 2 + passlen + 3);
stringPntr = preResponse.LockBuffer(loginlen + passlen + 3);
// +3 to make room for the two \0-chars between the tokens and
// the final delimiter added by sprintf().
sprintf (stringPntr, "%c%s%c%s", 0, login, 0, password);
preResponse.UnlockBuffer(loginlen + passlen + 2);
// +2 in order to leave out the final delimiter (which is not part
// of the string).
stringPntr = postResponse.LockBuffer(preResponse.Length() * 3);
encodedLength = ::encode_base64(stringPntr, preResponse.String(),
preResponse.Length(), true /* headerMode */);

View File

@ -51,6 +51,7 @@ class SMTPProtocol : public BMailFilter {
#endif
status_t fStatus;
BString fServerName; // required for DIGEST-MD5
};
#endif /* ZOIDBERG_SMTP_H */