1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3 #include "auth-common.h"
4 #include "ioloop.h"
5 #include "istream.h"
6 #include "ostream.h"
7 #include "net.h"
8 #include "hex-binary.h"
9 #include "hostpid.h"
10 #include "llist.h"
11 #include "str.h"
12 #include "str-sanitize.h"
13 #include "randgen.h"
14 #include "safe-memset.h"
15 #include "master-service.h"
16 #include "mech.h"
17 #include "auth-fields.h"
18 #include "auth-request-handler.h"
19 #include "auth-client-interface.h"
20 #include "auth-client-connection.h"
21 #include "auth-master-connection.h"
22
23
24 #define OUTBUF_THROTTLE_SIZE (1024*50)
25
26 #define AUTH_DEBUG_SENSITIVE_SUFFIX \
27 " (previous base64 data may contain sensitive data)"
28
29 static void auth_client_disconnected(struct auth_client_connection **_conn);
30 static void auth_client_connection_unref(struct auth_client_connection **_conn);
31 static void auth_client_input(struct auth_client_connection *conn);
32
33 static struct auth_client_connection *auth_client_connections;
34
reply_line_hide_pass(const char * line)35 static const char *reply_line_hide_pass(const char *line)
36 {
37 string_t *newline;
38 const char *p, *p2;
39
40 if (strstr(line, "pass") == NULL)
41 return line;
42
43 newline = t_str_new(strlen(line));
44
45 const char *const *fields = t_strsplit(line, "\t");
46
47 while(*fields != NULL) {
48 p = strstr(*fields, "pass");
49 p2 = strchr(*fields, '=');
50 if (p == NULL || p2 == NULL || p2 < p) {
51 str_append(newline, *fields);
52 } else {
53 /* include = */
54 str_append_data(newline, *fields, (p2 - *fields)+1);
55 str_append(newline, PASSWORD_HIDDEN_STR);
56 }
57 str_append_c(newline, '\t');
58 fields++;
59 }
60
61 return str_c(newline);
62 }
63
auth_client_send(struct auth_client_connection * conn,const char * cmd)64 static void auth_client_send(struct auth_client_connection *conn,
65 const char *cmd)
66 {
67 struct const_iovec iov[2];
68
69 iov[0].iov_base = cmd;
70 iov[0].iov_len = strlen(cmd);
71 iov[1].iov_base = "\n";
72 iov[1].iov_len = 1;
73 o_stream_nsendv(conn->output, iov, 2);
74
75 if (o_stream_get_buffer_used_size(conn->output) >=
76 OUTBUF_THROTTLE_SIZE) {
77 /* stop reading new requests until client has read the pending
78 replies. */
79 io_remove(&conn->io);
80 }
81
82 e_debug(conn->event, "client passdb out: %s",
83 conn->auth->set->debug_passwords ?
84 cmd : reply_line_hide_pass(cmd));
85 }
86
auth_callback(const char * reply,struct auth_client_connection * conn)87 static void auth_callback(const char *reply,
88 struct auth_client_connection *conn)
89 {
90 if (reply == NULL) {
91 /* handler destroyed */
92 auth_client_connection_unref(&conn);
93 } else {
94 auth_client_send(conn, reply);
95 }
96 }
97
98 static bool
auth_client_input_cpid(struct auth_client_connection * conn,const char * args)99 auth_client_input_cpid(struct auth_client_connection *conn, const char *args)
100 {
101 struct auth_client_connection *old;
102 unsigned int pid;
103
104 i_assert(conn->pid == 0);
105
106 if (str_to_uint(args, &pid) < 0 || pid == 0) {
107 e_error(conn->event, "BUG: Authentication client said it's PID 0");
108 return FALSE;
109 }
110
111 if (conn->login_requests)
112 old = auth_client_connection_lookup(pid);
113 else {
114 /* the client is only authenticating, not logging in.
115 the PID isn't necessary, and since we allow authentication
116 via TCP sockets the PIDs may conflict, so ignore them. */
117 old = NULL;
118 pid = 0;
119 }
120
121 if (old != NULL) {
122 /* already exists. it's possible that it just reconnected,
123 see if the old connection is still there. */
124 i_assert(old != conn);
125 if (i_stream_read(old->input) == -1) {
126 auth_client_disconnected(&old);
127 old = NULL;
128 }
129 }
130
131 if (old != NULL) {
132 e_error(conn->event, "BUG: Authentication client gave a PID "
133 "%u of existing connection", pid);
134 return FALSE;
135 }
136
137 /* handshake complete, we can now actually start serving requests */
138 conn->refcount++;
139 conn->request_handler =
140 auth_request_handler_create(conn->token_auth, auth_callback, conn,
141 !conn->login_requests ? NULL :
142 auth_master_request_callback);
143 auth_request_handler_set(conn->request_handler, conn->connect_uid, pid);
144
145 conn->pid = pid;
146 e_debug(conn->event, "auth client connected (pid=%u)", conn->pid);
147 return TRUE;
148 }
149
auth_client_output(struct auth_client_connection * conn)150 static int auth_client_output(struct auth_client_connection *conn)
151 {
152 if (o_stream_flush(conn->output) < 0) {
153 auth_client_disconnected(&conn);
154 return 1;
155 }
156
157 if (o_stream_get_buffer_used_size(conn->output) <=
158 OUTBUF_THROTTLE_SIZE/3 && conn->io == NULL) {
159 /* allow input again */
160 conn->io = io_add(conn->fd, IO_READ, auth_client_input, conn);
161 }
162 return 1;
163 }
164
165 static const char *
auth_line_hide_pass(struct auth_client_connection * conn,const char * line)166 auth_line_hide_pass(struct auth_client_connection *conn, const char *line)
167 {
168 const char *p, *p2;
169
170 p = strstr(line, "\tresp=");
171 if (p == NULL)
172 return line;
173 p += 6;
174
175 if (conn->auth->set->debug_passwords)
176 return t_strconcat(line, AUTH_DEBUG_SENSITIVE_SUFFIX, NULL);
177
178 p2 = strchr(p, '\t');
179 return t_strconcat(t_strdup_until(line, p), PASSWORD_HIDDEN_STR,
180 p2, NULL);
181 }
182
183 static const char *
cont_line_hide_pass(struct auth_client_connection * conn,const char * line)184 cont_line_hide_pass(struct auth_client_connection *conn, const char *line)
185 {
186 const char *p;
187
188 if (conn->auth->set->debug_passwords)
189 return t_strconcat(line, AUTH_DEBUG_SENSITIVE_SUFFIX, NULL);
190
191 p = strchr(line, '\t');
192 if (p == NULL)
193 return line;
194
195 return t_strconcat(t_strdup_until(line, p), PASSWORD_HIDDEN_STR, NULL);
196 }
197
198 static bool
auth_client_cancel(struct auth_client_connection * conn,const char * line)199 auth_client_cancel(struct auth_client_connection *conn, const char *line)
200 {
201 unsigned int client_id;
202
203 if (str_to_uint(line, &client_id) < 0) {
204 e_error(conn->event, "BUG: Authentication client sent broken CANCEL");
205 return FALSE;
206 }
207
208 auth_request_handler_cancel_request(conn->request_handler, client_id);
209 return TRUE;
210 }
211
212 static bool
auth_client_handle_line(struct auth_client_connection * conn,const char * line)213 auth_client_handle_line(struct auth_client_connection *conn, const char *line)
214 {
215 if (str_begins(line, "AUTH\t")) {
216 if (conn->auth->set->debug) {
217 e_debug(conn->event, "client in: %s",
218 auth_line_hide_pass(conn, line));
219 }
220 return auth_request_handler_auth_begin(conn->request_handler,
221 line + 5);
222 }
223 if (str_begins(line, "CONT\t")) {
224 if (conn->auth->set->debug) {
225 e_debug(conn->event, "client in: %s",
226 cont_line_hide_pass(conn, line));
227 }
228 return auth_request_handler_auth_continue(conn->request_handler,
229 line + 5);
230 }
231 if (str_begins(line, "CANCEL\t")) {
232 if (conn->auth->set->debug)
233 e_debug(conn->event, "client in: %s", line);
234 return auth_client_cancel(conn, line + 7);
235 }
236
237 e_error(conn->event, "BUG: Authentication client sent unknown command: %s",
238 str_sanitize(line, 80));
239 return FALSE;
240 }
241
auth_client_input(struct auth_client_connection * conn)242 static void auth_client_input(struct auth_client_connection *conn)
243 {
244 char *line;
245 bool ret;
246
247 switch (i_stream_read(conn->input)) {
248 case 0:
249 return;
250 case -1:
251 /* disconnected */
252 auth_client_disconnected(&conn);
253 return;
254 case -2:
255 /* buffer full */
256 e_error(conn->event, "BUG: Auth client %u sent us more than %d bytes",
257 conn->pid, (int)AUTH_CLIENT_MAX_LINE_LENGTH);
258 auth_client_connection_destroy(&conn);
259 return;
260 }
261
262 while (conn->request_handler == NULL) {
263 /* still handshaking */
264 line = i_stream_next_line(conn->input);
265 if (line == NULL)
266 return;
267
268 if (!conn->version_received) {
269 unsigned int vmajor, vminor;
270 const char *p;
271
272 /* split the version line */
273 if (!str_begins(line, "VERSION\t") ||
274 str_parse_uint(line + 8, &vmajor, &p) < 0 ||
275 *(p++) != '\t' || str_to_uint(p, &vminor) < 0) {
276 e_error(conn->event, "Authentication client "
277 "sent invalid VERSION line: %s", line);
278 auth_client_connection_destroy(&conn);
279 return;
280 }
281 /* make sure the major version matches */
282 if (vmajor != AUTH_MASTER_PROTOCOL_MAJOR_VERSION) {
283 e_error(conn->event, "Authentication client "
284 "not compatible with this server "
285 "(mixed old and new binaries?)");
286 auth_client_connection_destroy(&conn);
287 return;
288 }
289 conn->version_minor = vminor;
290 conn->version_received = TRUE;
291 continue;
292 }
293
294 if (str_begins(line, "CPID\t")) {
295 if (!auth_client_input_cpid(conn, line + 5)) {
296 auth_client_connection_destroy(&conn);
297 return;
298 }
299 } else {
300 e_error(conn->event, "BUG: Authentication client sent "
301 "unknown handshake command: %s",
302 str_sanitize(line, 80));
303 auth_client_connection_destroy(&conn);
304 return;
305 }
306 }
307
308 conn->refcount++;
309 while ((line = i_stream_next_line(conn->input)) != NULL) {
310 T_BEGIN {
311 ret = auth_client_handle_line(conn, line);
312 safe_memset(line, 0, strlen(line));
313 } T_END;
314
315 if (!ret) {
316 struct auth_client_connection *tmp_conn = conn;
317 auth_client_connection_destroy(&tmp_conn);
318 break;
319 }
320 }
321 auth_client_connection_unref(&conn);
322 }
323
auth_client_connection_create(struct auth * auth,int fd,bool login_requests,bool token_auth)324 void auth_client_connection_create(struct auth *auth, int fd,
325 bool login_requests, bool token_auth)
326 {
327 static unsigned int connect_uid_counter = 0;
328 struct auth_client_connection *conn;
329 const char *mechanisms;
330 string_t *str;
331
332 conn = i_new(struct auth_client_connection, 1);
333 conn->auth = auth;
334 conn->refcount = 1;
335 conn->connect_uid = ++connect_uid_counter;
336 conn->login_requests = login_requests;
337 conn->token_auth = token_auth;
338 conn->event = event_create(auth_event);
339 event_set_forced_debug(conn->event, auth->set->debug);
340 random_fill(conn->cookie, sizeof(conn->cookie));
341
342 conn->fd = fd;
343 conn->input = i_stream_create_fd(fd, AUTH_CLIENT_MAX_LINE_LENGTH);
344 conn->output = o_stream_create_fd(fd, SIZE_MAX);
345 o_stream_set_no_error_handling(conn->output, TRUE);
346 o_stream_set_flush_callback(conn->output, auth_client_output, conn);
347 conn->io = io_add(fd, IO_READ, auth_client_input, conn);
348
349 DLLIST_PREPEND(&auth_client_connections, conn);
350
351 if (token_auth) {
352 mechanisms = t_strconcat("MECH\t",
353 mech_dovecot_token.mech_name, "\n", NULL);
354 } else {
355 mechanisms = str_c(auth->reg->handshake);
356 }
357
358 str = t_str_new(128);
359 str_printfa(str, "VERSION\t%u\t%u\n%sSPID\t%s\nCUID\t%u\nCOOKIE\t",
360 AUTH_CLIENT_PROTOCOL_MAJOR_VERSION,
361 AUTH_CLIENT_PROTOCOL_MINOR_VERSION,
362 mechanisms, my_pid, conn->connect_uid);
363 binary_to_hex_append(str, conn->cookie, sizeof(conn->cookie));
364 str_append(str, "\nDONE\n");
365
366 if (o_stream_send(conn->output, str_data(str), str_len(str)) < 0)
367 auth_client_disconnected(&conn);
368 }
369
auth_client_connection_destroy(struct auth_client_connection ** _conn)370 void auth_client_connection_destroy(struct auth_client_connection **_conn)
371 {
372 struct auth_client_connection *conn = *_conn;
373
374 *_conn = NULL;
375 if (conn->fd == -1)
376 return;
377
378 DLLIST_REMOVE(&auth_client_connections, conn);
379
380 i_stream_close(conn->input);
381 o_stream_close(conn->output);
382
383 io_remove(&conn->io);
384
385 net_disconnect(conn->fd);
386 conn->fd = -1;
387
388 if (conn->request_handler != NULL) {
389 auth_request_handler_abort_requests(conn->request_handler);
390 auth_request_handler_destroy(&conn->request_handler);
391 }
392
393 master_service_client_connection_destroyed(master_service);
394 auth_client_connection_unref(&conn);
395 }
396
auth_client_disconnected(struct auth_client_connection ** _conn)397 static void auth_client_disconnected(struct auth_client_connection **_conn)
398 {
399 struct auth_client_connection *conn = *_conn;
400 unsigned int request_count;
401 int err;
402
403 *_conn = NULL;
404
405 if (conn->input->stream_errno != 0)
406 err = conn->input->stream_errno;
407 else if (conn->output->stream_errno != 0)
408 err = conn->output->stream_errno;
409 else
410 err = 0;
411
412 request_count = conn->request_handler == NULL ? 0 :
413 auth_request_handler_get_request_count(conn->request_handler);
414 if (request_count > 0) {
415 e_error(conn->event, "auth client %u disconnected with %u "
416 "pending requests: %s", conn->pid, request_count,
417 err == 0 ? "EOF" : strerror(err));
418 }
419 auth_client_connection_destroy(&conn);
420 }
421
auth_client_connection_unref(struct auth_client_connection ** _conn)422 static void auth_client_connection_unref(struct auth_client_connection **_conn)
423 {
424 struct auth_client_connection *conn = *_conn;
425
426 *_conn = NULL;
427 if (--conn->refcount > 0)
428 return;
429
430 event_unref(&conn->event);
431 i_stream_unref(&conn->input);
432 o_stream_unref(&conn->output);
433 i_free(conn);
434 }
435
436 struct auth_client_connection *
auth_client_connection_lookup(unsigned int pid)437 auth_client_connection_lookup(unsigned int pid)
438 {
439 struct auth_client_connection *conn;
440
441 for (conn = auth_client_connections; conn != NULL; conn = conn->next) {
442 if (conn->pid == pid)
443 return conn;
444 }
445 return NULL;
446 }
447
auth_client_connections_destroy_all(void)448 void auth_client_connections_destroy_all(void)
449 {
450 struct auth_client_connection *conn;
451
452 while (auth_client_connections != NULL) {
453 conn = auth_client_connections;
454 auth_client_connection_destroy(&conn);
455 }
456 }
457