Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * auth.c
4 : : * Routines to handle network authentication
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/libpq/auth.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : :
16 : : #include "postgres.h"
17 : :
18 : : #include <sys/param.h>
19 : : #include <sys/select.h>
20 : : #include <sys/socket.h>
21 : : #include <netinet/in.h>
22 : : #include <netdb.h>
23 : : #include <pwd.h>
24 : : #include <unistd.h>
25 : :
26 : : #include "commands/user.h"
27 : : #include "common/ip.h"
28 : : #include "common/md5.h"
29 : : #include "libpq/auth.h"
30 : : #include "libpq/crypt.h"
31 : : #include "libpq/libpq.h"
32 : : #include "libpq/oauth.h"
33 : : #include "libpq/pqformat.h"
34 : : #include "libpq/sasl.h"
35 : : #include "libpq/scram.h"
36 : : #include "miscadmin.h"
37 : : #include "port/pg_bswap.h"
38 : : #include "postmaster/postmaster.h"
39 : : #include "replication/walsender.h"
40 : : #include "storage/ipc.h"
41 : : #include "tcop/backend_startup.h"
42 : : #include "utils/memutils.h"
43 : :
44 : : /*----------------------------------------------------------------
45 : : * Global authentication functions
46 : : *----------------------------------------------------------------
47 : : */
48 : : static void auth_failed(Port *port, int elevel, int status,
49 : : const char *logdetail);
50 : : static char *recv_password_packet(Port *port);
51 : : static bool md5_password_warning_enabled(void);
52 : : static void queue_md5_password_warning(void);
53 : :
54 : :
55 : : /*----------------------------------------------------------------
56 : : * Password-based authentication methods (password, md5, and scram-sha-256)
57 : : *----------------------------------------------------------------
58 : : */
59 : : static int CheckPasswordAuth(Port *port, const char **logdetail);
60 : : static int CheckPWChallengeAuth(Port *port, const char **logdetail);
61 : :
62 : : static int CheckMD5Auth(Port *port, char *shadow_pass,
63 : : const char **logdetail);
64 : :
65 : :
66 : : /*----------------------------------------------------------------
67 : : * Ident authentication
68 : : *----------------------------------------------------------------
69 : : */
70 : : /* Max size of username ident server can return (per RFC 1413) */
71 : : #define IDENT_USERNAME_MAX 512
72 : :
73 : : /* Standard TCP port number for Ident service. Assigned by IANA */
74 : : #define IDENT_PORT 113
75 : :
76 : : static int ident_inet(Port *port);
77 : :
78 : :
79 : : /*----------------------------------------------------------------
80 : : * Peer authentication
81 : : *----------------------------------------------------------------
82 : : */
83 : : static int auth_peer(Port *port);
84 : :
85 : :
86 : : /*----------------------------------------------------------------
87 : : * PAM authentication
88 : : *----------------------------------------------------------------
89 : : */
90 : : #ifdef USE_PAM
91 : : #ifdef HAVE_PAM_PAM_APPL_H
92 : : #include <pam/pam_appl.h>
93 : : #endif
94 : : #ifdef HAVE_SECURITY_PAM_APPL_H
95 : : #include <security/pam_appl.h>
96 : : #endif
97 : :
98 : : #define PGSQL_PAM_SERVICE "postgresql" /* Service name passed to PAM */
99 : :
100 : : /* Work around original Solaris' lack of "const" in the conv_proc signature */
101 : : #ifdef _PAM_LEGACY_NONCONST
102 : : #define PG_PAM_CONST
103 : : #else
104 : : #define PG_PAM_CONST const
105 : : #endif
106 : :
107 : : static int CheckPAMAuth(Port *port, const char *user, const char *password);
108 : : static int pam_passwd_conv_proc(int num_msg,
109 : : PG_PAM_CONST struct pam_message **msg,
110 : : struct pam_response **resp, void *appdata_ptr);
111 : :
112 : : static struct pam_conv pam_passw_conv = {
113 : : &pam_passwd_conv_proc,
114 : : NULL
115 : : };
116 : :
117 : : static const char *pam_passwd = NULL; /* Workaround for Solaris 2.6
118 : : * brokenness */
119 : : static Port *pam_port_cludge; /* Workaround for passing "Port *port" into
120 : : * pam_passwd_conv_proc */
121 : : static bool pam_no_password; /* For detecting no-password-given */
122 : : #endif /* USE_PAM */
123 : :
124 : :
125 : : /*----------------------------------------------------------------
126 : : * BSD authentication
127 : : *----------------------------------------------------------------
128 : : */
129 : : #ifdef USE_BSD_AUTH
130 : : #include <bsd_auth.h>
131 : :
132 : : static int CheckBSDAuth(Port *port, char *user);
133 : : #endif /* USE_BSD_AUTH */
134 : :
135 : :
136 : : /*----------------------------------------------------------------
137 : : * LDAP authentication
138 : : *----------------------------------------------------------------
139 : : */
140 : : #ifdef USE_LDAP
141 : : #ifndef WIN32
142 : : /* We use a deprecated function to keep the codepath the same as win32. */
143 : : #define LDAP_DEPRECATED 1
144 : : #include <ldap.h>
145 : : #else
146 : : #include <winldap.h>
147 : :
148 : : #endif
149 : :
150 : : static int CheckLDAPAuth(Port *port);
151 : :
152 : : /* LDAP_OPT_DIAGNOSTIC_MESSAGE is the newer spelling */
153 : : #ifndef LDAP_OPT_DIAGNOSTIC_MESSAGE
154 : : #define LDAP_OPT_DIAGNOSTIC_MESSAGE LDAP_OPT_ERROR_STRING
155 : : #endif
156 : :
157 : : /* Default LDAP password mutator hook, can be overridden by a shared library */
158 : : static char *dummy_ldap_password_mutator(char *input);
159 : : auth_password_hook_typ ldap_password_hook = dummy_ldap_password_mutator;
160 : :
161 : : #endif /* USE_LDAP */
162 : :
163 : : /*----------------------------------------------------------------
164 : : * Cert authentication
165 : : *----------------------------------------------------------------
166 : : */
167 : : #ifdef USE_SSL
168 : : static int CheckCertAuth(Port *port);
169 : : #endif
170 : :
171 : :
172 : : /*----------------------------------------------------------------
173 : : * Kerberos and GSSAPI GUCs
174 : : *----------------------------------------------------------------
175 : : */
176 : : char *pg_krb_server_keyfile;
177 : : bool pg_krb_caseins_users;
178 : : bool pg_gss_accept_delegation;
179 : :
180 : :
181 : : /*----------------------------------------------------------------
182 : : * GSSAPI Authentication
183 : : *----------------------------------------------------------------
184 : : */
185 : : #ifdef ENABLE_GSS
186 : : #include "libpq/be-gssapi-common.h"
187 : :
188 : : static int pg_GSS_checkauth(Port *port);
189 : : static int pg_GSS_recvauth(Port *port);
190 : : #endif /* ENABLE_GSS */
191 : :
192 : :
193 : : /*----------------------------------------------------------------
194 : : * SSPI Authentication
195 : : *----------------------------------------------------------------
196 : : */
197 : : #ifdef ENABLE_SSPI
198 : : typedef SECURITY_STATUS
199 : : (WINAPI * QUERY_SECURITY_CONTEXT_TOKEN_FN) (PCtxtHandle, void **);
200 : : static int pg_SSPI_recvauth(Port *port);
201 : : static int pg_SSPI_make_upn(char *accountname,
202 : : size_t accountnamesize,
203 : : char *domainname,
204 : : size_t domainnamesize,
205 : : bool update_accountname);
206 : : #endif
207 : :
208 : :
209 : : /*----------------------------------------------------------------
210 : : * Global authentication functions
211 : : *----------------------------------------------------------------
212 : : */
213 : :
214 : : /*
215 : : * This hook allows plugins to get control following client authentication,
216 : : * but before the user has been informed about the results. It could be used
217 : : * to record login events, insert a delay after failed authentication, etc.
218 : : */
219 : : ClientAuthentication_hook_type ClientAuthentication_hook = NULL;
220 : :
221 : : /*
222 : : * Tell the user the authentication failed, but not (much about) why.
223 : : *
224 : : * There is a tradeoff here between security concerns and making life
225 : : * unnecessarily difficult for legitimate users. We would not, for example,
226 : : * want to report the password we were expecting to receive...
227 : : * But it seems useful to report the username and authorization method
228 : : * in use, and these are items that must be presumed known to an attacker
229 : : * anyway.
230 : : * Note that many sorts of failure report additional information in the
231 : : * postmaster log, which we hope is only readable by good guys. In
232 : : * particular, if logdetail isn't NULL, we send that string to the log
233 : : * when the elevel allows.
234 : : */
235 : : static void
236 : 51 : auth_failed(Port *port, int elevel, int status, const char *logdetail)
237 : : {
238 : : const char *errstr;
239 : : char *cdetail;
240 : 51 : int errcode_return = ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION;
241 : :
242 : : Assert(elevel >= FATAL); /* we must exit here */
243 : :
244 : : /*
245 : : * If we failed due to EOF from client, just quit; there's no point in
246 : : * trying to send a message to the client, and not much point in logging
247 : : * the failure in the postmaster log. (Logging the failure might be
248 : : * desirable, were it not for the fact that libpq closes the connection
249 : : * unceremoniously if challenged for a password when it hasn't got one to
250 : : * send. We'll get a useless log entry for every psql connection under
251 : : * password auth, even if it's perfectly successful, if we log STATUS_EOF
252 : : * events.)
253 : : */
254 [ + + ]: 51 : if (status == STATUS_EOF)
255 : 23 : proc_exit(0);
256 : :
257 [ - + - + : 28 : switch (port->hba->auth_method)
+ - - - -
+ + - - ]
258 : : {
259 : 0 : case uaReject:
260 : : case uaImplicitReject:
261 : 0 : errstr = gettext_noop("authentication failed for user \"%s\": host rejected");
262 : 0 : break;
263 : 1 : case uaTrust:
264 : 1 : errstr = gettext_noop("\"trust\" authentication failed for user \"%s\"");
265 : 1 : break;
266 : 0 : case uaIdent:
267 : 0 : errstr = gettext_noop("Ident authentication failed for user \"%s\"");
268 : 0 : break;
269 : 6 : case uaPeer:
270 : 6 : errstr = gettext_noop("Peer authentication failed for user \"%s\"");
271 : 6 : break;
272 : 6 : case uaPassword:
273 : : case uaMD5:
274 : : case uaSCRAM:
275 : 6 : errstr = gettext_noop("password authentication failed for user \"%s\"");
276 : : /* We use it to indicate if a .pgpass password failed. */
277 : 6 : errcode_return = ERRCODE_INVALID_PASSWORD;
278 : 6 : break;
279 : 0 : case uaGSS:
280 : 0 : errstr = gettext_noop("GSSAPI authentication failed for user \"%s\"");
281 : 0 : break;
282 : 0 : case uaSSPI:
283 : 0 : errstr = gettext_noop("SSPI authentication failed for user \"%s\"");
284 : 0 : break;
285 : 0 : case uaPAM:
286 : 0 : errstr = gettext_noop("PAM authentication failed for user \"%s\"");
287 : 0 : break;
288 : 0 : case uaBSD:
289 : 0 : errstr = gettext_noop("BSD authentication failed for user \"%s\"");
290 : 0 : break;
291 : 14 : case uaLDAP:
292 : 14 : errstr = gettext_noop("LDAP authentication failed for user \"%s\"");
293 : 14 : break;
294 : 1 : case uaCert:
295 : 1 : errstr = gettext_noop("certificate authentication failed for user \"%s\"");
296 : 1 : break;
297 : 0 : case uaOAuth:
298 : 0 : errstr = gettext_noop("OAuth bearer authentication failed for user \"%s\"");
299 : 0 : break;
300 : 0 : default:
301 : 0 : errstr = gettext_noop("authentication failed for user \"%s\": invalid authentication method");
302 : 0 : break;
303 : : }
304 : :
305 : 28 : cdetail = psprintf(_("Connection matched file \"%s\" line %d: \"%s\""),
306 : 28 : port->hba->sourcefile, port->hba->linenumber,
307 : 28 : port->hba->rawline);
308 [ + + ]: 28 : if (logdetail)
309 : 2 : logdetail = psprintf("%s\n%s", logdetail, cdetail);
310 : : else
311 : 26 : logdetail = cdetail;
312 : :
313 [ + - + - ]: 28 : ereport(elevel,
314 : : (errcode(errcode_return),
315 : : errmsg(errstr, port->user_name),
316 : : logdetail ? errdetail_log("%s", logdetail) : 0));
317 : :
318 : : /* doesn't return */
319 : 0 : pg_unreachable();
320 : : }
321 : :
322 : :
323 : : /*
324 : : * Sets the authenticated identity for the current user. The provided string
325 : : * will be stored into MyClientConnectionInfo, alongside the current HBA
326 : : * method in use. The ID will be logged if log_connections has the
327 : : * 'authentication' option specified.
328 : : *
329 : : * Auth methods should call this routine exactly once, as soon as the user is
330 : : * successfully authenticated, even if they have reasons to know that
331 : : * authorization will fail later.
332 : : *
333 : : * The provided string will be copied into TopMemoryContext, to match the
334 : : * lifetime of MyClientConnectionInfo, so it is safe to pass a string that is
335 : : * managed by an external library.
336 : : */
337 : : void
338 : 139 : set_authn_id(Port *port, const char *id)
339 : : {
340 : : Assert(id);
341 : :
342 [ - + ]: 139 : if (MyClientConnectionInfo.authn_id)
343 : : {
344 : : /*
345 : : * An existing authn_id should never be overwritten; that means two
346 : : * authentication providers are fighting (or one is fighting itself).
347 : : * Don't leak any authn details to the client, but don't let the
348 : : * connection continue, either.
349 : : */
350 [ # # ]: 0 : ereport(FATAL,
351 : : (errmsg("authentication identifier set more than once"),
352 : : errdetail_log("previous identifier: \"%s\"; new identifier: \"%s\"",
353 : : MyClientConnectionInfo.authn_id, id)));
354 : : }
355 : :
356 : 139 : MyClientConnectionInfo.authn_id = MemoryContextStrdup(TopMemoryContext, id);
357 : 139 : MyClientConnectionInfo.auth_method = port->hba->auth_method;
358 : :
359 [ + + ]: 139 : if (log_connections & LOG_CONNECTION_AUTHENTICATION)
360 : : {
361 [ + - ]: 106 : ereport(LOG,
362 : : errmsg("connection authenticated: identity=\"%s\" method=%s "
363 : : "(%s:%d)",
364 : : MyClientConnectionInfo.authn_id,
365 : : hba_authname(MyClientConnectionInfo.auth_method),
366 : : port->hba->sourcefile, port->hba->linenumber));
367 : : }
368 : 139 : }
369 : :
370 : :
371 : : /*
372 : : * Client authentication starts here. If there is an error, this
373 : : * function does not return and the backend process is terminated.
374 : : */
375 : : void
376 : 14842 : ClientAuthentication(Port *port)
377 : : {
378 : 14842 : int status = STATUS_ERROR;
379 : 14842 : const char *logdetail = NULL;
380 : :
381 : : /*
382 : : * "Abandoned" is a SASL-specific state similar to STATUS_EOF, in that we
383 : : * don't want to generate any server logs. But it's caused by an in-band
384 : : * client action that requires a server response, not an out-of-band
385 : : * connection closure, so we can't just proc_exit() like we do with
386 : : * STATUS_EOF.
387 : : */
388 : 14842 : bool abandoned = false;
389 : :
390 : : /*
391 : : * Get the authentication method to use for this frontend/database
392 : : * combination. Note: we do not parse the file at this point; this has
393 : : * already been done elsewhere. hba.c dropped an error message into the
394 : : * server logfile if parsing the hba config file failed.
395 : : */
396 : 14842 : hba_getauthmethod(port);
397 : :
398 [ - + ]: 14842 : CHECK_FOR_INTERRUPTS();
399 : :
400 : : /*
401 : : * This is the first point where we have access to the hba record for the
402 : : * current connection, so perform any verifications based on the hba
403 : : * options field that should be done *before* the authentication here.
404 : : */
405 [ + + ]: 14842 : if (port->hba->clientcert != clientCertOff)
406 : : {
407 : : /* If we haven't loaded a root certificate store, fail */
408 [ + + ]: 36 : if (!secure_loaded_verify_locations())
409 [ + - ]: 2 : ereport(FATAL,
410 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
411 : : errmsg("client certificates can only be checked if a root certificate store is available")));
412 : :
413 : : /*
414 : : * If we loaded a root certificate store, and if a certificate is
415 : : * present on the client, then it has been verified against our root
416 : : * certificate store, and the connection would have been aborted
417 : : * already if it didn't verify ok.
418 : : */
419 [ + + ]: 34 : if (!port->peer_cert_valid)
420 [ + - ]: 5 : ereport(FATAL,
421 : : (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
422 : : errmsg("connection requires a valid client certificate")));
423 : : }
424 : :
425 : : /*
426 : : * Now proceed to do the actual authentication check
427 : : */
428 [ - + - - : 14835 : switch (port->hba->auth_method)
+ - + + -
- + + -
- ]
429 : : {
430 : 0 : case uaReject:
431 : :
432 : : /*
433 : : * An explicit "reject" entry in pg_hba.conf. This report exposes
434 : : * the fact that there's an explicit reject entry, which is
435 : : * perhaps not so desirable from a security standpoint; but the
436 : : * message for an implicit reject could confuse the DBA a lot when
437 : : * the true situation is a match to an explicit reject. And we
438 : : * don't want to change the message for an implicit reject. As
439 : : * noted below, the additional information shown here doesn't
440 : : * expose anything not known to an attacker.
441 : : */
442 : : {
443 : : char hostinfo[NI_MAXHOST];
444 : : const char *encryption_state;
445 : :
446 : 0 : pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
447 : : hostinfo, sizeof(hostinfo),
448 : : NULL, 0,
449 : : NI_NUMERICHOST);
450 : :
451 : 0 : encryption_state =
452 : : #ifdef ENABLE_GSS
453 : : (port->gss && port->gss->enc) ? _("GSS encryption") :
454 : : #endif
455 : : #ifdef USE_SSL
456 [ # # ]: 0 : port->ssl_in_use ? _("SSL encryption") :
457 : : #endif
458 : 0 : _("no encryption");
459 : :
460 [ # # # # ]: 0 : if (am_walsender && !am_db_walsender)
461 [ # # ]: 0 : ereport(FATAL,
462 : : (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
463 : : /* translator: last %s describes encryption state */
464 : : errmsg("pg_hba.conf rejects replication connection for host \"%s\", user \"%s\", %s",
465 : : hostinfo, port->user_name,
466 : : encryption_state)));
467 : : else
468 [ # # ]: 0 : ereport(FATAL,
469 : : (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
470 : : /* translator: last %s describes encryption state */
471 : : errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\", %s",
472 : : hostinfo, port->user_name,
473 : : port->database_name,
474 : : encryption_state)));
475 : : break;
476 : : }
477 : :
478 : 13 : case uaImplicitReject:
479 : :
480 : : /*
481 : : * No matching entry, so tell the user we fell through.
482 : : *
483 : : * NOTE: the extra info reported here is not a security breach,
484 : : * because all that info is known at the frontend and must be
485 : : * assumed known to bad guys. We're merely helping out the less
486 : : * clueful good guys.
487 : : */
488 : : {
489 : : char hostinfo[NI_MAXHOST];
490 : : const char *encryption_state;
491 : :
492 : 13 : pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
493 : : hostinfo, sizeof(hostinfo),
494 : : NULL, 0,
495 : : NI_NUMERICHOST);
496 : :
497 : 26 : encryption_state =
498 : : #ifdef ENABLE_GSS
499 : : (port->gss && port->gss->enc) ? _("GSS encryption") :
500 : : #endif
501 : : #ifdef USE_SSL
502 [ + + ]: 13 : port->ssl_in_use ? _("SSL encryption") :
503 : : #endif
504 : 10 : _("no encryption");
505 : :
506 : : #define HOSTNAME_LOOKUP_DETAIL(port) \
507 : : (port->remote_hostname ? \
508 : : (port->remote_hostname_resolv == +1 ? \
509 : : errdetail_log("Client IP address resolved to \"%s\", forward lookup matches.", \
510 : : port->remote_hostname) : \
511 : : port->remote_hostname_resolv == 0 ? \
512 : : errdetail_log("Client IP address resolved to \"%s\", forward lookup not checked.", \
513 : : port->remote_hostname) : \
514 : : port->remote_hostname_resolv == -1 ? \
515 : : errdetail_log("Client IP address resolved to \"%s\", forward lookup does not match.", \
516 : : port->remote_hostname) : \
517 : : port->remote_hostname_resolv == -2 ? \
518 : : errdetail_log("Could not translate client host name \"%s\" to IP address: %s.", \
519 : : port->remote_hostname, \
520 : : gai_strerror(port->remote_hostname_errcode)) : \
521 : : 0) \
522 : : : (port->remote_hostname_resolv == -2 ? \
523 : : errdetail_log("Could not resolve client IP address to a host name: %s.", \
524 : : gai_strerror(port->remote_hostname_errcode)) : \
525 : : 0))
526 : :
527 [ - + - - ]: 13 : if (am_walsender && !am_db_walsender)
528 [ # # # # : 0 : ereport(FATAL,
# # # # #
# # # #
# ]
529 : : (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
530 : : /* translator: last %s describes encryption state */
531 : : errmsg("no pg_hba.conf entry for replication connection from host \"%s\", user \"%s\", %s",
532 : : hostinfo, port->user_name,
533 : : encryption_state),
534 : : HOSTNAME_LOOKUP_DETAIL(port)));
535 : : else
536 [ + - + + : 13 : ereport(FATAL,
- + + - -
- - - -
+ ]
537 : : (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
538 : : /* translator: last %s describes encryption state */
539 : : errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
540 : : hostinfo, port->user_name,
541 : : port->database_name,
542 : : encryption_state),
543 : : HOSTNAME_LOOKUP_DETAIL(port)));
544 : : break;
545 : : }
546 : :
547 : 0 : case uaGSS:
548 : : #ifdef ENABLE_GSS
549 : : /* We might or might not have the gss workspace already */
550 : : if (port->gss == NULL)
551 : : port->gss = (pg_gssinfo *)
552 : : MemoryContextAllocZero(TopMemoryContext,
553 : : sizeof(pg_gssinfo));
554 : : port->gss->auth = true;
555 : :
556 : : /*
557 : : * If GSS state was set up while enabling encryption, we can just
558 : : * check the client's principal. Otherwise, ask for it.
559 : : */
560 : : if (port->gss->enc)
561 : : status = pg_GSS_checkauth(port);
562 : : else
563 : : {
564 : : sendAuthRequest(port, AUTH_REQ_GSS, NULL, 0);
565 : : status = pg_GSS_recvauth(port);
566 : : }
567 : : #else
568 : : Assert(false);
569 : : #endif
570 : 0 : break;
571 : :
572 : 0 : case uaSSPI:
573 : : #ifdef ENABLE_SSPI
574 : : if (port->gss == NULL)
575 : : port->gss = (pg_gssinfo *)
576 : : MemoryContextAllocZero(TopMemoryContext,
577 : : sizeof(pg_gssinfo));
578 : : sendAuthRequest(port, AUTH_REQ_SSPI, NULL, 0);
579 : : status = pg_SSPI_recvauth(port);
580 : : #else
581 : : Assert(false);
582 : : #endif
583 : 0 : break;
584 : :
585 : 28 : case uaPeer:
586 : 28 : status = auth_peer(port);
587 : 28 : break;
588 : :
589 : 0 : case uaIdent:
590 : 0 : status = ident_inet(port);
591 : 0 : break;
592 : :
593 : 80 : case uaMD5:
594 : : case uaSCRAM:
595 : 80 : status = CheckPWChallengeAuth(port, &logdetail);
596 : 80 : break;
597 : :
598 : 20 : case uaPassword:
599 : 20 : status = CheckPasswordAuth(port, &logdetail);
600 : 20 : break;
601 : :
602 : 0 : case uaPAM:
603 : : #ifdef USE_PAM
604 : 0 : status = CheckPAMAuth(port, port->user_name, "");
605 : : #else
606 : : Assert(false);
607 : : #endif /* USE_PAM */
608 : 0 : break;
609 : :
610 : 0 : case uaBSD:
611 : : #ifdef USE_BSD_AUTH
612 : : status = CheckBSDAuth(port, port->user_name);
613 : : #else
614 : : Assert(false);
615 : : #endif /* USE_BSD_AUTH */
616 : 0 : break;
617 : :
618 : 29 : case uaLDAP:
619 : : #ifdef USE_LDAP
620 : 29 : status = CheckLDAPAuth(port);
621 : : #else
622 : : Assert(false);
623 : : #endif
624 : 29 : break;
625 : 14665 : case uaCert:
626 : : /* uaCert will be treated as if clientcert=verify-full (uaTrust) */
627 : : case uaTrust:
628 : 14665 : status = STATUS_OK;
629 : 14665 : break;
630 : 0 : case uaOAuth:
631 : 0 : status = CheckSASLAuth(&pg_be_oauth_mech, port, NULL, &logdetail,
632 : : &abandoned);
633 : 0 : break;
634 : : }
635 : :
636 [ + + + + ]: 14822 : if ((status == STATUS_OK && port->hba->clientcert == clientCertFull)
637 [ - + ]: 14794 : || port->hba->auth_method == uaCert)
638 : : {
639 : : /*
640 : : * Make sure we only check the certificate if we use the cert method
641 : : * or verify-full option.
642 : : */
643 : : #ifdef USE_SSL
644 : 28 : status = CheckCertAuth(port);
645 : : #else
646 : : Assert(false);
647 : : #endif
648 : : }
649 : :
650 [ + + + + ]: 14822 : if ((log_connections & LOG_CONNECTION_AUTHENTICATION) &&
651 : 254 : status == STATUS_OK &&
652 [ + + ]: 254 : !MyClientConnectionInfo.authn_id)
653 : : {
654 : : /*
655 : : * Normally, if log_connections is set, the call to set_authn_id()
656 : : * will log the connection. However, if that function is never
657 : : * called, perhaps because the trust method is in use, then we handle
658 : : * the logging here instead.
659 : : */
660 [ + - ]: 155 : ereport(LOG,
661 : : errmsg("connection authenticated: user=\"%s\" method=%s "
662 : : "(%s:%d)",
663 : : port->user_name, hba_authname(port->hba->auth_method),
664 : : port->hba->sourcefile, port->hba->linenumber));
665 : : }
666 : :
667 [ - + ]: 14822 : if (ClientAuthentication_hook)
668 : 0 : (*ClientAuthentication_hook) (port, status);
669 : :
670 [ + + ]: 14822 : if (status == STATUS_OK)
671 : 14771 : sendAuthRequest(port, AUTH_REQ_OK, NULL, 0);
672 : : else
673 : 51 : auth_failed(port,
674 [ - + ]: 51 : abandoned ? FATAL_CLIENT_ONLY : FATAL,
675 : : status,
676 : : logdetail);
677 : 14771 : }
678 : :
679 : :
680 : : /*
681 : : * Send an authentication request packet to the frontend.
682 : : */
683 : : void
684 : 15024 : sendAuthRequest(Port *port, AuthRequest areq, const void *extradata, int extralen)
685 : : {
686 : : StringInfoData buf;
687 : :
688 [ - + ]: 15024 : CHECK_FOR_INTERRUPTS();
689 : :
690 : 15024 : pq_beginmessage(&buf, PqMsg_AuthenticationRequest);
691 : 15024 : pq_sendint32(&buf, (int32) areq);
692 [ + + ]: 15024 : if (extralen > 0)
693 : 204 : pq_sendbytes(&buf, extradata, extralen);
694 : :
695 : 15024 : pq_endmessage(&buf);
696 : :
697 : : /*
698 : : * Flush message so client will see it, except for AUTH_REQ_OK and
699 : : * AUTH_REQ_SASL_FIN, which need not be sent until we are ready for
700 : : * queries.
701 : : */
702 [ + + + + ]: 15024 : if (areq != AUTH_REQ_OK && areq != AUTH_REQ_SASL_FIN)
703 : 194 : pq_flush();
704 : :
705 [ - + ]: 15024 : CHECK_FOR_INTERRUPTS();
706 : 15024 : }
707 : :
708 : : /*
709 : : * Collect password response packet from frontend.
710 : : *
711 : : * Returns NULL if couldn't get password, else palloc'd string.
712 : : */
713 : : static char *
714 : 53 : recv_password_packet(Port *port)
715 : : {
716 : : StringInfoData buf;
717 : : int mtype;
718 : :
719 : 53 : pq_startmsgread();
720 : :
721 : : /* Expect 'p' message type */
722 : 53 : mtype = pq_getbyte();
723 [ + + ]: 53 : if (mtype != PqMsg_PasswordMessage)
724 : : {
725 : : /*
726 : : * If the client just disconnects without offering a password, don't
727 : : * make a log entry. This is legal per protocol spec and in fact
728 : : * commonly done by psql, so complaining just clutters the log.
729 : : */
730 [ - + ]: 12 : if (mtype != EOF)
731 [ # # ]: 0 : ereport(ERROR,
732 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
733 : : errmsg("expected password response, got message type %d",
734 : : mtype)));
735 : 12 : return NULL; /* EOF or bad message type */
736 : : }
737 : :
738 : 41 : initStringInfo(&buf);
739 [ - + ]: 41 : if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH)) /* receive password */
740 : : {
741 : : /* EOF - pq_getmessage already logged a suitable message */
742 : 0 : pfree(buf.data);
743 : 0 : return NULL;
744 : : }
745 : :
746 : : /*
747 : : * Apply sanity check: password packet length should agree with length of
748 : : * contained string. Note it is safe to use strlen here because
749 : : * StringInfo is guaranteed to have an appended '\0'.
750 : : */
751 [ - + ]: 41 : if (strlen(buf.data) + 1 != buf.len)
752 [ # # ]: 0 : ereport(ERROR,
753 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
754 : : errmsg("invalid password packet size")));
755 : :
756 : : /*
757 : : * Don't allow an empty password. Libpq treats an empty password the same
758 : : * as no password at all, and won't even try to authenticate. But other
759 : : * clients might, so allowing it would be confusing.
760 : : *
761 : : * Note that this only catches an empty password sent by the client in
762 : : * plaintext. There's also a check in CREATE/ALTER USER that prevents an
763 : : * empty string from being stored as a user's password in the first place.
764 : : * We rely on that for MD5 and SCRAM authentication, but we still need
765 : : * this check here, to prevent an empty password from being used with
766 : : * authentication methods that check the password against an external
767 : : * system, like PAM and LDAP.
768 : : */
769 [ - + ]: 41 : if (buf.len == 1)
770 [ # # ]: 0 : ereport(ERROR,
771 : : (errcode(ERRCODE_INVALID_PASSWORD),
772 : : errmsg("empty password returned by client")));
773 : :
774 : : /* Do not echo password to logs, for security. */
775 [ - + ]: 41 : elog(DEBUG5, "received password packet");
776 : :
777 : : /*
778 : : * Return the received string. Note we do not attempt to do any
779 : : * character-set conversion on it; since we don't yet know the client's
780 : : * encoding, there wouldn't be much point.
781 : : */
782 : 41 : return buf.data;
783 : : }
784 : :
785 : :
786 : : /*----------------------------------------------------------------
787 : : * Password-based authentication mechanisms
788 : : *----------------------------------------------------------------
789 : : */
790 : :
791 : : /*
792 : : * Plaintext password authentication.
793 : : */
794 : : static int
795 : 20 : CheckPasswordAuth(Port *port, const char **logdetail)
796 : : {
797 : : char *passwd;
798 : : int result;
799 : : char *shadow_pass;
800 : 20 : bool md5_password = false;
801 : :
802 : 20 : sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
803 : :
804 : 20 : passwd = recv_password_packet(port);
805 [ + + ]: 20 : if (passwd == NULL)
806 : 9 : return STATUS_EOF; /* client wouldn't send password */
807 : :
808 : 11 : shadow_pass = get_role_password(port->user_name, logdetail);
809 [ + - ]: 11 : if (shadow_pass)
810 : : {
811 : 11 : result = plain_crypt_verify(port->user_name, shadow_pass, passwd,
812 : : logdetail);
813 : 11 : md5_password = (get_password_type(shadow_pass) == PASSWORD_TYPE_MD5);
814 : : }
815 : : else
816 : 0 : result = STATUS_ERROR;
817 : :
818 [ + - ]: 11 : if (shadow_pass)
819 : 11 : pfree(shadow_pass);
820 : 11 : pfree(passwd);
821 : :
822 [ + - ]: 11 : if (result == STATUS_OK)
823 : : {
824 [ + + ]: 11 : if (md5_password)
825 : 2 : queue_md5_password_warning();
826 : 11 : set_authn_id(port, port->user_name);
827 : : }
828 : :
829 : 11 : return result;
830 : : }
831 : :
832 : : /*
833 : : * MD5 and SCRAM authentication.
834 : : */
835 : : static int
836 : 80 : CheckPWChallengeAuth(Port *port, const char **logdetail)
837 : : {
838 : : int auth_result;
839 : : char *shadow_pass;
840 : : PasswordType pwtype;
841 : :
842 : : Assert(port->hba->auth_method == uaSCRAM ||
843 : : port->hba->auth_method == uaMD5);
844 : :
845 : : /* First look up the user's password. */
846 : 80 : shadow_pass = get_role_password(port->user_name, logdetail);
847 : :
848 : : /*
849 : : * If the user does not exist, or has no password or it's expired, we
850 : : * still go through the motions of authentication, to avoid revealing to
851 : : * the client that the user didn't exist. If 'md5' is allowed, we choose
852 : : * whether to use 'md5' or 'scram-sha-256' authentication based on current
853 : : * password_encryption setting. The idea is that most genuine users
854 : : * probably have a password of that type, and if we pretend that this user
855 : : * had a password of that type, too, it "blends in" best.
856 : : */
857 [ + + ]: 80 : if (!shadow_pass)
858 : 1 : pwtype = Password_encryption;
859 : : else
860 : 79 : pwtype = get_password_type(shadow_pass);
861 : :
862 : : /*
863 : : * If 'md5' authentication is allowed, decide whether to perform 'md5' or
864 : : * 'scram-sha-256' authentication based on the type of password the user
865 : : * has. If it's an MD5 hash, we must do MD5 authentication, and if it's a
866 : : * SCRAM secret, we must do SCRAM authentication.
867 : : *
868 : : * If MD5 authentication is not allowed, always use SCRAM. If the user
869 : : * had an MD5 password, CheckSASLAuth() with the SCRAM mechanism will
870 : : * fail.
871 : : */
872 [ + + + + ]: 80 : if (port->hba->auth_method == uaMD5 && pwtype == PASSWORD_TYPE_MD5)
873 : 4 : auth_result = CheckMD5Auth(port, shadow_pass, logdetail);
874 : : else
875 : 76 : auth_result = CheckSASLAuth(&pg_be_scram_mech, port, shadow_pass,
876 : : logdetail, NULL /* can't abandon SCRAM */ );
877 : :
878 [ + + ]: 80 : if (shadow_pass)
879 : 79 : pfree(shadow_pass);
880 : : else
881 : : {
882 : : /*
883 : : * If get_role_password() returned error, authentication better not
884 : : * have succeeded.
885 : : */
886 : : Assert(auth_result != STATUS_OK);
887 : : }
888 : :
889 [ + + ]: 80 : if (auth_result == STATUS_OK)
890 : 61 : set_authn_id(port, port->user_name);
891 : :
892 : 80 : return auth_result;
893 : : }
894 : :
895 : : static int
896 : 4 : CheckMD5Auth(Port *port, char *shadow_pass, const char **logdetail)
897 : : {
898 : : uint8 md5Salt[4]; /* Password salt */
899 : : char *passwd;
900 : : int result;
901 : :
902 : : /* include the salt to use for computing the response */
903 [ - + ]: 4 : if (!pg_strong_random(md5Salt, 4))
904 : : {
905 [ # # ]: 0 : ereport(LOG,
906 : : (errmsg("could not generate random MD5 salt")));
907 : 0 : return STATUS_ERROR;
908 : : }
909 : :
910 : 4 : sendAuthRequest(port, AUTH_REQ_MD5, md5Salt, 4);
911 : :
912 : 4 : passwd = recv_password_packet(port);
913 [ + + ]: 4 : if (passwd == NULL)
914 : 2 : return STATUS_EOF; /* client wouldn't send password */
915 : :
916 [ + - ]: 2 : if (shadow_pass)
917 : 2 : result = md5_crypt_verify(port->user_name, shadow_pass, passwd,
918 : : md5Salt, 4, logdetail);
919 : : else
920 : 0 : result = STATUS_ERROR;
921 : :
922 : 2 : pfree(passwd);
923 : :
924 [ + - ]: 2 : if (result == STATUS_OK)
925 : 2 : queue_md5_password_warning();
926 : :
927 : 2 : return result;
928 : : }
929 : :
930 : : static bool
931 : 4 : md5_password_warning_enabled(void)
932 : : {
933 : 4 : return md5_password_warnings;
934 : : }
935 : :
936 : : static void
937 : 4 : queue_md5_password_warning(void)
938 : : {
939 : : MemoryContext oldcontext;
940 : : char *warning;
941 : : char *detail;
942 : :
943 : 4 : oldcontext = MemoryContextSwitchTo(TopMemoryContext);
944 : :
945 : 4 : warning = pstrdup(_("authenticated with an MD5-encrypted password"));
946 : 4 : detail = pstrdup(_("MD5 password support is deprecated and will be removed in a future release of PostgreSQL."));
947 : 4 : StoreConnectionWarning(warning, detail, md5_password_warning_enabled);
948 : :
949 : 4 : MemoryContextSwitchTo(oldcontext);
950 : 4 : }
951 : :
952 : :
953 : : /*----------------------------------------------------------------
954 : : * GSSAPI authentication system
955 : : *----------------------------------------------------------------
956 : : */
957 : : #ifdef ENABLE_GSS
958 : : static int
959 : : pg_GSS_recvauth(Port *port)
960 : : {
961 : : OM_uint32 maj_stat,
962 : : min_stat,
963 : : lmin_s,
964 : : gflags;
965 : : int mtype;
966 : : StringInfoData buf;
967 : : gss_buffer_desc gbuf;
968 : : gss_cred_id_t delegated_creds;
969 : :
970 : : /*
971 : : * Use the configured keytab, if there is one. As we now require MIT
972 : : * Kerberos, we might consider using the credential store extensions in
973 : : * the future instead of the environment variable.
974 : : */
975 : : if (pg_krb_server_keyfile != NULL && pg_krb_server_keyfile[0] != '\0')
976 : : {
977 : : if (setenv("KRB5_KTNAME", pg_krb_server_keyfile, 1) != 0)
978 : : {
979 : : /* The only likely failure cause is OOM, so use that errcode */
980 : : ereport(FATAL,
981 : : (errcode(ERRCODE_OUT_OF_MEMORY),
982 : : errmsg("could not set environment: %m")));
983 : : }
984 : : }
985 : :
986 : : /*
987 : : * We accept any service principal that's present in our keytab. This
988 : : * increases interoperability between kerberos implementations that see
989 : : * for example case sensitivity differently, while not really opening up
990 : : * any vector of attack.
991 : : */
992 : : port->gss->cred = GSS_C_NO_CREDENTIAL;
993 : :
994 : : /*
995 : : * Initialize sequence with an empty context
996 : : */
997 : : port->gss->ctx = GSS_C_NO_CONTEXT;
998 : :
999 : : delegated_creds = GSS_C_NO_CREDENTIAL;
1000 : : port->gss->delegated_creds = false;
1001 : :
1002 : : /*
1003 : : * Loop through GSSAPI message exchange. This exchange can consist of
1004 : : * multiple messages sent in both directions. First message is always from
1005 : : * the client. All messages from client to server are password packets
1006 : : * (type 'p').
1007 : : */
1008 : : do
1009 : : {
1010 : : pq_startmsgread();
1011 : :
1012 : : CHECK_FOR_INTERRUPTS();
1013 : :
1014 : : mtype = pq_getbyte();
1015 : : if (mtype != PqMsg_GSSResponse)
1016 : : {
1017 : : /* Only log error if client didn't disconnect. */
1018 : : if (mtype != EOF)
1019 : : ereport(ERROR,
1020 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
1021 : : errmsg("expected GSS response, got message type %d",
1022 : : mtype)));
1023 : : return STATUS_ERROR;
1024 : : }
1025 : :
1026 : : /* Get the actual GSS token */
1027 : : initStringInfo(&buf);
1028 : : if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH))
1029 : : {
1030 : : /* EOF - pq_getmessage already logged error */
1031 : : pfree(buf.data);
1032 : : return STATUS_ERROR;
1033 : : }
1034 : :
1035 : : /* Map to GSSAPI style buffer */
1036 : : gbuf.length = buf.len;
1037 : : gbuf.value = buf.data;
1038 : :
1039 : : elog(DEBUG4, "processing received GSS token of length %zu",
1040 : : gbuf.length);
1041 : :
1042 : : maj_stat = gss_accept_sec_context(&min_stat,
1043 : : &port->gss->ctx,
1044 : : port->gss->cred,
1045 : : &gbuf,
1046 : : GSS_C_NO_CHANNEL_BINDINGS,
1047 : : &port->gss->name,
1048 : : NULL,
1049 : : &port->gss->outbuf,
1050 : : &gflags,
1051 : : NULL,
1052 : : pg_gss_accept_delegation ? &delegated_creds : NULL);
1053 : :
1054 : : /* gbuf no longer used */
1055 : : pfree(buf.data);
1056 : :
1057 : : elog(DEBUG5, "gss_accept_sec_context major: %u, "
1058 : : "minor: %u, outlen: %zu, outflags: %x",
1059 : : maj_stat, min_stat,
1060 : : port->gss->outbuf.length, gflags);
1061 : :
1062 : : CHECK_FOR_INTERRUPTS();
1063 : :
1064 : : if (delegated_creds != GSS_C_NO_CREDENTIAL && gflags & GSS_C_DELEG_FLAG)
1065 : : {
1066 : : pg_store_delegated_credential(delegated_creds);
1067 : : port->gss->delegated_creds = true;
1068 : : }
1069 : :
1070 : : if (port->gss->outbuf.length != 0)
1071 : : {
1072 : : /*
1073 : : * Negotiation generated data to be sent to the client.
1074 : : */
1075 : : elog(DEBUG4, "sending GSS response token of length %zu",
1076 : : port->gss->outbuf.length);
1077 : :
1078 : : sendAuthRequest(port, AUTH_REQ_GSS_CONT,
1079 : : port->gss->outbuf.value, port->gss->outbuf.length);
1080 : :
1081 : : gss_release_buffer(&lmin_s, &port->gss->outbuf);
1082 : : }
1083 : :
1084 : : if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
1085 : : {
1086 : : gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
1087 : : pg_GSS_error(_("accepting GSS security context failed"),
1088 : : maj_stat, min_stat);
1089 : : return STATUS_ERROR;
1090 : : }
1091 : :
1092 : : if (maj_stat == GSS_S_CONTINUE_NEEDED)
1093 : : elog(DEBUG4, "GSS continue needed");
1094 : :
1095 : : } while (maj_stat == GSS_S_CONTINUE_NEEDED);
1096 : :
1097 : : if (port->gss->cred != GSS_C_NO_CREDENTIAL)
1098 : : {
1099 : : /*
1100 : : * Release service principal credentials
1101 : : */
1102 : : gss_release_cred(&min_stat, &port->gss->cred);
1103 : : }
1104 : : return pg_GSS_checkauth(port);
1105 : : }
1106 : :
1107 : : /*
1108 : : * Check whether the GSSAPI-authenticated user is allowed to connect as the
1109 : : * claimed username.
1110 : : */
1111 : : static int
1112 : : pg_GSS_checkauth(Port *port)
1113 : : {
1114 : : int ret;
1115 : : OM_uint32 maj_stat,
1116 : : min_stat,
1117 : : lmin_s;
1118 : : gss_buffer_desc gbuf;
1119 : : char *princ;
1120 : :
1121 : : /*
1122 : : * Get the name of the user that authenticated, and compare it to the pg
1123 : : * username that was specified for the connection.
1124 : : */
1125 : : maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
1126 : : if (maj_stat != GSS_S_COMPLETE)
1127 : : {
1128 : : pg_GSS_error(_("retrieving GSS user name failed"),
1129 : : maj_stat, min_stat);
1130 : : return STATUS_ERROR;
1131 : : }
1132 : :
1133 : : /*
1134 : : * gbuf.value might not be null-terminated, so turn it into a regular
1135 : : * null-terminated string.
1136 : : */
1137 : : princ = palloc(gbuf.length + 1);
1138 : : memcpy(princ, gbuf.value, gbuf.length);
1139 : : princ[gbuf.length] = '\0';
1140 : : gss_release_buffer(&lmin_s, &gbuf);
1141 : :
1142 : : /*
1143 : : * Copy the original name of the authenticated principal into our backend
1144 : : * memory for display later.
1145 : : *
1146 : : * This is also our authenticated identity. Set it now, rather than
1147 : : * waiting for the usermap check below, because authentication has already
1148 : : * succeeded and we want the log file to reflect that.
1149 : : */
1150 : : port->gss->princ = MemoryContextStrdup(TopMemoryContext, princ);
1151 : : set_authn_id(port, princ);
1152 : :
1153 : : /*
1154 : : * Split the username at the realm separator
1155 : : */
1156 : : if (strchr(princ, '@'))
1157 : : {
1158 : : char *cp = strchr(princ, '@');
1159 : :
1160 : : /*
1161 : : * If we are not going to include the realm in the username that is
1162 : : * passed to the ident map, destructively modify it here to remove the
1163 : : * realm. Then advance past the separator to check the realm.
1164 : : */
1165 : : if (!port->hba->include_realm)
1166 : : *cp = '\0';
1167 : : cp++;
1168 : :
1169 : : if (port->hba->krb_realm != NULL && strlen(port->hba->krb_realm))
1170 : : {
1171 : : /*
1172 : : * Match the realm part of the name first
1173 : : */
1174 : : if (pg_krb_caseins_users)
1175 : : ret = pg_strcasecmp(port->hba->krb_realm, cp);
1176 : : else
1177 : : ret = strcmp(port->hba->krb_realm, cp);
1178 : :
1179 : : if (ret)
1180 : : {
1181 : : /* GSS realm does not match */
1182 : : elog(DEBUG2,
1183 : : "GSSAPI realm (%s) and configured realm (%s) don't match",
1184 : : cp, port->hba->krb_realm);
1185 : : pfree(princ);
1186 : : return STATUS_ERROR;
1187 : : }
1188 : : }
1189 : : }
1190 : : else if (port->hba->krb_realm && strlen(port->hba->krb_realm))
1191 : : {
1192 : : elog(DEBUG2,
1193 : : "GSSAPI did not return realm but realm matching was requested");
1194 : : pfree(princ);
1195 : : return STATUS_ERROR;
1196 : : }
1197 : :
1198 : : ret = check_usermap(port->hba->usermap, port->user_name, princ,
1199 : : pg_krb_caseins_users);
1200 : :
1201 : : pfree(princ);
1202 : :
1203 : : return ret;
1204 : : }
1205 : : #endif /* ENABLE_GSS */
1206 : :
1207 : :
1208 : : /*----------------------------------------------------------------
1209 : : * SSPI authentication system
1210 : : *----------------------------------------------------------------
1211 : : */
1212 : : #ifdef ENABLE_SSPI
1213 : :
1214 : : /*
1215 : : * Generate an error for SSPI authentication. The caller should apply
1216 : : * _() to errmsg to make it translatable.
1217 : : */
1218 : : static void
1219 : : pg_SSPI_error(int severity, const char *errmsg, SECURITY_STATUS r)
1220 : : {
1221 : : char sysmsg[256];
1222 : :
1223 : : if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
1224 : : FORMAT_MESSAGE_FROM_SYSTEM,
1225 : : NULL, r, 0,
1226 : : sysmsg, sizeof(sysmsg), NULL) == 0)
1227 : : ereport(severity,
1228 : : (errmsg_internal("%s", errmsg),
1229 : : errdetail_internal("SSPI error %x", (unsigned int) r)));
1230 : : else
1231 : : ereport(severity,
1232 : : (errmsg_internal("%s", errmsg),
1233 : : errdetail_internal("%s (%x)", sysmsg, (unsigned int) r)));
1234 : : }
1235 : :
1236 : : static int
1237 : : pg_SSPI_recvauth(Port *port)
1238 : : {
1239 : : int mtype;
1240 : : StringInfoData buf;
1241 : : SECURITY_STATUS r;
1242 : : CredHandle sspicred;
1243 : : CtxtHandle *sspictx = NULL,
1244 : : newctx;
1245 : : TimeStamp expiry;
1246 : : ULONG contextattr;
1247 : : SecBufferDesc inbuf;
1248 : : SecBufferDesc outbuf;
1249 : : SecBuffer OutBuffers[1];
1250 : : SecBuffer InBuffers[1];
1251 : : HANDLE token;
1252 : : TOKEN_USER *tokenuser;
1253 : : DWORD retlen;
1254 : : char accountname[MAXPGPATH];
1255 : : char domainname[MAXPGPATH];
1256 : : DWORD accountnamesize = sizeof(accountname);
1257 : : DWORD domainnamesize = sizeof(domainname);
1258 : : SID_NAME_USE accountnameuse;
1259 : : char *authn_id;
1260 : :
1261 : : /*
1262 : : * Acquire a handle to the server credentials.
1263 : : */
1264 : : r = AcquireCredentialsHandle(NULL,
1265 : : "negotiate",
1266 : : SECPKG_CRED_INBOUND,
1267 : : NULL,
1268 : : NULL,
1269 : : NULL,
1270 : : NULL,
1271 : : &sspicred,
1272 : : &expiry);
1273 : : if (r != SEC_E_OK)
1274 : : pg_SSPI_error(ERROR, _("could not acquire SSPI credentials"), r);
1275 : :
1276 : : /*
1277 : : * Loop through SSPI message exchange. This exchange can consist of
1278 : : * multiple messages sent in both directions. First message is always from
1279 : : * the client. All messages from client to server are password packets
1280 : : * (type 'p').
1281 : : */
1282 : : do
1283 : : {
1284 : : pq_startmsgread();
1285 : : mtype = pq_getbyte();
1286 : : if (mtype != PqMsg_GSSResponse)
1287 : : {
1288 : : if (sspictx != NULL)
1289 : : {
1290 : : DeleteSecurityContext(sspictx);
1291 : : free(sspictx);
1292 : : }
1293 : : FreeCredentialsHandle(&sspicred);
1294 : :
1295 : : /* Only log error if client didn't disconnect. */
1296 : : if (mtype != EOF)
1297 : : ereport(ERROR,
1298 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
1299 : : errmsg("expected SSPI response, got message type %d",
1300 : : mtype)));
1301 : : return STATUS_ERROR;
1302 : : }
1303 : :
1304 : : /* Get the actual SSPI token */
1305 : : initStringInfo(&buf);
1306 : : if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH))
1307 : : {
1308 : : /* EOF - pq_getmessage already logged error */
1309 : : pfree(buf.data);
1310 : : if (sspictx != NULL)
1311 : : {
1312 : : DeleteSecurityContext(sspictx);
1313 : : free(sspictx);
1314 : : }
1315 : : FreeCredentialsHandle(&sspicred);
1316 : : return STATUS_ERROR;
1317 : : }
1318 : :
1319 : : /* Map to SSPI style buffer */
1320 : : inbuf.ulVersion = SECBUFFER_VERSION;
1321 : : inbuf.cBuffers = 1;
1322 : : inbuf.pBuffers = InBuffers;
1323 : : InBuffers[0].pvBuffer = buf.data;
1324 : : InBuffers[0].cbBuffer = buf.len;
1325 : : InBuffers[0].BufferType = SECBUFFER_TOKEN;
1326 : :
1327 : : /* Prepare output buffer */
1328 : : OutBuffers[0].pvBuffer = NULL;
1329 : : OutBuffers[0].BufferType = SECBUFFER_TOKEN;
1330 : : OutBuffers[0].cbBuffer = 0;
1331 : : outbuf.cBuffers = 1;
1332 : : outbuf.pBuffers = OutBuffers;
1333 : : outbuf.ulVersion = SECBUFFER_VERSION;
1334 : :
1335 : : elog(DEBUG4, "processing received SSPI token of length %u",
1336 : : (unsigned int) buf.len);
1337 : :
1338 : : r = AcceptSecurityContext(&sspicred,
1339 : : sspictx,
1340 : : &inbuf,
1341 : : ASC_REQ_ALLOCATE_MEMORY,
1342 : : SECURITY_NETWORK_DREP,
1343 : : &newctx,
1344 : : &outbuf,
1345 : : &contextattr,
1346 : : NULL);
1347 : :
1348 : : /* input buffer no longer used */
1349 : : pfree(buf.data);
1350 : :
1351 : : if (outbuf.cBuffers > 0 && outbuf.pBuffers[0].cbBuffer > 0)
1352 : : {
1353 : : /*
1354 : : * Negotiation generated data to be sent to the client.
1355 : : */
1356 : : elog(DEBUG4, "sending SSPI response token of length %u",
1357 : : (unsigned int) outbuf.pBuffers[0].cbBuffer);
1358 : :
1359 : : port->gss->outbuf.length = outbuf.pBuffers[0].cbBuffer;
1360 : : port->gss->outbuf.value = outbuf.pBuffers[0].pvBuffer;
1361 : :
1362 : : sendAuthRequest(port, AUTH_REQ_GSS_CONT,
1363 : : port->gss->outbuf.value, port->gss->outbuf.length);
1364 : :
1365 : : FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
1366 : : }
1367 : :
1368 : : if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED)
1369 : : {
1370 : : if (sspictx != NULL)
1371 : : {
1372 : : DeleteSecurityContext(sspictx);
1373 : : free(sspictx);
1374 : : }
1375 : : FreeCredentialsHandle(&sspicred);
1376 : : pg_SSPI_error(ERROR,
1377 : : _("could not accept SSPI security context"), r);
1378 : : }
1379 : :
1380 : : /*
1381 : : * Overwrite the current context with the one we just received. If
1382 : : * sspictx is NULL it was the first loop and we need to allocate a
1383 : : * buffer for it. On subsequent runs, we can just overwrite the buffer
1384 : : * contents since the size does not change.
1385 : : */
1386 : : if (sspictx == NULL)
1387 : : {
1388 : : sspictx = malloc(sizeof(CtxtHandle));
1389 : : if (sspictx == NULL)
1390 : : ereport(ERROR,
1391 : : (errmsg("out of memory")));
1392 : : }
1393 : :
1394 : : memcpy(sspictx, &newctx, sizeof(CtxtHandle));
1395 : :
1396 : : if (r == SEC_I_CONTINUE_NEEDED)
1397 : : elog(DEBUG4, "SSPI continue needed");
1398 : :
1399 : : } while (r == SEC_I_CONTINUE_NEEDED);
1400 : :
1401 : :
1402 : : /*
1403 : : * Release service principal credentials
1404 : : */
1405 : : FreeCredentialsHandle(&sspicred);
1406 : :
1407 : :
1408 : : /*
1409 : : * SEC_E_OK indicates that authentication is now complete.
1410 : : *
1411 : : * Get the name of the user that authenticated, and compare it to the pg
1412 : : * username that was specified for the connection.
1413 : : */
1414 : :
1415 : : r = QuerySecurityContextToken(sspictx, &token);
1416 : : if (r != SEC_E_OK)
1417 : : pg_SSPI_error(ERROR,
1418 : : _("could not get token from SSPI security context"), r);
1419 : :
1420 : : /*
1421 : : * No longer need the security context, everything from here on uses the
1422 : : * token instead.
1423 : : */
1424 : : DeleteSecurityContext(sspictx);
1425 : : free(sspictx);
1426 : :
1427 : : if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
1428 : : ereport(ERROR,
1429 : : (errmsg_internal("could not get token information buffer size: error code %lu",
1430 : : GetLastError())));
1431 : :
1432 : : tokenuser = malloc(retlen);
1433 : : if (tokenuser == NULL)
1434 : : ereport(ERROR,
1435 : : (errmsg("out of memory")));
1436 : :
1437 : : if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
1438 : : ereport(ERROR,
1439 : : (errmsg_internal("could not get token information: error code %lu",
1440 : : GetLastError())));
1441 : :
1442 : : CloseHandle(token);
1443 : :
1444 : : if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
1445 : : domainname, &domainnamesize, &accountnameuse))
1446 : : ereport(ERROR,
1447 : : (errmsg_internal("could not look up account SID: error code %lu",
1448 : : GetLastError())));
1449 : :
1450 : : free(tokenuser);
1451 : :
1452 : : if (!port->hba->compat_realm)
1453 : : {
1454 : : int status = pg_SSPI_make_upn(accountname, sizeof(accountname),
1455 : : domainname, sizeof(domainname),
1456 : : port->hba->upn_username);
1457 : :
1458 : : if (status != STATUS_OK)
1459 : : /* Error already reported from pg_SSPI_make_upn */
1460 : : return status;
1461 : : }
1462 : :
1463 : : /*
1464 : : * We have all of the information necessary to construct the authenticated
1465 : : * identity. Set it now, rather than waiting for check_usermap below,
1466 : : * because authentication has already succeeded and we want the log file
1467 : : * to reflect that.
1468 : : */
1469 : : if (port->hba->compat_realm)
1470 : : {
1471 : : /* SAM-compatible format. */
1472 : : authn_id = psprintf("%s\\%s", domainname, accountname);
1473 : : }
1474 : : else
1475 : : {
1476 : : /* Kerberos principal format. */
1477 : : authn_id = psprintf("%s@%s", accountname, domainname);
1478 : : }
1479 : :
1480 : : set_authn_id(port, authn_id);
1481 : : pfree(authn_id);
1482 : :
1483 : : /*
1484 : : * Compare realm/domain if requested. In SSPI, always compare case
1485 : : * insensitive.
1486 : : */
1487 : : if (port->hba->krb_realm && strlen(port->hba->krb_realm))
1488 : : {
1489 : : if (pg_strcasecmp(port->hba->krb_realm, domainname) != 0)
1490 : : {
1491 : : elog(DEBUG2,
1492 : : "SSPI domain (%s) and configured domain (%s) don't match",
1493 : : domainname, port->hba->krb_realm);
1494 : :
1495 : : return STATUS_ERROR;
1496 : : }
1497 : : }
1498 : :
1499 : : /*
1500 : : * We have the username (without domain/realm) in accountname, compare to
1501 : : * the supplied value. In SSPI, always compare case insensitive.
1502 : : *
1503 : : * If set to include realm, append it in <username>@<realm> format.
1504 : : */
1505 : : if (port->hba->include_realm)
1506 : : {
1507 : : char *namebuf;
1508 : : int retval;
1509 : :
1510 : : namebuf = psprintf("%s@%s", accountname, domainname);
1511 : : retval = check_usermap(port->hba->usermap, port->user_name, namebuf, true);
1512 : : pfree(namebuf);
1513 : : return retval;
1514 : : }
1515 : : else
1516 : : return check_usermap(port->hba->usermap, port->user_name, accountname, true);
1517 : : }
1518 : :
1519 : : /*
1520 : : * Replaces the domainname with the Kerberos realm name,
1521 : : * and optionally the accountname with the Kerberos user name.
1522 : : */
1523 : : static int
1524 : : pg_SSPI_make_upn(char *accountname,
1525 : : size_t accountnamesize,
1526 : : char *domainname,
1527 : : size_t domainnamesize,
1528 : : bool update_accountname)
1529 : : {
1530 : : char *samname;
1531 : : char *upname = NULL;
1532 : : char *p = NULL;
1533 : : ULONG upnamesize = 0;
1534 : : size_t upnamerealmsize;
1535 : : BOOLEAN res;
1536 : :
1537 : : /*
1538 : : * Build SAM name (DOMAIN\user), then translate to UPN
1539 : : * (user@kerberos.realm). The realm name is returned in lower case, but
1540 : : * that is fine because in SSPI auth, string comparisons are always
1541 : : * case-insensitive.
1542 : : */
1543 : :
1544 : : samname = psprintf("%s\\%s", domainname, accountname);
1545 : : res = TranslateName(samname, NameSamCompatible, NameUserPrincipal,
1546 : : NULL, &upnamesize);
1547 : :
1548 : : if ((!res && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1549 : : || upnamesize == 0)
1550 : : {
1551 : : pfree(samname);
1552 : : ereport(LOG,
1553 : : (errcode(ERRCODE_INVALID_ROLE_SPECIFICATION),
1554 : : errmsg("could not translate name")));
1555 : : return STATUS_ERROR;
1556 : : }
1557 : :
1558 : : /* upnamesize includes the terminating NUL. */
1559 : : upname = palloc(upnamesize);
1560 : :
1561 : : res = TranslateName(samname, NameSamCompatible, NameUserPrincipal,
1562 : : upname, &upnamesize);
1563 : :
1564 : : pfree(samname);
1565 : : if (res)
1566 : : p = strchr(upname, '@');
1567 : :
1568 : : if (!res || p == NULL)
1569 : : {
1570 : : pfree(upname);
1571 : : ereport(LOG,
1572 : : (errcode(ERRCODE_INVALID_ROLE_SPECIFICATION),
1573 : : errmsg("could not translate name")));
1574 : : return STATUS_ERROR;
1575 : : }
1576 : :
1577 : : /* Length of realm name after the '@', including the NUL. */
1578 : : upnamerealmsize = upnamesize - (p - upname + 1);
1579 : :
1580 : : /* Replace domainname with realm name. */
1581 : : if (upnamerealmsize > domainnamesize)
1582 : : {
1583 : : pfree(upname);
1584 : : ereport(LOG,
1585 : : (errcode(ERRCODE_INVALID_ROLE_SPECIFICATION),
1586 : : errmsg("realm name too long")));
1587 : : return STATUS_ERROR;
1588 : : }
1589 : :
1590 : : /* Length is now safe. */
1591 : : strcpy(domainname, p + 1);
1592 : :
1593 : : /* Replace account name as well (in case UPN != SAM)? */
1594 : : if (update_accountname)
1595 : : {
1596 : : if ((p - upname + 1) > accountnamesize)
1597 : : {
1598 : : pfree(upname);
1599 : : ereport(LOG,
1600 : : (errcode(ERRCODE_INVALID_ROLE_SPECIFICATION),
1601 : : errmsg("translated account name too long")));
1602 : : return STATUS_ERROR;
1603 : : }
1604 : :
1605 : : *p = 0;
1606 : : strcpy(accountname, upname);
1607 : : }
1608 : :
1609 : : pfree(upname);
1610 : : return STATUS_OK;
1611 : : }
1612 : : #endif /* ENABLE_SSPI */
1613 : :
1614 : :
1615 : :
1616 : : /*----------------------------------------------------------------
1617 : : * Ident authentication system
1618 : : *----------------------------------------------------------------
1619 : : */
1620 : :
1621 : : /*
1622 : : * Per RFC 1413, space and tab are whitespace in ident messages.
1623 : : */
1624 : : static bool
1625 : 0 : is_ident_whitespace(const char c)
1626 : : {
1627 [ # # # # ]: 0 : return c == ' ' || c == '\t';
1628 : : }
1629 : :
1630 : : /*
1631 : : * Parse the string "*ident_response" as a response from a query to an Ident
1632 : : * server. If it's a normal response indicating a user name, return true
1633 : : * and store the user name at *ident_user. If it's anything else,
1634 : : * return false.
1635 : : */
1636 : : static bool
1637 : 0 : interpret_ident_response(const char *ident_response,
1638 : : char *ident_user)
1639 : : {
1640 : 0 : const char *cursor = ident_response; /* Cursor into *ident_response */
1641 : :
1642 : : /*
1643 : : * Ident's response, in the telnet tradition, should end in crlf (\r\n).
1644 : : */
1645 [ # # ]: 0 : if (strlen(ident_response) < 2)
1646 : 0 : return false;
1647 [ # # ]: 0 : else if (ident_response[strlen(ident_response) - 2] != '\r')
1648 : 0 : return false;
1649 : : else
1650 : : {
1651 [ # # # # ]: 0 : while (*cursor != ':' && *cursor != '\r')
1652 : 0 : cursor++; /* skip port field */
1653 : :
1654 [ # # ]: 0 : if (*cursor != ':')
1655 : 0 : return false;
1656 : : else
1657 : : {
1658 : : /* We're positioned to colon before response type field */
1659 : : char response_type[80];
1660 : : int i; /* Index into *response_type */
1661 : :
1662 : 0 : cursor++; /* Go over colon */
1663 [ # # ]: 0 : while (is_ident_whitespace(*cursor))
1664 : 0 : cursor++; /* skip blanks */
1665 : 0 : i = 0;
1666 [ # # # # : 0 : while (*cursor != ':' && *cursor != '\r' && !is_ident_whitespace(*cursor) &&
# # # # ]
1667 : : i < (int) (sizeof(response_type) - 1))
1668 : 0 : response_type[i++] = *cursor++;
1669 : 0 : response_type[i] = '\0';
1670 [ # # ]: 0 : while (is_ident_whitespace(*cursor))
1671 : 0 : cursor++; /* skip blanks */
1672 [ # # ]: 0 : if (strcmp(response_type, "USERID") != 0)
1673 : 0 : return false;
1674 : : else
1675 : : {
1676 : : /*
1677 : : * It's a USERID response. Good. "cursor" should be pointing
1678 : : * to the colon that precedes the operating system type.
1679 : : */
1680 [ # # ]: 0 : if (*cursor != ':')
1681 : 0 : return false;
1682 : : else
1683 : : {
1684 : 0 : cursor++; /* Go over colon */
1685 : : /* Skip over operating system field. */
1686 [ # # # # ]: 0 : while (*cursor != ':' && *cursor != '\r')
1687 : 0 : cursor++;
1688 [ # # ]: 0 : if (*cursor != ':')
1689 : 0 : return false;
1690 : : else
1691 : : {
1692 : 0 : cursor++; /* Go over colon */
1693 [ # # ]: 0 : while (is_ident_whitespace(*cursor))
1694 : 0 : cursor++; /* skip blanks */
1695 : : /* Rest of line is user name. Copy it over. */
1696 : 0 : i = 0;
1697 [ # # # # ]: 0 : while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
1698 : 0 : ident_user[i++] = *cursor++;
1699 : 0 : ident_user[i] = '\0';
1700 : 0 : return true;
1701 : : }
1702 : : }
1703 : : }
1704 : : }
1705 : : }
1706 : : }
1707 : :
1708 : :
1709 : : /*
1710 : : * Talk to the ident server on "remote_addr" and find out who
1711 : : * owns the tcp connection to "local_addr"
1712 : : * If the username is successfully retrieved, check the usermap.
1713 : : *
1714 : : * XXX: Using WaitLatchOrSocket() and doing a CHECK_FOR_INTERRUPTS() if the
1715 : : * latch was set would improve the responsiveness to timeouts/cancellations.
1716 : : */
1717 : : static int
1718 : 0 : ident_inet(Port *port)
1719 : : {
1720 : 0 : const SockAddr remote_addr = port->raddr;
1721 : 0 : const SockAddr local_addr = port->laddr;
1722 : : char ident_user[IDENT_USERNAME_MAX + 1];
1723 : 0 : pgsocket sock_fd = PGINVALID_SOCKET; /* for talking to Ident server */
1724 : : int rc; /* Return code from a locally called function */
1725 : : bool ident_return;
1726 : : char remote_addr_s[NI_MAXHOST];
1727 : : char remote_port[NI_MAXSERV];
1728 : : char local_addr_s[NI_MAXHOST];
1729 : : char local_port[NI_MAXSERV];
1730 : : char ident_port[NI_MAXSERV];
1731 : : char ident_query[80];
1732 : : char ident_response[80 + IDENT_USERNAME_MAX];
1733 : 0 : struct addrinfo *ident_serv = NULL,
1734 : 0 : *la = NULL,
1735 : : hints;
1736 : :
1737 : : /*
1738 : : * Might look a little weird to first convert it to text and then back to
1739 : : * sockaddr, but it's protocol independent.
1740 : : */
1741 : 0 : pg_getnameinfo_all(&remote_addr.addr, remote_addr.salen,
1742 : : remote_addr_s, sizeof(remote_addr_s),
1743 : : remote_port, sizeof(remote_port),
1744 : : NI_NUMERICHOST | NI_NUMERICSERV);
1745 : 0 : pg_getnameinfo_all(&local_addr.addr, local_addr.salen,
1746 : : local_addr_s, sizeof(local_addr_s),
1747 : : local_port, sizeof(local_port),
1748 : : NI_NUMERICHOST | NI_NUMERICSERV);
1749 : :
1750 : 0 : snprintf(ident_port, sizeof(ident_port), "%d", IDENT_PORT);
1751 : 0 : hints.ai_flags = AI_NUMERICHOST;
1752 : 0 : hints.ai_family = remote_addr.addr.ss_family;
1753 : 0 : hints.ai_socktype = SOCK_STREAM;
1754 : 0 : hints.ai_protocol = 0;
1755 : 0 : hints.ai_addrlen = 0;
1756 : 0 : hints.ai_canonname = NULL;
1757 : 0 : hints.ai_addr = NULL;
1758 : 0 : hints.ai_next = NULL;
1759 : 0 : rc = pg_getaddrinfo_all(remote_addr_s, ident_port, &hints, &ident_serv);
1760 [ # # # # ]: 0 : if (rc || !ident_serv)
1761 : : {
1762 : : /* we don't expect this to happen */
1763 : 0 : ident_return = false;
1764 : 0 : goto ident_inet_done;
1765 : : }
1766 : :
1767 : 0 : hints.ai_flags = AI_NUMERICHOST;
1768 : 0 : hints.ai_family = local_addr.addr.ss_family;
1769 : 0 : hints.ai_socktype = SOCK_STREAM;
1770 : 0 : hints.ai_protocol = 0;
1771 : 0 : hints.ai_addrlen = 0;
1772 : 0 : hints.ai_canonname = NULL;
1773 : 0 : hints.ai_addr = NULL;
1774 : 0 : hints.ai_next = NULL;
1775 : 0 : rc = pg_getaddrinfo_all(local_addr_s, NULL, &hints, &la);
1776 [ # # # # ]: 0 : if (rc || !la)
1777 : : {
1778 : : /* we don't expect this to happen */
1779 : 0 : ident_return = false;
1780 : 0 : goto ident_inet_done;
1781 : : }
1782 : :
1783 : 0 : sock_fd = socket(ident_serv->ai_family, ident_serv->ai_socktype,
1784 : 0 : ident_serv->ai_protocol);
1785 [ # # ]: 0 : if (sock_fd == PGINVALID_SOCKET)
1786 : : {
1787 [ # # ]: 0 : ereport(LOG,
1788 : : (errcode_for_socket_access(),
1789 : : errmsg("could not create socket for Ident connection: %m")));
1790 : 0 : ident_return = false;
1791 : 0 : goto ident_inet_done;
1792 : : }
1793 : :
1794 : : /*
1795 : : * Bind to the address which the client originally contacted, otherwise
1796 : : * the ident server won't be able to match up the right connection. This
1797 : : * is necessary if the PostgreSQL server is running on an IP alias.
1798 : : */
1799 : 0 : rc = bind(sock_fd, la->ai_addr, la->ai_addrlen);
1800 [ # # ]: 0 : if (rc != 0)
1801 : : {
1802 [ # # ]: 0 : ereport(LOG,
1803 : : (errcode_for_socket_access(),
1804 : : errmsg("could not bind to local address \"%s\": %m",
1805 : : local_addr_s)));
1806 : 0 : ident_return = false;
1807 : 0 : goto ident_inet_done;
1808 : : }
1809 : :
1810 : 0 : rc = connect(sock_fd, ident_serv->ai_addr,
1811 : 0 : ident_serv->ai_addrlen);
1812 [ # # ]: 0 : if (rc != 0)
1813 : : {
1814 [ # # ]: 0 : ereport(LOG,
1815 : : (errcode_for_socket_access(),
1816 : : errmsg("could not connect to Ident server at address \"%s\", port %s: %m",
1817 : : remote_addr_s, ident_port)));
1818 : 0 : ident_return = false;
1819 : 0 : goto ident_inet_done;
1820 : : }
1821 : :
1822 : : /* The query we send to the Ident server */
1823 : 0 : snprintf(ident_query, sizeof(ident_query), "%s,%s\r\n",
1824 : : remote_port, local_port);
1825 : :
1826 : : /* loop in case send is interrupted */
1827 : : do
1828 : : {
1829 [ # # ]: 0 : CHECK_FOR_INTERRUPTS();
1830 : :
1831 : 0 : rc = send(sock_fd, ident_query, strlen(ident_query), 0);
1832 [ # # # # ]: 0 : } while (rc < 0 && errno == EINTR);
1833 : :
1834 [ # # ]: 0 : if (rc < 0)
1835 : : {
1836 [ # # ]: 0 : ereport(LOG,
1837 : : (errcode_for_socket_access(),
1838 : : errmsg("could not send query to Ident server at address \"%s\", port %s: %m",
1839 : : remote_addr_s, ident_port)));
1840 : 0 : ident_return = false;
1841 : 0 : goto ident_inet_done;
1842 : : }
1843 : :
1844 : : do
1845 : : {
1846 [ # # ]: 0 : CHECK_FOR_INTERRUPTS();
1847 : :
1848 : 0 : rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
1849 [ # # # # ]: 0 : } while (rc < 0 && errno == EINTR);
1850 : :
1851 [ # # ]: 0 : if (rc < 0)
1852 : : {
1853 [ # # ]: 0 : ereport(LOG,
1854 : : (errcode_for_socket_access(),
1855 : : errmsg("could not receive response from Ident server at address \"%s\", port %s: %m",
1856 : : remote_addr_s, ident_port)));
1857 : 0 : ident_return = false;
1858 : 0 : goto ident_inet_done;
1859 : : }
1860 : :
1861 : 0 : ident_response[rc] = '\0';
1862 : 0 : ident_return = interpret_ident_response(ident_response, ident_user);
1863 [ # # ]: 0 : if (!ident_return)
1864 [ # # ]: 0 : ereport(LOG,
1865 : : (errmsg("invalidly formatted response from Ident server: \"%s\"",
1866 : : ident_response)));
1867 : :
1868 : 0 : ident_inet_done:
1869 [ # # ]: 0 : if (sock_fd != PGINVALID_SOCKET)
1870 : 0 : closesocket(sock_fd);
1871 [ # # ]: 0 : if (ident_serv)
1872 : 0 : pg_freeaddrinfo_all(remote_addr.addr.ss_family, ident_serv);
1873 [ # # ]: 0 : if (la)
1874 : 0 : pg_freeaddrinfo_all(local_addr.addr.ss_family, la);
1875 : :
1876 [ # # ]: 0 : if (ident_return)
1877 : : {
1878 : : /*
1879 : : * Success! Store the identity, then check the usermap. Note that
1880 : : * setting the authenticated identity is done before checking the
1881 : : * usermap, because at this point authentication has succeeded.
1882 : : */
1883 : 0 : set_authn_id(port, ident_user);
1884 : 0 : return check_usermap(port->hba->usermap, port->user_name, ident_user, false);
1885 : : }
1886 : 0 : return STATUS_ERROR;
1887 : : }
1888 : :
1889 : :
1890 : : /*----------------------------------------------------------------
1891 : : * Peer authentication system
1892 : : *----------------------------------------------------------------
1893 : : */
1894 : :
1895 : : /*
1896 : : * Ask kernel about the credentials of the connecting process,
1897 : : * determine the symbolic name of the corresponding user, and check
1898 : : * if valid per the usermap.
1899 : : *
1900 : : * Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR.
1901 : : */
1902 : : static int
1903 : 28 : auth_peer(Port *port)
1904 : : {
1905 : : uid_t uid;
1906 : : gid_t gid;
1907 : : #ifndef WIN32
1908 : : struct passwd pwbuf;
1909 : : struct passwd *pw;
1910 : : char buf[1024];
1911 : : int rc;
1912 : : int ret;
1913 : : #endif
1914 : :
1915 [ - + ]: 28 : if (getpeereid(port->sock, &uid, &gid) != 0)
1916 : : {
1917 : : /* Provide special error message if getpeereid is a stub */
1918 [ # # ]: 0 : if (errno == ENOSYS)
1919 [ # # ]: 0 : ereport(LOG,
1920 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1921 : : errmsg("peer authentication is not supported on this platform")));
1922 : : else
1923 [ # # ]: 0 : ereport(LOG,
1924 : : (errcode_for_socket_access(),
1925 : : errmsg("could not get peer credentials: %m")));
1926 : 0 : return STATUS_ERROR;
1927 : : }
1928 : :
1929 : : #ifndef WIN32
1930 : 28 : rc = getpwuid_r(uid, &pwbuf, buf, sizeof buf, &pw);
1931 [ - + ]: 28 : if (rc != 0)
1932 : : {
1933 : 0 : errno = rc;
1934 [ # # ]: 0 : ereport(LOG,
1935 : : errmsg("could not look up local user ID %ld: %m", (long) uid));
1936 : 0 : return STATUS_ERROR;
1937 : : }
1938 [ - + ]: 28 : else if (!pw)
1939 : : {
1940 [ # # ]: 0 : ereport(LOG,
1941 : : errmsg("local user with ID %ld does not exist", (long) uid));
1942 : 0 : return STATUS_ERROR;
1943 : : }
1944 : :
1945 : : /*
1946 : : * Make a copy of static getpw*() result area; this is our authenticated
1947 : : * identity. Set it before calling check_usermap, because authentication
1948 : : * has already succeeded and we want the log file to reflect that.
1949 : : */
1950 : 28 : set_authn_id(port, pw->pw_name);
1951 : :
1952 : 28 : ret = check_usermap(port->hba->usermap, port->user_name,
1953 : : MyClientConnectionInfo.authn_id, false);
1954 : :
1955 : 28 : return ret;
1956 : : #else
1957 : : /* should have failed with ENOSYS above */
1958 : : Assert(false);
1959 : : return STATUS_ERROR;
1960 : : #endif
1961 : : }
1962 : :
1963 : :
1964 : : /*----------------------------------------------------------------
1965 : : * PAM authentication system
1966 : : *----------------------------------------------------------------
1967 : : */
1968 : : #ifdef USE_PAM
1969 : :
1970 : : /*
1971 : : * PAM conversation function
1972 : : */
1973 : :
1974 : : static int
1975 : 0 : pam_passwd_conv_proc(int num_msg, PG_PAM_CONST struct pam_message **msg,
1976 : : struct pam_response **resp, void *appdata_ptr)
1977 : : {
1978 : : const char *passwd;
1979 : : struct pam_response *reply;
1980 : : int i;
1981 : :
1982 [ # # ]: 0 : if (appdata_ptr)
1983 : 0 : passwd = (char *) appdata_ptr;
1984 : : else
1985 : : {
1986 : : /*
1987 : : * Workaround for Solaris 2.6 where the PAM library is broken and does
1988 : : * not pass appdata_ptr to the conversation routine
1989 : : */
1990 : 0 : passwd = pam_passwd;
1991 : : }
1992 : :
1993 : 0 : *resp = NULL; /* in case of error exit */
1994 : :
1995 [ # # # # ]: 0 : if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG)
1996 : 0 : return PAM_CONV_ERR;
1997 : :
1998 : : /*
1999 : : * Explicitly not using palloc here - PAM will free this memory in
2000 : : * pam_end()
2001 : : */
2002 [ # # ]: 0 : if ((reply = calloc(num_msg, sizeof(struct pam_response))) == NULL)
2003 : : {
2004 [ # # ]: 0 : ereport(LOG,
2005 : : (errcode(ERRCODE_OUT_OF_MEMORY),
2006 : : errmsg("out of memory")));
2007 : 0 : return PAM_CONV_ERR;
2008 : : }
2009 : :
2010 [ # # ]: 0 : for (i = 0; i < num_msg; i++)
2011 : : {
2012 [ # # # # ]: 0 : switch (msg[i]->msg_style)
2013 : : {
2014 : 0 : case PAM_PROMPT_ECHO_OFF:
2015 [ # # ]: 0 : if (strlen(passwd) == 0)
2016 : : {
2017 : : /*
2018 : : * Password wasn't passed to PAM the first time around -
2019 : : * let's go ask the client to send a password, which we
2020 : : * then stuff into PAM.
2021 : : */
2022 : 0 : sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD, NULL, 0);
2023 : 0 : passwd = recv_password_packet(pam_port_cludge);
2024 [ # # ]: 0 : if (passwd == NULL)
2025 : : {
2026 : : /*
2027 : : * Client didn't want to send password. We
2028 : : * intentionally do not log anything about this,
2029 : : * either here or at higher levels.
2030 : : */
2031 : 0 : pam_no_password = true;
2032 : 0 : goto fail;
2033 : : }
2034 : : }
2035 [ # # ]: 0 : if ((reply[i].resp = strdup(passwd)) == NULL)
2036 : 0 : goto fail;
2037 : 0 : reply[i].resp_retcode = PAM_SUCCESS;
2038 : 0 : break;
2039 : 0 : case PAM_ERROR_MSG:
2040 [ # # ]: 0 : ereport(LOG,
2041 : : (errmsg("error from underlying PAM layer: %s",
2042 : : msg[i]->msg)));
2043 : : pg_fallthrough;
2044 : : case PAM_TEXT_INFO:
2045 : : /* we don't bother to log TEXT_INFO messages */
2046 [ # # ]: 0 : if ((reply[i].resp = strdup("")) == NULL)
2047 : 0 : goto fail;
2048 : 0 : reply[i].resp_retcode = PAM_SUCCESS;
2049 : 0 : break;
2050 : 0 : default:
2051 [ # # # # ]: 0 : ereport(LOG,
2052 : : (errmsg("unsupported PAM conversation %d/\"%s\"",
2053 : : msg[i]->msg_style,
2054 : : msg[i]->msg ? msg[i]->msg : "(none)")));
2055 : 0 : goto fail;
2056 : : }
2057 : : }
2058 : :
2059 : 0 : *resp = reply;
2060 : 0 : return PAM_SUCCESS;
2061 : :
2062 : 0 : fail:
2063 : : /* free up whatever we allocated */
2064 [ # # ]: 0 : for (i = 0; i < num_msg; i++)
2065 : 0 : free(reply[i].resp);
2066 : 0 : free(reply);
2067 : :
2068 : 0 : return PAM_CONV_ERR;
2069 : : }
2070 : :
2071 : :
2072 : : /*
2073 : : * Check authentication against PAM.
2074 : : */
2075 : : static int
2076 : 0 : CheckPAMAuth(Port *port, const char *user, const char *password)
2077 : : {
2078 : : int retval;
2079 : 0 : pam_handle_t *pamh = NULL;
2080 : :
2081 : : /*
2082 : : * We can't entirely rely on PAM to pass through appdata --- it appears
2083 : : * not to work on at least Solaris 2.6. So use these ugly static
2084 : : * variables instead.
2085 : : */
2086 : 0 : pam_passwd = password;
2087 : 0 : pam_port_cludge = port;
2088 : 0 : pam_no_password = false;
2089 : :
2090 : : /*
2091 : : * Set the application data portion of the conversation struct. This is
2092 : : * later used inside the PAM conversation to pass the password to the
2093 : : * authentication module.
2094 : : */
2095 : 0 : pam_passw_conv.appdata_ptr = unconstify(char *, password); /* from password above,
2096 : : * not allocated */
2097 : :
2098 : : /* Optionally, one can set the service name in pg_hba.conf */
2099 [ # # # # ]: 0 : if (port->hba->pamservice && port->hba->pamservice[0] != '\0')
2100 : 0 : retval = pam_start(port->hba->pamservice, "pgsql@",
2101 : : &pam_passw_conv, &pamh);
2102 : : else
2103 : 0 : retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@",
2104 : : &pam_passw_conv, &pamh);
2105 : :
2106 [ # # ]: 0 : if (retval != PAM_SUCCESS)
2107 : : {
2108 [ # # ]: 0 : ereport(LOG,
2109 : : (errmsg("could not create PAM authenticator: %s",
2110 : : pam_strerror(pamh, retval))));
2111 : 0 : pam_passwd = NULL; /* Unset pam_passwd */
2112 : 0 : return STATUS_ERROR;
2113 : : }
2114 : :
2115 : 0 : retval = pam_set_item(pamh, PAM_USER, user);
2116 : :
2117 [ # # ]: 0 : if (retval != PAM_SUCCESS)
2118 : : {
2119 [ # # ]: 0 : ereport(LOG,
2120 : : (errmsg("pam_set_item(PAM_USER) failed: %s",
2121 : : pam_strerror(pamh, retval))));
2122 : 0 : pam_passwd = NULL; /* Unset pam_passwd */
2123 : 0 : return STATUS_ERROR;
2124 : : }
2125 : :
2126 [ # # ]: 0 : if (port->hba->conntype != ctLocal)
2127 : : {
2128 : : char hostinfo[NI_MAXHOST];
2129 : : int flags;
2130 : :
2131 [ # # ]: 0 : if (port->hba->pam_use_hostname)
2132 : 0 : flags = 0;
2133 : : else
2134 : 0 : flags = NI_NUMERICHOST | NI_NUMERICSERV;
2135 : :
2136 : 0 : retval = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
2137 : : hostinfo, sizeof(hostinfo), NULL, 0,
2138 : : flags);
2139 [ # # ]: 0 : if (retval != 0)
2140 : : {
2141 [ # # ]: 0 : ereport(WARNING,
2142 : : (errmsg_internal("pg_getnameinfo_all() failed: %s",
2143 : : gai_strerror(retval))));
2144 : 0 : return STATUS_ERROR;
2145 : : }
2146 : :
2147 : 0 : retval = pam_set_item(pamh, PAM_RHOST, hostinfo);
2148 : :
2149 [ # # ]: 0 : if (retval != PAM_SUCCESS)
2150 : : {
2151 [ # # ]: 0 : ereport(LOG,
2152 : : (errmsg("pam_set_item(PAM_RHOST) failed: %s",
2153 : : pam_strerror(pamh, retval))));
2154 : 0 : pam_passwd = NULL;
2155 : 0 : return STATUS_ERROR;
2156 : : }
2157 : : }
2158 : :
2159 : 0 : retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv);
2160 : :
2161 [ # # ]: 0 : if (retval != PAM_SUCCESS)
2162 : : {
2163 [ # # ]: 0 : ereport(LOG,
2164 : : (errmsg("pam_set_item(PAM_CONV) failed: %s",
2165 : : pam_strerror(pamh, retval))));
2166 : 0 : pam_passwd = NULL; /* Unset pam_passwd */
2167 : 0 : return STATUS_ERROR;
2168 : : }
2169 : :
2170 : 0 : retval = pam_authenticate(pamh, 0);
2171 : :
2172 [ # # ]: 0 : if (retval != PAM_SUCCESS)
2173 : : {
2174 : : /* If pam_passwd_conv_proc saw EOF, don't log anything */
2175 [ # # ]: 0 : if (!pam_no_password)
2176 [ # # ]: 0 : ereport(LOG,
2177 : : (errmsg("pam_authenticate failed: %s",
2178 : : pam_strerror(pamh, retval))));
2179 : 0 : pam_passwd = NULL; /* Unset pam_passwd */
2180 [ # # ]: 0 : return pam_no_password ? STATUS_EOF : STATUS_ERROR;
2181 : : }
2182 : :
2183 : 0 : retval = pam_acct_mgmt(pamh, 0);
2184 : :
2185 [ # # ]: 0 : if (retval != PAM_SUCCESS)
2186 : : {
2187 : : /* If pam_passwd_conv_proc saw EOF, don't log anything */
2188 [ # # ]: 0 : if (!pam_no_password)
2189 [ # # ]: 0 : ereport(LOG,
2190 : : (errmsg("pam_acct_mgmt failed: %s",
2191 : : pam_strerror(pamh, retval))));
2192 : 0 : pam_passwd = NULL; /* Unset pam_passwd */
2193 [ # # ]: 0 : return pam_no_password ? STATUS_EOF : STATUS_ERROR;
2194 : : }
2195 : :
2196 : 0 : retval = pam_end(pamh, retval);
2197 : :
2198 [ # # ]: 0 : if (retval != PAM_SUCCESS)
2199 : : {
2200 [ # # ]: 0 : ereport(LOG,
2201 : : (errmsg("could not release PAM authenticator: %s",
2202 : : pam_strerror(pamh, retval))));
2203 : : }
2204 : :
2205 : 0 : pam_passwd = NULL; /* Unset pam_passwd */
2206 : :
2207 [ # # ]: 0 : if (retval == PAM_SUCCESS)
2208 : 0 : set_authn_id(port, user);
2209 : :
2210 [ # # ]: 0 : return (retval == PAM_SUCCESS ? STATUS_OK : STATUS_ERROR);
2211 : : }
2212 : : #endif /* USE_PAM */
2213 : :
2214 : :
2215 : : /*----------------------------------------------------------------
2216 : : * BSD authentication system
2217 : : *----------------------------------------------------------------
2218 : : */
2219 : : #ifdef USE_BSD_AUTH
2220 : : static int
2221 : : CheckBSDAuth(Port *port, char *user)
2222 : : {
2223 : : char *passwd;
2224 : : int retval;
2225 : :
2226 : : /* Send regular password request to client, and get the response */
2227 : : sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
2228 : :
2229 : : passwd = recv_password_packet(port);
2230 : : if (passwd == NULL)
2231 : : return STATUS_EOF;
2232 : :
2233 : : /*
2234 : : * Ask the BSD auth system to verify password. Note that auth_userokay
2235 : : * will overwrite the password string with zeroes, but it's just a
2236 : : * temporary string so we don't care.
2237 : : */
2238 : : retval = auth_userokay(user, NULL, "auth-postgresql", passwd);
2239 : :
2240 : : pfree(passwd);
2241 : :
2242 : : if (!retval)
2243 : : return STATUS_ERROR;
2244 : :
2245 : : set_authn_id(port, user);
2246 : : return STATUS_OK;
2247 : : }
2248 : : #endif /* USE_BSD_AUTH */
2249 : :
2250 : :
2251 : : /*----------------------------------------------------------------
2252 : : * LDAP authentication system
2253 : : *----------------------------------------------------------------
2254 : : */
2255 : : #ifdef USE_LDAP
2256 : :
2257 : : static int errdetail_for_ldap(LDAP *ldap);
2258 : :
2259 : : /*
2260 : : * Initialize a connection to the LDAP server, including setting up
2261 : : * TLS if requested.
2262 : : */
2263 : : static int
2264 : 28 : InitializeLDAPConnection(Port *port, LDAP **ldap)
2265 : : {
2266 : : const char *scheme;
2267 : 28 : int ldapversion = LDAP_VERSION3;
2268 : : int r;
2269 : :
2270 : 28 : scheme = port->hba->ldapscheme;
2271 [ + + ]: 28 : if (scheme == NULL)
2272 : 17 : scheme = "ldap";
2273 : : #ifdef WIN32
2274 : : if (strcmp(scheme, "ldaps") == 0)
2275 : : *ldap = ldap_sslinit(port->hba->ldapserver, port->hba->ldapport, 1);
2276 : : else
2277 : : *ldap = ldap_init(port->hba->ldapserver, port->hba->ldapport);
2278 : : if (!*ldap)
2279 : : {
2280 : : ereport(LOG,
2281 : : (errmsg("could not initialize LDAP: error code %lu",
2282 : : LdapGetLastError())));
2283 : :
2284 : : return STATUS_ERROR;
2285 : : }
2286 : : #else
2287 : : #ifdef HAVE_LDAP_INITIALIZE
2288 : :
2289 : : /*
2290 : : * OpenLDAP provides a non-standard extension ldap_initialize() that takes
2291 : : * a list of URIs, allowing us to request "ldaps" instead of "ldap". It
2292 : : * also provides ldap_domain2hostlist() to find LDAP servers automatically
2293 : : * using DNS SRV. They were introduced in the same version, so for now we
2294 : : * don't have an extra configure check for the latter.
2295 : : */
2296 : : {
2297 : : StringInfoData uris;
2298 : 28 : char *hostlist = NULL;
2299 : : char *p;
2300 : : bool append_port;
2301 : :
2302 : : /* We'll build a space-separated scheme://hostname:port list here */
2303 : 28 : initStringInfo(&uris);
2304 : :
2305 : : /*
2306 : : * If pg_hba.conf provided no hostnames, we can ask OpenLDAP to try to
2307 : : * find some by extracting a domain name from the base DN and looking
2308 : : * up DSN SRV records for _ldap._tcp.<domain>.
2309 : : */
2310 [ + - - + ]: 28 : if (!port->hba->ldapserver || port->hba->ldapserver[0] == '\0')
2311 : 0 : {
2312 : : char *domain;
2313 : :
2314 : : /* ou=blah,dc=foo,dc=bar -> foo.bar */
2315 [ # # ]: 0 : if (ldap_dn2domain(port->hba->ldapbasedn, &domain))
2316 : : {
2317 [ # # ]: 0 : ereport(LOG,
2318 : : (errmsg("could not extract domain name from ldapbasedn")));
2319 : 0 : return STATUS_ERROR;
2320 : : }
2321 : :
2322 : : /* Look up a list of LDAP server hosts and port numbers */
2323 [ # # ]: 0 : if (ldap_domain2hostlist(domain, &hostlist))
2324 : : {
2325 [ # # ]: 0 : ereport(LOG,
2326 : : (errmsg("LDAP authentication could not find DNS SRV records for \"%s\"",
2327 : : domain),
2328 : : (errhint("Set an LDAP server name explicitly."))));
2329 : 0 : ldap_memfree(domain);
2330 : 0 : return STATUS_ERROR;
2331 : : }
2332 : 0 : ldap_memfree(domain);
2333 : :
2334 : : /* We have a space-separated list of host:port entries */
2335 : 0 : p = hostlist;
2336 : 0 : append_port = false;
2337 : : }
2338 : : else
2339 : : {
2340 : : /* We have a space-separated list of hosts from pg_hba.conf */
2341 : 28 : p = port->hba->ldapserver;
2342 : 28 : append_port = true;
2343 : : }
2344 : :
2345 : : /* Convert the list of host[:port] entries to full URIs */
2346 : : do
2347 : : {
2348 : : size_t size;
2349 : :
2350 : : /* Find the span of the next entry */
2351 : 31 : size = strcspn(p, " ");
2352 : :
2353 : : /* Append a space separator if this isn't the first URI */
2354 [ + + ]: 31 : if (uris.len > 0)
2355 : 3 : appendStringInfoChar(&uris, ' ');
2356 : :
2357 : : /* Append scheme://host:port */
2358 : 31 : appendStringInfoString(&uris, scheme);
2359 : 31 : appendStringInfoString(&uris, "://");
2360 : 31 : appendBinaryStringInfo(&uris, p, size);
2361 [ + - ]: 31 : if (append_port)
2362 : 31 : appendStringInfo(&uris, ":%d", port->hba->ldapport);
2363 : :
2364 : : /* Step over this entry and any number of trailing spaces */
2365 : 31 : p += size;
2366 [ + + ]: 34 : while (*p == ' ')
2367 : 3 : ++p;
2368 [ + + ]: 31 : } while (*p);
2369 : :
2370 : : /* Free memory from OpenLDAP if we looked up SRV records */
2371 [ - + ]: 28 : if (hostlist)
2372 : 0 : ldap_memfree(hostlist);
2373 : :
2374 : : /* Finally, try to connect using the URI list */
2375 : 28 : r = ldap_initialize(ldap, uris.data);
2376 : 28 : pfree(uris.data);
2377 [ - + ]: 28 : if (r != LDAP_SUCCESS)
2378 : : {
2379 [ # # ]: 0 : ereport(LOG,
2380 : : (errmsg("could not initialize LDAP: %s",
2381 : : ldap_err2string(r))));
2382 : :
2383 : 0 : return STATUS_ERROR;
2384 : : }
2385 : : }
2386 : : #else
2387 : : if (strcmp(scheme, "ldaps") == 0)
2388 : : {
2389 : : ereport(LOG,
2390 : : (errmsg("ldaps not supported with this LDAP library")));
2391 : :
2392 : : return STATUS_ERROR;
2393 : : }
2394 : : *ldap = ldap_init(port->hba->ldapserver, port->hba->ldapport);
2395 : : if (!*ldap)
2396 : : {
2397 : : ereport(LOG,
2398 : : (errmsg("could not initialize LDAP: %m")));
2399 : :
2400 : : return STATUS_ERROR;
2401 : : }
2402 : : #endif
2403 : : #endif
2404 : :
2405 [ - + ]: 28 : if ((r = ldap_set_option(*ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapversion)) != LDAP_SUCCESS)
2406 : : {
2407 [ # # ]: 0 : ereport(LOG,
2408 : : (errmsg("could not set LDAP protocol version: %s",
2409 : : ldap_err2string(r)),
2410 : : errdetail_for_ldap(*ldap)));
2411 : 0 : ldap_unbind(*ldap);
2412 : 0 : return STATUS_ERROR;
2413 : : }
2414 : :
2415 [ + + ]: 28 : if (port->hba->ldaptls)
2416 : : {
2417 : : #ifndef WIN32
2418 [ - + ]: 1 : if ((r = ldap_start_tls_s(*ldap, NULL, NULL)) != LDAP_SUCCESS)
2419 : : #else
2420 : : if ((r = ldap_start_tls_s(*ldap, NULL, NULL, NULL, NULL)) != LDAP_SUCCESS)
2421 : : #endif
2422 : : {
2423 [ # # ]: 0 : ereport(LOG,
2424 : : (errmsg("could not start LDAP TLS session: %s",
2425 : : ldap_err2string(r)),
2426 : : errdetail_for_ldap(*ldap)));
2427 : 0 : ldap_unbind(*ldap);
2428 : 0 : return STATUS_ERROR;
2429 : : }
2430 : : }
2431 : :
2432 : 28 : return STATUS_OK;
2433 : : }
2434 : :
2435 : : /* Placeholders recognized by FormatSearchFilter. For now just one. */
2436 : : #define LPH_USERNAME "$username"
2437 : : #define LPH_USERNAME_LEN (sizeof(LPH_USERNAME) - 1)
2438 : :
2439 : : /* Not all LDAP implementations define this. */
2440 : : #ifndef LDAP_NO_ATTRS
2441 : : #define LDAP_NO_ATTRS "1.1"
2442 : : #endif
2443 : :
2444 : : /* Not all LDAP implementations define this. */
2445 : : #ifndef LDAPS_PORT
2446 : : #define LDAPS_PORT 636
2447 : : #endif
2448 : :
2449 : : static char *
2450 : 0 : dummy_ldap_password_mutator(char *input)
2451 : : {
2452 : 0 : return input;
2453 : : }
2454 : :
2455 : : /*
2456 : : * Return a newly allocated C string copied from "pattern" with all
2457 : : * occurrences of the placeholder "$username" replaced with "user_name".
2458 : : */
2459 : : static char *
2460 : 8 : FormatSearchFilter(const char *pattern, const char *user_name)
2461 : : {
2462 : : StringInfoData output;
2463 : :
2464 : 8 : initStringInfo(&output);
2465 [ + + ]: 119 : while (*pattern != '\0')
2466 : : {
2467 [ + + ]: 111 : if (strncmp(pattern, LPH_USERNAME, LPH_USERNAME_LEN) == 0)
2468 : : {
2469 : 13 : appendStringInfoString(&output, user_name);
2470 : 13 : pattern += LPH_USERNAME_LEN;
2471 : : }
2472 : : else
2473 : 98 : appendStringInfoChar(&output, *pattern++);
2474 : : }
2475 : :
2476 : 8 : return output.data;
2477 : : }
2478 : :
2479 : : /*
2480 : : * Perform LDAP authentication
2481 : : */
2482 : : static int
2483 : 29 : CheckLDAPAuth(Port *port)
2484 : : {
2485 : : char *passwd;
2486 : : LDAP *ldap;
2487 : : int r;
2488 : : char *fulluser;
2489 : : const char *server_name;
2490 : :
2491 : : #ifdef HAVE_LDAP_INITIALIZE
2492 : :
2493 : : /*
2494 : : * For OpenLDAP, allow empty hostname if we have a basedn. We'll look for
2495 : : * servers with DNS SRV records via OpenLDAP library facilities.
2496 : : */
2497 [ + - - + ]: 29 : if ((!port->hba->ldapserver || port->hba->ldapserver[0] == '\0') &&
2498 [ # # # # ]: 0 : (!port->hba->ldapbasedn || port->hba->ldapbasedn[0] == '\0'))
2499 : : {
2500 [ # # ]: 0 : ereport(LOG,
2501 : : (errmsg("LDAP server not specified, and no ldapbasedn")));
2502 : 0 : return STATUS_ERROR;
2503 : : }
2504 : : #else
2505 : : if (!port->hba->ldapserver || port->hba->ldapserver[0] == '\0')
2506 : : {
2507 : : ereport(LOG,
2508 : : (errmsg("LDAP server not specified")));
2509 : : return STATUS_ERROR;
2510 : : }
2511 : : #endif
2512 : :
2513 : : /*
2514 : : * If we're using SRV records, we don't have a server name so we'll just
2515 : : * show an empty string in error messages.
2516 : : */
2517 [ + - ]: 29 : server_name = port->hba->ldapserver ? port->hba->ldapserver : "";
2518 : :
2519 [ - + ]: 29 : if (port->hba->ldapport == 0)
2520 : : {
2521 [ # # ]: 0 : if (port->hba->ldapscheme != NULL &&
2522 [ # # ]: 0 : strcmp(port->hba->ldapscheme, "ldaps") == 0)
2523 : 0 : port->hba->ldapport = LDAPS_PORT;
2524 : : else
2525 : 0 : port->hba->ldapport = LDAP_PORT;
2526 : : }
2527 : :
2528 : 29 : sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
2529 : :
2530 : 29 : passwd = recv_password_packet(port);
2531 [ + + ]: 29 : if (passwd == NULL)
2532 : 1 : return STATUS_EOF; /* client wouldn't send password */
2533 : :
2534 [ - + ]: 28 : if (InitializeLDAPConnection(port, &ldap) == STATUS_ERROR)
2535 : : {
2536 : : /* Error message already sent */
2537 : 0 : pfree(passwd);
2538 : 0 : return STATUS_ERROR;
2539 : : }
2540 : :
2541 [ + + ]: 28 : if (port->hba->ldapbasedn)
2542 : : {
2543 : : /*
2544 : : * First perform an LDAP search to find the DN for the user we are
2545 : : * trying to log in as.
2546 : : */
2547 : : char *filter;
2548 : : LDAPMessage *search_message;
2549 : : LDAPMessage *entry;
2550 : 20 : char *attributes[] = {LDAP_NO_ATTRS, NULL};
2551 : : char *dn;
2552 : : char *c;
2553 : : int count;
2554 : :
2555 : : /*
2556 : : * Disallow any characters that we would otherwise need to escape,
2557 : : * since they aren't really reasonable in a username anyway. Allowing
2558 : : * them would make it possible to inject any kind of custom filters in
2559 : : * the LDAP filter.
2560 : : */
2561 [ + + ]: 144 : for (c = port->user_name; *c; c++)
2562 : : {
2563 [ + - ]: 124 : if (*c == '*' ||
2564 [ + - ]: 124 : *c == '(' ||
2565 [ + - ]: 124 : *c == ')' ||
2566 [ + - ]: 124 : *c == '\\' ||
2567 [ - + ]: 124 : *c == '/')
2568 : : {
2569 [ # # ]: 0 : ereport(LOG,
2570 : : (errmsg("invalid character in user name for LDAP authentication")));
2571 : 0 : ldap_unbind(ldap);
2572 : 0 : pfree(passwd);
2573 : 6 : return STATUS_ERROR;
2574 : : }
2575 : : }
2576 : :
2577 : : /*
2578 : : * Bind with a pre-defined username/password (if available) for
2579 : : * searching. If none is specified, this turns into an anonymous bind.
2580 : : */
2581 : 38 : r = ldap_simple_bind_s(ldap,
2582 [ + + ]: 20 : port->hba->ldapbinddn ? port->hba->ldapbinddn : "",
2583 [ + + ]: 20 : port->hba->ldapbindpasswd ? ldap_password_hook(port->hba->ldapbindpasswd) : "");
2584 [ + + ]: 20 : if (r != LDAP_SUCCESS)
2585 : : {
2586 [ + - + - ]: 3 : ereport(LOG,
2587 : : (errmsg("could not perform initial LDAP bind for ldapbinddn \"%s\" on server \"%s\": %s",
2588 : : port->hba->ldapbinddn ? port->hba->ldapbinddn : "",
2589 : : server_name,
2590 : : ldap_err2string(r)),
2591 : : errdetail_for_ldap(ldap)));
2592 : 3 : ldap_unbind(ldap);
2593 : 3 : pfree(passwd);
2594 : 3 : return STATUS_ERROR;
2595 : : }
2596 : :
2597 : : /* Build a custom filter or a single attribute filter? */
2598 [ + + ]: 17 : if (port->hba->ldapsearchfilter)
2599 : 8 : filter = FormatSearchFilter(port->hba->ldapsearchfilter, port->user_name);
2600 [ + + ]: 9 : else if (port->hba->ldapsearchattribute)
2601 : 3 : filter = psprintf("(%s=%s)", port->hba->ldapsearchattribute, port->user_name);
2602 : : else
2603 : 6 : filter = psprintf("(uid=%s)", port->user_name);
2604 : :
2605 : 17 : search_message = NULL;
2606 : 17 : r = ldap_search_s(ldap,
2607 : 17 : port->hba->ldapbasedn,
2608 : 17 : port->hba->ldapscope,
2609 : : filter,
2610 : : attributes,
2611 : : 0,
2612 : : &search_message);
2613 : :
2614 [ - + ]: 17 : if (r != LDAP_SUCCESS)
2615 : : {
2616 [ # # ]: 0 : ereport(LOG,
2617 : : (errmsg("could not search LDAP for filter \"%s\" on server \"%s\": %s",
2618 : : filter, server_name, ldap_err2string(r)),
2619 : : errdetail_for_ldap(ldap)));
2620 [ # # ]: 0 : if (search_message != NULL)
2621 : 0 : ldap_msgfree(search_message);
2622 : 0 : ldap_unbind(ldap);
2623 : 0 : pfree(passwd);
2624 : 0 : pfree(filter);
2625 : 0 : return STATUS_ERROR;
2626 : : }
2627 : :
2628 : 17 : count = ldap_count_entries(ldap, search_message);
2629 [ + + ]: 17 : if (count != 1)
2630 : : {
2631 [ + - ]: 3 : if (count == 0)
2632 [ + - ]: 3 : ereport(LOG,
2633 : : (errmsg("LDAP user \"%s\" does not exist", port->user_name),
2634 : : errdetail("LDAP search for filter \"%s\" on server \"%s\" returned no entries.",
2635 : : filter, server_name)));
2636 : : else
2637 [ # # ]: 0 : ereport(LOG,
2638 : : (errmsg("LDAP user \"%s\" is not unique", port->user_name),
2639 : : errdetail_plural("LDAP search for filter \"%s\" on server \"%s\" returned %d entry.",
2640 : : "LDAP search for filter \"%s\" on server \"%s\" returned %d entries.",
2641 : : count,
2642 : : filter, server_name, count)));
2643 : :
2644 : 3 : ldap_unbind(ldap);
2645 : 3 : pfree(passwd);
2646 : 3 : pfree(filter);
2647 : 3 : ldap_msgfree(search_message);
2648 : 3 : return STATUS_ERROR;
2649 : : }
2650 : :
2651 : 14 : entry = ldap_first_entry(ldap, search_message);
2652 : 14 : dn = ldap_get_dn(ldap, entry);
2653 [ - + ]: 14 : if (dn == NULL)
2654 : : {
2655 : : int error;
2656 : :
2657 : 0 : (void) ldap_get_option(ldap, LDAP_OPT_ERROR_NUMBER, &error);
2658 [ # # ]: 0 : ereport(LOG,
2659 : : (errmsg("could not get dn for the first entry matching \"%s\" on server \"%s\": %s",
2660 : : filter, server_name,
2661 : : ldap_err2string(error)),
2662 : : errdetail_for_ldap(ldap)));
2663 : 0 : ldap_unbind(ldap);
2664 : 0 : pfree(passwd);
2665 : 0 : pfree(filter);
2666 : 0 : ldap_msgfree(search_message);
2667 : 0 : return STATUS_ERROR;
2668 : : }
2669 : 14 : fulluser = pstrdup(dn);
2670 : :
2671 : 14 : pfree(filter);
2672 : 14 : ldap_memfree(dn);
2673 : 14 : ldap_msgfree(search_message);
2674 : : }
2675 : : else
2676 : 8 : fulluser = psprintf("%s%s%s",
2677 [ + - ]: 8 : port->hba->ldapprefix ? port->hba->ldapprefix : "",
2678 : : port->user_name,
2679 [ + - ]: 8 : port->hba->ldapsuffix ? port->hba->ldapsuffix : "");
2680 : :
2681 : 22 : r = ldap_simple_bind_s(ldap, fulluser, passwd);
2682 : :
2683 [ + + ]: 22 : if (r != LDAP_SUCCESS)
2684 : : {
2685 [ + - ]: 8 : ereport(LOG,
2686 : : (errmsg("LDAP login failed for user \"%s\" on server \"%s\": %s",
2687 : : fulluser, server_name, ldap_err2string(r)),
2688 : : errdetail_for_ldap(ldap)));
2689 : 8 : ldap_unbind(ldap);
2690 : 8 : pfree(passwd);
2691 : 8 : pfree(fulluser);
2692 : 8 : return STATUS_ERROR;
2693 : : }
2694 : :
2695 : : /* Save the original bind DN as the authenticated identity. */
2696 : 14 : set_authn_id(port, fulluser);
2697 : :
2698 : 14 : ldap_unbind(ldap);
2699 : 14 : pfree(passwd);
2700 : 14 : pfree(fulluser);
2701 : :
2702 : 14 : return STATUS_OK;
2703 : : }
2704 : :
2705 : : /*
2706 : : * Add a detail error message text to the current error if one can be
2707 : : * constructed from the LDAP 'diagnostic message'.
2708 : : */
2709 : : static int
2710 : 11 : errdetail_for_ldap(LDAP *ldap)
2711 : : {
2712 : : char *message;
2713 : : int rc;
2714 : :
2715 : 11 : rc = ldap_get_option(ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, &message);
2716 [ + - + + ]: 11 : if (rc == LDAP_SUCCESS && message != NULL)
2717 : : {
2718 : 2 : errdetail("LDAP diagnostics: %s", message);
2719 : 2 : ldap_memfree(message);
2720 : : }
2721 : :
2722 : 11 : return 0;
2723 : : }
2724 : :
2725 : : #endif /* USE_LDAP */
2726 : :
2727 : :
2728 : : /*----------------------------------------------------------------
2729 : : * SSL client certificate authentication
2730 : : *----------------------------------------------------------------
2731 : : */
2732 : : #ifdef USE_SSL
2733 : : static int
2734 : 28 : CheckCertAuth(Port *port)
2735 : : {
2736 : 28 : int status_check_usermap = STATUS_ERROR;
2737 : 28 : char *peer_username = NULL;
2738 : :
2739 : : Assert(port->ssl);
2740 : :
2741 : : /* select the correct field to compare */
2742 [ + + - ]: 28 : switch (port->hba->clientcertname)
2743 : : {
2744 : 2 : case clientCertDN:
2745 : 2 : peer_username = port->peer_dn;
2746 : 2 : break;
2747 : 26 : case clientCertCN:
2748 : 26 : peer_username = port->peer_cn;
2749 : : }
2750 : :
2751 : : /* Make sure we have received a username in the certificate */
2752 [ + - ]: 28 : if (peer_username == NULL ||
2753 [ - + ]: 28 : strlen(peer_username) <= 0)
2754 : : {
2755 [ # # ]: 0 : ereport(LOG,
2756 : : (errmsg("certificate authentication failed for user \"%s\": client certificate contains no user name",
2757 : : port->user_name)));
2758 : 0 : return STATUS_ERROR;
2759 : : }
2760 : :
2761 [ + + ]: 28 : if (port->hba->auth_method == uaCert)
2762 : : {
2763 : : /*
2764 : : * For cert auth, the client's Subject DN is always our authenticated
2765 : : * identity, even if we're only using its CN for authorization. Set
2766 : : * it now, rather than waiting for check_usermap() below, because
2767 : : * authentication has already succeeded and we want the log file to
2768 : : * reflect that.
2769 : : */
2770 [ - + ]: 25 : if (!port->peer_dn)
2771 : : {
2772 : : /*
2773 : : * This should not happen as both peer_dn and peer_cn should be
2774 : : * set in this context.
2775 : : */
2776 [ # # ]: 0 : ereport(LOG,
2777 : : (errmsg("certificate authentication failed for user \"%s\": unable to retrieve subject DN",
2778 : : port->user_name)));
2779 : 0 : return STATUS_ERROR;
2780 : : }
2781 : :
2782 : 25 : set_authn_id(port, port->peer_dn);
2783 : : }
2784 : :
2785 : : /* Just pass the certificate cn/dn to the usermap check */
2786 : 28 : status_check_usermap = check_usermap(port->hba->usermap, port->user_name, peer_username, false);
2787 [ + + ]: 28 : if (status_check_usermap != STATUS_OK)
2788 : : {
2789 : : /*
2790 : : * If clientcert=verify-full was specified and the authentication
2791 : : * method is other than uaCert, log the reason for rejecting the
2792 : : * authentication.
2793 : : */
2794 [ + - + + ]: 2 : if (port->hba->clientcert == clientCertFull && port->hba->auth_method != uaCert)
2795 : : {
2796 [ - + - ]: 1 : switch (port->hba->clientcertname)
2797 : : {
2798 : 0 : case clientCertDN:
2799 [ # # ]: 0 : ereport(LOG,
2800 : : (errmsg("certificate validation (clientcert=verify-full) failed for user \"%s\": DN mismatch",
2801 : : port->user_name)));
2802 : 0 : break;
2803 : 1 : case clientCertCN:
2804 [ + - ]: 1 : ereport(LOG,
2805 : : (errmsg("certificate validation (clientcert=verify-full) failed for user \"%s\": CN mismatch",
2806 : : port->user_name)));
2807 : : }
2808 : : }
2809 : : }
2810 : 28 : return status_check_usermap;
2811 : : }
2812 : : #endif
|