1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
|
/* Copyright (c) 2002-2016 Pigeonhole authors, see the included COPYING file
*/
#include "login-common.h"
#include "base64.h"
#include "buffer.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "safe-memset.h"
#include "str.h"
#include "str-sanitize.h"
#include "auth-client.h"
#include "managesieve-parser.h"
#include "managesieve-quote.h"
#include "client.h"
#include "client-authenticate.h"
#include "managesieve-proxy.h"
const char *client_authenticate_get_capabilities
(struct client *client)
{
const struct auth_mech_desc *mech;
unsigned int i, count;
bool first = TRUE;
string_t *str;
str = t_str_new(128);
mech = sasl_server_get_advertised_mechs(client, &count);
for (i = 0; i < count; i++) {
/* Filter ANONYMOUS mechanism, ManageSieve has no use-case for it */
if ( (mech[i].flags & MECH_SEC_ANONYMOUS) == 0 ) {
if ( !first )
str_append_c(str, ' ');
else
first = FALSE;
str_append(str, mech[i].name);
}
}
return str_c(str);
}
void managesieve_client_auth_result(struct client *client,
enum client_auth_result result,
const struct client_auth_reply *reply, const char *text)
{
struct managesieve_client *msieve_client =
(struct managesieve_client *)client;
string_t *referral;
switch (result) {
case CLIENT_AUTH_RESULT_SUCCESS:
/* nothing to be done for IMAP */
break;
case CLIENT_AUTH_RESULT_REFERRAL_SUCCESS:
case CLIENT_AUTH_RESULT_REFERRAL_NOLOGIN:
/* MANAGESIEVE referral
[nologin] referral host=.. [port=..] [destuser=..]
[reason=..]
NO [REFERRAL sieve://user;AUTH=mech@host:port/] "Can't login."
OK [...] "Logged in, but you should use this server instead."
.. [REFERRAL ..] Reason from auth server
*/
referral = t_str_new(128);
str_printfa(referral, "REFERRAL sieve://%s;AUTH=%s@%s",
reply->destuser, client->auth_mech_name, reply->host);
if ( reply->port != 4190 )
str_printfa(referral, ":%u", reply->port);
if ( result == CLIENT_AUTH_RESULT_REFERRAL_SUCCESS ) {
client_send_okresp(client, str_c(referral), text);;
} else {
client_send_noresp(client, str_c(referral), text);
}
break;
case CLIENT_AUTH_RESULT_ABORTED:
case CLIENT_AUTH_RESULT_AUTHFAILED_REASON:
case CLIENT_AUTH_RESULT_AUTHZFAILED:
client_send_no(client, text);
break;
case CLIENT_AUTH_RESULT_TEMPFAIL:
client_send_noresp(client, "TRYLATER", text);
break;
case CLIENT_AUTH_RESULT_SSL_REQUIRED:
client_send_noresp(client, "ENCRYPT-NEEDED", text);
break;
case CLIENT_AUTH_RESULT_AUTHFAILED:
default:
client_send_no(client, text);
break;
}
msieve_client->auth_response_input = NULL;
managesieve_parser_reset(msieve_client->parser);
}
void managesieve_client_auth_send_challenge
(struct client *client, const char *data)
{
struct managesieve_client *msieve_client =
(struct managesieve_client *) client;
T_BEGIN {
string_t *str = t_str_new(256);
managesieve_quote_append_string(str, data, TRUE);
str_append(str, "\r\n");
client_send_raw_data(client, str_c(str), str_len(str));
} T_END;
msieve_client->auth_response_input = NULL;
managesieve_parser_reset(msieve_client->parser);
}
static int managesieve_client_auth_read_response
(struct managesieve_client *msieve_client, bool initial, const char **error_r)
{
struct client *client = &msieve_client->common;
const struct managesieve_arg *args;
const char *error;
bool fatal;
const unsigned char *data;
size_t size;
uoff_t resp_size;
int ret;
*error_r = NULL;
if ( i_stream_read(client->input) == -1 ) {
/* disconnected */
client_destroy(client, "Disconnected");
return -1;
}
if ( msieve_client->auth_response_input == NULL ) {
if ( msieve_client->skip_line ) {
if ( i_stream_next_line(client->input) == NULL )
return 0;
msieve_client->skip_line = FALSE;
}
switch ( managesieve_parser_read_args(msieve_client->parser, 0,
MANAGESIEVE_PARSE_FLAG_STRING_STREAM, &args) ) {
case -1:
error = managesieve_parser_get_error(msieve_client->parser, &fatal);
if (fatal) {
client_send_bye(client, error);
client_destroy(client, t_strconcat
("Disconnected: parse error during auth: ", error, NULL));
} else {
*error_r = error;
}
msieve_client->skip_line = TRUE;
return -1;
case -2:
/* not enough data */
return 0;
default:
break;
}
if ( MANAGESIEVE_ARG_IS_EOL(&args[0]) ) {
if (!initial) {
*error_r = "Received empty AUTHENTICATE client response line.";
msieve_client->skip_line = TRUE;
return -1;
}
msieve_client->skip_line = TRUE;
return 1;
}
if ( !managesieve_arg_get_string_stream
(&args[0], &msieve_client->auth_response_input)
|| !MANAGESIEVE_ARG_IS_EOL(&args[1]) ) {
if ( !initial )
*error_r = "Invalid AUTHENTICATE client response.";
else
*error_r = "Invalid AUTHENTICATE initial response.";
msieve_client->skip_line = TRUE;
return -1;
}
if ( i_stream_get_size
(msieve_client->auth_response_input, FALSE, &resp_size) <= 0 )
resp_size = 0;
if (client->auth_response == NULL)
client->auth_response = str_new(default_pool, I_MAX(resp_size+1, 256));
}
while ( (ret=i_stream_read_data
(msieve_client->auth_response_input, &data, &size, 0) ) > 0 ) {
if (str_len(client->auth_response) + size > LOGIN_MAX_AUTH_BUF_SIZE) {
client_destroy(client, "Authentication response too large");
return -1;
}
str_append_n(client->auth_response, data, size);
i_stream_skip(msieve_client->auth_response_input, size);
}
if ( ret == 0 ) return 0;
if ( msieve_client->auth_response_input->stream_errno != 0 ) {
if ( !client->input->eof &&
msieve_client->auth_response_input->stream_errno == EINVAL ) {
msieve_client->skip_line = TRUE;
*error_r = t_strconcat
("Error in AUTHENTICATE response string: ",
i_stream_get_error(msieve_client->auth_response_input), NULL);
return -1;
}
client_destroy(client, "Disconnected");
return -1;
}
if ( i_stream_next_line(client->input) == NULL )
return 0;
return 1;
}
void managesieve_client_auth_parse_response(struct client *client)
{
struct managesieve_client *msieve_client =
(struct managesieve_client *) client;
const char *error = NULL;
int ret;
if ( (ret=managesieve_client_auth_read_response(msieve_client, FALSE, &error))
< 0 ) {
if ( error != NULL )
client_auth_fail(client, error);
return;
}
if ( ret == 0 ) return;
if ( strcmp(str_c(client->auth_response), "*") == 0 ) {
client_auth_abort(client);
return;
}
client_auth_respond(client, str_c(client->auth_response));
memset(str_c_modifiable(client->auth_response), 0,
str_len(client->auth_response));
}
int cmd_authenticate
(struct managesieve_client *msieve_client, const struct managesieve_arg *args)
{
/* NOTE: This command's input is handled specially because the
SASL-IR can be large. */
struct client *client = &msieve_client->common;
const char *mech_name, *init_response;
const char *error;
int ret;
if (!msieve_client->auth_mech_name_parsed) {
i_assert(args != NULL);
/* one mandatory argument: authentication mechanism name */
if ( !managesieve_arg_get_string(&args[0], &mech_name) )
return -1;
if (*mech_name == '\0')
return -1;
/* Refuse the ANONYMOUS mechanism. */
if ( strncasecmp(mech_name, "ANONYMOUS", 9) == 0 ) {
client_send_no(client, "ANONYMOUS login is not allowed.");
return 1;
}
i_free(client->auth_mech_name);
client->auth_mech_name = i_strdup(mech_name);
msieve_client->auth_mech_name_parsed = TRUE;
msieve_client->auth_response_input = NULL;
managesieve_parser_reset(msieve_client->parser);
}
msieve_client->skip_line = FALSE;
if ( (ret=managesieve_client_auth_read_response(msieve_client, TRUE, &error))
< 0 ) {
if ( error != NULL ) {
msieve_client->auth_mech_name_parsed = FALSE;
client_send_no(client, error);
}
return 1;
}
if ( ret == 0 ) return 0;
init_response = ( client->auth_response == NULL ? NULL :
t_strdup(str_c(client->auth_response)) );
msieve_client->auth_mech_name_parsed = FALSE;
if ( (ret=client_auth_begin
(client, t_strdup(client->auth_mech_name), init_response)) < 0 )
return ret;
msieve_client->cmd_finished = TRUE;
return 0;
}
|