Index: configure.in =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/configure.in,v retrieving revision 1.119 diff -u -r1.119 configure.in --- configure.in 2001/04/23 15:14:58 1.119 +++ configure.in 2001/04/27 15:36:57 @@ -389,7 +389,6 @@ AC_MSG_RESULT([$with_perl]) AC_SUBST(with_perl) - # # Optionally build Python interface module # @@ -489,7 +488,30 @@ [The name of the PostgreSQL service principal in Kerberos]) +# +# PAM +# +AC_MSG_CHECKING([whether to build with PAM support]) +PGAC_ARG_OPTARG(with, pam, + [ --with-pam[=DIR] build with PAM support [/usr]], + [pam_prefix=/usr], + [pam_prefix=$withval], +[ + AC_MSG_RESULT([yes]) + AC_DEFINE([USE_PAM], 1, [Define to build with PAM support]) + if test -d "${pam_prefix}/include" ; then + INCLUDES="$INCLUDES -I${pam_prefix}/include/security" + fi + if test -d "${pam_prefix}/lib" ; then + LIBDIRS="$LIBDIRS -L${pam_prefix}/lib" + fi +], +[AC_MSG_RESULT(no)]) + +AC_SUBST(with_pam) + + # # OpenSSL # @@ -701,7 +723,11 @@ AC_CHECK_LIB(ssl, [SSL_library_init], [], [AC_MSG_ERROR([library 'ssl' is required for OpenSSL])]) fi +if test "$with_pam" = yes ; then + AC_CHECK_LIB(pam, [pam_start], [], [AC_MSG_ERROR([library 'pam' is required for PAM])]) +fi + ## ## Header files ## @@ -737,6 +763,10 @@ if test "$with_openssl" = yes ; then AC_CHECK_HEADER([openssl/ssl.h], [], [AC_MSG_ERROR([header file is required for OpenSSL])]) AC_CHECK_HEADER([openssl/err.h], [], [AC_MSG_ERROR([header file is required for OpenSSL])]) +fi + +if test "$with_pam" = yes ; then + AC_CHECK_HEADER([security/pam_appl.h], [], [AC_MSG_ERROR([header file is required for PAM])]) fi Index: doc/src/sgml/client-auth.sgml =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v retrieving revision 1.10 diff -u -r1.10 client-auth.sgml --- doc/src/sgml/client-auth.sgml 2001/03/15 20:01:32 1.10 +++ doc/src/sgml/client-auth.sgml 2001/04/27 15:36:57 @@ -243,6 +243,19 @@ + + + pam + + + PAM is used to authenticate the user. If the + authentication option + is specified, PostgreSQL will use it + as service name, otherwise it will use the default, which is + postgresql. + + + Index: src/backend/libpq/Makefile =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/Makefile,v retrieving revision 1.24 diff -u -r1.24 Makefile --- src/backend/libpq/Makefile 2000/08/25 10:00:30 1.24 +++ src/backend/libpq/Makefile 2001/04/27 15:36:57 @@ -18,7 +18,6 @@ auth.o crypt.o hba.o password.o \ pqcomm.o pqformat.o pqpacket.o pqsignal.o util.o - all: SUBSYS.o SUBSYS.o: $(OBJS) Index: src/backend/libpq/auth.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/auth.c,v retrieving revision 1.52 diff -u -r1.52 auth.c --- src/backend/libpq/auth.c 2001/03/22 03:59:30 1.52 +++ src/backend/libpq/auth.c 2001/04/27 15:36:57 @@ -30,6 +30,7 @@ #include #include "postgres.h" +#include "config.h" #include "libpq/auth.h" #include "libpq/crypt.h" @@ -38,6 +39,27 @@ #include "libpq/password.h" #include "miscadmin.h" +#ifdef USE_PAM +#include + +#define PGSQL_PAM_SERVICE "postgresql" /* Service name passed to PAM */ + +static int handle_pam_auth(void *arg, PacketLen len, void *pkt); +static int checkPamPasswd(Port *port, char *user, char *password); +static int pam_passw_conv_proc(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr); +static int readPamPasswordPacket(void *arg, PacketLen len, void *pkt); + + +static struct pam_conv pam_passw_conv = { + &pam_passw_conv_proc, + NULL +}; + +static char * pam_passwd = NULL; /* Workaround for Solaris 2.6 brokenness */ + +#endif /* USE_PAM */ + static void sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler); static int handle_done_auth(void *arg, PacketLen len, void *pkt); static int handle_krb4_auth(void *arg, PacketLen len, void *pkt); @@ -439,6 +461,11 @@ case uaCrypt: authmethod = "Password"; break; +#ifdef USE_PAM + case uaPam: + authmethod = "PAM"; + break; +#endif } sprintf(buffer, "%s authentication failed for user '%s'", @@ -545,6 +572,12 @@ areq = AUTH_REQ_CRYPT; auth_handler = handle_password_auth; break; +#ifdef USE_PAM + case uaPam: + areq = AUTH_REQ_PAM; + auth_handler = handle_pam_auth; + break; +#endif } /* Tell the frontend what we want next. */ @@ -666,8 +699,158 @@ return STATUS_OK; } +#ifdef USE_PAM +/* + * Called when we have told the front end that it should use password + * authentication, however, we authenticate into PAM on the backend. + */ + +static int +handle_pam_auth(void *arg, PacketLen len, void *pkt) +{ + Port *port = (Port *) arg; + + /* Set up the read of the password packet. */ + + PacketReceiveSetup(&port->pktInfo, readPamPasswordPacket, (void *) port); + + return STATUS_OK; +} + +/* + * Called when we have received the password packet, and check into PAM. + */ +static int +readPamPasswordPacket(void *arg, PacketLen len, void *pkt) +{ + char password[sizeof(PasswordPacket) + 1]; + Port *port = (Port *) arg; + + /* Silently truncate a password that is too big. */ + + if (len > sizeof(PasswordPacket)) + len = sizeof(PasswordPacket); + + StrNCpy(password, ((PasswordPacket *) pkt)->passwd, len); + + if (checkPamPasswd(port, port->user, password) != STATUS_OK) + auth_failed(port); + else + sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth); + + return STATUS_OK; /* don't close the connection yet */ +} + +/* + * PAM conversation function + */ + +static int +pam_passw_conv_proc (int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) +{ + if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pam_passwd_conv: 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; + } + if (!appdata_ptr) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "ERROR: No password available to pam_passw_conv_proc!\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return PAM_CONV_ERR; + } + + /* 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, "ERROR: Out of memory!\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + 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 +checkPamPasswd(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, + "Failed to create PAM authenticator!\n"); + 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); + if (retval == PAM_SUCCESS) + retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv); + if (retval == PAM_SUCCESS) + retval = pam_authenticate(pamh, 0); + if (retval == PAM_SUCCESS) + retval = pam_acct_mgmt(pamh, 0); + if (retval == PAM_SUCCESS) { + retval = pam_end(pamh, retval); + if(retval != PAM_SUCCESS) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "Failed to release PAM authenticator!\n"); + 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 /* PAM */ + +/* * Called when we have received the password packet. */ @@ -760,8 +943,11 @@ { switch (port->auth_method) { - case uaCrypt: - case uaReject: + case uaCrypt: + case uaReject: +#ifdef USE_PAM + case uaPam: +#endif status = STATUS_ERROR; break; Index: src/backend/libpq/hba.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/hba.c,v retrieving revision 1.55 diff -u -r1.55 hba.c --- src/backend/libpq/hba.c 2001/02/10 02:31:26 1.55 +++ src/backend/libpq/hba.c 2001/04/27 15:36:57 @@ -125,6 +125,10 @@ *userauth_p = uaReject; else if (strcmp(buf, "crypt") == 0) *userauth_p = uaCrypt; +#ifdef USE_PAM + else if (strcmp(buf, "pam") == 0) + *userauth_p = uaPam; +#endif else { *error_p = true; Index: src/backend/libpq/pg_hba.conf.sample =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/pg_hba.conf.sample,v retrieving revision 1.17 diff -u -r1.17 pg_hba.conf.sample --- src/backend/libpq/pg_hba.conf.sample 2000/11/21 20:44:32 1.17 +++ src/backend/libpq/pg_hba.conf.sample 2001/04/27 15:36:57 @@ -121,10 +121,15 @@ # # krb5: Kerberos V5 authentication is used. # +# 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. +# # reject: Reject the connection. # # Local (UNIX socket) connections support only AUTHTYPEs "trust", -# "password", "crypt", and "reject". +# "password", "crypt", "pam", and "reject". # Examples Index: src/include/config.h.in =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/config.h.in,v retrieving revision 1.162 diff -u -r1.162 config.h.in --- src/include/config.h.in 2001/04/14 22:55:02 1.162 +++ src/include/config.h.in 2001/04/27 15:36:57 @@ -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; Index: src/include/libpq/hba.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/hba.h,v retrieving revision 1.19 diff -u -r1.19 hba.h --- src/include/libpq/hba.h 2001/03/22 04:00:47 1.19 +++ src/include/libpq/hba.h 2001/04/27 15:36:57 @@ -35,6 +35,9 @@ uaTrust, uaIdent, uaPassword, +#ifdef USE_PAM + uaPam, +#endif uaCrypt } UserAuth; Index: src/include/libpq/pqcomm.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/pqcomm.h,v retrieving revision 1.55 diff -u -r1.55 pqcomm.h --- src/include/libpq/pqcomm.h 2001/03/22 04:00:48 1.55 +++ src/include/libpq/pqcomm.h 2001/04/27 15:36:57 @@ -132,6 +132,9 @@ #define AUTH_REQ_KRB5 2 /* Kerberos V5 */ #define AUTH_REQ_PASSWORD 3 /* Password */ #define AUTH_REQ_CRYPT 4 /* Encrypted password */ +#ifdef USE_PAM +#define AUTH_REQ_PAM 5 /* Password, handed off to PAM */ +#endif typedef uint32 AuthRequest; Index: src/interfaces/libpq/fe-auth.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v retrieving revision 1.47 diff -u -r1.47 fe-auth.c --- src/interfaces/libpq/fe-auth.c 2001/03/22 04:01:25 1.47 +++ src/interfaces/libpq/fe-auth.c 2001/04/27 15:36:58 @@ -493,6 +493,9 @@ case AUTH_REQ_PASSWORD: case AUTH_REQ_CRYPT: +#ifdef USE_PAM + case AUTH_REQ_PAM: +#endif if (password == NULL || *password == '\0') { (void) sprintf(PQerrormsg,