~ubuntu-branches/ubuntu/trusty/dovecot/trusty-updates

« back to all changes in this revision

Viewing changes to src/imap-urlauth/imap-urlauth-client.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-08 09:35:49 UTC
  • mfrom: (1.15.3) (96.1.1 trusty-proposed)
  • Revision ID: package-import@ubuntu.com-20140108093549-814nkqdcxfbvgktg
Tags: 1:2.2.9-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + Use the autotools-dev dh addon to update config.guess/config.sub for
    arm64.
* Dropped changes, included in Debian:
  - Update Dovecot name to reflect distribution in login greeting.
  - Update Drac plugin for >= 2.0.0 support.
* d/control: Drop dovecot-postfix package as its no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "imap-urlauth-common.h"
 
4
#include "array.h"
 
5
#include "ioloop.h"
 
6
#include "net.h"
 
7
#include "fdpass.h"
 
8
#include "istream.h"
 
9
#include "ostream.h"
 
10
#include "str.h"
 
11
#include "strescape.h"
 
12
#include "eacces-error.h"
 
13
#include "llist.h"
 
14
#include "hostpid.h"
 
15
#include "execv-const.h"
 
16
#include "env-util.h"
 
17
#include "var-expand.h"
 
18
#include "restrict-access.h"
 
19
#include "master-service.h"
 
20
#include "master-interface.h"
 
21
 
 
22
#include <stdlib.h>
 
23
#include <unistd.h>
 
24
#include <sys/wait.h>
 
25
 
 
26
#define IMAP_URLAUTH_PROTOCOL_MAJOR_VERSION 1
 
27
#define IMAP_URLAUTH_PROTOCOL_MINOR_VERSION 0
 
28
 
 
29
#define IMAP_URLAUTH_WORKER_SOCKET "imap-urlauth-worker"
 
30
 
 
31
/* max. length of input lines (URLs) */
 
32
#define MAX_INBUF_SIZE 2048
 
33
 
 
34
/* Disconnect client after idling this many milliseconds */
 
35
#define CLIENT_IDLE_TIMEOUT_MSECS (10*60*1000)
 
36
 
 
37
#define USER_EXECUTABLE "imap-urlauth-worker"
 
38
 
 
39
#define IS_STANDALONE() \
 
40
        (getenv(MASTER_IS_PARENT_ENV) == NULL)
 
41
 
 
42
struct client *imap_urlauth_clients;
 
43
unsigned int imap_urlauth_client_count;
 
44
 
 
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);
 
48
 
 
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)
 
52
{
 
53
        struct client *client;
 
54
        const char *app;
 
55
 
 
56
        /* always use nonblocking I/O */
 
57
        net_set_nonblock(fd_in, TRUE);
 
58
        net_set_nonblock(fd_out, TRUE);
 
59
 
 
60
        client = i_new(struct client, 1);
 
61
        client->fd_in = fd_in;
 
62
        client->fd_out = fd_out;
 
63
        client->fd_ctrl = -1;
 
64
        client->set = set;
 
65
 
 
66
        if (client_worker_connect(client) < 0) {
 
67
                i_free(client);
 
68
                return -1;
 
69
        }
 
70
 
 
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) {
 
76
                        if (set->mail_debug)
 
77
                                i_debug("User %s has URLAUTH submit access", username);
 
78
                        app = "submit+";
 
79
                        array_append(&client->access_apps, &app, 1);
 
80
                }
 
81
                if (set->imap_urlauth_stream_user != NULL &&
 
82
                    strcmp(set->imap_urlauth_stream_user, username) == 0) {
 
83
                        if (set->mail_debug)
 
84
                                i_debug("User %s has URLAUTH stream access", username);
 
85
                        app = "stream";
 
86
                        array_append(&client->access_apps, &app, 1);
 
87
                }
 
88
        }
 
89
 
 
90
        if (username != NULL)
 
91
                client->username = i_strdup(username);
 
92
 
 
93
        client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
 
94
 
 
95
        imap_urlauth_client_count++;
 
96
        DLLIST_PREPEND(&imap_urlauth_clients, client);
 
97
 
 
98
        imap_urlauth_refresh_proctitle();
 
99
        *client_r = client;
 
100
        return 0;
 
101
}
 
102
 
 
103
void client_send_line(struct client *client, const char *fmt, ...)
 
104
{
 
105
        va_list va;
 
106
        ssize_t ret;
 
107
 
 
108
        if (client->output->closed)
 
109
                return;
 
110
 
 
111
        va_start(va, fmt);
 
112
 
 
113
        T_BEGIN {
 
114
                string_t *str;
 
115
 
 
116
                str = t_str_new(256);
 
117
                str_vprintfa(str, fmt, va);
 
118
                str_append(str, "\n");
 
119
 
 
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));
 
123
        } T_END;
 
124
 
 
125
        va_end(va);
 
126
}
 
127
 
 
128
static int client_worker_connect(struct client *client)
 
129
{
 
130
        static const char handshake[] = "VERSION\timap-urlauth-worker\t1\t0\n";
 
131
        const char *socket_path;
 
132
        ssize_t ret;
 
133
        unsigned char data;
 
134
 
 
135
        socket_path = t_strconcat(client->set->base_dir,
 
136
                                  "/"IMAP_URLAUTH_WORKER_SOCKET, NULL);
 
137
 
 
138
        if (client->set->mail_debug)
 
139
                i_debug("Connecting to worker socket %s", socket_path);
 
140
 
 
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",
 
146
                                                 socket_path));
 
147
                } else {
 
148
                        i_error("imap-urlauth-client: net_connect_unix(%s) failed: %m",
 
149
                                socket_path);
 
150
                }
 
151
                return -1;
 
152
        }
 
153
 
 
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) {
 
158
                data = '0';
 
159
                ret = fd_send(client->fd_ctrl, client->fd_out,
 
160
                              &data, sizeof(data));
 
161
        }
 
162
 
 
163
        if (ret <= 0) {
 
164
                if (ret < 0) {
 
165
                        i_error("fd_send(%s, %d) failed: %m",
 
166
                                socket_path, client->fd_ctrl);
 
167
                } else {
 
168
                        i_error("fd_send(%s, %d) failed to send byte",
 
169
                                socket_path, client->fd_ctrl);
 
170
                }
 
171
                client_worker_disconnect(client);
 
172
                return -1;
 
173
        }
 
174
 
 
175
        client->ctrl_output =
 
176
                o_stream_create_fd(client->fd_ctrl, (size_t)-1, FALSE);
 
177
 
 
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);
 
182
                return -1;
 
183
        }
 
184
 
 
185
        client->ctrl_input =
 
186
                i_stream_create_fd(client->fd_ctrl, MAX_INBUF_SIZE, FALSE);
 
187
        client->ctrl_io =
 
188
                io_add(client->fd_ctrl, IO_READ, client_worker_input, client);  
 
189
        return 0;
 
190
}
 
191
 
 
192
void client_worker_disconnect(struct client *client)
 
193
{
 
194
        client->worker_state = IMAP_URLAUTH_WORKER_STATE_INACTIVE;
 
195
 
 
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;
 
205
        }
 
206
}
 
207
 
 
208
static int
 
209
client_worker_input_line(struct client *client, const char *response)
 
210
{
 
211
        const char *const *apps;
 
212
        unsigned int count, i;
 
213
        bool restart;
 
214
        string_t *str;
 
215
        int ret;
 
216
 
 
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");
 
221
                        return -1;
 
222
                }
 
223
                client->worker_state = IMAP_URLAUTH_WORKER_STATE_CONNECTED;
 
224
 
 
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]);
 
238
                        }
 
239
                }
 
240
                str_append(str, "\n");
 
241
 
 
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));
 
245
                if (ret < 0) {
 
246
                        client_disconnect(client,
 
247
                                "Failed to send ACCESS control command to worker");
 
248
                        return -1;
 
249
                }
 
250
                break;
 
251
 
 
252
        case IMAP_URLAUTH_WORKER_STATE_CONNECTED:
 
253
                if (strcasecmp(response, "OK") != 0) {
 
254
                        client_disconnect(client,
 
255
                                "Failed to negotiate access parameters");
 
256
                        return -1;
 
257
                }
 
258
                client->worker_state = IMAP_URLAUTH_WORKER_STATE_ACTIVE;
 
259
                break;
 
260
 
 
261
        case IMAP_URLAUTH_WORKER_STATE_ACTIVE:
 
262
                restart = TRUE;
 
263
                if (strcasecmp(response, "DISCONNECTED") == 0) {
 
264
                        /* worker detected client disconnect */
 
265
                        restart = FALSE;
 
266
                } else if (strcasecmp(response, "FINISHED") != 0) {
 
267
                        /* unknown response */
 
268
                        client_disconnect(client,
 
269
                                "Worker finished with unknown response");
 
270
                        return -1;
 
271
                }
 
272
 
 
273
                if (client->set->mail_debug)
 
274
                        i_debug("Worker finished successfully");
 
275
 
 
276
                if (restart) {
 
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");
 
282
                                return -1;
 
283
                        }
 
284
 
 
285
                        /* indicate success of "END" command */
 
286
                        client_send_line(client, "OK");
 
287
                } else {
 
288
                        client_disconnect(client, "Client disconnected");
 
289
                }
 
290
                return -1;
 
291
        default:
 
292
                i_unreached();
 
293
        }
 
294
        return 0;
 
295
}
 
296
 
 
297
void client_worker_input(struct client *client)
 
298
{
 
299
        struct istream *input = client->ctrl_input;
 
300
        const char *line;
 
301
 
 
302
        if (input->closed) {
 
303
                /* disconnected */
 
304
                client_disconnect(client, "Worker disconnected unexpectedly");
 
305
                return;
 
306
        }
 
307
 
 
308
        switch (i_stream_read(input)) {
 
309
        case -1:
 
310
                /* disconnected */
 
311
                client_disconnect(client, "Worker disconnected unexpectedly");
 
312
                return;
 
313
        case -2:
 
314
                /* input buffer full */
 
315
                client_disconnect(client, "Worker sent too large input");
 
316
                return;
 
317
        }
 
318
 
 
319
        while ((line = i_stream_next_line(input)) != NULL) {
 
320
                if (client_worker_input_line(client, line) < 0)
 
321
                        return;
 
322
        }
 
323
}
 
324
 
 
325
void client_destroy(struct client *client, const char *reason)
 
326
{
 
327
        i_set_failure_prefix("%s: ", master_service_get_name(master_service));
 
328
 
 
329
        if (!client->disconnected) {
 
330
                if (reason == NULL)
 
331
                        reason = "Connection closed";
 
332
                i_info("Disconnected: %s", reason);
 
333
        }
 
334
 
 
335
        imap_urlauth_client_count--;
 
336
        DLLIST_REMOVE(&imap_urlauth_clients, client);
 
337
 
 
338
        if (client->to_idle != NULL)
 
339
                timeout_remove(&client->to_idle);
 
340
 
 
341
        client_worker_disconnect(client);
 
342
        
 
343
        o_stream_destroy(&client->output);
 
344
 
 
345
        net_disconnect(client->fd_in);
 
346
        if (client->fd_in != client->fd_out)
 
347
                net_disconnect(client->fd_out);
 
348
 
 
349
        if (client->username != NULL)
 
350
                i_free(client->username);
 
351
        array_free(&client->access_apps);
 
352
        i_free(client);
 
353
 
 
354
        master_service_client_connection_destroyed(master_service);
 
355
        imap_urlauth_refresh_proctitle();
 
356
}
 
357
 
 
358
static void client_destroy_timeout(struct client *client)
 
359
{
 
360
        client_destroy(client, NULL);
 
361
}
 
362
 
 
363
void client_disconnect(struct client *client, const char *reason)
 
364
{
 
365
        if (client->disconnected)
 
366
                return;
 
367
 
 
368
        client->disconnected = TRUE;
 
369
        i_info("Disconnected: %s", reason);
 
370
 
 
371
        client->to_idle = timeout_add(0, client_destroy_timeout, client);
 
372
}
 
373
 
 
374
void clients_destroy_all(void)
 
375
{
 
376
        while (imap_urlauth_clients != NULL)
 
377
                client_destroy(imap_urlauth_clients, "Server shutting down.");
 
378
}