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