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