client-common.c revision 0266a571e98246e2e1b9dd7fe0301e21e226929a
a8c5a86d183db25a57bf193c06b41e092ec2e151Timo Sirainen/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
c519de264df14a9d525e2604671c332590ce54e3Timo Sirainen/* When max. number of simultaneous connections is reached, few of the
61530b48694398df42744204e35535dbe3f745c4Timo Sirainen oldest connections are disconnected. Since we have to go through all of the
61530b48694398df42744204e35535dbe3f745c4Timo Sirainen clients, it's faster if we disconnect multiple clients. */
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainenstatic unsigned int clients_count = 0;
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainenstatic void client_idle_disconnect_timeout(struct client *client)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BAD,
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainen "Disconnected for inactivity.");
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen client_destroy(client, "Disconnected: Inactivity");
2b3b0df76184799317584b596af8df5afec3ebddTimo Sirainenstatic void client_open_streams(struct client *client)
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainen i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE, FALSE);
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE, FALSE);
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainenstruct client *client_create(int fd, bool ssl, pool_t pool,
d244c6cadd5f077f5d0f1e00c3652d0108a2d908Timo Sirainen if (clients_get_count() >= set->login_max_connections) {
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen /* reached max. users count, kill few of the
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen oldest connections */
baf1148108b7d9739626b47cc57298c36929586aTimo Sirainen /* always use nonblocking I/O */
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen client->v.auth_send_challenge = client_auth_send_challenge;
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen client->v.auth_parse_response = client_auth_parse_response;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenvoid client_destroy(struct client *client, const char *reason)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (!client->login_success && reason != NULL) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen client_get_extra_disconnect_reason(client), NULL);
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen master_auth_request_abort(master_service, client->master_tag);
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainenvoid client_destroy_success(struct client *client, const char *reason)
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainenvoid client_destroy_internal_failure(struct client *client)
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen "Internal login failure. "
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen "Refer to server log for more information.");
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen client_destroy(client, "Internal login failure");
4145aa5025b57ec64418e503c2a5a6bf5a02aec5Timo Sirainen master_service_client_connection_destroyed(master_service);
4145aa5025b57ec64418e503c2a5a6bf5a02aec5Timo Sirainen struct client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen unsigned int i, destroy_count;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen /* find the oldest clients and put them to destroy-buffer */
4145aa5025b57ec64418e503c2a5a6bf5a02aec5Timo Sirainen destroy_count = max_connections > CLIENT_DESTROY_OLDEST_COUNT*2 ?
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen CLIENT_DESTROY_OLDEST_COUNT : I_MIN(max_connections/2, 1);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen for (client = clients; client != NULL; client = client->next) {
4145aa5025b57ec64418e503c2a5a6bf5a02aec5Timo Sirainen for (i = 0; i < destroy_count; i++) {
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen /* @UNSAFE */
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen /* then kill them */
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen for (i = 0; i < destroy_count; i++) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen "Disconnected: Connection queue full");
4145aa5025b57ec64418e503c2a5a6bf5a02aec5Timo Sirainen for (client = clients; client != NULL; client = next) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen client_destroy(client, "Disconnected: Shutting down");
4145aa5025b57ec64418e503c2a5a6bf5a02aec5Timo Sirainenstatic void client_start_tls(struct client *client)
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (!client_unref(client) || client->destroyed)
cdc5c81463995a153c57c68c299e98cc3de0b287Timo Sirainen fd_ssl = ssl_proxy_new(client->fd, &client->ip,
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BYE,
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen "TLS initialization failed.");
4145aa5025b57ec64418e503c2a5a6bf5a02aec5Timo Sirainen "Disconnected: TLS initialization failed.");
4145aa5025b57ec64418e503c2a5a6bf5a02aec5Timo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainenstatic int client_output_starttls(struct client *client)
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen o_stream_unset_flush_callback(client->output);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainenvoid client_cmd_starttls(struct client *client)
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BAD,
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen "TLS is already active.");
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BAD,
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen "TLS support isn't enabled.");
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen /* remove input handler, SSL proxy gives us a new fd. we also have to
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen remove it in case we have to wait for buffer to be flushed */
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen "Begin TLS negotiation now.");
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen /* uncork the old fd */
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen /* the buffer has to be flushed */
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen o_stream_set_flush_pending(client->output, TRUE);
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainenunsigned int clients_get_count(void)
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen master_service_get_client_limit(master_service) > 1)
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen process_title_set(t_strdup_printf(client->tls ?
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic const struct var_expand_table *
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen static struct var_expand_table static_tab[] = {
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen unsigned int i;
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen tab[1].value = t_strcut(client->virtual_user, '@');
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen tab[2].value = strchr(client->virtual_user, '@');
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen for (i = 0; i < 3; i++)
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen tab[i].value = str_sanitize(tab[i].value, 80);
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen tab[5].value = net_ip2addr(&client->local_ip);
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen tab[8].value = client->auth_mech_name == NULL ? NULL :
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen tab[11].value = client->secured ? "secured" : NULL;
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen tab[11].value = ssl_error == NULL ? ssl_state :
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen t_strdup_printf("%s: %s", ssl_state, ssl_error);
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen ssl_proxy_get_security_string(client->ssl_proxy);
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainenstatic bool have_key(const struct var_expand_table *table, const char *str)
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen unsigned int i;
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainenstatic const char *
d21f14c01d5546f4bf1b2cbb28ac1f00c24d952aTimo Sirainenclient_get_log_str(struct client *client, const char *msg)
d21f14c01d5546f4bf1b2cbb28ac1f00c24d952aTimo Sirainen static struct var_expand_table static_tab[3] = {
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen const struct var_expand_table *var_expand_table;
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen const char *p;
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen char *const *e;
d21f14c01d5546f4bf1b2cbb28ac1f00c24d952aTimo Sirainen var_expand_table = get_var_expand_table(client);
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen for (e = client->set->log_format_elements_split; *e != NULL; e++) {
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen for (p = *e; *p != '\0'; p++) {
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen var_expand(str, client->set->login_log_format, tab);
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainenvoid client_log(struct client *client, const char *msg)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen i_info("%s", client_get_log_str(client, msg));
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainenvoid client_log_err(struct client *client, const char *msg)
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen i_error("%s", client_get_log_str(client, msg));
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen const char *const *net;
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen unsigned int bits;
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen if (client->set->login_trusted_networks == NULL)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen net = t_strsplit_spaces(client->set->login_trusted_networks, ", ");
d21f14c01d5546f4bf1b2cbb28ac1f00c24d952aTimo Sirainen if (net_parse_range(*net, &net_ip, &bits) < 0) {
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen if (net_is_in_network(&client->ip, &net_ip, bits))
d21f14c01d5546f4bf1b2cbb28ac1f00c24d952aTimo Sirainenconst char *client_get_extra_disconnect_reason(struct client *client)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (client->set->ssl_require_client_cert && client->ssl_proxy != NULL) {
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen if (ssl_proxy_has_broken_client_cert(client->ssl_proxy))
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen return "(client sent an invalid cert)";
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen if (!ssl_proxy_has_valid_client_cert(client->ssl_proxy))
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen return "(client didn't send a cert)";
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen return "(no auth attempts)";
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen /* some auth attempts without SSL/TLS */
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return "(tried to use disabled plaintext auth)";
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen return "(cert required, client didn't start TLS)";
16a815d92a3202e3a66fd7f8e3478664b852bf1eTimo Sirainen return t_strdup_printf("(auth failed, %u attempts)",
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenvoid client_send_line(struct client *client, enum client_cmd_reply reply,
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainenvoid client_send_raw_data(struct client *client, const void *data, size_t size)
5a7acd67806132cbc1ec9578df60d712d307e4beTimo Sirainen ret = o_stream_send(client->output, data, size);
16a815d92a3202e3a66fd7f8e3478664b852bf1eTimo Sirainen /* either disconnection or buffer full. in either case we want
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen this connection destroyed. however destroying it here might
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen break things if client is still tried to be accessed without
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen being referenced.. */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenvoid client_send_raw(struct client *client, const char *data)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen client_send_raw_data(client, data, strlen(data));
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* buffer full */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BYE,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen "Input buffer full, aborting");
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen client_destroy(client, "Disconnected: Input buffer full");
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* disconnected */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* nothing new read */
16a815d92a3202e3a66fd7f8e3478664b852bf1eTimo Sirainen /* something was read */