auth-request.c revision 04052d7cacaa866a3f00afb4e104fa46c04c1dd7
/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */
#include "auth-common.h"
#include "ioloop.h"
#include "buffer.h"
#include "hash.h"
#include "sha1.h"
#include "hex-binary.h"
#include "str.h"
#include "safe-memset.h"
#include "str-sanitize.h"
#include "strescape.h"
#include "var-expand.h"
#include "auth-cache.h"
#include "auth-request.h"
#include "auth-request-handler.h"
#include "auth-client-connection.h"
#include "auth-master-connection.h"
#include "passdb.h"
#include "passdb-blocking.h"
#include "passdb-cache.h"
#include "passdb-template.h"
#include "userdb-blocking.h"
#include "userdb-template.h"
#include "password-scheme.h"
#include <stdlib.h>
#define CACHED_PASSWORD_SCHEME "SHA1"
unsigned int auth_request_state_count[AUTH_REQUEST_STATE_MAX];
const char *subsystem);
struct auth_request *
{
struct auth_request *request;
return request;
}
struct auth_request *auth_request_new_dummy(void)
{
struct auth_request *request;
return request;
}
enum auth_request_state state)
{
return;
}
{
}
{
}
{
if (request->passdb_failure) {
/* password was valid, but some other check failed. */
return;
}
}
{
NULL, 0);
}
{
}
{
}
{
return;
else
}
struct auth_stream_reply *reply)
{
}
}
}
if (request->local_port != 0) {
}
if (request->remote_port != 0) {
}
if (request->skip_password_check)
if (request->valid_client_cert)
if (request->no_penalty)
if (request->successful)
}
{
/* get username from SSL certificate. it overrides
the username given by the auth mechanism. */
}
else
return FALSE;
return TRUE;
}
{
}
{
}
enum passdb_result result)
{
const char *extra_fields, *encoded_password;
switch (result) {
case PASSDB_RESULT_OK:
/* can be cached */
break;
/* FIXME: we can't cache this now, or cache lookup would
return success. */
return;
i_unreached();
}
return;
if (result < 0) {
/* lookup failed. */
if (result == PASSDB_RESULT_USER_UNKNOWN) {
}
return;
}
/* passdb didn't provide the correct password */
if (result != PASSDB_RESULT_OK ||
return;
/* we can still cache valid password lookups though.
strdup() it so that mech_password doesn't get
cleared too early. */
i_unreached();
}
/* save all except the currently given password in cache */
/* cached passwords must have a known scheme */
}
}
}
if (*extra_fields != '\0') {
}
}
result == PASSDB_RESULT_OK);
}
{
const char *str, *p;
/* reset the reply and add the new username */
/* add the rest */
if (p != NULL)
}
{
struct auth_passdb *passdb;
if (request->passdb_failure)
return TRUE;
/* master login successful. update user and master_user variables. */
/* skip the passdb lookup, we're authenticated now. */
return TRUE;
}
/* the authentication continues with passdb lookup for the
requested_login_user. */
break;
}
"No passdbs support skipping password verification - "
"pass=yes can't be used in master passdb");
}
return FALSE;
}
static bool
struct auth_request *request)
{
}
*result != PASSDB_RESULT_USER_UNKNOWN) {
/* deny passdb. we can get through this step only if the
lookup returned that user doesn't exist in it. internal
errors are fatal here. */
if (*result != PASSDB_RESULT_INTERNAL_FAILURE) {
"User found from deny passdb");
}
} else if (*result == PASSDB_RESULT_OK) {
/* success */
/* this was a master user lookup. */
return FALSE;
} else {
/* this wasn't the final passdb lookup,
continue to next passdb */
return FALSE;
}
}
} else if (*result == PASSDB_RESULT_PASS_EXPIRED) {
}
"Password expired");
/* try next passdb. */
if (*result == PASSDB_RESULT_USER_UNKNOWN) {
/* remember that we did at least one successful
passdb lookup */
} else if (*result == PASSDB_RESULT_INTERNAL_FAILURE) {
/* remember that we have had an internal failure. at
the end return internal failure if we couldn't
successfully login. */
}
return FALSE;
} else if (request->passdb_internal_failure) {
/* last passdb lookup returned internal failure. it may have
had the correct password, so return internal failure
instead of plain failure. */
}
return TRUE;
}
static void
struct auth_request *request)
{
/* try next passdb */
} else {
}
}
struct auth_request *request)
{
if (result != PASSDB_RESULT_INTERNAL_FAILURE) {
} else {
/* lookup failed. if we're looking here only because the
request was expired in cache, fallback to using cached
expired record. */
"Fallbacking to expired data from cache");
}
}
}
static bool password_has_illegal_chars(const char *password)
{
switch (*password) {
case '\001':
case '\t':
case '\r':
case '\n':
/* these characters have a special meaning in internal
protocols, make sure the password doesn't
accidentally get there unescaped. */
return TRUE;
}
}
return FALSE;
}
const char *password,
{
struct passdb_module *passdb;
enum passdb_result result;
const char *cache_key;
/* no masterdbs, master logins not supported */
"Attempted master login with no master passdbs "
"(trying to log in as user: %s)",
return;
}
if (password_has_illegal_chars(password)) {
"Attempted login with password having illegal chars");
return;
}
else
return;
}
/* we're deinitializing and just want to get rid of this
request */
}
}
static void
const unsigned char *credentials,
struct auth_request *request)
{
/* try next passdb */
} else {
result == PASSDB_RESULT_OK) {
"Credentials: %s",
}
if (result == PASSDB_RESULT_SCHEME_NOT_AVAILABLE &&
/* one of the passdbs accepted the scheme,
but the user was unknown there */
}
}
}
const unsigned char *credentials,
struct auth_request *request)
{
const char *cache_cred, *cache_scheme;
if (result != PASSDB_RESULT_INTERNAL_FAILURE) {
} else {
/* lookup failed. if we're looking here only because the
request was expired in cache, fallback to using cached
expired record. */
"Fallbacking to expired data from cache");
request);
return;
}
}
request);
}
const char *scheme,
{
enum passdb_result result;
request);
return;
}
}
/* this passdb doesn't support credentials */
"passdb doesn't support credential lookups");
} else {
}
}
{
const char *cache_key, *new_credentials;
callback);
} else {
/* this passdb doesn't support credentials update */
}
}
enum userdb_result result)
{
const char *str;
return;
/* last_success has no meaning with userdb */
}
const char *key,
struct auth_stream_reply **reply_r,
enum userdb_result *result_r,
bool use_expired)
{
const char *value;
struct auth_cache_node *node;
bool expired, neg_expired;
return FALSE;
&expired, &neg_expired);
return FALSE;
}
if (*value == '\0') {
/* negative cache entry */
return TRUE;
}
return TRUE;
}
struct auth_request *request)
{
/* try next userdb. */
if (result == USERDB_RESULT_INTERNAL_FAILURE)
return;
}
if (result == USERDB_RESULT_OK)
else if (request->userdb_internal_failure) {
/* one of the userdb lookups failed. the user might have been
in there, so this is an internal failure */
} else if (result == USERDB_RESULT_USER_UNKNOWN &&
request->client_pid != 0) {
/* this was an actual login attempt, the user should
have been found. */
"user not found from userdb %s",
} else {
"user not found from any userdbs");
}
}
if (request->userdb_lookup_failed) {
/* no caching */
} else if (result != USERDB_RESULT_INTERNAL_FAILURE)
/* lookup failed. if we're looking here only because the
request was expired in cache, fallback to using cached
expired record. */
struct auth_stream_reply *reply;
"Fallbacking to expired data from cache");
}
}
}
{
const char *cache_key;
/* (for now) auth_cache is shared between passdb and userdb */
struct auth_stream_reply *reply;
enum userdb_result result;
return;
}
}
/* we are deinitializing */
request);
else
}
static char *
const char **error_r)
{
unsigned char *p;
char *user;
} else {
}
for (p = (unsigned char *)user; *p != '\0'; p++) {
"Username character disallowed by auth_username_chars: "
"0x%02x (username: %s)", *p,
return NULL;
}
}
/* username format given, put it through variable expansion.
we'll have to temporarily replace request->user to get
%u to be the wanted username */
const struct var_expand_table *table;
char *old_username;
}
return user;
}
{
const char *p, *login_username = NULL;
/* check if the username contains a master user */
if (p != NULL) {
/* it does, set it. */
if (*login_username == '\0') {
*error_r = "Empty login username";
return FALSE;
}
/* username is the master user */
username = p + 1;
}
}
/* the username may change later, but we need to use this
username when verifying at least DIGEST-MD5 password. */
}
if (request->cert_username) {
/* cert_username overrides the username given by
authentication mechanism. but still do checks and
translations to it. */
}
if (*username == '\0') {
/* Some PAM plugins go nuts with empty usernames */
*error_r = "Empty username";
return FALSE;
}
return FALSE;
/* similar to original_username, but after translations */
}
if (login_username != NULL) {
error_r))
return FALSE;
}
return TRUE;
}
const char *username,
const char **error_r)
{
/* The usernames are the same, we don't really wish to log
in as someone else */
return TRUE;
}
/* lookup request->user from masterdb first */
return FALSE;
"Master user lookup for login: %s",
return TRUE;
}
const char *networks)
{
const char *const *net;
unsigned int bits;
/* IP not known */
"allow_nets check failed: Remote IP not known");
return;
}
"allow_nets: Matching for network %s", *net);
"allow_nets: Invalid network '%s'", *net);
}
break;
}
}
if (!found) {
"allow_nets check failed: IP not in allowed networks");
}
}
static void
const char *default_scheme, bool noscheme)
{
"Multiple password values not supported");
return;
}
/* if the password starts with '{' it most likely contains
also '}'. check it anyway to make sure, because we
assert-crash later if it doesn't exist. this could happen
if plaintext passwords are used. */
else {
}
}
{
/* user can't actually login - don't keep this
reply for master */
/* we're proxying authentication for this user. send
password back if using plaintext authentication. */
/* like "proxy", but log in normally if we're proxying to
ourself */
}
}
static const char *
get_updated_username(const char *old_username,
{
const char *p;
/* replace the whole username */
return value;
}
return value;
/* preserve the current @domain */
}
if (p == NULL) {
/* add the domain */
} else {
/* replace the existing domain */
}
}
return NULL;
}
static bool
{
const char *new_value;
return FALSE;
"username changed %s -> %s",
}
return TRUE;
}
const char *default_scheme)
{
return;
}
return;
}
/* don't change the original value so it gets saved correctly
to cache. */
/* don't delay replying to client of the failure */
/* NULL password - anything goes */
(void)password_get_scheme(&password);
if (*password != '\0') {
"nopassword set but password is "
"non-empty");
return;
}
}
/* for prefetch userdb */
} else {
/* these fields are returned to client */
return;
}
if ((passdb_cache != NULL &&
/* we'll need to get this field stored into cache,
or we're a worker and we'll need to send this to the main
auth process that can store it in the cache. */
}
}
}
const char *const *fields,
const char *default_scheme)
{
if (**fields == '\0')
continue;
value = "";
} else {
value++;
}
}
}
{
}
const char *path_template)
{
} else {
}
}
{
return;
}
return;
}
return;
return;
return;
return;
/* FIXME: the system_user is for backwards compatibility */
if (!warned) {
i_warning("userdb: Replace system_user with system_groups_user");
}
name = "system_groups_user";
}
}
const char *name,
const char *const *values)
{
return;
/* convert gids to comma separated list */
return;
}
}
} else {
/* add only one */
"Multiple values found for '%s', "
}
}
}
{
}
/* broken setup */
return FALSE;
}
return FALSE;
return FALSE;
}
{
return;
if (!success) {
/* drop all proxy fields */
} else if (!request->proxy_maybe) {
/* proxying */
return;
} else if (!auth_request_proxy_is_self(request)) {
/* proxy destination isn't ourself - proxy */
return;
} else {
/* proxying to ourself - log in without proxying by dropping
all the proxying fields. */
}
}
const char *plain_password,
const char *crypted_password,
const char *subsystem)
{
const char *working_scheme;
if (!scheme_ok) {
/* perhaps the scheme is wrong - see if we can find
a working one */
if (working_scheme != NULL) {
}
}
}
const char *subsystem)
{
return;
}
unsigned char sha1[SHA1_RESULTLEN];
} else {
i_unreached();
}
}
const char *plain_password,
const char *crypted_password,
{
const unsigned char *raw_password;
const char *error;
int ret;
if (request->skip_password_check) {
/* currently this can happen only with master logins */
return 1;
}
/* this is a deny database, we don't care about the password */
return 0;
}
if (request->no_password) {
"Allowing any password");
return 1;
}
if (ret <= 0) {
if (ret < 0) {
"Password in passdb is not in expected scheme %s",
scheme);
} else {
"Unknown scheme %s", scheme);
}
return -1;
}
/* Use original_username since it may be important for some
password schemes (eg. digest-md5). Otherwise the username is used
only for logging purposes. */
if (ret < 0) {
"Invalid password%s in passdb: %s",
} else if (ret == 0) {
}
} T_END;
return ret;
}
static const char *
escape_none(const char *string,
{
return string;
}
const char *
auth_request_str_escape(const char *string,
{
return str_escape(string);
}
const struct var_expand_table *
{
static struct var_expand_table static_tab[] = {
};
struct var_expand_table *tab;
if (escape_func == NULL)
/* tab[4] = we have no home dir */
}
if (auth_request->userdb_lookup) {
} else {
}
}
}
return tab;
}
const char *subsystem)
{
#define MAX_LOG_USERNAME_LEN 64
const char *ip;
else {
}
}
}
static const char * ATTR_FORMAT(3, 0)
{
}
const char *subsystem,
const char *format, ...)
{
return;
T_BEGIN {
} T_END;
}
const char *subsystem,
const char *format, ...)
{
return;
T_BEGIN {
} T_END;
}
const char *subsystem,
const char *format, ...)
{
T_BEGIN {
} T_END;
}
const char *subsystem,
const char *format, ...)
{
T_BEGIN {
} T_END;
}
{
}