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.132 2005/10/17 16:24:19 tgl 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" into
69 * 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 NULL.
151 * 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 : "server hostname", pg_krb_srvnam, retval)));
167 com_err("postgres", retval,
168 "while getting server principal for server \"%s\" for service \"%s\"",
169 khostname ? khostname : "server hostname", 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);
266 pg_krb5_recvauth(Port *port)
269 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
270 errmsg("Kerberos 5 not implemented on this server")));
277 * Tell the user the authentication failed, but not (much about) why.
279 * There is a tradeoff here between security concerns and making life
280 * unnecessarily difficult for legitimate users. We would not, for example,
281 * want to report the password we were expecting to receive...
282 * But it seems useful to report the username and authorization method
283 * in use, and these are items that must be presumed known to an attacker
285 * Note that many sorts of failure report additional information in the
286 * postmaster log, which we hope is only readable by good guys.
289 auth_failed(Port *port, int status)
294 * If we failed due to EOF from client, just quit; there's no point in
295 * trying to send a message to the client, and not much point in logging
296 * the failure in the postmaster log. (Logging the failure might be
297 * desirable, were it not for the fact that libpq closes the connection
298 * unceremoniously if challenged for a password when it hasn't got one to
299 * send. We'll get a useless log entry for every psql connection under
300 * password auth, even if it's perfectly successful, if we log STATUS_EOF
303 if (status == STATUS_EOF)
306 switch (port->auth_method)
309 errstr = gettext_noop("authentication failed for user \"%s\": host rejected");
312 errstr = gettext_noop("Kerberos 5 authentication failed for user \"%s\"");
315 errstr = gettext_noop("\"trust\" authentication failed for user \"%s\"");
318 errstr = gettext_noop("Ident authentication failed for user \"%s\"");
323 errstr = gettext_noop("password authentication failed for user \"%s\"");
327 errstr = gettext_noop("PAM authentication failed for user \"%s\"");
331 errstr = gettext_noop("authentication failed for user \"%s\": invalid authentication method");
336 (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
337 errmsg(errstr, port->user_name)));
343 * Client authentication starts here. If there is an error, this
344 * function does not return and the backend process is terminated.
347 ClientAuthentication(Port *port)
349 int status = STATUS_ERROR;
352 * Get the authentication method to use for this frontend/database
353 * combination. Note: a failure return indicates a problem with the hba
354 * config file, not with the request. hba.c should have dropped an error
355 * message into the postmaster logfile if it failed.
357 if (hba_getauthmethod(port) != STATUS_OK)
359 (errcode(ERRCODE_CONFIG_FILE_ERROR),
360 errmsg("missing or erroneous pg_hba.conf file"),
361 errhint("See server log for details.")));
363 switch (port->auth_method)
368 * This could have come from an explicit "reject" entry in
369 * pg_hba.conf, but more likely it means there was no matching
370 * entry. Take pity on the poor user and issue a helpful error
371 * message. NOTE: this is not a security breach, because all the
372 * info reported here is known at the frontend and must be assumed
373 * known to bad guys. We're merely helping out the less clueful
377 char hostinfo[NI_MAXHOST];
379 pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
380 hostinfo, sizeof(hostinfo),
386 (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
387 errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
388 hostinfo, port->user_name, port->database_name,
389 port->ssl ? _("SSL on") : _("SSL off"))));
392 (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
393 errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"",
394 hostinfo, port->user_name, port->database_name)));
400 sendAuthRequest(port, AUTH_REQ_KRB5);
401 status = pg_krb5_recvauth(port);
407 * If we are doing ident on unix-domain sockets, use SCM_CREDS
408 * only if it is defined and SO_PEERCRED isn't.
410 #if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && \
411 (defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \
412 (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS)))
413 if (port->raddr.addr.ss_family == AF_UNIX)
415 #if defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
418 * Receive credentials on next message receipt, BSD/OS,
419 * NetBSD. We need to set this before the client sends the
424 if (setsockopt(port->sock, 0, LOCAL_CREDS, &on, sizeof(on)) < 0)
426 (errcode_for_socket_access(),
427 errmsg("could not enable credential reception: %m")));
430 sendAuthRequest(port, AUTH_REQ_SCM_CREDS);
433 status = authident(port);
437 sendAuthRequest(port, AUTH_REQ_MD5);
438 status = recv_and_check_password_packet(port);
442 sendAuthRequest(port, AUTH_REQ_CRYPT);
443 status = recv_and_check_password_packet(port);
447 sendAuthRequest(port, AUTH_REQ_PASSWORD);
448 status = recv_and_check_password_packet(port);
453 pam_port_cludge = port;
454 status = CheckPAMAuth(port, port->user_name, "");
463 if (status == STATUS_OK)
464 sendAuthRequest(port, AUTH_REQ_OK);
466 auth_failed(port, status);
471 * Send an authentication request packet to the frontend.
474 sendAuthRequest(Port *port, AuthRequest areq)
478 pq_beginmessage(&buf, 'R');
479 pq_sendint(&buf, (int32) areq, sizeof(int32));
481 /* Add the salt for encrypted passwords. */
482 if (areq == AUTH_REQ_MD5)
483 pq_sendbytes(&buf, port->md5Salt, 4);
484 else if (areq == AUTH_REQ_CRYPT)
485 pq_sendbytes(&buf, port->cryptSalt, 2);
490 * Flush message so client will see it, except for AUTH_REQ_OK, which need
491 * not be sent until we are ready for queries.
493 if (areq != AUTH_REQ_OK)
501 * PAM conversation function
505 pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
506 struct pam_response ** resp, void *appdata_ptr)
508 if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
510 switch (msg[0]->msg_style)
514 (errmsg("error from underlying PAM layer: %s",
519 (errmsg("unsupported PAM conversation %d/%s",
520 msg[0]->msg_style, msg[0]->msg)));
528 * Workaround for Solaris 2.6 where the PAM library is broken and does
529 * not pass appdata_ptr to the conversation routine
531 appdata_ptr = pam_passwd;
535 * Password wasn't passed to PAM the first time around - let's go ask the
536 * client to send a password, which we then stuff into PAM.
538 if (strlen(appdata_ptr) == 0)
542 sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
543 passwd = recv_password_packet(pam_port_cludge);
546 return PAM_CONV_ERR; /* client didn't want to send password */
548 if (strlen(passwd) == 0)
551 (errmsg("empty password returned by client")));
554 appdata_ptr = passwd;
558 * Explicitly not using palloc here - PAM will free this memory in
561 *resp = calloc(num_msg, sizeof(struct pam_response));
565 (errcode(ERRCODE_OUT_OF_MEMORY),
566 errmsg("out of memory")));
570 (*resp)[0].resp = strdup((char *) appdata_ptr);
571 (*resp)[0].resp_retcode = 0;
573 return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
578 * Check authentication against PAM.
581 CheckPAMAuth(Port *port, char *user, char *password)
584 pam_handle_t *pamh = NULL;
587 * Apparently, Solaris 2.6 is broken, and needs ugly static variable
590 pam_passwd = password;
593 * Set the application data portion of the conversation struct This is
594 * later used inside the PAM conversation to pass the password to the
595 * authentication module.
597 pam_passw_conv.appdata_ptr = (char *) password; /* from password above,
600 /* Optionally, one can set the service name in pg_hba.conf */
601 if (port->auth_arg && port->auth_arg[0] != '\0')
602 retval = pam_start(port->auth_arg, "pgsql@",
603 &pam_passw_conv, &pamh);
605 retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@",
606 &pam_passw_conv, &pamh);
608 if (retval != PAM_SUCCESS)
611 (errmsg("could not create PAM authenticator: %s",
612 pam_strerror(pamh, retval))));
613 pam_passwd = NULL; /* Unset pam_passwd */
617 retval = pam_set_item(pamh, PAM_USER, user);
619 if (retval != PAM_SUCCESS)
622 (errmsg("pam_set_item(PAM_USER) failed: %s",
623 pam_strerror(pamh, retval))));
624 pam_passwd = NULL; /* Unset pam_passwd */
628 retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv);
630 if (retval != PAM_SUCCESS)
633 (errmsg("pam_set_item(PAM_CONV) failed: %s",
634 pam_strerror(pamh, retval))));
635 pam_passwd = NULL; /* Unset pam_passwd */
639 retval = pam_authenticate(pamh, 0);
641 if (retval != PAM_SUCCESS)
644 (errmsg("pam_authenticate failed: %s",
645 pam_strerror(pamh, retval))));
646 pam_passwd = NULL; /* Unset pam_passwd */
650 retval = pam_acct_mgmt(pamh, 0);
652 if (retval != PAM_SUCCESS)
655 (errmsg("pam_acct_mgmt failed: %s",
656 pam_strerror(pamh, retval))));
657 pam_passwd = NULL; /* Unset pam_passwd */
661 retval = pam_end(pamh, retval);
663 if (retval != PAM_SUCCESS)
666 (errmsg("could not release PAM authenticator: %s",
667 pam_strerror(pamh, retval))));
670 pam_passwd = NULL; /* Unset pam_passwd */
672 return (retval == PAM_SUCCESS ? STATUS_OK : STATUS_ERROR);
678 * Collect password response packet from frontend.
680 * Returns NULL if couldn't get password, else palloc'd string.
683 recv_password_packet(Port *port)
687 if (PG_PROTOCOL_MAJOR(port->proto) >= 3)
689 /* Expect 'p' message type */
692 mtype = pq_getbyte();
696 * If the client just disconnects without offering a password,
697 * don't make a log entry. This is legal per protocol spec and in
698 * fact commonly done by psql, so complaining just clutters the
703 (errcode(ERRCODE_PROTOCOL_VIOLATION),
704 errmsg("expected password response, got message type %d",
706 return NULL; /* EOF or bad message type */
711 /* For pre-3.0 clients, avoid log entry if they just disconnect */
712 if (pq_peekbyte() == EOF)
713 return NULL; /* EOF */
716 initStringInfo(&buf);
717 if (pq_getmessage(&buf, 1000)) /* receive password */
719 /* EOF - pq_getmessage already logged a suitable message */
725 * Apply sanity check: password packet length should agree with length of
726 * contained string. Note it is safe to use strlen here because
727 * StringInfo is guaranteed to have an appended '\0'.
729 if (strlen(buf.data) + 1 != buf.len)
731 (errcode(ERRCODE_PROTOCOL_VIOLATION),
732 errmsg("invalid password packet size")));
734 /* Do not echo password to logs, for security. */
736 (errmsg("received password packet")));
739 * Return the received string. Note we do not attempt to do any
740 * character-set conversion on it; since we don't yet know the client's
741 * encoding, there wouldn't be much point.
748 * Called when we have sent an authorization request for a password.
749 * Get the response and check it.
752 recv_and_check_password_packet(Port *port)
757 passwd = recv_password_packet(port);
760 return STATUS_EOF; /* client wouldn't send password */
762 result = md5_crypt_verify(port, port->user_name, passwd);