From: Stephan Bosch Date: Mon, 26 Sep 2016 07:25:32 +0000 (+0200) Subject: imap-urlauth: Allow connections from services other than IMAP. X-Git-Tag: 2.3.0.rc1~143 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f1edf7f20661ef9627acbf4054acddcba4d2eb3f;p=thirdparty%2Fdovecot%2Fcore.git imap-urlauth: Allow connections from services other than IMAP. 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. --- diff --git a/src/auth/mech-dovecot-token.c b/src/auth/mech-dovecot-token.c index 16e62bf4c5..ed0b801547 100644 --- a/src/auth/mech-dovecot-token.c +++ b/src/auth/mech-dovecot-token.c @@ -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); diff --git a/src/imap-urlauth/imap-urlauth-client.c b/src/imap-urlauth/imap-urlauth-client.c index cda6bf2b91..9f94041200 100644 --- a/src/imap-urlauth/imap-urlauth-client.c +++ b/src/imap-urlauth/imap-urlauth-client.c @@ -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; @@ -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); @@ -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; @@ -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) { @@ -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); diff --git a/src/imap-urlauth/imap-urlauth-client.h b/src/imap-urlauth/imap-urlauth-client.h index 41b905606a..408c3bc90f 100644 --- a/src/imap-urlauth/imap-urlauth-client.h +++ b/src/imap-urlauth/imap-urlauth-client.h @@ -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: */ @@ -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); diff --git a/src/imap-urlauth/imap-urlauth-login.c b/src/imap-urlauth/imap-urlauth-login.c index ef00ce6bf7..7d10b2b5fb 100644 --- a/src/imap-urlauth/imap-urlauth-login.c +++ b/src/imap-urlauth/imap-urlauth-login.c @@ -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 { @@ -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; @@ -67,15 +67,22 @@ static void imap_urlauth_client_handle_input(struct client *client) return; /* read authentication info from input; - "AUTH"\t\t\t\t */ + "AUTH"\t\t\t\t\t */ 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) { @@ -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]); } diff --git a/src/imap-urlauth/imap-urlauth-worker.c b/src/imap-urlauth/imap-urlauth-worker.c index 08ad3017c0..67a5c954f4 100644 --- a/src/imap-urlauth/imap-urlauth-worker.c +++ b/src/imap-urlauth/imap-urlauth-worker.c @@ -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 { @@ -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; @@ -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); @@ -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): ", @@ -854,13 +857,14 @@ 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; @@ -868,6 +872,9 @@ static void client_ctrl_input(struct client *client) 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); @@ -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); } } diff --git a/src/imap-urlauth/imap-urlauth.c b/src/imap-urlauth/imap-urlauth.c index 5ef31ef5d7..ca0ca97ccc 100644 --- a/src/imap-urlauth/imap-urlauth.c +++ b/src/imap-urlauth/imap-urlauth.c @@ -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" @@ -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; @@ -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 @@ -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); @@ -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); } diff --git a/src/imap/imap-client.c b/src/imap/imap-client.c index e8b2816064..2a778cf681 100644 --- a/src/imap/imap-client.c +++ b/src/imap/imap-client.c @@ -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); } diff --git a/src/lib-imap-urlauth/imap-urlauth-connection.c b/src/lib-imap-urlauth/imap-urlauth-connection.c index 396e8b79ec..13b5692432 100644 --- a/src/lib-imap-urlauth/imap-urlauth-connection.c +++ b/src/lib-imap-urlauth/imap-urlauth-connection.c @@ -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; @@ -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) @@ -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); @@ -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); @@ -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; } @@ -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) diff --git a/src/lib-imap-urlauth/imap-urlauth-connection.h b/src/lib-imap-urlauth/imap-urlauth-connection.h index afe4ab7a0b..b60186a2c7 100644 --- a/src/lib-imap-urlauth/imap-urlauth-connection.h +++ b/src/lib-imap-urlauth/imap-urlauth-connection.h @@ -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); diff --git a/src/lib-imap-urlauth/imap-urlauth-private.h b/src/lib-imap-urlauth/imap-urlauth-private.h index 3dcf798e9e..3b3622c8de 100644 --- a/src/lib-imap-urlauth/imap-urlauth-private.h +++ b/src/lib-imap-urlauth/imap-urlauth-private.h @@ -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; diff --git a/src/lib-imap-urlauth/imap-urlauth.c b/src/lib-imap-urlauth/imap-urlauth.c index 4109399814..39fff7bd07 100644 --- a/src/lib-imap-urlauth/imap-urlauth.c +++ b/src/lib-imap-urlauth/imap-urlauth.c @@ -46,6 +46,7 @@ imap_urlauth_init(struct mail_user *user, 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) { @@ -59,7 +60,7 @@ imap_urlauth_init(struct mail_user *user, 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; } @@ -74,6 +75,7 @@ void imap_urlauth_deinit(struct imap_urlauth_context **_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); } @@ -117,8 +119,8 @@ imap_urlauth_internal_verify(const char *rumpurl, } 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; @@ -131,16 +133,17 @@ access_applications_have_access(struct imap_url *url, 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; @@ -151,51 +154,60 @@ imap_urlauth_check_access(struct imap_urlauth_context *uctx, 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+ */ - 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+ */ + 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 diff --git a/src/lib-imap-urlauth/imap-urlauth.h b/src/lib-imap-urlauth/imap-urlauth.h index 2c48163053..b93271b5af 100644 --- a/src/lib-imap-urlauth/imap-urlauth.h +++ b/src/lib-imap-urlauth/imap-urlauth.h @@ -16,8 +16,13 @@ struct imap_urlauth_config { 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; };