1 /*-------------------------------------------------------------------------
4 * Routines to handle network authentication
6 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.129 2005/10/13 22:55:19 momjian Exp $
13 *-------------------------------------------------------------------------
18 #include <sys/param.h>
19 #include <sys/socket.h>
20 #if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
22 #include <sys/ucred.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
28 #include "libpq/auth.h"
29 #include "libpq/crypt.h"
30 #include "libpq/hba.h"
31 #include "libpq/libpq.h"
32 #include "libpq/pqcomm.h"
33 #include "libpq/pqformat.h"
34 #include "miscadmin.h"
35 #include "storage/ipc.h"
38 static void sendAuthRequest(Port *port, AuthRequest areq);
39 static void auth_failed(Port *port, int status);
40 static char *recv_password_packet(Port *port);
41 static int recv_and_check_password_packet(Port *port);
43 char *pg_krb_server_keyfile;
45 bool pg_krb_caseins_users;
46 char *pg_krb_server_hostname = NULL;
49 #ifdef HAVE_PAM_PAM_APPL_H
50 #include <pam/pam_appl.h>
52 #ifdef HAVE_SECURITY_PAM_APPL_H
53 #include <security/pam_appl.h>
56 #define PGSQL_PAM_SERVICE "postgresql" /* Service name passed to PAM */
58 static int CheckPAMAuth(Port *port, char *user, char *password);
59 static int pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
60 struct pam_response ** resp, void *appdata_ptr);
62 static struct pam_conv pam_passw_conv = {
63 &pam_passwd_conv_proc,
67 static char *pam_passwd = NULL; /* Workaround for Solaris 2.6 brokenness */
68 static Port *pam_port_cludge; /* Workaround for passing "Port *port"
69 * into pam_passwd_conv_proc */
73 /*----------------------------------------------------------------
74 * MIT Kerberos authentication system - protocol version 5
75 *----------------------------------------------------------------
79 /* Some old versions of Kerberos do not include <com_err.h> in <krb5.h> */
80 #if !defined(__COM_ERR_H) && !defined(__COM_ERR_H__)
85 * pg_an_to_ln -- return the local name corresponding to an authentication
88 * XXX Assumes that the first aname component is the user name. This is NOT
89 * necessarily so, since an aname can actually be something out of your
90 * worst X.400 nightmare, like
91 * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
92 * Note that the MIT an_to_ln code does the same thing if you don't
93 * provide an aname mapping database...it may be a better idea to use
94 * krb5_an_to_ln, except that it punts if multiple components are found,
95 * and we can't afford to punt.
98 pg_an_to_ln(char *aname)
102 if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
109 * Various krb5 state which is not connection specfic, and a flag to
110 * indicate whether we have initialised it yet.
112 static int pg_krb5_initialised;
113 static krb5_context pg_krb5_context;
114 static krb5_keytab pg_krb5_keytab;
115 static krb5_principal pg_krb5_server;
121 krb5_error_code retval;
124 if (pg_krb5_initialised)
127 retval = krb5_init_context(&pg_krb5_context);
131 (errmsg("Kerberos initialization returned error %d",
133 com_err("postgres", retval, "while initializing krb5");
137 retval = krb5_kt_resolve(pg_krb5_context, pg_krb_server_keyfile, &pg_krb5_keytab);
141 (errmsg("Kerberos keytab resolving returned error %d",
143 com_err("postgres", retval, "while resolving keytab file \"%s\"",
144 pg_krb_server_keyfile);
145 krb5_free_context(pg_krb5_context);
150 * If no hostname was specified, pg_krb_server_hostname is already
151 * NULL. If it's set to blank, force it to NULL.
153 khostname = pg_krb_server_hostname;
154 if (khostname && khostname[0] == '\0')
157 retval = krb5_sname_to_principal(pg_krb5_context,
165 (errmsg("Kerberos sname_to_principal(\"%s\", \"%s\") returned error %d",
166 khostname ? khostname : "localhost", pg_krb_srvnam, retval)));
167 com_err("postgres", retval,
168 "while getting server principal for server \"%s\" for service \"%s\"",
169 khostname ? khostname : "localhost", pg_krb_srvnam);
170 krb5_kt_close(pg_krb5_context, pg_krb5_keytab);
171 krb5_free_context(pg_krb5_context);
175 pg_krb5_initialised = 1;
181 * pg_krb5_recvauth -- server routine to receive authentication information
184 * We still need to compare the username obtained from the client's setup
185 * packet to the authenticated name.
187 * We have our own keytab file because postgres is unlikely to run as root,
188 * and so cannot read the default keytab.
191 pg_krb5_recvauth(Port *port)
193 krb5_error_code retval;
195 krb5_auth_context auth_context = NULL;
199 ret = pg_krb5_init();
200 if (ret != STATUS_OK)
203 retval = krb5_recvauth(pg_krb5_context, &auth_context,
204 (krb5_pointer) & port->sock, pg_krb_srvnam,
205 pg_krb5_server, 0, pg_krb5_keytab, &ticket);
209 (errmsg("Kerberos recvauth returned error %d",
211 com_err("postgres", retval, "from krb5_recvauth");
216 * The "client" structure comes out of the ticket and is therefore
217 * authenticated. Use it to check the username obtained from the
218 * postmaster startup packet.
220 * I have no idea why this is considered necessary.
222 #if defined(HAVE_KRB5_TICKET_ENC_PART2)
223 retval = krb5_unparse_name(pg_krb5_context,
224 ticket->enc_part2->client, &kusername);
225 #elif defined(HAVE_KRB5_TICKET_CLIENT)
226 retval = krb5_unparse_name(pg_krb5_context,
227 ticket->client, &kusername);
229 #error "bogus configuration"
234 (errmsg("Kerberos unparse_name returned error %d",
236 com_err("postgres", retval, "while unparsing client name");
237 krb5_free_ticket(pg_krb5_context, ticket);
238 krb5_auth_con_free(pg_krb5_context, auth_context);
242 kusername = pg_an_to_ln(kusername);
243 if (pg_krb_caseins_users)
244 ret = pg_strncasecmp(port->user_name, kusername, SM_DATABASE_USER);
246 ret = strncmp(port->user_name, kusername, SM_DATABASE_USER);
250 (errmsg("unexpected Kerberos user name received from client (received \"%s\", expected \"%s\")",
251 port->user_name, kusername)));
257 krb5_free_ticket(pg_krb5_context, ticket);
258 krb5_auth_con_free(pg_krb5_context, auth_context);
267 pg_krb5_recvauth(Port *port)
270 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
271 errmsg("Kerberos 5 not implemented on this server")));
278 * Tell the user the authentication failed, but not (much about) why.
280 * There is a tradeoff here between security concerns and making life
281 * unnecessarily difficult for legitimate users. We would not, for example,
282 * want to report the password we were expecting to receive...
283 * But it seems useful to report the username and authorization method
284 * in use, and these are items that must be presumed known to an attacker
286 * Note that many sorts of failure report additional information in the
287 * postmaster log, which we hope is only readable by good guys.
290 auth_failed(Port *port, int status)
295 * If we failed due to EOF from client, just quit; there's no point in
296 * trying to send a message to the client, and not much point in
297 * logging the failure in the postmaster log. (Logging the failure
298 * might be desirable, were it not for the fact that libpq closes the
299 * connection unceremoniously if challenged for a password when it
300 * hasn't got one to send. We'll get a useless log entry for every
301 * psql connection under password auth, even if it's perfectly
302 * successful, if we log STATUS_EOF events.)
304 if (status == STATUS_EOF)
307 switch (port->auth_method)
310 errstr = gettext_noop("authentication failed for user \"%s\": host rejected");
313 errstr = gettext_noop("Kerberos 5 authentication failed for user \"%s\"");
316 errstr = gettext_noop("\"trust\" authentication failed for user \"%s\"");
319 errstr = gettext_noop("Ident authentication failed for user \"%s\"");
324 errstr = gettext_noop("password authentication failed for user \"%s\"");
328 errstr = gettext_noop("PAM authentication failed for user \"%s\"");
332 errstr = gettext_noop("authentication failed for user \"%s\": invalid authentication method");
337 (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
338 errmsg(errstr, port->user_name)));
344 * Client authentication starts here. If there is an error, this
345 * function does not return and the backend process is terminated.
348 ClientAuthentication(Port *port)
350 int status = STATUS_ERROR;
353 * Get the authentication method to use for this frontend/database
354 * combination. Note: a failure return indicates a problem with the
355 * hba config file, not with the request. hba.c should have dropped
356 * an error message into the postmaster logfile if it failed.
358 if (hba_getauthmethod(port) != STATUS_OK)
360 (errcode(ERRCODE_CONFIG_FILE_ERROR),
361 errmsg("missing or erroneous pg_hba.conf file"),
362 errhint("See server log for details.")));
364 switch (port->auth_method)
369 * This could have come from an explicit "reject" entry in
370 * pg_hba.conf, but more likely it means there was no matching
371 * entry. Take pity on the poor user and issue a helpful
372 * error message. NOTE: this is not a security breach,
373 * because all the info reported here is known at the frontend
374 * and must be assumed known to bad guys. We're merely helping
375 * out the less clueful good guys.
378 char hostinfo[NI_MAXHOST];
380 getnameinfo_all(&port->raddr.addr, port->raddr.salen,
381 hostinfo, sizeof(hostinfo),
387 (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
388 errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
389 hostinfo, port->user_name, port->database_name,
390 port->ssl ? _("SSL on") : _("SSL off"))));
393 (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
394 errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"",
395 hostinfo, port->user_name, port->database_name)));
401 sendAuthRequest(port, AUTH_REQ_KRB5);
402 status = pg_krb5_recvauth(port);
408 * If we are doing ident on unix-domain sockets, use SCM_CREDS
409 * only if it is defined and SO_PEERCRED isn't.
411 #if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && \
412 (defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \
413 (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS)))
414 if (port->raddr.addr.ss_family == AF_UNIX)
416 #if defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
419 * Receive credentials on next message receipt, BSD/OS,
420 * NetBSD. We need to set this before the client sends the
425 if (setsockopt(port->sock, 0, LOCAL_CREDS, &on, sizeof(on)) < 0)
427 (errcode_for_socket_access(),
428 errmsg("could not enable credential reception: %m")));
431 sendAuthRequest(port, AUTH_REQ_SCM_CREDS);
434 status = authident(port);
438 sendAuthRequest(port, AUTH_REQ_MD5);
439 status = recv_and_check_password_packet(port);
443 sendAuthRequest(port, AUTH_REQ_CRYPT);
444 status = recv_and_check_password_packet(port);
448 sendAuthRequest(port, AUTH_REQ_PASSWORD);
449 status = recv_and_check_password_packet(port);
454 pam_port_cludge = port;
455 status = CheckPAMAuth(port, port->user_name, "");
464 if (status == STATUS_OK)
465 sendAuthRequest(port, AUTH_REQ_OK);
467 auth_failed(port, status);
472 * Send an authentication request packet to the frontend.
475 sendAuthRequest(Port *port, AuthRequest areq)
479 pq_beginmessage(&buf, 'R');
480 pq_sendint(&buf, (int32) areq, sizeof(int32));
482 /* Add the salt for encrypted passwords. */
483 if (areq == AUTH_REQ_MD5)
484 pq_sendbytes(&buf, port->md5Salt, 4);
485 else if (areq == AUTH_REQ_CRYPT)
486 pq_sendbytes(&buf, port->cryptSalt, 2);
491 * Flush message so client will see it, except for AUTH_REQ_OK, which
492 * need not be sent until we are ready for queries.
494 if (areq != AUTH_REQ_OK)
502 * PAM conversation function
506 pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
507 struct pam_response ** resp, void *appdata_ptr)
509 if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
511 switch (msg[0]->msg_style)
515 (errmsg("error from underlying PAM layer: %s",
520 (errmsg("unsupported PAM conversation %d/%s",
521 msg[0]->msg_style, msg[0]->msg)));
529 * Workaround for Solaris 2.6 where the PAM library is broken and
530 * does not pass appdata_ptr to the conversation routine
532 appdata_ptr = pam_passwd;
536 * Password wasn't passed to PAM the first time around - let's go ask
537 * the client to send a password, which we then stuff into PAM.
539 if (strlen(appdata_ptr) == 0)
543 sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
544 passwd = recv_password_packet(pam_port_cludge);
547 return PAM_CONV_ERR; /* client didn't want to send password */
549 if (strlen(passwd) == 0)
552 (errmsg("empty password returned by client")));
555 appdata_ptr = passwd;
559 * Explicitly not using palloc here - PAM will free this memory in
562 *resp = calloc(num_msg, sizeof(struct pam_response));
566 (errcode(ERRCODE_OUT_OF_MEMORY),
567 errmsg("out of memory")));
571 (*resp)[0].resp = strdup((char *) appdata_ptr);
572 (*resp)[0].resp_retcode = 0;
574 return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
579 * Check authentication against PAM.
582 CheckPAMAuth(Port *port, char *user, char *password)
585 pam_handle_t *pamh = NULL;
588 * Apparently, Solaris 2.6 is broken, and needs ugly static variable
591 pam_passwd = password;
594 * Set the application data portion of the conversation struct This is
595 * later used inside the PAM conversation to pass the password to the
596 * authentication module.
598 pam_passw_conv.appdata_ptr = (char *) password; /* from password above,
601 /* Optionally, one can set the service name in pg_hba.conf */
602 if (port->auth_arg && port->auth_arg[0] != '\0')
603 retval = pam_start(port->auth_arg, "pgsql@",
604 &pam_passw_conv, &pamh);
606 retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@",
607 &pam_passw_conv, &pamh);
609 if (retval != PAM_SUCCESS)
612 (errmsg("could not create PAM authenticator: %s",
613 pam_strerror(pamh, retval))));
614 pam_passwd = NULL; /* Unset pam_passwd */
618 retval = pam_set_item(pamh, PAM_USER, user);
620 if (retval != PAM_SUCCESS)
623 (errmsg("pam_set_item(PAM_USER) failed: %s",
624 pam_strerror(pamh, retval))));
625 pam_passwd = NULL; /* Unset pam_passwd */
629 retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv);
631 if (retval != PAM_SUCCESS)
634 (errmsg("pam_set_item(PAM_CONV) failed: %s",
635 pam_strerror(pamh, retval))));
636 pam_passwd = NULL; /* Unset pam_passwd */
640 retval = pam_authenticate(pamh, 0);
642 if (retval != PAM_SUCCESS)
645 (errmsg("pam_authenticate failed: %s",
646 pam_strerror(pamh, retval))));
647 pam_passwd = NULL; /* Unset pam_passwd */
651 retval = pam_acct_mgmt(pamh, 0);
653 if (retval != PAM_SUCCESS)
656 (errmsg("pam_acct_mgmt failed: %s",
657 pam_strerror(pamh, retval))));
658 pam_passwd = NULL; /* Unset pam_passwd */
662 retval = pam_end(pamh, retval);
664 if (retval != PAM_SUCCESS)
667 (errmsg("could not release PAM authenticator: %s",
668 pam_strerror(pamh, retval))));
671 pam_passwd = NULL; /* Unset pam_passwd */
673 return (retval == PAM_SUCCESS ? STATUS_OK : STATUS_ERROR);
679 * Collect password response packet from frontend.
681 * Returns NULL if couldn't get password, else palloc'd string.
684 recv_password_packet(Port *port)
688 if (PG_PROTOCOL_MAJOR(port->proto) >= 3)
690 /* Expect 'p' message type */
693 mtype = pq_getbyte();
697 * If the client just disconnects without offering a password,
698 * don't make a log entry. This is legal per protocol spec
699 * and in fact commonly done by psql, so complaining just
704 (errcode(ERRCODE_PROTOCOL_VIOLATION),
705 errmsg("expected password response, got message type %d",
707 return NULL; /* EOF or bad message type */
712 /* For pre-3.0 clients, avoid log entry if they just disconnect */
713 if (pq_peekbyte() == EOF)
714 return NULL; /* EOF */
717 initStringInfo(&buf);
718 if (pq_getmessage(&buf, 1000)) /* receive password */
720 /* EOF - pq_getmessage already logged a suitable message */
726 * Apply sanity check: password packet length should agree with length
727 * of contained string. Note it is safe to use strlen here because
728 * StringInfo is guaranteed to have an appended '\0'.
730 if (strlen(buf.data) + 1 != buf.len)
732 (errcode(ERRCODE_PROTOCOL_VIOLATION),
733 errmsg("invalid password packet size")));
735 /* Do not echo password to logs, for security. */
737 (errmsg("received password packet")));
740 * Return the received string. Note we do not attempt to do any
741 * character-set conversion on it; since we don't yet know the
742 * client's encoding, there wouldn't be much point.
749 * Called when we have sent an authorization request for a password.
750 * Get the response and check it.
753 recv_and_check_password_packet(Port *port)
758 passwd = recv_password_packet(port);
761 return STATUS_EOF; /* client wouldn't send password */
763 result = md5_crypt_verify(port, port->user_name, passwd);