Skip to content

Commit

Permalink
login-common: Implement post-login proxying and use it with SSL conne…
Browse files Browse the repository at this point in the history
…ctions

Note: This temporarily breaks the SSL connections a bit. If post-login
process disconnects the client, it's not noticed by the login process.
Client connections are noticed by the post-login though.
  • Loading branch information
sirainen authored and Timo Sirainen committed Nov 6, 2017
1 parent be6e55f commit 87dbf3e
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 8 deletions.
65 changes: 64 additions & 1 deletion src/login-common/client-common.c
Expand Up @@ -6,6 +6,7 @@
#include "llist.h"
#include "istream.h"
#include "ostream.h"
#include "iostream-proxy.h"
#include "iostream-rawlog.h"
#include "process-title.h"
#include "hook-build.h"
Expand Down Expand Up @@ -252,8 +253,11 @@ void client_destroy(struct client *client, const char *reason)
if (client->output != NULL)
o_stream_uncork(client->output);
if (!client->login_success) {
io_remove(&client->io);
if (client->ssl_proxy != NULL)
ssl_proxy_destroy(client->ssl_proxy);
if (client->iostream_fd_proxy != NULL)
iostream_proxy_unref(&client->iostream_fd_proxy);
i_stream_close(client->input);
o_stream_close(client->output);
i_close_fd(&client->fd);
Expand All @@ -276,7 +280,6 @@ void client_destroy(struct client *client, const char *reason)
i_assert(!client->authenticating);
}

io_remove(&client->io);
timeout_remove(&client->to_disconnect);
timeout_remove(&client->to_auth_waiting);
if (client->auth_response != NULL)
Expand Down Expand Up @@ -341,6 +344,8 @@ bool client_unref(struct client **_client)

if (client->ssl_proxy != NULL)
ssl_proxy_free(&client->ssl_proxy);
if (client->iostream_fd_proxy != NULL)
iostream_proxy_unref(&client->iostream_fd_proxy);
i_stream_unref(&client->input);
o_stream_unref(&client->output);
i_close_fd(&client->fd);
Expand Down Expand Up @@ -497,6 +502,64 @@ void client_cmd_starttls(struct client *client)
}
}

static void
iostream_fd_proxy_finished(enum iostream_proxy_side side ATTR_UNUSED,
enum iostream_proxy_status status ATTR_UNUSED,
struct client *client)
{
/* Destroy the proxy now. The other side of the proxy is still
unfinished and we don't want to get back here and unreference
the client twice. */
iostream_proxy_unref(&client->iostream_fd_proxy);
client_unref(&client);
}

int client_get_plaintext_fd(struct client *client, int *fd_r, bool *close_fd_r)
{
int fds[2];

if (!client->tls) {
/* Plaintext connection - We can send the fd directly to
the post-login process without any proxying. */
*fd_r = client->fd;
*close_fd_r = FALSE;
return 0;
}

/* We'll have to start proxying from now on until either side
disconnects. Create a socketpair where login process is proxying on
one side and the other side is sent to the post-login process. */
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
client_log_err(client, t_strdup_printf("socketpair() failed: %m"));
return -1;
}
fd_set_nonblock(fds[0], TRUE);
fd_set_nonblock(fds[1], TRUE);

struct ostream *output = o_stream_create_fd(fds[0], IO_BLOCK_SIZE);
struct istream *input =
i_stream_create_fd_autoclose(&fds[0], IO_BLOCK_SIZE);
o_stream_set_no_error_handling(output, TRUE);

i_assert(client->io == NULL);

client_ref(client);
client->iostream_fd_proxy =
iostream_proxy_create(input, output,
client->input, client->output);
i_stream_unref(&input);
o_stream_unref(&output);

iostream_proxy_set_completion_callback(client->iostream_fd_proxy,
iostream_fd_proxy_finished,
client);
iostream_proxy_start(client->iostream_fd_proxy);

*fd_r = fds[1];
*close_fd_r = TRUE;
return 0;
}

unsigned int clients_get_count(void)
{
return clients_count;
Expand Down
5 changes: 5 additions & 0 deletions src/login-common/client-common.h
Expand Up @@ -156,6 +156,7 @@ struct client {
struct istream *input;
struct ostream *output;
struct io *io;
struct iostream_proxy *iostream_fd_proxy;
struct timeout *to_auth_waiting;
struct timeout *to_disconnect;

Expand Down Expand Up @@ -244,6 +245,8 @@ client_alloc(int fd, pool_t pool,
const struct master_service_ssl_settings *ssl_set);
void client_init(struct client *client, void **other_sets);
void client_destroy(struct client *client, const char *reason);
/* Destroy the client after a successful login. Either the client fd was
sent to the post-login process, or the connection will be proxied. */
void client_destroy_success(struct client *client, const char *reason);

void client_ref(struct client *client);
Expand All @@ -252,6 +255,8 @@ bool client_unref(struct client **client) ATTR_NOWARN_UNUSED_RESULT;
int client_init_ssl(struct client *client);
void client_cmd_starttls(struct client *client);

int client_get_plaintext_fd(struct client *client, int *fd_r, bool *close_fd_r);

unsigned int clients_get_count(void) ATTR_PURE;

void client_add_forward_field(struct client *client, const char *key,
Expand Down
27 changes: 20 additions & 7 deletions src/login-common/sasl-server.c
Expand Up @@ -115,7 +115,7 @@ master_auth_callback(const struct master_auth_reply *reply, void *context)
call_client_callback(client, sasl_reply, data, NULL);
}

static void master_send_request(struct anvil_request *anvil_request)
static int master_send_request(struct anvil_request *anvil_request)
{
struct client *client = anvil_request->client;
struct master_auth_request_params params;
Expand All @@ -124,6 +124,11 @@ static void master_send_request(struct anvil_request *anvil_request)
size_t size;
buffer_t *buf;
const char *session_id = client_get_session_id(client);
int fd;
bool close_fd;

if (client_get_plaintext_fd(client, &fd, &close_fd) < 0)
return -1;

i_zero(&req);
req.auth_pid = anvil_request->auth_pid;
Expand Down Expand Up @@ -151,12 +156,15 @@ static void master_send_request(struct anvil_request *anvil_request)
client->master_auth_id = req.auth_id;

i_zero(&params);
params.client_fd = client->fd;
params.client_fd = fd;
params.socket_path = client->postlogin_socket_path;
params.request = req;
params.data = buf->data;
master_auth_request_full(master_auth, &params, master_auth_callback,
client, &client->master_tag);
if (close_fd)
i_close_fd(&fd);
return 0;
}

static void ATTR_NULL(1)
Expand All @@ -167,20 +175,25 @@ anvil_lookup_callback(const char *reply, void *context)
const struct login_settings *set = client->set;
const char *errmsg;
unsigned int conn_count;
int ret;

conn_count = 0;
if (reply != NULL && str_to_uint(reply, &conn_count) < 0)
i_fatal("Received invalid reply from anvil: %s", reply);

/* reply=NULL if we didn't need to do anvil lookup,
or if the anvil lookup failed. allow failed anvil lookups in. */
if (reply == NULL || conn_count < set->mail_max_userip_connections)
master_send_request(req);
else {
client->authenticating = FALSE;
auth_client_send_cancel(auth_client, req->auth_id);
if (reply == NULL || conn_count < set->mail_max_userip_connections) {
ret = master_send_request(req);
errmsg = NULL; /* client will see internal error */
} else {
ret = -1;
errmsg = t_strdup_printf(ERR_TOO_MANY_USERIP_CONNECTIONS,
set->mail_max_userip_connections);
}
if (ret < 0) {
client->authenticating = FALSE;
auth_client_send_cancel(auth_client, req->auth_id);
call_client_callback(client, SASL_SERVER_REPLY_MASTER_FAILED,
errmsg, NULL);
}
Expand Down

0 comments on commit 87dbf3e

Please sign in to comment.