1 /* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "base64.h"
6 #include "ioloop.h"
7 #include "net.h"
8 #include "istream.h"
9 #include "istream-multiplex.h"
10 #include "ostream.h"
11 #include "ostream-dot.h"
12 #include "str.h"
13 #include "strescape.h"
14 #include "iostream-ssl.h"
15 #include "master-service.h"
16 #include "master-service-settings.h"
17 #include "settings-parser.h"
18 #include "doveadm.h"
19 #include "doveadm-print.h"
20 #include "doveadm-util.h"
21 #include "doveadm-server.h"
22 #include "doveadm-settings.h"
23 #include "server-connection.h"
24
25 #include <sysexits.h>
26 #include <unistd.h>
27
28 #define DOVEADM_LOG_CHANNEL_ID 'L'
29
30 #define MAX_INBUF_SIZE (1024*32)
31
32 enum server_reply_state {
33 SERVER_REPLY_STATE_DONE = 0,
34 SERVER_REPLY_STATE_PRINT,
35 SERVER_REPLY_STATE_RET
36 };
37
38 struct server_connection {
39 struct doveadm_server *server;
40
41 pool_t pool;
42 struct doveadm_settings *set;
43
44 int fd;
45 unsigned int minor;
46
47 struct io *io;
48 struct io *io_log;
49 struct istream *input;
50 struct istream *log_input;
51 struct ostream *output;
52 struct ssl_iostream *ssl_iostream;
53
54 struct istream *cmd_input;
55 struct ostream *cmd_output;
56 const char *delayed_cmd;
57 server_cmd_callback_t *callback;
58 void *context;
59
60 enum server_reply_state state;
61
62 bool version_received:1;
63 bool authenticate_sent:1;
64 bool authenticated:1;
65 bool streaming:1;
66 bool ssl_done:1;
67 };
68
69 static struct server_connection *printing_conn = NULL;
70 static ARRAY(struct doveadm_server *) print_pending_servers = ARRAY_INIT;
71
72 static void server_connection_input(struct server_connection *conn);
73 static bool server_connection_input_one(struct server_connection *conn);
74 static int server_connection_init_ssl(struct server_connection *conn,
75 const char **error_r);
76
server_set_print_pending(struct doveadm_server * server)77 static void server_set_print_pending(struct doveadm_server *server)
78 {
79 struct doveadm_server *pending_server;
80
81 if (!array_is_created(&print_pending_servers))
82 i_array_init(&print_pending_servers, 16);
83 array_foreach_elem(&print_pending_servers, pending_server) {
84 if (pending_server == server)
85 return;
86 }
87 array_push_back(&print_pending_servers, &server);
88 }
89
server_print_connection_released(struct doveadm_server * server)90 static void server_print_connection_released(struct doveadm_server *server)
91 {
92 struct server_connection *const *conns;
93 unsigned int i, count;
94
95 conns = array_get(&server->connections, &count);
96 for (i = 0; i < count; i++) {
97 if (conns[i]->io != NULL)
98 continue;
99
100 conns[i]->io = io_add(conns[i]->fd, IO_READ,
101 server_connection_input, conns[i]);
102 io_set_pending(conns[i]->io);
103 }
104 }
105
print_connection_released(void)106 static void print_connection_released(void)
107 {
108 struct doveadm_server *server;
109
110 printing_conn = NULL;
111 if (!array_is_created(&print_pending_servers))
112 return;
113
114 array_foreach_elem(&print_pending_servers, server)
115 server_print_connection_released(server);
116 array_free(&print_pending_servers);
117 }
118
server_connection_send_cmd_input_more(struct server_connection * conn)119 static int server_connection_send_cmd_input_more(struct server_connection *conn)
120 {
121 enum ostream_send_istream_result res;
122 int ret = -1;
123
124 /* ostream-dot writes only up to max buffer size, so keep it non-zero */
125 o_stream_set_max_buffer_size(conn->cmd_output, IO_BLOCK_SIZE);
126 res = o_stream_send_istream(conn->cmd_output, conn->cmd_input);
127 o_stream_set_max_buffer_size(conn->cmd_output, SIZE_MAX);
128
129 switch (res) {
130 case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
131 break;
132 case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
133 return 1;
134 case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
135 return 0;
136 case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
137 i_error("read(%s) failed: %s",
138 i_stream_get_name(conn->cmd_input),
139 i_stream_get_error(conn->cmd_input));
140 break;
141 case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
142 i_error("write(%s) failed: %s",
143 o_stream_get_name(conn->cmd_output),
144 o_stream_get_error(conn->cmd_output));
145 break;
146 }
147 if (res == OSTREAM_SEND_ISTREAM_RESULT_FINISHED) {
148 if ((ret = o_stream_finish(conn->cmd_output)) == 0)
149 return 0;
150 else if (ret < 0) {
151 i_error("write(%s) failed: %s",
152 o_stream_get_name(conn->cmd_output),
153 o_stream_get_error(conn->cmd_output));
154 }
155 }
156
157 i_stream_destroy(&conn->cmd_input);
158 o_stream_destroy(&conn->cmd_output);
159 return ret;
160 }
161
server_connection_send_cmd_input(struct server_connection * conn)162 static void server_connection_send_cmd_input(struct server_connection *conn)
163 {
164 if (conn->cmd_input == NULL)
165 return;
166
167 conn->cmd_output = o_stream_create_dot(conn->output, TRUE);
168 (void)server_connection_send_cmd_input_more(conn);
169 }
170
server_connection_output(struct server_connection * conn)171 static int server_connection_output(struct server_connection *conn)
172 {
173 int ret;
174
175 ret = o_stream_flush(conn->output);
176 if (ret > 0 && conn->cmd_input != NULL && conn->delayed_cmd == NULL)
177 ret = server_connection_send_cmd_input_more(conn);
178 if (ret < 0)
179 server_connection_destroy(&conn);
180 return ret;
181 }
182
183 static void
server_connection_callback(struct server_connection * conn,int exit_code,const char * error)184 server_connection_callback(struct server_connection *conn,
185 int exit_code, const char *error)
186 {
187 server_cmd_callback_t *callback = conn->callback;
188
189 conn->callback = NULL;
190 callback(exit_code, error, conn->context);
191 }
192
stream_data(string_t * str,const unsigned char * data,size_t size)193 static void stream_data(string_t *str, const unsigned char *data, size_t size)
194 {
195 str_truncate(str, 0);
196 str_append_tabunescaped(str, data, size);
197 doveadm_print_stream(str->data, str->used);
198 }
199
server_flush_field(struct server_connection * conn,string_t * str,const unsigned char * data,size_t size)200 static void server_flush_field(struct server_connection *conn, string_t *str,
201 const unsigned char *data, size_t size)
202 {
203 if (conn->streaming) {
204 conn->streaming = FALSE;
205 if (size > 0)
206 stream_data(str, data, size);
207 doveadm_print_stream("", 0);
208 } else {
209 str_truncate(str, 0);
210 str_append_tabunescaped(str, data, size);
211 doveadm_print(str_c(str));
212 }
213 }
214
215 static void
server_handle_input(struct server_connection * conn,const unsigned char * data,size_t size)216 server_handle_input(struct server_connection *conn,
217 const unsigned char *data, size_t size)
218 {
219 string_t *str;
220 size_t i, start;
221
222 if (printing_conn == conn) {
223 /* continue printing */
224 } else if (printing_conn == NULL) {
225 printing_conn = conn;
226 } else {
227 /* someone else is printing. don't continue until it
228 goes away */
229 server_set_print_pending(conn->server);
230 io_remove(&conn->io);
231 return;
232 }
233
234 if (data[size-1] == '\001') {
235 /* last character is an escape */
236 size--;
237 }
238
239 str = t_str_new(128);
240 for (i = start = 0; i < size; i++) {
241 if (data[i] == '\n') {
242 if (i != start) {
243 i_error("doveadm server sent broken print input");
244 server_connection_destroy(&conn);
245 return;
246 }
247 conn->state = SERVER_REPLY_STATE_RET;
248 i_stream_skip(conn->input, i + 1);
249
250 print_connection_released();
251 return;
252 }
253 if (data[i] == '\t') {
254 server_flush_field(conn, str, data + start, i - start);
255 start = i + 1;
256 }
257 }
258 if (start != size) {
259 conn->streaming = TRUE;
260 stream_data(str, data + start, size - start);
261 }
262 i_stream_skip(conn->input, size);
263 }
264
server_connection_authenticated(struct server_connection * conn)265 static void server_connection_authenticated(struct server_connection *conn)
266 {
267 conn->authenticated = TRUE;
268 if (conn->delayed_cmd != NULL) {
269 o_stream_nsend_str(conn->output, conn->delayed_cmd);
270 conn->delayed_cmd = NULL;
271 server_connection_send_cmd_input(conn);
272 }
273 }
274
275 static int
server_connection_authenticate(struct server_connection * conn)276 server_connection_authenticate(struct server_connection *conn)
277 {
278 string_t *plain = t_str_new(128);
279 string_t *cmd = t_str_new(128);
280
281 if (*conn->set->doveadm_password == '\0') {
282 i_error("doveadm_password not set, "
283 "can't authenticate to remote server");
284 return -1;
285 }
286
287 str_append_c(plain, '\0');
288 str_append(plain, conn->set->doveadm_username);
289 str_append_c(plain, '\0');
290 str_append(plain, conn->set->doveadm_password);
291
292 str_append(cmd, "PLAIN\t");
293 base64_encode(plain->data, plain->used, cmd);
294 str_append_c(cmd, '\n');
295
296 o_stream_nsend(conn->output, cmd->data, cmd->used);
297 conn->authenticate_sent = TRUE;
298 return 0;
299 }
300
server_log_disconnect_error(struct server_connection * conn)301 static void server_log_disconnect_error(struct server_connection *conn)
302 {
303 const char *error;
304
305 error = conn->ssl_iostream == NULL ? NULL :
306 ssl_iostream_get_last_error(conn->ssl_iostream);
307 if (error == NULL) {
308 error = conn->input->stream_errno == 0 ? "EOF" :
309 strerror(conn->input->stream_errno);
310 }
311 i_error("doveadm server disconnected before handshake: %s", error);
312 }
313
server_connection_print_log(struct server_connection * conn)314 static void server_connection_print_log(struct server_connection *conn)
315 {
316 const char *line;
317 struct failure_context ctx;
318 i_zero(&ctx);
319
320 while((line = i_stream_read_next_line(conn->log_input))!=NULL) {
321 /* skip empty lines */
322 if (*line == '\0') continue;
323
324 if (!doveadm_log_type_from_char(line[0], &ctx.type))
325 i_warning("Doveadm server sent invalid log type 0x%02x",
326 line[0]);
327 line++;
328 i_log_type(&ctx, "remote(%s): %s", conn->server->name, line);
329 }
330 }
331
server_connection_start_multiplex(struct server_connection * conn)332 static void server_connection_start_multiplex(struct server_connection *conn)
333 {
334 struct istream *is = conn->input;
335 conn->input = i_stream_create_multiplex(is, MAX_INBUF_SIZE);
336 i_stream_unref(&is);
337 io_remove(&conn->io);
338 conn->io = io_add_istream(conn->input, server_connection_input, conn);
339 conn->log_input = i_stream_multiplex_add_channel(conn->input, DOVEADM_LOG_CHANNEL_ID);
340 conn->io_log = io_add_istream(conn->log_input, server_connection_print_log, conn);
341 i_stream_set_return_partial_line(conn->log_input, TRUE);
342 }
343
server_connection_input(struct server_connection * conn)344 static void server_connection_input(struct server_connection *conn)
345 {
346 const char *line;
347 const char *error;
348
349 if (i_stream_read(conn->input) < 0) {
350 /* disconnected */
351 server_log_disconnect_error(conn);
352 server_connection_destroy(&conn);
353 return;
354 }
355
356 while (!conn->authenticated) {
357 if ((line = i_stream_next_line(conn->input)) == NULL) {
358 if (conn->input->eof) {
359 /* we'll also get here if the line is too long */
360 server_log_disconnect_error(conn);
361 server_connection_destroy(&conn);
362 }
363 return;
364 }
365 /* Allow VERSION before or after the "+" or "-" line,
366 because v2.2.33 sent the version after and newer
367 versions send before. */
368 if (!conn->version_received &&
369 str_begins(line, "VERSION\t")) {
370 if (!version_string_verify_full(line, "doveadm-client",
371 DOVEADM_SERVER_PROTOCOL_VERSION_MAJOR,
372 &conn->minor)) {
373 i_error("doveadm server not compatible with this client"
374 "(mixed old and new binaries?)");
375 server_connection_destroy(&conn);
376 return;
377 }
378 conn->version_received = TRUE;
379 } else if (strcmp(line, "+") == 0) {
380 if (conn->minor > 0)
381 server_connection_start_multiplex(conn);
382 server_connection_authenticated(conn);
383 } else if (strcmp(line, "-") == 0) {
384 if (conn->authenticate_sent) {
385 i_error("doveadm authentication failed (%s)",
386 line+1);
387 server_connection_destroy(&conn);
388 return;
389 }
390 if (!conn->ssl_done &&
391 (conn->server->ssl_flags & PROXY_SSL_FLAG_STARTTLS) != 0) {
392 io_remove(&conn->io);
393 if (conn->minor < 2) {
394 i_error("doveadm STARTTLS failed: Server does not support it");
395 server_connection_destroy(&conn);
396 return;
397 }
398 /* send STARTTLS */
399 o_stream_nsend_str(conn->output, "STARTTLS\n");
400 if (server_connection_init_ssl(conn, &error) < 0) {
401 i_error("doveadm STARTTLS failed: %s", error);
402 server_connection_destroy(&conn);
403 return;
404 }
405 conn->ssl_done = TRUE;
406 conn->io = io_add_istream(conn->input, server_connection_input, conn);
407 }
408 if (server_connection_authenticate(conn) < 0) {
409 server_connection_destroy(&conn);
410 return;
411 }
412 } else {
413 i_error("doveadm server sent invalid handshake: %s",
414 line);
415 server_connection_destroy(&conn);
416 return;
417 }
418 }
419
420 while (server_connection_input_one(conn)) ;
421 }
422
server_connection_input_one(struct server_connection * conn)423 static bool server_connection_input_one(struct server_connection *conn)
424 {
425 const unsigned char *data;
426 size_t size;
427 const char *line;
428 int exit_code;
429
430 /* check logs - NOTE: must be before i_stream_get_data() since checking
431 for logs may add data to our channel. */
432 if (conn->log_input != NULL)
433 (void)server_connection_print_log(conn);
434
435 data = i_stream_get_data(conn->input, &size);
436 if (size == 0)
437 return FALSE;
438
439 switch (conn->state) {
440 case SERVER_REPLY_STATE_DONE:
441 i_error("doveadm server sent unexpected input");
442 server_connection_destroy(&conn);
443 return FALSE;
444 case SERVER_REPLY_STATE_PRINT:
445 server_handle_input(conn, data, size);
446 if (conn->state != SERVER_REPLY_STATE_RET)
447 return FALSE;
448 /* fall through */
449 case SERVER_REPLY_STATE_RET:
450 line = i_stream_next_line(conn->input);
451 if (line == NULL)
452 return FALSE;
453 if (line[0] == '+')
454 server_connection_callback(conn, 0, "");
455 else if (line[0] == '-') {
456 line++;
457 exit_code = doveadm_str_to_exit_code(line);
458 if (exit_code == DOVEADM_EX_UNKNOWN &&
459 str_to_int(line, &exit_code) < 0) {
460 /* old doveadm-server */
461 exit_code = EX_TEMPFAIL;
462 }
463 server_connection_callback(conn, exit_code, line);
464 } else {
465 i_error("doveadm server sent broken input "
466 "(expected cmd reply): %s", line);
467 server_connection_destroy(&conn);
468 return FALSE;
469 }
470 if (conn->callback == NULL) {
471 /* we're finished, close the connection */
472 server_connection_destroy(&conn);
473 return FALSE;
474 }
475 return TRUE;
476 }
477 i_unreached();
478 }
479
server_connection_read_settings(struct server_connection * conn,const char ** error_r)480 static int server_connection_read_settings(struct server_connection *conn,
481 const char **error_r)
482 {
483 const struct setting_parser_info *set_roots[] = {
484 &doveadm_setting_parser_info,
485 NULL
486 };
487 struct master_service_settings_input input;
488 struct master_service_settings_output output;
489 const char *error;
490 in_port_t port;
491 void *set;
492
493 i_zero(&input);
494 input.roots = set_roots;
495 input.service = "doveadm";
496
497 (void)net_getsockname(conn->fd, &input.local_ip, &port);
498 (void)net_getpeername(conn->fd, &input.remote_ip, &port);
499
500 if (master_service_settings_read(master_service, &input,
501 &output, &error) < 0) {
502 *error_r = t_strdup_printf(
503 "Error reading configuration: %s", error);
504 return -1;
505 }
506 set = master_service_settings_get_others(master_service)[0];
507 conn->set = settings_dup(&doveadm_setting_parser_info, set, conn->pool);
508 return 0;
509 }
510
server_connection_init_ssl(struct server_connection * conn,const char ** error_r)511 static int server_connection_init_ssl(struct server_connection *conn,
512 const char **error_r)
513 {
514 struct ssl_iostream_settings ssl_set;
515 const char *error;
516
517 if (conn->server->ssl_flags == 0)
518 return 0;
519
520 doveadm_get_ssl_settings(&ssl_set, pool_datastack_create());
521
522 if ((conn->server->ssl_flags & PROXY_SSL_FLAG_ANY_CERT) != 0)
523 ssl_set.allow_invalid_cert = TRUE;
524 if (ssl_set.allow_invalid_cert)
525 ssl_set.verbose_invalid_cert = TRUE;
526
527 if (conn->server->ssl_ctx == NULL &&
528 ssl_iostream_client_context_cache_get(&ssl_set,
529 &conn->server->ssl_ctx,
530 &error) < 0) {
531 *error_r = t_strdup_printf(
532 "Couldn't initialize SSL client: %s", error);
533 return -1;
534 }
535
536 if (io_stream_create_ssl_client(conn->server->ssl_ctx,
537 conn->server->hostname, &ssl_set,
538 &conn->input, &conn->output,
539 &conn->ssl_iostream, &error) < 0) {
540 *error_r = t_strdup_printf(
541 "Couldn't initialize SSL client: %s", error);
542 return -1;
543 }
544 if (ssl_iostream_handshake(conn->ssl_iostream) < 0) {
545 *error_r = t_strdup_printf(
546 "SSL handshake failed: %s",
547 ssl_iostream_get_last_error(conn->ssl_iostream));
548 return -1;
549 }
550 return 0;
551 }
552
server_connection_create(struct doveadm_server * server,struct server_connection ** conn_r,const char ** error_r)553 int server_connection_create(struct doveadm_server *server,
554 struct server_connection **conn_r,
555 const char **error_r)
556 {
557 const char *target;
558 struct server_connection *conn;
559 pool_t pool;
560
561 pool = pool_alloconly_create("doveadm server connection", 1024*16);
562 conn = p_new(pool, struct server_connection, 1);
563 conn->pool = pool;
564 conn->server = server;
565 if (server->ip.family != 0) {
566 (void)net_ipport2str(&server->ip, server->port, &target);
567 } else {
568 target = server->name;
569 }
570 conn->fd = doveadm_connect_with_default_port(target,
571 doveadm_settings->doveadm_port);
572 net_set_nonblock(conn->fd, TRUE);
573 conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE);
574 conn->output = o_stream_create_fd(conn->fd, SIZE_MAX);
575 o_stream_set_flush_callback(conn->output, server_connection_output, conn);
576 o_stream_set_no_error_handling(conn->output, TRUE);
577
578 i_stream_set_name(conn->input, server->name);
579 o_stream_set_name(conn->output, server->name);
580
581 array_push_back(&conn->server->connections, &conn);
582
583 if (server_connection_read_settings(conn, error_r) < 0 ||
584 ((server->ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0 &&
585 server_connection_init_ssl(conn, error_r) < 0)) {
586 server_connection_destroy(&conn);
587 return -1;
588 }
589 conn->io = io_add_istream(conn->input, server_connection_input, conn);
590
591 conn->state = SERVER_REPLY_STATE_DONE;
592 o_stream_nsend_str(conn->output, DOVEADM_SERVER_PROTOCOL_VERSION_LINE"\n");
593
594 *conn_r = conn;
595 return 0;
596 }
597
server_connection_destroy(struct server_connection ** _conn)598 void server_connection_destroy(struct server_connection **_conn)
599 {
600 struct server_connection *conn = *_conn;
601 struct server_connection *const *conns;
602 const char *error;
603 unsigned int i, count;
604
605 *_conn = NULL;
606
607 conns = array_get(&conn->server->connections, &count);
608 for (i = 0; i < count; i++) {
609 if (conns[i] == conn) {
610 array_delete(&conn->server->connections, i, 1);
611 break;
612 }
613 }
614
615 if (conn->callback != NULL) {
616 error = conn->ssl_iostream == NULL ? NULL :
617 ssl_iostream_get_last_error(conn->ssl_iostream);
618 if (error == NULL) {
619 error = conn->input->stream_errno == 0 ? "EOF" :
620 strerror(conn->input->stream_errno);
621 }
622 server_connection_callback(conn, SERVER_EXIT_CODE_DISCONNECTED,
623 error);
624 }
625 if (printing_conn == conn)
626 print_connection_released();
627
628 i_stream_destroy(&conn->input);
629 o_stream_destroy(&conn->output);
630 i_stream_destroy(&conn->cmd_input);
631 /* close cmd_output after its parent, so the "." isn't sent */
632 o_stream_destroy(&conn->cmd_output);
633 ssl_iostream_destroy(&conn->ssl_iostream);
634 io_remove(&conn->io_log);
635 /* make sure all logs got consumed */
636 if (conn->log_input != NULL)
637 server_connection_print_log(conn);
638 i_stream_unref(&conn->log_input);
639 io_remove(&conn->io);
640 i_close_fd(&conn->fd);
641 pool_unref(&conn->pool);
642 }
643
644 struct doveadm_server *
server_connection_get_server(struct server_connection * conn)645 server_connection_get_server(struct server_connection *conn)
646 {
647 return conn->server;
648 }
649
server_connection_cmd(struct server_connection * conn,const char * line,struct istream * cmd_input,server_cmd_callback_t * callback,void * context)650 void server_connection_cmd(struct server_connection *conn, const char *line,
651 struct istream *cmd_input,
652 server_cmd_callback_t *callback, void *context)
653 {
654 i_assert(conn->delayed_cmd == NULL);
655
656 conn->state = SERVER_REPLY_STATE_PRINT;
657 if (cmd_input != NULL) {
658 i_assert(conn->cmd_input == NULL);
659 i_stream_ref(cmd_input);
660 conn->cmd_input = cmd_input;
661 }
662 if (!conn->authenticated)
663 conn->delayed_cmd = p_strdup(conn->pool, line);
664 else {
665 o_stream_nsend_str(conn->output, line);
666 server_connection_send_cmd_input(conn);
667 }
668 conn->callback = callback;
669 conn->context = context;
670 }
671
server_connection_is_idle(struct server_connection * conn)672 bool server_connection_is_idle(struct server_connection *conn)
673 {
674 return conn->callback == NULL;
675 }
676
server_connection_extract(struct server_connection * conn,struct istream ** istream_r,struct ostream ** ostream_r,struct ssl_iostream ** ssl_iostream_r)677 void server_connection_extract(struct server_connection *conn,
678 struct istream **istream_r,
679 struct ostream **ostream_r,
680 struct ssl_iostream **ssl_iostream_r)
681 {
682 *istream_r = conn->input;
683 *ostream_r = conn->output;
684 *ssl_iostream_r = conn->ssl_iostream;
685
686 conn->input = NULL;
687 conn->output = NULL;
688 conn->ssl_iostream = NULL;
689 io_remove(&conn->io);
690 conn->fd = -1;
691 }
692