Index: configure.in =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/configure.in,v retrieving revision 1.134 diff -u -r1.134 configure.in --- configure.in 2001/08/26 22:28:04 1.134 +++ configure.in 2001/08/31 20:21:53 @@ -417,7 +417,6 @@ AC_MSG_RESULT([$with_perl]) AC_SUBST(with_perl) - # # Optionally build Python interface module # @@ -514,6 +513,23 @@ [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]) + +], +[AC_MSG_RESULT(no)]) + +AC_SUBST(with_pam) + # # OpenSSL @@ -737,11 +753,14 @@ 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 + if test "$enable_nls" = yes ; then PGAC_CHECK_GETTEXT fi - ## ## Header files ## @@ -777,6 +796,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.17 diff -u -r1.17 client-auth.sgml --- doc/src/sgml/client-auth.sgml 2001/08/16 16:24:15 1.17 +++ doc/src/sgml/client-auth.sgml 2001/08/31 20:21:54 @@ -278,6 +278,27 @@ + + + pam + + + This authentication type operates similar to + password, with the main difference that + it will use PAM (Pluggable Authentication Modules) as the + authentication mechanism. The authentication + option following the pam keyword + specifies the service name that will be passed to PAM. The + default service name is postgresql. + For more information about PAM, please read Linux-PAM + Page and Solaris-PAM + Page. + + + + Index: src/backend/libpq/auth.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/auth.c,v retrieving revision 1.64 diff -u -r1.64 auth.c --- src/backend/libpq/auth.c 2001/08/21 15:21:25 1.64 +++ src/backend/libpq/auth.c 2001/08/31 20:21:54 @@ -43,6 +43,24 @@ char *pg_krb_server_keyfile; +#ifdef USE_PAM +#include + +#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 @@ case uaPassword: authmethod = "Password"; break; +#ifdef USE_PAM + case uaPAM: + authmethod = "PAM"; + break; +#endif /* USE_PAM */ } elog(FATAL, "%s authentication failed for user \"%s\"", @@ -524,16 +547,22 @@ sendAuthRequest(port, AUTH_REQ_MD5); 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 @@ 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 @@ case uaMD5: case uaCrypt: case uaReject: +#ifdef USE_PAM + case uaPAM: +#endif /* USE_PAM */ 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.68 diff -u -r1.68 hba.c --- src/backend/libpq/hba.c 2001/08/21 15:49:17 1.68 +++ src/backend/libpq/hba.c 2001/08/31 20:21:54 @@ -235,6 +235,10 @@ *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 @@ line_number = lfirsti(line); line = lnext(line); Assert(line != NIL); - /* Check the record type. */ token = lfirst(line); if (strcmp(token, "local") == 0) 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.26 diff -u -r1.26 pg_hba.conf.sample --- src/backend/libpq/pg_hba.conf.sample 2001/08/21 00:33:27 1.26 +++ src/backend/libpq/pg_hba.conf.sample 2001/08/31 20:21:54 @@ -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 Index: src/include/pg_config.h.in =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/pg_config.h.in,v retrieving revision 1.1 diff -u -r1.1 pg_config.h.in --- src/include/pg_config.h.in 2001/08/24 14:07:49 1.1 +++ src/include/pg_config.h.in 2001/08/31 20:21:54 @@ -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.25 diff -u -r1.25 hba.h --- src/include/libpq/hba.h 2001/08/24 16:59:10 1.25 +++ src/include/libpq/hba.h 2001/08/31 20:21:54 @@ -31,6 +31,9 @@ typedef enum UserAuth { +#ifdef USE_PAM + uaPAM, +#endif /* USE_PAM */ uaReject, uaKrb4, uaKrb5,