1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * fe-auth.c |
4 | * The front-end (client) authorization routines |
5 | * |
6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
7 | * Portions Copyright (c) 1994, Regents of the University of California |
8 | * |
9 | * IDENTIFICATION |
10 | * src/interfaces/libpq/fe-auth.c |
11 | * |
12 | *------------------------------------------------------------------------- |
13 | */ |
14 | |
15 | /* |
16 | * INTERFACE ROUTINES |
17 | * frontend (client) routines: |
18 | * pg_fe_sendauth send authentication information |
19 | * pg_fe_getauthname get user's name according to the client side |
20 | * of the authentication system |
21 | */ |
22 | |
23 | #include "postgres_fe.h" |
24 | |
25 | #ifdef WIN32 |
26 | #include "win32.h" |
27 | #else |
28 | #include <unistd.h> |
29 | #include <fcntl.h> |
30 | #include <sys/param.h> /* for MAXHOSTNAMELEN on most */ |
31 | #include <sys/socket.h> |
32 | #ifdef HAVE_SYS_UCRED_H |
33 | #include <sys/ucred.h> |
34 | #endif |
35 | #ifndef MAXHOSTNAMELEN |
36 | #include <netdb.h> /* for MAXHOSTNAMELEN on some */ |
37 | #endif |
38 | #include <pwd.h> |
39 | #endif |
40 | |
41 | #include "common/md5.h" |
42 | #include "common/scram-common.h" |
43 | #include "libpq-fe.h" |
44 | #include "fe-auth.h" |
45 | |
46 | |
47 | #ifdef ENABLE_GSS |
48 | /* |
49 | * GSSAPI authentication system. |
50 | */ |
51 | |
52 | #include "fe-gssapi-common.h" |
53 | |
54 | /* |
55 | * Continue GSS authentication with next token as needed. |
56 | */ |
57 | static int |
58 | pg_GSS_continue(PGconn *conn, int payloadlen) |
59 | { |
60 | OM_uint32 maj_stat, |
61 | min_stat, |
62 | lmin_s; |
63 | gss_buffer_desc ginbuf; |
64 | gss_buffer_desc goutbuf; |
65 | |
66 | /* |
67 | * On first call, there's no input token. On subsequent calls, read the |
68 | * input token into a GSS buffer. |
69 | */ |
70 | if (conn->gctx != GSS_C_NO_CONTEXT) |
71 | { |
72 | ginbuf.length = payloadlen; |
73 | ginbuf.value = malloc(payloadlen); |
74 | if (!ginbuf.value) |
75 | { |
76 | printfPQExpBuffer(&conn->errorMessage, |
77 | libpq_gettext("out of memory allocating GSSAPI buffer (%d)\n" ), |
78 | payloadlen); |
79 | return STATUS_ERROR; |
80 | } |
81 | if (pqGetnchar(ginbuf.value, payloadlen, conn)) |
82 | { |
83 | /* |
84 | * Shouldn't happen, because the caller should've ensured that the |
85 | * whole message is already in the input buffer. |
86 | */ |
87 | free(ginbuf.value); |
88 | return STATUS_ERROR; |
89 | } |
90 | } |
91 | else |
92 | { |
93 | ginbuf.length = 0; |
94 | ginbuf.value = NULL; |
95 | } |
96 | |
97 | maj_stat = gss_init_sec_context(&min_stat, |
98 | GSS_C_NO_CREDENTIAL, |
99 | &conn->gctx, |
100 | conn->gtarg_nam, |
101 | GSS_C_NO_OID, |
102 | GSS_C_MUTUAL_FLAG, |
103 | 0, |
104 | GSS_C_NO_CHANNEL_BINDINGS, |
105 | (ginbuf.value == NULL) ? GSS_C_NO_BUFFER : &ginbuf, |
106 | NULL, |
107 | &goutbuf, |
108 | NULL, |
109 | NULL); |
110 | |
111 | if (ginbuf.value) |
112 | free(ginbuf.value); |
113 | |
114 | if (goutbuf.length != 0) |
115 | { |
116 | /* |
117 | * GSS generated data to send to the server. We don't care if it's the |
118 | * first or subsequent packet, just send the same kind of password |
119 | * packet. |
120 | */ |
121 | if (pqPacketSend(conn, 'p', |
122 | goutbuf.value, goutbuf.length) != STATUS_OK) |
123 | { |
124 | gss_release_buffer(&lmin_s, &goutbuf); |
125 | return STATUS_ERROR; |
126 | } |
127 | } |
128 | gss_release_buffer(&lmin_s, &goutbuf); |
129 | |
130 | if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) |
131 | { |
132 | pg_GSS_error(libpq_gettext("GSSAPI continuation error" ), |
133 | conn, |
134 | maj_stat, min_stat); |
135 | gss_release_name(&lmin_s, &conn->gtarg_nam); |
136 | if (conn->gctx) |
137 | gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER); |
138 | return STATUS_ERROR; |
139 | } |
140 | |
141 | if (maj_stat == GSS_S_COMPLETE) |
142 | gss_release_name(&lmin_s, &conn->gtarg_nam); |
143 | |
144 | return STATUS_OK; |
145 | } |
146 | |
147 | /* |
148 | * Send initial GSS authentication token |
149 | */ |
150 | static int |
151 | pg_GSS_startup(PGconn *conn, int payloadlen) |
152 | { |
153 | int ret; |
154 | char *host = conn->connhost[conn->whichhost].host; |
155 | |
156 | if (!(host && host[0] != '\0')) |
157 | { |
158 | printfPQExpBuffer(&conn->errorMessage, |
159 | libpq_gettext("host name must be specified\n" )); |
160 | return STATUS_ERROR; |
161 | } |
162 | |
163 | if (conn->gctx) |
164 | { |
165 | printfPQExpBuffer(&conn->errorMessage, |
166 | libpq_gettext("duplicate GSS authentication request\n" )); |
167 | return STATUS_ERROR; |
168 | } |
169 | |
170 | ret = pg_GSS_load_servicename(conn); |
171 | if (ret != STATUS_OK) |
172 | return ret; |
173 | |
174 | /* |
175 | * Initial packet is the same as a continuation packet with no initial |
176 | * context. |
177 | */ |
178 | conn->gctx = GSS_C_NO_CONTEXT; |
179 | |
180 | return pg_GSS_continue(conn, payloadlen); |
181 | } |
182 | #endif /* ENABLE_GSS */ |
183 | |
184 | |
185 | #ifdef ENABLE_SSPI |
186 | /* |
187 | * SSPI authentication system (Windows only) |
188 | */ |
189 | |
190 | static void |
191 | pg_SSPI_error(PGconn *conn, const char *mprefix, SECURITY_STATUS r) |
192 | { |
193 | char sysmsg[256]; |
194 | |
195 | if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | |
196 | FORMAT_MESSAGE_FROM_SYSTEM, |
197 | NULL, r, 0, |
198 | sysmsg, sizeof(sysmsg), NULL) == 0) |
199 | printfPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x\n" , |
200 | mprefix, (unsigned int) r); |
201 | else |
202 | printfPQExpBuffer(&conn->errorMessage, "%s: %s (%x)\n" , |
203 | mprefix, sysmsg, (unsigned int) r); |
204 | } |
205 | |
206 | /* |
207 | * Continue SSPI authentication with next token as needed. |
208 | */ |
209 | static int |
210 | pg_SSPI_continue(PGconn *conn, int payloadlen) |
211 | { |
212 | SECURITY_STATUS r; |
213 | CtxtHandle newContext; |
214 | ULONG contextAttr; |
215 | SecBufferDesc inbuf; |
216 | SecBufferDesc outbuf; |
217 | SecBuffer OutBuffers[1]; |
218 | SecBuffer InBuffers[1]; |
219 | char *inputbuf = NULL; |
220 | |
221 | if (conn->sspictx != NULL) |
222 | { |
223 | /* |
224 | * On runs other than the first we have some data to send. Put this |
225 | * data in a SecBuffer type structure. |
226 | */ |
227 | inputbuf = malloc(payloadlen); |
228 | if (!inputbuf) |
229 | { |
230 | printfPQExpBuffer(&conn->errorMessage, |
231 | libpq_gettext("out of memory allocating SSPI buffer (%d)\n" ), |
232 | payloadlen); |
233 | return STATUS_ERROR; |
234 | } |
235 | if (pqGetnchar(inputbuf, payloadlen, conn)) |
236 | { |
237 | /* |
238 | * Shouldn't happen, because the caller should've ensured that the |
239 | * whole message is already in the input buffer. |
240 | */ |
241 | free(inputbuf); |
242 | return STATUS_ERROR; |
243 | } |
244 | |
245 | inbuf.ulVersion = SECBUFFER_VERSION; |
246 | inbuf.cBuffers = 1; |
247 | inbuf.pBuffers = InBuffers; |
248 | InBuffers[0].pvBuffer = inputbuf; |
249 | InBuffers[0].cbBuffer = payloadlen; |
250 | InBuffers[0].BufferType = SECBUFFER_TOKEN; |
251 | } |
252 | |
253 | OutBuffers[0].pvBuffer = NULL; |
254 | OutBuffers[0].BufferType = SECBUFFER_TOKEN; |
255 | OutBuffers[0].cbBuffer = 0; |
256 | outbuf.cBuffers = 1; |
257 | outbuf.pBuffers = OutBuffers; |
258 | outbuf.ulVersion = SECBUFFER_VERSION; |
259 | |
260 | r = InitializeSecurityContext(conn->sspicred, |
261 | conn->sspictx, |
262 | conn->sspitarget, |
263 | ISC_REQ_ALLOCATE_MEMORY, |
264 | 0, |
265 | SECURITY_NETWORK_DREP, |
266 | (conn->sspictx == NULL) ? NULL : &inbuf, |
267 | 0, |
268 | &newContext, |
269 | &outbuf, |
270 | &contextAttr, |
271 | NULL); |
272 | |
273 | /* we don't need the input anymore */ |
274 | if (inputbuf) |
275 | free(inputbuf); |
276 | |
277 | if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED) |
278 | { |
279 | pg_SSPI_error(conn, libpq_gettext("SSPI continuation error" ), r); |
280 | |
281 | return STATUS_ERROR; |
282 | } |
283 | |
284 | if (conn->sspictx == NULL) |
285 | { |
286 | /* On first run, transfer retrieved context handle */ |
287 | conn->sspictx = malloc(sizeof(CtxtHandle)); |
288 | if (conn->sspictx == NULL) |
289 | { |
290 | printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n" )); |
291 | return STATUS_ERROR; |
292 | } |
293 | memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle)); |
294 | } |
295 | |
296 | /* |
297 | * If SSPI returned any data to be sent to the server (as it normally |
298 | * would), send this data as a password packet. |
299 | */ |
300 | if (outbuf.cBuffers > 0) |
301 | { |
302 | if (outbuf.cBuffers != 1) |
303 | { |
304 | /* |
305 | * This should never happen, at least not for Kerberos |
306 | * authentication. Keep check in case it shows up with other |
307 | * authentication methods later. |
308 | */ |
309 | printfPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n" ); |
310 | return STATUS_ERROR; |
311 | } |
312 | |
313 | /* |
314 | * If the negotiation is complete, there may be zero bytes to send. |
315 | * The server is at this point not expecting any more data, so don't |
316 | * send it. |
317 | */ |
318 | if (outbuf.pBuffers[0].cbBuffer > 0) |
319 | { |
320 | if (pqPacketSend(conn, 'p', |
321 | outbuf.pBuffers[0].pvBuffer, outbuf.pBuffers[0].cbBuffer)) |
322 | { |
323 | FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); |
324 | return STATUS_ERROR; |
325 | } |
326 | } |
327 | FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); |
328 | } |
329 | |
330 | /* Cleanup is handled by the code in freePGconn() */ |
331 | return STATUS_OK; |
332 | } |
333 | |
334 | /* |
335 | * Send initial SSPI authentication token. |
336 | * If use_negotiate is 0, use kerberos authentication package which is |
337 | * compatible with Unix. If use_negotiate is 1, use the negotiate package |
338 | * which supports both kerberos and NTLM, but is not compatible with Unix. |
339 | */ |
340 | static int |
341 | pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen) |
342 | { |
343 | SECURITY_STATUS r; |
344 | TimeStamp expire; |
345 | char *host = conn->connhost[conn->whichhost].host; |
346 | |
347 | if (conn->sspictx) |
348 | { |
349 | printfPQExpBuffer(&conn->errorMessage, |
350 | libpq_gettext("duplicate SSPI authentication request\n" )); |
351 | return STATUS_ERROR; |
352 | } |
353 | |
354 | /* |
355 | * Retrieve credentials handle |
356 | */ |
357 | conn->sspicred = malloc(sizeof(CredHandle)); |
358 | if (conn->sspicred == NULL) |
359 | { |
360 | printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n" )); |
361 | return STATUS_ERROR; |
362 | } |
363 | |
364 | r = AcquireCredentialsHandle(NULL, |
365 | use_negotiate ? "negotiate" : "kerberos" , |
366 | SECPKG_CRED_OUTBOUND, |
367 | NULL, |
368 | NULL, |
369 | NULL, |
370 | NULL, |
371 | conn->sspicred, |
372 | &expire); |
373 | if (r != SEC_E_OK) |
374 | { |
375 | pg_SSPI_error(conn, libpq_gettext("could not acquire SSPI credentials" ), r); |
376 | free(conn->sspicred); |
377 | conn->sspicred = NULL; |
378 | return STATUS_ERROR; |
379 | } |
380 | |
381 | /* |
382 | * Compute target principal name. SSPI has a different format from GSSAPI, |
383 | * but not more complex. We can skip the @REALM part, because Windows will |
384 | * fill that in for us automatically. |
385 | */ |
386 | if (!(host && host[0] != '\0')) |
387 | { |
388 | printfPQExpBuffer(&conn->errorMessage, |
389 | libpq_gettext("host name must be specified\n" )); |
390 | return STATUS_ERROR; |
391 | } |
392 | conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(host) + 2); |
393 | if (!conn->sspitarget) |
394 | { |
395 | printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n" )); |
396 | return STATUS_ERROR; |
397 | } |
398 | sprintf(conn->sspitarget, "%s/%s" , conn->krbsrvname, host); |
399 | |
400 | /* |
401 | * Indicate that we're in SSPI authentication mode to make sure that |
402 | * pg_SSPI_continue is called next time in the negotiation. |
403 | */ |
404 | conn->usesspi = 1; |
405 | |
406 | return pg_SSPI_continue(conn, payloadlen); |
407 | } |
408 | #endif /* ENABLE_SSPI */ |
409 | |
410 | /* |
411 | * Initialize SASL authentication exchange. |
412 | */ |
413 | static int |
414 | pg_SASL_init(PGconn *conn, int payloadlen) |
415 | { |
416 | char *initialresponse = NULL; |
417 | int initialresponselen; |
418 | bool done; |
419 | bool success; |
420 | const char *selected_mechanism; |
421 | PQExpBufferData mechanism_buf; |
422 | char *password; |
423 | |
424 | initPQExpBuffer(&mechanism_buf); |
425 | |
426 | if (conn->sasl_state) |
427 | { |
428 | printfPQExpBuffer(&conn->errorMessage, |
429 | libpq_gettext("duplicate SASL authentication request\n" )); |
430 | goto error; |
431 | } |
432 | |
433 | /* |
434 | * Parse the list of SASL authentication mechanisms in the |
435 | * AuthenticationSASL message, and select the best mechanism that we |
436 | * support. SCRAM-SHA-256-PLUS and SCRAM-SHA-256 are the only ones |
437 | * supported at the moment, listed by order of decreasing importance. |
438 | */ |
439 | selected_mechanism = NULL; |
440 | for (;;) |
441 | { |
442 | if (pqGets(&mechanism_buf, conn)) |
443 | { |
444 | printfPQExpBuffer(&conn->errorMessage, |
445 | "fe_sendauth: invalid authentication request from server: invalid list of authentication mechanisms\n" ); |
446 | goto error; |
447 | } |
448 | if (PQExpBufferDataBroken(mechanism_buf)) |
449 | goto oom_error; |
450 | |
451 | /* An empty string indicates end of list */ |
452 | if (mechanism_buf.data[0] == '\0') |
453 | break; |
454 | |
455 | /* |
456 | * Select the mechanism to use. Pick SCRAM-SHA-256-PLUS over anything |
457 | * else if a channel binding type is set and if the client supports |
458 | * it. Pick SCRAM-SHA-256 if nothing else has already been picked. If |
459 | * we add more mechanisms, a more refined priority mechanism might |
460 | * become necessary. |
461 | */ |
462 | if (strcmp(mechanism_buf.data, SCRAM_SHA_256_PLUS_NAME) == 0) |
463 | { |
464 | if (conn->ssl_in_use) |
465 | { |
466 | /* |
467 | * The server has offered SCRAM-SHA-256-PLUS, which is only |
468 | * supported by the client if a hash of the peer certificate |
469 | * can be created. |
470 | */ |
471 | #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH |
472 | selected_mechanism = SCRAM_SHA_256_PLUS_NAME; |
473 | #endif |
474 | } |
475 | else |
476 | { |
477 | /* |
478 | * The server offered SCRAM-SHA-256-PLUS, but the connection |
479 | * is not SSL-encrypted. That's not sane. Perhaps SSL was |
480 | * stripped by a proxy? There's no point in continuing, |
481 | * because the server will reject the connection anyway if we |
482 | * try authenticate without channel binding even though both |
483 | * the client and server supported it. The SCRAM exchange |
484 | * checks for that, to prevent downgrade attacks. |
485 | */ |
486 | printfPQExpBuffer(&conn->errorMessage, |
487 | libpq_gettext("server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n" )); |
488 | goto error; |
489 | } |
490 | } |
491 | else if (strcmp(mechanism_buf.data, SCRAM_SHA_256_NAME) == 0 && |
492 | !selected_mechanism) |
493 | selected_mechanism = SCRAM_SHA_256_NAME; |
494 | } |
495 | |
496 | if (!selected_mechanism) |
497 | { |
498 | printfPQExpBuffer(&conn->errorMessage, |
499 | libpq_gettext("none of the server's SASL authentication mechanisms are supported\n" )); |
500 | goto error; |
501 | } |
502 | |
503 | /* |
504 | * Now that the SASL mechanism has been chosen for the exchange, |
505 | * initialize its state information. |
506 | */ |
507 | |
508 | /* |
509 | * First, select the password to use for the exchange, complaining if |
510 | * there isn't one. Currently, all supported SASL mechanisms require a |
511 | * password, so we can just go ahead here without further distinction. |
512 | */ |
513 | conn->password_needed = true; |
514 | password = conn->connhost[conn->whichhost].password; |
515 | if (password == NULL) |
516 | password = conn->pgpass; |
517 | if (password == NULL || password[0] == '\0') |
518 | { |
519 | printfPQExpBuffer(&conn->errorMessage, |
520 | PQnoPasswordSupplied); |
521 | goto error; |
522 | } |
523 | |
524 | /* |
525 | * Initialize the SASL state information with all the information gathered |
526 | * during the initial exchange. |
527 | * |
528 | * Note: Only tls-unique is supported for the moment. |
529 | */ |
530 | conn->sasl_state = pg_fe_scram_init(conn, |
531 | password, |
532 | selected_mechanism); |
533 | if (!conn->sasl_state) |
534 | goto oom_error; |
535 | |
536 | /* Get the mechanism-specific Initial Client Response, if any */ |
537 | pg_fe_scram_exchange(conn->sasl_state, |
538 | NULL, -1, |
539 | &initialresponse, &initialresponselen, |
540 | &done, &success); |
541 | |
542 | if (done && !success) |
543 | goto error; |
544 | |
545 | /* |
546 | * Build a SASLInitialResponse message, and send it. |
547 | */ |
548 | if (pqPutMsgStart('p', true, conn)) |
549 | goto error; |
550 | if (pqPuts(selected_mechanism, conn)) |
551 | goto error; |
552 | if (initialresponse) |
553 | { |
554 | if (pqPutInt(initialresponselen, 4, conn)) |
555 | goto error; |
556 | if (pqPutnchar(initialresponse, initialresponselen, conn)) |
557 | goto error; |
558 | } |
559 | if (pqPutMsgEnd(conn)) |
560 | goto error; |
561 | if (pqFlush(conn)) |
562 | goto error; |
563 | |
564 | termPQExpBuffer(&mechanism_buf); |
565 | if (initialresponse) |
566 | free(initialresponse); |
567 | |
568 | return STATUS_OK; |
569 | |
570 | error: |
571 | termPQExpBuffer(&mechanism_buf); |
572 | if (initialresponse) |
573 | free(initialresponse); |
574 | return STATUS_ERROR; |
575 | |
576 | oom_error: |
577 | termPQExpBuffer(&mechanism_buf); |
578 | if (initialresponse) |
579 | free(initialresponse); |
580 | printfPQExpBuffer(&conn->errorMessage, |
581 | libpq_gettext("out of memory\n" )); |
582 | return STATUS_ERROR; |
583 | } |
584 | |
585 | /* |
586 | * Exchange a message for SASL communication protocol with the backend. |
587 | * This should be used after calling pg_SASL_init to set up the status of |
588 | * the protocol. |
589 | */ |
590 | static int |
591 | pg_SASL_continue(PGconn *conn, int payloadlen, bool final) |
592 | { |
593 | char *output; |
594 | int outputlen; |
595 | bool done; |
596 | bool success; |
597 | int res; |
598 | char *challenge; |
599 | |
600 | /* Read the SASL challenge from the AuthenticationSASLContinue message. */ |
601 | challenge = malloc(payloadlen + 1); |
602 | if (!challenge) |
603 | { |
604 | printfPQExpBuffer(&conn->errorMessage, |
605 | libpq_gettext("out of memory allocating SASL buffer (%d)\n" ), |
606 | payloadlen); |
607 | return STATUS_ERROR; |
608 | } |
609 | |
610 | if (pqGetnchar(challenge, payloadlen, conn)) |
611 | { |
612 | free(challenge); |
613 | return STATUS_ERROR; |
614 | } |
615 | /* For safety and convenience, ensure the buffer is NULL-terminated. */ |
616 | challenge[payloadlen] = '\0'; |
617 | |
618 | pg_fe_scram_exchange(conn->sasl_state, |
619 | challenge, payloadlen, |
620 | &output, &outputlen, |
621 | &done, &success); |
622 | free(challenge); /* don't need the input anymore */ |
623 | |
624 | if (final && !done) |
625 | { |
626 | if (outputlen != 0) |
627 | free(output); |
628 | |
629 | printfPQExpBuffer(&conn->errorMessage, |
630 | libpq_gettext("AuthenticationSASLFinal received from server, but SASL authentication was not completed\n" )); |
631 | return STATUS_ERROR; |
632 | } |
633 | if (outputlen != 0) |
634 | { |
635 | /* |
636 | * Send the SASL response to the server. |
637 | */ |
638 | res = pqPacketSend(conn, 'p', output, outputlen); |
639 | free(output); |
640 | |
641 | if (res != STATUS_OK) |
642 | return STATUS_ERROR; |
643 | } |
644 | |
645 | if (done && !success) |
646 | return STATUS_ERROR; |
647 | |
648 | return STATUS_OK; |
649 | } |
650 | |
651 | /* |
652 | * Respond to AUTH_REQ_SCM_CREDS challenge. |
653 | * |
654 | * Note: this is dead code as of Postgres 9.1, because current backends will |
655 | * never send this challenge. But we must keep it as long as libpq needs to |
656 | * interoperate with pre-9.1 servers. It is believed to be needed only on |
657 | * Debian/kFreeBSD (ie, FreeBSD kernel with Linux userland, so that the |
658 | * getpeereid() function isn't provided by libc). |
659 | */ |
660 | static int |
661 | pg_local_sendauth(PGconn *conn) |
662 | { |
663 | #ifdef HAVE_STRUCT_CMSGCRED |
664 | char buf; |
665 | struct iovec iov; |
666 | struct msghdr msg; |
667 | struct cmsghdr *cmsg; |
668 | union |
669 | { |
670 | struct cmsghdr hdr; |
671 | unsigned char buf[CMSG_SPACE(sizeof(struct cmsgcred))]; |
672 | } cmsgbuf; |
673 | |
674 | /* |
675 | * The backend doesn't care what we send here, but it wants exactly one |
676 | * character to force recvmsg() to block and wait for us. |
677 | */ |
678 | buf = '\0'; |
679 | iov.iov_base = &buf; |
680 | iov.iov_len = 1; |
681 | |
682 | memset(&msg, 0, sizeof(msg)); |
683 | msg.msg_iov = &iov; |
684 | msg.msg_iovlen = 1; |
685 | |
686 | /* We must set up a message that will be filled in by kernel */ |
687 | memset(&cmsgbuf, 0, sizeof(cmsgbuf)); |
688 | msg.msg_control = &cmsgbuf.buf; |
689 | msg.msg_controllen = sizeof(cmsgbuf.buf); |
690 | cmsg = CMSG_FIRSTHDR(&msg); |
691 | cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); |
692 | cmsg->cmsg_level = SOL_SOCKET; |
693 | cmsg->cmsg_type = SCM_CREDS; |
694 | |
695 | if (sendmsg(conn->sock, &msg, 0) == -1) |
696 | { |
697 | char sebuf[PG_STRERROR_R_BUFLEN]; |
698 | |
699 | printfPQExpBuffer(&conn->errorMessage, |
700 | "pg_local_sendauth: sendmsg: %s\n" , |
701 | strerror_r(errno, sebuf, sizeof(sebuf))); |
702 | return STATUS_ERROR; |
703 | } |
704 | return STATUS_OK; |
705 | #else |
706 | printfPQExpBuffer(&conn->errorMessage, |
707 | libpq_gettext("SCM_CRED authentication method not supported\n" )); |
708 | return STATUS_ERROR; |
709 | #endif |
710 | } |
711 | |
712 | static int |
713 | pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq) |
714 | { |
715 | int ret; |
716 | char *crypt_pwd = NULL; |
717 | const char *pwd_to_send; |
718 | char md5Salt[4]; |
719 | |
720 | /* Read the salt from the AuthenticationMD5Password message. */ |
721 | if (areq == AUTH_REQ_MD5) |
722 | { |
723 | if (pqGetnchar(md5Salt, 4, conn)) |
724 | return STATUS_ERROR; /* shouldn't happen */ |
725 | } |
726 | |
727 | /* Encrypt the password if needed. */ |
728 | |
729 | switch (areq) |
730 | { |
731 | case AUTH_REQ_MD5: |
732 | { |
733 | char *crypt_pwd2; |
734 | |
735 | /* Allocate enough space for two MD5 hashes */ |
736 | crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1)); |
737 | if (!crypt_pwd) |
738 | { |
739 | printfPQExpBuffer(&conn->errorMessage, |
740 | libpq_gettext("out of memory\n" )); |
741 | return STATUS_ERROR; |
742 | } |
743 | |
744 | crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1; |
745 | if (!pg_md5_encrypt(password, conn->pguser, |
746 | strlen(conn->pguser), crypt_pwd2)) |
747 | { |
748 | free(crypt_pwd); |
749 | return STATUS_ERROR; |
750 | } |
751 | if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5" ), md5Salt, |
752 | 4, crypt_pwd)) |
753 | { |
754 | free(crypt_pwd); |
755 | return STATUS_ERROR; |
756 | } |
757 | |
758 | pwd_to_send = crypt_pwd; |
759 | break; |
760 | } |
761 | case AUTH_REQ_PASSWORD: |
762 | pwd_to_send = password; |
763 | break; |
764 | default: |
765 | return STATUS_ERROR; |
766 | } |
767 | /* Packet has a message type as of protocol 3.0 */ |
768 | if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) |
769 | ret = pqPacketSend(conn, 'p', pwd_to_send, strlen(pwd_to_send) + 1); |
770 | else |
771 | ret = pqPacketSend(conn, 0, pwd_to_send, strlen(pwd_to_send) + 1); |
772 | if (crypt_pwd) |
773 | free(crypt_pwd); |
774 | return ret; |
775 | } |
776 | |
777 | /* |
778 | * pg_fe_sendauth |
779 | * client demux routine for processing an authentication request |
780 | * |
781 | * The server has sent us an authentication challenge (or OK). Send an |
782 | * appropriate response. The caller has ensured that the whole message is |
783 | * now in the input buffer, and has already read the type and length of |
784 | * it. We are responsible for reading any remaining extra data, specific |
785 | * to the authentication method. 'payloadlen' is the remaining length in |
786 | * the message. |
787 | */ |
788 | int |
789 | pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn) |
790 | { |
791 | switch (areq) |
792 | { |
793 | case AUTH_REQ_OK: |
794 | break; |
795 | |
796 | case AUTH_REQ_KRB4: |
797 | printfPQExpBuffer(&conn->errorMessage, |
798 | libpq_gettext("Kerberos 4 authentication not supported\n" )); |
799 | return STATUS_ERROR; |
800 | |
801 | case AUTH_REQ_KRB5: |
802 | printfPQExpBuffer(&conn->errorMessage, |
803 | libpq_gettext("Kerberos 5 authentication not supported\n" )); |
804 | return STATUS_ERROR; |
805 | |
806 | #if defined(ENABLE_GSS) || defined(ENABLE_SSPI) |
807 | case AUTH_REQ_GSS: |
808 | #if !defined(ENABLE_SSPI) |
809 | /* no native SSPI, so use GSSAPI library for it */ |
810 | case AUTH_REQ_SSPI: |
811 | #endif |
812 | { |
813 | int r; |
814 | |
815 | pglock_thread(); |
816 | |
817 | /* |
818 | * If we have both GSS and SSPI support compiled in, use SSPI |
819 | * support by default. This is overridable by a connection |
820 | * string parameter. Note that when using SSPI we still leave |
821 | * the negotiate parameter off, since we want SSPI to use the |
822 | * GSSAPI kerberos protocol. For actual SSPI negotiate |
823 | * protocol, we use AUTH_REQ_SSPI. |
824 | */ |
825 | #if defined(ENABLE_GSS) && defined(ENABLE_SSPI) |
826 | if (conn->gsslib && (pg_strcasecmp(conn->gsslib, "gssapi" ) == 0)) |
827 | r = pg_GSS_startup(conn, payloadlen); |
828 | else |
829 | r = pg_SSPI_startup(conn, 0, payloadlen); |
830 | #elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI) |
831 | r = pg_GSS_startup(conn, payloadlen); |
832 | #elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI) |
833 | r = pg_SSPI_startup(conn, 0, payloadlen); |
834 | #endif |
835 | if (r != STATUS_OK) |
836 | { |
837 | /* Error message already filled in. */ |
838 | pgunlock_thread(); |
839 | return STATUS_ERROR; |
840 | } |
841 | pgunlock_thread(); |
842 | } |
843 | break; |
844 | |
845 | case AUTH_REQ_GSS_CONT: |
846 | { |
847 | int r; |
848 | |
849 | pglock_thread(); |
850 | #if defined(ENABLE_GSS) && defined(ENABLE_SSPI) |
851 | if (conn->usesspi) |
852 | r = pg_SSPI_continue(conn, payloadlen); |
853 | else |
854 | r = pg_GSS_continue(conn, payloadlen); |
855 | #elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI) |
856 | r = pg_GSS_continue(conn, payloadlen); |
857 | #elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI) |
858 | r = pg_SSPI_continue(conn, payloadlen); |
859 | #endif |
860 | if (r != STATUS_OK) |
861 | { |
862 | /* Error message already filled in. */ |
863 | pgunlock_thread(); |
864 | return STATUS_ERROR; |
865 | } |
866 | pgunlock_thread(); |
867 | } |
868 | break; |
869 | #else /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */ |
870 | /* No GSSAPI *or* SSPI support */ |
871 | case AUTH_REQ_GSS: |
872 | case AUTH_REQ_GSS_CONT: |
873 | printfPQExpBuffer(&conn->errorMessage, |
874 | libpq_gettext("GSSAPI authentication not supported\n" )); |
875 | return STATUS_ERROR; |
876 | #endif /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */ |
877 | |
878 | #ifdef ENABLE_SSPI |
879 | case AUTH_REQ_SSPI: |
880 | |
881 | /* |
882 | * SSPI has its own startup message so libpq can decide which |
883 | * method to use. Indicate to pg_SSPI_startup that we want SSPI |
884 | * negotiation instead of Kerberos. |
885 | */ |
886 | pglock_thread(); |
887 | if (pg_SSPI_startup(conn, 1, payloadlen) != STATUS_OK) |
888 | { |
889 | /* Error message already filled in. */ |
890 | pgunlock_thread(); |
891 | return STATUS_ERROR; |
892 | } |
893 | pgunlock_thread(); |
894 | break; |
895 | #else |
896 | |
897 | /* |
898 | * No SSPI support. However, if we have GSSAPI but not SSPI |
899 | * support, AUTH_REQ_SSPI will have been handled in the codepath |
900 | * for AUTH_REQ_GSS above, so don't duplicate the case label in |
901 | * that case. |
902 | */ |
903 | #if !defined(ENABLE_GSS) |
904 | case AUTH_REQ_SSPI: |
905 | printfPQExpBuffer(&conn->errorMessage, |
906 | libpq_gettext("SSPI authentication not supported\n" )); |
907 | return STATUS_ERROR; |
908 | #endif /* !define(ENABLE_GSS) */ |
909 | #endif /* ENABLE_SSPI */ |
910 | |
911 | |
912 | case AUTH_REQ_CRYPT: |
913 | printfPQExpBuffer(&conn->errorMessage, |
914 | libpq_gettext("Crypt authentication not supported\n" )); |
915 | return STATUS_ERROR; |
916 | |
917 | case AUTH_REQ_MD5: |
918 | case AUTH_REQ_PASSWORD: |
919 | { |
920 | char *password; |
921 | |
922 | conn->password_needed = true; |
923 | password = conn->connhost[conn->whichhost].password; |
924 | if (password == NULL) |
925 | password = conn->pgpass; |
926 | if (password == NULL || password[0] == '\0') |
927 | { |
928 | printfPQExpBuffer(&conn->errorMessage, |
929 | PQnoPasswordSupplied); |
930 | return STATUS_ERROR; |
931 | } |
932 | if (pg_password_sendauth(conn, password, areq) != STATUS_OK) |
933 | { |
934 | printfPQExpBuffer(&conn->errorMessage, |
935 | "fe_sendauth: error sending password authentication\n" ); |
936 | return STATUS_ERROR; |
937 | } |
938 | break; |
939 | } |
940 | |
941 | case AUTH_REQ_SASL: |
942 | |
943 | /* |
944 | * The request contains the name (as assigned by IANA) of the |
945 | * authentication mechanism. |
946 | */ |
947 | if (pg_SASL_init(conn, payloadlen) != STATUS_OK) |
948 | { |
949 | /* pg_SASL_init already set the error message */ |
950 | return STATUS_ERROR; |
951 | } |
952 | break; |
953 | |
954 | case AUTH_REQ_SASL_CONT: |
955 | case AUTH_REQ_SASL_FIN: |
956 | if (conn->sasl_state == NULL) |
957 | { |
958 | printfPQExpBuffer(&conn->errorMessage, |
959 | "fe_sendauth: invalid authentication request from server: AUTH_REQ_SASL_CONT without AUTH_REQ_SASL\n" ); |
960 | return STATUS_ERROR; |
961 | } |
962 | if (pg_SASL_continue(conn, payloadlen, |
963 | (areq == AUTH_REQ_SASL_FIN)) != STATUS_OK) |
964 | { |
965 | /* Use error message, if set already */ |
966 | if (conn->errorMessage.len == 0) |
967 | printfPQExpBuffer(&conn->errorMessage, |
968 | "fe_sendauth: error in SASL authentication\n" ); |
969 | return STATUS_ERROR; |
970 | } |
971 | break; |
972 | |
973 | case AUTH_REQ_SCM_CREDS: |
974 | if (pg_local_sendauth(conn) != STATUS_OK) |
975 | return STATUS_ERROR; |
976 | break; |
977 | |
978 | default: |
979 | printfPQExpBuffer(&conn->errorMessage, |
980 | libpq_gettext("authentication method %u not supported\n" ), areq); |
981 | return STATUS_ERROR; |
982 | } |
983 | |
984 | return STATUS_OK; |
985 | } |
986 | |
987 | |
988 | /* |
989 | * pg_fe_getauthname |
990 | * |
991 | * Returns a pointer to malloc'd space containing whatever name the user |
992 | * has authenticated to the system. If there is an error, return NULL, |
993 | * and put a suitable error message in *errorMessage if that's not NULL. |
994 | */ |
995 | char * |
996 | pg_fe_getauthname(PQExpBuffer errorMessage) |
997 | { |
998 | char *result = NULL; |
999 | const char *name = NULL; |
1000 | |
1001 | #ifdef WIN32 |
1002 | /* Microsoft recommends buffer size of UNLEN+1, where UNLEN = 256 */ |
1003 | char username[256 + 1]; |
1004 | DWORD namesize = sizeof(username); |
1005 | #else |
1006 | uid_t user_id = geteuid(); |
1007 | char pwdbuf[BUFSIZ]; |
1008 | struct passwd pwdstr; |
1009 | struct passwd *pw = NULL; |
1010 | int pwerr; |
1011 | #endif |
1012 | |
1013 | /* |
1014 | * Some users are using configure --enable-thread-safety-force, so we |
1015 | * might as well do the locking within our library to protect |
1016 | * pqGetpwuid(). In fact, application developers can use getpwuid() in |
1017 | * their application if they use the locking call we provide, or install |
1018 | * their own locking function using PQregisterThreadLock(). |
1019 | */ |
1020 | pglock_thread(); |
1021 | |
1022 | #ifdef WIN32 |
1023 | if (GetUserName(username, &namesize)) |
1024 | name = username; |
1025 | else if (errorMessage) |
1026 | printfPQExpBuffer(errorMessage, |
1027 | libpq_gettext("user name lookup failure: error code %lu\n" ), |
1028 | GetLastError()); |
1029 | #else |
1030 | pwerr = pqGetpwuid(user_id, &pwdstr, pwdbuf, sizeof(pwdbuf), &pw); |
1031 | if (pw != NULL) |
1032 | name = pw->pw_name; |
1033 | else if (errorMessage) |
1034 | { |
1035 | if (pwerr != 0) |
1036 | printfPQExpBuffer(errorMessage, |
1037 | libpq_gettext("could not look up local user ID %d: %s\n" ), |
1038 | (int) user_id, |
1039 | strerror_r(pwerr, pwdbuf, sizeof(pwdbuf))); |
1040 | else |
1041 | printfPQExpBuffer(errorMessage, |
1042 | libpq_gettext("local user with ID %d does not exist\n" ), |
1043 | (int) user_id); |
1044 | } |
1045 | #endif |
1046 | |
1047 | if (name) |
1048 | { |
1049 | result = strdup(name); |
1050 | if (result == NULL && errorMessage) |
1051 | printfPQExpBuffer(errorMessage, |
1052 | libpq_gettext("out of memory\n" )); |
1053 | } |
1054 | |
1055 | pgunlock_thread(); |
1056 | |
1057 | return result; |
1058 | } |
1059 | |
1060 | |
1061 | /* |
1062 | * PQencryptPassword -- exported routine to encrypt a password with MD5 |
1063 | * |
1064 | * This function is equivalent to calling PQencryptPasswordConn with |
1065 | * "md5" as the encryption method, except that this doesn't require |
1066 | * a connection object. This function is deprecated, use |
1067 | * PQencryptPasswordConn instead. |
1068 | */ |
1069 | char * |
1070 | PQencryptPassword(const char *passwd, const char *user) |
1071 | { |
1072 | char *crypt_pwd; |
1073 | |
1074 | crypt_pwd = malloc(MD5_PASSWD_LEN + 1); |
1075 | if (!crypt_pwd) |
1076 | return NULL; |
1077 | |
1078 | if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd)) |
1079 | { |
1080 | free(crypt_pwd); |
1081 | return NULL; |
1082 | } |
1083 | |
1084 | return crypt_pwd; |
1085 | } |
1086 | |
1087 | /* |
1088 | * PQencryptPasswordConn -- exported routine to encrypt a password |
1089 | * |
1090 | * This is intended to be used by client applications that wish to send |
1091 | * commands like ALTER USER joe PASSWORD 'pwd'. The password need not |
1092 | * be sent in cleartext if it is encrypted on the client side. This is |
1093 | * good because it ensures the cleartext password won't end up in logs, |
1094 | * pg_stat displays, etc. We export the function so that clients won't |
1095 | * be dependent on low-level details like whether the encryption is MD5 |
1096 | * or something else. |
1097 | * |
1098 | * Arguments are a connection object, the cleartext password, the SQL |
1099 | * name of the user it is for, and a string indicating the algorithm to |
1100 | * use for encrypting the password. If algorithm is NULL, this queries |
1101 | * the server for the current 'password_encryption' value. If you wish |
1102 | * to avoid that, e.g. to avoid blocking, you can execute |
1103 | * 'show password_encryption' yourself before calling this function, and |
1104 | * pass it as the algorithm. |
1105 | * |
1106 | * Return value is a malloc'd string. The client may assume the string |
1107 | * doesn't contain any special characters that would require escaping. |
1108 | * On error, an error message is stored in the connection object, and |
1109 | * returns NULL. |
1110 | */ |
1111 | char * |
1112 | PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, |
1113 | const char *algorithm) |
1114 | { |
1115 | #define MAX_ALGORITHM_NAME_LEN 50 |
1116 | char algobuf[MAX_ALGORITHM_NAME_LEN + 1]; |
1117 | char *crypt_pwd = NULL; |
1118 | |
1119 | if (!conn) |
1120 | return NULL; |
1121 | |
1122 | /* If no algorithm was given, ask the server. */ |
1123 | if (algorithm == NULL) |
1124 | { |
1125 | PGresult *res; |
1126 | char *val; |
1127 | |
1128 | res = PQexec(conn, "show password_encryption" ); |
1129 | if (res == NULL) |
1130 | { |
1131 | /* PQexec() should've set conn->errorMessage already */ |
1132 | return NULL; |
1133 | } |
1134 | if (PQresultStatus(res) != PGRES_TUPLES_OK) |
1135 | { |
1136 | /* PQexec() should've set conn->errorMessage already */ |
1137 | PQclear(res); |
1138 | return NULL; |
1139 | } |
1140 | if (PQntuples(res) != 1 || PQnfields(res) != 1) |
1141 | { |
1142 | PQclear(res); |
1143 | printfPQExpBuffer(&conn->errorMessage, |
1144 | libpq_gettext("unexpected shape of result set returned for SHOW\n" )); |
1145 | return NULL; |
1146 | } |
1147 | val = PQgetvalue(res, 0, 0); |
1148 | |
1149 | if (strlen(val) > MAX_ALGORITHM_NAME_LEN) |
1150 | { |
1151 | PQclear(res); |
1152 | printfPQExpBuffer(&conn->errorMessage, |
1153 | libpq_gettext("password_encryption value too long\n" )); |
1154 | return NULL; |
1155 | } |
1156 | strcpy(algobuf, val); |
1157 | PQclear(res); |
1158 | |
1159 | algorithm = algobuf; |
1160 | } |
1161 | |
1162 | /* |
1163 | * Also accept "on" and "off" as aliases for "md5", because |
1164 | * password_encryption was a boolean before PostgreSQL 10. We refuse to |
1165 | * send the password in plaintext even if it was "off". |
1166 | */ |
1167 | if (strcmp(algorithm, "on" ) == 0 || |
1168 | strcmp(algorithm, "off" ) == 0) |
1169 | algorithm = "md5" ; |
1170 | |
1171 | /* |
1172 | * Ok, now we know what algorithm to use |
1173 | */ |
1174 | if (strcmp(algorithm, "scram-sha-256" ) == 0) |
1175 | { |
1176 | crypt_pwd = pg_fe_scram_build_verifier(passwd); |
1177 | } |
1178 | else if (strcmp(algorithm, "md5" ) == 0) |
1179 | { |
1180 | crypt_pwd = malloc(MD5_PASSWD_LEN + 1); |
1181 | if (crypt_pwd) |
1182 | { |
1183 | if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd)) |
1184 | { |
1185 | free(crypt_pwd); |
1186 | crypt_pwd = NULL; |
1187 | } |
1188 | } |
1189 | } |
1190 | else |
1191 | { |
1192 | printfPQExpBuffer(&conn->errorMessage, |
1193 | libpq_gettext("unrecognized password encryption algorithm \"%s\"\n" ), |
1194 | algorithm); |
1195 | return NULL; |
1196 | } |
1197 | |
1198 | if (!crypt_pwd) |
1199 | printfPQExpBuffer(&conn->errorMessage, |
1200 | libpq_gettext("out of memory\n" )); |
1201 | |
1202 | return crypt_pwd; |
1203 | } |
1204 | |