Fix #3: broken DIGEST-MD5 auth with ejabberd.
This commit is contained in:
49
src/sasl.c
49
src/sasl.c
@@ -128,11 +128,10 @@ static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
|
|||||||
t++;
|
t++;
|
||||||
while ((*t != *s) && (*t != '\0'))
|
while ((*t != *s) && (*t != '\0'))
|
||||||
t++;
|
t++;
|
||||||
|
value = _make_string(ctx, (char *)s+1, (t-s-1));
|
||||||
if (*t == *s) {
|
if (*t == *s) {
|
||||||
value = _make_string(ctx, (char *)s+1, (t-s-2));
|
|
||||||
s = t + 1;
|
s = t + 1;
|
||||||
} else {
|
} else {
|
||||||
value = _make_string(ctx, (char *)s+1, (t-s-1));
|
|
||||||
s = t;
|
s = t;
|
||||||
}
|
}
|
||||||
/* otherwise, accumulate a value ending in ',' or '\0' */
|
/* otherwise, accumulate a value ending in ',' or '\0' */
|
||||||
@@ -189,7 +188,7 @@ static char *_add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key,
|
|||||||
value = hash_get(table, key);
|
value = hash_get(table, key);
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
xmpp_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
|
xmpp_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
|
||||||
value = "\"\"";
|
value = "";
|
||||||
}
|
}
|
||||||
if (quote) {
|
if (quote) {
|
||||||
qvalue = _make_quoted(ctx, value);
|
qvalue = _make_quoted(ctx, value);
|
||||||
@@ -222,13 +221,12 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
const char *jid, const char *password) {
|
const char *jid, const char *password) {
|
||||||
hash_t *table;
|
hash_t *table;
|
||||||
char *result = NULL;
|
char *result = NULL;
|
||||||
char *node, *domain;
|
char *node, *domain, *realm;
|
||||||
char *value;
|
char *value;
|
||||||
unsigned char *A1, *A2; /* MD5 hashed versions */
|
|
||||||
char *response;
|
char *response;
|
||||||
int rlen;
|
int rlen;
|
||||||
struct MD5Context MD5;
|
struct MD5Context MD5;
|
||||||
unsigned char digest[16];
|
unsigned char digest[16], HA1[16], HA2[16];
|
||||||
char hex[32];
|
char hex[32];
|
||||||
|
|
||||||
/* our digest response is
|
/* our digest response is
|
||||||
@@ -237,7 +235,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
))
|
))
|
||||||
|
|
||||||
where KD(k, s) = MD5(k ':' s),
|
where KD(k, s) = MD5(k ':' s),
|
||||||
A1 = MD5( node ':' domain ':' password ) ':' nonce ':' cnonce
|
A1 = MD5( node ':' realm ':' password ) ':' nonce ':' cnonce
|
||||||
A2 = "AUTHENTICATE" ':' "xmpp/" domain
|
A2 = "AUTHENTICATE" ':' "xmpp/" domain
|
||||||
|
|
||||||
If there is an authzid it is ':'-appended to A1 */
|
If there is an authzid it is ':'-appended to A1 */
|
||||||
@@ -252,6 +250,14 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
node = xmpp_jid_node(ctx, jid);
|
node = xmpp_jid_node(ctx, jid);
|
||||||
domain = xmpp_jid_domain(ctx, jid);
|
domain = xmpp_jid_domain(ctx, jid);
|
||||||
|
|
||||||
|
/* generate default realm of domain if one didn't come from the
|
||||||
|
server */
|
||||||
|
realm = hash_get(table, "realm");
|
||||||
|
if (realm == NULL || strlen(realm) == 0) {
|
||||||
|
hash_add(table, "realm", xmpp_strdup(ctx, domain));
|
||||||
|
realm = hash_get(table, "realm");
|
||||||
|
}
|
||||||
|
|
||||||
/* add our response fields */
|
/* add our response fields */
|
||||||
hash_add(table, "username", xmpp_strdup(ctx, node));
|
hash_add(table, "username", xmpp_strdup(ctx, node));
|
||||||
/* TODO: generate a random cnonce */
|
/* TODO: generate a random cnonce */
|
||||||
@@ -266,17 +272,19 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
|
|
||||||
/* generate response */
|
/* generate response */
|
||||||
|
|
||||||
/* construct MD5(A1) */
|
/* construct MD5(node : realm : password) */
|
||||||
MD5Init(&MD5);
|
MD5Init(&MD5);
|
||||||
MD5Update(&MD5, (unsigned char *)node, strlen(node));
|
MD5Update(&MD5, (unsigned char *)node, strlen(node));
|
||||||
MD5Update(&MD5, (unsigned char *)":", 1);
|
MD5Update(&MD5, (unsigned char *)":", 1);
|
||||||
MD5Update(&MD5, (unsigned char *)domain, strlen(domain));
|
MD5Update(&MD5, (unsigned char *)realm, strlen(realm));
|
||||||
MD5Update(&MD5, (unsigned char *)":", 1);
|
MD5Update(&MD5, (unsigned char *)":", 1);
|
||||||
MD5Update(&MD5, (unsigned char *)password, strlen(password));
|
MD5Update(&MD5, (unsigned char *)password, strlen(password));
|
||||||
MD5Final(digest, &MD5);
|
MD5Final(digest, &MD5);
|
||||||
|
|
||||||
|
/* digest now contains the first field of A1 */
|
||||||
|
|
||||||
MD5Init(&MD5);
|
MD5Init(&MD5);
|
||||||
MD5Update(&MD5, (unsigned char *)digest, 16);
|
MD5Update(&MD5, digest, 16);
|
||||||
MD5Update(&MD5, (unsigned char *)":", 1);
|
MD5Update(&MD5, (unsigned char *)":", 1);
|
||||||
value = hash_get(table, "nonce");
|
value = hash_get(table, "nonce");
|
||||||
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
||||||
@@ -285,28 +293,32 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
||||||
MD5Final(digest, &MD5);
|
MD5Final(digest, &MD5);
|
||||||
|
|
||||||
A1 = xmpp_alloc(ctx, 16);
|
/* now digest is MD5(A1) */
|
||||||
memcpy(A1, digest, 16);
|
memcpy(HA1, digest, 16);
|
||||||
|
|
||||||
/* construct MD5(A2) */
|
/* construct MD5(A2) */
|
||||||
MD5Init(&MD5);
|
MD5Init(&MD5);
|
||||||
MD5Update(&MD5, (unsigned char *)"AUTHENTICATE:", 13);
|
MD5Update(&MD5, (unsigned char *)"AUTHENTICATE:", 13);
|
||||||
value = hash_get(table, "digest-uri");
|
value = hash_get(table, "digest-uri");
|
||||||
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
||||||
|
if (strcmp(hash_get(table, "qop"), "auth") != 0) {
|
||||||
|
MD5Update(&MD5, (unsigned char *)":00000000000000000000000000000000",
|
||||||
|
33);
|
||||||
|
}
|
||||||
MD5Final(digest, &MD5);
|
MD5Final(digest, &MD5);
|
||||||
|
|
||||||
A2 = xmpp_alloc(ctx, 16);
|
memcpy(HA2, digest, 16);
|
||||||
memcpy(A2, digest, 16);
|
|
||||||
|
|
||||||
/* construct response */
|
/* construct response */
|
||||||
MD5Init(&MD5);
|
MD5Init(&MD5);
|
||||||
_digest_to_hex((char *)A1, hex);
|
_digest_to_hex((char *)HA1, hex);
|
||||||
MD5Update(&MD5, (unsigned char *)hex, 32);
|
MD5Update(&MD5, (unsigned char *)hex, 32);
|
||||||
MD5Update(&MD5, (unsigned char *)":", 1);
|
MD5Update(&MD5, (unsigned char *)":", 1);
|
||||||
value = hash_get(table, "nonce");
|
value = hash_get(table, "nonce");
|
||||||
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
||||||
MD5Update(&MD5, (unsigned char *)":", 1);
|
MD5Update(&MD5, (unsigned char *)":", 1);
|
||||||
MD5Update(&MD5, (unsigned char *)"00000001", 8);
|
value = hash_get(table, "nc");
|
||||||
|
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
||||||
MD5Update(&MD5, (unsigned char *)":", 1);
|
MD5Update(&MD5, (unsigned char *)":", 1);
|
||||||
value = hash_get(table, "cnonce");
|
value = hash_get(table, "cnonce");
|
||||||
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
||||||
@@ -314,7 +326,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
value = hash_get(table, "qop");
|
value = hash_get(table, "qop");
|
||||||
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
||||||
MD5Update(&MD5, (unsigned char *)":", 1);
|
MD5Update(&MD5, (unsigned char *)":", 1);
|
||||||
_digest_to_hex((char *)A2, hex);
|
_digest_to_hex((char *)HA2, hex);
|
||||||
MD5Update(&MD5, (unsigned char *)hex, 32);
|
MD5Update(&MD5, (unsigned char *)hex, 32);
|
||||||
MD5Final(digest, &MD5);
|
MD5Final(digest, &MD5);
|
||||||
|
|
||||||
@@ -324,9 +336,6 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
response[32] = '\0';
|
response[32] = '\0';
|
||||||
hash_add(table, "response", response);
|
hash_add(table, "response", response);
|
||||||
|
|
||||||
xmpp_free(ctx, A1);
|
|
||||||
xmpp_free(ctx, A2);
|
|
||||||
|
|
||||||
/* construct reply */
|
/* construct reply */
|
||||||
result = NULL;
|
result = NULL;
|
||||||
rlen = 0;
|
rlen = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user