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 "hex-binary.h"
7 #include "ioloop.h"
8 #include "istream.h"
9 #include "ostream.h"
10 #include "safe-memset.h"
11 #include "str.h"
12 #include "str-sanitize.h"
13 #include "auth-client.h"
14 #include "../pop3/pop3-capability.h"
15 #include "client.h"
16 #include "client-authenticate.h"
17 #include "pop3-proxy.h"
18 
19 
20 static const char *capability_string = POP3_CAPABILITY_REPLY;
21 
cmd_capa(struct pop3_client * client,const char * args ATTR_UNUSED)22 bool cmd_capa(struct pop3_client *client, const char *args ATTR_UNUSED)
23 {
24 	const struct auth_mech_desc *mech;
25 	unsigned int i, count;
26 	string_t *str;
27 
28 	str = t_str_new(128);
29 	str_append(str, "+OK\r\n");
30 	str_append(str, capability_string);
31 
32 	if (client_is_tls_enabled(&client->common) && !client->common.tls)
33 		str_append(str, "STLS\r\n");
34 	if (!client->common.set->disable_plaintext_auth ||
35 	    client->common.secured)
36 		str_append(str, "USER\r\n");
37 
38 	str_append(str, "SASL");
39 	mech = sasl_server_get_advertised_mechs(&client->common, &count);
40 	for (i = 0; i < count; i++) {
41 		str_append_c(str, ' ');
42 		str_append(str, mech[i].name);
43 	}
44 	str_append(str, "\r\n.\r\n");
45 
46 	client_send_raw(&client->common, str_c(str));
47 	return TRUE;
48 }
49 
pop3_client_auth_result(struct client * client,enum client_auth_result result,const struct client_auth_reply * reply ATTR_UNUSED,const char * text)50 void pop3_client_auth_result(struct client *client,
51 			     enum client_auth_result result,
52 			     const struct client_auth_reply *reply ATTR_UNUSED,
53 			     const char *text)
54 {
55 	switch (result) {
56 	case CLIENT_AUTH_RESULT_SUCCESS:
57 		/* nothing to be done for POP3 */
58 		break;
59 	case CLIENT_AUTH_RESULT_TEMPFAIL:
60 		client_send_reply(client, POP3_CMD_REPLY_TEMPFAIL, text);
61 		break;
62 	case CLIENT_AUTH_RESULT_AUTHFAILED:
63 	case CLIENT_AUTH_RESULT_AUTHFAILED_REASON:
64 	case CLIENT_AUTH_RESULT_AUTHZFAILED:
65 	case CLIENT_AUTH_RESULT_PASS_EXPIRED:
66 	case CLIENT_AUTH_RESULT_SSL_REQUIRED:
67 	case CLIENT_AUTH_RESULT_LOGIN_DISABLED:
68 	case CLIENT_AUTH_RESULT_MECH_INVALID:
69 	case CLIENT_AUTH_RESULT_MECH_SSL_REQUIRED:
70 	case CLIENT_AUTH_RESULT_INVALID_BASE64:
71 		client_send_reply(client, POP3_CMD_REPLY_AUTH_ERROR, text);
72 		break;
73 	default:
74 		client_send_reply(client, POP3_CMD_REPLY_ERROR, text);
75 		break;
76 	}
77 }
78 
cmd_auth(struct pop3_client * pop3_client)79 int cmd_auth(struct pop3_client *pop3_client)
80 {
81 	/* NOTE: This command's input is handled specially because the
82 	   SASL-IR can be large. */
83 	struct client *client = &pop3_client->common;
84 	const unsigned char *data;
85 	size_t i, size;
86 	int ret;
87 
88 	/* <auth mechanism name> [<initial SASL response>] */
89 	if (!pop3_client->auth_mech_name_parsed) {
90 		data = i_stream_get_data(client->input, &size);
91 		for (i = 0; i < size; i++) {
92 			if (data[i] == ' ' ||
93 			    data[i] == '\r' || data[i] == '\n')
94 				break;
95 		}
96 		if (i == size)
97 			return 0;
98 		if (i == 0) {
99 			/* Old-style SASL discovery, used by MS Outlook */
100 			unsigned int i, count;
101 			const struct auth_mech_desc *mech;
102 
103 			client_send_raw(client, "+OK\r\n");
104 			mech = sasl_server_get_advertised_mechs(client, &count);
105 			for (i = 0; i < count; i++) {
106 				client_send_raw(client, mech[i].name);
107 				client_send_raw(client, "\r\n");
108 			}
109 			client_send_raw(client, ".\r\n");
110 			(void)i_stream_read_next_line(client->input);
111 			return 1;
112 		}
113 		i_free(client->auth_mech_name);
114 		client->auth_mech_name = i_strndup(data, i);
115 		pop3_client->auth_mech_name_parsed = TRUE;
116 		if (data[i] == ' ')
117 			i++;
118 		i_stream_skip(client->input, i);
119 	}
120 
121 	/* get SASL-IR, if any */
122 	if ((ret = client_auth_read_line(client)) <= 0)
123 		return ret;
124 
125 	const char *ir = NULL;
126 	if (client->auth_response->used > 0)
127 		ir = t_strdup(str_c(client->auth_response));
128 
129 	pop3_client->auth_mech_name_parsed = FALSE;
130 	/* The whole AUTH line command is parsed now. The rest of the SASL
131 	   protocol exchange happens in login-common code. We can free the
132 	   current command here already, because no pop3-login code is called
133 	   until the authentication is finished. Also, there's currently no
134 	   single location that is called in pop3-login code after the
135 	   authentication is finished. For example it could be an auth failure
136 	   or it could be a successful authentication with a proxying
137 	   failure. */
138 	i_free(pop3_client->current_cmd);
139 	return client_auth_begin(client, t_strdup(client->auth_mech_name), ir);
140 }
141 
cmd_user(struct pop3_client * pop3_client,const char * args)142 bool cmd_user(struct pop3_client *pop3_client, const char *args)
143 {
144 	if (!client_check_plaintext_auth(&pop3_client->common, FALSE)) {
145 		if (pop3_client->common.virtual_user == NULL)
146 			pop3_client->common.virtual_user = i_strdup(args);
147 		return TRUE;
148 	}
149 
150 	i_free(pop3_client->last_user);
151 	pop3_client->last_user = i_strdup(args);
152 
153 	client_send_raw(&pop3_client->common, "+OK\r\n");
154 	return TRUE;
155 }
156 
cmd_pass(struct pop3_client * pop3_client,const char * args)157 bool cmd_pass(struct pop3_client *pop3_client, const char *args)
158 {
159 	struct client *client = &pop3_client->common;
160 	string_t *plain_login, *base64;
161 
162 	if (pop3_client->last_user == NULL) {
163 		/* client may ignore the USER reply and only display the error
164 		   message from PASS */
165 		if (!client_check_plaintext_auth(client, TRUE))
166 			return TRUE;
167 
168 		client_send_reply(client, POP3_CMD_REPLY_ERROR,
169 				  "No username given.");
170 		return TRUE;
171 	}
172 
173 	/* authorization ID \0 authentication ID \0 pass */
174 	plain_login = t_str_new(128);
175 	str_append_c(plain_login, '\0');
176 	str_append(plain_login, pop3_client->last_user);
177 	str_append_c(plain_login, '\0');
178 	str_append(plain_login, args);
179 
180 	i_free_and_null(pop3_client->last_user);
181 
182 	base64 = t_buffer_create(MAX_BASE64_ENCODED_SIZE(plain_login->used));
183 	base64_encode(plain_login->data, plain_login->used, base64);
184 
185 	(void)client_auth_begin(client, "PLAIN", str_c(base64));
186 	return TRUE;
187 }
188 
cmd_apop(struct pop3_client * pop3_client,const char * args)189 bool cmd_apop(struct pop3_client *pop3_client, const char *args)
190 {
191 	struct client *client = &pop3_client->common;
192 	buffer_t *apop_data, *base64;
193 	const char *p;
194 	unsigned int server_pid, connect_uid;
195 
196 	if (pop3_client->apop_challenge == NULL) {
197 		if (client->set->auth_verbose)
198 			e_info(client->event, "APOP failed: APOP not enabled");
199 		client_send_reply(client, POP3_CMD_REPLY_ERROR,
200 				  "APOP not enabled.");
201 		return TRUE;
202 	}
203 
204 	/* <username> <md5 sum in hex> */
205 	p = strchr(args, ' ');
206 	if (p == NULL || strlen(p+1) != 32) {
207 		if (client->set->auth_verbose)
208 			e_info(client->event, "APOP failed: Invalid parameters");
209 		client_send_reply(client, POP3_CMD_REPLY_ERROR,
210 				  "Invalid parameters.");
211 		return TRUE;
212 	}
213 
214 	/* APOP challenge \0 username \0 APOP response */
215 	apop_data = t_buffer_create(128);
216 	buffer_append(apop_data, pop3_client->apop_challenge,
217 		      strlen(pop3_client->apop_challenge)+1);
218 	buffer_append(apop_data, args, (size_t)(p-args));
219 	buffer_append_c(apop_data, '\0');
220 
221 	if (hex_to_binary(p+1, apop_data) < 0) {
222 		if (client->set->auth_verbose) {
223 			e_info(client->event, "APOP failed: "
224 			       "Invalid characters in MD5 response");
225 		}
226 		client_send_reply(client, POP3_CMD_REPLY_ERROR,
227 				  "Invalid characters in MD5 response.");
228 		return TRUE;
229 	}
230 
231 	base64 = t_buffer_create(MAX_BASE64_ENCODED_SIZE(apop_data->used));
232 	base64_encode(apop_data->data, apop_data->used, base64);
233 
234 	auth_client_get_connect_id(auth_client, &server_pid, &connect_uid);
235 	if (pop3_client->apop_server_pid != server_pid ||
236 	    pop3_client->apop_connect_uid != connect_uid) {
237 		/* we reconnected to auth server and can't authenticate
238 		   with APOP in this session anymore. disconnecting the user
239 		   is probably the best solution now. */
240 		client_destroy(client,
241 			"Reconnected to auth server, can't do APOP");
242 		return TRUE;
243 	}
244 
245 	(void)client_auth_begin_private(client, "APOP", str_c(base64));
246 	return TRUE;
247 }
248