Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 76c09db

Browse files
committed
Rewrite pam_passwd_conv_proc to be more robust: avoid assuming that the
pam_message array contains exactly one PAM_PROMPT_ECHO_OFF message. Instead, deal with however many messages there are, and don't throw error for PAM_ERROR_MSG and PAM_TEXT_INFO messages. This logic is borrowed from openssh 5.2p1, which hopefully has seen more real-world PAM usage than we have. Per bug #5121 from Ryan Douglas, which turned out to be caused by the conv_proc being called with zero messages. Apparently that is normal behavior given the combination of Linux pam_krb5 with MS Active Directory as the domain controller. Patch all the way back, since this code has been essentially untouched since 7.4. (Surprising we've not heard complaints before.)
1 parent c02350d commit 76c09db

File tree

1 file changed

+80
-48
lines changed

1 file changed

+80
-48
lines changed

src/backend/libpq/auth.c

Lines changed: 80 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.186 2009/10/14 22:09:46 heikki Exp $
11+
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.187 2009/10/16 22:08:36 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -452,7 +452,6 @@ ClientAuthentication(Port *port)
452452

453453
case uaPAM:
454454
#ifdef USE_PAM
455-
pam_port_cludge = port;
456455
status = CheckPAMAuth(port, port->user_name, "");
457456
#else
458457
Assert(false);
@@ -1888,72 +1887,103 @@ static int
18881887
pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
18891888
struct pam_response ** resp, void *appdata_ptr)
18901889
{
1891-
if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
1892-
{
1893-
switch (msg[0]->msg_style)
1894-
{
1895-
case PAM_ERROR_MSG:
1896-
ereport(LOG,
1897-
(errmsg("error from underlying PAM layer: %s",
1898-
msg[0]->msg)));
1899-
return PAM_CONV_ERR;
1900-
default:
1901-
ereport(LOG,
1902-
(errmsg("unsupported PAM conversation %d/%s",
1903-
msg[0]->msg_style, msg[0]->msg)));
1904-
return PAM_CONV_ERR;
1905-
}
1906-
}
1890+
char *passwd;
1891+
struct pam_response *reply;
1892+
int i;
19071893

1908-
if (!appdata_ptr)
1894+
if (appdata_ptr)
1895+
passwd = (char *) appdata_ptr;
1896+
else
19091897
{
19101898
/*
19111899
* Workaround for Solaris 2.6 where the PAM library is broken and does
19121900
* not pass appdata_ptr to the conversation routine
19131901
*/
1914-
appdata_ptr = pam_passwd;
1902+
passwd = pam_passwd;
19151903
}
19161904

1917-
/*
1918-
* Password wasn't passed to PAM the first time around - let's go ask the
1919-
* client to send a password, which we then stuff into PAM.
1920-
*/
1921-
if (strlen(appdata_ptr) == 0)
1922-
{
1923-
char *passwd;
1924-
1925-
sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
1926-
passwd = recv_password_packet(pam_port_cludge);
1927-
1928-
if (passwd == NULL)
1929-
return PAM_CONV_ERR; /* client didn't want to send password */
1905+
*resp = NULL; /* in case of error exit */
19301906

1931-
if (strlen(passwd) == 0)
1932-
{
1933-
ereport(LOG,
1934-
(errmsg("empty password returned by client")));
1935-
return PAM_CONV_ERR;
1936-
}
1937-
appdata_ptr = passwd;
1938-
}
1907+
if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG)
1908+
return PAM_CONV_ERR;
19391909

19401910
/*
19411911
* Explicitly not using palloc here - PAM will free this memory in
19421912
* pam_end()
19431913
*/
1944-
*resp = calloc(num_msg, sizeof(struct pam_response));
1945-
if (!*resp)
1914+
if ((reply = calloc(num_msg, sizeof(struct pam_response))) == NULL)
19461915
{
19471916
ereport(LOG,
19481917
(errcode(ERRCODE_OUT_OF_MEMORY),
19491918
errmsg("out of memory")));
19501919
return PAM_CONV_ERR;
19511920
}
19521921

1953-
(*resp)[0].resp = strdup((char *) appdata_ptr);
1954-
(*resp)[0].resp_retcode = 0;
1922+
for (i = 0; i < num_msg; i++)
1923+
{
1924+
switch (msg[i]->msg_style)
1925+
{
1926+
case PAM_PROMPT_ECHO_OFF:
1927+
if (strlen(passwd) == 0)
1928+
{
1929+
/*
1930+
* Password wasn't passed to PAM the first time around -
1931+
* let's go ask the client to send a password, which we
1932+
* then stuff into PAM.
1933+
*/
1934+
sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
1935+
passwd = recv_password_packet(pam_port_cludge);
1936+
if (passwd == NULL)
1937+
{
1938+
/*
1939+
* Client didn't want to send password. We
1940+
* intentionally do not log anything about this.
1941+
*/
1942+
goto fail;
1943+
}
1944+
if (strlen(passwd) == 0)
1945+
{
1946+
ereport(LOG,
1947+
(errmsg("empty password returned by client")));
1948+
goto fail;
1949+
}
1950+
}
1951+
if ((reply[i].resp = strdup(passwd)) == NULL)
1952+
goto fail;
1953+
reply[i].resp_retcode = PAM_SUCCESS;
1954+
break;
1955+
case PAM_ERROR_MSG:
1956+
ereport(LOG,
1957+
(errmsg("error from underlying PAM layer: %s",
1958+
msg[i]->msg)));
1959+
/* FALL THROUGH */
1960+
case PAM_TEXT_INFO:
1961+
/* we don't bother to log TEXT_INFO messages */
1962+
if ((reply[i].resp = strdup("")) == NULL)
1963+
goto fail;
1964+
reply[i].resp_retcode = PAM_SUCCESS;
1965+
break;
1966+
default:
1967+
elog(LOG, "unsupported PAM conversation %d/\"%s\"",
1968+
msg[i]->msg_style,
1969+
msg[i]->msg ? msg[i]->msg : "(none)");
1970+
goto fail;
1971+
}
1972+
}
1973+
1974+
*resp = reply;
1975+
return PAM_SUCCESS;
1976+
1977+
fail:
1978+
/* free up whatever we allocated */
1979+
for (i = 0; i < num_msg; i++)
1980+
{
1981+
if (reply[i].resp != NULL)
1982+
free(reply[i].resp);
1983+
}
1984+
free(reply);
19551985

1956-
return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
1986+
return PAM_CONV_ERR;
19571987
}
19581988

19591989

@@ -1967,10 +1997,12 @@ CheckPAMAuth(Port *port, char *user, char *password)
19671997
pam_handle_t *pamh = NULL;
19681998

19691999
/*
1970-
* Apparently, Solaris 2.6 is broken, and needs ugly static variable
1971-
* workaround
2000+
* We can't entirely rely on PAM to pass through appdata --- it appears
2001+
* not to work on at least Solaris 2.6. So use these ugly static
2002+
* variables instead.
19722003
*/
19732004
pam_passwd = password;
2005+
pam_port_cludge = port;
19742006

19752007
/*
19762008
* Set the application data portion of the conversation struct This is

0 commit comments

Comments
 (0)