auth-request-handler.c \
auth-settings.c \
auth-stream.c \
+ auth-token.c \
auth-worker-client.c \
auth-worker-server.c \
db-checkpassword.c \
mech-rpa.c \
mech-apop.c \
mech-winbind.c \
+ mech-dovecot-token.c \
passdb.c \
passdb-blocking.c \
passdb-bsdauth.c \
auth-request-handler.h \
auth-settings.h \
auth-stream.h \
+ auth-token.h \
auth-worker-client.h \
auth-worker-server.h \
db-dict.h \
/* handshake complete, we can now actually start serving requests */
conn->refcount++;
conn->request_handler =
- auth_request_handler_create(auth_callback, conn,
+ auth_request_handler_create(conn->token_auth, auth_callback, conn,
!conn->login_requests ? NULL :
auth_master_request_callback);
auth_request_handler_set(conn->request_handler, conn->connect_uid, pid);
}
void auth_client_connection_create(struct auth *auth, int fd,
- bool login_requests)
+ bool login_requests, bool token_auth)
{
static unsigned int connect_uid_counter = 0;
struct auth_client_connection *conn;
+ const char *mechanisms;
string_t *str;
conn = i_new(struct auth_client_connection, 1);
conn->refcount = 1;
conn->connect_uid = ++connect_uid_counter;
conn->login_requests = login_requests;
+ conn->token_auth = token_auth;
random_fill(conn->cookie, sizeof(conn->cookie));
conn->fd = fd;
DLLIST_PREPEND(&auth_client_connections, conn);
+ if (token_auth) {
+ mechanisms = t_strconcat("MECH\t",
+ mech_dovecot_token.mech_name, "\n", NULL);
+ } else {
+ mechanisms = str_c(auth->reg->handshake);
+ }
+
str = t_str_new(128);
str_printfa(str, "VERSION\t%u\t%u\n%sSPID\t%s\nCUID\t%u\nCOOKIE\t",
AUTH_CLIENT_PROTOCOL_MAJOR_VERSION,
AUTH_CLIENT_PROTOCOL_MINOR_VERSION,
- str_c(auth->reg->handshake), my_pid, conn->connect_uid);
+ mechanisms, my_pid, conn->connect_uid);
binary_to_hex_append(str, conn->cookie, sizeof(conn->cookie));
str_append(str, "\nDONE\n");
unsigned int login_requests:1;
unsigned int version_received:1;
+ unsigned int token_auth:1;
};
void auth_client_connection_create(struct auth *auth, int fd,
- bool login_requests);
+ bool login_requests, bool token_auth);
void auth_client_connection_destroy(struct auth_client_connection **conn);
struct auth_client_connection *
master_input_request(struct auth_master_connection *conn, const char *args)
{
struct auth_client_connection *client_conn;
- const char *const *list;
+ const char *const *list, *const *params;
unsigned int id, client_pid, client_id;
uint8_t cookie[MASTER_AUTH_COOKIE_SIZE];
buffer_t buf;
- /* <id> <client-pid> <client-id> <cookie> */
+ /* <id> <client-pid> <client-id> <cookie> [<parameters>] */
list = t_strsplit_tab(args);
if (str_array_length(list) < 4 ||
str_to_uint(list[0], &id) < 0 ||
i_error("BUG: Master sent broken REQUEST cookie");
return FALSE;
}
+ params = list + 4;
client_conn = auth_client_connection_lookup(client_pid);
if (client_conn == NULL) {
o_stream_nsend_str(conn->output,
t_strdup_printf("FAIL\t%u\n", id));
} else if (!auth_request_handler_master_request(
- client_conn->request_handler, conn, id, client_id)) {
+ client_conn->request_handler, conn, id, client_id, params)) {
i_error("Master requested auth for non-login client %u",
client_pid);
o_stream_nsend_str(conn->output,
#include "aqueue.h"
#include "base64.h"
#include "hash.h"
+#include "network.h"
#include "str.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"
auth_request_callback_t *master_callback;
unsigned int destroyed:1;
+ unsigned int token_auth:1;
};
static ARRAY(struct auth_request *) auth_failures_arr;
#undef auth_request_handler_create
struct auth_request_handler *
-auth_request_handler_create(auth_request_callback_t *callback, void *context,
- auth_request_callback_t *master_callback)
+auth_request_handler_create(bool token_auth, auth_request_callback_t *callback,
+ void *context, auth_request_callback_t *master_callback)
{
struct auth_request_handler *handler;
pool_t pool;
handler->callback = callback;
handler->context = context;
handler->master_callback = master_callback;
+ handler->token_auth = token_auth;
return handler;
}
return FALSE;
}
- mech = mech_module_find(list[1]);
- if (mech == NULL) {
- /* unsupported mechanism */
- i_error("BUG: Authentication client %u requested unsupported "
- "authentication mechanism %s", handler->client_pid,
- str_sanitize(list[1], MAX_MECH_NAME_LEN));
- return FALSE;
+ if (handler->token_auth) {
+ mech = &mech_dovecot_token;
+ if (strcmp(list[1], mech->mech_name) != 0) {
+ /* unsupported mechanism */
+ i_error("BUG: Authentication client %u requested invalid "
+ "authentication mechanism %s (DOVECOT-TOKEN required)",
+ handler->client_pid, str_sanitize(list[1], MAX_MECH_NAME_LEN));
+ return FALSE;
+ }
+ } else {
+ mech = mech_module_find(list[1]);
+ if (mech == NULL) {
+ /* unsupported mechanism */
+ i_error("BUG: Authentication client %u requested unsupported "
+ "authentication mechanism %s", handler->client_pid,
+ str_sanitize(list[1], MAX_MECH_NAME_LEN));
+ return FALSE;
+ }
}
request = auth_request_new(mech);
auth_stream_reply_add(request->userdb_reply,
"anonymous", NULL);
}
+
auth_stream_reply_import(reply,
auth_stream_reply_export(request->userdb_reply));
+
+ /* generate auth_token when master service provided session_pid */
+ if (request->session_pid != (pid_t)-1) {
+ const char *auth_token =
+ auth_token_get(request->service,
+ dec2str(request->session_pid),
+ request->user,
+ request->session_id);
+ auth_stream_reply_add(reply, "auth_token", auth_token);
+ }
break;
}
handler->master_callback(reply, request->master);
auth_request_handler_unref(&handler);
}
+static bool
+auth_master_request_failed(struct auth_request_handler *handler,
+ struct auth_master_connection *master,
+ struct auth_stream_reply *reply, unsigned int id)
+{
+ auth_stream_reply_add(reply, "FAIL", NULL);
+ auth_stream_reply_add(reply, NULL, dec2str(id));
+ if (handler->master_callback == NULL)
+ return FALSE;
+ handler->master_callback(reply, master);
+ return TRUE;
+}
+
bool auth_request_handler_master_request(struct auth_request_handler *handler,
struct auth_master_connection *master,
- unsigned int id,
- unsigned int client_id)
+ unsigned int id, unsigned int client_id,
+ const char *const *params)
{
struct auth_request *request;
struct auth_stream_reply *reply;
+ struct net_unix_cred cred;
reply = auth_stream_reply_init(pool_datastack_create());
if (request == NULL) {
i_error("Master request %u.%u not found",
handler->client_pid, client_id);
- auth_stream_reply_add(reply, "FAIL", NULL);
- auth_stream_reply_add(reply, NULL, dec2str(id));
- if (handler->master_callback == NULL)
- return FALSE;
- handler->master_callback(reply, master);
- return TRUE;
+ return auth_master_request_failed(handler, master, reply, id);
}
auth_request_ref(request);
auth_request_handler_remove(handler, request);
+ for (; *params != NULL; params++) {
+ const char *name, *param = strchr(*params, '=');
+
+ if (param == NULL) {
+ name = *params;
+ param = "";
+ } else {
+ name = t_strdup_until(*params, param);
+ param++;
+ }
+
+ (void)auth_request_import_master(request, name, param);
+ }
+
+ /* verify session pid if specified and possible */
+ if (request->session_pid != (pid_t)-1 &&
+ net_getunixcred(master->fd, &cred) == 0 &&
+ cred.pid != (pid_t)-1 && request->session_pid != cred.pid) {
+ 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,
+ handler->client_pid, client_id,
+ (long)cred.pid, (long)cred.uid);
+ return auth_master_request_failed(handler, master, reply, id);
+ }
+
if (request->state != AUTH_REQUEST_STATE_FINISHED ||
!request->successful) {
i_error("Master requested unfinished authentication request "
auth_request_callback_t(struct auth_stream_reply *reply, void *context);
struct auth_request_handler *
-auth_request_handler_create(auth_request_callback_t *callback, void *context,
- auth_request_callback_t *master_callback);
-#define auth_request_handler_create(callback, context, master_callback)\
- auth_request_handler_create( \
+auth_request_handler_create(bool token_auth, auth_request_callback_t *callback,
+ void *context, auth_request_callback_t *master_callback);
+#define auth_request_handler_create(token_auth, callback, context, master_callback)\
+ auth_request_handler_create(token_auth, \
(auth_request_callback_t *)callback, \
(void *)((char*)context + \
CALLBACK_TYPECHECK(callback, void (*)( \
auth_request_handler_get_request_count(struct auth_request_handler *handler);
bool auth_request_handler_master_request(struct auth_request_handler *handler,
struct auth_master_connection *master,
- unsigned int id,
- unsigned int client_id);
+ unsigned int id, unsigned int client_id,
+ const char *const *params);
void auth_request_handler_cancel_request(struct auth_request_handler *handler,
unsigned int client_id);
request->refcount = 1;
request->last_access = ioloop_time;
+ request->session_pid = (pid_t)-1;
request->set = global_auth_settings;
request->mech = mech;
request->refcount = 1;
request->last_access = ioloop_time;
+ request->session_pid = (pid_t)-1;
request->set = global_auth_settings;
return request;
}
return TRUE;
}
+bool auth_request_import_master(struct auth_request *request,
+ const char *key, const char *value)
+{
+ pid_t pid;
+
+ /* master request lookups may set these */
+ if (strcmp(key, "session_pid") == 0) {
+ if (str_to_pid(value, &pid) == 0)
+ request->session_pid = pid;
+ } else
+ return FALSE;
+ return TRUE;
+}
+
bool auth_request_import(struct auth_request *request,
const char *key, const char *value)
{
unsigned int client_pid;
unsigned int id;
time_t last_access;
+ pid_t session_pid;
const char *service, *mech_name, *session_id;
struct ip_addr local_ip, remote_ip;
const char *key, const char *value);
bool auth_request_import_auth(struct auth_request *request,
const char *key, const char *value);
+bool auth_request_import_master(struct auth_request *request,
+ const char *key, const char *value);
void auth_request_initial(struct auth_request *request);
void auth_request_continue(struct auth_request *request,
/* <settings checks> */
static struct file_listener_settings auth_unix_listeners_array[] = {
{ "login/login", 0666, "", "" },
+ { "token-login/token-login", 0666, "", "" },
{ "auth-login", 0600, "$default_internal_user", "" },
{ "auth-client", 0600, "", "" },
{ "auth-userdb", 0666, "$default_internal_user", "" },
&auth_unix_listeners_array[1],
&auth_unix_listeners_array[2],
&auth_unix_listeners_array[3],
- &auth_unix_listeners_array[4]
+ &auth_unix_listeners_array[4],
+ &auth_unix_listeners_array[5]
};
static buffer_t auth_unix_listeners_buf = {
auth_unix_listeners, sizeof(auth_unix_listeners), { 0, }
--- /dev/null
+/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
+
+/* Auth process maintains a random secret. Once a user authenticates the
+ response to the REQUEST command from a master service is augmented with an
+ auth_token value. This token is the SHA1 hash of the secret, the service
+ name and the username of the user that just logged in. Using this token the
+ service (e.g. imap) can login to another service (e.g. imap-urlauth) to
+ gain access to resources that require additional privileges (e.g. another
+ user's e-mail).
+*/
+
+#include "auth-common.h"
+#include "hex-binary.h"
+#include "hmac-sha1.h"
+#include "randgen.h"
+#include "read-full.h"
+#include "write-full.h"
+#include "safe-memset.h"
+#include "auth-settings.h"
+#include "auth-token.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define AUTH_TOKEN_SECRET_LEN 32
+
+#define AUTH_TOKEN_SECRET_FNAME "auth-token-secret.dat"
+
+static unsigned char auth_token_secret[AUTH_TOKEN_SECRET_LEN];
+
+static int
+auth_token_read_secret(const char *path,
+ unsigned char secret_r[AUTH_TOKEN_SECRET_LEN])
+{
+ struct stat st, lst;
+ int fd, ret;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ if (errno != ENOENT)
+ i_error("open(%s) failed: %m", path);
+ return -1;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ i_error("fstat(%s) failed: %m", path);
+ i_close_fd(&fd);
+ return -1;
+ }
+
+ /* check secret len and file type */
+ if (st.st_size != AUTH_TOKEN_SECRET_LEN || !S_ISREG(st.st_mode)) {
+ i_error("Corrupted token secret file: %s", path);
+ i_close_fd(&fd);
+ if (unlink(path) < 0)
+ i_error("unlink(%s) failed: %m", path);
+ return -1;
+ }
+
+ /* verify that we're not dealing with a symbolic link */
+ if (lstat(path, &lst) < 0) {
+ i_error("lstat(%s) failed: %m", path);
+ i_close_fd(&fd);
+ return -1;
+ }
+
+ /* check security parameters for compromise */
+ if ((st.st_mode & 07777) != 0600 || st.st_uid != 0 || st.st_nlink > 1 ||
+ !S_ISREG(lst.st_mode) || st.st_ino != lst.st_ino ||
+ !CMP_DEV_T(st.st_dev, lst.st_dev)) {
+ i_error("Compromised token secret file: %s", path);
+ i_close_fd(&fd);
+ if (unlink(path) < 0)
+ i_error("unlink(%s) failed: %m", path);
+ return -1;
+ }
+
+ /* FIXME: fail here to generate new secret if stored one is too old */
+
+ ret = read_full(fd, secret_r, AUTH_TOKEN_SECRET_LEN);
+ if (ret < 0)
+ i_error("read(%s) failed: %m", path);
+ else if (ret == 0) {
+ i_error("Token secret file unexpectedly shrank: %s", path);
+ ret = -1;
+ }
+ if (close(fd) < 0)
+ i_error("close(%s) failed: %m", path);
+
+ if (global_auth_settings->debug)
+ i_debug("Read auth token secret from %s", path);
+ return ret;
+}
+
+static int
+auth_token_write_secret(const char *path,
+ const unsigned char secret[AUTH_TOKEN_SECRET_LEN])
+{
+ const char *temp_path;
+ mode_t old_mask;
+ int fd, ret;
+
+ i_assert(getuid() == 0);
+
+ temp_path = t_strconcat(path, ".tmp", NULL);
+
+ old_mask = umask(0);
+ fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ umask(old_mask);
+
+ if (fd == -1) {
+ i_error("open(%s) failed: %m", temp_path);
+ return -1;
+ }
+
+ ret = write_full(fd, secret, AUTH_TOKEN_SECRET_LEN);
+ if (ret < 0)
+ i_error("write(%s) failed: %m", temp_path);
+ if (close(fd) < 0) {
+ i_error("close(%s) failed: %m", temp_path);
+ ret = -1;
+ }
+
+ if (ret < 0) {
+ if (unlink(temp_path) < 0)
+ i_error("unlink(%s) failed: %m", temp_path);
+ return -1;
+ }
+
+ if (rename(temp_path, path) < 0) {
+ i_error("rename(%s, %s) failed: %m", temp_path, path);
+ if (unlink(temp_path) < 0)
+ i_error("unlink(%s) failed: %m", temp_path);
+ return -1;
+ }
+
+ if (global_auth_settings->debug)
+ i_debug("Wrote new auth token secret to %s", path);
+ return 0;
+}
+
+void auth_token_init(void)
+{
+ const char *secret_path =
+ t_strconcat(global_auth_settings->base_dir, "/",
+ AUTH_TOKEN_SECRET_FNAME, NULL);
+
+ if (auth_token_read_secret(secret_path, auth_token_secret) < 0) {
+ random_fill(auth_token_secret, sizeof(auth_token_secret));
+
+ if (auth_token_write_secret(secret_path, auth_token_secret) < 0) {
+ i_error("Failed to write auth token secret file; "
+ "returned tokens will be invalid once auth restarts");
+ }
+ }
+}
+
+void auth_token_deinit(void)
+{
+ /* not very useful, but we do it anyway */
+ safe_memset(auth_token_secret, 0, sizeof(auth_token_secret));
+}
+
+const char *auth_token_get(const char *service, const char *session_pid,
+ const char *username, const char *session_id)
+{
+ struct hmac_sha1_context ctx;
+ unsigned char result[SHA1_RESULTLEN];
+
+ hmac_sha1_init(&ctx, username, strlen(username));
+ hmac_sha1_update(&ctx, session_pid, strlen(session_pid));
+ if (session_id != NULL && *session_id != '\0')
+ hmac_sha1_update(&ctx, session_id, strlen(session_id));
+ hmac_sha1_update(&ctx, service, strlen(service));
+ hmac_sha1_update(&ctx, auth_token_secret, sizeof(auth_token_secret));
+ hmac_sha1_final(&ctx, result);
+
+ return binary_to_hex(result, sizeof(result));
+}
--- /dev/null
+#ifndef AUTH_TOKEN_H
+#define AUTH_TOKEN_H
+
+void auth_token_init(void);
+void auth_token_deinit(void);
+
+const char *auth_token_get(const char *service, const char *session_pid,
+ const char *username, const char *session_id);
+
+#endif
+
#include "mech.h"
#include "auth.h"
#include "auth-penalty.h"
+#include "auth-token.h"
#include "auth-request-handler.h"
#include "auth-worker-server.h"
#include "auth-worker-client.h"
AUTH_SOCKET_LOGIN_CLIENT,
AUTH_SOCKET_MASTER,
AUTH_SOCKET_USERDB,
- AUTH_SOCKET_POSTFIX
+ AUTH_SOCKET_POSTFIX,
+ AUTH_SOCKET_TOKEN,
+ AUTH_SOCKET_TOKEN_LOGIN
};
struct auth_socket_listener {
return AUTH_SOCKET_USERDB;
else if (strcmp(suffix, "postmap") == 0)
return AUTH_SOCKET_POSTFIX;
+ else if (strcmp(suffix, "token") == 0)
+ return AUTH_SOCKET_TOKEN;
+ else if (strcmp(suffix, "tokenlogin") == 0)
+ return AUTH_SOCKET_TOKEN_LOGIN;
else
return AUTH_SOCKET_CLIENT;
}
mech_reg, services);
listeners_init();
+ auth_token_init();
/* Password lookups etc. may require roots, allow it. */
restrict_access_by_env(NULL, FALSE);
auths_free();
dict_drivers_unregister_builtin();
+ auth_token_deinit();
+
auth_client_connections_destroy_all();
auth_master_connections_destroy_all();
auth_postfix_connections_destroy_all();
(void)auth_postfix_connection_create(auth, conn->fd);
break;
case AUTH_SOCKET_LOGIN_CLIENT:
- auth_client_connection_create(auth, conn->fd, TRUE);
+ auth_client_connection_create(auth, conn->fd, TRUE, FALSE);
break;
case AUTH_SOCKET_CLIENT:
- auth_client_connection_create(auth, conn->fd, FALSE);
+ auth_client_connection_create(auth, conn->fd, FALSE, FALSE);
+ break;
+ case AUTH_SOCKET_TOKEN_LOGIN:
+ auth_client_connection_create(auth, conn->fd, TRUE, TRUE);
+ break;
+ case AUTH_SOCKET_TOKEN:
+ auth_client_connection_create(auth, conn->fd, FALSE, TRUE);
break;
default:
i_unreached();
--- /dev/null
+/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
+
+/* Used internally by Dovecot processes to authenticate against each others
+ (e.g. imap to imap-urlauth). See auth-token.c */
+
+#include "auth-common.h"
+#include "mech.h"
+#include "safe-memset.h"
+#include "auth-token.h"
+
+static void
+mech_dovecot_token_auth_continue(struct auth_request *request,
+ const unsigned char *data, size_t data_size)
+{
+ const char *session_id, *username, *pid, *service, *error;
+ char *auth_token;
+ size_t i, len;
+ int count;
+
+ /* service \0 pid \0 username \0 session_id \0 auth_token */
+ service = (const char *) data;
+ session_id = username = pid = auth_token = NULL;
+ count = 0;
+ for (i = 0; i < data_size; i++) {
+ if (data[i] == '\0') {
+ count++; i++;
+ if (count == 1)
+ pid = (const char *)data + i;
+ else if (count == 2)
+ username = (const char *)data + i;
+ else if (count == 3)
+ session_id = (const char *)data + i;
+ else {
+ len = data_size - i;
+ auth_token = p_strndup(unsafe_data_stack_pool,
+ data+i, len);
+ break;
+ }
+ }
+ }
+
+ if (count != 4) {
+ /* invalid input */
+ auth_request_log_info(request, "dovecot-token", "invalid input");
+ auth_request_fail(request);
+ } else if (!auth_request_set_username(request, username, &error)) {
+ /* invalid username */
+ auth_request_log_info(request, "dovecot-token", "%s", error);
+ auth_request_fail(request);
+ } else {
+ const char *valid_token =
+ auth_token_get(service, pid, request->user, session_id);
+
+ if (strcmp(auth_token, valid_token) == 0)
+ auth_request_success(request, NULL, 0);
+ else
+ auth_request_fail(request);
+ }
+
+ /* make sure it's cleared */
+ if (auth_token != NULL)
+ safe_memset(auth_token, 0, strlen(auth_token));
+}
+
+static struct auth_request *mech_dovecot_token_auth_new(void)
+{
+ struct auth_request *request;
+ pool_t pool;
+
+ pool = pool_alloconly_create("dovecot_token_auth_request", 512);
+ request = p_new(pool, struct auth_request, 1);
+ request->pool = pool;
+ return request;
+}
+
+const struct mech_module mech_dovecot_token = {
+ "DOVECOT-TOKEN",
+
+ .flags = MECH_SEC_PRIVATE,
+ .passdb_need = MECH_PASSDB_NEED_NOTHING,
+
+ mech_dovecot_token_auth_new,
+ mech_generic_auth_initial,
+ mech_dovecot_token_auth_continue,
+ mech_generic_auth_free
+};
buffer_t *handshake;
};
+extern const struct mech_module mech_dovecot_token;
+
void mech_register_module(const struct mech_module *module);
void mech_unregister_module(const struct mech_module *module);
const struct mech_module *mech_module_find(const char *name);
memset(&login_set, 0, sizeof(login_set));
login_set.postlogin_timeout_secs = MASTER_POSTLOGIN_TIMEOUT_DEFAULT;
+ login_set.request_auth_token = TRUE;
if (IS_STANDALONE() && getuid() == 0 &&
net_getpeername(1, NULL, NULL) == 0) {
#include "lib.h"
#include "network.h"
#include "ioloop.h"
+#include "hostpid.h"
#include "istream.h"
#include "ostream.h"
#include "llist.h"
pid_t auth_server_pid;
+ unsigned int request_auth_token:1;
unsigned int version_received:1;
unsigned int spid_received:1;
};
static void master_login_auth_set_timeout(struct master_login_auth *auth);
static void master_login_auth_check_spids(struct master_login_auth *auth);
-struct master_login_auth *master_login_auth_init(const char *auth_socket_path)
+struct master_login_auth *
+master_login_auth_init(const char *auth_socket_path, bool request_auth_token)
{
struct master_login_auth *auth;
pool_t pool;
auth = p_new(pool, struct master_login_auth, 1);
auth->pool = pool;
auth->auth_socket_path = p_strdup(pool, auth_socket_path);
+ auth->request_auth_token = request_auth_token;
auth->refcount = 1;
auth->fd = -1;
hash_table_create_direct(&auth->requests, pool, 0);
str_printfa(str, "REQUEST\t%u\t%u\t%u\t", req->id,
req->client_pid, req->auth_id);
binary_to_hex_append(str, req->cookie, sizeof(req->cookie));
+ if (auth->request_auth_token)
+ str_printfa(str, "\tsession_pid=%s", my_pid);
str_append_c(str, '\n');
o_stream_nsend(auth->output, str_data(str), str_len(str));
}
master_login_auth_request_callback_t(const char *const *auth_args,
const char *errormsg, void *context);
-struct master_login_auth *master_login_auth_init(const char *auth_socket_path);
+struct master_login_auth *
+master_login_auth_init(const char *auth_socket_path, bool request_auth_token);
void master_login_auth_deinit(struct master_login_auth **auth);
void master_login_auth_disconnect(struct master_login_auth *auth);
login->service = service;
login->callback = set->callback;
login->failure_callback = set->failure_callback;
- login->auth = master_login_auth_init(set->auth_socket_path);
+ login->auth = master_login_auth_init(set->auth_socket_path,
+ set->request_auth_token);
login->postlogin_socket_path = i_strdup(set->postlogin_socket_path);
login->postlogin_timeout_secs = set->postlogin_timeout_secs;
master_login_callback_t *callback;
master_login_failure_callback_t *failure_callback;
+
+ unsigned int request_auth_token:1;
};
struct master_login *
enum mail_storage_service_flags flags;
struct ioloop_context *ioloop_ctx;
- const char *log_prefix;
+ const char *log_prefix, *auth_token;
const char *system_groups_user, *uid_source, *gid_source;
const struct mail_user_settings *user_set;
i_error("setpriority(%d) failed: %m", n);
}
#endif
+ } else if (strncmp(line, "auth_token=", 11) == 0) {
+ user->auth_token = p_strdup(user->pool, line+11);
} else T_BEGIN {
ret = set_line(ctx, user, line);
} T_END;
mail_user->uid = priv->uid == (uid_t)-1 ? geteuid() : priv->uid;
mail_user->gid = priv->gid == (gid_t)-1 ? getegid() : priv->gid;
mail_user->anonymous = user->anonymous;
+ mail_user->auth_token = p_strdup(mail_user->pool, user->auth_token);
mail_set = mail_user_set_get_storage_set(mail_user);
gid_t gid;
const char *service;
struct ip_addr *local_ip, *remote_ip;
+ const char *auth_token;
+
const struct var_expand_table *var_expand_table;
/* If non-NULL, fail the user initialization with this error.
This could be set by plugins that need to fail the initialization. */
static void client_auth_input(struct client *client)
{
+ i_assert(client->v.auth_parse_response != NULL);
client->v.auth_parse_response(client);
}
client_destroy_success(client, data);
break;
case SASL_SERVER_REPLY_CONTINUE:
+ i_assert(client->v.auth_send_challenge != NULL);
client->v.auth_send_challenge(client, data);
if (client->to_auth_waiting != NULL)
if (client->login_proxy != NULL)
login_proxy_free(&client->login_proxy);
- client->v.destroy(client);
+ if (client->v.destroy != NULL)
+ client->v.destroy(client);
if (client_unref(&client) && initial_service_count == 1) {
/* as soon as this connection is done with proxying
(or whatever), the process will die. there's no need for
const char *text)
{
if (!client->notified_disconnect) {
- client->v.notify_disconnect(client, reason, text);
+ if (client->v.notify_disconnect != NULL)
+ client->v.notify_disconnect(client, reason, text);
client->notified_disconnect = TRUE;
}
}
void client_notify_auth_ready(struct client *client)
{
if (!client->notified_auth_ready) {
- client->v.notify_auth_ready(client);
+ if (client->v.notify_auth_ready != NULL)
+ client->v.notify_auth_ready(client);
client->notified_auth_ready = TRUE;
}
}
#define AUTH_PLAINTEXT_DISABLED_MSG \
"Plaintext authentication disallowed on non-secure (SSL/TLS) connections."
+#define LOGIN_DEFAULT_SOCKET "login"
+#define LOGIN_TOKEN_DEFAULT_SOCKET "token-login"
+
struct login_binary {
/* e.g. imap, pop3 */
const char *protocol;
/* e.g. 993, 995. if there is no ssl port, use 0. */
unsigned int default_ssl_port;
+ /* if value is NULL, LOGIN_DEFAULT_SOCKET is used as the default */
+ const char *default_login_socket;
+
const struct client_vfuncs *client_vfuncs;
void (*preinit)(void);
void (*init)(void);
#include <unistd.h>
#include <syslog.h>
-#define DEFAULT_LOGIN_SOCKET "login"
#define AUTH_CLIENT_IDLE_TIMEOUT_MSECS (1000*60)
struct login_access_lookup {
MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE;
pool_t set_pool;
bool allow_core_dumps = FALSE;
- const char *login_socket = DEFAULT_LOGIN_SOCKET;
+ const char *login_socket;
int c;
login_binary = binary;
+ login_socket = binary->default_login_socket != NULL ?
+ binary->default_login_socket : LOGIN_DEFAULT_SOCKET;
master_service = master_service_init(login_binary->process_name,
service_flags, &argc, &argv,
(void)closedir(dirp);
}
+static void
+mkdir_login_dir(const struct master_settings *set, const char *login_dir)
+{
+ mode_t mode;
+ gid_t gid;
+
+ if (settings_have_auth_unix_listeners_in(set, login_dir)) {
+ /* we are not using external authentication, so make sure the
+ login directory exists with correct permissions and it's
+ empty. with external auth we wouldn't want to delete
+ existing sockets or break the permissions required by the
+ auth server. */
+ mode = login_want_core_dumps(set, &gid) ? 0770 : 0750;
+ if (safe_mkdir(login_dir, mode, master_uid, gid) == 0) {
+ i_warning("Corrected permissions for login directory "
+ "%s", login_dir);
+ }
+
+ unlink_sockets(login_dir, "");
+ } else {
+ /* still make sure that login directory exists */
+ if (mkdir(login_dir, 0755) < 0 && errno != EEXIST)
+ i_fatal("mkdir(%s) failed: %m", login_dir);
+ }
+}
+
void master_settings_do_fixes(const struct master_settings *set)
{
- const char *login_dir, *empty_dir;
+ const char *empty_dir;
struct stat st;
- gid_t gid;
/* since base dir is under /var/run by default, it may have been
deleted. */
if (mkdir_parents(set->state_dir, 0755) < 0 && errno != EEXIST)
i_fatal("mkdir(%s) failed: %m", set->state_dir);
- login_dir = t_strconcat(set->base_dir, "/login", NULL);
- if (settings_have_auth_unix_listeners_in(set, login_dir)) {
- /* we are not using external authentication, so make sure the
- login directory exists with correct permissions and it's
- empty. with external auth we wouldn't want to delete
- existing sockets or break the permissions required by the
- auth server. */
- mode_t mode = login_want_core_dumps(set, &gid) ? 0770 : 0750;
- if (safe_mkdir(login_dir, mode, master_uid, gid) == 0) {
- i_warning("Corrected permissions for login directory "
- "%s", login_dir);
- }
-
- unlink_sockets(login_dir, "");
- } else {
- /* still make sure that login directory exists */
- if (mkdir(login_dir, 0755) < 0 && errno != EEXIST)
- i_fatal("mkdir(%s) failed: %m", login_dir);
- }
+ mkdir_login_dir(set, t_strconcat(set->base_dir, "/login", NULL));
+ mkdir_login_dir(set, t_strconcat(set->base_dir, "/token-login", NULL));
empty_dir = t_strconcat(set->base_dir, "/empty", NULL);
if (safe_mkdir(empty_dir, 0755, master_uid, getegid()) == 0) {