auth-request-handler.c revision cca4ba2a504d70a9fe9fee37f8433997359de52c
#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-master-connection.h"
#include "auth-request-handler.h"
#include <stdlib.h>
#define AUTH_FAILURE_DELAY_CHECK_MSECS 500
struct auth_request_handler {
int refcount;
unsigned int connect_uid, client_pid;
void *context;
unsigned int destroyed:1;
unsigned int token_auth:1;
};
static struct aqueue *auth_failures;
static struct timeout *to_auth_failures;
struct auth_request_handler *
{
struct auth_request_handler *handler;
return handler;
}
unsigned int
{
}
{
struct hash_iterate_context *iter;
void *key;
struct auth_request *auth_request;
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
{
return;
/* 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) {
auth_failure_timeout, (void *)NULL);
}
}
static void
{
/* reset penalty */
}
/* 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 */
}
/* this is normally a hidden field, need to add it explicitly */
}
switch (request->passdb_result) {
case PASSDB_RESULT_OK:
break;
break;
break;
}
}
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);
}
struct auth_request *request,
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)
{
const struct mech_module *mech;
struct auth_request *request;
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;
}
/* Empty initial response is a "=" base64 string. Completely empty
string shouldn't really be sent, but at least Exim does it,
so just allow it for backwards compatibility.. */
"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)
{
struct auth_request *request;
const char *data;
unsigned int id;
i_error("BUG: Authentication client sent broken CONT request");
return FALSE;
}
data++;
const char *reply = t_strdup_printf(
"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;
}
struct auth_request *request)
{
const char *value;
if (request->userdb_lookup_failed)
switch (result) {
if (request->userdb_lookup_failed) {
}
break;
break;
case USERDB_RESULT_OK:
}
/* 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 */
const char *auth_token =
}
break;
}
}
static bool
struct auth_master_connection *master,
unsigned int id)
{
return FALSE;
return TRUE;
}
struct auth_master_connection *master,
const char *const *params)
{
struct auth_request *request;
struct net_unix_cred cred;
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)
{
struct auth_request *request;
}
void auth_request_handler_flush_failures(bool flush_all)
{
unsigned int i, count;
if (count == 0) {
if (to_auth_failures != NULL)
return;
}
for (i = 0; i < count; i++) {
/* FIXME: assumess that failure_delay is always the same. */
break;
&uchar_nul, 0);
}
}
{
}
void auth_request_handler_init(void)
{
}
void auth_request_handler_deinit(void)
{
if (to_auth_failures != NULL)
}