diff options
Diffstat (limited to 'src/backend/libpq/auth.c')
-rw-r--r-- | src/backend/libpq/auth.c | 233 |
1 files changed, 224 insertions, 9 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; |