mech-digest-md5.c revision a13f3b206d88ed402cf597d7ae6eafd825de7969
/* Copyright (C) 2002 Timo Sirainen */
/* Digest-MD5 SASL authentication, see RFC-2831 */
#include "common.h"
#include "base64.h"
#include "buffer.h"
#include "hex-binary.h"
#include "md5.h"
#include "randgen.h"
#include "str.h"
#include "mech.h"
#include "passdb.h"
#include <stdlib.h>
#define SERVICE_TYPE "imap"
/* Linear whitespace */
enum qop_option {
QOP_COUNT = 3
};
struct digest_auth_request {
struct auth_request auth_request;
unsigned int authenticated:1;
/* requested: */
char *nonce;
enum qop_option qop;
/* received: */
char *realm; /* may be NULL */
char *username;
char *cnonce;
char *nonce_count;
char *qop_value;
char *digest_uri; /* may be NULL */
unsigned char response[32];
unsigned long maxbuf;
unsigned int nonce_found:1;
/* final reply: */
char *rspauth;
};
{
const char *const *tmp;
unsigned char nonce[16];
int i, first_qop;
/*
realm="hostname" (multiple allowed)
nonce="randomized data, at least 64bit"
qop="auth,auth-int,auth-conf"
maxbuf=number (with auth-int, auth-conf, defaults to 64k)
charset="utf-8" (iso-8859-1 if it doesn't exist)
algorithm="md5-sess"
cipher="3des,des,rc4-40,rc4,rc4-56" (with auth-conf)
*/
/* get 128bit of random data as nonce */
t_push();
t_pop();
}
for (i = 0; i < QOP_COUNT; i++) {
if (first_qop)
else
}
}
"algorithm=\"md5-sess\"");
return str;
}
const char *credentials)
{
struct md5_context ctx;
unsigned char digest[16];
int i;
/* get the MD5 password */
return FALSE;
return FALSE;
/*
response =
HEX( KD ( HEX(H(A1)),
{ nonce-value, ":" nc-value, ":",
cnonce-value, ":", qop-value, ":", HEX(H(A2)) }))
and since we don't support authzid yet:
A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
":", nonce-value, ":", cnonce-value }
If the "qop" directive's value is "auth", then A2 is:
A2 = { "AUTHENTICATE:", digest-uri-value }
If the "qop" value is "auth-int" or "auth-conf" then A2 is:
A2 = { "AUTHENTICATE:", digest-uri-value,
":00000000000000000000000000000000" }
*/
/* A1 */
/* do it twice, first verify the user's response, the second is
sent for client as a reply */
for (i = 0; i < 2; i++) {
/* A2 */
if (i == 0)
else
}
33);
}
/* response */
if (i == 0) {
/* verify response */
if (verbose) {
i_info("digest-md5(%s): "
"password mismatch",
}
return FALSE;
}
} else {
response_hex, NULL);
}
}
return TRUE;
}
static int verify_realm(const char *realm)
{
const char *const *tmp;
return TRUE;
}
return FALSE;
}
{
/* @UNSAFE */
char *p, *dest;
p = *data;
while (IS_LWS(*p)) p++;
/* get key */
*key = p;
while (*p != '\0' && *p != '=' && *p != ',')
p++;
if (*p != '=') {
*data = p;
return FALSE;
}
*value = p+1;
/* skip trailing whitespace in key */
while (IS_LWS(p[-1]))
p--;
*p = '\0';
/* get value */
p = *value;
while (IS_LWS(*p)) p++;
if (*p != '"') {
while (*p != '\0' && *p != ',')
p++;
*data = p+1;
while (IS_LWS(p[-1]))
p--;
*p = '\0';
} else {
/* quoted string */
while (*p != '\0' && *p != '"') {
if (*p == '\\' && p[1] != '\0')
p++;
*dest++ = *p++;
}
*dest = '\0';
}
return TRUE;
}
/* remove leading and trailing whitespace */
{
const char *ret;
}
return ret;
}
{
int i;
if (!verify_realm(value)) {
*error = "Invalid realm";
return FALSE;
}
return TRUE;
}
*error = "username must not exist more than once";
return FALSE;
}
if (*value == '\0') {
*error = "empty username";
return FALSE;
}
return TRUE;
}
/* nonce must be same */
*error = "Invalid nonce";
return FALSE;
}
return TRUE;
}
*error = "cnonce must not exist more than once";
return FALSE;
}
if (*value == '\0') {
*error = "cnonce can't contain empty value";
return FALSE;
}
return TRUE;
}
*error = "nonce-count must not exist more than once";
return FALSE;
}
*error = "re-auth not supported currently";
return FALSE;
}
return TRUE;
}
for (i = 0; i < QOP_COUNT; i++) {
break;
}
if (i == QOP_COUNT) {
*error = "Unknown QoP value";
return FALSE;
}
*error = "Nonallowed QoP requested";
return FALSE;
}
return TRUE;
}
/* type / host / serv-name */
*error = "Invalid digest-uri";
return FALSE;
}
*error = "Unexpected service type in digest-uri";
return FALSE;
}
But isn't the realm enough already? That'd be just extra
configuration.. Maybe optionally list valid hosts in
config file? */
return TRUE;
}
*error = "maxbuf must not exist more than once";
return FALSE;
}
*error = "Invalid maxbuf value";
return FALSE;
}
return TRUE;
}
*error = "Only utf-8 charset is allowed";
return FALSE;
}
return TRUE;
}
*error = "Invalid response value";
return FALSE;
}
return TRUE;
}
/* not supported, ignore */
return TRUE;
}
/* not supported, abort */
return FALSE;
}
/* unknown key, ignore */
return TRUE;
}
const char **error)
{
int failed;
/*
realm="realm"
username="username"
nonce="randomized data"
cnonce="??"
nc=00000001
qop="auth|auth-int|auth-conf"
response=32 HEX digits
maxbuf=number (with auth-int, auth-conf, defaults to 64k)
charset="utf-8" (iso-8859-1 if it doesn't exist)
cipher="cipher-value"
authzid="authzid-value"
*/
t_push();
while (*copy != '\0') {
break;
}
}
if (*copy == ',')
copy++;
}
if (!auth->nonce_found) {
*error = "Missing nonce parameter";
*error = "Missing cnonce parameter";
*error = "Missing username parameter";
}
t_pop();
return !failed;
}
static void credentials_callback(const char *result,
struct auth_request *request)
{
struct digest_auth_request *auth =
(struct digest_auth_request *) request;
struct auth_login_reply reply;
else {
}
}
static int
struct auth_request *auth_request,
struct auth_login_request_continue *request,
const unsigned char *data,
{
struct digest_auth_request *auth =
(struct digest_auth_request *)auth_request;
struct auth_login_reply reply;
const char *error;
/* initialize reply */
if (auth->authenticated) {
/* authentication is done, we were just waiting the last
word from client */
return TRUE;
}
return TRUE;
}
error = "Authentication failed";
else if (verbose)
/* failed */
return FALSE;
}
{
}
static struct auth_request *
{
struct auth_login_reply reply;
struct digest_auth_request *auth;
/* initialize reply */
/* send the initial challenge */
return &auth->auth_request;
}
struct mech_module mech_digest_md5 = {
};