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-syntax.h"
7 
8 #include "smtp-server-private.h"
9 
10 /* EHLO, HELO commands */
11 
12 static void
cmd_helo_completed(struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_helo * data)13 cmd_helo_completed(struct smtp_server_cmd_ctx *cmd,
14 		   struct smtp_server_cmd_helo *data)
15 {
16 	struct smtp_server_connection *conn = cmd->conn;
17 	struct smtp_server_command *command = cmd->cmd;
18 
19 	i_assert(smtp_server_command_is_replied(command));
20 	if (!smtp_server_command_replied_success(command)) {
21 		/* Failure */
22 		return;
23 	}
24 
25 	if (conn->pending_helo == &data->helo)
26 		conn->pending_helo = NULL;
27 
28 	/* Success */
29 	smtp_server_connection_reset_state(conn);
30 
31 	i_free(conn->helo_domain);
32 	conn->helo_domain = i_strdup(data->helo.domain);
33 	conn->helo.domain = conn->helo_domain;
34 	conn->helo.domain_valid = data->helo.domain_valid;
35 	conn->helo.old_smtp = data->helo.old_smtp;
36 }
37 
38 static void
cmd_helo_next(struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_helo * data)39 cmd_helo_next(struct smtp_server_cmd_ctx *cmd,
40 	      struct smtp_server_cmd_helo *data)
41 {
42 	struct smtp_server_connection *conn = cmd->conn;
43 
44 	if (conn->helo.domain == NULL ||
45 	    strcmp(conn->helo.domain, data->helo.domain) != 0 ||
46 	    conn->helo.old_smtp != data->helo.old_smtp)
47 		data->changed = TRUE; /* Definitive assessment */
48 }
49 
50 static void
smtp_server_cmd_helo_run(struct smtp_server_cmd_ctx * cmd,const char * params,bool old_smtp)51 smtp_server_cmd_helo_run(struct smtp_server_cmd_ctx *cmd, const char *params,
52 			 bool old_smtp)
53 {
54 	struct smtp_server_connection *conn = cmd->conn;
55 	const struct smtp_server_callbacks *callbacks = conn->callbacks;
56 	struct smtp_server_cmd_helo *helo_data;
57 	struct smtp_server_command *command = cmd->cmd;
58 	bool first = (conn->pending_helo == NULL && conn->helo.domain == NULL);
59 	const char *domain = NULL;
60 	int ret;
61 
62 	/* Parse domain argument */
63 
64 	if (*params == '\0') {
65 		smtp_server_reply(cmd, 501, "", "Missing hostname");
66 		return;
67 	}
68 	ret = smtp_helo_domain_parse(params, !old_smtp, &domain);
69 
70 	smtp_server_command_input_lock(cmd);
71 	if (conn->state.state == SMTP_SERVER_STATE_GREETING) {
72 		smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_HELO,
73 						 NULL);
74 	}
75 
76 	helo_data = p_new(cmd->pool, struct smtp_server_cmd_helo, 1);
77 	helo_data->helo.domain = p_strdup(cmd->pool, domain);
78 	helo_data->helo.domain_valid = ( ret >= 0 );
79 	helo_data->helo.old_smtp = old_smtp;
80 	helo_data->first = first;
81 	command->data = helo_data;
82 
83 	if (conn->helo.domain == NULL ||
84 	    (domain != NULL && strcmp(conn->helo.domain, domain) != 0) ||
85 	    conn->helo.old_smtp != old_smtp)
86 		helo_data->changed = TRUE; /* Preliminary assessment */
87 
88 	if (conn->pending_helo == NULL)
89 		conn->pending_helo = &helo_data->helo;
90 
91 	smtp_server_command_add_hook(
92 		command, SMTP_SERVER_COMMAND_HOOK_NEXT,
93 		cmd_helo_next, helo_data);
94 	smtp_server_command_add_hook(
95 		command, SMTP_SERVER_COMMAND_HOOK_COMPLETED,
96 		cmd_helo_completed, helo_data);
97 
98 	smtp_server_command_ref(command);
99 	if (callbacks != NULL && callbacks->conn_cmd_helo != NULL) {
100 		/* Specific implementation of EHLO command */
101 		ret = callbacks->conn_cmd_helo(conn->context, cmd, helo_data);
102 		if (ret <= 0) {
103 			i_assert(ret == 0 ||
104 				 smtp_server_command_is_replied(command));
105 			/* Command is waiting for external event or it failed */
106 			smtp_server_command_unref(&command);
107 			return;
108 		}
109 	}
110 
111 	if (!smtp_server_command_is_replied(command)) {
112 		/* Submit default EHLO reply if none is provided */
113 		smtp_server_cmd_ehlo_reply_default(cmd);
114 	}
115 	smtp_server_command_unref(&command);
116 }
117 
smtp_server_cmd_ehlo(struct smtp_server_cmd_ctx * cmd,const char * params)118 void smtp_server_cmd_ehlo(struct smtp_server_cmd_ctx *cmd, const char *params)
119 {
120 	/* ehlo = "EHLO" SP ( Domain / address-literal ) CRLF */
121 
122 	smtp_server_cmd_helo_run(cmd, params, FALSE);
123 }
124 
smtp_server_cmd_helo(struct smtp_server_cmd_ctx * cmd,const char * params)125 void smtp_server_cmd_helo(struct smtp_server_cmd_ctx *cmd, const char *params)
126 {
127 	/* helo = "HELO" SP Domain CRLF */
128 
129 	smtp_server_cmd_helo_run(cmd, params, TRUE);
130 }
131 
132 struct smtp_server_reply *
smtp_server_cmd_ehlo_reply_create(struct smtp_server_cmd_ctx * cmd)133 smtp_server_cmd_ehlo_reply_create(struct smtp_server_cmd_ctx *cmd)
134 {
135 	static struct {
136 		const char *name;
137 		void (*add)(struct smtp_server_reply *reply);
138 	} standard_caps[] = {
139 		/* Sorted alphabetically */
140 		{ "8BITMIME", smtp_server_reply_ehlo_add_8bitmime },
141 		{ "BINARYMIME", smtp_server_reply_ehlo_add_binarymime },
142 		{ "CHUNKING", smtp_server_reply_ehlo_add_chunking },
143 		{ "DSN", smtp_server_reply_ehlo_add_dsn },
144 		{ "ENHANCEDSTATUSCODES",
145 		  smtp_server_reply_ehlo_add_enhancedstatuscodes },
146 		{ "PIPELINING", smtp_server_reply_ehlo_add_pipelining },
147 		{ "SIZE", smtp_server_reply_ehlo_add_size },
148 		{ "STARTTLS", smtp_server_reply_ehlo_add_starttls },
149 		{ "VRFY", smtp_server_reply_ehlo_add_vrfy },
150 		{ "XCLIENT", smtp_server_reply_ehlo_add_xclient }
151 	};
152 	const unsigned int standard_caps_count = N_ELEMENTS(standard_caps);
153 	struct smtp_server_connection *conn = cmd->conn;
154 	struct smtp_server_command *command = cmd->cmd;
155 	struct smtp_server_cmd_helo *helo_data = command->data;
156 	const struct smtp_capability_extra *extra_caps = NULL;
157 	unsigned int extra_caps_count, i, j;
158 	struct smtp_server_reply *reply;
159 
160 	reply = smtp_server_reply_create_ehlo(cmd->cmd);
161 
162 	if (helo_data->helo.old_smtp) {
163 		i_assert(cmd->cmd->reg->func == smtp_server_cmd_helo);
164 		return reply;
165 	}
166 	i_assert(cmd->cmd->reg->func == smtp_server_cmd_ehlo);
167 
168 	extra_caps_count = 0;
169 	if (array_is_created(&conn->extra_capabilities)) {
170 		extra_caps = array_get(&conn->extra_capabilities,
171 				       &extra_caps_count);
172 	}
173 
174 	i = j = 0;
175 	while (i < standard_caps_count || j < extra_caps_count) {
176 		if (i < standard_caps_count &&
177 		    (j >= extra_caps_count ||
178 		     strcasecmp(standard_caps[i].name,
179 				extra_caps[j].name) < 0)) {
180 			standard_caps[i].add(reply);
181 			i++;
182 		} else {
183 			smtp_server_reply_ehlo_add_params(
184 				reply, extra_caps[j].name,
185 				extra_caps[j].params);
186 			j++;
187 		}
188 	}
189 	return reply;
190 }
191 
smtp_server_cmd_ehlo_reply_default(struct smtp_server_cmd_ctx * cmd)192 void smtp_server_cmd_ehlo_reply_default(struct smtp_server_cmd_ctx *cmd)
193 {
194 	struct smtp_server_reply *reply;
195 
196 	reply = smtp_server_cmd_ehlo_reply_create(cmd);
197 	smtp_server_reply_submit(reply);
198 }
199