Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * fe-auth.c
4 : : * The front-end (client) authorization routines
5 : : *
6 : : * Portions Copyright (c) 1996-2024, 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 <limits.h>
31 : : #include <sys/param.h> /* for MAXHOSTNAMELEN on most */
32 : : #include <sys/socket.h>
33 : : #ifdef HAVE_SYS_UCRED_H
34 : : #include <sys/ucred.h>
35 : : #endif
36 : : #ifndef MAXHOSTNAMELEN
37 : : #include <netdb.h> /* for MAXHOSTNAMELEN on some */
38 : : #endif
39 : : #endif
40 : :
41 : : #include "common/md5.h"
42 : : #include "common/scram-common.h"
43 : : #include "fe-auth.h"
44 : : #include "fe-auth-sasl.h"
45 : : #include "libpq-fe.h"
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
2558 heikki.linnakangas@i 58 :CBC 23 : pg_GSS_continue(PGconn *conn, int payloadlen)
59 : : {
60 : : OM_uint32 maj_stat,
61 : : min_stat,
62 : : lmin_s,
367 sfrost@snowman.net 63 : 23 : gss_flags = GSS_C_MUTUAL_FLAG;
64 : : gss_buffer_desc ginbuf;
65 : : gss_buffer_desc goutbuf;
66 : :
67 : : /*
68 : : * On first call, there's no input token. On subsequent calls, read the
69 : : * input token into a GSS buffer.
70 : : */
2558 heikki.linnakangas@i 71 [ + + ]: 23 : if (conn->gctx != GSS_C_NO_CONTEXT)
72 : : {
73 : 11 : ginbuf.length = payloadlen;
74 : 11 : ginbuf.value = malloc(payloadlen);
75 [ - + ]: 11 : if (!ginbuf.value)
76 : : {
516 peter@eisentraut.org 77 :UBC 0 : libpq_append_conn_error(conn, "out of memory allocating GSSAPI buffer (%d)",
78 : : payloadlen);
2558 heikki.linnakangas@i 79 : 0 : return STATUS_ERROR;
80 : : }
2558 heikki.linnakangas@i 81 [ - + ]:CBC 11 : 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 : : */
2558 heikki.linnakangas@i 87 :UBC 0 : free(ginbuf.value);
88 : 0 : return STATUS_ERROR;
89 : : }
90 : : }
91 : : else
92 : : {
2503 heikki.linnakangas@i 93 :CBC 12 : ginbuf.length = 0;
94 : 12 : ginbuf.value = NULL;
95 : : }
96 : :
97 : : /* Only try to acquire credentials if GSS delegation isn't disabled. */
367 sfrost@snowman.net 98 [ + + ]: 23 : if (!pg_GSS_have_cred_cache(&conn->gcred))
99 : 1 : conn->gcred = GSS_C_NO_CREDENTIAL;
100 : :
328 tgl@sss.pgh.pa.us 101 [ + - + + ]: 23 : if (conn->gssdelegation && conn->gssdelegation[0] == '1')
367 sfrost@snowman.net 102 : 12 : gss_flags |= GSS_C_DELEG_FLAG;
103 : :
6123 magnus@hagander.net 104 : 23 : maj_stat = gss_init_sec_context(&min_stat,
105 : : conn->gcred,
106 : : &conn->gctx,
107 : : conn->gtarg_nam,
108 : : GSS_C_NO_OID,
109 : : gss_flags,
110 : : 0,
111 : : GSS_C_NO_CHANNEL_BINDINGS,
2489 tgl@sss.pgh.pa.us 112 [ + + ]: 23 : (ginbuf.value == NULL) ? GSS_C_NO_BUFFER : &ginbuf,
113 : : NULL,
114 : : &goutbuf,
115 : : NULL,
116 : : NULL);
117 : :
668 peter@eisentraut.org 118 : 23 : free(ginbuf.value);
119 : :
2558 heikki.linnakangas@i 120 [ + + ]: 23 : if (goutbuf.length != 0)
121 : : {
122 : : /*
123 : : * GSS generated data to send to the server. We don't care if it's the
124 : : * first or subsequent packet, just send the same kind of password
125 : : * packet.
126 : : */
6123 magnus@hagander.net 127 [ - + ]: 11 : if (pqPacketSend(conn, 'p',
2558 heikki.linnakangas@i 128 : 11 : goutbuf.value, goutbuf.length) != STATUS_OK)
129 : : {
2558 heikki.linnakangas@i 130 :UBC 0 : gss_release_buffer(&lmin_s, &goutbuf);
6123 magnus@hagander.net 131 : 0 : return STATUS_ERROR;
132 : : }
133 : : }
2558 heikki.linnakangas@i 134 :CBC 23 : gss_release_buffer(&lmin_s, &goutbuf);
135 : :
6123 magnus@hagander.net 136 [ + + + + ]: 23 : if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
137 : : {
138 : 1 : pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
139 : : conn,
140 : : maj_stat, min_stat);
141 : 1 : gss_release_name(&lmin_s, &conn->gtarg_nam);
142 [ - + ]: 1 : if (conn->gctx)
6123 magnus@hagander.net 143 :UBC 0 : gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
6123 magnus@hagander.net 144 :CBC 1 : return STATUS_ERROR;
145 : : }
146 : :
147 [ + + ]: 22 : if (maj_stat == GSS_S_COMPLETE)
148 : : {
397 michael@paquier.xyz 149 : 11 : conn->client_finished_auth = true;
6123 magnus@hagander.net 150 : 11 : gss_release_name(&lmin_s, &conn->gtarg_nam);
367 sfrost@snowman.net 151 : 11 : conn->gssapi_used = true;
152 : : }
153 : :
6123 magnus@hagander.net 154 : 22 : return STATUS_OK;
155 : : }
156 : :
157 : : /*
158 : : * Send initial GSS authentication token
159 : : */
160 : : static int
2558 heikki.linnakangas@i 161 : 12 : pg_GSS_startup(PGconn *conn, int payloadlen)
162 : : {
163 : : int ret;
2081 tgl@sss.pgh.pa.us 164 : 12 : char *host = conn->connhost[conn->whichhost].host;
165 : :
2700 rhaas@postgresql.org 166 [ + - - + ]: 12 : if (!(host && host[0] != '\0'))
167 : : {
516 peter@eisentraut.org 168 :UBC 0 : libpq_append_conn_error(conn, "host name must be specified");
5151 magnus@hagander.net 169 : 0 : return STATUS_ERROR;
170 : : }
171 : :
6123 magnus@hagander.net 172 [ - + ]:CBC 12 : if (conn->gctx)
173 : : {
516 peter@eisentraut.org 174 :UBC 0 : libpq_append_conn_error(conn, "duplicate GSS authentication request");
6123 magnus@hagander.net 175 : 0 : return STATUS_ERROR;
176 : : }
177 : :
1838 sfrost@snowman.net 178 :CBC 12 : ret = pg_GSS_load_servicename(conn);
179 [ - + ]: 12 : if (ret != STATUS_OK)
1838 sfrost@snowman.net 180 :UBC 0 : return ret;
181 : :
182 : : /*
183 : : * Initial packet is the same as a continuation packet with no initial
184 : : * context.
185 : : */
6123 magnus@hagander.net 186 :CBC 12 : conn->gctx = GSS_C_NO_CONTEXT;
187 : :
2558 heikki.linnakangas@i 188 : 12 : return pg_GSS_continue(conn, payloadlen);
189 : : }
190 : : #endif /* ENABLE_GSS */
191 : :
192 : :
193 : : #ifdef ENABLE_SSPI
194 : : /*
195 : : * SSPI authentication system (Windows only)
196 : : */
197 : :
198 : : static void
199 : : pg_SSPI_error(PGconn *conn, const char *mprefix, SECURITY_STATUS r)
200 : : {
201 : : char sysmsg[256];
202 : :
203 : : if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
204 : : FORMAT_MESSAGE_FROM_SYSTEM,
205 : : NULL, r, 0,
206 : : sysmsg, sizeof(sysmsg), NULL) == 0)
207 : : appendPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x\n",
208 : : mprefix, (unsigned int) r);
209 : : else
210 : : appendPQExpBuffer(&conn->errorMessage, "%s: %s (%x)\n",
211 : : mprefix, sysmsg, (unsigned int) r);
212 : : }
213 : :
214 : : /*
215 : : * Continue SSPI authentication with next token as needed.
216 : : */
217 : : static int
218 : : pg_SSPI_continue(PGconn *conn, int payloadlen)
219 : : {
220 : : SECURITY_STATUS r;
221 : : CtxtHandle newContext;
222 : : ULONG contextAttr;
223 : : SecBufferDesc inbuf;
224 : : SecBufferDesc outbuf;
225 : : SecBuffer OutBuffers[1];
226 : : SecBuffer InBuffers[1];
227 : : char *inputbuf = NULL;
228 : :
229 : : if (conn->sspictx != NULL)
230 : : {
231 : : /*
232 : : * On runs other than the first we have some data to send. Put this
233 : : * data in a SecBuffer type structure.
234 : : */
235 : : inputbuf = malloc(payloadlen);
236 : : if (!inputbuf)
237 : : {
238 : : libpq_append_conn_error(conn, "out of memory allocating SSPI buffer (%d)",
239 : : payloadlen);
240 : : return STATUS_ERROR;
241 : : }
242 : : if (pqGetnchar(inputbuf, payloadlen, conn))
243 : : {
244 : : /*
245 : : * Shouldn't happen, because the caller should've ensured that the
246 : : * whole message is already in the input buffer.
247 : : */
248 : : free(inputbuf);
249 : : return STATUS_ERROR;
250 : : }
251 : :
252 : : inbuf.ulVersion = SECBUFFER_VERSION;
253 : : inbuf.cBuffers = 1;
254 : : inbuf.pBuffers = InBuffers;
255 : : InBuffers[0].pvBuffer = inputbuf;
256 : : InBuffers[0].cbBuffer = payloadlen;
257 : : InBuffers[0].BufferType = SECBUFFER_TOKEN;
258 : : }
259 : :
260 : : OutBuffers[0].pvBuffer = NULL;
261 : : OutBuffers[0].BufferType = SECBUFFER_TOKEN;
262 : : OutBuffers[0].cbBuffer = 0;
263 : : outbuf.cBuffers = 1;
264 : : outbuf.pBuffers = OutBuffers;
265 : : outbuf.ulVersion = SECBUFFER_VERSION;
266 : :
267 : : r = InitializeSecurityContext(conn->sspicred,
268 : : conn->sspictx,
269 : : conn->sspitarget,
270 : : ISC_REQ_ALLOCATE_MEMORY,
271 : : 0,
272 : : SECURITY_NETWORK_DREP,
273 : : (conn->sspictx == NULL) ? NULL : &inbuf,
274 : : 0,
275 : : &newContext,
276 : : &outbuf,
277 : : &contextAttr,
278 : : NULL);
279 : :
280 : : /* we don't need the input anymore */
281 : : free(inputbuf);
282 : :
283 : : if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED)
284 : : {
285 : : pg_SSPI_error(conn, libpq_gettext("SSPI continuation error"), r);
286 : :
287 : : return STATUS_ERROR;
288 : : }
289 : :
290 : : if (conn->sspictx == NULL)
291 : : {
292 : : /* On first run, transfer retrieved context handle */
293 : : conn->sspictx = malloc(sizeof(CtxtHandle));
294 : : if (conn->sspictx == NULL)
295 : : {
296 : : libpq_append_conn_error(conn, "out of memory");
297 : : return STATUS_ERROR;
298 : : }
299 : : memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle));
300 : : }
301 : :
302 : : /*
303 : : * If SSPI returned any data to be sent to the server (as it normally
304 : : * would), send this data as a password packet.
305 : : */
306 : : if (outbuf.cBuffers > 0)
307 : : {
308 : : if (outbuf.cBuffers != 1)
309 : : {
310 : : /*
311 : : * This should never happen, at least not for Kerberos
312 : : * authentication. Keep check in case it shows up with other
313 : : * authentication methods later.
314 : : */
315 : : appendPQExpBufferStr(&conn->errorMessage,
316 : : "SSPI returned invalid number of output buffers\n");
317 : : return STATUS_ERROR;
318 : : }
319 : :
320 : : /*
321 : : * If the negotiation is complete, there may be zero bytes to send.
322 : : * The server is at this point not expecting any more data, so don't
323 : : * send it.
324 : : */
325 : : if (outbuf.pBuffers[0].cbBuffer > 0)
326 : : {
327 : : if (pqPacketSend(conn, 'p',
328 : : outbuf.pBuffers[0].pvBuffer, outbuf.pBuffers[0].cbBuffer))
329 : : {
330 : : FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
331 : : return STATUS_ERROR;
332 : : }
333 : : }
334 : : FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
335 : : }
336 : :
337 : : if (r == SEC_E_OK)
338 : : conn->client_finished_auth = true;
339 : :
340 : : /* Cleanup is handled by the code in freePGconn() */
341 : : return STATUS_OK;
342 : : }
343 : :
344 : : /*
345 : : * Send initial SSPI authentication token.
346 : : * If use_negotiate is 0, use kerberos authentication package which is
347 : : * compatible with Unix. If use_negotiate is 1, use the negotiate package
348 : : * which supports both kerberos and NTLM, but is not compatible with Unix.
349 : : */
350 : : static int
351 : : pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen)
352 : : {
353 : : SECURITY_STATUS r;
354 : : TimeStamp expire;
355 : : char *host = conn->connhost[conn->whichhost].host;
356 : :
357 : : if (conn->sspictx)
358 : : {
359 : : libpq_append_conn_error(conn, "duplicate SSPI authentication request");
360 : : return STATUS_ERROR;
361 : : }
362 : :
363 : : /*
364 : : * Retrieve credentials handle
365 : : */
366 : : conn->sspicred = malloc(sizeof(CredHandle));
367 : : if (conn->sspicred == NULL)
368 : : {
369 : : libpq_append_conn_error(conn, "out of memory");
370 : : return STATUS_ERROR;
371 : : }
372 : :
373 : : r = AcquireCredentialsHandle(NULL,
374 : : use_negotiate ? "negotiate" : "kerberos",
375 : : SECPKG_CRED_OUTBOUND,
376 : : NULL,
377 : : NULL,
378 : : NULL,
379 : : NULL,
380 : : conn->sspicred,
381 : : &expire);
382 : : if (r != SEC_E_OK)
383 : : {
384 : : pg_SSPI_error(conn, libpq_gettext("could not acquire SSPI credentials"), r);
385 : : free(conn->sspicred);
386 : : conn->sspicred = NULL;
387 : : return STATUS_ERROR;
388 : : }
389 : :
390 : : /*
391 : : * Compute target principal name. SSPI has a different format from GSSAPI,
392 : : * but not more complex. We can skip the @REALM part, because Windows will
393 : : * fill that in for us automatically.
394 : : */
395 : : if (!(host && host[0] != '\0'))
396 : : {
397 : : libpq_append_conn_error(conn, "host name must be specified");
398 : : return STATUS_ERROR;
399 : : }
400 : : conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(host) + 2);
401 : : if (!conn->sspitarget)
402 : : {
403 : : libpq_append_conn_error(conn, "out of memory");
404 : : return STATUS_ERROR;
405 : : }
406 : : sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, host);
407 : :
408 : : /*
409 : : * Indicate that we're in SSPI authentication mode to make sure that
410 : : * pg_SSPI_continue is called next time in the negotiation.
411 : : */
412 : : conn->usesspi = 1;
413 : :
414 : : return pg_SSPI_continue(conn, payloadlen);
415 : : }
416 : : #endif /* ENABLE_SSPI */
417 : :
418 : : /*
419 : : * Initialize SASL authentication exchange.
420 : : */
421 : : static int
422 : 44 : pg_SASL_init(PGconn *conn, int payloadlen)
423 : : {
424 : 44 : char *initialresponse = NULL;
425 : : int initialresponselen;
426 : : const char *selected_mechanism;
427 : : PQExpBufferData mechanism_buf;
24 dgustafsson@postgres 428 :GNC 44 : char *password = NULL;
429 : : SASLStatus status;
430 : :
2558 heikki.linnakangas@i 431 :CBC 44 : initPQExpBuffer(&mechanism_buf);
432 : :
1665 jdavis@postgresql.or 433 [ + + ]: 44 : if (conn->channel_binding[0] == 'r' && /* require */
434 [ + + ]: 4 : !conn->ssl_in_use)
435 : : {
516 peter@eisentraut.org 436 : 1 : libpq_append_conn_error(conn, "channel binding required, but SSL not in use");
1665 jdavis@postgresql.or 437 : 1 : goto error;
438 : : }
439 : :
2558 heikki.linnakangas@i 440 [ - + ]: 43 : if (conn->sasl_state)
441 : : {
516 peter@eisentraut.org 442 :UBC 0 : libpq_append_conn_error(conn, "duplicate SASL authentication request");
2558 heikki.linnakangas@i 443 : 0 : goto error;
444 : : }
445 : :
446 : : /*
447 : : * Parse the list of SASL authentication mechanisms in the
448 : : * AuthenticationSASL message, and select the best mechanism that we
449 : : * support. Mechanisms are listed by order of decreasing importance.
450 : : */
2558 heikki.linnakangas@i 451 :CBC 43 : selected_mechanism = NULL;
452 : : for (;;)
453 : : {
454 [ - + ]: 93 : if (pqGets(&mechanism_buf, conn))
455 : : {
1189 tgl@sss.pgh.pa.us 456 :UBC 0 : appendPQExpBufferStr(&conn->errorMessage,
457 : : "fe_sendauth: invalid authentication request from server: invalid list of authentication mechanisms\n");
2558 heikki.linnakangas@i 458 : 0 : goto error;
459 : : }
2558 heikki.linnakangas@i 460 [ - + ]:CBC 93 : if (PQExpBufferDataBroken(mechanism_buf))
2558 heikki.linnakangas@i 461 :UBC 0 : goto oom_error;
462 : :
463 : : /* An empty string indicates end of list */
2558 heikki.linnakangas@i 464 [ + + ]:CBC 93 : if (mechanism_buf.data[0] == '\0')
465 : 43 : break;
466 : :
467 : : /*
468 : : * Select the mechanism to use. Pick SCRAM-SHA-256-PLUS over anything
469 : : * else if a channel binding type is set and if the client supports it
470 : : * (and did not set channel_binding=disable). Pick SCRAM-SHA-256 if
471 : : * nothing else has already been picked. If we add more mechanisms, a
472 : : * more refined priority mechanism might become necessary.
473 : : */
2079 474 [ + + ]: 50 : if (strcmp(mechanism_buf.data, SCRAM_SHA_256_PLUS_NAME) == 0)
475 : : {
476 [ + - ]: 7 : if (conn->ssl_in_use)
477 : : {
478 : : /* The server has offered SCRAM-SHA-256-PLUS. */
479 : :
480 : : #ifdef USE_SSL
481 : : /*
482 : : * The client supports channel binding, which is chosen if
483 : : * channel_binding is not disabled.
484 : : */
1665 jdavis@postgresql.or 485 [ + + ]: 7 : if (conn->channel_binding[0] != 'd') /* disable */
486 : : {
487 : 5 : selected_mechanism = SCRAM_SHA_256_PLUS_NAME;
1012 michael@paquier.xyz 488 : 5 : conn->sasl = &pg_scram_mech;
24 dgustafsson@postgres 489 :GNC 5 : conn->password_needed = true;
490 : : }
491 : : #else
492 : : /*
493 : : * The client does not support channel binding. If it is
494 : : * required, complain immediately instead of the error below
495 : : * which would be confusing as the server is publishing
496 : : * SCRAM-SHA-256-PLUS.
497 : : */
498 : : if (conn->channel_binding[0] == 'r') /* require */
499 : : {
500 : : libpq_append_conn_error(conn, "channel binding is required, but client does not support it");
501 : : goto error;
502 : : }
503 : : #endif
504 : : }
505 : : else
506 : : {
507 : : /*
508 : : * The server offered SCRAM-SHA-256-PLUS, but the connection
509 : : * is not SSL-encrypted. That's not sane. Perhaps SSL was
510 : : * stripped by a proxy? There's no point in continuing,
511 : : * because the server will reject the connection anyway if we
512 : : * try authenticate without channel binding even though both
513 : : * the client and server supported it. The SCRAM exchange
514 : : * checks for that, to prevent downgrade attacks.
515 : : */
516 peter@eisentraut.org 516 :UBC 0 : libpq_append_conn_error(conn, "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection");
2079 heikki.linnakangas@i 517 : 0 : goto error;
518 : : }
519 : : }
2266 peter_e@gmx.net 520 [ + - + + ]:CBC 43 : else if (strcmp(mechanism_buf.data, SCRAM_SHA_256_NAME) == 0 &&
521 : : !selected_mechanism)
522 : : {
523 : 38 : selected_mechanism = SCRAM_SHA_256_NAME;
1012 michael@paquier.xyz 524 : 38 : conn->sasl = &pg_scram_mech;
24 dgustafsson@postgres 525 :GNC 38 : conn->password_needed = true;
526 : : }
527 : : }
528 : :
1659 tgl@sss.pgh.pa.us 529 [ - + ]:CBC 43 : if (!selected_mechanism)
530 : : {
516 peter@eisentraut.org 531 :UBC 0 : libpq_append_conn_error(conn, "none of the server's SASL authentication mechanisms are supported");
1665 jdavis@postgresql.or 532 : 0 : goto error;
533 : : }
534 : :
1659 tgl@sss.pgh.pa.us 535 [ + + ]:CBC 43 : if (conn->channel_binding[0] == 'r' && /* require */
536 [ - + ]: 3 : strcmp(selected_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0)
537 : : {
516 peter@eisentraut.org 538 :UBC 0 : libpq_append_conn_error(conn, "channel binding is required, but server did not offer an authentication method that supports channel binding");
2558 heikki.linnakangas@i 539 : 0 : goto error;
540 : : }
541 : :
542 : : /*
543 : : * Now that the SASL mechanism has been chosen for the exchange,
544 : : * initialize its state information.
545 : : */
546 : :
547 : : /*
548 : : * First, select the password to use for the exchange, complaining if
549 : : * there isn't one and the selected SASL mechanism needs it.
550 : : */
24 dgustafsson@postgres 551 [ + - ]:GNC 43 : if (conn->password_needed)
552 : : {
553 : 43 : password = conn->connhost[conn->whichhost].password;
554 [ + + ]: 43 : if (password == NULL)
555 : 41 : password = conn->pgpass;
556 [ + - - + ]: 43 : if (password == NULL || password[0] == '\0')
557 : : {
24 dgustafsson@postgres 558 :UNC 0 : appendPQExpBufferStr(&conn->errorMessage,
559 : : PQnoPasswordSupplied);
560 : 0 : goto error;
561 : : }
562 : : }
563 : :
1012 michael@paquier.xyz 564 [ - + ]:CBC 43 : Assert(conn->sasl);
565 : :
566 : : /*
567 : : * Initialize the SASL state information with all the information gathered
568 : : * during the initial exchange.
569 : : *
570 : : * Note: Only tls-unique is supported for the moment.
571 : : */
572 : 43 : conn->sasl_state = conn->sasl->init(conn,
573 : : password,
574 : : selected_mechanism);
2339 peter_e@gmx.net 575 [ - + ]: 43 : if (!conn->sasl_state)
2339 peter_e@gmx.net 576 :UBC 0 : goto oom_error;
577 : :
578 : : /* Get the mechanism-specific Initial Client Response, if any */
24 dgustafsson@postgres 579 :GNC 43 : status = conn->sasl->exchange(conn->sasl_state,
580 : : NULL, -1,
581 : : &initialresponse, &initialresponselen);
582 : :
583 [ - + ]: 43 : if (status == SASL_FAILED)
2558 heikki.linnakangas@i 584 :UBC 0 : goto error;
585 : :
586 : : /*
587 : : * Build a SASLInitialResponse message, and send it.
588 : : */
236 nathan@postgresql.or 589 [ - + ]:GNC 43 : if (pqPutMsgStart(PqMsg_SASLInitialResponse, conn))
2558 heikki.linnakangas@i 590 :UBC 0 : goto error;
2558 heikki.linnakangas@i 591 [ - + ]:CBC 43 : if (pqPuts(selected_mechanism, conn))
2558 heikki.linnakangas@i 592 :UBC 0 : goto error;
2558 heikki.linnakangas@i 593 [ + - ]:CBC 43 : if (initialresponse)
594 : : {
595 [ - + ]: 43 : if (pqPutInt(initialresponselen, 4, conn))
2558 heikki.linnakangas@i 596 :UBC 0 : goto error;
2558 heikki.linnakangas@i 597 [ - + ]:CBC 43 : if (pqPutnchar(initialresponse, initialresponselen, conn))
2558 heikki.linnakangas@i 598 :UBC 0 : goto error;
599 : : }
2558 heikki.linnakangas@i 600 [ - + ]:CBC 43 : if (pqPutMsgEnd(conn))
2558 heikki.linnakangas@i 601 :UBC 0 : goto error;
2558 heikki.linnakangas@i 602 [ - + ]:CBC 43 : if (pqFlush(conn))
2558 heikki.linnakangas@i 603 :UBC 0 : goto error;
604 : :
2558 heikki.linnakangas@i 605 :CBC 43 : termPQExpBuffer(&mechanism_buf);
668 peter@eisentraut.org 606 : 43 : free(initialresponse);
607 : :
2558 heikki.linnakangas@i 608 : 43 : return STATUS_OK;
609 : :
610 : 1 : error:
611 : 1 : termPQExpBuffer(&mechanism_buf);
668 peter@eisentraut.org 612 : 1 : free(initialresponse);
2558 heikki.linnakangas@i 613 : 1 : return STATUS_ERROR;
614 : :
2558 heikki.linnakangas@i 615 :UBC 0 : oom_error:
616 : 0 : termPQExpBuffer(&mechanism_buf);
668 peter@eisentraut.org 617 : 0 : free(initialresponse);
516 618 : 0 : libpq_append_conn_error(conn, "out of memory");
2558 heikki.linnakangas@i 619 : 0 : return STATUS_ERROR;
620 : : }
621 : :
622 : : /*
623 : : * Exchange a message for SASL communication protocol with the backend.
624 : : * This should be used after calling pg_SASL_init to set up the status of
625 : : * the protocol.
626 : : */
627 : : static int
2558 heikki.linnakangas@i 628 :CBC 80 : pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
629 : : {
630 : : char *output;
631 : : int outputlen;
632 : : int res;
633 : : char *challenge;
634 : : SASLStatus status;
635 : :
636 : : /* Read the SASL challenge from the AuthenticationSASLContinue message. */
637 : 80 : challenge = malloc(payloadlen + 1);
638 [ - + ]: 80 : if (!challenge)
639 : : {
516 peter@eisentraut.org 640 :UBC 0 : libpq_append_conn_error(conn, "out of memory allocating SASL buffer (%d)",
641 : : payloadlen);
2558 heikki.linnakangas@i 642 : 0 : return STATUS_ERROR;
643 : : }
644 : :
2558 heikki.linnakangas@i 645 [ - + ]:CBC 80 : if (pqGetnchar(challenge, payloadlen, conn))
646 : : {
2558 heikki.linnakangas@i 647 :UBC 0 : free(challenge);
648 : 0 : return STATUS_ERROR;
649 : : }
650 : : /* For safety and convenience, ensure the buffer is NULL-terminated. */
2558 heikki.linnakangas@i 651 :CBC 80 : challenge[payloadlen] = '\0';
652 : :
24 dgustafsson@postgres 653 :GNC 80 : status = conn->sasl->exchange(conn->sasl_state,
654 : : challenge, payloadlen,
655 : : &output, &outputlen);
2558 heikki.linnakangas@i 656 :CBC 80 : free(challenge); /* don't need the input anymore */
657 : :
24 dgustafsson@postgres 658 [ + + - + ]:GNC 80 : if (final && status == SASL_CONTINUE)
659 : : {
2558 heikki.linnakangas@i 660 [ # # ]:UBC 0 : if (outputlen != 0)
661 : 0 : free(output);
662 : :
516 peter@eisentraut.org 663 : 0 : libpq_append_conn_error(conn, "AuthenticationSASLFinal received from server, but SASL authentication was not completed");
2558 heikki.linnakangas@i 664 : 0 : return STATUS_ERROR;
665 : : }
666 : :
667 : : /*
668 : : * If the exchange is not completed yet, we need to make sure that the
669 : : * SASL mechanism has generated a message to send back.
670 : : */
24 dgustafsson@postgres 671 [ + + - + ]:GNC 80 : if (output == NULL && status == SASL_CONTINUE)
672 : : {
516 peter@eisentraut.org 673 :UBC 0 : libpq_append_conn_error(conn, "no client response found after SASL exchange success");
1009 michael@paquier.xyz 674 : 0 : return STATUS_ERROR;
675 : : }
676 : :
677 : : /*
678 : : * SASL allows zero-length responses, so this check uses "output" and not
679 : : * "outputlen" to allow the case of an empty message.
680 : : */
1009 michael@paquier.xyz 681 [ + + ]:CBC 80 : if (output)
682 : : {
683 : : /*
684 : : * Send the SASL response to the server.
685 : : */
2595 heikki.linnakangas@i 686 : 43 : res = pqPacketSend(conn, 'p', output, outputlen);
687 : 43 : free(output);
688 : :
689 [ - + ]: 43 : if (res != STATUS_OK)
2595 heikki.linnakangas@i 690 :UBC 0 : return STATUS_ERROR;
691 : : }
692 : :
24 dgustafsson@postgres 693 [ - + ]:GNC 80 : if (status == SASL_FAILED)
2595 heikki.linnakangas@i 694 :UBC 0 : return STATUS_ERROR;
695 : :
2595 heikki.linnakangas@i 696 :CBC 80 : return STATUS_OK;
697 : : }
698 : :
699 : : static int
9575 scrappy@hub.org 700 : 39 : pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
701 : : {
702 : : int ret;
4422 peter_e@gmx.net 703 : 39 : char *crypt_pwd = NULL;
704 : : const char *pwd_to_send;
705 : : char md5Salt[4];
706 : :
707 : : /* Read the salt from the AuthenticationMD5Password message. */
2558 heikki.linnakangas@i 708 [ + + ]: 39 : if (areq == AUTH_REQ_MD5)
709 : : {
710 [ - + ]: 1 : if (pqGetnchar(md5Salt, 4, conn))
2558 heikki.linnakangas@i 711 :UBC 0 : return STATUS_ERROR; /* shouldn't happen */
712 : : }
713 : :
714 : : /* Encrypt the password if needed. */
715 : :
8278 bruce@momjian.us 716 [ + + - ]:CBC 39 : switch (areq)
717 : : {
8276 718 : 1 : case AUTH_REQ_MD5:
719 : : {
720 : : char *crypt_pwd2;
824 michael@paquier.xyz 721 : 1 : const char *errstr = NULL;
722 : :
723 : : /* Allocate enough space for two MD5 hashes */
6863 neilc@samurai.com 724 : 1 : crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1));
725 [ - + ]: 1 : if (!crypt_pwd)
726 : : {
516 peter@eisentraut.org 727 :UBC 0 : libpq_append_conn_error(conn, "out of memory");
8207 bruce@momjian.us 728 : 0 : return STATUS_ERROR;
729 : : }
730 : :
6863 neilc@samurai.com 731 :CBC 1 : crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1;
6754 tgl@sss.pgh.pa.us 732 [ - + ]: 1 : if (!pg_md5_encrypt(password, conn->pguser,
824 michael@paquier.xyz 733 : 1 : strlen(conn->pguser), crypt_pwd2,
734 : : &errstr))
735 : : {
516 peter@eisentraut.org 736 :UBC 0 : libpq_append_conn_error(conn, "could not encrypt password: %s", errstr);
8207 bruce@momjian.us 737 : 0 : free(crypt_pwd);
738 : 0 : return STATUS_ERROR;
739 : : }
2558 heikki.linnakangas@i 740 [ - + ]:CBC 1 : if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), md5Salt,
741 : : 4, crypt_pwd, &errstr))
742 : : {
516 peter@eisentraut.org 743 :UBC 0 : libpq_append_conn_error(conn, "could not encrypt password: %s", errstr);
8207 bruce@momjian.us 744 : 0 : free(crypt_pwd);
745 : 0 : return STATUS_ERROR;
746 : : }
747 : :
4422 peter_e@gmx.net 748 :CBC 1 : pwd_to_send = crypt_pwd;
8207 bruce@momjian.us 749 : 1 : break;
750 : : }
8272 751 : 38 : case AUTH_REQ_PASSWORD:
4422 peter_e@gmx.net 752 : 38 : pwd_to_send = password;
8278 bruce@momjian.us 753 : 38 : break;
8272 bruce@momjian.us 754 :UBC 0 : default:
755 : 0 : return STATUS_ERROR;
756 : : }
1137 heikki.linnakangas@i 757 :CBC 39 : ret = pqPacketSend(conn, 'p', pwd_to_send, strlen(pwd_to_send) + 1);
668 peter@eisentraut.org 758 : 39 : free(crypt_pwd);
8278 bruce@momjian.us 759 : 39 : return ret;
760 : : }
761 : :
762 : : /*
763 : : * Translate a disallowed AuthRequest code into an error message.
764 : : */
765 : : static const char *
397 michael@paquier.xyz 766 : 17 : auth_method_description(AuthRequest areq)
767 : : {
768 [ + - + - : 17 : switch (areq)
+ - ]
769 : : {
770 : 6 : case AUTH_REQ_PASSWORD:
771 : 6 : return libpq_gettext("server requested a cleartext password");
397 michael@paquier.xyz 772 :LBC (5) : case AUTH_REQ_MD5:
773 : (5) : return libpq_gettext("server requested a hashed password");
397 michael@paquier.xyz 774 :CBC 1 : case AUTH_REQ_GSS:
775 : : case AUTH_REQ_GSS_CONT:
776 : 1 : return libpq_gettext("server requested GSSAPI authentication");
397 michael@paquier.xyz 777 :UBC 0 : case AUTH_REQ_SSPI:
778 : 0 : return libpq_gettext("server requested SSPI authentication");
397 michael@paquier.xyz 779 :CBC 10 : case AUTH_REQ_SASL:
780 : : case AUTH_REQ_SASL_CONT:
781 : : case AUTH_REQ_SASL_FIN:
782 : 10 : return libpq_gettext("server requested SASL authentication");
783 : : }
784 : :
397 michael@paquier.xyz 785 :UBC 0 : return libpq_gettext("server requested an unknown authentication type");
786 : : }
787 : :
788 : : /*
789 : : * Convenience macro for checking the allowed_auth_methods bitmask. Caller
790 : : * must ensure that type is not greater than 31 (high bit of the bitmask).
791 : : */
792 : : #define auth_method_allowed(conn, type) \
793 : : (((conn)->allowed_auth_methods & (1 << (type))) != 0)
794 : :
795 : : /*
796 : : * Verify that the authentication request is expected, given the connection
797 : : * parameters. This is especially important when the client wishes to
798 : : * authenticate the server before any sensitive information is exchanged.
799 : : */
800 : : static bool
1665 jdavis@postgresql.or 801 :CBC 11637 : check_expected_areq(AuthRequest areq, PGconn *conn)
802 : : {
803 : 11637 : bool result = true;
397 michael@paquier.xyz 804 : 11637 : const char *reason = NULL;
805 : :
806 : : StaticAssertDecl((sizeof(conn->allowed_auth_methods) * CHAR_BIT) > AUTH_REQ_MAX,
807 : : "AUTH_REQ_MAX overflows the allowed_auth_methods bitmask");
808 : :
387 809 [ + + ]: 11637 : if (conn->sslcertmode[0] == 'r' /* require */
810 [ + - ]: 3 : && areq == AUTH_REQ_OK)
811 : : {
812 : : /*
813 : : * Trade off a little bit of complexity to try to get these error
814 : : * messages as precise as possible.
815 : : */
816 [ - + ]: 3 : if (!conn->ssl_cert_requested)
817 : : {
387 michael@paquier.xyz 818 :UBC 0 : libpq_append_conn_error(conn, "server did not request an SSL certificate");
819 : 0 : return false;
820 : : }
387 michael@paquier.xyz 821 [ + + ]:CBC 3 : else if (!conn->ssl_cert_sent)
822 : : {
823 : 1 : libpq_append_conn_error(conn, "server accepted connection without a valid SSL certificate");
824 : 1 : return false;
825 : : }
826 : : }
827 : :
828 : : /*
829 : : * If the user required a specific auth method, or specified an allowed
830 : : * set, then reject all others here, and make sure the server actually
831 : : * completes an authentication exchange.
832 : : */
397 833 [ + + ]: 11636 : if (conn->require_auth)
834 : : {
835 [ + + - ]: 80 : switch (areq)
836 : : {
837 : 32 : case AUTH_REQ_OK:
838 : :
839 : : /*
840 : : * Check to make sure we've actually finished our exchange (or
841 : : * else that the user has allowed an authentication-less
842 : : * connection).
843 : : *
844 : : * If the user has allowed both SCRAM and unauthenticated
845 : : * (trust) connections, then this check will silently accept
846 : : * partial SCRAM exchanges, where a misbehaving server does
847 : : * not provide its verifier before sending an OK. This is
848 : : * consistent with historical behavior, but it may be a point
849 : : * to revisit in the future, since it could allow a server
850 : : * that doesn't know the user's password to silently harvest
851 : : * material for a brute force attack.
852 : : */
853 [ + + + + ]: 32 : if (!conn->auth_required || conn->client_finished_auth)
854 : : break;
855 : :
856 : : /*
857 : : * No explicit authentication request was made by the server
858 : : * -- or perhaps it was made and not completed, in the case of
859 : : * SCRAM -- but there is one special case to check. If the
860 : : * user allowed "gss", then a GSS-encrypted channel also
861 : : * satisfies the check.
862 : : */
863 : : #ifdef ENABLE_GSS
864 [ + + + + ]: 11 : if (auth_method_allowed(conn, AUTH_REQ_GSS) && conn->gssenc)
865 : : {
866 : : /*
867 : : * If implicit GSS auth has already been performed via GSS
868 : : * encryption, we don't need to have performed an
869 : : * AUTH_REQ_GSS exchange. This allows require_auth=gss to
870 : : * be combined with gssencmode, since there won't be an
871 : : * explicit authentication request in that case.
872 : : */
873 : : }
874 : : else
875 : : #endif
876 : : {
877 : 8 : reason = libpq_gettext("server did not complete authentication");
878 : 8 : result = false;
879 : : }
880 : :
881 : 11 : break;
882 : :
883 : 48 : case AUTH_REQ_PASSWORD:
884 : : case AUTH_REQ_MD5:
885 : : case AUTH_REQ_GSS:
886 : : case AUTH_REQ_GSS_CONT:
887 : : case AUTH_REQ_SSPI:
888 : : case AUTH_REQ_SASL:
889 : : case AUTH_REQ_SASL_CONT:
890 : : case AUTH_REQ_SASL_FIN:
891 : :
892 : : /*
893 : : * We don't handle these with the default case, to avoid
894 : : * bit-shifting past the end of the allowed_auth_methods mask
895 : : * if the server sends an unexpected AuthRequest.
896 : : */
897 : 48 : result = auth_method_allowed(conn, areq);
898 : 48 : break;
899 : :
397 michael@paquier.xyz 900 :UBC 0 : default:
901 : 0 : result = false;
902 : 0 : break;
903 : : }
904 : : }
905 : :
397 michael@paquier.xyz 906 [ + + ]:CBC 11636 : if (!result)
907 : : {
908 [ + + ]: 25 : if (!reason)
909 : 17 : reason = auth_method_description(areq);
910 : :
334 peter@eisentraut.org 911 : 25 : libpq_append_conn_error(conn, "authentication method requirement \"%s\" failed: %s",
912 : : conn->require_auth, reason);
397 michael@paquier.xyz 913 : 25 : return result;
914 : : }
915 : :
916 : : /*
917 : : * When channel_binding=require, we must protect against two cases: (1) we
918 : : * must not respond to non-SASL authentication requests, which might leak
919 : : * information such as the client's password; and (2) even if we receive
920 : : * AUTH_REQ_OK, we still must ensure that channel binding has happened in
921 : : * order to authenticate the server.
922 : : */
1665 jdavis@postgresql.or 923 [ + + ]: 11611 : if (conn->channel_binding[0] == 'r' /* require */ )
924 : : {
925 [ + + + ]: 17 : switch (areq)
926 : : {
927 : 10 : case AUTH_REQ_SASL:
928 : : case AUTH_REQ_SASL_CONT:
929 : : case AUTH_REQ_SASL_FIN:
930 : 10 : break;
931 : 4 : case AUTH_REQ_OK:
1012 michael@paquier.xyz 932 [ + + - + ]: 4 : if (!conn->sasl || !conn->sasl->channel_bound(conn->sasl_state))
933 : : {
516 peter@eisentraut.org 934 : 1 : libpq_append_conn_error(conn, "channel binding required, but server authenticated client without channel binding");
1665 jdavis@postgresql.or 935 : 1 : result = false;
936 : : }
937 : 4 : break;
938 : 3 : default:
516 peter@eisentraut.org 939 : 3 : libpq_append_conn_error(conn, "channel binding required but not supported by server's authentication request");
1665 jdavis@postgresql.or 940 : 3 : result = false;
941 : 3 : break;
942 : : }
943 : : }
944 : :
945 : 11611 : return result;
946 : : }
947 : :
948 : : /*
949 : : * pg_fe_sendauth
950 : : * client demux routine for processing an authentication request
951 : : *
952 : : * The server has sent us an authentication challenge (or OK). Send an
953 : : * appropriate response. The caller has ensured that the whole message is
954 : : * now in the input buffer, and has already read the type and length of
955 : : * it. We are responsible for reading any remaining extra data, specific
956 : : * to the authentication method. 'payloadlen' is the remaining length in
957 : : * the message.
958 : : */
959 : : int
2558 heikki.linnakangas@i 960 : 11637 : pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
961 : : {
962 : : int oldmsglen;
963 : :
1665 jdavis@postgresql.or 964 [ + + ]: 11637 : if (!check_expected_areq(areq, conn))
965 : 30 : return STATUS_ERROR;
966 : :
9575 scrappy@hub.org 967 [ + - - + : 11607 : switch (areq)
+ - + + +
- ]
968 : : {
8768 bruce@momjian.us 969 : 11420 : case AUTH_REQ_OK:
9544 970 : 11420 : break;
971 : :
9544 bruce@momjian.us 972 :UBC 0 : case AUTH_REQ_KRB4:
516 peter@eisentraut.org 973 : 0 : libpq_append_conn_error(conn, "Kerberos 4 authentication not supported");
9357 bruce@momjian.us 974 : 0 : return STATUS_ERROR;
975 : :
9544 976 : 0 : case AUTH_REQ_KRB5:
516 peter@eisentraut.org 977 : 0 : libpq_append_conn_error(conn, "Kerberos 5 authentication not supported");
9357 bruce@momjian.us 978 : 0 : return STATUS_ERROR;
979 : :
980 : : #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
6123 magnus@hagander.net 981 :CBC 12 : case AUTH_REQ_GSS:
982 : : #if !defined(ENABLE_SSPI)
983 : : /* no native SSPI, so use GSSAPI library for it */
984 : : case AUTH_REQ_SSPI:
985 : : #endif
986 : : {
987 : : int r;
988 : :
6110 989 : 12 : pglock_thread();
990 : :
991 : : /*
992 : : * If we have both GSS and SSPI support compiled in, use SSPI
993 : : * support by default. This is overridable by a connection
994 : : * string parameter. Note that when using SSPI we still leave
995 : : * the negotiate parameter off, since we want SSPI to use the
996 : : * GSSAPI kerberos protocol. For actual SSPI negotiate
997 : : * protocol, we use AUTH_REQ_SSPI.
998 : : */
999 : : #if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
1000 : : if (conn->gsslib && (pg_strcasecmp(conn->gsslib, "gssapi") == 0))
1001 : : r = pg_GSS_startup(conn, payloadlen);
1002 : : else
1003 : : r = pg_SSPI_startup(conn, 0, payloadlen);
1004 : : #elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
2558 heikki.linnakangas@i 1005 : 12 : r = pg_GSS_startup(conn, payloadlen);
1006 : : #elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
1007 : : r = pg_SSPI_startup(conn, 0, payloadlen);
1008 : : #endif
6110 magnus@hagander.net 1009 [ + + ]: 12 : if (r != STATUS_OK)
1010 : : {
1011 : : /* Error message already filled in. */
1012 : 1 : pgunlock_thread();
1013 : 1 : return STATUS_ERROR;
1014 : : }
6123 1015 : 11 : pgunlock_thread();
1016 : : }
1017 : 11 : break;
1018 : :
1019 : 11 : case AUTH_REQ_GSS_CONT:
1020 : : {
1021 : : int r;
1022 : :
6110 1023 : 11 : pglock_thread();
1024 : : #if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
1025 : : if (conn->usesspi)
1026 : : r = pg_SSPI_continue(conn, payloadlen);
1027 : : else
1028 : : r = pg_GSS_continue(conn, payloadlen);
1029 : : #elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
2558 heikki.linnakangas@i 1030 : 11 : r = pg_GSS_continue(conn, payloadlen);
1031 : : #elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
1032 : : r = pg_SSPI_continue(conn, payloadlen);
1033 : : #endif
6110 magnus@hagander.net 1034 [ - + ]: 11 : if (r != STATUS_OK)
1035 : : {
1036 : : /* Error message already filled in. */
6110 magnus@hagander.net 1037 :UBC 0 : pgunlock_thread();
1038 : 0 : return STATUS_ERROR;
1039 : : }
6123 magnus@hagander.net 1040 :CBC 11 : pgunlock_thread();
1041 : : }
1042 : 11 : break;
1043 : : #else /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */
1044 : : /* No GSSAPI *or* SSPI support */
1045 : : case AUTH_REQ_GSS:
1046 : : case AUTH_REQ_GSS_CONT:
1047 : : libpq_append_conn_error(conn, "GSSAPI authentication not supported");
1048 : : return STATUS_ERROR;
1049 : : #endif /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */
1050 : :
1051 : : #ifdef ENABLE_SSPI
1052 : : case AUTH_REQ_SSPI:
1053 : :
1054 : : /*
1055 : : * SSPI has its own startup message so libpq can decide which
1056 : : * method to use. Indicate to pg_SSPI_startup that we want SSPI
1057 : : * negotiation instead of Kerberos.
1058 : : */
1059 : : pglock_thread();
1060 : : if (pg_SSPI_startup(conn, 1, payloadlen) != STATUS_OK)
1061 : : {
1062 : : /* Error message already filled in. */
1063 : : pgunlock_thread();
1064 : : return STATUS_ERROR;
1065 : : }
1066 : : pgunlock_thread();
1067 : : break;
1068 : : #else
1069 : :
1070 : : /*
1071 : : * No SSPI support. However, if we have GSSAPI but not SSPI
1072 : : * support, AUTH_REQ_SSPI will have been handled in the codepath
1073 : : * for AUTH_REQ_GSS above, so don't duplicate the case label in
1074 : : * that case.
1075 : : */
1076 : : #if !defined(ENABLE_GSS)
1077 : : case AUTH_REQ_SSPI:
1078 : : libpq_append_conn_error(conn, "SSPI authentication not supported");
1079 : : return STATUS_ERROR;
1080 : : #endif /* !define(ENABLE_GSS) */
1081 : : #endif /* ENABLE_SSPI */
1082 : :
1083 : :
8276 bruce@momjian.us 1084 :UBC 0 : case AUTH_REQ_CRYPT:
516 peter@eisentraut.org 1085 : 0 : libpq_append_conn_error(conn, "Crypt authentication not supported");
5647 magnus@hagander.net 1086 : 0 : return STATUS_ERROR;
1087 : :
5647 magnus@hagander.net 1088 :CBC 40 : case AUTH_REQ_MD5:
1089 : : case AUTH_REQ_PASSWORD:
1090 : : {
1091 : : char *password;
1092 : :
2637 tgl@sss.pgh.pa.us 1093 : 40 : conn->password_needed = true;
1094 : 40 : password = conn->connhost[conn->whichhost].password;
2719 rhaas@postgresql.org 1095 [ + + ]: 40 : if (password == NULL)
1096 : 35 : password = conn->pgpass;
1097 [ + + - + ]: 40 : if (password == NULL || password[0] == '\0')
1098 : : {
1189 tgl@sss.pgh.pa.us 1099 : 1 : appendPQExpBufferStr(&conn->errorMessage,
1100 : : PQnoPasswordSupplied);
2719 rhaas@postgresql.org 1101 : 1 : return STATUS_ERROR;
1102 : : }
1103 [ - + ]: 39 : if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
1104 : : {
1189 tgl@sss.pgh.pa.us 1105 :UBC 0 : appendPQExpBufferStr(&conn->errorMessage,
1106 : : "fe_sendauth: error sending password authentication\n");
2719 rhaas@postgresql.org 1107 : 0 : return STATUS_ERROR;
1108 : : }
1109 : :
1110 : : /* We expect no further authentication requests. */
397 michael@paquier.xyz 1111 :CBC 39 : conn->client_finished_auth = true;
2719 rhaas@postgresql.org 1112 : 39 : break;
1113 : : }
1114 : :
2595 heikki.linnakangas@i 1115 : 44 : case AUTH_REQ_SASL:
1116 : :
1117 : : /*
1118 : : * The request contains the name (as assigned by IANA) of the
1119 : : * authentication mechanism.
1120 : : */
2558 1121 [ + + ]: 44 : if (pg_SASL_init(conn, payloadlen) != STATUS_OK)
1122 : : {
1123 : : /* pg_SASL_init already set the error message */
2595 1124 : 1 : return STATUS_ERROR;
1125 : : }
2558 1126 : 43 : break;
1127 : :
2595 1128 : 80 : case AUTH_REQ_SASL_CONT:
1129 : : case AUTH_REQ_SASL_FIN:
1130 [ - + ]: 80 : if (conn->sasl_state == NULL)
1131 : : {
1189 tgl@sss.pgh.pa.us 1132 :UBC 0 : appendPQExpBufferStr(&conn->errorMessage,
1133 : : "fe_sendauth: invalid authentication request from server: AUTH_REQ_SASL_CONT without AUTH_REQ_SASL\n");
2595 heikki.linnakangas@i 1134 : 0 : return STATUS_ERROR;
1135 : : }
1189 tgl@sss.pgh.pa.us 1136 :CBC 80 : oldmsglen = conn->errorMessage.len;
2558 heikki.linnakangas@i 1137 [ - + ]: 80 : if (pg_SASL_continue(conn, payloadlen,
1138 : : (areq == AUTH_REQ_SASL_FIN)) != STATUS_OK)
1139 : : {
1140 : : /* Use this message if pg_SASL_continue didn't supply one */
1189 tgl@sss.pgh.pa.us 1141 [ # # ]:UBC 0 : if (conn->errorMessage.len == oldmsglen)
1142 : 0 : appendPQExpBufferStr(&conn->errorMessage,
1143 : : "fe_sendauth: error in SASL authentication\n");
2595 heikki.linnakangas@i 1144 : 0 : return STATUS_ERROR;
1145 : : }
2595 heikki.linnakangas@i 1146 :CBC 80 : break;
1147 : :
9544 bruce@momjian.us 1148 :UBC 0 : default:
516 peter@eisentraut.org 1149 : 0 : libpq_append_conn_error(conn, "authentication method %u not supported", areq);
9357 bruce@momjian.us 1150 : 0 : return STATUS_ERROR;
1151 : : }
1152 : :
9357 bruce@momjian.us 1153 :CBC 11604 : return STATUS_OK;
1154 : : }
1155 : :
1156 : :
1157 : : /*
1158 : : * pg_fe_getusername
1159 : : *
1160 : : * Returns a pointer to malloc'd space containing the name of the
1161 : : * specified user_id. If there is an error, return NULL, and append
1162 : : * a suitable error message to *errorMessage if that's not NULL.
1163 : : *
1164 : : * Caution: on Windows, the user_id argument is ignored, and we always
1165 : : * fetch the current user's name.
1166 : : */
1167 : : char *
824 tgl@sss.pgh.pa.us 1168 : 10874 : pg_fe_getusername(uid_t user_id, PQExpBuffer errorMessage)
1169 : : {
3381 1170 : 10874 : char *result = NULL;
7403 neilc@samurai.com 1171 : 10874 : const char *name = NULL;
1172 : :
1173 : : #ifdef WIN32
1174 : : /* Microsoft recommends buffer size of UNLEN+1, where UNLEN = 256 */
1175 : : char username[256 + 1];
1176 : : DWORD namesize = sizeof(username);
1177 : : #else
1178 : : char pwdbuf[BUFSIZ];
1179 : : #endif
1180 : :
1181 : : #ifdef WIN32
1182 : : if (GetUserName(username, &namesize))
1183 : : name = username;
1184 : : else if (errorMessage)
1185 : : libpq_append_error(errorMessage,
1186 : : "user name lookup failure: error code %lu",
1187 : : GetLastError());
1188 : : #else
824 tgl@sss.pgh.pa.us 1189 [ + - ]: 10874 : if (pg_get_user_name(user_id, pwdbuf, sizeof(pwdbuf)))
1190 : 10874 : name = pwdbuf;
3381 tgl@sss.pgh.pa.us 1191 [ # # ]:UBC 0 : else if (errorMessage)
824 1192 : 0 : appendPQExpBuffer(errorMessage, "%s\n", pwdbuf);
1193 : : #endif
1194 : :
3381 tgl@sss.pgh.pa.us 1195 [ + - ]:CBC 10874 : if (name)
1196 : : {
1197 : 10874 : result = strdup(name);
1198 [ - + - - ]: 10874 : if (result == NULL && errorMessage)
516 peter@eisentraut.org 1199 :UBC 0 : libpq_append_error(errorMessage, "out of memory");
1200 : : }
1201 : :
3381 tgl@sss.pgh.pa.us 1202 :CBC 10874 : return result;
1203 : : }
1204 : :
1205 : : /*
1206 : : * pg_fe_getauthname
1207 : : *
1208 : : * Returns a pointer to malloc'd space containing whatever name the user
1209 : : * has authenticated to the system. If there is an error, return NULL,
1210 : : * and append a suitable error message to *errorMessage if that's not NULL.
1211 : : */
1212 : : char *
824 1213 : 10874 : pg_fe_getauthname(PQExpBuffer errorMessage)
1214 : : {
1215 : : #ifdef WIN32
1216 : : return pg_fe_getusername(0, errorMessage);
1217 : : #else
1218 : 10874 : return pg_fe_getusername(geteuid(), errorMessage);
1219 : : #endif
1220 : : }
1221 : :
1222 : :
1223 : : /*
1224 : : * PQencryptPassword -- exported routine to encrypt a password with MD5
1225 : : *
1226 : : * This function is equivalent to calling PQencryptPasswordConn with
1227 : : * "md5" as the encryption method, except that this doesn't require
1228 : : * a connection object. This function is deprecated, use
1229 : : * PQencryptPasswordConn instead.
1230 : : */
1231 : : char *
2538 heikki.linnakangas@i 1232 :UBC 0 : PQencryptPassword(const char *passwd, const char *user)
1233 : : {
1234 : : char *crypt_pwd;
824 michael@paquier.xyz 1235 : 0 : const char *errstr = NULL;
1236 : :
2538 heikki.linnakangas@i 1237 : 0 : crypt_pwd = malloc(MD5_PASSWD_LEN + 1);
1238 [ # # ]: 0 : if (!crypt_pwd)
1239 : 0 : return NULL;
1240 : :
824 michael@paquier.xyz 1241 [ # # ]: 0 : if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd, &errstr))
1242 : : {
2538 heikki.linnakangas@i 1243 : 0 : free(crypt_pwd);
1244 : 0 : return NULL;
1245 : : }
1246 : :
1247 : 0 : return crypt_pwd;
1248 : : }
1249 : :
1250 : : /*
1251 : : * PQencryptPasswordConn -- exported routine to encrypt a password
1252 : : *
1253 : : * This is intended to be used by client applications that wish to send
1254 : : * commands like ALTER USER joe PASSWORD 'pwd'. The password need not
1255 : : * be sent in cleartext if it is encrypted on the client side. This is
1256 : : * good because it ensures the cleartext password won't end up in logs,
1257 : : * pg_stat displays, etc. We export the function so that clients won't
1258 : : * be dependent on low-level details like whether the encryption is MD5
1259 : : * or something else.
1260 : : *
1261 : : * Arguments are a connection object, the cleartext password, the SQL
1262 : : * name of the user it is for, and a string indicating the algorithm to
1263 : : * use for encrypting the password. If algorithm is NULL, this queries
1264 : : * the server for the current 'password_encryption' value. If you wish
1265 : : * to avoid that, e.g. to avoid blocking, you can execute
1266 : : * 'show password_encryption' yourself before calling this function, and
1267 : : * pass it as the algorithm.
1268 : : *
1269 : : * Return value is a malloc'd string. The client may assume the string
1270 : : * doesn't contain any special characters that would require escaping.
1271 : : * On error, an error message is stored in the connection object, and
1272 : : * returns NULL.
1273 : : */
1274 : : char *
2538 heikki.linnakangas@i 1275 :CBC 1 : PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
1276 : : const char *algorithm)
1277 : : {
1278 : : #define MAX_ALGORITHM_NAME_LEN 50
1279 : : char algobuf[MAX_ALGORITHM_NAME_LEN + 1];
1280 : 1 : char *crypt_pwd = NULL;
1281 : :
1282 [ - + ]: 1 : if (!conn)
6687 tgl@sss.pgh.pa.us 1283 :UBC 0 : return NULL;
1284 : :
786 tgl@sss.pgh.pa.us 1285 :CBC 1 : pqClearConnErrorState(conn);
1286 : :
1287 : : /* If no algorithm was given, ask the server. */
2538 heikki.linnakangas@i 1288 [ + - ]: 1 : if (algorithm == NULL)
1289 : : {
1290 : : PGresult *res;
1291 : : char *val;
1292 : :
1293 : 1 : res = PQexec(conn, "show password_encryption");
1294 [ - + ]: 1 : if (res == NULL)
1295 : : {
1296 : : /* PQexec() should've set conn->errorMessage already */
2538 heikki.linnakangas@i 1297 :UBC 0 : return NULL;
1298 : : }
2538 heikki.linnakangas@i 1299 [ - + ]:CBC 1 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
1300 : : {
1301 : : /* PQexec() should've set conn->errorMessage already */
2538 heikki.linnakangas@i 1302 :UBC 0 : PQclear(res);
1303 : 0 : return NULL;
1304 : : }
2538 heikki.linnakangas@i 1305 [ + - - + ]:CBC 1 : if (PQntuples(res) != 1 || PQnfields(res) != 1)
1306 : : {
2538 heikki.linnakangas@i 1307 :UBC 0 : PQclear(res);
516 peter@eisentraut.org 1308 : 0 : libpq_append_conn_error(conn, "unexpected shape of result set returned for SHOW");
2538 heikki.linnakangas@i 1309 : 0 : return NULL;
1310 : : }
2538 heikki.linnakangas@i 1311 :CBC 1 : val = PQgetvalue(res, 0, 0);
1312 : :
1313 [ - + ]: 1 : if (strlen(val) > MAX_ALGORITHM_NAME_LEN)
1314 : : {
2538 heikki.linnakangas@i 1315 :UBC 0 : PQclear(res);
516 peter@eisentraut.org 1316 : 0 : libpq_append_conn_error(conn, "password_encryption value too long");
2538 heikki.linnakangas@i 1317 : 0 : return NULL;
1318 : : }
2538 heikki.linnakangas@i 1319 :CBC 1 : strcpy(algobuf, val);
1320 : 1 : PQclear(res);
1321 : :
1322 : 1 : algorithm = algobuf;
1323 : : }
1324 : :
1325 : : /*
1326 : : * Also accept "on" and "off" as aliases for "md5", because
1327 : : * password_encryption was a boolean before PostgreSQL 10. We refuse to
1328 : : * send the password in plaintext even if it was "off".
1329 : : */
2537 1330 [ + - ]: 1 : if (strcmp(algorithm, "on") == 0 ||
2533 1331 [ - + ]: 1 : strcmp(algorithm, "off") == 0)
2537 heikki.linnakangas@i 1332 :UBC 0 : algorithm = "md5";
1333 : :
1334 : : /*
1335 : : * Ok, now we know what algorithm to use
1336 : : */
2538 heikki.linnakangas@i 1337 [ + - ]:CBC 1 : if (strcmp(algorithm, "scram-sha-256") == 0)
1338 : : {
822 michael@paquier.xyz 1339 : 1 : const char *errstr = NULL;
1340 : :
384 dgustafsson@postgres 1341 : 1 : crypt_pwd = pg_fe_scram_build_secret(passwd,
1342 : : conn->scram_sha_256_iterations,
1343 : : &errstr);
824 tgl@sss.pgh.pa.us 1344 [ - + ]: 1 : if (!crypt_pwd)
516 peter@eisentraut.org 1345 :UBC 0 : libpq_append_conn_error(conn, "could not encrypt password: %s", errstr);
1346 : : }
2538 heikki.linnakangas@i 1347 [ # # ]: 0 : else if (strcmp(algorithm, "md5") == 0)
1348 : : {
1349 : 0 : crypt_pwd = malloc(MD5_PASSWD_LEN + 1);
1350 [ # # ]: 0 : if (crypt_pwd)
1351 : : {
824 michael@paquier.xyz 1352 : 0 : const char *errstr = NULL;
1353 : :
1354 [ # # ]: 0 : if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd, &errstr))
1355 : : {
516 peter@eisentraut.org 1356 : 0 : libpq_append_conn_error(conn, "could not encrypt password: %s", errstr);
2538 heikki.linnakangas@i 1357 : 0 : free(crypt_pwd);
1358 : 0 : crypt_pwd = NULL;
1359 : : }
1360 : : }
1361 : : else
516 peter@eisentraut.org 1362 : 0 : libpq_append_conn_error(conn, "out of memory");
1363 : : }
1364 : : else
1365 : : {
1366 : 0 : libpq_append_conn_error(conn, "unrecognized password encryption algorithm \"%s\"",
1367 : : algorithm);
6687 tgl@sss.pgh.pa.us 1368 : 0 : return NULL;
1369 : : }
1370 : :
6687 tgl@sss.pgh.pa.us 1371 :CBC 1 : return crypt_pwd;
1372 : : }
1373 : :
1374 : : /*
1375 : : * PQchangePassword -- exported routine to change a password
1376 : : *
1377 : : * This is intended to be used by client applications that wish to
1378 : : * change the password for a user. The password is not sent in
1379 : : * cleartext because it is encrypted on the client side. This is
1380 : : * good because it ensures the cleartext password is never known by
1381 : : * the server, and therefore won't end up in logs, pg_stat displays,
1382 : : * etc. The password encryption is performed by PQencryptPasswordConn(),
1383 : : * which is passed a NULL for the algorithm argument. Hence encryption
1384 : : * is done according to the server's password_encryption
1385 : : * setting. We export the function so that clients won't be dependent
1386 : : * on the implementation specific details with respect to how the
1387 : : * server changes passwords.
1388 : : *
1389 : : * Arguments are a connection object, the SQL name of the target user,
1390 : : * and the cleartext password.
1391 : : *
1392 : : * Return value is the PGresult of the executed ALTER USER statement
1393 : : * or NULL if we never get there. The caller is responsible to PQclear()
1394 : : * the returned PGresult.
1395 : : *
1396 : : * PQresultStatus() should be called to check the return value for errors,
1397 : : * and PQerrorMessage() used to get more information about such errors.
1398 : : */
1399 : : PGresult *
96 mail@joeconway.com 1400 :GNC 1 : PQchangePassword(PGconn *conn, const char *user, const char *passwd)
1401 : : {
1402 : 1 : char *encrypted_password = PQencryptPasswordConn(conn, passwd,
1403 : : user, NULL);
1404 : :
1405 [ - + ]: 1 : if (!encrypted_password)
1406 : : {
1407 : : /* PQencryptPasswordConn() already registered the error */
96 mail@joeconway.com 1408 :UNC 0 : return NULL;
1409 : : }
1410 : : else
1411 : : {
96 mail@joeconway.com 1412 :GNC 1 : char *fmtpw = PQescapeLiteral(conn, encrypted_password,
1413 : : strlen(encrypted_password));
1414 : :
1415 : : /* no longer needed, so clean up now */
1416 : 1 : PQfreemem(encrypted_password);
1417 : :
1418 [ - + ]: 1 : if (!fmtpw)
1419 : : {
1420 : : /* PQescapeLiteral() already registered the error */
96 mail@joeconway.com 1421 :UNC 0 : return NULL;
1422 : : }
1423 : : else
1424 : : {
96 mail@joeconway.com 1425 :GNC 1 : char *fmtuser = PQescapeIdentifier(conn, user, strlen(user));
1426 : :
1427 [ - + ]: 1 : if (!fmtuser)
1428 : : {
1429 : : /* PQescapeIdentifier() already registered the error */
96 mail@joeconway.com 1430 :UNC 0 : PQfreemem(fmtpw);
1431 : 0 : return NULL;
1432 : : }
1433 : : else
1434 : : {
1435 : : PQExpBufferData buf;
1436 : : PGresult *res;
1437 : :
96 mail@joeconway.com 1438 :GNC 1 : initPQExpBuffer(&buf);
1439 : 1 : printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD %s",
1440 : : fmtuser, fmtpw);
1441 : :
1442 : 1 : res = PQexec(conn, buf.data);
1443 : :
1444 : : /* clean up */
1445 : 1 : termPQExpBuffer(&buf);
1446 : 1 : PQfreemem(fmtuser);
1447 : 1 : PQfreemem(fmtpw);
1448 : :
1449 : 1 : return res;
1450 : : }
1451 : : }
1452 : : }
1453 : : }
|