mech-rpa.c revision 63969c244e8973a61760a98a23b127827d3d652c
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/*
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen * Compuserve RPA authentication mechanism.
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen * This program is free software; you can redistribute it and/or modify
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen * it under the terms of the GNU Lesser General Public License as published
4b9355028a364c6e901ce2d8a7d91180d3e55399Timo Sirainen * by the Free Software Foundation; either version 2 of the License, or
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen * (at your option) any later version.
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "common.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "mech.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "passdb.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "str.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "strfuncs.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "safe-memset.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "randgen.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "buffer.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "hostpid.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "hex-binary.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "md5.h"
c7a9a1fdd4373558aed893635b7493f6acbb60a9Timo Sirainen
c7a9a1fdd4373558aed893635b7493f6acbb60a9Timo Sirainenstruct rpa_auth_request {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct auth_request auth_request;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen pool_t pool;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen int phase;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* cached: */
17062405030a3fa095e43d5b8b71afc5536f0a22Timo Sirainen unsigned char *pwd_md5;
17062405030a3fa095e43d5b8b71afc5536f0a22Timo Sirainen size_t service_len;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *service_ucs2be;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t username_len;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *username_ucs2be;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t realm_len;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *realm_ucs2be;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* requested: */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned char *service_challenge;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned char *service_timestamp;
17062405030a3fa095e43d5b8b71afc5536f0a22Timo Sirainen
17062405030a3fa095e43d5b8b71afc5536f0a22Timo Sirainen /* received: */
17062405030a3fa095e43d5b8b71afc5536f0a22Timo Sirainen unsigned int user_challenge_len;
17062405030a3fa095e43d5b8b71afc5536f0a22Timo Sirainen unsigned char *user_challenge;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned char *user_response;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned char *session_key;
17062405030a3fa095e43d5b8b71afc5536f0a22Timo Sirainen};
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#define RPA_SCHALLENGE_LEN 32
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#define RPA_UCHALLENGE_LEN 16
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#define RPA_TIMESTAMP_LEN 14
17062405030a3fa095e43d5b8b71afc5536f0a22Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#define ASN1_APPLICATION 0x60
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen/* Object id encoded using ASN.1 DER */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic const unsigned char rpa_oid[] = {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x73, 0x01, 0x01
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen};
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenvoid *ucs2be_str(pool_t pool, const char *str, size_t *size);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen/*
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen * Compute client -> server authentication response.
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic void rpa_user_response(struct rpa_auth_request *auth,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned char *digest)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct md5_context ctx;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned char z[48];
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen memset(z, 0, sizeof(z));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_init(&ctx);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->pwd_md5, 16);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, z, sizeof(z));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->username_ucs2be, auth->username_len);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->service_ucs2be, auth->service_len);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->realm_ucs2be, auth->realm_len);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->user_challenge, auth->user_challenge_len);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->service_challenge, RPA_SCHALLENGE_LEN);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->service_timestamp, RPA_TIMESTAMP_LEN);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->pwd_md5, 16);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_final(&ctx, digest);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen/*
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen * Compute server -> client authentication response.
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic void rpa_server_response(struct rpa_auth_request *auth,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned char *digest)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct md5_context ctx;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned char tmp[16];
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned char z[48];
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen int i;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen memset(z, 0, sizeof(z));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_init(&ctx);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->pwd_md5, 16);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, z, sizeof(z));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->service_ucs2be, auth->service_len);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->username_ucs2be, auth->username_len);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->realm_ucs2be, auth->realm_len);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->service_challenge, RPA_SCHALLENGE_LEN);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->user_challenge, auth->user_challenge_len);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->service_timestamp, RPA_TIMESTAMP_LEN);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->pwd_md5, 16);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_final(&ctx, tmp);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen for (i = 0; i < 16; i++)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen tmp[i] = auth->session_key[i] ^ tmp[i];
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_init(&ctx);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->pwd_md5, 16);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, z, sizeof(z));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->service_ucs2be, auth->service_len);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->username_ucs2be, auth->username_len);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->realm_ucs2be, auth->realm_len);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->session_key, 16);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->service_challenge, RPA_SCHALLENGE_LEN);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->user_challenge, auth->user_challenge_len);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, auth->service_timestamp, RPA_TIMESTAMP_LEN);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen md5_update(&ctx, tmp, 16);
md5_update(&ctx, auth->pwd_md5, 16);
md5_final(&ctx, digest);
}
static const unsigned char *
rpa_check_message(const unsigned char *data, const unsigned char *end,
const char **error)
{
const unsigned char *p = data;
unsigned int len = 0;
if (p + 2 > end) {
*error = "message too short";
return NULL;
}
if (*p++ != ASN1_APPLICATION) {
*error = "invalid data type";
return NULL;
}
if ((*p & 0x80) != 0) {
unsigned int nbytes = *p++ & 0x7f;
while (nbytes-- > 0) {
if (p >= end) {
*error = "invalid structure length";
return NULL;
}
len = (len << 8) | *p++;
}
} else
len = *p++;
if ((size_t)(end - p) != len) {
*error = "structure length disagrees with data size";
return NULL;
}
if (p + sizeof(rpa_oid) > end) {
*error = "not enough space for object id";
return NULL;
}
if (memcmp(p, rpa_oid, sizeof(rpa_oid)) != 0) {
*error = "invalid object id";
return NULL;
}
return p + sizeof(rpa_oid);
}
static int
rpa_parse_token1(const void *data, size_t data_size, const char **error)
{
const unsigned char *end = ((unsigned char *) data) + data_size;
const unsigned char *p;
unsigned int version_lo, version_hi;
p = rpa_check_message(data, end, error);
if (p == NULL)
return FALSE;
if (p + 6 > end) {
*error = "message too short";
return FALSE;
}
version_lo = p[0] + (p[1] << 8);
version_hi = p[2] + (p[3] << 8);
if ((version_lo > 3) || (version_hi < 3)) {
*error = "protocol version mismatch";
return FALSE;
}
p += 4;
if ((p[0] != 0) || (p[1] != 1)) {
*error = "invalid message flags";
return FALSE;
}
p += 2;
if (p != end) {
*error = "unneeded data found";
return FALSE;
}
return TRUE;
}
static unsigned int
rpa_read_buffer(pool_t pool, const unsigned char **data,
const unsigned char *end, unsigned char **buffer)
{
const unsigned char *p = *data;
unsigned int len;
if (p > end)
return 0;
len = *p++;
if (p + len > end)
return 0;
*buffer = p_malloc(pool, len);
memcpy(*buffer, p, len);
*data += 1 + len;
return len;
}
static char *
rpa_parse_username(pool_t pool, const char *username)
{
const char *p = strrchr(username, '@');
return p == NULL ? p_strdup(pool, username) :
p_strdup_until(pool, username, p);
}
static int
rpa_parse_token3(struct rpa_auth_request *auth, const void *data,
size_t data_size, const char **error)
{
struct auth_request *auth_request = (struct auth_request *)auth;
const unsigned char *end = ((unsigned char *)data) + data_size;
const unsigned char *p;
unsigned int len;
const char *user;
p = rpa_check_message(data, end, error);
if (p == NULL)
return FALSE;
/* Read username@realm */
if (p + 2 > end) {
*error = "message too short";
return FALSE;
}
len = (p[0] >> 8) + p[1];
if (p + 2 + len > end) {
*error = "message too short";
return FALSE;
}
p += 2;
user = t_strndup(p, len);
p += len;
auth_request->user = rpa_parse_username(auth->pool, user);
auth->username_ucs2be = ucs2be_str(auth->pool, auth_request->user,
&auth->username_len);
/* Read user challenge */
auth->user_challenge_len = rpa_read_buffer(auth->pool, &p, end,
&auth->user_challenge);
if (auth->user_challenge_len == 0) {
*error = "invalid user challenge";
return FALSE;
}
/* Read user response */
len = rpa_read_buffer(auth->pool, &p, end, &auth->user_response);
if (len != RPA_UCHALLENGE_LEN) {
*error = "invalid user response";
return FALSE;
}
if (p != end) {
*error = "unneeded data found";
return FALSE;
}
return TRUE;
}
static void
buffer_append_asn1_length(buffer_t *buf, unsigned int length)
{
if (length < 0x80) {
buffer_append_c(buf, length);
} else if (length < 0x100) {
buffer_append_c(buf, 0x81);
buffer_append_c(buf, length);
} else {
buffer_append_c(buf, 0x82);
buffer_append_c(buf, length >> 8);
buffer_append_c(buf, length & 0xff);
}
}
static const unsigned char *
mech_rpa_build_token2(struct rpa_auth_request *auth,
const char *realms, size_t *size)
{
unsigned int realms_len;
unsigned int length;
buffer_t *buf;
unsigned char timestamp[RPA_TIMESTAMP_LEN / 2];
realms_len = strlen(realms);
length = sizeof(rpa_oid) + 3 + RPA_SCHALLENGE_LEN +
RPA_TIMESTAMP_LEN + 2 + realms_len;
buf = buffer_create_dynamic(auth->pool, length + 4, (size_t)-1);
buffer_append_c(buf, ASN1_APPLICATION);
buffer_append_asn1_length(buf, length);
buffer_append(buf, rpa_oid, sizeof(rpa_oid));
/* Protocol version */
buffer_append_c(buf, 3);
buffer_append_c(buf, 0);
/* Service challenge */
auth->service_challenge = p_malloc(auth->pool, RPA_SCHALLENGE_LEN);
random_fill(auth->service_challenge, RPA_SCHALLENGE_LEN);
buffer_append_c(buf, RPA_SCHALLENGE_LEN);
buffer_append(buf, auth->service_challenge, RPA_SCHALLENGE_LEN);
/* Timestamp, looks like clients accept anything we send */
random_fill(timestamp, sizeof(timestamp));
auth->service_timestamp = p_malloc(auth->pool, RPA_TIMESTAMP_LEN);
memcpy(auth->service_timestamp,
binary_to_hex(timestamp, sizeof(timestamp)),
RPA_TIMESTAMP_LEN);
buffer_append(buf, auth->service_timestamp, RPA_TIMESTAMP_LEN);
/* Realm list */
buffer_append_c(buf, realms_len >> 8);
buffer_append_c(buf, realms_len & 0xff);
buffer_append(buf, realms, realms_len);
*size = buffer_get_used_size(buf);
return buffer_free_without_data(buf);
}
static const unsigned char *
mech_rpa_build_token4(struct rpa_auth_request *auth, size_t *size)
{
unsigned int length = sizeof(rpa_oid) + 17 + 17 + 1;
buffer_t *buf;
unsigned char server_response[16];
buf = buffer_create_dynamic(auth->pool, length + 4, (size_t)-1);
buffer_append_c(buf, ASN1_APPLICATION);
buffer_append_asn1_length(buf, length);
buffer_append(buf, rpa_oid, sizeof(rpa_oid));
/* Generate random session key */
auth->session_key = p_malloc(auth->pool, 16);
random_fill(auth->session_key, 16);
/* Server authentication response */
rpa_server_response(auth, server_response);
buffer_append_c(buf, 16);
buffer_append(buf, server_response, 16);
buffer_append_c(buf, 16);
buffer_append(buf, auth->session_key, 16);
/* Status, 0 - success */
buffer_append_c(buf, 0);
*size = buffer_get_used_size(buf);
return buffer_free_without_data(buf);
}
static void
rpa_credentials_callback(const char *credentials,
struct auth_request *auth_request)
{
struct rpa_auth_request *auth =
(struct rpa_auth_request *)auth_request;
buffer_t *hash_buffer;
if (credentials == NULL)
return;
auth->pwd_md5 = p_malloc(auth->pool, 16);
hash_buffer = buffer_create_data(auth->pool, auth->pwd_md5, 16);
hex_to_binary(credentials, hash_buffer);
}
static int
mech_rpa_auth_phase1(struct auth_request *auth_request,
const unsigned char *data, size_t data_size,
mech_callback_t *callback)
{
struct rpa_auth_request *auth =
(struct rpa_auth_request *)auth_request;
struct auth_client_request_reply reply;
const unsigned char *token2;
size_t token2_size;
const char *service, *error;
if (!rpa_parse_token1(data, data_size, &error)) {
if (verbose) {
i_info("rpa(%s): invalid token 1, %s",
get_log_prefix(auth_request), error);
}
mech_auth_finish(auth_request, NULL, 0, FALSE);
return TRUE;
}
service = t_str_lcase(auth_request->protocol);
token2 = mech_rpa_build_token2(auth, t_strconcat(service, "@",
my_hostname, NULL), &token2_size);
auth->service_ucs2be = ucs2be_str(auth->pool, service,
&auth->service_len);
auth->realm_ucs2be = ucs2be_str(auth->pool, my_hostname,
&auth->realm_len);
mech_init_auth_client_reply(&reply);
reply.id = auth_request->id;
reply.result = AUTH_CLIENT_RESULT_CONTINUE;
reply.reply_idx = 0;
reply.data_size = token2_size;
callback(&reply, token2, auth_request->conn);
auth->phase = 1;
return TRUE;
}
static int
mech_rpa_auth_phase2(struct auth_request *auth_request,
const unsigned char *data, size_t data_size,
mech_callback_t *callback)
{
struct rpa_auth_request *auth =
(struct rpa_auth_request *)auth_request;
struct auth_client_request_reply reply;
unsigned char response[16];
const unsigned char *token4;
const char *error;
size_t token4_size;
if (!rpa_parse_token3(auth, data, data_size, &error)) {
if (verbose) {
i_info("rpa(%s): invalid token 3, %s",
get_log_prefix(auth_request), error);
}
mech_auth_finish(auth_request, NULL, 0, FALSE);
return TRUE;
}
if (!mech_fix_username(auth_request->user, &error)) {
if (verbose) {
i_info("rpa(%s): %s",
get_log_prefix(auth_request), error);
}
mech_auth_finish(auth_request, NULL, 0, FALSE);
return TRUE;
}
passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_RPA,
rpa_credentials_callback);
if (auth->pwd_md5 == NULL) {
mech_auth_finish(auth_request, NULL, 0, FALSE);
return TRUE;
}
rpa_user_response(auth, response);
if (memcmp(response, auth->user_response, 16) != 0) {
mech_auth_finish(auth_request, NULL, 0, FALSE);
return TRUE;
}
token4 = mech_rpa_build_token4(auth, &token4_size);
mech_init_auth_client_reply(&reply);
reply.id = auth_request->id;
reply.result = AUTH_CLIENT_RESULT_CONTINUE;
reply.reply_idx = 0;
reply.data_size = token4_size;
callback(&reply, token4, auth_request->conn);
auth->phase = 2;
return TRUE;
}
static int
mech_rpa_auth_phase3(struct auth_request *auth_request,
const unsigned char *data, size_t data_size,
mech_callback_t *callback __attr_unused__)
{
static const unsigned char client_ack[3] = { 0x60, 0x01, 0x00 };
int ret = TRUE;
if ((data_size != sizeof(client_ack)) ||
(memcmp(data, client_ack, sizeof(client_ack)) != 0)) {
if (verbose) {
i_info("rpa(%s): invalid token 5 or client rejects us",
get_log_prefix(auth_request));
}
ret = FALSE;
}
mech_auth_finish(auth_request, NULL, 0, ret);
return TRUE;
}
static int
mech_rpa_auth_continue(struct auth_request *auth_request,
const unsigned char *data, size_t data_size,
mech_callback_t *callback)
{
struct rpa_auth_request *auth =
(struct rpa_auth_request *)auth_request;
auth_request->callback = callback;
switch (auth->phase) {
case 0: return mech_rpa_auth_phase1(auth_request, data,
data_size, callback);
case 1: return mech_rpa_auth_phase2(auth_request, data,
data_size, callback);
case 2: return mech_rpa_auth_phase3(auth_request, data,
data_size, callback);
}
mech_auth_finish(auth_request, NULL, 0, FALSE);
return TRUE;
}
static int
mech_rpa_auth_initial(struct auth_request *auth_request,
struct auth_client_request_new *request,
const unsigned char *data __attr_unused__,
mech_callback_t *callback)
{
struct auth_client_request_reply reply;
mech_init_auth_client_reply(&reply);
reply.id = request->id;
reply.result = AUTH_CLIENT_RESULT_CONTINUE;
reply.reply_idx = 0;
reply.data_size = 0;
callback(&reply, "", auth_request->conn);
return TRUE;
}
static void
mech_rpa_auth_free(struct auth_request *auth_request)
{
struct rpa_auth_request *auth =
(struct rpa_auth_request *)auth_request;
if (auth->pwd_md5 != NULL)
safe_memset(auth->pwd_md5, 0, 16);
pool_unref(auth_request->pool);
}
static struct auth_request *mech_rpa_auth_new(void)
{
struct rpa_auth_request *auth;
pool_t pool;
pool = pool_alloconly_create("rpa_auth_request", 256);
auth = p_new(pool, struct rpa_auth_request, 1);
auth->pool = pool;
auth->phase = 0;
auth->auth_request.refcount = 1;
auth->auth_request.pool = pool;
auth->auth_request.auth_initial = mech_rpa_auth_initial;
auth->auth_request.auth_continue = mech_rpa_auth_continue;
auth->auth_request.auth_free = mech_rpa_auth_free;
return &auth->auth_request;
}
const struct mech_module mech_rpa = {
"RPA",
MEMBER(plaintext) FALSE,
MEMBER(advertise) TRUE,
MEMBER(passdb_need_plain) FALSE,
MEMBER(passdb_need_credentials) TRUE,
mech_rpa_auth_new,
};