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