bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch#define IMAP_URLAUTH_WORKER_SOCKET "imap-urlauth-worker"
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch/* max. length of input lines (URLs) */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch/* Disconnect client after idling this many milliseconds */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschstatic int client_worker_connect(struct client *client);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschstatic void client_worker_disconnect(struct client *client);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschstatic void client_worker_input(struct client *client);
f1edf7f20661ef9627acbf4054acddcba4d2eb3fStephan Boschint client_create(const char *service, const char *username,
f1edf7f20661ef9627acbf4054acddcba4d2eb3fStephan Bosch int fd_in, int fd_out, const struct imap_urlauth_settings *set,
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* always use nonblocking I/O */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* determine user's special privileges */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch strcmp(set->imap_urlauth_submit_user, username) == 0) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_debug("User %s has URLAUTH submit access", username);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch strcmp(set->imap_urlauth_stream_user, username) == 0) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_debug("User %s has URLAUTH stream access", username);
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi client->output = o_stream_create_fd(fd_out, (size_t)-1);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch DLLIST_PREPEND(&imap_urlauth_clients, client);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschvoid client_send_line(struct client *client, const char *fmt, ...)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_assert(ret < 0 || (size_t)ret == str_len(str));
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschstatic int client_worker_connect(struct client *client)
f1edf7f20661ef9627acbf4054acddcba4d2eb3fStephan Bosch static const char handshake[] = "VERSION\timap-urlauth-worker\t2\t0\n";
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch socket_path = t_strconcat(client->set->base_dir,
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_debug("Connecting to worker socket %s", socket_path);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch client->fd_ctrl = net_connect_unix_with_retries(socket_path, 1000);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_error("imap-urlauth-client: net_connect_unix(%s) failed: %m",
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* transfer one or two fds */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch data = (client->fd_in == client->fd_out ? '0' : '1');
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch ret = fd_send(client->fd_ctrl, client->fd_in, &data, sizeof(data));
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (ret > 0 && client->fd_in != client->fd_out) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch ret = fd_send(client->fd_ctrl, client->fd_out,
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_error("fd_send(%s, %d) failed to send byte",
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi client->ctrl_output = o_stream_create_fd(client->fd_ctrl, (size_t)-1);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* send protocol version handshake */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (o_stream_send_str(client->ctrl_output, handshake) < 0) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_error("Error sending handshake to imap-urlauth worker: %m");
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi i_stream_create_fd(client->fd_ctrl, MAX_INBUF_SIZE);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch io_add(client->fd_ctrl, IO_READ, client_worker_input, client);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschvoid client_worker_disconnect(struct client *client)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch client->worker_state = IMAP_URLAUTH_WORKER_STATE_INACTIVE;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschclient_worker_input_line(struct client *client, const char *response)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch client_disconnect(client, "Worker handshake failed");
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch client->worker_state = IMAP_URLAUTH_WORKER_STATE_CONNECTED;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch apps = array_get(&client->access_apps, &count);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_assert(ret < 0 || (size_t)ret == str_len(str));
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch "Failed to send ACCESS control command to worker");
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch "Failed to negotiate access parameters");
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch client->worker_state = IMAP_URLAUTH_WORKER_STATE_ACTIVE;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (strcasecmp(response, "DISCONNECTED") == 0) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* worker detected client disconnect */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch } else if (strcasecmp(response, "FINISHED") != 0) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* unknown response */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch "Worker finished with unknown response");
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* connect to new worker for accessing different user */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch "Failed to connect to new worker");
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* indicate success of "END" command */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch client_disconnect(client, "Client disconnected");
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschvoid client_worker_input(struct client *client)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* disconnected */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch client_disconnect(client, "Worker disconnected unexpectedly");
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* disconnected */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch client_disconnect(client, "Worker disconnected unexpectedly");
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* input buffer full */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch client_disconnect(client, "Worker sent too large input");
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch while ((line = i_stream_next_line(input)) != NULL) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (client_worker_input_line(client, line) < 0)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschvoid client_destroy(struct client *client, const char *reason)
ca4526e3b5fbf5ea3dd477a2098522a44c9ac52cTimo Sirainen i_set_failure_prefix("%s: ", master_service_get_name(master_service));
33a80622828063f5be6f743855d5273fabe8ae58Timo Sirainen fd_close_maybe_stdio(&client->fd_in, &client->fd_out);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch master_service_client_connection_destroyed(master_service);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschstatic void client_destroy_timeout(struct client *client)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschvoid client_disconnect(struct client *client, const char *reason)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch client->to_idle = timeout_add(0, client_destroy_timeout, client);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch client_destroy(imap_urlauth_clients, "Server shutting down.");