1 /*
2  * PgBouncer - Lightweight connection pooler for PostgreSQL.
3  *
4  * Copyright (c) 2007-2009  Marko Kreen, Skype Technologies OÜ
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Client connection handling
21  */
22 
23 #include "bouncer.h"
24 #include "pam.h"
25 #include "scram.h"
26 
27 #include <usual/pgutil.h>
28 
hdr2hex(const struct MBuf * data,char * buf,unsigned buflen)29 static const char *hdr2hex(const struct MBuf *data, char *buf, unsigned buflen)
30 {
31 	const uint8_t *bin = data->data + data->read_pos;
32 	unsigned int dlen;
33 
34 	dlen = mbuf_avail_for_read(data);
35 	return bin2hex(bin, dlen, buf, buflen);
36 }
37 
check_client_passwd(PgSocket * client,const char * passwd)38 static bool check_client_passwd(PgSocket *client, const char *passwd)
39 {
40 	PgUser *user = client->login_user;
41 	int auth_type = client->client_auth_type;
42 
43 	if (user->mock_auth)
44 		return false;
45 
46 	/* disallow empty passwords */
47 	if (!*user->passwd)
48 		return false;
49 
50 	switch (auth_type) {
51 	case AUTH_PLAIN:
52 		switch (get_password_type(user->passwd)) {
53 		case PASSWORD_TYPE_PLAINTEXT:
54 			return strcmp(user->passwd, passwd) == 0;
55 		case PASSWORD_TYPE_MD5: {
56 			char md5[MD5_PASSWD_LEN + 1];
57 			pg_md5_encrypt(passwd, user->name, strlen(user->name), md5);
58 			return strcmp(user->passwd, md5) == 0;
59 		}
60 		case PASSWORD_TYPE_SCRAM_SHA_256:
61 			return scram_verify_plain_password(client, user->name, passwd, user->passwd);
62 		default:
63 			return false;
64 		}
65 	case AUTH_MD5: {
66 		char *stored_passwd;
67 		char md5[MD5_PASSWD_LEN + 1];
68 
69 		if (strlen(passwd) != MD5_PASSWD_LEN)
70 			return false;
71 
72 		/*
73 		 * The client sends
74 		 * 'md5'+md5(md5(password+username)+salt).  The stored
75 		 * password is either 'md5'+md5(password+username) or
76 		 * plain text.  If the latter, we compute the inner
77 		 * md5() call first.
78 		 */
79 		if (get_password_type(user->passwd) == PASSWORD_TYPE_PLAINTEXT) {
80 			pg_md5_encrypt(user->passwd, user->name, strlen(user->name), md5);
81 			stored_passwd = md5;
82 		} else {
83 			stored_passwd = user->passwd;
84 		}
85 		pg_md5_encrypt(stored_passwd + 3, (char *)client->tmp_login_salt, 4, md5);
86 		return strcmp(md5, passwd) == 0;
87 	}
88 	}
89 	return false;
90 }
91 
send_client_authreq(PgSocket * client)92 static bool send_client_authreq(PgSocket *client)
93 {
94 	int res;
95 	int auth_type = client->client_auth_type;
96 
97 	if (auth_type == AUTH_MD5) {
98 		uint8_t saltlen = 4;
99 		get_random_bytes((void*)client->tmp_login_salt, saltlen);
100 		SEND_generic(res, client, 'R', "ib", AUTH_MD5, client->tmp_login_salt, saltlen);
101 	} else if (auth_type == AUTH_PLAIN || auth_type == AUTH_PAM) {
102 		SEND_generic(res, client, 'R', "i", AUTH_PLAIN);
103 	} else if (auth_type == AUTH_SCRAM_SHA_256) {
104 		SEND_generic(res, client, 'R', "iss", AUTH_SASL, "SCRAM-SHA-256", "");
105 	} else {
106 		return false;
107 	}
108 
109 	if (!res)
110 		disconnect_client(client, false, "failed to send auth req");
111 	return res;
112 }
113 
start_auth_query(PgSocket * client,const char * username)114 static void start_auth_query(PgSocket *client, const char *username)
115 {
116 	int res;
117 	PktBuf *buf;
118 
119 	/* have to fetch user info from db */
120 	client->pool = get_pool(client->db, client->db->auth_user);
121 	if (!find_server(client)) {
122 		client->wait_for_user_conn = true;
123 		return;
124 	}
125 	slog_noise(client, "doing auth_conn query");
126 	client->wait_for_user_conn = false;
127 	client->wait_for_user = true;
128 	if (!sbuf_pause(&client->sbuf)) {
129 		release_server(client->link);
130 		disconnect_client(client, true, "pause failed");
131 		return;
132 	}
133 	client->link->ready = false;
134 
135 	res = 0;
136 	buf = pktbuf_dynamic(512);
137 	if (buf) {
138 		pktbuf_write_ExtQuery(buf, cf_auth_query, 1, username);
139 		res = pktbuf_send_immediate(buf, client->link);
140 		pktbuf_free(buf);
141 		/*
142 		 * Should do instead:
143 		 *   res = pktbuf_send_queued(buf, client->link);
144 		 * but that needs better integration with SBuf.
145 		 */
146 	}
147 	if (!res)
148 		disconnect_server(client->link, false, "unable to send login query");
149 }
150 
login_via_cert(PgSocket * client)151 static bool login_via_cert(PgSocket *client)
152 {
153 	struct tls *tls = client->sbuf.tls;
154 
155 	if (!tls) {
156 		slog_error(client, "TLS connection required");
157 		goto fail;
158 	}
159 	if (!tls_peer_cert_provided(client->sbuf.tls)) {
160 		slog_error(client, "TLS client certificate required");
161 		goto fail;
162 	}
163 	if (client->login_user->mock_auth)
164 		goto fail;
165 
166 	log_debug("TLS cert login: %s", tls_peer_cert_subject(client->sbuf.tls));
167 	if (!tls_peer_cert_contains_name(client->sbuf.tls, client->login_user->name)) {
168 		slog_error(client, "TLS certificate name mismatch");
169 		goto fail;
170 	}
171 
172 	/* login successful */
173 	return finish_client_login(client);
174 fail:
175 	disconnect_client(client, true, "certificate authentication failed");
176 	return false;
177 }
178 
login_as_unix_peer(PgSocket * client)179 static bool login_as_unix_peer(PgSocket *client)
180 {
181 	if (!pga_is_unix(&client->remote_addr))
182 		goto fail;
183 	if (client->login_user->mock_auth)
184 		goto fail;
185 	if (!check_unix_peer_name(sbuf_socket(&client->sbuf), client->login_user->name))
186 		goto fail;
187 	return finish_client_login(client);
188 fail:
189 	disconnect_client(client, true, "unix socket login rejected");
190 	return false;
191 }
192 
finish_set_pool(PgSocket * client,bool takeover)193 static bool finish_set_pool(PgSocket *client, bool takeover)
194 {
195 	bool ok = false;
196 	int auth;
197 
198 	if (!client->login_user->mock_auth) {
199 		PgUser *pool_user;
200 
201 		if (client->db->forced_user)
202 			pool_user = client->db->forced_user;
203 		else
204 			pool_user = client->login_user;
205 
206 		client->pool = get_pool(client->db, pool_user);
207 		if (!client->pool) {
208 			disconnect_client(client, true, "no memory for pool");
209 			return false;
210 		}
211 	}
212 
213 	if (cf_log_connections) {
214 		if (client->sbuf.tls) {
215 			char infobuf[96] = "";
216 			tls_get_connection_info(client->sbuf.tls, infobuf, sizeof infobuf);
217 			slog_info(client, "login attempt: db=%s user=%s tls=%s",
218 				  client->db->name, client->login_user->name, infobuf);
219 		} else {
220 			slog_info(client, "login attempt: db=%s user=%s tls=no",
221 				  client->db->name, client->login_user->name);
222 		}
223 	}
224 
225 	if (!check_fast_fail(client))
226 		return false;
227 
228 	if (takeover)
229 		return true;
230 
231 	if (client->pool && client->pool->db->admin) {
232 		if (!admin_post_login(client))
233 			return false;
234 	}
235 
236 	if (client->own_user)
237 		return finish_client_login(client);
238 
239 	auth = cf_auth_type;
240 	if (auth == AUTH_HBA) {
241 		auth = hba_eval(parsed_hba, &client->remote_addr, !!client->sbuf.tls,
242 				client->db->name, client->login_user->name);
243 	}
244 
245 	if (auth == AUTH_MD5)
246 	{
247 		if (get_password_type(client->login_user->passwd) == PASSWORD_TYPE_SCRAM_SHA_256)
248 			auth = AUTH_SCRAM_SHA_256;
249 	}
250 
251 	/* remember method */
252 	client->client_auth_type = auth;
253 
254 	switch (auth) {
255 	case AUTH_ANY:
256 		ok = finish_client_login(client);
257 		break;
258 	case AUTH_TRUST:
259 		if (client->login_user->mock_auth)
260 			disconnect_client(client, true, "\"trust\" authentication failed");
261 		else
262 			ok = finish_client_login(client);
263 		break;
264 	case AUTH_PLAIN:
265 	case AUTH_MD5:
266 	case AUTH_PAM:
267 	case AUTH_SCRAM_SHA_256:
268 		ok = send_client_authreq(client);
269 		break;
270 	case AUTH_CERT:
271 		ok = login_via_cert(client);
272 		break;
273 	case AUTH_PEER:
274 		ok = login_as_unix_peer(client);
275 		break;
276 	default:
277 		disconnect_client(client, true, "login rejected");
278 		ok = false;
279 	}
280 	return ok;
281 }
282 
set_pool(PgSocket * client,const char * dbname,const char * username,const char * password,bool takeover)283 bool set_pool(PgSocket *client, const char *dbname, const char *username, const char *password, bool takeover)
284 {
285 	Assert((password && takeover) || (!password && !takeover));
286 
287 	/* find database */
288 	client->db = find_database(dbname);
289 	if (!client->db) {
290 		client->db = register_auto_database(dbname);
291 		if (!client->db) {
292 			disconnect_client(client, true, "no such database: %s", dbname);
293 			if (cf_log_connections)
294 				slog_info(client, "login failed: db=%s user=%s", dbname, username);
295 			return false;
296 		} else {
297 			slog_info(client, "registered new auto-database: db=%s", dbname);
298 		}
299 	}
300 
301 	/* are new connections allowed? */
302 	if (client->db->db_disabled) {
303 		disconnect_client(client, true, "database does not allow connections: %s", dbname);
304 		return false;
305 	}
306 
307 	if (client->db->admin) {
308 		if (admin_pre_login(client, username))
309 			return finish_set_pool(client, takeover);
310 	}
311 
312 	/* avoid dealing with invalid data below, and give an
313 	 * appropriate error message */
314 	if (strlen(username) >= MAX_USERNAME) {
315 		disconnect_client(client, true, "username too long");
316 		if (cf_log_connections)
317 			slog_info(client, "login failed: db=%s user=%s", dbname, username);
318 		return false;
319 	}
320 	if (password && strlen(password) >= MAX_PASSWORD) {
321 		disconnect_client(client, true, "password too long");
322 		if (cf_log_connections)
323 			slog_info(client, "login failed: db=%s user=%s", dbname, username);
324 		return false;
325 	}
326 
327 	/* find user */
328 	if (cf_auth_type == AUTH_ANY) {
329 		/* ignore requested user */
330 		if (client->db->forced_user == NULL) {
331 			slog_error(client, "auth_type=any requires forced user");
332 			disconnect_client(client, true, "bouncer config error");
333 			return false;
334 		}
335 		client->login_user = client->db->forced_user;
336 	} else if (cf_auth_type == AUTH_PAM) {
337 		if (client->db->auth_user) {
338 			slog_error(client, "PAM can't be used together with database authentication");
339 			disconnect_client(client, true, "bouncer config error");
340 			return false;
341 		}
342 		/* Password will be set after successful authentication when not in takeover mode */
343 		client->login_user = add_pam_user(username, password);
344 		if (!client->login_user) {
345 			slog_error(client, "set_pool(): failed to allocate new PAM user");
346 			disconnect_client(client, true, "bouncer resources exhaustion");
347 			return false;
348 		}
349 	} else {
350 		client->login_user = find_user(username);
351 		if (!client->login_user) {
352 			/*
353 			 * If the login user specified by the client
354 			 * does not exist, check if an auth_user is
355 			 * set and if so send off an auth_query.  If
356 			 * no auth_user is set for the db, see if the
357 			 * global auth_user is set and use that.
358 			 */
359 			if (!client->db->auth_user && cf_auth_user) {
360 				client->db->auth_user = find_user(cf_auth_user);
361 				if (!client->db->auth_user)
362 					client->db->auth_user = add_user(cf_auth_user, "");
363 			}
364 			if (client->db->auth_user) {
365 				if (takeover) {
366 					client->login_user = add_db_user(client->db, username, password);
367 					return finish_set_pool(client, takeover);
368 				}
369 				start_auth_query(client, username);
370 				return false;
371 			}
372 
373 			slog_info(client, "no such user: %s", username);
374 			client->login_user = calloc(1, sizeof(*client->login_user));
375 			client->login_user->mock_auth = true;
376 			safe_strcpy(client->login_user->name, username, sizeof(client->login_user->name));
377 		}
378 	}
379 
380 	return finish_set_pool(client, takeover);
381 }
382 
handle_auth_query_response(PgSocket * client,PktHdr * pkt)383 bool handle_auth_query_response(PgSocket *client, PktHdr *pkt) {
384 	uint16_t columns;
385 	uint32_t length;
386 	const char *username, *password;
387 	PgUser user;
388 	PgSocket *server = client->link;
389 
390 	switch(pkt->type) {
391 	case 'T':	/* RowDescription */
392 		if (!mbuf_get_uint16be(&pkt->data, &columns)) {
393 			disconnect_server(server, false, "bad packet");
394 			return false;
395 		}
396 		if (columns != 2u) {
397 			disconnect_server(server, false, "expected 2 columns from login query, not %hu", columns);
398 			return false;
399 		}
400 		break;
401 	case 'D':	/* DataRow */
402 		memset(&user, 0, sizeof(user));
403 		if (!mbuf_get_uint16be(&pkt->data, &columns)) {
404 			disconnect_server(server, false, "bad packet");
405 			return false;
406 		}
407 		if (columns != 2u) {
408 			disconnect_server(server, false, "expected 2 columns from login query, not %hu", columns);
409 			return false;
410 		}
411 		if (!mbuf_get_uint32be(&pkt->data, &length)) {
412 			disconnect_server(server, false, "bad packet");
413 			return false;
414 		}
415 		if (length == (uint32_t)-1) {
416 			disconnect_server(server, false, "login query response contained null user name");
417 			return false;
418 		}
419 		if (!mbuf_get_chars(&pkt->data, length, &username)) {
420 			disconnect_server(server, false, "bad packet");
421 			return false;
422 		}
423 		if (sizeof(user.name) - 1 < length)
424 			length = sizeof(user.name) - 1;
425 		memcpy(user.name, username, length);
426 		if (!mbuf_get_uint32be(&pkt->data, &length)) {
427 			disconnect_server(server, false, "bad packet");
428 			return false;
429 		}
430 		if (length == (uint32_t)-1) {
431 			/*
432 			 * NULL - set an md5 password with an impossible value,
433 			 * so that nothing will ever match
434 			 */
435 			password = "md5";
436 			length = 3;
437 		} else {
438 			if (!mbuf_get_chars(&pkt->data, length, &password)) {
439 				disconnect_server(server, false, "bad packet");
440 				return false;
441 			}
442 		}
443 		if (sizeof(user.passwd)  - 1 < length)
444 			length = sizeof(user.passwd) - 1;
445 		memcpy(user.passwd, password, length);
446 
447 		client->login_user = add_db_user(client->db, user.name, user.passwd);
448 		if (!client->login_user) {
449 			disconnect_server(server, false, "unable to allocate new user for auth");
450 			return false;
451 		}
452 		break;
453 	case 'N':	/* NoticeResponse */
454 		break;
455 	case 'C':	/* CommandComplete */
456 		break;
457 	case '1':	/* ParseComplete */
458 		break;
459 	case '2':	/* BindComplete */
460 		break;
461 	case 'S': /* ParameterStatus */
462 		break;
463 	case 'Z':	/* ReadyForQuery */
464 		sbuf_prepare_skip(&client->link->sbuf, pkt->len);
465 		if (!client->login_user) {
466 			if (cf_log_connections)
467 				slog_info(client, "login failed: db=%s", client->db->name);
468 			/*
469 			 * TODO: Currently no mock authentication when
470 			 * using auth_query/auth_user; we just abort
471 			 * with a revealing message to the client.
472 			 * The main problem is that at this point we
473 			 * don't know the original user name anymore
474 			 * to do that.  As a workaround, the
475 			 * auth_query could be written in a way that
476 			 * it returns a fake user and password if the
477 			 * requested user doesn't exist.
478 			 */
479 			disconnect_client(client, true, "no such user");
480 		} else {
481 			slog_noise(client, "auth query complete");
482 			client->link->resetting = true;
483 			sbuf_continue(&client->sbuf);
484 		}
485 		/*
486 		 * either sbuf_continue or disconnect_client could disconnect the server
487 		 * way down in their bowels of other callbacks. so check that, and
488 		 * return appropriately (similar to reuse_on_release)
489 		 */
490 		if (server->state == SV_FREE || server->state == SV_JUSTFREE)
491 			return false;
492 		return true;
493 	default:
494 		disconnect_server(server, false, "unexpected response from login query");
495 		return false;
496 	}
497 	sbuf_prepare_skip(&server->sbuf, pkt->len);
498 	return true;
499 }
500 
set_appname(PgSocket * client,const char * app_name)501 static void set_appname(PgSocket *client, const char *app_name)
502 {
503 	char buf[400], abuf[300];
504 	const char *details;
505 
506 	if (cf_application_name_add_host) {
507 		/* give app a name */
508 		if (!app_name)
509 			app_name = "app";
510 
511 		/* add details */
512 		details = pga_details(&client->remote_addr, abuf, sizeof(abuf));
513 		snprintf(buf, sizeof(buf), "%s - %s", app_name, details);
514 		app_name = buf;
515 	}
516 	if (app_name) {
517 		slog_debug(client, "using application_name: %s", app_name);
518 		varcache_set(&client->vars, "application_name", app_name);
519 	}
520 }
521 
decide_startup_pool(PgSocket * client,PktHdr * pkt)522 static bool decide_startup_pool(PgSocket *client, PktHdr *pkt)
523 {
524 	const char *username = NULL, *dbname = NULL;
525 	const char *key, *val;
526 	bool ok;
527 	bool appname_found = false;
528 
529 	while (1) {
530 		ok = mbuf_get_string(&pkt->data, &key);
531 		if (!ok || *key == 0)
532 			break;
533 		ok = mbuf_get_string(&pkt->data, &val);
534 		if (!ok)
535 			break;
536 
537 		if (strcmp(key, "database") == 0) {
538 			slog_debug(client, "got var: %s=%s", key, val);
539 			dbname = val;
540 		} else if (strcmp(key, "user") == 0) {
541 			slog_debug(client, "got var: %s=%s", key, val);
542 			username = val;
543 		} else if (strcmp(key, "application_name") == 0) {
544 			set_appname(client, val);
545 			appname_found = true;
546 		} else if (varcache_set(&client->vars, key, val)) {
547 			slog_debug(client, "got var: %s=%s", key, val);
548 		} else if (strlist_contains(cf_ignore_startup_params, key)) {
549 			slog_debug(client, "ignoring startup parameter: %s=%s", key, val);
550 		} else {
551 			slog_warning(client, "unsupported startup parameter: %s=%s", key, val);
552 			disconnect_client(client, true, "unsupported startup parameter: %s", key);
553 			return false;
554 		}
555 	}
556 	if (!username || !username[0]) {
557 		disconnect_client(client, true, "no username supplied");
558 		return false;
559 	}
560 
561 	/* if missing dbname, default to username */
562 	if (!dbname || !dbname[0])
563 		dbname = username;
564 
565 	/* create application_name if requested */
566 	if (!appname_found)
567 		set_appname(client, NULL);
568 
569 	/* check if limit allows, don't limit admin db
570 	   nb: new incoming conn will be attached to PgSocket, thus
571 	   get_active_client_count() counts it */
572 	if (get_active_client_count() > cf_max_client_conn) {
573 		if (strcmp(dbname, "pgbouncer") != 0) {
574 			disconnect_client(client, true, "no more connections allowed (max_client_conn)");
575 			return false;
576 		}
577 	}
578 
579 	/* find pool */
580 	return set_pool(client, dbname, username, NULL, false);
581 }
582 
scram_client_first(PgSocket * client,uint32_t datalen,const uint8_t * data)583 static bool scram_client_first(PgSocket *client, uint32_t datalen, const uint8_t *data)
584 {
585 	char *ibuf;
586 	char *input;
587 	int res;
588 	PgUser *user = client->login_user;
589 
590 	ibuf = malloc(datalen + 1);
591 	if (ibuf == NULL)
592 		return false;
593 	memcpy(ibuf, data, datalen);
594 	ibuf[datalen] = '\0';
595 
596 	input = ibuf;
597 	slog_debug(client, "SCRAM client-first-message = \"%s\"", input);
598 	if (!read_client_first_message(client, input,
599 				       &client->scram_state.cbind_flag,
600 				       &client->scram_state.client_first_message_bare,
601 				       &client->scram_state.client_nonce))
602 		goto failed;
603 
604 	if (!user->mock_auth) {
605 		slog_debug(client, "stored secret = \"%s\"", user->passwd);
606 		switch (get_password_type(user->passwd)) {
607 		case PASSWORD_TYPE_MD5:
608 			slog_error(client, "SCRAM authentication failed: user has MD5 secret");
609 			goto failed;
610 		case PASSWORD_TYPE_PLAINTEXT:
611 		case PASSWORD_TYPE_SCRAM_SHA_256:
612 			break;
613 		}
614 	}
615 
616 	if (!build_server_first_message(&client->scram_state, user->name, user->mock_auth ? NULL : user->passwd))
617 		goto failed;
618 	slog_debug(client, "SCRAM server-first-message = \"%s\"", client->scram_state.server_first_message);
619 
620 	SEND_generic(res, client, 'R', "ib",
621 		     AUTH_SASL_CONT,
622 		     client->scram_state.server_first_message,
623 		     strlen(client->scram_state.server_first_message));
624 
625 	free(ibuf);
626 	return res;
627 failed:
628 	free(ibuf);
629 	return false;
630 }
631 
scram_client_final(PgSocket * client,uint32_t datalen,const uint8_t * data)632 static bool scram_client_final(PgSocket *client, uint32_t datalen, const uint8_t *data)
633 {
634 	char *ibuf;
635 	char *input;
636 	const char *client_final_nonce = NULL;
637 	char *proof = NULL;
638 	char *server_final_message;
639 	int res;
640 
641 	ibuf = malloc(datalen + 1);
642 	if (ibuf == NULL)
643 		return false;
644 	memcpy(ibuf, data, datalen);
645 	ibuf[datalen] = '\0';
646 
647 	input = ibuf;
648 	slog_debug(client, "SCRAM client-final-message = \"%s\"", input);
649 	if (!read_client_final_message(client, data, input,
650 				       &client_final_nonce,
651 				       &proof))
652 		goto failed;
653 	slog_debug(client, "SCRAM client-final-message-without-proof = \"%s\"",
654 		   client->scram_state.client_final_message_without_proof);
655 
656 	if (!verify_final_nonce(&client->scram_state, client_final_nonce)) {
657 		slog_error(client, "invalid SCRAM response (nonce does not match)");
658 		goto failed;
659 	}
660 
661 	if (!verify_client_proof(&client->scram_state, proof)
662 	    || !client->login_user) {
663 		slog_error(client, "password authentication failed");
664 		goto failed;
665 	}
666 
667 	server_final_message = build_server_final_message(&client->scram_state);
668 	if (!server_final_message)
669 		goto failed;
670 	slog_debug(client, "SCRAM server-final-message = \"%s\"", server_final_message);
671 
672 	SEND_generic(res, client, 'R', "ib",
673 		     AUTH_SASL_FIN,
674 		     server_final_message,
675 		     strlen(server_final_message));
676 
677 	free(server_final_message);
678 	free(proof);
679 	free(ibuf);
680 	return res;
681 failed:
682 	free(proof);
683 	free(ibuf);
684 	return false;
685 }
686 
687 /* decide on packets of client in login phase */
handle_client_startup(PgSocket * client,PktHdr * pkt)688 static bool handle_client_startup(PgSocket *client, PktHdr *pkt)
689 {
690 	const char *passwd;
691 	const uint8_t *key;
692 	bool ok;
693 	bool is_unix = pga_is_unix(&client->remote_addr);
694 
695 	SBuf *sbuf = &client->sbuf;
696 
697 	/* don't tolerate partial packets */
698 	if (incomplete_pkt(pkt)) {
699 		disconnect_client(client, true, "client sent partial pkt in startup phase");
700 		return false;
701 	}
702 
703 	if (client->wait_for_welcome || client->wait_for_auth) {
704 		if  (finish_client_login(client)) {
705 			/* the packet was already parsed */
706 			sbuf_prepare_skip(sbuf, pkt->len);
707 			return true;
708 		} else {
709 			return false;
710 		}
711 	}
712 
713 	switch (pkt->type) {
714 	case PKT_SSLREQ:
715 		slog_noise(client, "C: req SSL");
716 
717 		if (client->sbuf.tls) {
718 			disconnect_client(client, false, "SSL req inside SSL");
719 			return false;
720 		}
721 		if (client_accept_sslmode != SSLMODE_DISABLED && !is_unix) {
722 			slog_noise(client, "P: SSL ack");
723 			if (!sbuf_answer(&client->sbuf, "S", 1)) {
724 				disconnect_client(client, false, "failed to ack SSL");
725 				return false;
726 			}
727 			if (!sbuf_tls_accept(&client->sbuf)) {
728 				disconnect_client(client, false, "failed to accept SSL");
729 				return false;
730 			}
731 			break;
732 		}
733 
734 		/* reject SSL attempt */
735 		slog_noise(client, "P: nak");
736 		if (!sbuf_answer(&client->sbuf, "N", 1)) {
737 			disconnect_client(client, false, "failed to nak SSL");
738 			return false;
739 		}
740 		break;
741 	case PKT_GSSENCREQ:
742 		/* reject GSS encryption attempt */
743 		slog_noise(client, "C: req GSS enc");
744 		if (!sbuf_answer(&client->sbuf, "N", 1)) {
745 			disconnect_client(client, false, "failed to nak GSS enc");
746 			return false;
747 		}
748 		break;
749 	case PKT_STARTUP_V2:
750 		disconnect_client(client, true, "old V2 protocol not supported");
751 		return false;
752 	case PKT_STARTUP:
753 		/* require SSL except on unix socket */
754 		if (client_accept_sslmode >= SSLMODE_REQUIRE && !client->sbuf.tls && !is_unix) {
755 			disconnect_client(client, true, "SSL required");
756 			return false;
757 		}
758 
759 		if (client->pool && !client->wait_for_user_conn && !client->wait_for_user) {
760 			disconnect_client(client, true, "client re-sent startup pkt");
761 			return false;
762 		}
763 
764 		if (client->wait_for_user) {
765 			client->wait_for_user = false;
766 			if (!finish_set_pool(client, false))
767 				return false;
768 		} else if (!decide_startup_pool(client, pkt)) {
769 			return false;
770 		}
771 
772 		break;
773 	case 'p':		/* PasswordMessage, SASLInitialResponse, or SASLResponse */
774 		/* too early */
775 		if (!client->login_user) {
776 			disconnect_client(client, true, "client password pkt before startup packet");
777 			return false;
778 		}
779 
780 		if (client->client_auth_type == AUTH_SCRAM_SHA_256) {
781 			const char *mech;
782 			uint32_t length;
783 			const uint8_t *data;
784 
785 			if (!client->scram_state.server_nonce) {
786 				/* process as SASLInitialResponse */
787 				if (!mbuf_get_string(&pkt->data, &mech))
788 					return false;
789 				slog_debug(client, "C: selected SASL mechanism: %s", mech);
790 				if (strcmp(mech, "SCRAM-SHA-256") != 0) {
791 					disconnect_client(client, true, "client selected an invalid SASL authentication mechanism");
792 					return false;
793 				}
794 				if (!mbuf_get_uint32be(&pkt->data, &length))
795 					return false;
796 				if (!mbuf_get_bytes(&pkt->data, length, &data))
797 					return false;
798 				if (!scram_client_first(client, length, data)) {
799 					disconnect_client(client, true, "SASL authentication failed");
800 					return false;
801 				}
802 			} else {
803 				/* process as SASLResponse */
804 				length = mbuf_avail_for_read(&pkt->data);
805 				if (!mbuf_get_bytes(&pkt->data, length, &data))
806 					return false;
807 				if (scram_client_final(client, length, data)) {
808 					/* save SCRAM keys for user */
809 					if (!client->scram_state.adhoc) {
810 						memcpy(client->pool->user->scram_ClientKey,
811 						       client->scram_state.ClientKey,
812 						       sizeof(client->scram_state.ClientKey));
813 						memcpy(client->pool->user->scram_ServerKey,
814 						       client->scram_state.ServerKey,
815 						       sizeof(client->scram_state.ServerKey));
816 						client->pool->user->has_scram_keys = true;
817 					}
818 
819 					free_scram_state(&client->scram_state);
820 					if (!finish_client_login(client))
821 						return false;
822 				}
823 				else {
824 					disconnect_client(client, true, "SASL authentication failed");
825 					return false;
826 				}
827 			}
828 		} else {
829 			/* process as PasswordMessage */
830 			ok = mbuf_get_string(&pkt->data, &passwd);
831 
832 			if (ok) {
833 				/*
834 				 * Don't allow an empty password; see
835 				 * PostgreSQL recv_password_packet().
836 				 */
837 				if (!*passwd) {
838 					disconnect_client(client, true, "empty password returned by client");
839 					return false;
840 				}
841 
842 				if (client->client_auth_type == AUTH_PAM) {
843 					if (!sbuf_pause(&client->sbuf)) {
844 						disconnect_client(client, true, "pause failed");
845 						return false;
846 					}
847 					pam_auth_begin(client, passwd);
848 					return false;
849 				}
850 
851 				if (check_client_passwd(client, passwd)) {
852 					if (!finish_client_login(client))
853 						return false;
854 				} else {
855 					disconnect_client(client, true, "password authentication failed");
856 					return false;
857 				}
858 			}
859 		}
860 		break;
861 	case PKT_CANCEL:
862 		if (mbuf_avail_for_read(&pkt->data) == BACKENDKEY_LEN
863 		    && mbuf_get_bytes(&pkt->data, BACKENDKEY_LEN, &key))
864 		{
865 			memcpy(client->cancel_key, key, BACKENDKEY_LEN);
866 			accept_cancel_request(client);
867 		} else {
868 			disconnect_client(client, false, "bad cancel request");
869 		}
870 		return false;
871 	default:
872 		disconnect_client(client, false, "bad packet");
873 		return false;
874 	}
875 	sbuf_prepare_skip(sbuf, pkt->len);
876 	client->request_time = get_cached_time();
877 	return true;
878 }
879 
880 /* decide on packets of logged-in client */
handle_client_work(PgSocket * client,PktHdr * pkt)881 static bool handle_client_work(PgSocket *client, PktHdr *pkt)
882 {
883 	SBuf *sbuf = &client->sbuf;
884 	int rfq_delta = 0;
885 
886 	switch (pkt->type) {
887 
888 	/* one-packet queries */
889 	case 'Q':		/* Query */
890 		if (cf_disable_pqexec) {
891 			slog_error(client, "client used 'Q' packet type");
892 			disconnect_client(client, true, "PQexec disallowed");
893 			return false;
894 		}
895 		rfq_delta++;
896 		break;
897 	case 'F':		/* FunctionCall */
898 		rfq_delta++;
899 		break;
900 
901 	/* request immediate response from server */
902 	case 'S':		/* Sync */
903 		rfq_delta++;
904 		break;
905 	case 'H':		/* Flush */
906 		break;
907 
908 	/* copy end markers */
909 	case 'c':		/* CopyDone(F/B) */
910 	case 'f':		/* CopyFail(F/B) */
911 		break;
912 
913 	/*
914 	 * extended protocol allows server (and thus pooler)
915 	 * to buffer packets until sync or flush is sent by client
916 	 */
917 	case 'P':		/* Parse */
918 	case 'E':		/* Execute */
919 	case 'C':		/* Close */
920 	case 'B':		/* Bind */
921 	case 'D':		/* Describe */
922 	case 'd':		/* CopyData(F/B) */
923 		break;
924 
925 	/* client wants to go away */
926 	default:
927 		slog_error(client, "unknown pkt from client: %d/0x%x", pkt->type, pkt->type);
928 		disconnect_client(client, true, "unknown pkt");
929 		return false;
930 	case 'X': /* Terminate */
931 		disconnect_client(client, false, "client close request");
932 		return false;
933 	}
934 
935 	/* update stats */
936 	if (!client->query_start) {
937 		client->pool->stats.query_count++;
938 		client->query_start = get_cached_time();
939 	}
940 
941 	/* remember timestamp of the first query in a transaction */
942 	if (!client->xact_start) {
943 		client->pool->stats.xact_count++;
944 		client->xact_start = client->query_start;
945 	}
946 
947 	if (client->pool->db->admin)
948 		return admin_handle_client(client, pkt);
949 
950 	/* acquire server */
951 	if (!find_server(client))
952 		return false;
953 
954 	/* postpone rfq change until certain that client will not be paused */
955 	if (rfq_delta) {
956 		client->expect_rfq_count += rfq_delta;
957 	}
958 
959 	client->pool->stats.client_bytes += pkt->len;
960 
961 	/* tag the server as dirty */
962 	client->link->ready = false;
963 	client->link->idle_tx = false;
964 
965 	/* forward the packet */
966 	sbuf_prepare_send(sbuf, &client->link->sbuf, pkt->len);
967 
968 	return true;
969 }
970 
971 /* callback from SBuf */
client_proto(SBuf * sbuf,SBufEvent evtype,struct MBuf * data)972 bool client_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *data)
973 {
974 	bool res = false;
975 	PgSocket *client = container_of(sbuf, PgSocket, sbuf);
976 	PktHdr pkt;
977 
978 
979 	Assert(!is_server_socket(client));
980 	Assert(client->sbuf.sock);
981 	Assert(client->state != CL_FREE);
982 
983 	/* may happen if close failed */
984 	if (client->state == CL_JUSTFREE)
985 		return false;
986 
987 	switch (evtype) {
988 	case SBUF_EV_CONNECT_OK:
989 	case SBUF_EV_CONNECT_FAILED:
990 		/* ^ those should not happen */
991 	case SBUF_EV_RECV_FAILED:
992 		/*
993 		 * Don't log error if client disconnects right away,
994 		 * could be monitoring probe.
995 		 */
996 		if (client->state == CL_LOGIN && mbuf_avail_for_read(data) == 0)
997 			disconnect_client(client, false, NULL);
998 		else
999 			disconnect_client(client, false, "client unexpected eof");
1000 		break;
1001 	case SBUF_EV_SEND_FAILED:
1002 		disconnect_server(client->link, false, "server connection closed");
1003 		break;
1004 	case SBUF_EV_READ:
1005 		/* Wait until full packet headers is available. */
1006 		if (incomplete_header(data)) {
1007 			slog_noise(client, "C: got partial header, trying to wait a bit");
1008 			return false;
1009 		}
1010 		if (!get_header(data, &pkt)) {
1011 			char hex[8*2 + 1];
1012 			disconnect_client(client, true, "bad packet header: '%s'",
1013 					  hdr2hex(data, hex, sizeof(hex)));
1014 			return false;
1015 		}
1016 		slog_noise(client, "read pkt='%c' len=%d", pkt_desc(&pkt), pkt.len);
1017 
1018 		client->request_time = get_cached_time();
1019 		switch (client->state) {
1020 		case CL_LOGIN:
1021 			res = handle_client_startup(client, &pkt);
1022 			break;
1023 		case CL_ACTIVE:
1024 			if (client->wait_for_welcome)
1025 				res = handle_client_startup(client, &pkt);
1026 			else
1027 				res = handle_client_work(client, &pkt);
1028 			break;
1029 		case CL_WAITING:
1030 			fatal("why waiting client in client_proto()");
1031 		default:
1032 			fatal("bad client state: %d", client->state);
1033 		}
1034 		break;
1035 	case SBUF_EV_FLUSH:
1036 		/* client is not interested in it */
1037 		break;
1038 	case SBUF_EV_PKT_CALLBACK:
1039 		/* unused ATM */
1040 		break;
1041 	case SBUF_EV_TLS_READY:
1042 		sbuf_continue(&client->sbuf);
1043 		res = true;
1044 		break;
1045 	}
1046 	return res;
1047 }
1048