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