1
/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
10
#include "str-sanitize.h"
11
#include "safe-memset.h"
15
#include "managesieve-quote.h"
16
#include "managesieve-proxy.h"
17
#include "managesieve-parser.h"
19
static int proxy_input_line(struct managesieve_client *client,
20
struct ostream *output, const char *line)
25
i_assert(!client->destroyed);
27
if (!client->proxy_login_sent) {
28
string_t *plain_login, *base64;
29
struct istream *input;
30
struct managesieve_parser *parser;
31
struct managesieve_arg *args;
33
bool fatal = FALSE, greeting_recvd = FALSE;
35
/* Server will send greeting which is actually a capability
36
* response. Output from a faulty server should not be accepted,
37
* so the response is parsed and verified.
40
/* Build an input stream for the managesieve parser
41
* FIXME: It would be nice if the line-wise parsing could be
42
* substituded by something similar to the command line interpreter.
43
* However, the current login_proxy structure does not make streams
44
* known until inside proxy_input handler.
46
line = t_strconcat(line, "\r\n", NULL);
47
input = i_stream_create_from_data(line, strlen(line));
48
parser = managesieve_parser_create(input, NULL, MAX_MANAGESIEVE_LINE);
49
managesieve_parser_reset(parser);
52
* FIXME: Theoretically the OK response could include a
53
* response code which could be rejected by the parser.
55
(void)i_stream_read(input);
56
ret = managesieve_parser_read_args(parser, 2, 0, &args);
59
if ( args[0].type == MANAGESIEVE_ARG_ATOM &&
60
strncasecmp(MANAGESIEVE_ARG_STR(&(args[0])), "OK", 2) == 0 ) {
62
/* Received OK response; greeting is finished */
63
greeting_recvd = TRUE;
65
} else if ( args[0].type == MANAGESIEVE_ARG_STRING ) {
66
if ( strncasecmp(MANAGESIEVE_ARG_STR(&(args[0])), "SASL", 4) == 0 ) {
67
/* Check whether the server supports the SASL mechanism
68
* we are going to use (currently only PLAIN supported).
70
if ( ret == 2 && args[1].type == MANAGESIEVE_ARG_STRING ) {
71
char *p = MANAGESIEVE_ARG_STR(&(args[1]));
72
int mech_found = FALSE;
75
if ( strncasecmp(p, "PLAIN", 5) == 0 ) {
85
client_syslog(&client->common, "proxy: "
86
"Server does not support required PLAIN SASL mechanism.");
93
/* Do not accept faulty server */
94
client_syslog(&client->common, t_strdup_printf("proxy: "
95
"Remote returned with invalid capability/greeting line: %s",
96
str_sanitize(line,160)));
101
} else if ( ret == -2 ) {
102
/* Parser needs more data (not possible on mem stream) */
105
} else if ( ret < 0 ) {
106
const char *error_str = managesieve_parser_get_error(parser, &fatal);
107
error_str = (error_str != NULL ? error_str : "unknown (bug)" );
109
/* Do not accept faulty server */
110
client_syslog(&client->common, t_strdup_printf("proxy: "
111
"Protocol parse error(%d) in capability/greeting line: %s (line='%s')",
112
ret, error_str, line));
118
managesieve_parser_destroy(&parser);
119
i_stream_destroy(&input);
121
/* Time to exit if greeting was not accepted */
123
client_destroy_internal_failure(client);
128
/* Wait until greeting is received completely */
129
if ( !greeting_recvd ) return 0;
131
/* Send AUTHENTICATE "PLAIN" command
132
* FIXME: Currently there seems to be no SASL client implementation,
133
* so only implement the trivial PLAIN method
138
/* Base64-encode the credentials
139
* [authorization ID \0 authentication ID \0 pass]
141
plain_login = buffer_create_dynamic(pool_datastack_create(), 64);
142
buffer_append_c(plain_login, '\0');
143
buffer_append(plain_login, client->proxy_user, strlen(client->proxy_user));
144
buffer_append_c(plain_login, '\0');
145
buffer_append(plain_login, client->proxy_password, strlen(client->proxy_password));
147
base64 = buffer_create_dynamic(pool_datastack_create(),
148
MAX_BASE64_ENCODED_SIZE(plain_login->used));
149
base64_encode(plain_login->data, plain_login->used, base64);
152
str = t_str_new(128);
153
str_append(str, "AUTHENTICATE \"PLAIN\" ");
154
managesieve_quote_append_string(str, str_c(base64), FALSE);
155
str_append(str, "\r\n");
156
(void)o_stream_send(output, str_data(str), str_len(str));
161
/* Cleanup sensitive data */
162
safe_memset(client->proxy_password, 0,
163
strlen(client->proxy_password));
164
i_free(client->proxy_password);
165
client->proxy_password = NULL;
166
client->proxy_login_sent = TRUE;
171
if (strncasecmp(line, "OK ", 3) == 0) {
172
/* Login successful. Send this line to client. */
173
o_stream_cork(client->output);
174
(void)o_stream_send_str(client->output, line);
175
(void)o_stream_send(client->output, "\r\n", 2);
176
o_stream_uncork(client->output);
178
msg = t_strdup_printf("proxy(%s): started proxying to %s:%u",
179
client->common.virtual_user,
180
login_proxy_get_host(client->proxy),
181
login_proxy_get_port(client->proxy));
183
(void)client_skip_line(client);
184
login_proxy_detach(client->proxy, client->input,
187
client->proxy = NULL;
188
client->input = NULL;
189
client->output = NULL;
190
client->common.fd = -1;
191
client_destroy(client, msg);
194
/* Login failed. Send our own failure reply so client can't
195
* figure out if user exists or not just by looking at the
198
client_send_no(client, AUTH_FAILED_MSG);
200
/* allow client input again */
201
i_assert(client->io == NULL);
202
client->io = io_add(client->common.fd, IO_READ,
203
client_input, client);
205
login_proxy_free(client->proxy);
206
client->proxy = NULL;
208
i_free(client->proxy_user);
209
client->proxy_user = NULL;
219
static void proxy_input(struct istream *input, struct ostream *output,
222
struct managesieve_client *client = context;
226
if (client->io != NULL) {
227
/* remote authentication failed, we're just
232
if (client->destroyed) {
233
/* we came here from client_destroy() */
237
/* failed for some reason, probably server disconnected */
238
client_send_byeresp(client, "TRYLATER", "Temporary login failure.");
239
client_destroy(client, NULL);
243
i_assert(!client->destroyed);
245
switch (i_stream_read(input)) {
248
client_syslog(&client->common, "proxy: Remote input buffer full");
249
client_destroy_internal_failure(client);
253
client_destroy(client, "Proxy: Remote disconnected");
257
while ((line = i_stream_next_line(input)) != NULL) {
258
if (proxy_input_line(client, output, line) < 0)
263
int managesieve_proxy_new(struct managesieve_client *client, const char *host,
264
unsigned int port, const char *user, const char *password)
266
i_assert(user != NULL);
267
i_assert(!client->destroyed);
269
if (password == NULL) {
270
client_syslog(&client->common, "proxy: password not given");
274
i_assert(client->refcount > 1);
275
connection_queue_add(1);
277
if (client->destroyed) {
278
/* connection_queue_add() decided that we were the oldest
279
connection and killed us. */
283
client->proxy = login_proxy_new(&client->common, host, port,
284
proxy_input, client);
285
if (client->proxy == NULL)
288
client->proxy_login_sent = FALSE;
289
client->proxy_user = i_strdup(user);
290
client->proxy_password = i_strdup(password);
292
/* disable input until authentication is finished */
293
if (client->io != NULL)
294
io_remove(&client->io);