]> granicus.if.org Git - pgbouncer/blob - src/proto.c
Fix some scan-build warnings
[pgbouncer] / src / proto.c
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  * Pieces that need to have detailed info about protocol.
21  */
22
23 #include "bouncer.h"
24 #include "scram.h"
25
26 /*
27  * parse protocol header from struct MBuf
28  */
29
30 /* parses pkt header from buffer, returns false if failed */
31 bool get_header(struct MBuf *data, PktHdr *pkt)
32 {
33         unsigned type;
34         uint32_t len;
35         unsigned got;
36         unsigned avail;
37         uint16_t len16;
38         uint8_t type8;
39         uint32_t code;
40         struct MBuf hdr;
41         const uint8_t *ptr;
42
43         mbuf_copy(data, &hdr);
44
45         if (mbuf_avail_for_read(&hdr) < NEW_HEADER_LEN) {
46                 log_noise("get_header: less than 5 bytes available");
47                 return false;
48         }
49         if (!mbuf_get_byte(&hdr, &type8))
50                 return false;
51         type = type8;
52         if (type != 0) {
53                 /* wire length does not include type byte */
54                 if (!mbuf_get_uint32be(&hdr, &len))
55                         return false;
56                 len++;
57                 got = NEW_HEADER_LEN;
58         } else {
59                 if (!mbuf_get_byte(&hdr, &type8))
60                         return false;
61                 if (type8 != 0) {
62                         log_noise("get_header: unknown special pkt");
63                         return false;
64                 }
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");
68                         return false;
69                 }
70                 if (!mbuf_get_uint16be(&hdr, &len16))
71                         return false;
72                 len = len16;
73                 if (!mbuf_get_uint32be(&hdr, &code))
74                         return false;
75                 if (code == PKT_CANCEL) {
76                         type = PKT_CANCEL;
77                 } else if (code == PKT_SSLREQ) {
78                         type = PKT_SSLREQ;
79                 } else if (code == PKT_GSSENCREQ) {
80                         type = PKT_GSSENCREQ;
81                 } else if ((code >> 16) == 3 && (code & 0xFFFF) < 2) {
82                         type = PKT_STARTUP;
83                 } else if (code == PKT_STARTUP_V2) {
84                         type = PKT_STARTUP_V2;
85                 } else {
86                         log_noise("get_header: unknown special pkt: len=%u code=%u", len, code);
87                         return false;
88                 }
89                 got = OLD_HEADER_LEN;
90         }
91
92         /* don't believe nonsense */
93         if (len < got || len > cf_max_packet_size)
94                 return false;
95
96         /* store pkt info */
97         pkt->type = type;
98         pkt->len = len;
99
100         /* fill pkt with only data for this packet */
101         if (len > mbuf_avail_for_read(data)) {
102                 avail = mbuf_avail_for_read(data);
103         } else {
104                 avail = len;
105         }
106         if (!mbuf_slice(data, avail, &pkt->data))
107                 return false;
108
109         /* tag header as read */
110         return mbuf_get_bytes(&pkt->data, got, &ptr);
111 }
112
113
114 /*
115  * Send error message packet to client.
116  */
117
118 bool send_pooler_error(PgSocket *client, bool send_ready, const char *msg)
119 {
120         uint8_t tmpbuf[512];
121         PktBuf buf;
122
123         if (cf_log_pooler_errors)
124                 slog_warning(client, "pooler error: %s", msg);
125
126         pktbuf_static(&buf, tmpbuf, sizeof(tmpbuf));
127         pktbuf_write_generic(&buf, 'E', "cscscsc",
128                              'S', "ERROR", 'C', "08P01", 'M', msg, 0);
129         if (send_ready)
130                 pktbuf_write_ReadyForQuery(&buf);
131         return pktbuf_send_immediate(&buf, client);
132 }
133
134 /*
135  * Parse server error message and log it.
136  */
137 void parse_server_error(PktHdr *pkt, const char **level_p, const char **msg_p)
138 {
139         const char *level = NULL, *msg = NULL, *val;
140         uint8_t type;
141         while (mbuf_avail_for_read(&pkt->data)) {
142                 if (!mbuf_get_byte(&pkt->data, &type))
143                         break;
144                 if (type == 0)
145                         break;
146                 if (!mbuf_get_string(&pkt->data, &val))
147                         break;
148                 if (type == 'S') {
149                         level = val;
150                 } else if (type == 'M') {
151                         msg = val;
152                 }
153         }
154         *level_p = level;
155         *msg_p = msg;
156 }
157
158 void log_server_error(const char *note, PktHdr *pkt)
159 {
160         const char *level = NULL, *msg = NULL;
161
162         parse_server_error(pkt, &level, &msg);
163
164         if (!msg || !level) {
165                 log_error("%s: partial error message, cannot log", note);
166         } else {
167                 log_error("%s: %s: %s", note, level, msg);
168         }
169 }
170
171
172 /*
173  * Preparation of welcome message for client connection.
174  */
175
176 /* add another server parameter packet to cache */
177 bool add_welcome_parameter(PgPool *pool, const char *key, const char *val)
178 {
179         PktBuf *msg = pool->welcome_msg;
180
181         if (pool->welcome_msg_ready)
182                 return true;
183
184         if (!msg) {
185                 msg = pktbuf_dynamic(128);
186                 if (!msg)
187                         return false;
188                 pool->welcome_msg = msg;
189         }
190
191         /* first packet must be AuthOk */
192         if (msg->write_pos == 0)
193                 pktbuf_write_AuthenticationOk(msg);
194
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);
198
199         return !msg->failed;
200 }
201
202 /* all parameters processed */
203 void finish_welcome_msg(PgSocket *server)
204 {
205         PgPool *pool = server->pool;
206         if (pool->welcome_msg_ready)
207                 return;
208         pool->welcome_msg_ready = 1;
209 }
210
211 bool welcome_client(PgSocket *client)
212 {
213         int res;
214         PgPool *pool = client->pool;
215         const PktBuf *pmsg = pool->welcome_msg;
216         PktBuf *msg;
217
218         slog_noise(client, "P: welcome_client");
219
220         /* copy prepared stuff around */
221         msg = pktbuf_temp();
222         pktbuf_put_bytes(msg, pmsg->buf, pmsg->write_pos);
223
224         /* fill vars */
225         varcache_fill_unset(&pool->orig_vars, client);
226         varcache_add_params(msg, &client->vars);
227
228         /* give each client its own cancel key */
229         get_random_bytes(client->cancel_key, 8);
230         pktbuf_write_BackendKeyData(msg, client->cancel_key);
231
232         /* finish */
233         pktbuf_write_ReadyForQuery(msg);
234         if (msg->failed) {
235                 disconnect_client(client, true, "failed to prepare welcome message");
236                 return false;
237         }
238
239         /* send all together */
240         res = pktbuf_send_immediate(msg, client);
241         if (!res) {
242                 disconnect_client(client, true, "failed to send welcome message");
243                 return false;
244         }
245         return true;
246 }
247
248 /*
249  * Password authentication for server
250  */
251
252 static PgUser *get_srv_psw(PgSocket *server)
253 {
254         PgDatabase *db = server->pool->db;
255         PgUser *user = server->pool->user;
256
257         /* if forced user without password, use userlist psw */
258         if (!user->passwd[0] && db->forced_user) {
259                 PgUser *u2 = find_user(user->name);
260                 if (u2)
261                         return u2;
262         }
263         return user;
264 }
265
266 /* actual packet send */
267 static bool send_password(PgSocket *server, const char *enc_psw)
268 {
269         bool res;
270         SEND_PasswordMessage(res, server, enc_psw);
271         return res;
272 }
273
274 static bool login_clear_psw(PgSocket *server)
275 {
276         PgUser *user = get_srv_psw(server);
277         slog_debug(server, "P: send clear password");
278         return send_password(server, user->passwd);
279 }
280
281 static bool login_md5_psw(PgSocket *server, const uint8_t *salt)
282 {
283         char txt[MD5_PASSWD_LEN + 1], *src;
284         PgUser *user = get_srv_psw(server);
285
286         slog_debug(server, "P: send md5 password");
287
288         switch (get_password_type(user->passwd)) {
289         case PASSWORD_TYPE_PLAINTEXT:
290                 pg_md5_encrypt(user->passwd, user->name, strlen(user->name), txt);
291                 src = txt + 3;
292                 break;
293         case PASSWORD_TYPE_MD5:
294                 src = user->passwd + 3;
295                 break;
296         default:
297                 slog_error(server, "cannot do MD5 authentication: wrong password type");
298                 kill_pool_logins(server->pool, "server login failed: wrong password type");
299                 return false;
300         }
301
302         pg_md5_encrypt(src, (char *)salt, 4, txt);
303
304         return send_password(server, txt);
305 }
306
307 static bool login_scram_sha_256(PgSocket *server)
308 {
309         PgUser *user = get_srv_psw(server);
310         bool res;
311         char *client_first_message = NULL;
312
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");
316                 return false;
317         }
318
319         if (server->scram_state.client_nonce)
320         {
321                 slog_error(server, "protocol error: duplicate AuthenticationSASL message from server");
322                 return false;
323         }
324
325         client_first_message = build_client_first_message(&server->scram_state);
326         if (!client_first_message)
327                 return false;
328
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);
332
333         free(client_first_message);
334         return res;
335 }
336
337 static bool login_scram_sha_256_cont(PgSocket *server, unsigned datalen, const uint8_t *data)
338 {
339         PgUser *user = get_srv_psw(server);
340         char *ibuf = NULL;
341         char *input;
342         char *server_nonce;
343         int saltlen;
344         char *salt = NULL;
345         int iterations;
346         bool res;
347         char *client_final_message = NULL;
348
349         if (!server->scram_state.client_nonce)
350         {
351                 slog_error(server, "protocol error: AuthenticationSASLContinue without prior AuthenticationSASL");
352                 return false;
353         }
354
355         if (server->scram_state.server_first_message)
356         {
357                 slog_error(server, "SCRAM exchange protocol error: received second AuthenticationSASLContinue");
358                 return false;
359         }
360
361         ibuf = malloc(datalen + 1);
362         if (ibuf == NULL)
363                 return false;
364         memcpy(ibuf, data, datalen);
365         ibuf[datalen] = '\0';
366
367         input = ibuf;
368         slog_debug(server, "SCRAM server-first-message = \"%s\"", input);
369         if (!read_server_first_message(server, input,
370                                        &server_nonce, &salt, &saltlen, &iterations))
371                 goto failed;
372
373         client_final_message = build_client_final_message(&server->scram_state,
374                                                           user->passwd, server_nonce,
375                                                           salt, saltlen, iterations);
376
377         free(salt);
378         free(ibuf);
379
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);
383
384         free(client_final_message);
385         return res;
386 failed:
387         free(salt);
388         free(ibuf);
389         free(client_final_message);
390         return false;
391 }
392
393 static bool login_scram_sha_256_final(PgSocket *server, unsigned datalen, const uint8_t *data)
394 {
395         char *ibuf = NULL;
396         char *input;
397         char ServerSignature[SHA256_DIGEST_LENGTH];
398
399         if (!server->scram_state.server_first_message)
400         {
401                 slog_error(server, "protocol error: AuthenticationSASLFinal without prior AuthenticationSASLContinue");
402                 return false;
403         }
404
405         ibuf = malloc(datalen + 1);
406         if (ibuf == NULL)
407                 return false;
408         memcpy(ibuf, data, datalen);
409         ibuf[datalen] = '\0';
410
411         input = ibuf;
412         slog_debug(server, "SCRAM server-final-message = \"%s\"", input);
413         if (!read_server_final_message(server, input, ServerSignature))
414                 goto failed;
415
416         if (!verify_server_signature(&server->scram_state, ServerSignature))
417         {
418                 slog_error(server, "invalid server signature");
419                 kill_pool_logins(server->pool, "server login failed: invalid server signature");
420                 return false;
421         }
422
423         free(ibuf);
424         return true;
425 failed:
426         free(ibuf);
427         return false;
428 }
429
430 /* answer server authentication request */
431 bool answer_authreq(PgSocket *server, PktHdr *pkt)
432 {
433         uint32_t cmd;
434         const uint8_t *salt;
435         bool res = false;
436
437         /* authreq body must contain 32bit cmd */
438         if (mbuf_avail_for_read(&pkt->data) < 4)
439                 return false;
440
441         if (!mbuf_get_uint32be(&pkt->data, &cmd))
442                 return false;
443         switch (cmd) {
444         case AUTH_OK:
445                 slog_debug(server, "S: auth ok");
446                 res = true;
447                 break;
448         case AUTH_PLAIN:
449                 slog_debug(server, "S: req cleartext password");
450                 res = login_clear_psw(server);
451                 break;
452         case AUTH_MD5:
453                 slog_debug(server, "S: req md5-crypted psw");
454                 if (!mbuf_get_bytes(&pkt->data, 4, &salt))
455                         return false;
456                 res = login_md5_psw(server, salt);
457                 break;
458         case AUTH_SASL:
459         {
460                 bool selected_mechanism = false;
461
462                 slog_debug(server, "S: req SASL");
463
464                 do {
465                         const char *mech;
466
467                         if (!mbuf_get_string(&pkt->data, &mech))
468                                 return false;
469                         if (!mech[0])
470                                 break;
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);
475
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");
479                 } else
480                         res = login_scram_sha_256(server);
481                 break;
482         }
483         case AUTH_SASL_CONT:
484         {
485                 unsigned len;
486                 const uint8_t *data;
487
488                 slog_debug(server, "S: SASL cont");
489                 len = mbuf_avail_for_read(&pkt->data);
490                 if (!mbuf_get_bytes(&pkt->data, len, &data))
491                         return false;
492                 res = login_scram_sha_256_cont(server, len, data);
493                 break;
494         }
495         case AUTH_SASL_FIN:
496         {
497                 unsigned len;
498                 const uint8_t *data;
499
500                 slog_debug(server, "S: SASL final");
501                 len = mbuf_avail_for_read(&pkt->data);
502                 if (!mbuf_get_bytes(&pkt->data, len, &data))
503                         return false;
504                 res = login_scram_sha_256_final(server, len, data);
505                 free_scram_state(&server->scram_state);
506                 break;
507         }
508         default:
509                 slog_error(server, "unknown/unsupported auth method: %d", cmd);
510                 res = false;
511                 break;
512         }
513         return res;
514 }
515
516 bool send_startup_packet(PgSocket *server)
517 {
518         PgDatabase *db = server->pool->db;
519         const char *username = server->pool->user->name;
520         PktBuf *pkt;
521
522         pkt = pktbuf_temp();
523         pktbuf_write_StartupMessage(pkt, username,
524                                     db->startup_params->buf,
525                                     db->startup_params->write_pos);
526         return pktbuf_send_immediate(pkt, server);
527 }
528
529 bool send_sslreq_packet(PgSocket *server)
530 {
531         int res;
532         SEND_wrap(16, pktbuf_write_SSLRequest, res, server);
533         return res;
534 }
535
536 int scan_text_result(struct MBuf *pkt, const char *tupdesc, ...)
537 {
538         const char *val = NULL;
539         uint32_t len;
540         uint16_t ncol;
541         unsigned i, asked;
542         va_list ap;
543         int *int_p;
544         uint64_t *long_p;
545         const char **str_p;
546
547         asked = strlen(tupdesc);
548         if (!mbuf_get_uint16be(pkt, &ncol))
549                 return -1;
550
551         va_start(ap, tupdesc);
552         for (i = 0; i < asked; i++) {
553                 if (i < ncol) {
554                         if (!mbuf_get_uint32be(pkt, &len)) {
555                                 va_end(ap);
556                                 return -1;
557                         }
558                         if ((int32_t)len < 0) {
559                                 val = NULL;
560                         } else {
561                                 if (!mbuf_get_chars(pkt, len, &val)) {
562                                         va_end(ap);
563                                         return -1;
564                                 }
565                         }
566
567                         /* hack to zero-terminate the result */
568                         if (val) {
569                                 char *xval = (char *)val - 1;
570                                 memmove(xval, val, len);
571                                 xval[len] = 0;
572                                 val = xval;
573                         }
574                 } else {
575                         /* tuple was shorter than requested */
576                         val = NULL;
577                 }
578
579                 switch (tupdesc[i]) {
580                 case 'i':
581                         int_p = va_arg(ap, int *);
582                         *int_p = val ? atoi(val) : 0;
583                         break;
584                 case 'q':
585                         long_p = va_arg(ap, uint64_t *);
586                         *long_p = val ? atoll(val) : 0;
587                         break;
588                 case 's':
589                         str_p = va_arg(ap, const char **);
590                         *str_p = val;
591                         break;
592                 default:
593                         fatal("bad tupdesc: %s", tupdesc);
594                 }
595         }
596         va_end(ap);
597
598         return ncol;
599 }