mail-storages.txt \
multiaccess.txt \
nfs.txt \
- securecoding.txt
+ securecoding.txt \
+ variables.txt
EXTRA_DIST = \
mkcert.sh \
--- /dev/null
+You can use special variables in several places:
+ - default_mail_env setting
+ - namespace locations
+ - static userdb template string
+ - LDAP and SQL userdb query strings
+ - log prefix for imap/pop3 process
+
+The variables are:
+
+ %u - username
+ %n - user part in user@domain, same as %u if there's no domain
+ %d - domain part in user@domain, empty if user there's no domain
+ %h - home directory
+ %p - protocol (IMAP or POP3)
+ %P - PID of the current process (login or imap/pop3 process)
+ %l - local IP address
+ %r - remote IP address
+
+You can apply a modifiers for each variable (eg. %Lp = pop3):
+
+ %L - lowercase
+ %U - uppercase
+ %E - escape '"', "'" and '\' characters by inserting '\' before them.
+
+You can also limit a width of string by giving the number of max. characters
+after the '%' character. For example %1u gives the first character of
+username.
# Default MAIL environment to use when it's not set. By leaving this empty
# dovecot tries to do some automatic detection as described in
-# doc/mail-storages.txt. There's a few special variables you can use:
+# doc/mail-storages.txt. There's a few special variables you can use, eg.:
#
# %u - username
# %n - user part in user@domain, same as %u if there's no domain
# %d - domain part in user@domain, empty if user there's no domain
# %h - home directory
-# %p - protocol (IMAP or POP3)
#
-# You can apply a modifiers for each variable (eg. %Lp = pop3):
-# %L - lowercase
-# %U - uppercase
-# %E - escape '"', "'" and '\' characters by inserting '\' before them.
-#
-# You can also limit a width of string by giving the number of max. characters
-# after the '%' character. For example %1u gives the first character of
-# username. Some examples:
+# See doc/variables.txt for full list. Some examples:
#
# default_mail_env = maildir:/var/mail/%1u/%u/Maildir
# default_mail_env = mbox:~/mail/:INBOX=/var/mail/%u
# files, so it shouldn't harm much even if this limit is set pretty high.
#mail_process_size = 256
+# Log prefix for mail processes. See doc/variables.txt for list of possible
+# variables you can use.
+#mail_log_prefix = "%Up(%u): "
+
##
## IMAP specific settings
##
enum auth_client_request_new_flags flags;
+ uint32_t ip_family; /* if non-zero, data begins with local/remote IPs */
+
uint32_t protocol_idx;
uint32_t mech_idx;
uint32_t initial_resp_idx;
{
struct mech_module *mech;
struct auth_request *auth_request;
+ size_t ip_size = 1;
+
+ if (request->ip_family == AF_INET)
+ ip_size = 4;
+ else if (request->ip_family != 0)
+ ip_size = sizeof(auth_request->local_ip.ip);
+ else
+ ip_size = 0;
/* make sure data is NUL-terminated */
- if (request->data_size == 0 || request->initial_resp_idx == 0 ||
+ if (request->data_size <= ip_size*2 || request->initial_resp_idx == 0 ||
request->mech_idx >= request->data_size ||
request->protocol_idx >= request->data_size ||
request->initial_resp_idx > request->data_size ||
p_strdup(auth_request->pool,
(const char *)data + request->protocol_idx);
+ if (request->ip_family != 0) {
+ auth_request->local_ip.family = request->ip_family;
+ auth_request->remote_ip.family = request->ip_family;
+
+
+ memcpy(&auth_request->local_ip, data, ip_size);
+ memcpy(&auth_request->remote_ip, data + ip_size,
+ ip_size);
+ }
+
hash_insert(conn->auth_requests, POINTER_CAST(request->id),
auth_request);
{ 'n', NULL },
{ 'd', NULL },
{ 'p', NULL },
+ { 'h', NULL },
+ { 'l', NULL },
+ { 'r', NULL },
+ { 'P', NULL },
{ '\0', NULL }
};
struct var_expand_table *tab;
if (tab[2].value != NULL)
tab[2].value = escape_func(tab[2].value+1);
tab[3].value = auth_request->protocol;
+ /* tab[4] = we have no home dir */
+ if (auth_request->local_ip.family != 0)
+ tab[5].value = net_ip2addr(&auth_request->local_ip);
+ if (auth_request->remote_ip.family != 0)
+ tab[6].value = net_ip2addr(&auth_request->remote_ip);
+ tab[7].value = dec2str(auth_request->conn->pid);
return tab;
}
#ifndef __MECH_H
#define __MECH_H
+#include "network.h"
#include "auth-client-interface.h"
struct auth_client_connection;
time_t created;
char *protocol;
+ struct ip_addr local_ip, remote_ip;
mech_callback_t *callback;
int (*auth_initial)(struct auth_request *auth_request,
{
struct imap_client *client = context;
const char *error;
- const void *ptr;
- size_t size;
switch (auth_callback(request, reply, data, &client->common,
master_callback, &error)) {
int cmd_login(struct imap_client *client, struct imap_arg *args)
{
const char *user, *pass, *error;
- string_t *str;
+ struct auth_request_info info;
+ string_t *plain_login;
/* two arguments: username and password */
if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING)
}
/* authorization ID \0 authentication ID \0 pass */
- str = t_str_new(64);
- str_append_c(str, '\0');
- str_append(str, user);
- str_append_c(str, '\0');
- str_append(str, pass);
+ plain_login = t_str_new(64);
+ str_append_c(plain_login, '\0');
+ str_append(plain_login, user);
+ str_append_c(plain_login, '\0');
+ str_append(plain_login, pass);
+
+ memset(&info, 0, sizeof(info));
+ info.mech = "PLAIN";
+ info.protocol = "IMAP";
+ info.flags = client_get_auth_flags(client);
+ info.local_ip = client->common.local_ip;
+ info.remote_ip = client->common.ip;
+ info.initial_resp_data = str_data(plain_login);
+ info.initial_resp_size = str_len(plain_login);
client_ref(client);
client->common.auth_request =
- auth_client_request_new(auth_client, "PLAIN", "IMAP",
- client_get_auth_flags(client),
- str_data(str), str_len(str),
- login_callback,
- client, &error);
+ auth_client_request_new(auth_client, &info,
+ login_callback, client, &error);
if (client->common.auth_request == NULL) {
client_send_tagline(client, t_strconcat(
"NO Login failed: ", error, NULL));
{
const struct auth_mech_desc *mech;
const char *mech_name, *error;
+ struct auth_request_info info;
/* we want only one argument: authentication mechanism name */
if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING)
return TRUE;
}
+ memset(&info, 0, sizeof(info));
+ info.mech = mech->name;
+ info.protocol = "IMAP";
+ info.flags = client_get_auth_flags(client);
+ info.local_ip = client->common.local_ip;
+ info.remote_ip = client->common.ip;
+
client_ref(client);
client->common.auth_request =
- auth_client_request_new(auth_client, mech->name, "IMAP",
- client_get_auth_flags(client),
- NULL, 0, authenticate_callback,
- client, &error);
+ auth_client_request_new(auth_client, &info,
+ authenticate_callback, client, &error);
if (client->common.auth_request != NULL) {
/* following input data will go to authentication */
if (client->common.io != NULL)
}
}
-struct client *client_create(int fd, struct ip_addr *ip, int ssl)
+struct client *client_create(int fd, int ssl, const struct ip_addr *local_ip,
+ const struct ip_addr *ip)
{
struct imap_client *client;
const char *addr;
(IPADDR_IS_V4(ip) && strncmp(addr, "127.", 4) == 0) ||
(IPADDR_IS_V6(ip) && strcmp(addr, "::1") == 0);
+ client->common.local_ip = *local_ip;
client->common.ip = *ip;
client->common.fd = fd;
unsigned int destroyed:1;
};
-struct client *client_create(int fd, struct ip_addr *ip, int ssl);
void client_destroy(struct imap_client *client, const char *reason);
void client_send_line(struct imap_client *client, const char *line);
#ifndef __AUTH_CLIENT_H
#define __AUTH_CLIENT_H
+#include "network.h"
#include "../auth/auth-client-interface.h"
struct auth_client;
unsigned int advertise:1;
};
+struct auth_request_info {
+ const char *mech;
+ const char *protocol;
+ enum auth_client_request_new_flags flags;
+
+ struct ip_addr local_ip, remote_ip;
+
+ const unsigned char *initial_resp_data;
+ size_t initial_resp_size;
+};
+
/* reply is NULL if auth connection died */
typedef void auth_request_callback_t(struct auth_request *request,
struct auth_client_request_reply *reply,
happens for the request. */
struct auth_request *
auth_client_request_new(struct auth_client *client,
- const char *mech, const char *protocol,
- enum auth_client_request_new_flags flags,
- const unsigned char *initial_resp_data,
- size_t initial_resp_size,
+ const struct auth_request_info *request_info,
auth_request_callback_t *callback, void *context,
const char **error_r);
struct auth_request {
struct auth_server_connection *conn;
+ unsigned int id;
+
char *mech, *protocol;
enum auth_client_request_new_flags flags;
-
- unsigned int id;
+ struct ip_addr local_ip, remote_ip;
unsigned char *initial_resp_data;
size_t initial_resp_size;
{
struct auth_client_request_new auth_request;
buffer_t *buf;
+ size_t size;
int ret;
memset(&auth_request, 0, sizeof(auth_request));
auth_request.id = request->id;
auth_request.flags = request->flags;
+ if (request->local_ip.family == request->remote_ip.family)
+ auth_request.ip_family = request->local_ip.family;
+
t_push();
buf = buffer_create_dynamic(pool_datastack_create(), 256, (size_t)-1);
buffer_set_used_size(buf, sizeof(auth_request));
+ if (auth_request.ip_family != 0) {
+ size = IPADDR_IS_V4(&request->local_ip) ? 4 :
+ sizeof(request->local_ip.ip);
+ buffer_append(buf, &request->local_ip.ip, size);
+ buffer_append(buf, &request->remote_ip.ip, size);
+ }
+
auth_request.mech_idx =
buffer_get_used_size(buf) - sizeof(auth_request);
buffer_append(buf, request->mech, strlen(request->mech)+1);
struct auth_request *
auth_client_request_new(struct auth_client *client,
- const char *mech, const char *protocol,
- enum auth_client_request_new_flags flags,
- const unsigned char *initial_resp_data,
- size_t initial_resp_size,
+ const struct auth_request_info *request_info,
auth_request_callback_t *callback, void *context,
const char **error_r)
{
struct auth_server_connection *conn;
struct auth_request *request;
- conn = auth_server_connection_find_mech(client, mech, error_r);
+ conn = auth_server_connection_find_mech(client, request_info->mech,
+ error_r);
if (conn == NULL)
return NULL;
request = i_new(struct auth_request, 1);
request->conn = conn;
- request->mech = i_strdup(mech);
- request->protocol = i_strdup(protocol);
- request->flags = flags;
+ request->mech = i_strdup(request_info->mech);
+ request->protocol = i_strdup(request_info->protocol);
+ request->flags = request_info->flags;
+ request->local_ip = request_info->local_ip;
+ request->remote_ip = request_info->remote_ip;
request->id = ++client->request_id_counter;
- if (initial_resp_size != 0) {
- request->initial_resp_size = initial_resp_size;
- request->initial_resp_data = i_malloc(initial_resp_size);
- memcpy(request->initial_resp_data, initial_resp_data,
- initial_resp_size);
+ if (request_info->initial_resp_size != 0) {
+ request->initial_resp_size = request_info->initial_resp_size;
+ request->initial_resp_data =
+ i_malloc(request_info->initial_resp_size);
+ memcpy(request->initial_resp_data,
+ request_info->initial_resp_data,
+ request_info->initial_resp_size);
}
if (request->id == 0) {
#include "master.h"
struct client {
+ struct ip_addr local_ip;
struct ip_addr ip;
struct ssl_proxy *proxy;
/* ... */
};
-struct client *client_create(int fd, struct ip_addr *ip, int ssl);
+struct client *client_create(int fd, int ssl, const struct ip_addr *local_ip,
+ const struct ip_addr *ip);
unsigned int clients_get_count(void);
void clients_notify_auth_connected(void);
static void login_accept(void *context __attr_unused__)
{
- struct ip_addr ip;
+ struct ip_addr ip, local_ip;
int fd;
fd = net_accept(LOGIN_LISTEN_FD, &ip, NULL);
if (process_per_connection)
main_close_listen();
- (void)client_create(fd, &ip, FALSE);
+ if (net_getsockname(fd, &local_ip, NULL) < 0)
+ memset(&local_ip, 0, sizeof(local_ip));
+
+ (void)client_create(fd, FALSE, &local_ip, &ip);
}
static void login_accept_ssl(void *context __attr_unused__)
{
- struct ip_addr ip;
+ struct ip_addr ip, local_ip;
struct client *client;
struct ssl_proxy *proxy;
int fd, fd_ssl;
if (process_per_connection)
main_close_listen();
+ if (net_getsockname(fd, &local_ip, NULL) < 0)
+ memset(&local_ip, 0, sizeof(local_ip));
fd_ssl = ssl_proxy_new(fd, &ip, &proxy);
if (fd_ssl == -1)
net_disconnect(fd);
else {
- client = client_create(fd_ssl, &ip, TRUE);
+ client = client_create(fd_ssl, TRUE, &local_ip, &ip);
client->proxy = proxy;
}
}
int main(int argc __attr_unused__, char *argv[], char *envp[])
{
const char *name, *group_name;
- struct ip_addr ip;
+ struct ip_addr ip, local_ip;
struct ssl_proxy *proxy = NULL;
struct client *client;
int i, fd = -1, master_fd = -1;
main_init();
if (is_inetd) {
- if (net_getsockname(1, &ip, NULL) < 0) {
+ if (net_getpeername(1, &ip, NULL) < 0) {
i_fatal("%s can be started only through dovecot "
"master process, inetd or equilevant", argv[0]);
}
+ if (net_getsockname(1, &local_ip, NULL) < 0)
+ memset(&local_ip, 0, sizeof(local_ip));
fd = 1;
for (i = 1; i < argc; i++) {
master_init(master_fd, FALSE);
closing_down = TRUE;
- }
- if (fd != -1) {
- client = client_create(fd, &ip, TRUE);
- client->proxy = proxy;
+ if (fd != -1) {
+ client = client_create(fd, TRUE, &local_ip, &ip);
+ client->proxy = proxy;
+ }
}
io_loop_run(ioloop);
req.tag = ++master_tag_counter;
req.auth_pid = auth_pid;
req.auth_id = auth_id;
- req.ip = client->ip;
+ req.local_ip = client->local_ip;
+ req.remote_ip = client->ip;
if (fd_send(master_fd, client->fd, &req, sizeof(req)) != sizeof(req))
i_fatal("fd_send(%d) failed: %m", client->fd);
unsigned int login_tag;
int fd;
- struct ip_addr ip;
+ struct ip_addr local_ip, remote_ip;
};
static unsigned int auth_id_counter, login_pid_counter;
else {
struct login_group *group = request->process->group;
+ t_push();
master_reply.success =
- create_mail_process(group, request->fd, &request->ip,
+ create_mail_process(group, request->fd,
+ &request->local_ip,
+ &request->remote_ip,
reply, (const char *) data);
+ t_pop();
}
/* reply to login */
authreq->tag = ++auth_id_counter;
authreq->login_tag = req.tag;
authreq->fd = client_fd;
- authreq->ip = req.ip;
+ authreq->local_ip = req.local_ip;
+ authreq->remote_ip = req.remote_ip;
auth_process = auth_process_find(req.auth_pid);
if (auth_process == NULL) {
}
static const struct var_expand_table *
-get_var_expand_table(const char *user, const char *home,
- enum process_type process_type)
+get_var_expand_table(enum process_type process_type,
+ const char *user, const char *home,
+ const struct ip_addr *local_ip,
+ const struct ip_addr *remote_ip, pid_t pid)
{
static struct var_expand_table static_tab[] = {
{ 'u', NULL },
{ 'd', NULL },
{ 'p', NULL },
{ 'h', NULL },
+ { 'l', NULL },
+ { 'r', NULL },
+ { 'P', NULL },
{ '\0', NULL }
};
struct var_expand_table *tab;
if (tab[2].value != NULL) tab[2].value++;
tab[3].value = t_str_ucase(process_names[process_type]);
tab[4].value = home;
+ tab[5].value = net_ip2addr(local_ip);
+ tab[6].value = net_ip2addr(remote_ip);
+ tab[7].value = dec2str(pid);
return tab;
}
}
int create_mail_process(struct login_group *group, int socket,
- struct ip_addr *ip,
+ const struct ip_addr *local_ip,
+ const struct ip_addr *remote_ip,
struct auth_master_reply *reply, const char *data)
{
struct settings *set = group->set;
const struct var_expand_table *var_expand_table;
const char *argv[4];
const char *addr, *mail, *user, *chroot_dir, *home_dir, *full_home_dir;
- const char *executable, *p, *prefix;
+ const char *executable, *p;
struct log_io *log;
char title[1024];
+ string_t *str;
pid_t pid;
int i, err, ret, log_fd;
data + reply->virtual_user_idx))
return FALSE;
+ user = data + reply->virtual_user_idx;
+ mail = data + reply->mail_idx;
home_dir = data + reply->home_idx;
chroot_dir = data + reply->chroot_idx;
return FALSE;
}
+ var_expand_table =
+ get_var_expand_table(group->process_type, user, home_dir,
+ local_ip, remote_ip,
+ pid != 0 ? pid : getpid());
+ str = t_str_new(128);
+
if (pid != 0) {
/* master */
- prefix = t_strdup_printf("%s(%s): ",
- process_names[group->process_type],
- data + reply->virtual_user_idx);
- log_set_prefix(log, prefix);
+ var_expand(str, set->mail_log_prefix, var_expand_table);
+ log_set_prefix(log, str_c(str));
mail_process_count++;
PID_ADD_PROCESS_TYPE(pid, group->process_type);
return TRUE;
}
- prefix = t_strdup_printf("master-%s(%s): ",
- process_names[group->process_type],
- data + reply->virtual_user_idx);
- log_set_prefix(log, prefix);
+ str_append(str, "master-");
+ var_expand(str, set->mail_log_prefix, var_expand_table);
+ log_set_prefix(log, str_c(str));
child_process_init_env();
/* user given environment - may be malicious. virtual_user comes from
auth process, but don't trust that too much either. Some auth
mechanism might allow leaving extra data there. */
- mail = data + reply->mail_idx;
- user = data + reply->virtual_user_idx;
-
- var_expand_table =
- get_var_expand_table(user, home_dir, group->process_type);
-
if (*mail == '\0' && set->default_mail_env != NULL)
mail = expand_mail_env(set->default_mail_env, var_expand_table);
env_put(t_strconcat("MAIL=", mail, NULL));
env_put(t_strconcat("USER=", data + reply->virtual_user_idx, NULL));
- addr = net_ip2addr(ip);
+ addr = net_ip2addr(remote_ip);
env_put(t_strconcat("IP=", addr, NULL));
if (!set->verbose_proctitle)
struct auth_master_reply;
int create_mail_process(struct login_group *group, int socket,
- struct ip_addr *ip,
+ const struct ip_addr *local_ip,
+ const struct ip_addr *remote_ip,
struct auth_master_reply *reply, const char *data);
void mail_process_destroyed(pid_t pid);
unsigned int auth_pid;
unsigned int auth_id;
- struct ip_addr ip;
+ struct ip_addr local_ip, remote_ip;
};
struct master_login_reply {
DEF(SET_INT, mail_process_size),
DEF(SET_BOOL, mail_use_modules),
DEF(SET_STR, mail_modules),
+ DEF(SET_STR, mail_log_prefix),
/* imap */
DEF(SET_INT, imap_max_line_length),
MEMBER(mail_process_size) 256,
MEMBER(mail_use_modules) FALSE,
MEMBER(mail_modules) MODULEDIR"/imap",
+ MEMBER(mail_log_prefix) "%Up(%u): ",
/* imap */
MEMBER(imap_max_line_length) 65536,
unsigned int mail_process_size;
int mail_use_modules;
const char *mail_modules;
+ const char *mail_log_prefix;
/* imap */
unsigned int imap_max_line_length;
return TRUE;
}
- /* authorization ID \0 authentication ID \0 pass */
- buffer_set_used_size(client->plain_login, 0);
- buffer_append_c(client->plain_login, '\0');
- buffer_append(client->plain_login, args, strlen(args));
+ i_free(client->last_user);
+ client->last_user = i_strdup(args);
client_send_line(client, "+OK");
return TRUE;
int cmd_pass(struct pop3_client *client, const char *args)
{
const char *error;
+ struct auth_request_info info;
+ string_t *plain_login;
- if (buffer_get_used_size(client->plain_login) == 0) {
+ if (client->last_user == NULL) {
client_send_line(client, "-ERR No username given.");
return TRUE;
}
- buffer_append_c(client->plain_login, '\0');
- buffer_append(client->plain_login, args, strlen(args));
+ /* authorization ID \0 authentication ID \0 pass */
+ plain_login = t_str_new(128);
+ str_append_c(plain_login, '\0');
+ str_append(plain_login, client->last_user);
+ str_append_c(plain_login, '\0');
+ str_append(plain_login, args);
+
+ memset(&info, 0, sizeof(info));
+ info.mech = "PLAIN";
+ info.protocol = "POP3";
+ info.flags = client_get_auth_flags(client);
+ info.local_ip = client->common.local_ip;
+ info.remote_ip = client->common.ip;
+ info.initial_resp_data = str_data(plain_login);
+ info.initial_resp_size = str_len(plain_login);
client_ref(client);
client->common.auth_request =
- auth_client_request_new(auth_client, "PLAIN", "POP3",
- client_get_auth_flags(client),
- str_data(client->plain_login),
- str_len(client->plain_login),
+ auth_client_request_new(auth_client, &info,
login_callback, client, &error);
- buffer_set_used_size(client->plain_login, 0);
if (client->common.auth_request != NULL) {
/* don't read any input from client until login is finished */
int cmd_auth(struct pop3_client *client, const char *args)
{
+ struct auth_request_info info;
const struct auth_mech_desc *mech;
const char *mech_name, *error, *p;
string_t *buf;
return TRUE;
}
+ memset(&info, 0, sizeof(info));
+ info.mech = mech->name;
+ info.protocol = "POP3";
+ info.flags = client_get_auth_flags(client);
+ info.local_ip = client->common.local_ip;
+ info.remote_ip = client->common.ip;
+ info.initial_resp_data = str_data(buf);
+ info.initial_resp_size = str_len(buf);
+
client_ref(client);
client->common.auth_request =
- auth_client_request_new(auth_client, mech->name, "POP3",
- client_get_auth_flags(client),
- str_data(buf), str_len(buf),
+ auth_client_request_new(auth_client, &info,
authenticate_callback, client, &error);
if (client->common.auth_request != NULL) {
/* following input data will go to authentication */
}
}
-struct client *client_create(int fd, struct ip_addr *ip, int ssl)
+struct client *client_create(int fd, int ssl, const struct ip_addr *local_ip,
+ const struct ip_addr *ip)
{
struct pop3_client *client;
const char *addr;
(IPADDR_IS_V4(ip) && strncmp(addr, "127.", 4) == 0) ||
(IPADDR_IS_V6(ip) && strcmp(addr, "::1") == 0);
+ client->common.local_ip = *local_ip;
client->common.ip = *ip;
client->common.fd = fd;
client->common.io = io_add(fd, IO_READ, client_input, client);
client_open_streams(client, fd);
- client->plain_login = buffer_create_dynamic(system_pool, 128, 8192);
client->last_input = ioloop_time;
hash_insert(clients, client, client);
i_stream_unref(client->input);
o_stream_unref(client->output);
- buffer_free(client->plain_login);
i_free(client->common.virtual_user);
i_free(client);
time_t last_input;
unsigned int bad_counter;
- buffer_t *plain_login;
+ char *last_user;
unsigned int tls:1;
unsigned int secured:1;
unsigned int destroyed:1;
};
-struct client *client_create(int fd, struct ip_addr *ip, int ssl);
void client_destroy(struct pop3_client *client, const char *reason);
void client_send_line(struct pop3_client *client, const char *line);