/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
#include "auth-common.h"
#include "ioloop.h"
#include "array.h"
#include "aqueue.h"
#include "base64.h"
#include "hash.h"
#include "net.h"
#include "str.h"
#include "strescape.h"
#include "str-sanitize.h"
#include "master-interface.h"
#include "auth-penalty.h"
#include "auth-request.h"
#include "auth-token.h"
#include "auth-client-connection.h"
#include "auth-master-connection.h"
#include "auth-request-handler.h"
#include "auth-policy.h"
struct auth_request_handler {
int refcount;
};
struct auth_request_handler *
struct auth_client_connection *conn,
{
return handler;
}
unsigned int
{
}
{
void *key;
switch (auth_request->state) {
case AUTH_REQUEST_STATE_NEW:
break;
break;
case AUTH_REQUEST_STATE_MAX:
i_unreached();
}
}
}
{
return;
/* notify parent that we're done with all requests */
}
{
}
unsigned int connect_uid,
unsigned int client_pid)
{
}
struct auth_request *request)
{
if (request->removed_from_handler) {
/* already removed it */
return;
}
/* if db lookup is stuck, this call doesn't actually free the auth
request, so make sure we don't get back here. */
}
static void
{
}
static void
{
}
}
/* we're proxying */
/* send back the password that was sent by user
(not the password in passdb). */
}
/* the master username needs to be forwarded */
}
}
}
static void
{
if (request->in_delayed_failure_queue) {
/* we came here from flush_failures() */
return;
}
/* remove the request from requests-list */
/* passdb specifically requested not to delay the reply. */
return;
}
/* failure. don't announce it immediately to avoid
a) timing attacks, b) flooding */
if (auth_penalty != NULL) {
}
if (to_auth_failures == NULL) {
}
}
static void
{
/* reset penalty */
}
/* sanitize these fields, since the login code currently assumes they
are exactly in this format. */
/* this request doesn't have to wait for master
process to pick it up. delete it */
}
}
static void
{
}
if (request->internal_failure) {
/* authentication succeeded, but we can't log in
as the wanted user */
} else {
switch (request->passdb_result) {
case PASSDB_RESULT_NEXT:
case PASSDB_RESULT_OK:
break;
break;
break;
}
}
/* this is normally a hidden field, need to add it explicitly */
}
}
}
static void
{
if (success)
else
}
enum auth_client_result result,
{
int ret;
/* the client connection was already closed. we can't do
anything but abort this request */
/* make sure this request is set to finished state
(it's not with result=continue) */
}
switch (result) {
break;
if (reply_size > 0) {
}
if (ret < 0)
else if (ret > 0)
else
return;
break;
break;
}
/* NOTE: request may be destroyed now */
}
{
reply, reply_size);
}
static void
struct auth_request *request,
{
if (*fail_code != '\0') {
}
}
static void auth_request_handler_auth_fail
const char *reason)
{
}
{
/* client's fault */
"Request %u.%u timed out after %u secs, state=%d",
"Request timed out waiting for client to continue authentication "
"(%u secs)", secs);
}
}
{
}
static void
{
unsigned int secs;
if (penalty == 0)
else {
request);
}
}
const char *args)
{
void *initial_resp_data;
unsigned int id;
/* <id> <mechanism> [...] */
i_error("BUG: Authentication client %u "
return FALSE;
}
if (handler->token_auth) {
/* unsupported mechanism */
i_error("BUG: Authentication client %u requested invalid "
"authentication mechanism %s (DOVECOT-TOKEN required)",
return FALSE;
}
} else {
/* unsupported mechanism */
i_error("BUG: Authentication client %u requested unsupported "
return FALSE;
}
}
/* parse optional parameters */
initial_resp = NULL;
arg = "";
} else {
arg++;
}
;
initial_resp = arg;
/* this must be the last parameter */
list++;
break;
}
}
i_error("BUG: Authentication client %u "
"sent AUTH parameters after 'resp'",
return FALSE;
}
i_error("BUG: Authentication client %u "
"didn't specify service in request",
return FALSE;
}
i_error("BUG: Authentication client %u "
return FALSE;
}
!request->valid_client_cert) {
/* we fail without valid certificate */
"Client didn't present valid SSL certificate");
return TRUE;
}
/* Handle initial respose */
if (initial_resp == NULL) {
/* No initial response */
request->initial_response_len = 0;
/* Some authentication clients like Exim send and empty initial
response field when it is in fact absent in the
authentication command. This was allowed for older versions
of the Dovecot authentication protocol. */
request->initial_response_len = 0;
/* Empty initial response - Protocols that use SASL often
use '=' to indicate an empty initial response; i.e., to
distinguish it from an absent initial response. However, that
should not be conveyed to the SASL layer (it is not even
valid Base64); only the empty string should be passed on.
Still, we recognize it here anyway, because we used to make
the same mistake. */
request->initial_response_len = 0;
} else {
/* Initial response encoded in Bas64 */
"Invalid base64 data in initial response");
return TRUE;
}
}
/* handler is referenced until auth_request_handler_reply()
is called. */
/* before we start authenticating, see if we need to wait first */
return TRUE;
}
const char *args)
{
const char *data;
unsigned int id;
i_error("BUG: Authentication client sent broken CONT request");
return FALSE;
}
data++;
"FAIL\t%u\treason=Authentication request timed out", id);
return TRUE;
}
/* accept input only once after mechanism has sent a CONT reply */
if (!request->accept_cont_input) {
"Unexpected continuation");
return TRUE;
}
"Invalid base64 data in continued response");
return TRUE;
}
/* handler is referenced until auth_request_handler_reply()
is called. */
return TRUE;
}
{
}
/* this is an anonymous login, either via ANONYMOUS
SASL mechanism or simply logging in as the anonymous
user via another mechanism */
}
/* generate auth_token when master service provided session_pid */
if (request->request_auth_token &&
const char *auth_token =
}
}
}
struct auth_request *request)
{
const char *value;
switch (result) {
if (request->userdb_lookup_tempfailed) {
}
break;
break;
case USERDB_RESULT_OK:
break;
}
}
static bool
struct auth_master_connection *master,
unsigned int id)
{
return FALSE;
return TRUE;
}
struct auth_master_connection *master,
const char *const *params)
{
i_error("Master request %u.%u not found",
}
param = "";
} else {
param++;
}
}
/* verify session pid if specified and possible */
i_error("Session pid %ld provided by master for request %u.%u "
"did not match peer credentials (pid=%ld, uid=%ld)",
(long)request->session_pid,
}
!request->successful) {
i_error("Master requested unfinished authentication request "
master);
} else {
/* the request isn't being referenced anywhere anymore,
so we can do a bit of kludging.. replace the request's
old client_id with master's id. */
/* master and handler are referenced until userdb_callback i
s called. */
}
return TRUE;
}
unsigned int client_id)
{
}
{
unsigned int i, j, count;
if (count == 0) {
return;
}
/* count the number of requests that we need to flush */
for (i = 0; i < count; i++) {
/* FIXME: assumes that failure_delay is always the same. */
break;
}
/* shuffle these requests to try to prevent any kind of timing attacks
where attacker performs multiple requests in parallel and attempts
to figure out results based on the order of replies. */
count = i;
for (i = 0; i < count; i++) {
/* swap i & j */
}
/* flush the requests */
for (i = 0; i < count; i++) {
uchar_empty_ptr, 0);
}
}
{
}
void auth_request_handler_init(void)
{
}
void auth_request_handler_deinit(void)
{
}