summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>2001-09-06 03:23:38 +0000
committerBruce Momjian <bruce@momjian.us>2001-09-06 03:23:38 +0000
commit04c1f729202b00cf053535587820cd913dbddfc1 (patch)
tree3afd9375fb29892a03e9688f05fd9e966501d13f /src
parent2a34134b6c5a46d8f905a82af51c55a7ec91ecac (diff)
PAM authentication:
> pam_strerror() should be used a few more times, rather than just saying > "Error!". Also, the configure.in snippet seems wrong. You add > -I$pam_prefix/include/security to $INCLUDES and then you #include > <security/pam_appl.h>. This whole thing is probably unnecessary, since > PAM is a system library on the systems where it exists, so the headers > and libraries are found automatically, unlike OpenSSL and > Kerberos. See attached revised patch. (I'm sure the configure.in stuff can be done right/better, I'm just not enough of a autoconf guru to know what to change it to.) Dominic J. Eidson
Diffstat (limited to 'src')
-rw-r--r--src/backend/libpq/auth.c233
-rw-r--r--src/backend/libpq/hba.c7
-rw-r--r--src/backend/libpq/pg_hba.conf.sample7
-rw-r--r--src/include/libpq/hba.h5
-rw-r--r--src/include/pg_config.h.in5
5 files changed, 243 insertions, 14 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index adcb881c1ce..56c1d01c5c0 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.64 2001/08/21 15:21:25 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.65 2001/09/06 03:23:38 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -43,6 +43,24 @@ static int recv_and_check_passwordv0(Port *port);
char *pg_krb_server_keyfile;
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+
+#define PGSQL_PAM_SERVICE "postgresql" /* Service name passed to PAM */
+
+static int CheckPAMAuth(Port *port, char *user, char *password);
+static int pam_passwd_conv_proc(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *appdata_ptr);
+
+static struct pam_conv pam_passw_conv = {
+ &pam_passwd_conv_proc,
+ NULL
+};
+
+static char * pam_passwd = NULL; /* Workaround for Solaris 2.6 brokenness */
+static Port * pam_port_cludge; /* Workaround for passing "Port
+ * *port" into pam_passwd_conv_proc */
+#endif /* USE_PAM */
#ifdef KRB4
/*----------------------------------------------------------------
@@ -428,6 +446,11 @@ auth_failed(Port *port)
case uaPassword:
authmethod = "Password";
break;
+#ifdef USE_PAM
+ case uaPAM:
+ authmethod = "PAM";
+ break;
+#endif /* USE_PAM */
}
elog(FATAL, "%s authentication failed for user \"%s\"",
@@ -525,15 +548,21 @@ ClientAuthentication(Port *port)
status = recv_and_check_password_packet(port);
break;
- case uaCrypt:
- sendAuthRequest(port, AUTH_REQ_CRYPT);
- status = recv_and_check_password_packet(port);
- break;
-
- case uaPassword:
- sendAuthRequest(port, AUTH_REQ_PASSWORD);
- status = recv_and_check_password_packet(port);
+ case uaCrypt:
+ sendAuthRequest(port, AUTH_REQ_CRYPT);
+ status = recv_and_check_password_packet(port);
+ break;
+
+ case uaPassword:
+ sendAuthRequest(port, AUTH_REQ_PASSWORD);
+ status = recv_and_check_password_packet(port);
+ break;
+#ifdef USE_PAM
+ case uaPAM:
+ pam_port_cludge = port;
+ status = CheckPAMAuth(port, port->user, "");
break;
+#endif /* USE_PAM */
case uaTrust:
status = STATUS_OK;
@@ -577,7 +606,190 @@ sendAuthRequest(Port *port, AuthRequest areq)
pq_flush();
}
+#ifdef USE_PAM
+
+/*
+ * PAM conversation function
+ */
+
+static int
+pam_passwd_conv_proc (int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
+{
+ StringInfoData buf;
+ int32 len;
+
+ if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF) {
+ switch(msg[0]->msg_style) {
+ case PAM_ERROR_MSG:
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "pam_passwd_conv_proc: Error from underlying PAM layer: '%s'\n", msg[0]->msg);
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ return PAM_CONV_ERR;
+ default:
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "pam_passwd_conv_proc: Unexpected PAM conversation %d/'%s'\n",
+ msg[0]->msg_style, msg[0]->msg);
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ return PAM_CONV_ERR;
+ }
+ }
+
+ if (!appdata_ptr) {
+ /* Workaround for Solaris 2.6 where the PAM library is broken
+ * and does not pass appdata_ptr to the conversation routine
+ */
+ appdata_ptr = pam_passwd;
+ }
+
+ /* Password wasn't passed to PAM the first time around - let's go
+ * ask the client to send a password, which we then stuff into
+ * PAM.
+ */
+ if(strlen(appdata_ptr) == 0) {
+ sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
+ if (pq_eof() == EOF || pq_getint(&len, 4) == EOF) {
+ return PAM_CONV_ERR; /* client didn't want to send password */
+ }
+
+ initStringInfo(&buf);
+ pq_getstr(&buf);
+ if (DebugLvl)
+ fprintf(stderr, "received PAM packet with len=%d, pw=%s\n",
+ len, buf.data);
+
+ if(strlen(buf.data) == 0) {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, "pam_passwd_conv_proc: no password\n");
+ fputs(PQerrormsg, stderr);
+ return PAM_CONV_ERR;
+ }
+ appdata_ptr = buf.data;
+ }
+
+ /* Explicitly not using palloc here - PAM will free this memory in
+ * pam_end()
+ */
+ *resp = calloc(num_msg, sizeof(struct pam_response));
+ if (!*resp) {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, "pam_passwd_conv_proc: Out of memory!\n");
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ if(buf.data)
+ pfree(buf.data);
+ return PAM_CONV_ERR;
+ }
+
+ (*resp)[0].resp = strdup((char *) appdata_ptr);
+ (*resp)[0].resp_retcode = 0;
+
+ return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
+}
+
+
+/*
+ * Check authentication against PAM.
+ */
+static int
+CheckPAMAuth(Port *port, char *user, char *password)
+{
+ int retval;
+ pam_handle_t *pamh = NULL;
+
+ /*
+ * Apparently, Solaris 2.6 is broken, and needs ugly static
+ * variable workaround
+ */
+ pam_passwd = password;
+
+ /* Set the application data portion of the conversation struct
+ * This is later used inside the PAM conversation to pass the
+ * password to the authentication module.
+ */
+ pam_passw_conv.appdata_ptr = (char*) password; /* from password above, not allocated */
+
+ /* Optionally, one can set the service name in pg_hba.conf */
+ if(port->auth_arg[0] == '\0') {
+ retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@", &pam_passw_conv, &pamh);
+ } else {
+ retval = pam_start(port->auth_arg, "pgsql@", &pam_passw_conv, &pamh);
+ }
+
+ if (retval != PAM_SUCCESS) {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "CheckPAMAuth: Failed to create PAM authenticator: '%s'\n",
+ pam_strerror(pamh, retval));
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ pam_passwd = NULL; /* Unset pam_passwd */
+ return STATUS_ERROR;
+ }
+
+ if (retval == PAM_SUCCESS) {
+ retval = pam_set_item(pamh, PAM_USER, user);
+ } else {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "CheckPAMAuth: pam_set_item(PAM_USER) failed: '%s'\n",
+ pam_strerror(pamh, retval));
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ pam_passwd = NULL; /* Unset pam_passwd */
+ return STATUS_ERROR;
+ }
+ if (retval == PAM_SUCCESS) {
+ retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv);
+ } else {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "CheckPAMAuth: pam_set_item(PAM_CONV) failed: '%s'\n",
+ pam_strerror(pamh, retval));
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ pam_passwd = NULL; /* Unset pam_passwd */
+ return STATUS_ERROR;
+ }
+ if (retval == PAM_SUCCESS) {
+ retval = pam_authenticate(pamh, 0);
+ } else {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "CheckPAMAuth: pam_authenticate failed: '%s'\n",
+ pam_strerror(pamh, retval));
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ pam_passwd = NULL; /* Unset pam_passwd */
+ return STATUS_ERROR;
+ }
+ if (retval == PAM_SUCCESS) {
+ retval = pam_acct_mgmt(pamh, 0);
+ } else {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "CheckPAMAuth: pam_acct_mgmt failed: '%s'\n",
+ pam_strerror(pamh, retval));
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ pam_passwd = NULL; /* Unset pam_passwd */
+ return STATUS_ERROR;
+ }
+ if (retval == PAM_SUCCESS) {
+ retval = pam_end(pamh, retval);
+ if(retval != PAM_SUCCESS) {
+ snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+ "CheckPAMAuth: Failed to release PAM authenticator: '%s'\n",
+ pam_strerror(pamh, retval));
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ }
+
+ pam_passwd = NULL; /* Unset pam_passwd */
+
+ return (retval == PAM_SUCCESS ? STATUS_OK : STATUS_ERROR);
+ } else {
+ return STATUS_ERROR;
+ }
+}
+
+
+#endif /* USE_PAM */
/*
* Called when we have received the password packet.
@@ -670,6 +882,9 @@ map_old_to_new(Port *port, UserAuth old, int status)
case uaMD5:
case uaCrypt:
case uaReject:
+#ifdef USE_PAM
+ case uaPAM:
+#endif /* USE_PAM */
status = STATUS_ERROR;
break;
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 9dddb617540..69f88c257fd 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.68 2001/08/21 15:49:17 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.69 2001/09/06 03:23:38 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -235,6 +235,10 @@ parse_hba_auth(List *line, ProtocolVersion proto, UserAuth *userauth_p,
*userauth_p = uaMD5;
else if (strcmp(token, "crypt") == 0)
*userauth_p = uaCrypt;
+#ifdef USE_PAM
+ else if (strcmp(token, "pam") == 0)
+ *userauth_p = uaPAM;
+#endif
else
*error_p = true;
line = lnext(line);
@@ -277,7 +281,6 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
line_number = lfirsti(line);
line = lnext(line);
Assert(line != NIL);
-
/* Check the record type. */
token = lfirst(line);
if (strcmp(token, "local") == 0)
diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample
index c348b7668c0..0aff0f43fcc 100644
--- a/src/backend/libpq/pg_hba.conf.sample
+++ b/src/backend/libpq/pg_hba.conf.sample
@@ -148,7 +148,12 @@
# that are part of a network specified later in the file.
# To be effective, "reject" must appear before the later
# entries.
-#
+#
+# pam: Authentication is passed off to PAM (PostgreSQL must be
+# configured --with-pam), using the default service name
+# "postgresql" - you can specify your own service name, by
+# setting AUTH_ARGUMENT to the desired service name.
+#
#
#
# Examples
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 02eee16d9a0..6525d5ecb3f 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -4,7 +4,7 @@
* Interface to hba.c
*
*
- * $Id: hba.h,v 1.25 2001/08/24 16:59:10 momjian Exp $
+ * $Id: hba.h,v 1.26 2001/09/06 03:23:38 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -31,6 +31,9 @@
typedef enum UserAuth
{
+#ifdef USE_PAM
+ uaPAM,
+#endif /* USE_PAM */
uaReject,
uaKrb4,
uaKrb5,
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 8a165be62db..30a3834261d 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -8,7 +8,7 @@
* or in pg_config.h afterwards. Of course, if you edit pg_config.h, then your
* changes will be overwritten the next time you run configure.
*
- * $Id: pg_config.h.in,v 1.2 2001/09/06 02:56:32 momjian Exp $
+ * $Id: pg_config.h.in,v 1.3 2001/09/06 03:23:38 momjian Exp $
*/
#ifndef PG_CONFIG_H
@@ -63,6 +63,9 @@
/* Define to build with (Open)SSL support (--with-openssl[=DIR]) */
#undef USE_SSL
+/* Define to build with PAM Support */
+#undef USE_PAM
+
/*
* DEF_PGPORT is the TCP port number on which the Postmaster listens and
* which clients will try to connect to. This is just a default value;