2 * PgBouncer - Lightweight connection pooler for PostgreSQL.
4 * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
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.
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.
20 * Pieces that need to have detailed info about protocol.
27 * parse protocol header from struct MBuf
30 /* parses pkt header from buffer, returns false if failed */
31 bool get_header(struct MBuf *data, PktHdr *pkt)
43 mbuf_copy(data, &hdr);
45 if (mbuf_avail_for_read(&hdr) < NEW_HEADER_LEN) {
46 log_noise("get_header: less than 5 bytes available");
49 if (!mbuf_get_byte(&hdr, &type8))
53 /* wire length does not include type byte */
54 if (!mbuf_get_uint32be(&hdr, &len))
59 if (!mbuf_get_byte(&hdr, &type8))
62 log_noise("get_header: unknown special pkt");
65 /* don't tolerate partial pkt */
66 if (mbuf_avail_for_read(&hdr) < OLD_HEADER_LEN - 2) {
67 log_noise("get_header: less than 8 bytes for special pkt");
70 if (!mbuf_get_uint16be(&hdr, &len16))
73 if (!mbuf_get_uint32be(&hdr, &code))
75 if (code == PKT_CANCEL) {
77 } else if (code == PKT_SSLREQ) {
79 } else if (code == PKT_GSSENCREQ) {
81 } else if ((code >> 16) == 3 && (code & 0xFFFF) < 2) {
83 } else if (code == PKT_STARTUP_V2) {
84 type = PKT_STARTUP_V2;
86 log_noise("get_header: unknown special pkt: len=%u code=%u", len, code);
92 /* don't believe nonsense */
93 if (len < got || len > cf_max_packet_size)
100 /* fill pkt with only data for this packet */
101 if (len > mbuf_avail_for_read(data)) {
102 avail = mbuf_avail_for_read(data);
106 if (!mbuf_slice(data, avail, &pkt->data))
109 /* tag header as read */
110 return mbuf_get_bytes(&pkt->data, got, &ptr);
115 * Send error message packet to client.
118 bool send_pooler_error(PgSocket *client, bool send_ready, const char *msg)
123 if (cf_log_pooler_errors)
124 slog_warning(client, "pooler error: %s", msg);
126 pktbuf_static(&buf, tmpbuf, sizeof(tmpbuf));
127 pktbuf_write_generic(&buf, 'E', "cscscsc",
128 'S', "ERROR", 'C', "08P01", 'M', msg, 0);
130 pktbuf_write_ReadyForQuery(&buf);
131 return pktbuf_send_immediate(&buf, client);
135 * Parse server error message and log it.
137 void parse_server_error(PktHdr *pkt, const char **level_p, const char **msg_p)
139 const char *level = NULL, *msg = NULL, *val;
141 while (mbuf_avail_for_read(&pkt->data)) {
142 if (!mbuf_get_byte(&pkt->data, &type))
146 if (!mbuf_get_string(&pkt->data, &val))
150 } else if (type == 'M') {
158 void log_server_error(const char *note, PktHdr *pkt)
160 const char *level = NULL, *msg = NULL;
162 parse_server_error(pkt, &level, &msg);
164 if (!msg || !level) {
165 log_error("%s: partial error message, cannot log", note);
167 log_error("%s: %s: %s", note, level, msg);
173 * Preparation of welcome message for client connection.
176 /* add another server parameter packet to cache */
177 bool add_welcome_parameter(PgPool *pool, const char *key, const char *val)
179 PktBuf *msg = pool->welcome_msg;
181 if (pool->welcome_msg_ready)
185 msg = pktbuf_dynamic(128);
188 pool->welcome_msg = msg;
191 /* first packet must be AuthOk */
192 if (msg->write_pos == 0)
193 pktbuf_write_AuthenticationOk(msg);
195 /* if not stored in ->orig_vars, write full packet */
196 if (!varcache_set(&pool->orig_vars, key, val))
197 pktbuf_write_ParameterStatus(msg, key, val);
202 /* all parameters processed */
203 void finish_welcome_msg(PgSocket *server)
205 PgPool *pool = server->pool;
206 if (pool->welcome_msg_ready)
208 pool->welcome_msg_ready = 1;
211 bool welcome_client(PgSocket *client)
214 PgPool *pool = client->pool;
215 const PktBuf *pmsg = pool->welcome_msg;
218 slog_noise(client, "P: welcome_client");
220 /* copy prepared stuff around */
222 pktbuf_put_bytes(msg, pmsg->buf, pmsg->write_pos);
225 varcache_fill_unset(&pool->orig_vars, client);
226 varcache_add_params(msg, &client->vars);
228 /* give each client its own cancel key */
229 get_random_bytes(client->cancel_key, 8);
230 pktbuf_write_BackendKeyData(msg, client->cancel_key);
233 pktbuf_write_ReadyForQuery(msg);
235 disconnect_client(client, true, "failed to prepare welcome message");
239 /* send all together */
240 res = pktbuf_send_immediate(msg, client);
242 disconnect_client(client, true, "failed to send welcome message");
249 * Password authentication for server
252 static PgUser *get_srv_psw(PgSocket *server)
254 PgDatabase *db = server->pool->db;
255 PgUser *user = server->pool->user;
257 /* if forced user without password, use userlist psw */
258 if (!user->passwd[0] && db->forced_user) {
259 PgUser *u2 = find_user(user->name);
266 /* actual packet send */
267 static bool send_password(PgSocket *server, const char *enc_psw)
270 SEND_PasswordMessage(res, server, enc_psw);
274 static bool login_clear_psw(PgSocket *server)
276 PgUser *user = get_srv_psw(server);
277 slog_debug(server, "P: send clear password");
278 return send_password(server, user->passwd);
281 static bool login_md5_psw(PgSocket *server, const uint8_t *salt)
283 char txt[MD5_PASSWD_LEN + 1], *src;
284 PgUser *user = get_srv_psw(server);
286 slog_debug(server, "P: send md5 password");
288 switch (get_password_type(user->passwd)) {
289 case PASSWORD_TYPE_PLAINTEXT:
290 pg_md5_encrypt(user->passwd, user->name, strlen(user->name), txt);
293 case PASSWORD_TYPE_MD5:
294 src = user->passwd + 3;
297 slog_error(server, "cannot do MD5 authentication: wrong password type");
298 kill_pool_logins(server->pool, "server login failed: wrong password type");
302 pg_md5_encrypt(src, (char *)salt, 4, txt);
304 return send_password(server, txt);
307 static bool login_scram_sha_256(PgSocket *server)
309 PgUser *user = get_srv_psw(server);
311 char *client_first_message = NULL;
313 if (get_password_type(user->passwd) != PASSWORD_TYPE_PLAINTEXT) {
314 slog_error(server, "cannot do SCRAM authentication: password is not plain text");
315 kill_pool_logins(server->pool, "server login failed: wrong password type");
319 if (server->scram_state.client_nonce)
321 slog_error(server, "protocol error: duplicate AuthenticationSASL message from server");
325 client_first_message = build_client_first_message(&server->scram_state);
326 if (!client_first_message)
329 slog_debug(server, "SCRAM client-first-message = \"%s\"", client_first_message);
330 slog_debug(server, "P: send SASLInitialResponse");
331 SEND_SASLInitialResponseMessage(res, server, "SCRAM-SHA-256", client_first_message);
333 free(client_first_message);
337 static bool login_scram_sha_256_cont(PgSocket *server, unsigned datalen, const uint8_t *data)
339 PgUser *user = get_srv_psw(server);
347 char *client_final_message = NULL;
349 if (!server->scram_state.client_nonce)
351 slog_error(server, "protocol error: AuthenticationSASLContinue without prior AuthenticationSASL");
355 if (server->scram_state.server_first_message)
357 slog_error(server, "SCRAM exchange protocol error: received second AuthenticationSASLContinue");
361 ibuf = malloc(datalen + 1);
364 memcpy(ibuf, data, datalen);
365 ibuf[datalen] = '\0';
368 slog_debug(server, "SCRAM server-first-message = \"%s\"", input);
369 if (!read_server_first_message(server, input,
370 &server_nonce, &salt, &saltlen, &iterations))
373 client_final_message = build_client_final_message(&server->scram_state,
374 user->passwd, server_nonce,
375 salt, saltlen, iterations);
380 slog_debug(server, "SCRAM client-final-message = \"%s\"", client_final_message);
381 slog_debug(server, "P: send SASLResponse");
382 SEND_SASLResponseMessage(res, server, client_final_message);
384 free(client_final_message);
389 free(client_final_message);
393 static bool login_scram_sha_256_final(PgSocket *server, unsigned datalen, const uint8_t *data)
397 char ServerSignature[SHA256_DIGEST_LENGTH];
399 if (!server->scram_state.server_first_message)
401 slog_error(server, "protocol error: AuthenticationSASLFinal without prior AuthenticationSASLContinue");
405 ibuf = malloc(datalen + 1);
408 memcpy(ibuf, data, datalen);
409 ibuf[datalen] = '\0';
412 slog_debug(server, "SCRAM server-final-message = \"%s\"", input);
413 if (!read_server_final_message(server, input, ServerSignature))
416 if (!verify_server_signature(&server->scram_state, ServerSignature))
418 slog_error(server, "invalid server signature");
419 kill_pool_logins(server->pool, "server login failed: invalid server signature");
430 /* answer server authentication request */
431 bool answer_authreq(PgSocket *server, PktHdr *pkt)
437 /* authreq body must contain 32bit cmd */
438 if (mbuf_avail_for_read(&pkt->data) < 4)
441 if (!mbuf_get_uint32be(&pkt->data, &cmd))
445 slog_debug(server, "S: auth ok");
449 slog_debug(server, "S: req cleartext password");
450 res = login_clear_psw(server);
453 slog_debug(server, "S: req md5-crypted psw");
454 if (!mbuf_get_bytes(&pkt->data, 4, &salt))
456 res = login_md5_psw(server, salt);
460 bool selected_mechanism = false;
462 slog_debug(server, "S: req SASL");
467 if (!mbuf_get_string(&pkt->data, &mech))
471 slog_debug(server, "S: SASL advertised mechanism: %s", mech);
472 if (strcmp(mech, "SCRAM-SHA-256") == 0)
473 selected_mechanism = true;
474 } while (!selected_mechanism);
476 if (!selected_mechanism) {
477 slog_error(server, "none of the server's SASL authentication mechanisms are supported");
478 kill_pool_logins(server->pool, "server login failed: none of the server's SASL authentication mechanisms are supported");
480 res = login_scram_sha_256(server);
488 slog_debug(server, "S: SASL cont");
489 len = mbuf_avail_for_read(&pkt->data);
490 if (!mbuf_get_bytes(&pkt->data, len, &data))
492 res = login_scram_sha_256_cont(server, len, data);
500 slog_debug(server, "S: SASL final");
501 len = mbuf_avail_for_read(&pkt->data);
502 if (!mbuf_get_bytes(&pkt->data, len, &data))
504 res = login_scram_sha_256_final(server, len, data);
505 free_scram_state(&server->scram_state);
509 slog_error(server, "unknown/unsupported auth method: %d", cmd);
516 bool send_startup_packet(PgSocket *server)
518 PgDatabase *db = server->pool->db;
519 const char *username = server->pool->user->name;
523 pktbuf_write_StartupMessage(pkt, username,
524 db->startup_params->buf,
525 db->startup_params->write_pos);
526 return pktbuf_send_immediate(pkt, server);
529 bool send_sslreq_packet(PgSocket *server)
532 SEND_wrap(16, pktbuf_write_SSLRequest, res, server);
536 int scan_text_result(struct MBuf *pkt, const char *tupdesc, ...)
538 const char *val = NULL;
547 asked = strlen(tupdesc);
548 if (!mbuf_get_uint16be(pkt, &ncol))
551 va_start(ap, tupdesc);
552 for (i = 0; i < asked; i++) {
554 if (!mbuf_get_uint32be(pkt, &len)) {
558 if ((int32_t)len < 0) {
561 if (!mbuf_get_chars(pkt, len, &val)) {
567 /* hack to zero-terminate the result */
569 char *xval = (char *)val - 1;
570 memmove(xval, val, len);
575 /* tuple was shorter than requested */
579 switch (tupdesc[i]) {
581 int_p = va_arg(ap, int *);
582 *int_p = val ? atoi(val) : 0;
585 long_p = va_arg(ap, uint64_t *);
586 *long_p = val ? atoll(val) : 0;
589 str_p = va_arg(ap, const char **);
593 fatal("bad tupdesc: %s", tupdesc);