bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch * Command registry
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_register(struct smtp_server *server,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch const char *name, smtp_server_cmd_start_func_t *func,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_unregister(struct smtp_server *server,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch cmd = array_get(&server->commands_reg, &count);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch for (i = 0; i < count; i++) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch i_panic("smtp-server: Trying to unregister unknown command '%s'", name);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschsmtp_server_command_cmp(const struct smtp_server_command_reg *c1,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschsmtp_server_command_find(struct smtp_server *server, const char *name)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch array_sort(&server->commands_reg, smtp_server_command_cmp);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_commands_init(struct smtp_server *server)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch p_array_init(&server->commands_reg, server->pool, 16);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch SMTP_SERVER_CMD_FLAG_PRETLS | SMTP_SERVER_CMD_FLAG_PREAUTH);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch SMTP_SERVER_CMD_FLAG_PRETLS | SMTP_SERVER_CMD_FLAG_PREAUTH);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch SMTP_SERVER_CMD_FLAG_PRETLS | SMTP_SERVER_CMD_FLAG_PREAUTH);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_debug(struct smtp_server_cmd_ctx *cmd,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch const char *format, ...)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch const struct smtp_server_settings *set = &conn->set;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschsmtp_server_command_alloc(struct smtp_server_connection *conn)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch pool = pool_alloconly_create("smtp_server_command", 256);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch cmd = p_new(pool, struct smtp_server_command, 1);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschsmtp_server_command_new(struct smtp_server_connection *conn,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch const struct smtp_server_command_reg *cmd_reg;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch cmd->context.name = p_strdup(cmd->context.pool, name);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if ((cmd_reg=smtp_server_command_find(server, name)) == NULL) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* RFC 5321, Section 4.2.4: Reply Code 502
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch Questions have been raised as to when reply code 502 (Command
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch not implemented) SHOULD be returned in preference to other
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch codes. 502 SHOULD be used when the command is actually
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch recognized by the SMTP server, but not implemented. If the
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch command is not recognized, code 500 SHOULD be returned.
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch } else if (!conn->ssl_secured && conn->set.tls_required &&
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch (cmd_reg->flags & SMTP_SERVER_CMD_FLAG_PRETLS) == 0) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* RFC 3207, Section 4:
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch A SMTP server that is not publicly referenced may choose to
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch require that the client perform a TLS negotiation before
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch accepting any commands. In this case, the server SHOULD
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch return the reply code:
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch 530 Must issue a STARTTLS command first
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch to every command other than NOOP, EHLO, STARTTLS, or QUIT. If
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch the client and server are using the ENHANCEDSTATUSCODES ESMTP
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch extension [RFC2034], the status code to be returned SHOULD be
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch } else if (!conn->authenticated && !conn->set.auth_optional &&
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch (cmd_reg->flags & SMTP_SERVER_CMD_FLAG_PREAUTH) == 0) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* RFC 4954, Section 6: Status Codes
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch 530 5.7.0 Authentication required
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch This response SHOULD be returned by any command other than
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch AUTH, EHLO, HELO, NOOP, RSET, or QUIT when server policy
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch requires authentication in order to perform the requested
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch action and authentication is not currently in force.
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (tmp_cmd->state == SMTP_SERVER_COMMAND_STATE_NEW)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch tmp_cmd->state = SMTP_SERVER_COMMAND_STATE_PROCESSING;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_ref(struct smtp_server_command *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschbool smtp_server_command_unref(struct smtp_server_command **_cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->context.conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_command_debug(&cmd->context, "Destroy");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (cmd->state < SMTP_SERVER_COMMAND_STATE_FINISHED) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch cmd->state = SMTP_SERVER_COMMAND_STATE_ABORTED;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* execute hooks */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_abort(struct smtp_server_command **_cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->context.conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* preemptively remove command from queue (references may still exist)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (cmd->state < SMTP_SERVER_COMMAND_STATE_FINISHED) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch cmd->state = SMTP_SERVER_COMMAND_STATE_ABORTED;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_set_reply_count(struct smtp_server_command *cmd,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch unsigned int count)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_ready_to_reply(struct smtp_server_command *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch cmd->state = SMTP_SERVER_COMMAND_STATE_READY_TO_REPLY;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_command_debug(&cmd->context, "Ready to reply");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_connection_trigger_output(cmd->context.conn);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_next_to_reply(struct smtp_server_command *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (cmd->hook_next == NULL && cmd->context.hook_next == NULL)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_command_debug(&cmd->context, "Next to reply");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* execute private hook_next */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_cmd_func_t *hook_next = cmd->hook_next;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* execute public hook_next */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_cmd_func_t *hook_next = cmd->context.hook_next;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschsmtp_server_command_replied(struct smtp_server_command *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (cmd->hook_replied == NULL && cmd->context.hook_replied == NULL)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (cmd->replies_submitted == cmd->replies_expected) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_command_debug(&cmd->context, "Replied");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* execute private hook_replied */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* execute public hook_replied */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_completed(struct smtp_server_command *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (cmd->hook_completed == NULL && cmd->context.hook_completed == NULL)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (cmd->replies_submitted == cmd->replies_expected) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_command_debug(&cmd->context, "Completed");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* execute private hook_completed */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* execute public hook_completed */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_submit_reply(struct smtp_server_command *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->context.conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch i_assert(conn != NULL && array_is_created(&cmd->replies));
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch i_assert(submitted == cmd->replies_submitted);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* limit number of consecutive bad commands */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch else if (cmd->replies_submitted == cmd->replies_expected)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* submit reply */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "Not ready to reply");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch cmd->state = SMTP_SERVER_COMMAND_STATE_SUBMITTED_REPLY;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch case SMTP_SERVER_COMMAND_STATE_READY_TO_REPLY:
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (conn != NULL && conn->bad_counter > conn->set.max_bad_commands) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschbool smtp_server_command_is_replied(struct smtp_server_command *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch unsigned int i;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschsmtp_server_command_get_reply(struct smtp_server_command *cmd,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch unsigned int idx)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch reply = array_idx_modifiable(&cmd->replies, idx);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschbool smtp_server_command_reply_status_equals(struct smtp_server_command *cmd,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch unsigned int status)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch reply = smtp_server_command_get_reply(cmd, 0);
1d4c4128808d04cf4b8396ce04ce524da9194782Stephan Bosch return (reply->content != NULL && reply->content->status == status);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschbool smtp_server_command_replied_success(struct smtp_server_command *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch unsigned int i;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_finished(struct smtp_server_command *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->context.conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch i_assert(cmd->state < SMTP_SERVER_COMMAND_STATE_FINISHED);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch cmd->state = SMTP_SERVER_COMMAND_STATE_FINISHED;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch reply = array_idx_modifiable(&cmd->replies, 0);
1d4c4128808d04cf4b8396ce04ce524da9194782Stephan Bosch if (reply->content->status == 221 || reply->content->status == 421) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_connection_close(&conn, t_strdup_printf(
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "Server closed the connection: %s",
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "Client has quit the connection");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_fail(struct smtp_server_command *cmd,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch const char *fmt, ...)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch unsigned int i;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch } else for (i = 0; i < cmd->replies_expected; i++) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* send the same reply for all */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_input_lock(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
e69ac57b1a97e56011e988564985728be31bfb2aStephan Boschvoid smtp_server_command_input_unlock(struct smtp_server_cmd_ctx *cmd)
e69ac57b1a97e56011e988564985728be31bfb2aStephan Bosch struct smtp_server_command *command = cmd->cmd;
e69ac57b1a97e56011e988564985728be31bfb2aStephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_command_input_capture(struct smtp_server_cmd_ctx *cmd,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;