Skip to content

Commit

Permalink
imap-urlauth: Allow connections from services other than IMAP.
Browse files Browse the repository at this point in the history
The imap-urlauth service detects the new submission service and assigns the appropriate privileges.
The dovecot-token authentication mechanism provides information on which service connected to it.
  • Loading branch information
stephanbosch authored and sirainen committed Dec 11, 2017
1 parent dfafc4a commit f1edf7f
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 80 deletions.
1 change: 1 addition & 0 deletions src/auth/mech-dovecot-token.c
Expand Up @@ -54,6 +54,7 @@ mech_dovecot_token_auth_continue(struct auth_request *request,
if (auth_token != NULL &&
strcmp(auth_token, valid_token) == 0) {
request->passdb_success = TRUE;
auth_request_set_field(request, "userdb_client_service", service, "");
auth_request_success(request, NULL, 0);
} else {
auth_request_fail(request);
Expand Down
16 changes: 9 additions & 7 deletions src/imap-urlauth/imap-urlauth-client.c
Expand Up @@ -45,8 +45,8 @@ static int client_worker_connect(struct client *client);
static void client_worker_disconnect(struct client *client);
static void client_worker_input(struct client *client);

int client_create(const char *username, int fd_in, int fd_out,
const struct imap_urlauth_settings *set,
int client_create(const char *service, const char *username,
int fd_in, int fd_out, const struct imap_urlauth_settings *set,
struct client **client_r)
{
struct client *client;
Expand Down Expand Up @@ -86,8 +86,8 @@ int client_create(const char *username, int fd_in, int fd_out,
}
}

if (username != NULL)
client->username = i_strdup(username);
client->username = i_strdup(username);
client->service = i_strdup(service);

client->output = o_stream_create_fd(fd_out, (size_t)-1);

Expand Down Expand Up @@ -126,7 +126,7 @@ void client_send_line(struct client *client, const char *fmt, ...)

static int client_worker_connect(struct client *client)
{
static const char handshake[] = "VERSION\timap-urlauth-worker\t1\t0\n";
static const char handshake[] = "VERSION\timap-urlauth-worker\t2\t0\n";
const char *socket_path;
ssize_t ret;
unsigned char data;
Expand Down Expand Up @@ -221,6 +221,8 @@ client_worker_input_line(struct client *client, const char *response)
str_append(str, "ACCESS\t");
if (client->username != NULL)
str_append_tabescaped(str, client->username);
str_append(str, "\t");
str_append_tabescaped(str, client->service);
if (client->set->mail_debug)
str_append(str, "\tdebug");
if (array_count(&client->access_apps) > 0) {
Expand Down Expand Up @@ -338,8 +340,8 @@ void client_destroy(struct client *client, const char *reason)

fd_close_maybe_stdio(&client->fd_in, &client->fd_out);

if (client->username != NULL)
i_free(client->username);
i_free(client->username);
i_free(client->service);
array_free(&client->access_apps);
i_free(client);

Expand Down
6 changes: 3 additions & 3 deletions src/imap-urlauth/imap-urlauth-client.h
Expand Up @@ -19,7 +19,7 @@ struct client {
struct istream *ctrl_input;
struct timeout *to_idle;

char *username;
char *username, *service;
ARRAY_TYPE(const_string) access_apps;

/* settings: */
Expand All @@ -33,8 +33,8 @@ struct client {
extern struct client *imap_urlauth_clients;
extern unsigned int imap_urlauth_client_count;

int client_create(const char *username, int fd_in, int fd_out,
const struct imap_urlauth_settings *set,
int client_create(const char *service, const char *username,
int fd_in, int fd_out, const struct imap_urlauth_settings *set,
struct client **client_r);
void client_destroy(struct client *client, const char *reason);

Expand Down
19 changes: 13 additions & 6 deletions src/imap-urlauth/imap-urlauth-login.c
Expand Up @@ -13,7 +13,7 @@
#include "client-common.h"
#include "imap-urlauth-login-settings.h"

#define IMAP_URLAUTH_PROTOCOL_MAJOR_VERSION 1
#define IMAP_URLAUTH_PROTOCOL_MAJOR_VERSION 2
#define IMAP_URLAUTH_PROTOCOL_MINOR_VERSION 0

struct imap_urlauth_client {
Expand Down Expand Up @@ -41,7 +41,7 @@ imap_urlauth_client_auth_result(struct client *client,

static void imap_urlauth_client_handle_input(struct client *client)
{
#define AUTH_ARG_COUNT 5
#define AUTH_ARG_COUNT 6
struct imap_urlauth_client *uauth_client =
(struct imap_urlauth_client *)client;
struct net_unix_cred cred;
Expand All @@ -67,15 +67,22 @@ static void imap_urlauth_client_handle_input(struct client *client)
return;

/* read authentication info from input;
"AUTH"\t<session-pid>\t<auth-username>\t<session_id>\t<token> */
"AUTH"\t<service>\t<session-pid>\t<auth-username>\t<session_id>\t<token> */
args = t_strsplit_tabescaped(line);
if (str_array_length(args) < AUTH_ARG_COUNT ||
strcmp(args[0], "AUTH") != 0 || str_to_pid(args[1], &pid) < 0) {
strcmp(args[0], "AUTH") != 0 || str_to_pid(args[2], &pid) < 0) {
i_error("IMAP URLAUTH client sent unexpected AUTH input: %s", line);
client_destroy(client, "Disconnected: Unexpected input");
return;
}

/* only imap and submission have direct access to urlauth service */
if (strcmp(args[1], "imap") != 0 && strcmp(args[1], "submission") != 0) {
i_error("IMAP URLAUTH accessed from inappropriate service: %s", args[1]);
client_destroy(client, "Disconnected: Unexpected input");
return;
}

/* verify session pid if possible */
if (net_getunixcred(client->fd, &cred) == 0 &&
cred.pid != (pid_t)-1 && pid != cred.pid) {
Expand All @@ -91,8 +98,8 @@ static void imap_urlauth_client_handle_input(struct client *client)
string_t *init_resp;
unsigned int i;

str_append(auth_data, "imap");
for (i = 1; i < AUTH_ARG_COUNT; i++) {
str_append(auth_data, args[1]);
for (i = 2; i < AUTH_ARG_COUNT; i++) {
str_append_c(auth_data, '\0');
str_append(auth_data, args[i]);
}
Expand Down
21 changes: 14 additions & 7 deletions src/imap-urlauth/imap-urlauth-worker.c
Expand Up @@ -42,7 +42,7 @@
#define IS_STANDALONE() \
(getenv(MASTER_IS_PARENT_ENV) == NULL)

#define IMAP_URLAUTH_WORKER_PROTOCOL_MAJOR_VERSION 1
#define IMAP_URLAUTH_WORKER_PROTOCOL_MAJOR_VERSION 2
#define IMAP_URLAUTH_WORKER_PROTOCOL_MINOR_VERSION 0

struct client {
Expand All @@ -55,7 +55,7 @@ struct client {
struct ostream *output, *ctrl_output;
struct timeout *to_idle;

char *access_user;
char *access_user, *access_service;
ARRAY_TYPE(string) access_apps;

struct mail_storage_service_user *service_user;
Expand Down Expand Up @@ -255,6 +255,7 @@ static void client_destroy(struct client *client)
if (client->service_user != NULL)
mail_storage_service_user_unref(&client->service_user);
i_free(client->access_user);
i_free(client->access_service);
array_foreach_modifiable(&client->access_apps, app)
i_free(*app);
array_free(&client->access_apps);
Expand Down Expand Up @@ -636,14 +637,16 @@ client_handle_user_command(struct client *client, const char *cmd,
config.url_host = set->imap_urlauth_host;
config.url_port = set->imap_urlauth_port;
config.access_user = client->access_user;
config.access_service = client->access_service;
config.access_anonymous = client->access_anonymous;
config.access_applications =
(const void *)array_get(&client->access_apps, &count);

client->urlauth_ctx = imap_urlauth_init(client->mail_user, &config);
if (client->debug) {
i_debug("Providing access to user account `%s' on behalf of `%s'",
mail_user->username, client->access_user);
i_debug("Providing access to user account `%s' on behalf of user `%s' "
"using service `%s'", mail_user->username, client->access_user,
client->access_service);
}

i_set_failure_prefix("imap-urlauth[%s](%s->%s): ",
Expand Down Expand Up @@ -854,20 +857,24 @@ static void client_ctrl_input(struct client *client)
return;
}
args++;
if (*args == NULL) {
if (args[0] == NULL || args[1] == NULL) {
i_error("Invalid ACCESS command: %s", str_sanitize(line, 80));
client_abort(client, "Control session aborted: Invalid command");
return;
}

i_assert(client->access_user == NULL);
i_assert(client->access_service == NULL);
if (**args != '\0') {
client->access_user = i_strdup(*args);
client->access_anonymous = FALSE;
} else {
client->access_user = i_strdup("anonymous");
client->access_anonymous = TRUE;
}
args++;
client->access_service = i_strdup(*args);

i_set_failure_prefix("imap-urlauth[%s](%s): ",
my_pid, client->access_user);

Expand Down Expand Up @@ -912,8 +919,8 @@ static void client_ctrl_input(struct client *client)
o_stream_set_flush_callback(client->output, client_output, client);

if (client->debug) {
i_debug("Worker activated for access by user %s",
client->access_user);
i_debug("Worker activated for access by user `%s' using service `%s'",
client->access_user, client->access_service);
}
}

Expand Down
30 changes: 26 additions & 4 deletions src/imap-urlauth/imap-urlauth.c
Expand Up @@ -47,6 +47,7 @@ The imap-urlauth service thus consists of three separate stages:
#include "lib-signals.h"
#include "ioloop.h"
#include "buffer.h"
#include "array.h"
#include "istream.h"
#include "ostream.h"
#include "path-util.h"
Expand Down Expand Up @@ -102,11 +103,12 @@ static void imap_urlauth_die(void)
}

static int
client_create_from_input(const char *username, int fd_in, int fd_out)
client_create_from_input(const char *service, const char *username,
int fd_in, int fd_out)
{
struct client *client;

if (client_create(username, fd_in, fd_out,
if (client_create(service, username, fd_in, fd_out,
imap_urlauth_settings, &client) < 0)
return -1;

Expand All @@ -123,7 +125,7 @@ static void main_stdio_run(const char *username)
if (username == NULL)
i_fatal("USER environment missing");

(void)client_create_from_input(username, STDIN_FILENO, STDOUT_FILENO);
(void)client_create_from_input("", username, STDIN_FILENO, STDOUT_FILENO);
}

static void
Expand All @@ -133,6 +135,9 @@ login_client_connected(const struct master_login_client *client,
const char *msg = "NO\n";
struct auth_user_reply reply;
struct net_unix_cred cred;
const char *const *fields;
const char *service = NULL;
unsigned int count, i;

auth_user_fields_parse(extra_fields, pool_datastack_create(), &reply);

Expand All @@ -149,10 +154,27 @@ login_client_connected(const struct master_login_client *client,
return;
}

fields = array_get(&reply.extra_fields, &count);
for (i = 0; i < count; i++) {
if (strncmp(fields[i], "client_service=", 15) == 0) {
service = fields[i] + 15;
break;
}
}

if (service == NULL) {
i_error("Auth did not yield required client_service field (BUG).");
if (write(client->fd, msg, strlen(msg)) < 0) {
/* ignored */
}
net_disconnect(client->fd);
return;
}

if (reply.anonymous)
username = NULL;

if (client_create_from_input(username, client->fd, client->fd) < 0)
if (client_create_from_input(service, username, client->fd, client->fd) < 0)
net_disconnect(client->fd);
}

Expand Down
3 changes: 2 additions & 1 deletion src/imap/imap-client.c
Expand Up @@ -65,8 +65,9 @@ static void client_init_urlauth(struct client *client)
config.socket_path = t_strconcat(client->user->set->base_dir,
"/"IMAP_URLAUTH_SOCKET_NAME, NULL);
config.session_id = client->session_id;
config.access_anonymous = client->user->anonymous;
config.access_user = client->user->username;
config.access_service = "imap";
config.access_anonymous = client->user->anonymous;

client->urlauth_ctx = imap_urlauth_init(client->user, &config);
}
Expand Down
15 changes: 9 additions & 6 deletions src/lib-imap-urlauth/imap-urlauth-connection.c
Expand Up @@ -56,7 +56,7 @@ struct imap_urlauth_target {
struct imap_urlauth_connection {
int refcount;

char *path, *session_id;
char *path, *service, *session_id;
struct mail_user *user;

int fd;
Expand Down Expand Up @@ -87,7 +87,7 @@ struct imap_urlauth_connection {

#define IMAP_URLAUTH_RESPONSE_TIMEOUT_MSECS 2*60*1000

#define IMAP_URLAUTH_HANDSHAKE "VERSION\timap-urlauth\t1\t0\n"
#define IMAP_URLAUTH_HANDSHAKE "VERSION\timap-urlauth\t2\t0\n"

#define IMAP_URLAUTH_MAX_INLINE_LITERAL_SIZE (1024*32)

Expand All @@ -105,14 +105,15 @@ static void imap_urlauth_connection_fail
(struct imap_urlauth_connection *conn);

struct imap_urlauth_connection *
imap_urlauth_connection_init(const char *path, struct mail_user *user,
const char *session_id,
imap_urlauth_connection_init(const char *path, const char *service,
struct mail_user *user, const char *session_id,
unsigned int idle_timeout_msecs)
{
struct imap_urlauth_connection *conn;

conn = i_new(struct imap_urlauth_connection, 1);
conn->refcount = 1;
conn->service = i_strdup(service);
conn->path = i_strdup(path);
if (session_id != NULL)
conn->session_id = i_strdup(session_id);
Expand All @@ -132,6 +133,7 @@ void imap_urlauth_connection_deinit(struct imap_urlauth_connection **_conn)
imap_urlauth_connection_abort(conn, NULL);

i_free(conn->path);
i_free(conn->service);
if (conn->session_id != NULL)
i_free(conn->session_id);

Expand Down Expand Up @@ -891,7 +893,7 @@ imap_urlauth_connection_do_connect(struct imap_urlauth_connection *conn)

if (conn->user->auth_token == NULL) {
i_error("imap-urlauth: cannot authenticate because no auth token "
"is available for this session (standalone IMAP?).");
"is available for this session (running standalone?).");
imap_urlauth_connection_abort(conn, NULL);
return -1;
}
Expand All @@ -917,7 +919,8 @@ imap_urlauth_connection_do_connect(struct imap_urlauth_connection *conn)
conn->state = IMAP_URLAUTH_STATE_AUTHENTICATING;

str = t_str_new(128);
str_printfa(str, IMAP_URLAUTH_HANDSHAKE"AUTH\t%s\t", my_pid);
str_printfa(str, IMAP_URLAUTH_HANDSHAKE"AUTH\t%s\t%s\t",
conn->service, my_pid);
str_append_tabescaped(str, conn->user->username);
str_append_c(str, '\t');
if (conn->session_id != NULL)
Expand Down
4 changes: 2 additions & 2 deletions src/lib-imap-urlauth/imap-urlauth-connection.h
Expand Up @@ -11,8 +11,8 @@ imap_urlauth_request_callback_t(struct imap_urlauth_fetch_reply *reply,
/* If reconnect_callback is specified, it's called when connection is lost.
If the callback returns FALSE, reconnection isn't attempted. */
struct imap_urlauth_connection *
imap_urlauth_connection_init(const char *path, struct mail_user *user,
const char *session_id,
imap_urlauth_connection_init(const char *path, const char *service,
struct mail_user *user, const char *session_id,
unsigned int idle_timeout_msecs);
void imap_urlauth_connection_deinit(struct imap_urlauth_connection **conn);

Expand Down
1 change: 1 addition & 0 deletions src/lib-imap-urlauth/imap-urlauth-private.h
Expand Up @@ -11,6 +11,7 @@ struct imap_urlauth_context {
in_port_t url_port;

char *access_user;
char *access_service;
const char **access_applications;

bool access_anonymous:1;
Expand Down

0 comments on commit f1edf7f

Please sign in to comment.