/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
/* Digest-MD5 SASL authentication, see RFC-2831 */
#include "auth-common.h"
#include "base64.h"
#include "buffer.h"
#include "hex-binary.h"
#include "md5.h"
#include "randgen.h"
#include "str.h"
#include "str-sanitize.h"
#include "mech.h"
#include "passdb.h"
/* Linear whitespace */
enum qop_option {
};
struct digest_auth_request {
/* requested: */
char *nonce;
/* received: */
char *username;
char *cnonce;
char *nonce_count;
char *qop_value;
unsigned long maxbuf;
/* final reply: */
char *rspauth;
};
{
const char *const *tmp;
int i;
bool 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 */
/* If no realms are given, at least Cyrus SASL client defaults
to destination host name */
} else {
}
for (i = 0; i < QOP_COUNT; i++) {
if (first_qop)
else
}
}
"algorithm=\"md5-sess\"");
return str;
}
{
int i;
/* get the MD5 password */
if (size != MD5_RESULTLEN) {
"invalid credentials length");
return FALSE;
}
/*
response =
HEX( KD ( HEX(H(A1)),
{ nonce-value, ":" nc-value, ":",
cnonce-value, ":", qop-value, ":", HEX(H(A2)) }))
and if authzid is not empty:
A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
":", nonce-value, ":", cnonce-value, ":", authzid }
else:
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 */
return FALSE;
}
} else {
response_hex, NULL);
}
}
return TRUE;
}
{
/* @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 */
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;
}
{
unsigned int i;
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;
}
unsigned int nc;
*error = "nonce-count must not exist more than once";
return FALSE;
}
*error = "nonce-count value invalid";
return FALSE;
}
if (nc != 1) {
*error = "re-auth not supported currently";
return FALSE;
}
return TRUE;
}
for (i = 0; i < QOP_COUNT; i++) {
break;
}
if (i == QOP_COUNT) {
return FALSE;
}
*error = "Nonallowed QoP requested";
return FALSE;
}
return TRUE;
}
/* type / host / serv-name */
*error = "Invalid 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;
}
*error = "authzid must not exist more than once";
return FALSE;
}
if (*value == '\0') {
*error = "empty authzid";
return FALSE;
}
return TRUE;
}
/* unknown key, ignore */
return TRUE;
}
const char **error)
{
bool 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"
*/
if (size == 0) {
*error = "Client sent no input";
return FALSE;
}
/* treating response as NUL-terminated string also gets rid of all
potential problems with NUL characters in strings. */
while (*copy != '\0') {
break;
}
}
if (*copy == ',')
copy++;
}
if (!failed) {
if (!request->nonce_found) {
*error = "Missing nonce parameter";
*error = "Missing cnonce parameter";
*error = "Missing username parameter";
}
}
return !failed;
}
struct auth_request *auth_request)
{
(struct digest_auth_request *)auth_request;
switch (result) {
case PASSDB_RESULT_OK:
return;
}
break;
break;
default:
break;
}
}
static void
{
(struct digest_auth_request *)auth_request;
} else {
}
&error))) {
"DIGEST-MD5", credentials_callback);
return;
}
}
}
static void
const unsigned char *data ATTR_UNUSED,
{
(struct digest_auth_request *)auth_request;
/* FIXME: there's no support for subsequent authentication */
}
{
return &request->auth_request;
}
"DIGEST-MD5",
};