1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
3
#include "imap-urlauth-common.h"
11
#include "strescape.h"
12
#include "eacces-error.h"
15
#include "execv-const.h"
17
#include "var-expand.h"
18
#include "restrict-access.h"
19
#include "master-service.h"
20
#include "master-interface.h"
26
#define IMAP_URLAUTH_PROTOCOL_MAJOR_VERSION 1
27
#define IMAP_URLAUTH_PROTOCOL_MINOR_VERSION 0
29
#define IMAP_URLAUTH_WORKER_SOCKET "imap-urlauth-worker"
31
/* max. length of input lines (URLs) */
32
#define MAX_INBUF_SIZE 2048
34
/* Disconnect client after idling this many milliseconds */
35
#define CLIENT_IDLE_TIMEOUT_MSECS (10*60*1000)
37
#define USER_EXECUTABLE "imap-urlauth-worker"
39
#define IS_STANDALONE() \
40
(getenv(MASTER_IS_PARENT_ENV) == NULL)
42
struct client *imap_urlauth_clients;
43
unsigned int imap_urlauth_client_count;
45
static int client_worker_connect(struct client *client);
46
static void client_worker_disconnect(struct client *client);
47
static void client_worker_input(struct client *client);
49
int client_create(const char *username, int fd_in, int fd_out,
50
const struct imap_urlauth_settings *set,
51
struct client **client_r)
53
struct client *client;
56
/* always use nonblocking I/O */
57
net_set_nonblock(fd_in, TRUE);
58
net_set_nonblock(fd_out, TRUE);
60
client = i_new(struct client, 1);
61
client->fd_in = fd_in;
62
client->fd_out = fd_out;
66
if (client_worker_connect(client) < 0) {
71
/* determine user's special privileges */
72
i_array_init(&client->access_apps, 4);
73
if (username != NULL) {
74
if (set->imap_urlauth_submit_user != NULL &&
75
strcmp(set->imap_urlauth_submit_user, username) == 0) {
77
i_debug("User %s has URLAUTH submit access", username);
79
array_append(&client->access_apps, &app, 1);
81
if (set->imap_urlauth_stream_user != NULL &&
82
strcmp(set->imap_urlauth_stream_user, username) == 0) {
84
i_debug("User %s has URLAUTH stream access", username);
86
array_append(&client->access_apps, &app, 1);
91
client->username = i_strdup(username);
93
client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
95
imap_urlauth_client_count++;
96
DLLIST_PREPEND(&imap_urlauth_clients, client);
98
imap_urlauth_refresh_proctitle();
103
void client_send_line(struct client *client, const char *fmt, ...)
108
if (client->output->closed)
116
str = t_str_new(256);
117
str_vprintfa(str, fmt, va);
118
str_append(str, "\n");
120
ret = o_stream_send(client->output,
121
str_data(str), str_len(str));
122
i_assert(ret < 0 || (size_t)ret == str_len(str));
128
static int client_worker_connect(struct client *client)
130
static const char handshake[] = "VERSION\timap-urlauth-worker\t1\t0\n";
131
const char *socket_path;
135
socket_path = t_strconcat(client->set->base_dir,
136
"/"IMAP_URLAUTH_WORKER_SOCKET, NULL);
138
if (client->set->mail_debug)
139
i_debug("Connecting to worker socket %s", socket_path);
141
client->fd_ctrl = net_connect_unix_with_retries(socket_path, 1000);
142
if (client->fd_ctrl < 0) {
143
if (errno == EACCES) {
144
i_error("imap-urlauth-client: %s",
145
eacces_error_get("net_connect_unix",
148
i_error("imap-urlauth-client: net_connect_unix(%s) failed: %m",
154
/* transfer one or two fds */
155
data = (client->fd_in == client->fd_out ? '0' : '1');
156
ret = fd_send(client->fd_ctrl, client->fd_in, &data, sizeof(data));
157
if (ret > 0 && client->fd_in != client->fd_out) {
159
ret = fd_send(client->fd_ctrl, client->fd_out,
160
&data, sizeof(data));
165
i_error("fd_send(%s, %d) failed: %m",
166
socket_path, client->fd_ctrl);
168
i_error("fd_send(%s, %d) failed to send byte",
169
socket_path, client->fd_ctrl);
171
client_worker_disconnect(client);
175
client->ctrl_output =
176
o_stream_create_fd(client->fd_ctrl, (size_t)-1, FALSE);
178
/* send protocol version handshake */
179
if (o_stream_send_str(client->ctrl_output, handshake) < 0) {
180
i_error("Error sending handshake to imap-urlauth worker: %m");
181
client_worker_disconnect(client);
186
i_stream_create_fd(client->fd_ctrl, MAX_INBUF_SIZE, FALSE);
188
io_add(client->fd_ctrl, IO_READ, client_worker_input, client);
192
void client_worker_disconnect(struct client *client)
194
client->worker_state = IMAP_URLAUTH_WORKER_STATE_INACTIVE;
196
if (client->ctrl_io != NULL)
197
io_remove(&client->ctrl_io);
198
if (client->ctrl_output != NULL)
199
o_stream_destroy(&client->ctrl_output);
200
if (client->ctrl_input != NULL)
201
i_stream_destroy(&client->ctrl_input);
202
if (client->fd_ctrl >= 0) {
203
net_disconnect(client->fd_ctrl);
204
client->fd_ctrl = -1;
209
client_worker_input_line(struct client *client, const char *response)
211
const char *const *apps;
212
unsigned int count, i;
217
switch (client->worker_state) {
218
case IMAP_URLAUTH_WORKER_STATE_INACTIVE:
219
if (strcasecmp(response, "OK") != 0) {
220
client_disconnect(client, "Worker handshake failed");
223
client->worker_state = IMAP_URLAUTH_WORKER_STATE_CONNECTED;
225
str = t_str_new(256);
226
str_append(str, "ACCESS\t");
227
if (client->username != NULL)
228
str_append_tabescaped(str, client->username);
229
if (client->set->mail_debug)
230
str_append(str, "\tdebug");
231
if (array_count(&client->access_apps) > 0) {
232
str_append(str, "\tapps=");
233
apps = array_get(&client->access_apps, &count);
234
str_append(str, apps[0]);
235
for (i = 1; i < count; i++) {
236
str_append_c(str, ',');
237
str_append_tabescaped(str, apps[i]);
240
str_append(str, "\n");
242
ret = o_stream_send(client->ctrl_output,
243
str_data(str), str_len(str));
244
i_assert(ret < 0 || (size_t)ret == str_len(str));
246
client_disconnect(client,
247
"Failed to send ACCESS control command to worker");
252
case IMAP_URLAUTH_WORKER_STATE_CONNECTED:
253
if (strcasecmp(response, "OK") != 0) {
254
client_disconnect(client,
255
"Failed to negotiate access parameters");
258
client->worker_state = IMAP_URLAUTH_WORKER_STATE_ACTIVE;
261
case IMAP_URLAUTH_WORKER_STATE_ACTIVE:
263
if (strcasecmp(response, "DISCONNECTED") == 0) {
264
/* worker detected client disconnect */
266
} else if (strcasecmp(response, "FINISHED") != 0) {
267
/* unknown response */
268
client_disconnect(client,
269
"Worker finished with unknown response");
273
if (client->set->mail_debug)
274
i_debug("Worker finished successfully");
277
/* connect to new worker for accessing different user */
278
client_worker_disconnect(client);
279
if (client_worker_connect(client) < 0) {
280
client_disconnect(client,
281
"Failed to connect to new worker");
285
/* indicate success of "END" command */
286
client_send_line(client, "OK");
288
client_disconnect(client, "Client disconnected");
297
void client_worker_input(struct client *client)
299
struct istream *input = client->ctrl_input;
304
client_disconnect(client, "Worker disconnected unexpectedly");
308
switch (i_stream_read(input)) {
311
client_disconnect(client, "Worker disconnected unexpectedly");
314
/* input buffer full */
315
client_disconnect(client, "Worker sent too large input");
319
while ((line = i_stream_next_line(input)) != NULL) {
320
if (client_worker_input_line(client, line) < 0)
325
void client_destroy(struct client *client, const char *reason)
327
i_set_failure_prefix("%s: ", master_service_get_name(master_service));
329
if (!client->disconnected) {
331
reason = "Connection closed";
332
i_info("Disconnected: %s", reason);
335
imap_urlauth_client_count--;
336
DLLIST_REMOVE(&imap_urlauth_clients, client);
338
if (client->to_idle != NULL)
339
timeout_remove(&client->to_idle);
341
client_worker_disconnect(client);
343
o_stream_destroy(&client->output);
345
net_disconnect(client->fd_in);
346
if (client->fd_in != client->fd_out)
347
net_disconnect(client->fd_out);
349
if (client->username != NULL)
350
i_free(client->username);
351
array_free(&client->access_apps);
354
master_service_client_connection_destroyed(master_service);
355
imap_urlauth_refresh_proctitle();
358
static void client_destroy_timeout(struct client *client)
360
client_destroy(client, NULL);
363
void client_disconnect(struct client *client, const char *reason)
365
if (client->disconnected)
368
client->disconnected = TRUE;
369
i_info("Disconnected: %s", reason);
371
client->to_idle = timeout_add(0, client_destroy_timeout, client);
374
void clients_destroy_all(void)
376
while (imap_urlauth_clients != NULL)
377
client_destroy(imap_urlauth_clients, "Server shutting down.");