1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "str.h"
5 #include "array.h"
6 #include "smtp-parser.h"
7 #include "smtp-address.h"
8 #include "smtp-reply.h"
9 #include "smtp-syntax.h"
10 
11 #include "smtp-server-private.h"
12 
13 /* RCPT command */
14 
15 struct smtp_server_cmd_rcpt {
16 	struct smtp_server_recipient *rcpt;
17 };
18 
19 static void
cmd_rcpt_destroy(struct smtp_server_cmd_ctx * cmd ATTR_UNUSED,struct smtp_server_cmd_rcpt * data)20 cmd_rcpt_destroy(struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
21 		 struct smtp_server_cmd_rcpt *data)
22 {
23 	smtp_server_recipient_destroy(&data->rcpt);
24 }
25 
26 static bool
cmd_rcpt_check_state(struct smtp_server_cmd_ctx * cmd,bool next_to_reply)27 cmd_rcpt_check_state(struct smtp_server_cmd_ctx *cmd, bool next_to_reply)
28 {
29 	struct smtp_server_connection *conn = cmd->conn;
30 	struct smtp_server_command *command = cmd->cmd;
31 	struct smtp_server_transaction *trans = conn->state.trans;
32 
33 	if (smtp_server_command_is_replied(command) &&
34 	    !smtp_server_command_replied_success(command) &&
35 	    !smtp_server_command_reply_is_forwarded(command))
36 		return FALSE;
37 
38 	if (trans == NULL &&
39 	    (conn->state.pending_mail_cmds == 0 || next_to_reply)) {
40 		smtp_server_reply(cmd,
41 			503, "5.5.0", "MAIL needed first");
42 		return FALSE;
43 	}
44 	if (conn->set.max_recipients > 0 && trans != NULL &&
45 		smtp_server_transaction_rcpt_count(trans) >=
46 			conn->set.max_recipients) {
47 		smtp_server_reply(cmd,
48 			451, "4.5.3", "Too many recipients");
49 		return FALSE;
50 	}
51 
52 	return TRUE;
53 }
54 
55 static void
cmd_rcpt_completed(struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_rcpt * data)56 cmd_rcpt_completed(struct smtp_server_cmd_ctx *cmd,
57 		   struct smtp_server_cmd_rcpt *data)
58 {
59 	struct smtp_server_connection *conn = cmd->conn;
60 	struct smtp_server_command *command = cmd->cmd;
61 	struct smtp_server_recipient *rcpt = data->rcpt;
62 
63 	i_assert(conn->state.pending_rcpt_cmds > 0);
64 	conn->state.pending_rcpt_cmds--;
65 
66 	i_assert(smtp_server_command_is_replied(command));
67 	i_assert(conn->state.state == SMTP_SERVER_STATE_RCPT_TO ||
68 		 !smtp_server_command_replied_success(command));
69 
70 	if (!smtp_server_command_replied_success(command)) {
71 		/* Failure */
72 		conn->state.denied_rcpt_cmds++;
73 		smtp_server_recipient_denied(
74 			rcpt, smtp_server_command_get_reply(cmd->cmd, 0));
75 		return;
76 	}
77 
78 	/* Success */
79 	data->rcpt = NULL; /* clear to prevent destruction */
80 	(void)smtp_server_recipient_approved(&rcpt);
81 }
82 
83 static void
cmd_rcpt_recheck(struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_rcpt * data ATTR_UNUSED)84 cmd_rcpt_recheck(struct smtp_server_cmd_ctx *cmd,
85 		 struct smtp_server_cmd_rcpt *data ATTR_UNUSED)
86 {
87 	struct smtp_server_connection *conn = cmd->conn;
88 
89 	/* All preceeding commands have finished and now the transaction state
90 	   is clear. This provides the opportunity to re-check the transaction
91 	   state and abort the pending proxied mail command if it is bound to
92 	   fail */
93 	if (!cmd_rcpt_check_state(cmd, TRUE))
94 		return;
95 
96 	/* Advance state */
97 	smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_RCPT_TO,
98 					 smtp_address_encode(data->rcpt->path));
99 }
100 
smtp_server_cmd_rcpt(struct smtp_server_cmd_ctx * cmd,const char * params)101 void smtp_server_cmd_rcpt(struct smtp_server_cmd_ctx *cmd,
102 			  const char *params)
103 {
104 	struct smtp_server_connection *conn = cmd->conn;
105 	const struct smtp_server_settings *set = &conn->set;
106 	enum smtp_capability caps = set->capabilities;
107 	const struct smtp_server_callbacks *callbacks = conn->callbacks;
108 	struct smtp_server_command *command = cmd->cmd;
109 	struct smtp_server_cmd_rcpt *rcpt_data;
110 	struct smtp_server_recipient *rcpt;
111 	enum smtp_address_parse_flags path_parse_flags;
112 	enum smtp_param_rcpt_parse_flags param_parse_flags;
113 	const char *const *param_extensions = NULL;
114 	struct smtp_address *path;
115 	struct smtp_params_rcpt rcpt_params;
116 	enum smtp_param_parse_error pperror;
117 	const char *error;
118 	int ret;
119 
120 	/* rcpt         = "RCPT TO:" ( "<Postmaster@" Domain ">" /
121 	      "<Postmaster>" / Forward-path ) [SP Rcpt-parameters] CRLF
122 	   Forward-path = Path
123 	 */
124 
125 	/* Check transaction state as far as possible */
126 	if (!cmd_rcpt_check_state(cmd, FALSE))
127 		return;
128 
129 	/* ( "<Postmaster@" Domain ">" / "<Postmaster>" / Forward-path ) */
130 	if (params == NULL || strncasecmp(params, "TO:", 3) != 0) {
131 		smtp_server_reply(cmd,
132 			501, "5.5.4", "Invalid parameters");
133 		return;
134 	}
135 	if (params[3] != ' ' && params[3] != '\t') {
136 		params += 3;
137 	} else if ((set->workarounds &
138 		    SMTP_SERVER_WORKAROUND_WHITESPACE_BEFORE_PATH) != 0) {
139 		params += 3;
140 		while (*params == ' ' || *params == '\t')
141 			params++;
142 	} else {
143 		smtp_server_reply(cmd, 501, "5.5.4",
144 				  "Invalid TO: "
145 				  "Unexpected whitespace before path");
146 		return;
147 	}
148 	path_parse_flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART;
149 	if ((set->workarounds & SMTP_SERVER_WORKAROUND_MAILBOX_FOR_PATH) != 0)
150 		path_parse_flags |= SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL;
151 	if (smtp_address_parse_path_full(pool_datastack_create(), params,
152 					 path_parse_flags, &path, &error,
153 					 &params) < 0) {
154 		smtp_server_reply(cmd,
155 			501, "5.5.4", "Invalid TO: %s", error);
156 		return;
157 	}
158 	if (*params == ' ')
159 		params++;
160 	else if (*params != '\0') {
161 		smtp_server_reply(cmd, 501, "5.5.4",
162 			"Invalid TO: Invalid character in path");
163 		return;
164 	}
165 	if (path->domain == NULL && !conn->set.rcpt_domain_optional &&
166 		strcasecmp(path->localpart, "postmaster") != 0) {
167 		smtp_server_reply(cmd,
168 			501, "5.5.4", "Invalid TO: Missing domain");
169 		return;
170 	}
171 
172 	/* [SP Rcpt-parameters] */
173 	param_parse_flags = 0;
174 	if (conn->set.rcpt_domain_optional)
175 		param_parse_flags |= SMTP_PARAM_RCPT_FLAG_ORCPT_ALLOW_LOCALPART;
176 	if (array_is_created(&conn->rcpt_param_extensions))
177 		param_extensions = array_front(&conn->rcpt_param_extensions);
178 	if (smtp_params_rcpt_parse(pool_datastack_create(), params,
179 				   param_parse_flags, caps, param_extensions,
180 				   &rcpt_params, &pperror, &error) < 0) {
181 		switch (pperror) {
182 		case SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX:
183 			smtp_server_reply(cmd,
184 				501, "5.5.4", "%s", error);
185 			break;
186 		case SMTP_PARAM_PARSE_ERROR_NOT_SUPPORTED:
187 			smtp_server_reply(cmd,
188 				555, "5.5.4", "%s", error);
189 			break;
190 		default:
191 			i_unreached();
192 		}
193 		return;
194 	}
195 
196 	rcpt = smtp_server_recipient_create(cmd, path, &rcpt_params);
197 
198 	rcpt_data = p_new(cmd->pool, struct smtp_server_cmd_rcpt, 1);
199 	rcpt_data->rcpt = rcpt;
200 	command->data = rcpt_data;
201 
202 	smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_NEXT,
203 				     cmd_rcpt_recheck, rcpt_data);
204 	smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_COMPLETED,
205 				     cmd_rcpt_completed, rcpt_data);
206 	smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_DESTROY,
207 				     cmd_rcpt_destroy, rcpt_data);
208 
209 	conn->state.pending_rcpt_cmds++;
210 
211 	smtp_server_command_ref(command);
212 	i_assert(callbacks != NULL && callbacks->conn_cmd_rcpt != NULL);
213 	ret = callbacks->conn_cmd_rcpt(conn->context, cmd, rcpt);
214 	if (ret <= 0) {
215 		i_assert(ret == 0 || smtp_server_command_is_replied(command));
216 		/* Command is waiting for external event or it failed */
217 		smtp_server_command_unref(&command);
218 		return;
219 	}
220 	if (!smtp_server_command_is_replied(command)) {
221 		/* Set generic RCPT success reply if none is provided */
222 		smtp_server_cmd_rcpt_reply_success(cmd);
223 	}
224 	smtp_server_command_unref(&command);
225 }
226 
smtp_server_command_is_rcpt(struct smtp_server_cmd_ctx * cmd)227 bool smtp_server_command_is_rcpt(struct smtp_server_cmd_ctx *cmd)
228 {
229 	return (cmd->cmd->reg->func == smtp_server_cmd_rcpt);
230 }
231 
smtp_server_cmd_rcpt_reply_success(struct smtp_server_cmd_ctx * cmd)232 void smtp_server_cmd_rcpt_reply_success(struct smtp_server_cmd_ctx *cmd)
233 {
234 	struct smtp_server_cmd_rcpt *rcpt_data = cmd->cmd->data;
235 
236 	i_assert(smtp_server_command_is_rcpt(cmd));
237 
238 	smtp_server_recipient_reply(rcpt_data->rcpt, 250, "2.1.5", "OK");
239 }
240