static void login_connection_authreply_input(struct login_connection *conn) { const char *line; while ((line = i_stream_read_next_line(conn->input)) != NULL) T_BEGIN { if (!conn->handshaked) { if (!version_string_verify(line, "director-authreply-client", AUTHREPLY_PROTOCOL_MAJOR_VERSION)) { i_error("authreply client sent invalid handshake: %s", line); login_connection_deinit(&conn); return; } conn->handshaked = TRUE; } else { auth_input_line(line, conn); } } T_END; if (conn->input->eof) { if (conn->input->stream_errno != 0 && conn->input->stream_errno != ECONNRESET) { i_error("read(authreply connection) failed: %s", i_stream_get_error(conn->input)); } login_connection_deinit(&conn); } }
static void director_connect(struct director_context *ctx) { #define DIRECTOR_HANDSHAKE "VERSION\tdirector-doveadm\t1\t0\n" const char *line; int fd; fd = doveadm_connect(ctx->socket_path); net_set_nonblock(fd, FALSE); ctx->input = i_stream_create_fd_autoclose(&fd, (size_t)-1); director_send(ctx, DIRECTOR_HANDSHAKE); alarm(5); line = i_stream_read_next_line(ctx->input); alarm(0); if (line == NULL) { if (ctx->input->stream_errno != 0) i_fatal("read(%s) failed: %m", ctx->socket_path); else if (ctx->input->eof) i_fatal("%s disconnected", ctx->socket_path); else { i_fatal("read(%s) timed out (is director configured?)", ctx->socket_path); } } if (!version_string_verify(line, "director-doveadm", 1)) { i_fatal_status(EX_PROTOCOL, "%s not a compatible director-doveadm socket", ctx->socket_path); } }
static void master_connection_input(struct master_connection *conn) { const char *line; int ret; if (i_stream_read(conn->input) < 0) { master_service_stop(master_service); return; } if (!conn->version_received) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (!version_string_verify(line, INDEXER_MASTER_NAME, INDEXER_PROTOCOL_MAJOR_VERSION)) { i_error("Indexer master not compatible with this master " "(mixed old and new binaries?)"); master_service_stop(master_service); return; } conn->version_received = TRUE; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = master_connection_input_line(conn, line); } T_END; if (ret < 0) { master_service_stop(master_service); break; } } }
static void script_verify_version(const char *line) { if (line == NULL || !version_string_verify(line, "script", SCRIPT_MAJOR_VERSION)) { i_fatal("Client not compatible with this binary " "(connecting to wrong socket?)"); } }
static void client_connection_input(struct client_connection *conn) { const char *line; bool ok = TRUE; int ret; if (!conn->handshaked) { if ((line = i_stream_read_next_line(conn->input)) == NULL) { if (conn->input->eof || conn->input->stream_errno != 0) { client_log_disconnect_error(conn); client_connection_destroy(&conn); } return; } if (!version_string_verify(line, "doveadm-server", DOVEADM_SERVER_PROTOCOL_VERSION_MAJOR)) { i_error("doveadm client not compatible with this server " "(mixed old and new binaries?)"); client_connection_destroy(&conn); return; } conn->handshaked = TRUE; } if (!conn->authenticated) { if ((ret = client_connection_authenticate(conn)) <= 0) { if (ret < 0) { o_stream_nsend(conn->output, "-\n", 2); client_connection_destroy(&conn); } return; } o_stream_nsend(conn->output, "+\n", 2); conn->authenticated = TRUE; } while (ok && !conn->input->closed && (line = i_stream_read_next_line(conn->input)) != NULL) { T_BEGIN { char **args; args = p_strsplit(pool_datastack_create(), line, "\t"); ok = client_handle_command(conn, args); } T_END; } if (conn->input->eof || conn->input->stream_errno != 0 || !ok) client_connection_destroy(&conn); }
static int imap_hibernate_handshake(int fd, const char *path) { char buf[1024]; ssize_t ret; if (write_full(fd, IMAP_HIBERNATE_HANDSHAKE, strlen(IMAP_HIBERNATE_HANDSHAKE)) < 0) { i_error("write(%s) failed: %m", path); return -1; } else if ((ret = read(fd, buf, sizeof(buf)-1)) < 0) { i_error("read(%s) failed: %m", path); return -1; } else if (ret > 0 && buf[ret-1] == '\n') { buf[ret-1] = '\0'; if (version_string_verify(buf, "imap-hibernate", 1)) return 0; } i_error("%s sent invalid VERSION handshake: %s", path, buf); return -1; }
static void worker_connection_input(struct worker_connection *conn) { const char *line; if (i_stream_read(conn->input) < 0) { worker_connection_disconnect(conn); return; } if (!conn->version_received) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (!version_string_verify(line, INDEXER_WORKER_NAME, INDEXER_PROTOCOL_MAJOR_VERSION)) { i_error("Indexer worker not compatible with this master " "(mixed old and new binaries?)"); worker_connection_disconnect(conn); return; } conn->version_received = TRUE; } if (conn->process_limit == 0) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (str_to_uint(line, &conn->process_limit) < 0 || conn->process_limit == 0) { i_error("Indexer worker sent invalid handshake: %s", line); worker_connection_disconnect(conn); return; } } while ((line = i_stream_next_line(conn->input)) != NULL) { if (worker_connection_input_line(conn, line) < 0) { worker_connection_disconnect(conn); break; } } }
static void config_connection_input(void *context) { struct config_connection *conn = context; const char *const *args, *line; switch (i_stream_read(conn->input)) { case -2: i_error("BUG: Config client connection sent too much data"); config_connection_destroy(conn); return; case -1: config_connection_destroy(conn); return; } if (!conn->version_received) { line = i_stream_next_line(conn->input); if (line == NULL) return; if (!version_string_verify(line, "config", CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION)) { i_error("Config client not compatible with this server " "(mixed old and new binaries?)"); config_connection_destroy(conn); return; } conn->version_received = TRUE; } while ((args = config_connection_next_line(conn)) != NULL) { if (args[0] == NULL) continue; if (strcmp(args[0], "REQ") == 0) { if (config_connection_request(conn, args + 1) < 0) break; } } }
static void notify_connection_input(struct notify_connection *conn) { const char *line; int ret; switch (i_stream_read(conn->input)) { case -2: i_error("BUG: Client connection sent too much data"); notify_connection_destroy(conn); return; case -1: notify_connection_destroy(conn); return; } if (!conn->version_received) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (!version_string_verify(line, "replicator-notify", NOTIFY_CLIENT_PROTOCOL_MAJOR_VERSION)) { i_error("Notify client not compatible with this server " "(mixed old and new binaries?)"); notify_connection_destroy(conn); return; } conn->version_received = TRUE; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = notify_connection_input_line(conn, line); } T_END; if (ret < 0) { notify_connection_destroy(conn); break; } } }
static void client_connected(struct master_service_connection *conn) { enum mail_storage_service_flags flags = MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS; string_t *instr, *keys; const char **args, *key, *value, *error, *version_line, *data_line; struct mail_storage_service_ctx *service_ctx; struct mail_storage_service_input input; struct mail_storage_service_user *user; char buf[1024]; unsigned int i, socket_count; int fd = -1; ssize_t ret; alarm(SCRIPT_LOGIN_READ_TIMEOUT_SECS); net_set_nonblock(conn->fd, FALSE); instr = t_str_new(1024); ret = fd_read(conn->fd, buf, sizeof(buf), &fd); while (ret > 0) { str_append_n(instr, buf, ret); if (buf[ret-1] == '\n' && strchr(str_c(instr), '\n')[1] != '\0') { str_truncate(instr, str_len(instr)-1); break; } ret = read(conn->fd, buf, sizeof(buf)); } version_line = str_c(instr); data_line = strchr(version_line, '\n'); if (data_line != NULL) version_line = t_strdup_until(version_line, data_line++); else version_line = NULL; if (ret > 0 || version_line != NULL) { if (version_line == NULL || !version_string_verify(version_line, "script-login", SCRIPT_LOGIN_PROTOCOL_VERSION_MAJOR)) { i_fatal("Client not compatible with this binary " "(connecting to wrong socket?)"); } } if (ret <= 0) { if (ret < 0) i_fatal("read() failed: %m"); else i_fatal("read() failed: disconnected"); } if (fd == -1) i_fatal("client fd not received"); alarm(0); /* put everything to environment */ env_clean(); keys = t_str_new(256); args = t_strsplit_tab(data_line); if (str_array_length(args) < 3) i_fatal("Missing input fields"); i = 0; memset(&input, 0, sizeof(input)); input.module = "mail"; /* need to get mail_uid, mail_gid */ input.service = "script-login"; (void)net_addr2ip(args[i++], &input.local_ip); (void)net_addr2ip(args[i++], &input.remote_ip); input.username = args[i++]; input.userdb_fields = args + i; env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&input.local_ip), NULL)); env_put(t_strconcat("IP=", net_ip2addr(&input.remote_ip), NULL)); env_put(t_strconcat("USER="******"%s ", key); } } env_put(t_strconcat(ENV_USERDB_KEYS"=", str_c(keys), NULL)); master_service_init_log(master_service, t_strdup_printf("script-login(%s): ", input.username)); if (drop_to_userdb_privileges) { service_ctx = mail_storage_service_init(master_service, NULL, flags); if (mail_storage_service_lookup(service_ctx, &input, &user, &error) <= 0) i_fatal("%s", error); mail_storage_service_restrict_setenv(service_ctx, user); /* we can't exec anything in a chroot */ env_remove("RESTRICT_CHROOT"); restrict_access_by_env(getenv("HOME"), TRUE); } if (dup2(fd, STDIN_FILENO) < 0) i_fatal("dup2() failed: %m"); if (dup2(fd, STDOUT_FILENO) < 0) i_fatal("dup2() failed: %m"); if (close(fd) < 0) i_fatal("close() failed: %m"); if (conn->fd != SCRIPT_COMM_FD) { if (dup2(conn->fd, SCRIPT_COMM_FD) < 0) i_fatal("dup2() failed: %m"); if (close(conn->fd) < 0) i_fatal("close() failed: %m"); } /* close all listener sockets */ socket_count = master_service_get_socket_count(master_service); for (i = 0; i < socket_count; i++) { if (close(MASTER_LISTEN_FD_FIRST + i) < 0) i_error("close(listener) failed: %m"); } if (close(MASTER_STATUS_FD) < 0) i_error("close(status) failed: %m"); execvp_const(exec_args[0], exec_args); }
static void auth_worker_input(struct auth_worker_client *client) { char *line; bool ret; switch (i_stream_read(client->input)) { case 0: return; case -1: /* disconnected */ auth_worker_client_destroy(&client); return; case -2: /* buffer full */ i_error("BUG: Auth worker server sent us more than %d bytes", (int)AUTH_WORKER_MAX_LINE_LENGTH); auth_worker_client_destroy(&client); return; } if (!client->version_received) { line = i_stream_next_line(client->input); if (line == NULL) return; if (!version_string_verify(line, "auth-worker", AUTH_WORKER_PROTOCOL_MAJOR_VERSION)) { i_error("Auth worker not compatible with this server " "(mixed old and new binaries?)"); auth_worker_client_destroy(&client); return; } client->version_received = TRUE; } if (!client->dbhash_received) { line = i_stream_next_line(client->input); if (line == NULL) return; if (!auth_worker_verify_db_hash(line)) { i_error("Auth worker sees different passdbs/userdbs " "than auth server. Maybe config just changed " "and this goes away automatically?"); auth_worker_client_destroy(&client); return; } client->dbhash_received = TRUE; } client->refcount++; while ((line = i_stream_next_line(client->input)) != NULL) { T_BEGIN { ret = auth_worker_handle_line(client, line); } T_END; if (!ret) { struct auth_worker_client *client2 = client; auth_worker_client_destroy(&client2); break; } } auth_worker_client_unref(&client); }
static void client_ctrl_input(struct client *client) { const char *const *args; const char *line; int ret; timeout_reset(client->to_idle); if (client->fd_in == -1 || client->fd_out == -1) { if ((ret = client_ctrl_read_fds(client)) <= 0) { if (ret < 0) client_abort(client, "FD Transfer failed"); return; } } switch (i_stream_read(client->ctrl_input)) { case -1: /* disconnected */ client_destroy(client); return; case -2: /* line too long, kill it */ client_abort(client, "Control session aborted: Input line too long"); return; } if (!client->version_received) { if ((line = i_stream_next_line(client->ctrl_input)) == NULL) return; if (!version_string_verify(line, "imap-urlauth-worker", IMAP_URLAUTH_WORKER_PROTOCOL_MAJOR_VERSION)) { i_error("imap-urlauth-worker client not compatible with this server " "(mixed old and new binaries?) %s", line); client_abort(client, "Control session aborted: Version mismatch"); return; } client->version_received = TRUE; if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) { client_destroy(client); return; } } if (client->access_received) { client_abort(client, "Control session aborted: Unexpected input"); return; } if ((line = i_stream_next_line(client->ctrl_input)) == NULL) return; args = t_strsplit_tabescaped(line); if (*args == NULL || strcmp(*args, "ACCESS") != 0) { i_error("Invalid control command: %s", str_sanitize(line, 80)); client_abort(client, "Control session aborted: Invalid command"); return; } args++; if (*args == NULL) { i_error("Invalid ACCESS command: %s", str_sanitize(line, 80)); client_abort(client, "Control session aborted: Invalid command"); return; } i_assert(client->access_user == NULL); if (**args != '\0') { client->access_user = i_strdup(*args); client->access_anonymous = FALSE; } else { client->access_user = i_strdup("anonymous"); client->access_anonymous = TRUE; } i_set_failure_prefix("imap-urlauth[%s](%s): ", my_pid, client->access_user); args++; while (*args != NULL) { /* debug */ if (strcasecmp(*args, "debug") == 0) { client->debug = TRUE; /* apps=<access-application>[,<access-application,...] */ } else if (strncasecmp(*args, "apps=", 5) == 0 && (*args)[5] != '\0') { const char *const *apps = t_strsplit(*args+5, ","); while (*apps != NULL) { char *app = i_strdup(*apps); array_append(&client->access_apps, &app, 1); if (client->debug) { i_debug("User %s has URLAUTH %s access", client->access_user, app); } apps++; } } else { i_error("Invalid ACCESS parameter: %s", str_sanitize(*args, 80)); client_abort(client, "Control session aborted: Invalid command"); return; } args++; } client->access_received = TRUE; if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) { client_destroy(client); return; } client->input = i_stream_create_fd(client->fd_in, MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_fd(client->fd_out, (size_t)-1, FALSE); client->io = io_add(client->fd_in, IO_READ, client_input, client); o_stream_set_flush_callback(client->output, client_output, client); if (client->debug) { i_debug("Worker activated for access by user %s", client->access_user); } }