I found that PAM authentication in 7.2.3 doesn't appear to work properly. It appeared to me that the server wasn't waiting for the PAM conversation to complete before it rejected the request, so I did a little rewrite on the auth.c file to sort of force things to prompt for a password and then shove it into PAM.
The one thing I see still is that it appears that you still have to create user accounts in the database for things to work. This is kind of disappointing as I'd like to not have to repeat creating users in PAM if I've already got them defined, say in an LDAP database somewhere, but at least I have PAM authentication working with passwords. Attached is the patch if the developers want to look at it, clean it up, and stick it into the next version. -jth
*** src/backend/libpq/auth.c Mon Feb 25 15:07:33 2002 --- auth.c Mon Oct 28 20:34:06 2002 *************** *** 44,65 **** 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 --- 44,58 ---- char *pg_krb_server_keyfile; #ifdef USE_PAM ! #include <security/pam_appl.h> ! ! /* Constants */ ! #define PGSQL_PAM_SERVICE "postgresql" /* Service name passed to PAM */ ! ! /* PAM functions */ ! static int doPAMAuth(Port *port, char *user, char *password); ! static int doPAMConversation(int num_msg, const struct pam_message **msg, ! struct pam_response **resp, void *appdata_ptr); #endif /* USE_PAM */ #ifdef KRB4 *************** *** 583,590 **** #ifdef USE_PAM case uaPAM: ! pam_port_cludge = port; ! status = CheckPAMAuth(port, port->user, ""); break; #endif /* USE_PAM */ --- 576,583 ---- #ifdef USE_PAM case uaPAM: ! sendAuthRequest(port, AUTH_REQ_PASSWORD); ! status = recv_and_check_password_packet(port); break; #endif /* USE_PAM */ *************** *** 625,823 **** #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 > 5) ! 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; ! } ! ! retval = pam_set_item(pamh, PAM_USER, user); ! ! if (retval != PAM_SUCCESS) ! { ! 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; ! } ! ! retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv); ! ! if (retval != PAM_SUCCESS) ! { ! 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; ! } ! ! retval = pam_authenticate(pamh, 0); ! ! if (retval != PAM_SUCCESS) ! { ! 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; ! } ! ! retval = pam_acct_mgmt(pamh, 0); ! ! if (retval != PAM_SUCCESS) ! { ! 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; ! } ! ! 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); } #endif /* USE_PAM */ --- 618,729 ---- #ifdef USE_PAM /* ! * PAM conversation function - Not really doing any conversation here just ! * shoving the password into PAM */ ! static int doPAMConversation(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) ! { ! switch (msg[0]->msg_style) ! { ! case PAM_ERROR_MSG: ! snprintf(PQerrormsg, PQERRORMSG_LENGTH, ! "doPAMConversation: 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, ! "doPAMConversation: Unexpected PAM conversation %d/'%s'\n", ! msg[0]->msg_style, msg[0]->msg); ! 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, ! "doPAMConversation: 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 doPAMAuth(Port *port, char *user, char *password) { ! int retval; ! pam_handle_t *pamh = NULL; ! struct pam_conv pam_conversation = { &doPAMConversation, password }; ! ! /* Start PAM */ ! if (port->auth_arg[0] == '\0') ! retval = pam_start(PGSQL_PAM_SERVICE, user, &pam_conversation, &pamh); ! else ! retval = pam_start(port->auth_arg, user, &pam_conversation, &pamh); ! if (retval != PAM_SUCCESS) ! { ! snprintf(PQerrormsg, PQERRORMSG_LENGTH, ! "doPAMAuth: Failed to create PAM authenticator: '%s'\n", ! pam_strerror(pamh, retval)); ! fputs(PQerrormsg, stderr); ! pqdebug("%s", PQerrormsg); ! return STATUS_ERROR; ! } ! ! /* Do the authentication */ ! retval = pam_authenticate(pamh, 0); ! if (retval != PAM_SUCCESS) ! { ! snprintf(PQerrormsg, PQERRORMSG_LENGTH, ! "doPAMAuth: pam_authenticate failed: '%s'\n", ! pam_strerror(pamh, retval)); ! fputs(PQerrormsg, stderr); ! pqdebug("%s", PQerrormsg); ! return STATUS_ERROR; ! } ! ! /* Do account management */ ! retval = pam_acct_mgmt(pamh, 0); ! if (retval != PAM_SUCCESS) ! { ! snprintf(PQerrormsg, PQERRORMSG_LENGTH, ! "doPAMAuth: pam_acct_mgmt failed: '%s'\n", ! pam_strerror(pamh, retval)); ! fputs(PQerrormsg, stderr); ! pqdebug("%s", PQerrormsg); ! return STATUS_ERROR; ! } ! ! /* Cleanup */ ! retval = pam_end(pamh, retval); ! if (retval != PAM_SUCCESS) ! { ! snprintf(PQerrormsg, PQERRORMSG_LENGTH, ! "doPAMAuth: Failed to release PAM authenticator: '%s'\n", ! pam_strerror(pamh, retval)); ! fputs(PQerrormsg, stderr); ! pqdebug("%s", PQerrormsg); ! } ! return (retval == PAM_SUCCESS ? STATUS_OK : STATUS_ERROR); } #endif /* USE_PAM */ *************** *** 846,852 **** --- 752,762 ---- fprintf(stderr, "received password packet with len=%d, pw=%s\n", len, buf.data); + #ifndef USE_PAM result = checkPassword(port, port->user, buf.data); + #else + result = doPAMAuth(port, port->user, buf.data); + #endif pfree(buf.data); return result; }
---------------------------(end of broadcast)--------------------------- TIP 4: Don't 'kill -9' the postmaster