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.
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);
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;
}
}
- 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);
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;
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) {
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);
struct istream *ctrl_input;
struct timeout *to_idle;
- char *username;
+ char *username, *service;
ARRAY_TYPE(const_string) access_apps;
/* settings: */
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);
#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 {
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;
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) {
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]);
}
#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 {
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;
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);
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): ",
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;
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);
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);
}
}
#include "lib-signals.h"
#include "ioloop.h"
#include "buffer.h"
+#include "array.h"
#include "istream.h"
#include "ostream.h"
#include "path-util.h"
}
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;
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
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);
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);
}
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);
}
struct imap_urlauth_connection {
int refcount;
- char *path, *session_id;
+ char *path, *service, *session_id;
struct mail_user *user;
int fd;
#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)
(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);
imap_urlauth_connection_abort(conn, NULL);
i_free(conn->path);
+ i_free(conn->service);
if (conn->session_id != NULL)
i_free(conn->session_id);
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;
}
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)
/* 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);
in_port_t url_port;
char *access_user;
+ char *access_service;
const char **access_applications;
bool access_anonymous:1;
uctx->access_user = i_strdup("anonymous");
else
uctx->access_user = i_strdup(config->access_user);
+ uctx->access_service = i_strdup(config->access_service);
uctx->access_anonymous = config->access_anonymous;
if (config->access_applications != NULL &&
*config->access_applications != NULL) {
if (config->socket_path != NULL) {
uctx->conn = imap_urlauth_connection_init(config->socket_path,
- user, config->session_id, timeout);
+ config->access_service, user, config->session_id, timeout);
}
return uctx;
}
imap_urlauth_connection_deinit(&uctx->conn);
i_free(uctx->url_host);
i_free(uctx->access_user);
+ i_free(uctx->access_service);
i_free(uctx->access_applications);
i_free(uctx);
}
}
static bool
-access_applications_have_access(struct imap_url *url,
- const char *const *access_applications)
+access_applications_have_access(struct imap_urlauth_context *uctx,
+ struct imap_url *url, const char *const *access_applications)
{
const char *const *application;
bool have_userid = FALSE;
size_t len = strlen(app);
- if (app[len-1] == '+') {
+ if (app[len-1] == '+')
have_userid = TRUE;
- app = t_strndup(app, len-1);
- }
- if (strcasecmp(url->uauth_access_application, app) == 0) {
- if (have_userid)
- return url->uauth_access_user != NULL;
- else
+ if (strncasecmp(url->uauth_access_application, app, len-1) == 0) {
+ if (!have_userid) {
+ /* this access application must have no userid */
return url->uauth_access_user == NULL;
+ }
+
+ /* this access application must have a userid */
+ return (!uctx->access_anonymous && url->uauth_access_user != NULL);
}
}
return FALSE;
struct imap_url *url, bool ignore_unknown,
const char **error_r)
{
+ const char *userid;
+
if (url->uauth_access_application == NULL) {
*error_r = "URL is missing URLAUTH";
return FALSE;
}
- if (strcasecmp(url->uauth_access_application, "user") == 0) {
- /* user+<access_user> */
- if (uctx->access_anonymous ||
- strcasecmp(url->uauth_access_user, uctx->access_user) != 0) {
- if (uctx->access_anonymous) {
- *error_r = t_strdup_printf(
- "No 'user+%s' access allowed for anonymous user",
- url->uauth_access_user);
- } else {
- *error_r = t_strdup_printf("No 'user+%s' access allowed for user %s",
- url->uauth_access_user, uctx->access_user);
- }
- return FALSE;
- }
- } else if (strcasecmp(url->uauth_access_application, "authuser") == 0) {
- /* authuser */
- if (uctx->access_anonymous) {
- *error_r = "No 'authuser' access allowed for anonymous user";
- return FALSE;
+ if (strcmp(uctx->access_service, "imap") == 0) {
+ /* these access types are only allowed if URL is accessed through imap */
+ if (strcasecmp(url->uauth_access_application, "user") == 0) {
+ /* user+<access_user> */
+ if (!uctx->access_anonymous ||
+ strcasecmp(url->uauth_access_user, uctx->access_user) == 0)
+ return TRUE;
+ } else if (strcasecmp(url->uauth_access_application, "authuser") == 0) {
+ /* authuser */
+ if (!uctx->access_anonymous)
+ return TRUE;
+ } else if (strcasecmp(url->uauth_access_application, "anonymous") == 0) {
+ /* anonymous */
+ return TRUE;
+ } else if (ignore_unknown || access_applications_have_access
+ (uctx, url, uctx->access_applications)) {
+ return TRUE;
}
- } else if (strcasecmp(url->uauth_access_application, "anonymous") == 0) {
- /* anonymous */
- } else if (!ignore_unknown &&
- !access_applications_have_access(url, uctx->access_applications)) {
- const char *userid = url->uauth_access_user == NULL ? "" :
- t_strdup_printf("+%s", url->uauth_access_user);
-
- if (uctx->access_anonymous) {
+ } else if (strcmp(uctx->access_service, "submission") == 0) {
+ /* accessed directly through submission service */
+
+ if (strcasecmp(url->uauth_access_application, "submit") != 0) {
+ userid = url->uauth_access_user == NULL ? "" :
+ t_strdup_printf("+%s", url->uauth_access_user);
*error_r = t_strdup_printf(
- "No '%s%s' access allowed for anonymous user",
+ "No '%s%s' access allowed for submission service",
url->uauth_access_application, userid);
- } else {
- *error_r = t_strdup_printf(
- "No '%s%s' access allowed for user %s",
- url->uauth_access_application, userid, uctx->access_user);
+ return FALSE;
+ } else if (!uctx->access_anonymous &&
+ strcasecmp(url->uauth_access_user, uctx->access_user) == 0) {
+ return TRUE;
}
- return FALSE;
}
- return TRUE;
+ userid = url->uauth_access_user == NULL ? "" :
+ t_strdup_printf("+%s", url->uauth_access_user);
+
+ if (uctx->access_anonymous) {
+ *error_r = t_strdup_printf(
+ "No '%s%s' access allowed for anonymous user",
+ url->uauth_access_application, userid);
+ } else {
+ *error_r = t_strdup_printf(
+ "No '%s%s' access allowed for user %s",
+ url->uauth_access_application, userid, uctx->access_user);
+ }
+ return FALSE;
}
static bool
const char *socket_path;
const char *session_id;
+ /* the user who is requesting access to URLAUTHs */
const char *access_user;
+ /* ... is using this service (i.e. imap or submission) */
+ const char *access_service;
+ /* ... represents these applications */
const char *const *access_applications;
+ /* ... is anonymous? */
bool access_anonymous;
};