1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "login-common.h"
4 #include "base64.h"
5 #include "buffer.h"
6 #include "ioloop.h"
7 #include "istream.h"
8 #include "ostream.h"
9 #include "randgen.h"
10 #include "hostpid.h"
11 #include "safe-memset.h"
12 #include "str.h"
13 #include "strescape.h"
14 #include "master-service.h"
15 #include "client.h"
16 #include "client-authenticate.h"
17 #include "auth-client.h"
18 #include "pop3-proxy.h"
19 #include "pop3-login-settings.h"
20 
21 #include <ctype.h>
22 
23 /* Disconnect client when it sends too many bad commands */
24 #define CLIENT_MAX_BAD_COMMANDS 3
25 #define CLIENT_MAX_CMD_LEN 8
26 
cmd_stls(struct pop3_client * client)27 static bool cmd_stls(struct pop3_client *client)
28 {
29 	client_cmd_starttls(&client->common);
30 	return TRUE;
31 }
32 
cmd_quit(struct pop3_client * client)33 static bool cmd_quit(struct pop3_client *client)
34 {
35 	client_send_reply(&client->common, POP3_CMD_REPLY_OK, "Logging out");
36 	client_destroy(&client->common, CLIENT_UNAUTHENTICATED_LOGOUT_MSG);
37 	return TRUE;
38 }
39 
cmd_xclient(struct pop3_client * client,const char * args)40 static bool cmd_xclient(struct pop3_client *client, const char *args)
41 {
42 	const char *const *tmp;
43 	in_port_t remote_port;
44 	bool args_ok = TRUE;
45 
46 	if (!client->common.trusted) {
47 		client_send_reply(&client->common, POP3_CMD_REPLY_OK,
48 				  "You are not from trusted IP - ignoring");
49 		return TRUE;
50 	}
51 	for (tmp = t_strsplit(args, " "); *tmp != NULL; tmp++) {
52 		if (strncasecmp(*tmp, "ADDR=", 5) == 0) {
53 			if (net_addr2ip(*tmp + 5, &client->common.ip) < 0)
54 				args_ok = FALSE;
55 		} else if (strncasecmp(*tmp, "PORT=", 5) == 0) {
56 			if (net_str2port(*tmp + 5, &remote_port) < 0)
57 				args_ok = FALSE;
58 			else
59 				client->common.remote_port = remote_port;
60 		} else if (strncasecmp(*tmp, "SESSION=", 8) == 0) {
61 			const char *value = *tmp + 8;
62 
63 			if (strlen(value) <= LOGIN_MAX_SESSION_ID_LEN) {
64 				client->common.session_id =
65 					p_strdup(client->common.pool, value);
66 			}
67 		} else if (strncasecmp(*tmp, "TTL=", 4) == 0) {
68 			if (str_to_uint(*tmp + 4, &client->common.proxy_ttl) < 0)
69 				args_ok = FALSE;
70 		} else if (strncasecmp(*tmp, "FORWARD=", 8) == 0) {
71 			size_t value_len = strlen((*tmp)+8);
72 			client->common.forward_fields =
73 				str_new(client->common.preproxy_pool,
74 					MAX_BASE64_DECODED_SIZE(value_len));
75 			if (base64_decode((*tmp)+8, value_len, NULL,
76 					  client->common.forward_fields) < 0)
77 				args_ok = FALSE;
78 		}
79 	}
80 	if (!args_ok) {
81 		client_send_reply(&client->common, POP3_CMD_REPLY_ERROR,
82 				  "Invalid parameters");
83 		return TRUE;
84 	}
85 
86 	/* args ok, set them and reset the state */
87 	client_send_reply(&client->common, POP3_CMD_REPLY_OK, "Updated");
88 	return TRUE;
89 }
90 
client_command_execute(struct pop3_client * client,const char * cmd,const char * args)91 static bool client_command_execute(struct pop3_client *client, const char *cmd,
92 				   const char *args)
93 {
94 	if (strcmp(cmd, "CAPA") == 0)
95 		return cmd_capa(client, args);
96 	if (strcmp(cmd, "USER") == 0)
97 		return cmd_user(client, args);
98 	if (strcmp(cmd, "PASS") == 0)
99 		return cmd_pass(client, args);
100 	if (strcmp(cmd, "APOP") == 0)
101 		return cmd_apop(client, args);
102 	if (strcmp(cmd, "STLS") == 0)
103 		return cmd_stls(client);
104 	if (strcmp(cmd, "QUIT") == 0)
105 		return cmd_quit(client);
106 	if (strcmp(cmd, "XCLIENT") == 0)
107 		return cmd_xclient(client, args);
108 	if (strcmp(cmd, "XOIP") == 0) {
109 		/* Compatibility with Zimbra's patched nginx */
110 		return cmd_xclient(client, t_strconcat("ADDR=", args, NULL));
111 	}
112 
113 	client_send_reply(&client->common, POP3_CMD_REPLY_ERROR,
114 			  "Unknown command.");
115 	return FALSE;
116 }
117 
pop3_client_input(struct client * client)118 static void pop3_client_input(struct client *client)
119 {
120 	i_assert(!client->authenticating);
121 
122 	if (!client_read(client))
123 		return;
124 
125 	client_ref(client);
126 
127 	o_stream_cork(client->output);
128 	/* if a command starts an authentication, stop processing further
129 	   commands until the authentication is finished. */
130 	while (!client->output->closed && !client->authenticating &&
131 	       auth_client_is_connected(auth_client)) {
132 		if (!client->v.input_next_cmd(client))
133 			break;
134 	}
135 
136 	if (auth_client != NULL && !auth_client_is_connected(auth_client))
137 		client->input_blocked = TRUE;
138 
139 	o_stream_uncork(client->output);
140 	client_unref(&client);
141 }
142 
client_read_cmd_name(struct client * client,const char ** cmd_r)143 static bool client_read_cmd_name(struct client *client, const char **cmd_r)
144 {
145 	const unsigned char *data;
146 	size_t size, i;
147 	string_t *cmd = t_str_new(CLIENT_MAX_CMD_LEN);
148 	if (i_stream_read_more(client->input, &data, &size) <= 0)
149 		return FALSE;
150 	for(i = 0; i < size; i++) {
151 		if (data[i] == '\r') continue;
152 		if (data[i] == ' ' ||
153 		    data[i] == '\n' ||
154 		    data[i] == '\0' ||
155 		    i >= CLIENT_MAX_CMD_LEN) {
156 			*cmd_r = str_c(cmd);
157 			/* only skip ws */
158 			i_stream_skip(client->input, i + (data[i] == ' ' ? 1 : 0));
159 			return TRUE;
160 		}
161 		str_append_c(cmd, i_toupper(data[i]));
162 	}
163 	return FALSE;
164 }
165 
pop3_client_input_next_cmd(struct client * client)166 static bool pop3_client_input_next_cmd(struct client *client)
167 {
168 	struct pop3_client *pop3_client = (struct pop3_client *)client;
169 	const char *cmd, *args;
170 
171 	if (pop3_client->current_cmd == NULL) {
172 		if (!client_read_cmd_name(client, &cmd))
173 			return FALSE;
174 		pop3_client->current_cmd = i_strdup(cmd);
175 	}
176 
177 	if (strcmp(pop3_client->current_cmd, "AUTH") == 0) {
178 		if (cmd_auth(pop3_client) <= 0) {
179 			/* Need more input / destroyed. We also get here when
180 			   SASL authentication is actually started. */
181 			return FALSE;
182 		}
183 		/* AUTH command finished already (SASL probe or ERR reply) */
184 		i_free(pop3_client->current_cmd);
185 		return TRUE;
186 	}
187 
188 	if ((args = i_stream_next_line(client->input)) == NULL)
189 		return FALSE;
190 
191 	if (client_command_execute(pop3_client, pop3_client->current_cmd, args))
192 		client->bad_counter = 0;
193 	else if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
194 		client_send_reply(client, POP3_CMD_REPLY_ERROR,
195 				  "Too many invalid bad commands.");
196 		client_destroy(client,
197 			       "Disconnected: Too many bad commands");
198 		return FALSE;
199 	}
200 	i_free(pop3_client->current_cmd);
201 	return TRUE;
202 }
203 
pop3_client_alloc(pool_t pool)204 static struct client *pop3_client_alloc(pool_t pool)
205 {
206 	struct pop3_client *pop3_client;
207 
208 	pop3_client = p_new(pool, struct pop3_client, 1);
209 	return &pop3_client->common;
210 }
211 
pop3_client_create(struct client * client ATTR_UNUSED,void ** other_sets ATTR_UNUSED)212 static void pop3_client_create(struct client *client ATTR_UNUSED,
213 			       void **other_sets ATTR_UNUSED)
214 {
215 }
216 
pop3_client_destroy(struct client * client)217 static void pop3_client_destroy(struct client *client)
218 {
219 	struct pop3_client *pop3_client = (struct pop3_client *)client;
220 
221 	i_free_and_null(pop3_client->current_cmd);
222 	i_free_and_null(pop3_client->last_user);
223 	i_free_and_null(pop3_client->apop_challenge);
224 }
225 
get_apop_challenge(struct pop3_client * client)226 static char *get_apop_challenge(struct pop3_client *client)
227 {
228 	unsigned char buffer[16];
229 	unsigned char buffer_base64[MAX_BASE64_ENCODED_SIZE(sizeof(buffer)) + 1];
230 	buffer_t buf;
231 
232 	if (sasl_server_find_available_mech(&client->common, "APOP") == NULL) {
233 		/* disabled, no need to present the challenge */
234 		return NULL;
235 	}
236 
237 	auth_client_get_connect_id(auth_client, &client->apop_server_pid,
238 				   &client->apop_connect_uid);
239 
240 	random_fill(buffer, sizeof(buffer));
241 	buffer_create_from_data(&buf, buffer_base64, sizeof(buffer_base64));
242 	base64_encode(buffer, sizeof(buffer), &buf);
243 	buffer_append_c(&buf, '\0');
244 
245 	return i_strdup_printf("<%x.%x.%lx.%s@%s>",
246 			       client->apop_server_pid,
247 			       client->apop_connect_uid,
248 			       (unsigned long)ioloop_time,
249 			       (const char *)buf.data, my_hostname);
250 }
251 
pop3_client_notify_auth_ready(struct client * client)252 static void pop3_client_notify_auth_ready(struct client *client)
253 {
254 	struct pop3_client *pop3_client = (struct pop3_client *)client;
255 	string_t *str;
256 
257 	client->io = io_add_istream(client->input, client_input, client);
258 
259 	str = t_str_new(128);
260 	if (client->trusted) {
261 		/* Dovecot extension to avoid extra roundtrip for CAPA */
262 		str_append(str, "[XCLIENT] ");
263 	}
264 	str_append(str, client->set->login_greeting);
265 
266 	pop3_client->apop_challenge = get_apop_challenge(pop3_client);
267 	if (pop3_client->apop_challenge != NULL)
268 		str_printfa(str, " %s", pop3_client->apop_challenge);
269 	client_send_reply(client, POP3_CMD_REPLY_OK, str_c(str));
270 
271 	client->banner_sent = TRUE;
272 }
273 
274 static void
pop3_client_notify_starttls(struct client * client,bool success,const char * text)275 pop3_client_notify_starttls(struct client *client,
276 			    bool success, const char *text)
277 {
278 	if (success)
279 		client_send_reply(client, POP3_CMD_REPLY_OK, text);
280 	else
281 		client_send_reply(client, POP3_CMD_REPLY_ERROR, text);
282 }
283 
pop3_client_starttls(struct client * client ATTR_UNUSED)284 static void pop3_client_starttls(struct client *client ATTR_UNUSED)
285 {
286 }
287 
client_send_reply(struct client * client,enum pop3_cmd_reply reply,const char * text)288 void client_send_reply(struct client *client, enum pop3_cmd_reply reply,
289 		       const char *text)
290 {
291 	const char *prefix = "-ERR";
292 
293 	switch (reply) {
294 	case POP3_CMD_REPLY_OK:
295 		prefix = "+OK";
296 		break;
297 	case POP3_CMD_REPLY_TEMPFAIL:
298 		prefix = "-ERR [SYS/TEMP]";
299 		break;
300 	case POP3_CMD_REPLY_AUTH_ERROR:
301 		if (text[0] == '[')
302 			prefix = "-ERR";
303 		else
304 			prefix = "-ERR [AUTH]";
305 		break;
306 	case POP3_CMD_REPLY_ERROR:
307 		break;
308 	}
309 
310 	T_BEGIN {
311 		string_t *line = t_str_new(256);
312 
313 		str_append(line, prefix);
314 		str_append_c(line, ' ');
315 		str_append(line, text);
316 		str_append(line, "\r\n");
317 
318 		client_send_raw_data(client, str_data(line), str_len(line));
319 	} T_END;
320 }
321 
322 static void
pop3_client_notify_disconnect(struct client * client,enum client_disconnect_reason reason,const char * text)323 pop3_client_notify_disconnect(struct client *client,
324 			      enum client_disconnect_reason reason,
325 			      const char *text)
326 {
327 	if (reason == CLIENT_DISCONNECT_INTERNAL_ERROR)
328 		client_send_reply(client, POP3_CMD_REPLY_TEMPFAIL, text);
329 	else
330 		client_send_reply(client, POP3_CMD_REPLY_ERROR, text);
331 }
332 
pop3_login_die(void)333 static void pop3_login_die(void)
334 {
335 	/* do nothing. pop3 connections typically die pretty quick anyway. */
336 }
337 
pop3_login_preinit(void)338 static void pop3_login_preinit(void)
339 {
340 	login_set_roots = pop3_login_setting_roots;
341 }
342 
pop3_login_init(void)343 static void pop3_login_init(void)
344 {
345 	/* override the default login_die() */
346 	master_service_set_die_callback(master_service, pop3_login_die);
347 }
348 
pop3_login_deinit(void)349 static void pop3_login_deinit(void)
350 {
351 	clients_destroy_all();
352 }
353 
354 static struct client_vfuncs pop3_client_vfuncs = {
355 	.alloc = pop3_client_alloc,
356 	.create = pop3_client_create,
357 	.destroy = pop3_client_destroy,
358 	.notify_auth_ready = pop3_client_notify_auth_ready,
359 	.notify_disconnect = pop3_client_notify_disconnect,
360 	.notify_starttls = pop3_client_notify_starttls,
361 	.starttls = pop3_client_starttls,
362 	.input = pop3_client_input,
363 	.auth_result = pop3_client_auth_result,
364 	.proxy_reset = pop3_proxy_reset,
365 	.proxy_parse_line = pop3_proxy_parse_line,
366 	.proxy_failed = pop3_proxy_failed,
367 	.proxy_get_state = pop3_proxy_get_state,
368 	.send_raw_data = client_common_send_raw_data,
369 	.input_next_cmd  = pop3_client_input_next_cmd,
370 	.free = client_common_default_free,
371 };
372 
373 static struct login_binary pop3_login_binary = {
374 	.protocol = "pop3",
375 	.process_name = "pop3-login",
376 	.default_port = 110,
377 	.default_ssl_port = 995,
378 
379 	.event_category = {
380 		.name = "pop3",
381 	},
382 
383 	.client_vfuncs = &pop3_client_vfuncs,
384 	.preinit = pop3_login_preinit,
385 	.init = pop3_login_init,
386 	.deinit = pop3_login_deinit,
387 
388 	.sasl_support_final_reply = FALSE,
389 	.anonymous_login_acceptable = TRUE,
390 };
391 
main(int argc,char * argv[])392 int main(int argc, char *argv[])
393 {
394 	return login_binary_run(&pop3_login_binary, argc, argv);
395 }
396