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