1
/* Copyright (c) 2004-2011 Dovecot authors, see the included COPYING file */
3
#include "login-common.h"
9
#include "str-sanitize.h"
10
#include "time-util.h"
11
#include "master-service.h"
12
#include "ipc-server.h"
13
#include "dns-lookup.h"
14
#include "client-common.h"
15
#include "ssl-proxy.h"
16
#include "login-proxy-state.h"
17
#include "login-proxy.h"
19
#define MAX_PROXY_INPUT_SIZE 4096
20
#define OUTBUF_THRESHOLD 1024
21
#define LOGIN_PROXY_DIE_IDLE_SECS 2
22
#define LOGIN_PROXY_DNS_WARN_MSECS 500
23
#define LOGIN_PROXY_IPC_PATH "ipc-proxy"
24
#define LOGIN_PROXY_IPC_NAME "proxy"
25
#define KILLED_BY_ADMIN_REASON "Killed by admin"
28
struct login_proxy *prev, *next;
30
struct client *client;
31
int client_fd, server_fd;
32
struct io *client_io, *server_io;
33
struct istream *server_input;
34
struct ostream *client_output, *server_output;
35
struct ssl_proxy *ssl_server_proxy;
38
struct timeval created;
39
struct timeout *to, *to_notify;
40
struct login_proxy_record *state_rec;
45
unsigned int connect_timeout_msecs;
46
unsigned int notify_refresh_secs;
47
enum login_proxy_ssl_flags ssl_flags;
49
proxy_callback_t *callback;
51
unsigned int destroying:1;
52
unsigned int disconnecting:1;
55
static struct login_proxy_state *proxy_state;
56
static struct login_proxy *login_proxies = NULL;
57
static struct login_proxy *login_proxies_pending = NULL;
58
static struct ipc_server *login_proxy_ipc_server;
60
static void login_proxy_ipc_cmd(struct ipc_cmd *cmd, const char *line);
63
login_proxy_free_reason(struct login_proxy **_proxy, const char *reason);
65
static void login_proxy_free_errno(struct login_proxy **proxy,
66
int err, const char *who)
70
reason = err == 0 || err == EPIPE ?
71
t_strdup_printf("Disconnected by %s", who) :
72
t_strdup_printf("Disconnected by %s: %s", who, strerror(errno));
73
login_proxy_free_reason(proxy, reason);
76
static void server_input(struct login_proxy *proxy)
78
unsigned char buf[OUTBUF_THRESHOLD];
81
proxy->last_io = ioloop_time;
82
if (o_stream_get_buffer_used_size(proxy->client_output) >
84
/* client's output buffer is already quite full.
85
don't send more until we're below threshold. */
86
io_remove(&proxy->server_io);
90
ret = net_receive(proxy->server_fd, buf, sizeof(buf));
92
login_proxy_free_errno(&proxy, errno, "server");
93
else if (o_stream_send(proxy->client_output, buf, ret) != ret) {
94
login_proxy_free_errno(&proxy,
95
proxy->client_output->stream_errno,
100
static void proxy_client_input(struct login_proxy *proxy)
102
unsigned char buf[OUTBUF_THRESHOLD];
105
proxy->last_io = ioloop_time;
106
if (o_stream_get_buffer_used_size(proxy->server_output) >
108
/* proxy's output buffer is already quite full.
109
don't send more until we're below threshold. */
110
io_remove(&proxy->client_io);
114
ret = net_receive(proxy->client_fd, buf, sizeof(buf));
116
login_proxy_free_errno(&proxy, errno, "client");
117
else if (o_stream_send(proxy->server_output, buf, ret) != ret) {
118
login_proxy_free_errno(&proxy,
119
proxy->server_output->stream_errno,
124
static int server_output(struct login_proxy *proxy)
126
proxy->last_io = ioloop_time;
127
if (o_stream_flush(proxy->server_output) < 0) {
128
login_proxy_free_errno(&proxy,
129
proxy->server_output->stream_errno,
134
if (proxy->client_io == NULL &&
135
o_stream_get_buffer_used_size(proxy->server_output) <
137
/* there's again space in proxy's output buffer, so we can
138
read more from client. */
139
proxy->client_io = io_add(proxy->client_fd, IO_READ,
140
proxy_client_input, proxy);
145
static int proxy_client_output(struct login_proxy *proxy)
147
proxy->last_io = ioloop_time;
148
if (o_stream_flush(proxy->client_output) < 0) {
149
login_proxy_free_errno(&proxy,
150
proxy->client_output->stream_errno,
155
if (proxy->server_io == NULL &&
156
o_stream_get_buffer_used_size(proxy->client_output) <
158
/* there's again space in client's output buffer, so we can
159
read more from proxy. */
161
io_add(proxy->server_fd, IO_READ, server_input, proxy);
166
static void proxy_prelogin_input(struct login_proxy *proxy)
168
proxy->callback(proxy->client);
171
static void proxy_plain_connected(struct login_proxy *proxy)
173
proxy->server_input =
174
i_stream_create_fd(proxy->server_fd, MAX_PROXY_INPUT_SIZE,
176
proxy->server_output =
177
o_stream_create_fd(proxy->server_fd, (size_t)-1, FALSE);
180
io_add(proxy->server_fd, IO_READ, proxy_prelogin_input, proxy);
183
static void proxy_fail_connect(struct login_proxy *proxy)
185
if (timeval_cmp(&proxy->created, &proxy->state_rec->last_success) < 0) {
186
/* there was a successful connection done since we started
187
connecting. perhaps this is just a temporary one-off
190
proxy->state_rec->last_failure = ioloop_timeval;
192
proxy->state_rec->num_waiting_connections--;
193
proxy->state_rec = NULL;
196
static void proxy_wait_connect(struct login_proxy *proxy)
200
err = net_geterror(proxy->server_fd);
202
i_error("proxy: connect(%s, %u) failed: %s",
203
proxy->host, proxy->port, strerror(err));
204
proxy_fail_connect(proxy);
205
login_proxy_free(&proxy);
208
proxy->state_rec->last_success = ioloop_timeval;
209
proxy->state_rec->num_waiting_connections--;
210
proxy->state_rec = NULL;
212
if (proxy->to != NULL)
213
timeout_remove(&proxy->to);
215
if ((proxy->ssl_flags & PROXY_SSL_FLAG_YES) != 0 &&
216
(proxy->ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0) {
217
if (login_proxy_starttls(proxy) < 0) {
218
login_proxy_free(&proxy);
222
io_remove(&proxy->server_io);
223
proxy_plain_connected(proxy);
227
static void proxy_connect_timeout(struct login_proxy *proxy)
229
i_error("proxy: connect(%s, %u) timed out", proxy->host, proxy->port);
230
proxy_fail_connect(proxy);
231
login_proxy_free(&proxy);
234
static int login_proxy_connect(struct login_proxy *proxy)
236
struct login_proxy_record *rec;
238
rec = login_proxy_state_get(proxy_state, &proxy->ip, proxy->port);
239
if (timeval_cmp(&rec->last_failure, &rec->last_success) > 0 &&
240
rec->num_waiting_connections != 0) {
241
/* the server is down. fail immediately */
242
i_error("proxy(%s): Host %s:%u is down",
243
proxy->client->virtual_user, proxy->host, proxy->port);
244
login_proxy_free(&proxy);
248
proxy->server_fd = net_connect_ip(&proxy->ip, proxy->port, NULL);
249
if (proxy->server_fd == -1) {
250
i_error("proxy(%s): connect(%s, %u) failed: %m",
251
proxy->client->virtual_user, proxy->host, proxy->port);
252
login_proxy_free(&proxy);
255
proxy->server_io = io_add(proxy->server_fd, IO_WRITE,
256
proxy_wait_connect, proxy);
257
if (proxy->connect_timeout_msecs != 0) {
258
proxy->to = timeout_add(proxy->connect_timeout_msecs,
259
proxy_connect_timeout, proxy);
262
proxy->state_rec = rec;
263
proxy->state_rec->num_waiting_connections++;
267
static void login_proxy_dns_done(const struct dns_lookup_result *result,
268
struct login_proxy *proxy)
270
if (result->ret != 0) {
271
i_error("proxy(%s): DNS lookup of %s failed: %s",
272
proxy->client->virtual_user, proxy->host,
274
login_proxy_free(&proxy);
276
if (result->msecs > LOGIN_PROXY_DNS_WARN_MSECS) {
277
i_warning("proxy(%s): DNS lookup for %s took %u.%03u s",
278
proxy->client->virtual_user, proxy->host,
279
result->msecs/1000, result->msecs % 1000);
282
proxy->ip = result->ips[0];
283
(void)login_proxy_connect(proxy);
287
int login_proxy_new(struct client *client,
288
const struct login_proxy_settings *set,
289
proxy_callback_t *callback)
291
struct login_proxy *proxy;
292
struct dns_lookup_settings dns_lookup_set;
294
i_assert(client->login_proxy == NULL);
296
if (set->host == NULL || *set->host == '\0') {
297
i_error("proxy(%s): host not given", client->virtual_user);
301
proxy = i_new(struct login_proxy, 1);
302
proxy->client = client;
303
proxy->client_fd = -1;
304
proxy->server_fd = -1;
305
proxy->created = ioloop_timeval;
306
proxy->host = i_strdup(set->host);
307
proxy->port = set->port;
308
proxy->connect_timeout_msecs = set->connect_timeout_msecs;
309
proxy->notify_refresh_secs = set->notify_refresh_secs;
310
proxy->ssl_flags = set->ssl_flags;
313
memset(&dns_lookup_set, 0, sizeof(dns_lookup_set));
314
dns_lookup_set.dns_client_socket_path = set->dns_client_socket_path;
315
dns_lookup_set.timeout_msecs = set->connect_timeout_msecs;
317
if (net_addr2ip(set->host, &proxy->ip) < 0) {
318
if (dns_lookup(set->host, &dns_lookup_set,
319
login_proxy_dns_done, proxy) < 0)
322
if (login_proxy_connect(proxy) < 0)
326
DLLIST_PREPEND(&login_proxies_pending, proxy);
328
proxy->callback = callback;
329
client->login_proxy = proxy;
334
login_proxy_free_reason(struct login_proxy **_proxy, const char *reason)
336
struct login_proxy *proxy = *_proxy;
337
struct client *client = proxy->client;
342
if (proxy->destroying)
344
proxy->destroying = TRUE;
346
if (proxy->to != NULL)
347
timeout_remove(&proxy->to);
348
if (proxy->to_notify != NULL)
349
timeout_remove(&proxy->to_notify);
351
if (proxy->state_rec != NULL)
352
proxy->state_rec->num_waiting_connections--;
353
if (proxy->to != NULL)
354
timeout_remove(&proxy->to);
356
if (proxy->server_io != NULL)
357
io_remove(&proxy->server_io);
358
if (proxy->server_input != NULL)
359
i_stream_destroy(&proxy->server_input);
360
if (proxy->server_output != NULL)
361
o_stream_destroy(&proxy->server_output);
363
if (proxy->client_fd != -1) {
365
DLLIST_REMOVE(&login_proxies, proxy);
367
ipstr = net_ip2addr(&proxy->client->ip);
368
i_info("proxy(%s): disconnecting %s%s",
369
proxy->client->virtual_user,
370
ipstr != NULL ? ipstr : "",
371
reason == NULL ? "" : t_strdup_printf(" (%s)", reason));
373
if (proxy->client_io != NULL)
374
io_remove(&proxy->client_io);
375
if (proxy->client_output != NULL)
376
o_stream_destroy(&proxy->client_output);
377
net_disconnect(proxy->client_fd);
379
i_assert(proxy->client_io == NULL);
380
i_assert(proxy->client_output == NULL);
382
DLLIST_REMOVE(&login_proxies_pending, proxy);
384
if (proxy->callback != NULL)
385
proxy->callback(proxy->client);
388
if (proxy->server_fd != -1)
389
net_disconnect(proxy->server_fd);
391
if (proxy->ssl_server_proxy != NULL)
392
ssl_proxy_free(&proxy->ssl_server_proxy);
396
client->login_proxy = NULL;
397
client_unref(&client);
400
void login_proxy_free(struct login_proxy **_proxy)
402
login_proxy_free_reason(_proxy, NULL);
405
bool login_proxy_is_ourself(const struct client *client, const char *host,
406
unsigned int port, const char *destuser)
410
if (port != client->local_port)
413
if (net_addr2ip(host, &ip) < 0)
415
if (!net_ip_compare(&ip, &client->local_ip))
418
return strcmp(client->virtual_user, destuser) == 0;
421
struct istream *login_proxy_get_istream(struct login_proxy *proxy)
423
return proxy->disconnecting ? NULL : proxy->server_input;
426
struct ostream *login_proxy_get_ostream(struct login_proxy *proxy)
428
return proxy->server_output;
431
const char *login_proxy_get_host(const struct login_proxy *proxy)
436
unsigned int login_proxy_get_port(const struct login_proxy *proxy)
441
enum login_proxy_ssl_flags
442
login_proxy_get_ssl_flags(const struct login_proxy *proxy)
444
return proxy->ssl_flags;
447
static void login_proxy_notify(struct login_proxy *proxy)
449
login_proxy_state_notify(proxy_state, proxy->client->proxy_user);
452
void login_proxy_detach(struct login_proxy *proxy)
454
struct client *client = proxy->client;
455
const unsigned char *data;
458
i_assert(proxy->client_fd == -1);
459
i_assert(proxy->server_output != NULL);
461
proxy->client_fd = i_stream_get_fd(client->input);
462
proxy->client_output = client->output;
464
o_stream_set_max_buffer_size(client->output, (size_t)-1);
465
o_stream_set_flush_callback(client->output, proxy_client_output, proxy);
466
client->output = NULL;
468
/* send all pending client input to proxy and get rid of the stream */
469
data = i_stream_get_data(client->input, &size);
471
(void)o_stream_send(proxy->server_output, data, size);
473
/* from now on, just do dummy proxying */
474
io_remove(&proxy->server_io);
476
io_add(proxy->server_fd, IO_READ, server_input, proxy);
478
io_add(proxy->client_fd, IO_READ, proxy_client_input, proxy);
479
o_stream_set_flush_callback(proxy->server_output, server_output, proxy);
480
i_stream_destroy(&proxy->server_input);
482
if (proxy->notify_refresh_secs != 0) {
484
timeout_add(proxy->notify_refresh_secs * 1000,
485
login_proxy_notify, proxy);
488
proxy->callback = NULL;
490
if (login_proxy_ipc_server == NULL) {
491
login_proxy_ipc_server =
492
ipc_server_init(LOGIN_PROXY_IPC_PATH,
493
LOGIN_PROXY_IPC_NAME,
494
login_proxy_ipc_cmd);
497
DLLIST_REMOVE(&login_proxies_pending, proxy);
498
DLLIST_PREPEND(&login_proxies, proxy);
501
client->login_proxy = NULL;
504
static int login_proxy_ssl_handshaked(void *context)
506
struct login_proxy *proxy = context;
508
if ((proxy->ssl_flags & PROXY_SSL_FLAG_ANY_CERT) != 0 ||
509
ssl_proxy_has_valid_client_cert(proxy->ssl_server_proxy))
512
if (!ssl_proxy_has_broken_client_cert(proxy->ssl_server_proxy)) {
513
client_log_err(proxy->client, t_strdup_printf(
514
"proxy: SSL certificate not received from %s:%u",
515
proxy->host, proxy->port));
517
client_log_err(proxy->client, t_strdup_printf(
518
"proxy: Received invalid SSL certificate from %s:%u",
519
proxy->host, proxy->port));
521
proxy->disconnecting = TRUE;
525
int login_proxy_starttls(struct login_proxy *proxy)
529
if (proxy->server_input != NULL)
530
i_stream_destroy(&proxy->server_input);
531
if (proxy->server_output != NULL)
532
o_stream_destroy(&proxy->server_output);
533
io_remove(&proxy->server_io);
535
fd = ssl_proxy_client_alloc(proxy->server_fd, &proxy->client->ip,
537
login_proxy_ssl_handshaked, proxy,
538
&proxy->ssl_server_proxy);
540
client_log_err(proxy->client, t_strdup_printf(
541
"proxy: SSL handshake failed to %s:%u",
542
proxy->host, proxy->port));
545
ssl_proxy_set_client(proxy->ssl_server_proxy, proxy->client);
546
ssl_proxy_start(proxy->ssl_server_proxy);
548
proxy->server_fd = fd;
549
proxy_plain_connected(proxy);
553
static void proxy_kill_idle(struct login_proxy *proxy)
555
login_proxy_free_reason(&proxy, KILLED_BY_ADMIN_REASON);
558
void login_proxy_kill_idle(void)
560
struct login_proxy *proxy, *next;
561
time_t now = time(NULL);
562
time_t stop_timestamp = now - LOGIN_PROXY_DIE_IDLE_SECS;
563
unsigned int stop_msecs;
565
for (proxy = login_proxies; proxy != NULL; proxy = next) {
568
if (proxy->last_io <= stop_timestamp)
569
proxy_kill_idle(proxy);
571
i_assert(proxy->to == NULL);
572
stop_msecs = (proxy->last_io - stop_timestamp) * 1000;
573
proxy->to = timeout_add(stop_msecs,
574
proxy_kill_idle, proxy);
580
login_proxy_cmd_kick(struct ipc_cmd *cmd, const char *const *args)
582
struct login_proxy *proxy, *next;
583
unsigned int count = 0;
585
if (args[0] == NULL) {
586
ipc_cmd_fail(&cmd, "Missing parameter");
590
for (proxy = login_proxies; proxy != NULL; proxy = next) {
593
if (strcmp(proxy->client->virtual_user, args[0]) == 0) {
594
login_proxy_free_reason(&proxy, KILLED_BY_ADMIN_REASON);
598
for (proxy = login_proxies_pending; proxy != NULL; proxy = next) {
601
if (strcmp(proxy->client->virtual_user, args[0]) == 0) {
602
client_destroy(proxy->client, "Connection kicked");
606
ipc_cmd_success_reply(&cmd, t_strdup_printf("%u", count));
609
static unsigned int director_username_hash(const char *username)
611
/* NOTE: If you modify this, modify also
612
user_directory_get_username_hash() in director/user-director.c */
613
unsigned char md5[MD5_RESULTLEN];
614
unsigned int i, hash = 0;
616
md5_get_digest(username, strlen(username), md5);
617
for (i = 0; i < sizeof(hash); i++)
618
hash = (hash << CHAR_BIT) | md5[i];
623
login_proxy_cmd_kick_director_hash(struct ipc_cmd *cmd, const char *const *args)
625
struct login_proxy *proxy, *next;
626
unsigned int hash, count = 0;
628
if (args[0] == NULL || str_to_uint(args[0], &hash) < 0) {
629
ipc_cmd_fail(&cmd, "Invalid parameters");
633
for (proxy = login_proxies; proxy != NULL; proxy = next) {
636
if (director_username_hash(proxy->client->virtual_user) == hash) {
637
login_proxy_free_reason(&proxy, KILLED_BY_ADMIN_REASON);
641
for (proxy = login_proxies_pending; proxy != NULL; proxy = next) {
644
if (director_username_hash(proxy->client->virtual_user) == hash) {
645
client_destroy(proxy->client, "Connection kicked");
649
ipc_cmd_success_reply(&cmd, t_strdup_printf("%u", count));
653
login_proxy_cmd_list_reply(struct ipc_cmd *cmd,
654
struct login_proxy *proxy)
659
reply = t_strdup_printf("%s\t%s\t%s\t%s\t%u",
660
proxy->client->virtual_user,
661
login_binary.protocol,
662
net_ip2addr(&proxy->client->ip),
663
net_ip2addr(&proxy->ip), proxy->port);
664
ipc_cmd_send(cmd, reply);
669
login_proxy_cmd_list(struct ipc_cmd *cmd, const char *const *args ATTR_UNUSED)
671
struct login_proxy *proxy;
673
for (proxy = login_proxies; proxy != NULL; proxy = proxy->next)
674
login_proxy_cmd_list_reply(cmd, proxy);
675
for (proxy = login_proxies_pending; proxy != NULL; proxy = proxy->next)
676
login_proxy_cmd_list_reply(cmd, proxy);
677
ipc_cmd_success(&cmd);
680
static void login_proxy_ipc_cmd(struct ipc_cmd *cmd, const char *line)
682
const char *const *args = t_strsplit(line, "\t");
683
const char *name = args[0];
686
if (strcmp(name, "KICK") == 0)
687
login_proxy_cmd_kick(cmd, args);
688
else if (strcmp(name, "KICK-DIRECTOR-HASH") == 0)
689
login_proxy_cmd_kick_director_hash(cmd, args);
690
else if (strcmp(name, "LIST") == 0)
691
login_proxy_cmd_list(cmd, args);
693
ipc_cmd_fail(&cmd, "Unknown command");
696
void login_proxy_init(const char *proxy_notify_pipe_path)
698
proxy_state = login_proxy_state_init(proxy_notify_pipe_path);
701
void login_proxy_deinit(void)
703
struct login_proxy *proxy;
705
while (login_proxies != NULL) {
706
proxy = login_proxies;
707
login_proxy_free_reason(&proxy, KILLED_BY_ADMIN_REASON);
709
if (login_proxy_ipc_server != NULL)
710
ipc_server_deinit(&login_proxy_ipc_server);
711
login_proxy_state_deinit(&proxy_state);