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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy