1 /*-------------------------------------------------------------------------
4 * Routines to handle network authentication
6 * Portions Copyright (c) 1996-2007, 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.148 2007/02/08 04:52:18 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>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
27 #include "libpq/auth.h"
28 #include "libpq/crypt.h"
30 #include "libpq/libpq.h"
31 #include "libpq/pqformat.h"
32 #include "storage/ipc.h"
35 static void sendAuthRequest(Port *port, AuthRequest areq);
36 static void auth_failed(Port *port, int status);
37 static char *recv_password_packet(Port *port);
38 static int recv_and_check_password_packet(Port *port);
40 char *pg_krb_server_keyfile;
42 bool pg_krb_caseins_users;
43 char *pg_krb_server_hostname = NULL;
46 #ifdef HAVE_PAM_PAM_APPL_H
47 #include <pam/pam_appl.h>
49 #ifdef HAVE_SECURITY_PAM_APPL_H
50 #include <security/pam_appl.h>
53 #define PGSQL_PAM_SERVICE "postgresql" /* Service name passed to PAM */
55 static int CheckPAMAuth(Port *port, char *user, char *password);
56 static int pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
57 struct pam_response ** resp, void *appdata_ptr);
59 static struct pam_conv pam_passw_conv = {
60 &pam_passwd_conv_proc,
64 static char *pam_passwd = NULL; /* Workaround for Solaris 2.6 brokenness */
65 static Port *pam_port_cludge; /* Workaround for passing "Port *port" into
66 * pam_passwd_conv_proc */
71 /* We use a deprecated function to keep the codepath the same as win32. */
72 #define LDAP_DEPRECATED 1
77 /* Correct header from the Platform SDK */
79 ULONG(*__ldap_start_tls_sA) (
80 IN PLDAP ExternalHandle,
81 OUT PULONG ServerReturnValue,
82 OUT LDAPMessage ** result,
83 IN PLDAPControlA * ServerControls,
84 IN PLDAPControlA * ClientControls
88 static int CheckLDAPAuth(Port *port);
93 /*----------------------------------------------------------------
94 * MIT Kerberos authentication system - protocol version 5
95 *----------------------------------------------------------------
99 /* Some old versions of Kerberos do not include <com_err.h> in <krb5.h> */
100 #if !defined(__COM_ERR_H) && !defined(__COM_ERR_H__)
105 * pg_an_to_ln -- return the local name corresponding to an authentication
108 * XXX Assumes that the first aname component is the user name. This is NOT
109 * necessarily so, since an aname can actually be something out of your
110 * worst X.400 nightmare, like
111 * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
112 * Note that the MIT an_to_ln code does the same thing if you don't
113 * provide an aname mapping database...it may be a better idea to use
114 * krb5_an_to_ln, except that it punts if multiple components are found,
115 * and we can't afford to punt.
118 pg_an_to_ln(char *aname)
122 if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
129 * Various krb5 state which is not connection specfic, and a flag to
130 * indicate whether we have initialised it yet.
132 static int pg_krb5_initialised;
133 static krb5_context pg_krb5_context;
134 static krb5_keytab pg_krb5_keytab;
135 static krb5_principal pg_krb5_server;
141 krb5_error_code retval;
144 if (pg_krb5_initialised)
147 retval = krb5_init_context(&pg_krb5_context);
151 (errmsg("Kerberos initialization returned error %d",
153 com_err("postgres", retval, "while initializing krb5");
157 retval = krb5_kt_resolve(pg_krb5_context, pg_krb_server_keyfile, &pg_krb5_keytab);
161 (errmsg("Kerberos keytab resolving returned error %d",
163 com_err("postgres", retval, "while resolving keytab file \"%s\"",
164 pg_krb_server_keyfile);
165 krb5_free_context(pg_krb5_context);
170 * If no hostname was specified, pg_krb_server_hostname is already NULL.
171 * If it's set to blank, force it to NULL.
173 khostname = pg_krb_server_hostname;
174 if (khostname && khostname[0] == '\0')
177 retval = krb5_sname_to_principal(pg_krb5_context,
185 (errmsg("Kerberos sname_to_principal(\"%s\", \"%s\") returned error %d",
186 khostname ? khostname : "server hostname", pg_krb_srvnam, retval)));
187 com_err("postgres", retval,
188 "while getting server principal for server \"%s\" for service \"%s\"",
189 khostname ? khostname : "server hostname", pg_krb_srvnam);
190 krb5_kt_close(pg_krb5_context, pg_krb5_keytab);
191 krb5_free_context(pg_krb5_context);
195 pg_krb5_initialised = 1;
201 * pg_krb5_recvauth -- server routine to receive authentication information
204 * We still need to compare the username obtained from the client's setup
205 * packet to the authenticated name.
207 * We have our own keytab file because postgres is unlikely to run as root,
208 * and so cannot read the default keytab.
211 pg_krb5_recvauth(Port *port)
213 krb5_error_code retval;
215 krb5_auth_context auth_context = NULL;
219 if (get_role_line(port->user_name) == NULL)
222 ret = pg_krb5_init();
223 if (ret != STATUS_OK)
226 retval = krb5_recvauth(pg_krb5_context, &auth_context,
227 (krb5_pointer) & port->sock, pg_krb_srvnam,
228 pg_krb5_server, 0, pg_krb5_keytab, &ticket);
232 (errmsg("Kerberos recvauth returned error %d",
234 com_err("postgres", retval, "from krb5_recvauth");
239 * The "client" structure comes out of the ticket and is therefore
240 * authenticated. Use it to check the username obtained from the
241 * postmaster startup packet.
243 * I have no idea why this is considered necessary.
245 #if defined(HAVE_KRB5_TICKET_ENC_PART2)
246 retval = krb5_unparse_name(pg_krb5_context,
247 ticket->enc_part2->client, &kusername);
248 #elif defined(HAVE_KRB5_TICKET_CLIENT)
249 retval = krb5_unparse_name(pg_krb5_context,
250 ticket->client, &kusername);
252 #error "bogus configuration"
257 (errmsg("Kerberos unparse_name returned error %d",
259 com_err("postgres", retval, "while unparsing client name");
260 krb5_free_ticket(pg_krb5_context, ticket);
261 krb5_auth_con_free(pg_krb5_context, auth_context);
265 kusername = pg_an_to_ln(kusername);
266 if (pg_krb_caseins_users)
267 ret = pg_strncasecmp(port->user_name, kusername, SM_DATABASE_USER);
269 ret = strncmp(port->user_name, kusername, SM_DATABASE_USER);
273 (errmsg("unexpected Kerberos user name received from client (received \"%s\", expected \"%s\")",
274 port->user_name, kusername)));
280 krb5_free_ticket(pg_krb5_context, ticket);
281 krb5_auth_con_free(pg_krb5_context, auth_context);
289 pg_krb5_recvauth(Port *port)
292 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
293 errmsg("Kerberos 5 not implemented on this server")));
300 * Tell the user the authentication failed, but not (much about) why.
302 * There is a tradeoff here between security concerns and making life
303 * unnecessarily difficult for legitimate users. We would not, for example,
304 * want to report the password we were expecting to receive...
305 * But it seems useful to report the username and authorization method
306 * in use, and these are items that must be presumed known to an attacker
308 * Note that many sorts of failure report additional information in the
309 * postmaster log, which we hope is only readable by good guys.
312 auth_failed(Port *port, int status)
317 * If we failed due to EOF from client, just quit; there's no point in
318 * trying to send a message to the client, and not much point in logging
319 * the failure in the postmaster log. (Logging the failure might be
320 * desirable, were it not for the fact that libpq closes the connection
321 * unceremoniously if challenged for a password when it hasn't got one to
322 * send. We'll get a useless log entry for every psql connection under
323 * password auth, even if it's perfectly successful, if we log STATUS_EOF
326 if (status == STATUS_EOF)
329 switch (port->auth_method)
332 errstr = gettext_noop("authentication failed for user \"%s\": host rejected");
335 errstr = gettext_noop("Kerberos 5 authentication failed for user \"%s\"");
338 errstr = gettext_noop("\"trust\" authentication failed for user \"%s\"");
341 errstr = gettext_noop("Ident authentication failed for user \"%s\"");
346 errstr = gettext_noop("password authentication failed for user \"%s\"");
350 errstr = gettext_noop("PAM authentication failed for user \"%s\"");
355 errstr = gettext_noop("LDAP authentication failed for user \"%s\"");
357 #endif /* USE_LDAP */
359 errstr = gettext_noop("authentication failed for user \"%s\": invalid authentication method");
364 (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
365 errmsg(errstr, port->user_name)));
371 * Client authentication starts here. If there is an error, this
372 * function does not return and the backend process is terminated.
375 ClientAuthentication(Port *port)
377 int status = STATUS_ERROR;
380 * Get the authentication method to use for this frontend/database
381 * combination. Note: a failure return indicates a problem with the hba
382 * config file, not with the request. hba.c should have dropped an error
383 * message into the postmaster logfile if it failed.
385 if (hba_getauthmethod(port) != STATUS_OK)
387 (errcode(ERRCODE_CONFIG_FILE_ERROR),
388 errmsg("missing or erroneous pg_hba.conf file"),
389 errhint("See server log for details.")));
391 switch (port->auth_method)
396 * This could have come from an explicit "reject" entry in
397 * pg_hba.conf, but more likely it means there was no matching
398 * entry. Take pity on the poor user and issue a helpful error
399 * message. NOTE: this is not a security breach, because all the
400 * info reported here is known at the frontend and must be assumed
401 * known to bad guys. We're merely helping out the less clueful
405 char hostinfo[NI_MAXHOST];
407 pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
408 hostinfo, sizeof(hostinfo),
414 (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
415 errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
416 hostinfo, port->user_name, port->database_name,
417 port->ssl ? _("SSL on") : _("SSL off"))));
420 (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
421 errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"",
422 hostinfo, port->user_name, port->database_name)));
428 sendAuthRequest(port, AUTH_REQ_KRB5);
429 status = pg_krb5_recvauth(port);
435 * If we are doing ident on unix-domain sockets, use SCM_CREDS
436 * only if it is defined and SO_PEERCRED isn't.
438 #if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && \
439 (defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \
440 (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS)))
441 if (port->raddr.addr.ss_family == AF_UNIX)
443 #if defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
446 * Receive credentials on next message receipt, BSD/OS,
447 * NetBSD. We need to set this before the client sends the
452 if (setsockopt(port->sock, 0, LOCAL_CREDS, &on, sizeof(on)) < 0)
454 (errcode_for_socket_access(),
455 errmsg("could not enable credential reception: %m")));
458 sendAuthRequest(port, AUTH_REQ_SCM_CREDS);
461 status = authident(port);
465 sendAuthRequest(port, AUTH_REQ_MD5);
466 status = recv_and_check_password_packet(port);
470 sendAuthRequest(port, AUTH_REQ_CRYPT);
471 status = recv_and_check_password_packet(port);
475 sendAuthRequest(port, AUTH_REQ_PASSWORD);
476 status = recv_and_check_password_packet(port);
481 pam_port_cludge = port;
482 status = CheckPAMAuth(port, port->user_name, "");
488 status = CheckLDAPAuth(port);
497 if (status == STATUS_OK)
498 sendAuthRequest(port, AUTH_REQ_OK);
500 auth_failed(port, status);
505 * Send an authentication request packet to the frontend.
508 sendAuthRequest(Port *port, AuthRequest areq)
512 pq_beginmessage(&buf, 'R');
513 pq_sendint(&buf, (int32) areq, sizeof(int32));
515 /* Add the salt for encrypted passwords. */
516 if (areq == AUTH_REQ_MD5)
517 pq_sendbytes(&buf, port->md5Salt, 4);
518 else if (areq == AUTH_REQ_CRYPT)
519 pq_sendbytes(&buf, port->cryptSalt, 2);
524 * Flush message so client will see it, except for AUTH_REQ_OK, which need
525 * not be sent until we are ready for queries.
527 if (areq != AUTH_REQ_OK)
535 * PAM conversation function
539 pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
540 struct pam_response ** resp, void *appdata_ptr)
542 if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
544 switch (msg[0]->msg_style)
548 (errmsg("error from underlying PAM layer: %s",
553 (errmsg("unsupported PAM conversation %d/%s",
554 msg[0]->msg_style, msg[0]->msg)));
562 * Workaround for Solaris 2.6 where the PAM library is broken and does
563 * not pass appdata_ptr to the conversation routine
565 appdata_ptr = pam_passwd;
569 * Password wasn't passed to PAM the first time around - let's go ask the
570 * client to send a password, which we then stuff into PAM.
572 if (strlen(appdata_ptr) == 0)
576 sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
577 passwd = recv_password_packet(pam_port_cludge);
580 return PAM_CONV_ERR; /* client didn't want to send password */
582 if (strlen(passwd) == 0)
585 (errmsg("empty password returned by client")));
588 appdata_ptr = passwd;
592 * Explicitly not using palloc here - PAM will free this memory in
595 *resp = calloc(num_msg, sizeof(struct pam_response));
599 (errcode(ERRCODE_OUT_OF_MEMORY),
600 errmsg("out of memory")));
604 (*resp)[0].resp = strdup((char *) appdata_ptr);
605 (*resp)[0].resp_retcode = 0;
607 return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
612 * Check authentication against PAM.
615 CheckPAMAuth(Port *port, char *user, char *password)
618 pam_handle_t *pamh = NULL;
621 * Apparently, Solaris 2.6 is broken, and needs ugly static variable
624 pam_passwd = password;
627 * Set the application data portion of the conversation struct This is
628 * later used inside the PAM conversation to pass the password to the
629 * authentication module.
631 pam_passw_conv.appdata_ptr = (char *) password; /* from password above,
634 /* Optionally, one can set the service name in pg_hba.conf */
635 if (port->auth_arg && port->auth_arg[0] != '\0')
636 retval = pam_start(port->auth_arg, "pgsql@",
637 &pam_passw_conv, &pamh);
639 retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@",
640 &pam_passw_conv, &pamh);
642 if (retval != PAM_SUCCESS)
645 (errmsg("could not create PAM authenticator: %s",
646 pam_strerror(pamh, retval))));
647 pam_passwd = NULL; /* Unset pam_passwd */
651 retval = pam_set_item(pamh, PAM_USER, user);
653 if (retval != PAM_SUCCESS)
656 (errmsg("pam_set_item(PAM_USER) failed: %s",
657 pam_strerror(pamh, retval))));
658 pam_passwd = NULL; /* Unset pam_passwd */
662 retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv);
664 if (retval != PAM_SUCCESS)
667 (errmsg("pam_set_item(PAM_CONV) failed: %s",
668 pam_strerror(pamh, retval))));
669 pam_passwd = NULL; /* Unset pam_passwd */
673 retval = pam_authenticate(pamh, 0);
675 if (retval != PAM_SUCCESS)
678 (errmsg("pam_authenticate failed: %s",
679 pam_strerror(pamh, retval))));
680 pam_passwd = NULL; /* Unset pam_passwd */
684 retval = pam_acct_mgmt(pamh, 0);
686 if (retval != PAM_SUCCESS)
689 (errmsg("pam_acct_mgmt failed: %s",
690 pam_strerror(pamh, retval))));
691 pam_passwd = NULL; /* Unset pam_passwd */
695 retval = pam_end(pamh, retval);
697 if (retval != PAM_SUCCESS)
700 (errmsg("could not release PAM authenticator: %s",
701 pam_strerror(pamh, retval))));
704 pam_passwd = NULL; /* Unset pam_passwd */
706 return (retval == PAM_SUCCESS ? STATUS_OK : STATUS_ERROR);
714 CheckLDAPAuth(Port *port)
724 int ldapversion = LDAP_VERSION3;
725 int ldapport = LDAP_PORT;
726 char fulluser[NAMEDATALEN + 256 + 1];
728 if (!port->auth_arg || port->auth_arg[0] == '\0')
731 (errmsg("LDAP configuration URL not specified")));
736 * Crack the LDAP url. We do a very trivial parse..
737 * ldap[s]://<server>[:<port>]/<basedn>[;prefix[;suffix]]
745 /* ldap, including port number */
746 r = sscanf(port->auth_arg,
747 "ldap://%127[^:]:%i/%127[^;];%127[^;];%127s",
748 server, &ldapport, basedn, prefix, suffix);
751 /* ldaps, including port number */
752 r = sscanf(port->auth_arg,
753 "ldaps://%127[^:]:%i/%127[^;];%127[^;];%127s",
754 server, &ldapport, basedn, prefix, suffix);
760 /* ldap, no port number */
761 r = sscanf(port->auth_arg,
762 "ldap://%127[^/]/%127[^;];%127[^;];%127s",
763 server, basedn, prefix, suffix);
767 /* ldaps, no port number */
768 r = sscanf(port->auth_arg,
769 "ldaps://%127[^/]/%127[^;];%127[^;];%127s",
770 server, basedn, prefix, suffix);
777 (errmsg("invalid LDAP URL: \"%s\"",
782 sendAuthRequest(port, AUTH_REQ_PASSWORD);
784 passwd = recv_password_packet(port);
786 return STATUS_EOF; /* client wouldn't send password */
788 ldap = ldap_init(server, ldapport);
793 (errmsg("could not initialize LDAP: error code %d",
797 (errmsg("could not initialize LDAP: error code %d",
798 (int) LdapGetLastError())));
803 if ((r = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapversion)) != LDAP_SUCCESS)
807 (errmsg("could not set LDAP protocol version: error code %d", r)));
814 if ((r = ldap_start_tls_s(ldap, NULL, NULL)) != LDAP_SUCCESS)
816 static __ldap_start_tls_sA _ldap_start_tls_sA = NULL;
818 if (_ldap_start_tls_sA == NULL)
821 * Need to load this function dynamically because it does not
822 * exist on Windows 2000, and causes a load error for the whole
827 ldaphandle = LoadLibrary("WLDAP32.DLL");
828 if (ldaphandle == NULL)
831 * should never happen since we import other files from
832 * wldap32, but check anyway
836 (errmsg("could not load wldap32.dll")));
839 _ldap_start_tls_sA = (__ldap_start_tls_sA) GetProcAddress(ldaphandle, "ldap_start_tls_sA");
840 if (_ldap_start_tls_sA == NULL)
844 (errmsg("could not load function _ldap_start_tls_sA in wldap32.dll"),
845 errdetail("LDAP over SSL is not supported on this platform.")));
850 * Leak LDAP handle on purpose, because we need the library to stay
851 * open. This is ok because it will only ever be leaked once per
852 * process and is automatically cleaned up on process exit.
855 if ((r = _ldap_start_tls_sA(ldap, NULL, NULL, NULL, NULL)) != LDAP_SUCCESS)
860 (errmsg("could not start LDAP TLS session: error code %d", r)));
865 snprintf(fulluser, sizeof(fulluser), "%s%s%s",
866 prefix, port->user_name, suffix);
867 fulluser[sizeof(fulluser) - 1] = '\0';
869 r = ldap_simple_bind_s(ldap, fulluser, passwd);
872 if (r != LDAP_SUCCESS)
875 (errmsg("LDAP login failed for user \"%s\" on server \"%s\": error code %d",
876 fulluser, server, r)));
882 #endif /* USE_LDAP */
885 * Collect password response packet from frontend.
887 * Returns NULL if couldn't get password, else palloc'd string.
890 recv_password_packet(Port *port)
894 if (PG_PROTOCOL_MAJOR(port->proto) >= 3)
896 /* Expect 'p' message type */
899 mtype = pq_getbyte();
903 * If the client just disconnects without offering a password,
904 * don't make a log entry. This is legal per protocol spec and in
905 * fact commonly done by psql, so complaining just clutters the
910 (errcode(ERRCODE_PROTOCOL_VIOLATION),
911 errmsg("expected password response, got message type %d",
913 return NULL; /* EOF or bad message type */
918 /* For pre-3.0 clients, avoid log entry if they just disconnect */
919 if (pq_peekbyte() == EOF)
920 return NULL; /* EOF */
923 initStringInfo(&buf);
924 if (pq_getmessage(&buf, 1000)) /* receive password */
926 /* EOF - pq_getmessage already logged a suitable message */
932 * Apply sanity check: password packet length should agree with length of
933 * contained string. Note it is safe to use strlen here because
934 * StringInfo is guaranteed to have an appended '\0'.
936 if (strlen(buf.data) + 1 != buf.len)
938 (errcode(ERRCODE_PROTOCOL_VIOLATION),
939 errmsg("invalid password packet size")));
941 /* Do not echo password to logs, for security. */
943 (errmsg("received password packet")));
946 * Return the received string. Note we do not attempt to do any
947 * character-set conversion on it; since we don't yet know the client's
948 * encoding, there wouldn't be much point.
955 * Called when we have sent an authorization request for a password.
956 * Get the response and check it.
959 recv_and_check_password_packet(Port *port)
964 passwd = recv_password_packet(port);
967 return STATUS_EOF; /* client wouldn't send password */
969 result = md5_crypt_verify(port, port->user_name, passwd);