1 /*-------------------------------------------------------------------------
4 * Routines to handle network authentication
6 * Portions Copyright (c) 1996-2006, 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.139 2006/07/14 14:52: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>
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 codepaths the same as the
73 #define LDAP_DEPRECATED 1
76 /* Header broken in MingW */
77 #define ldap_start_tls_sA __BROKEN_LDAP_HEADER
79 #undef ldap_start_tls_sA
81 /* Correct header from the Platform SDK */
82 WINLDAPAPI ULONG ldap_start_tls_sA (
83 IN PLDAP ExternalHandle,
84 OUT PULONG ServerReturnValue,
85 OUT LDAPMessage **result,
86 IN PLDAPControlA *ServerControls,
87 IN PLDAPControlA *ClientControls
91 static int CheckLDAPAuth(Port *port);
96 /*----------------------------------------------------------------
97 * MIT Kerberos authentication system - protocol version 5
98 *----------------------------------------------------------------
102 /* Some old versions of Kerberos do not include <com_err.h> in <krb5.h> */
103 #if !defined(__COM_ERR_H) && !defined(__COM_ERR_H__)
108 * pg_an_to_ln -- return the local name corresponding to an authentication
111 * XXX Assumes that the first aname component is the user name. This is NOT
112 * necessarily so, since an aname can actually be something out of your
113 * worst X.400 nightmare, like
114 * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
115 * Note that the MIT an_to_ln code does the same thing if you don't
116 * provide an aname mapping database...it may be a better idea to use
117 * krb5_an_to_ln, except that it punts if multiple components are found,
118 * and we can't afford to punt.
121 pg_an_to_ln(char *aname)
125 if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
132 * Various krb5 state which is not connection specfic, and a flag to
133 * indicate whether we have initialised it yet.
135 static int pg_krb5_initialised;
136 static krb5_context pg_krb5_context;
137 static krb5_keytab pg_krb5_keytab;
138 static krb5_principal pg_krb5_server;
144 krb5_error_code retval;
147 if (pg_krb5_initialised)
150 retval = krb5_init_context(&pg_krb5_context);
154 (errmsg("Kerberos initialization returned error %d",
156 com_err("postgres", retval, "while initializing krb5");
160 retval = krb5_kt_resolve(pg_krb5_context, pg_krb_server_keyfile, &pg_krb5_keytab);
164 (errmsg("Kerberos keytab resolving returned error %d",
166 com_err("postgres", retval, "while resolving keytab file \"%s\"",
167 pg_krb_server_keyfile);
168 krb5_free_context(pg_krb5_context);
173 * If no hostname was specified, pg_krb_server_hostname is already NULL.
174 * If it's set to blank, force it to NULL.
176 khostname = pg_krb_server_hostname;
177 if (khostname && khostname[0] == '\0')
180 retval = krb5_sname_to_principal(pg_krb5_context,
188 (errmsg("Kerberos sname_to_principal(\"%s\", \"%s\") returned error %d",
189 khostname ? khostname : "server hostname", pg_krb_srvnam, retval)));
190 com_err("postgres", retval,
191 "while getting server principal for server \"%s\" for service \"%s\"",
192 khostname ? khostname : "server hostname", pg_krb_srvnam);
193 krb5_kt_close(pg_krb5_context, pg_krb5_keytab);
194 krb5_free_context(pg_krb5_context);
198 pg_krb5_initialised = 1;
204 * pg_krb5_recvauth -- server routine to receive authentication information
207 * We still need to compare the username obtained from the client's setup
208 * packet to the authenticated name.
210 * We have our own keytab file because postgres is unlikely to run as root,
211 * and so cannot read the default keytab.
214 pg_krb5_recvauth(Port *port)
216 krb5_error_code retval;
218 krb5_auth_context auth_context = 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;
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);
759 /* ldap, no port number */
760 r = sscanf(port->auth_arg,
761 "ldap://%127[^/]/%127[^;];%127[^;];%127s",
762 server, basedn, prefix, suffix);
766 /* ldaps, no port number */
767 r = sscanf(port->auth_arg,
768 "ldaps://%127[^/]/%127[^;];%127[^;];%127s",
769 server, basedn, prefix, suffix);
775 (errmsg("invalid LDAP URL: \"%s\"",
780 sendAuthRequest(port, AUTH_REQ_PASSWORD);
782 passwd = recv_password_packet(port);
784 return STATUS_EOF; /* client wouldn't send password */
786 ldap = ldap_init(server, ldapport);
791 (errmsg("could not initialize LDAP: error %d",
795 (errmsg("could not initialize LDAP: error %d",
796 (int) LdapGetLastError())));
801 if ((r = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapversion)) != LDAP_SUCCESS)
804 (errmsg("could not set LDAP protocol version: error %d", r)));
811 if ((r = ldap_start_tls_s(ldap, NULL, NULL)) != LDAP_SUCCESS)
813 if ((r = ldap_start_tls_sA(ldap, NULL, NULL, NULL, NULL)) != LDAP_SUCCESS)
817 (errmsg("could not start LDAP TLS session: error %d", r)));
822 snprintf(fulluser, sizeof(fulluser)-1, "%s%s%s",
823 prefix, port->user_name, suffix);
824 fulluser[sizeof(fulluser)-1] = '\0';
826 r = ldap_simple_bind_s(ldap, fulluser, passwd);
829 if (r != LDAP_SUCCESS)
832 (errmsg("LDAP login failed for user \"%s\" on server \"%s\": error %d",
833 fulluser, server, r)));
840 #endif /* USE_LDAP */
843 * Collect password response packet from frontend.
845 * Returns NULL if couldn't get password, else palloc'd string.
848 recv_password_packet(Port *port)
852 if (PG_PROTOCOL_MAJOR(port->proto) >= 3)
854 /* Expect 'p' message type */
857 mtype = pq_getbyte();
861 * If the client just disconnects without offering a password,
862 * don't make a log entry. This is legal per protocol spec and in
863 * fact commonly done by psql, so complaining just clutters the
868 (errcode(ERRCODE_PROTOCOL_VIOLATION),
869 errmsg("expected password response, got message type %d",
871 return NULL; /* EOF or bad message type */
876 /* For pre-3.0 clients, avoid log entry if they just disconnect */
877 if (pq_peekbyte() == EOF)
878 return NULL; /* EOF */
881 initStringInfo(&buf);
882 if (pq_getmessage(&buf, 1000)) /* receive password */
884 /* EOF - pq_getmessage already logged a suitable message */
890 * Apply sanity check: password packet length should agree with length of
891 * contained string. Note it is safe to use strlen here because
892 * StringInfo is guaranteed to have an appended '\0'.
894 if (strlen(buf.data) + 1 != buf.len)
896 (errcode(ERRCODE_PROTOCOL_VIOLATION),
897 errmsg("invalid password packet size")));
899 /* Do not echo password to logs, for security. */
901 (errmsg("received password packet")));
904 * Return the received string. Note we do not attempt to do any
905 * character-set conversion on it; since we don't yet know the client's
906 * encoding, there wouldn't be much point.
913 * Called when we have sent an authorization request for a password.
914 * Get the response and check it.
917 recv_and_check_password_packet(Port *port)
922 passwd = recv_password_packet(port);
925 return STATUS_EOF; /* client wouldn't send password */
927 result = md5_crypt_verify(port, port->user_name, passwd);