1
/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
10
#include "safe-memset.h"
12
#include "str-sanitize.h"
14
#include "managesieve-parser.h"
15
#include "managesieve-quote.h"
16
#include "auth-client.h"
18
#include "client-authenticate.h"
19
#include "managesieve-proxy.h"
24
#define MANAGESIEVE_SERVICE_NAME "managesieve"
26
/* FIXME: The use of the ANONYMOUS mechanism is currently denied
28
static bool _sasl_mechanism_acceptable
29
(const struct auth_mech_desc *mech, bool secured) {
31
/* a) transport is secured
32
b) auth mechanism isn't plaintext
33
c) we allow insecure authentication
36
if ((mech->flags & MECH_SEC_PRIVATE) == 0 &&
37
(mech->flags & MECH_SEC_ANONYMOUS) == 0 &&
38
(secured || !disable_plaintext_auth ||
39
(mech->flags & MECH_SEC_PLAINTEXT) == 0)) {
46
const char *client_authenticate_get_capabilities(bool secured)
48
const struct auth_mech_desc *mech;
49
unsigned int i, count;
53
mech = auth_client_get_available_mechs(auth_client, &count);
56
if ( _sasl_mechanism_acceptable(&(mech[0]), secured) ) {
57
str_append(str, mech[0].name);
60
for (i = 1; i < count; i++) {
61
if ( _sasl_mechanism_acceptable(&(mech[i]), secured) ) {
62
str_append_c(str, ' ');
63
str_append(str, mech[i].name);
71
static void client_auth_input(struct managesieve_client *client)
73
struct managesieve_arg *args;
78
if (client->destroyed)
81
if (!client_read(client))
84
if (client->skip_line) {
85
if (i_stream_next_line(client->input) == NULL)
88
client->skip_line = FALSE;
91
switch (managesieve_parser_read_args(client->parser, 0, 0, &args)) {
94
msg = managesieve_parser_get_error(client->parser, &fatal);
96
/* FIXME: What to do? */
99
sasl_server_auth_client_error(&client->common, msg);
102
/* not enough data */
106
if (args[0].type != MANAGESIEVE_ARG_STRING ||
107
args[1].type != MANAGESIEVE_ARG_EOL) {
108
sasl_server_auth_client_error(&client->common, "Invalid AUTHENTICATE client response.");
112
line = MANAGESIEVE_ARG_STR(&args[0]);
114
auth_client_request_continue(client->common.auth_request, line);
115
io_remove(&client->io);
117
/* clear sensitive data */
118
safe_memset(line, 0, strlen(line));
121
static void client_auth_failed(struct managesieve_client *client)
123
/* get back to normal client input. */
124
if (client->io != NULL)
125
io_remove(&client->io);
126
client->io = io_add(client->common.fd, IO_READ,
127
client_input, client);
130
static bool client_handle_args(struct managesieve_client *client,
131
const char *const *args, bool success)
133
const char *reason = NULL, *host = NULL, *destuser = NULL, *pass = NULL;
135
unsigned int port = 2000;
136
bool proxy = FALSE, temp = FALSE, nologin = !success, proxy_self;
138
for (; *args != NULL; args++) {
139
if (strcmp(*args, "nologin") == 0)
141
else if (strcmp(*args, "proxy") == 0)
143
else if (strcmp(*args, "temp") == 0)
145
else if (strncmp(*args, "reason=", 7) == 0)
147
else if (strncmp(*args, "host=", 5) == 0)
149
else if (strncmp(*args, "port=", 5) == 0)
150
port = atoi(*args + 5);
151
else if (strncmp(*args, "destuser=", 9) == 0)
152
destuser = *args + 9;
153
else if (strncmp(*args, "pass=", 5) == 0)
157
if (destuser == NULL)
158
destuser = client->common.virtual_user;
160
proxy_self = proxy &&
161
login_proxy_is_ourself(&client->common, host, port, destuser);
162
if (proxy && !proxy_self) {
163
/* we want to proxy the connection to another server.
164
don't do this unless authentication succeeded. with
165
master user proxying we can get FAIL with proxy still set.
167
proxy host=.. [port=..] [destuser=..] pass=.. */
171
if (managesieve_proxy_new(client, host, port, destuser, pass) < 0)
172
client_destroy_internal_failure(client);
176
if (!proxy && host != NULL) {
177
/* MANAGESIEVE referral
179
[nologin] referral host=.. [port=..] [destuser=..]
182
NO (REFERRAL sieve://user;AUTH=mech@host:port/) Can't login.
183
OK (...) Logged in, but you should use this server instead.
184
.. [REFERRAL ..] (Reason from auth server)
186
resp_code = t_str_new(128);
187
str_printfa(resp_code, "REFERRAL sieve://%s;AUTH=%s@%s",
188
destuser, client->common.auth_mech_name, host);
190
str_printfa(resp_code, ":%u", port);
192
if (reason == NULL) {
194
reason = "Try this server instead.";
196
reason = "Logged in, but you should use "
197
"this server instead.";
201
client_send_okresp(client, str_c(resp_code), reason);
202
client_destroy(client, "Login with referral");
205
client_send_noresp(client, str_c(resp_code), reason);
206
} else if (nologin || proxy_self) {
207
/* Authentication went ok, but for some reason user isn't
208
allowed to log in. Shouldn't probably happen. */
210
client_syslog(&client->common,
211
"Proxying loops to itself");
215
client_send_no(client, reason);
217
client_send_no(client, AUTH_TEMP_FAILED_MSG);
219
client_send_no(client, AUTH_FAILED_MSG);
221
/* normal login/failure */
225
i_assert(nologin || proxy_self);
227
managesieve_parser_reset(client->parser);
229
if (!client->destroyed)
230
client_auth_failed(client);
234
static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
235
const char *data, const char *const *args)
237
struct managesieve_client *client = (struct managesieve_client *)_client;
240
i_assert(!client->destroyed ||
241
reply == SASL_SERVER_REPLY_CLIENT_ERROR ||
242
reply == SASL_SERVER_REPLY_MASTER_FAILED);
244
client->skip_line = TRUE;
247
case SASL_SERVER_REPLY_SUCCESS:
248
if ( client->to_auth_waiting != NULL )
249
timeout_remove(&client->to_auth_waiting);
251
if (client_handle_args(client, args, TRUE))
255
client_destroy(client, "Login");
257
case SASL_SERVER_REPLY_AUTH_FAILED:
258
case SASL_SERVER_REPLY_CLIENT_ERROR:
259
if ( client->to_auth_waiting != NULL )
260
timeout_remove(&client->to_auth_waiting);
262
if (client_handle_args(client, args, FALSE))
266
client_send_no(client, data != NULL ? data : AUTH_FAILED_MSG);
268
managesieve_parser_reset(client->parser);
270
if (!client->destroyed)
271
client_auth_failed(client);
273
case SASL_SERVER_REPLY_MASTER_FAILED:
275
client_destroy_internal_failure(client);
277
client_send_no(client, data);
278
client_destroy(client, data);
281
case SASL_SERVER_REPLY_CONTINUE:
283
str = t_str_new(256);
284
managesieve_quote_append_string(str, data, TRUE);
285
str_append(str, "\r\n");
287
/* don't check return value here. it gets tricky if we try
288
to call client_destroy() in here. */
289
(void)o_stream_send(client->output, str_c(str), str_len(str));
292
managesieve_parser_reset(client->parser);
294
i_assert(client->io == NULL);
295
client->io = io_add(client->common.fd, IO_READ,
296
client_auth_input, client);
297
client_auth_input(client);
302
client_unref(client);
305
int cmd_authenticate(struct managesieve_client *client, struct managesieve_arg *args)
307
const char *mech_name, *init_resp = NULL;
309
/* one mandatory argument: authentication mechanism name */
310
if (args[0].type != MANAGESIEVE_ARG_STRING)
312
if (args[1].type != MANAGESIEVE_ARG_EOL) {
313
/* optional SASL initial response */
314
if (args[1].type != MANAGESIEVE_ARG_STRING ||
315
args[2].type != MANAGESIEVE_ARG_EOL)
317
init_resp = MANAGESIEVE_ARG_STR(&args[1]);
320
mech_name = MANAGESIEVE_ARG_STR(&args[0]);
321
if (*mech_name == '\0')
324
/* FIXME: This refuses the ANONYMOUS mechanism.
325
* This can be removed once anonymous login is implemented according to the
326
* draft RFC. - Stephan
328
if ( strncasecmp(mech_name, "ANONYMOUS", 9) == 0 ) {
329
client_send_no(client, "ANONYMOUS mechanism is not implemented.");
334
sasl_server_auth_begin(&client->common, MANAGESIEVE_SERVICE_NAME, mech_name,
335
init_resp, sasl_callback);
336
if (!client->common.authenticating)
339
/* don't handle input until we get the initial auth reply */
340
if (client->io != NULL)
341
io_remove(&client->io);
342
client_set_auth_waiting(client);
344
managesieve_parser_reset(client->parser);