mech-digest-md5.c revision 853533bfe9e0e2fa96f6559d3fcf5945ea3300e5
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen/* Digest-MD5 SASL authentication, see RFC-2831 */
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen#include "common.h"
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen#include "base64.h"
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen#include "buffer.h"
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#include "hex-binary.h"
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen#include "md5.h"
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include "randgen.h"
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen#include "str.h"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#include "mech.h"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#include "passdb.h"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#include <stdlib.h>
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen/* Linear whitespace */
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#define IS_LWS(c) ((c) == ' ' || (c) == '\t')
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainenenum qop_option {
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen QOP_AUTH = 0x01, /* authenticate */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen QOP_AUTH_INT = 0x02, /* + integrity protection, not supported yet */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen QOP_AUTH_CONF = 0x04, /* + encryption, not supported yet */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen QOP_COUNT = 3
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen};
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainenstatic const char *qop_names[] = { "auth", "auth-int", "auth-conf" };
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainenstruct digest_auth_request {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen struct auth_request auth_request;
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen pool_t pool;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen unsigned int authenticated:1;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* requested: */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen char *nonce;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen enum qop_option qop;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
150e64c376365becf1ec5c9d45912ecb840eea96Timo Sirainen /* received: */
150e64c376365becf1ec5c9d45912ecb840eea96Timo Sirainen char *realm; /* may be NULL */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen char *username;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen char *cnonce;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen char *nonce_count;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen char *qop_value;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen char *digest_uri; /* may be NULL */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen unsigned char response[32];
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen unsigned long maxbuf;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen unsigned int nonce_found:1;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* final reply: */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen char *rspauth;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen};
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
5afa8e2edf4f313cd56e5909f92f39c3b5b7b4d3Timo Sirainenstatic string_t *get_digest_challenge(struct digest_auth_request *auth)
5afa8e2edf4f313cd56e5909f92f39c3b5b7b4d3Timo Sirainen{
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen buffer_t *buf;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen string_t *str;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen const char *const *tmp;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen unsigned char nonce[16];
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen int i, first_qop;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /*
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen realm="hostname" (multiple allowed)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen nonce="randomized data, at least 64bit"
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen qop="auth,auth-int,auth-conf"
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen maxbuf=number (with auth-int, auth-conf, defaults to 64k)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen charset="utf-8" (iso-8859-1 if it doesn't exist)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen algorithm="md5-sess"
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen cipher="3des,des,rc4-40,rc4,rc4-56" (with auth-conf)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* get 128bit of random data as nonce */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen random_fill(nonce, sizeof(nonce));
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen t_push();
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen buf = buffer_create_static(pool_datastack_create(),
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen MAX_BASE64_ENCODED_SIZE(sizeof(nonce))+1);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen base64_encode(nonce, sizeof(nonce), buf);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen buffer_append_c(buf, '\0');
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen auth->nonce = p_strdup(auth->pool, buffer_get_data(buf, NULL));
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen t_pop();
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen str = t_str_new(256);
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen for (tmp = auth_realms; *tmp != NULL; tmp++) {
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen str_printfa(str, "realm=\"%s\"", *tmp);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen str_append_c(str, ',');
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen }
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen str_printfa(str, "nonce=\"%s\",", auth->nonce);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen str_append(str, "qop=\""); first_qop = TRUE;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen for (i = 0; i < QOP_COUNT; i++) {
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen if (auth->qop & (1 << i)) {
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (first_qop)
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen first_qop = FALSE;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen else
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen str_append_c(str, ',');
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen str_append(str, qop_names[i]);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen }
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen }
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen str_append(str, "\",");
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen str_append(str, "charset=\"utf-8\","
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen "algorithm=\"md5-sess\"");
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen return str;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen}
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainenstatic int verify_credentials(struct digest_auth_request *auth,
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen const char *credentials)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen{
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen struct md5_context ctx;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen unsigned char digest[16];
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen const char *a1_hex, *a2_hex, *response_hex;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen buffer_t *digest_buf;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen int i;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* get the MD5 password */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (credentials == NULL || strlen(credentials) != sizeof(digest)*2)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen return FALSE;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen digest_buf = buffer_create_data(pool_datastack_create(),
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen digest, sizeof(digest));
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (hex_to_binary(credentials, digest_buf) <= 0)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen return FALSE;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen /*
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen response =
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen HEX( KD ( HEX(H(A1)),
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen { nonce-value, ":" nc-value, ":",
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen cnonce-value, ":", qop-value, ":", HEX(H(A2)) }))
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen and since we don't support authzid yet:
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen ":", nonce-value, ":", cnonce-value }
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen If the "qop" directive's value is "auth", then A2 is:
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen A2 = { "AUTHENTICATE:", digest-uri-value }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen If the "qop" value is "auth-int" or "auth-conf" then A2 is:
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen
bc3698b8892df8003b410daea6f5bbcd20433808Timo Sirainen A2 = { "AUTHENTICATE:", digest-uri-value,
06e72c658de3ce1252594b151313df90acf73271Timo Sirainen ":00000000000000000000000000000000" }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen */
06e72c658de3ce1252594b151313df90acf73271Timo Sirainen
06e72c658de3ce1252594b151313df90acf73271Timo Sirainen /* A1 */
06e72c658de3ce1252594b151313df90acf73271Timo Sirainen md5_init(&ctx);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen md5_update(&ctx, digest, 16);
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen md5_update(&ctx, ":", 1);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen md5_update(&ctx, auth->nonce, strlen(auth->nonce));
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen md5_update(&ctx, ":", 1);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen md5_update(&ctx, auth->cnonce, strlen(auth->cnonce));
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen md5_final(&ctx, digest);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen a1_hex = binary_to_hex(digest, 16);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen /* do it twice, first verify the user's response, the second is
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen sent for client as a reply */
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen for (i = 0; i < 2; i++) {
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen /* A2 */
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen md5_init(&ctx);
ccb77e2f63626ec46e5745ef4f38baa8e8e504fcTimo Sirainen if (i == 0)
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen md5_update(&ctx, "AUTHENTICATE:", 13);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen else
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen md5_update(&ctx, ":", 1);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (auth->digest_uri != NULL) {
bd74402ca1a39ec303075fefb1212d7e18a71531Timo Sirainen md5_update(&ctx, auth->digest_uri,
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen strlen(auth->digest_uri));
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen }
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen if (auth->qop == QOP_AUTH_INT || auth->qop == QOP_AUTH_CONF) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen md5_update(&ctx, ":00000000000000000000000000000000",
f5a24412980cb19b07cb0cd12dba75886f281875Timo Sirainen 33);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen md5_final(&ctx, digest);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen a2_hex = binary_to_hex(digest, 16);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen /* response */
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen md5_init(&ctx);
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen md5_update(&ctx, a1_hex, 32);
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen md5_update(&ctx, ":", 1);
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen md5_update(&ctx, auth->nonce, strlen(auth->nonce));
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen md5_update(&ctx, ":", 1);
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen md5_update(&ctx, auth->nonce_count, strlen(auth->nonce_count));
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen md5_update(&ctx, ":", 1);
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen md5_update(&ctx, auth->cnonce, strlen(auth->cnonce));
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen md5_update(&ctx, ":", 1);
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen md5_update(&ctx, auth->qop_value, strlen(auth->qop_value));
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen md5_update(&ctx, ":", 1);
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen md5_update(&ctx, a2_hex, 32);
63f36c2b47217fc2dc4ed49cfc1907311d5ed366Timo Sirainen md5_final(&ctx, digest);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen response_hex = binary_to_hex(digest, 16);
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen
bd74402ca1a39ec303075fefb1212d7e18a71531Timo Sirainen if (i == 0) {
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen /* verify response */
bd74402ca1a39ec303075fefb1212d7e18a71531Timo Sirainen if (memcmp(response_hex, auth->response, 32) != 0) {
bd74402ca1a39ec303075fefb1212d7e18a71531Timo Sirainen if (verbose) {
ef11d3930c3602fc86349a4e3a53442df470b601Timo Sirainen i_info("digest-md5(%s): "
c6ae908f6a2313573625d782bdd4e0ff3882c44aTimo Sirainen "password mismatch",
c6ae908f6a2313573625d782bdd4e0ff3882c44aTimo Sirainen auth->username);
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen }
ef11d3930c3602fc86349a4e3a53442df470b601Timo Sirainen return FALSE;
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen }
ef11d3930c3602fc86349a4e3a53442df470b601Timo Sirainen } else {
ef11d3930c3602fc86349a4e3a53442df470b601Timo Sirainen auth->rspauth = p_strconcat(auth->pool, "rspauth=",
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen response_hex, NULL);
17b03c9db961e1c004284907d969eb11b08a795eTimo Sirainen }
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen }
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen
ef11d3930c3602fc86349a4e3a53442df470b601Timo Sirainen return TRUE;
ef11d3930c3602fc86349a4e3a53442df470b601Timo Sirainen}
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainenstatic int verify_realm(const char *realm)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen{
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen const char *const *tmp;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (*realm == '\0')
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen return TRUE;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen for (tmp = auth_realms; *tmp != NULL; tmp++) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (strcasecmp(realm, *tmp) == 0)
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen return TRUE;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
ccb77e2f63626ec46e5745ef4f38baa8e8e504fcTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen return FALSE;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen}
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainenstatic int parse_next(char **data, char **key, char **value)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen{
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen /* @UNSAFE */
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen char *p, *dest;
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen p = *data;
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen while (IS_LWS(*p)) p++;
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen /* get key */
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen *key = p;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen while (*p != '\0' && *p != '=' && *p != ',')
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen p++;
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen if (*p != '=') {
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen *data = p;
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen return FALSE;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen }
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen *value = p+1;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen /* skip trailing whitespace in key */
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen while (IS_LWS(p[-1]))
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen p--;
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen *p = '\0';
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen /* get value */
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen p = *value;
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen while (IS_LWS(*p)) p++;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen if (*p != '"') {
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen while (*p != '\0' && *p != ',')
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen p++;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen *data = p+1;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen while (IS_LWS(p[-1]))
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen p--;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen *p = '\0';
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen } else {
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen /* quoted string */
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen *value = dest = ++p;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen while (*p != '\0' && *p != '"') {
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen if (*p == '\\' && p[1] != '\0')
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen p++;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen *dest++ = *p++;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen *data = *p == '"' ? p+1 : p;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen *dest = '\0';
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen return TRUE;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen}
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen/* remove leading and trailing whitespace */
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainenstatic const char *trim(const char *str)
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen{
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen const char *ret;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen while (IS_LWS(*str)) str++;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen ret = str;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen while (*str != '\0') str++;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (str > ret) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen while (IS_LWS(str[-1])) str--;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen ret = t_strdup_until(ret, str);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen return ret;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen}
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainenstatic int auth_handle_response(struct digest_auth_request *auth,
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen char *key, char *value, const char **error)
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen{
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen int i;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen str_lcase(key);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (strcmp(key, "realm") == 0) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (!verify_realm(value)) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen *error = "Invalid realm";
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen return FALSE;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (auth->realm == NULL && *value != '\0')
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen auth->realm = p_strdup(auth->pool, value);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen return TRUE;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (strcmp(key, "username") == 0) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (auth->username != NULL) {
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen *error = "username must not exist more than once";
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen return FALSE;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (*value == '\0') {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen *error = "empty username";
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen return FALSE;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen auth->username = p_strdup(auth->pool, value);
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen return TRUE;
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen if (strcmp(key, "nonce") == 0) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen /* nonce must be same */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (strcmp(value, auth->nonce) != 0) {
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen *error = "Invalid nonce";
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen return FALSE;
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen }
10cfe8a2bdc5ccfc05380689c71c27209327538fTimo Sirainen
10cfe8a2bdc5ccfc05380689c71c27209327538fTimo Sirainen auth->nonce_found = TRUE;
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen return TRUE;
10cfe8a2bdc5ccfc05380689c71c27209327538fTimo Sirainen }
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen if (strcmp(key, "cnonce") == 0) {
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen if (auth->cnonce != NULL) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen *error = "cnonce must not exist more than once";
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen return FALSE;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (*value == '\0') {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen *error = "cnonce can't contain empty value";
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen return FALSE;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen auth->cnonce = p_strdup(auth->pool, value);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen return TRUE;
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen }
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen if (strcmp(key, "nonce-count") == 0) {
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen if (auth->nonce_count != NULL) {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen *error = "nonce-count must not exist more than once";
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen return FALSE;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
e30e068c8fac372ae217b3b31791a0c8c8046b7fTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (atoi(value) != 1) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen *error = "re-auth not supported currently";
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen return FALSE;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen auth->nonce_count = p_strdup(auth->pool, value);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen return TRUE;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (strcmp(key, "qop") == 0) {
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen for (i = 0; i < QOP_COUNT; i++) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (strcasecmp(qop_names[i], value) == 0)
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen break;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen if (i == QOP_COUNT) {
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen *error = "Unknown QoP value";
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen return FALSE;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen auth->qop &= (1 << i);
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen if (auth->qop == 0) {
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen *error = "Nonallowed QoP requested";
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen return FALSE;
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen }
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen auth->qop_value = p_strdup(auth->pool, value);
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen return TRUE;
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen }
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen if (strcmp(key, "digest-uri") == 0) {
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen /* type / host / serv-name */
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen const char *const *uri = t_strsplit(value, "/");
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen if (uri[0] == NULL || uri[1] == NULL) {
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen *error = "Invalid digest-uri";
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen return FALSE;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen }
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen /* FIXME: RFC recommends that we verify the host/serv-type.
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen But isn't the realm enough already? That'd be just extra
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen configuration.. Maybe optionally list valid hosts in
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen config file? */
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen auth->digest_uri = p_strdup(auth->pool, value);
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen return TRUE;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen }
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen if (strcmp(key, "maxbuf") == 0) {
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen if (auth->maxbuf != 0) {
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen *error = "maxbuf must not exist more than once";
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen return FALSE;
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen auth->maxbuf = strtoul(value, NULL, 10);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (auth->maxbuf == 0) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen *error = "Invalid maxbuf value";
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen return FALSE;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen return TRUE;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen }
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen if (strcmp(key, "charset") == 0) {
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen if (strcasecmp(value, "utf-8") != 0) {
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen *error = "Only utf-8 charset is allowed";
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen return FALSE;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen }
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen return TRUE;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (strcmp(key, "response") == 0) {
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen if (strlen(value) != 32) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen *error = "Invalid response value";
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen return FALSE;
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen memcpy(auth->response, value, 32);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen return TRUE;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen if (strcmp(key, "cipher") == 0) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* not supported, ignore */
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen return TRUE;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (strcmp(key, "authzid") == 0) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* not supported, abort */
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen return FALSE;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen /* unknown key, ignore */
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen return TRUE;
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen}
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainenstatic int parse_digest_response(struct digest_auth_request *auth,
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen const unsigned char *data, size_t size,
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen const char **error)
3419b088ffe531799bdb47b3ff3fce85c8b7569aTimo Sirainen{
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen char *copy, *key, *value;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen int failed;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen /*
2d340205d897e23fbecb40c8e63a4ca49bd6739bTimo Sirainen realm="realm"
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen username="username"
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen nonce="randomized data"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen cnonce="??"
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen nc=00000001
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen qop="auth|auth-int|auth-conf"
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen digest-uri="serv-type/host[/serv-name]"
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen response=32 HEX digits
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen maxbuf=number (with auth-int, auth-conf, defaults to 64k)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen charset="utf-8" (iso-8859-1 if it doesn't exist)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen cipher="cipher-value"
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen authzid="authzid-value"
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen t_push();
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen *error = NULL;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen failed = FALSE;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen copy = t_strdup_noconst(t_strndup(data, size));
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen while (*copy != '\0') {
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen if (parse_next(&copy, &key, &value)) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (!auth_handle_response(auth, key, value, error)) {
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen failed = TRUE;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen break;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen if (*copy == ',')
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen copy++;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (!failed) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (!auth->nonce_found) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen *error = "Missing nonce parameter";
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen failed = TRUE;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen } else if (auth->cnonce == NULL) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen *error = "Missing cnonce parameter";
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen failed = TRUE;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen } else if (auth->username == NULL) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen *error = "Missing username parameter";
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen failed = TRUE;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (auth->nonce_count == NULL)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen auth->nonce_count = p_strdup(auth->pool, "00000001");
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (auth->qop_value == NULL)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen auth->qop_value = p_strdup(auth->pool, "auth");
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen t_pop();
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen return !failed;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen}
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainenstatic void credentials_callback(const char *result,
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen struct auth_request *request)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen{
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen struct digest_auth_request *auth =
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen (struct digest_auth_request *) request;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen struct auth_client_request_reply reply;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mech_init_auth_client_reply(&reply);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen reply.id = request->id;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (!verify_credentials(auth, result))
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen reply.result = AUTH_CLIENT_RESULT_FAILURE;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen else {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen reply.result = AUTH_CLIENT_RESULT_CONTINUE;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen reply.data_size = strlen(auth->rspauth);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen auth->authenticated = TRUE;
2d340205d897e23fbecb40c8e63a4ca49bd6739bTimo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen request->callback(&reply, auth->rspauth, request->conn);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen}
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainenstatic int
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainenmech_digest_md5_auth_continue(struct auth_request *auth_request,
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen struct auth_client_request_continue *request,
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen const unsigned char *data,
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen mech_callback_t *callback)
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen{
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen struct digest_auth_request *auth =
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen (struct digest_auth_request *)auth_request;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen struct auth_client_request_reply reply;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen const char *error, *realm;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* initialize reply */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mech_init_auth_client_reply(&reply);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen reply.id = request->id;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen if (auth->authenticated) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen /* authentication is done, we were just waiting the last
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen word from client */
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen mech_auth_finish(auth_request, NULL, 0, TRUE);
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen return TRUE;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen }
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen if (parse_digest_response(auth, data, request->data_size, &error)) {
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen auth_request->callback = callback;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen realm = auth->realm != NULL ? auth->realm : default_realm;
294f1a51763015cda0e2d874c5027d6fe7a2cd54Timo Sirainen if (realm == NULL) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen auth_request->user = p_strdup(auth_request->pool,
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen auth->username);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen } else {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen auth_request->user = p_strconcat(auth_request->pool,
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen auth->username, "@",
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen realm, NULL);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (mech_is_valid_username(auth_request->user)) {
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen passdb->lookup_credentials(&auth->auth_request,
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen PASSDB_CREDENTIALS_DIGEST_MD5,
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen credentials_callback);
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen return TRUE;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen }
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen error = "invalid username";
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen }
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen if (error == NULL)
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen error = "Authentication failed";
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen else if (verbose) {
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen i_info("digest-md5(%s): %s",
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen auth->username == NULL ? "" : auth->username, error);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen /* failed */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen reply.result = AUTH_CLIENT_RESULT_FAILURE;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen reply.data_size = strlen(error)+1;
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen callback(&reply, error, auth_request->conn);
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen return FALSE;
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen}
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainenstatic void mech_digest_md5_auth_free(struct auth_request *auth_request)
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen{
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen pool_unref(auth_request->pool);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen}
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainenstatic struct auth_request *
2d340205d897e23fbecb40c8e63a4ca49bd6739bTimo Sirainenmech_digest_md5_auth_new(struct auth_client_connection *conn,
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen unsigned int id, mech_callback_t *callback)
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen{
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen struct auth_client_request_reply reply;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen struct digest_auth_request *auth;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen pool_t pool;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen string_t *challenge;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen pool = pool_alloconly_create("digest_md5_auth_request", 2048);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen auth = p_new(pool, struct digest_auth_request, 1);
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen auth->pool = pool;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen auth->auth_request.refcount = 1;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen auth->auth_request.pool = pool;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen auth->auth_request.auth_continue = mech_digest_md5_auth_continue;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen auth->auth_request.auth_free = mech_digest_md5_auth_free;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen auth->qop = QOP_AUTH;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* initialize reply */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mech_init_auth_client_reply(&reply);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen reply.id = id;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen reply.result = AUTH_CLIENT_RESULT_CONTINUE;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* send the initial challenge */
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen reply.reply_idx = 0;
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen challenge = get_digest_challenge(auth);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen reply.data_size = str_len(challenge);
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen callback(&reply, str_data(challenge), conn);
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen return &auth->auth_request;
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen}
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainenstruct mech_module mech_digest_md5 = {
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen AUTH_MECH_DIGEST_MD5,
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen mech_digest_md5_auth_new
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen};
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen