1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-21 15:54:08 +03:00

Check return value of strdup() in libpq connection option parsing.

An out-of-memory in most of these would lead to strange behavior, like
connecting to a different database than intended, but some would lead to
an outright segfault.

Alex Shulgin and me. Backpatch to all supported versions.
This commit is contained in:
Heikki Linnakangas 2014-11-25 12:55:00 +02:00
parent 906599f65f
commit abcab2a64f

View File

@ -292,7 +292,7 @@ static int connectDBStart(PGconn *conn);
static int connectDBComplete(PGconn *conn); static int connectDBComplete(PGconn *conn);
static PGPing internal_ping(PGconn *conn); static PGPing internal_ping(PGconn *conn);
static PGconn *makeEmptyPGconn(void); static PGconn *makeEmptyPGconn(void);
static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions); static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
static void freePGconn(PGconn *conn); static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn); static void closePGconn(PGconn *conn);
static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage); static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage);
@ -522,7 +522,11 @@ PQconnectStartParams(const char *const * keywords,
/* /*
* Move option values into conn structure * Move option values into conn structure
*/ */
fillPGconn(conn, connOptions); if (!fillPGconn(conn, connOptions))
{
PQconninfoFree(connOptions);
return conn;
}
/* /*
* Free the option info - all is in conn now * Free the option info - all is in conn now
@ -602,63 +606,55 @@ PQconnectStart(const char *conninfo)
return conn; return conn;
} }
static void /*
* Move option values into conn structure
*
* Don't put anything cute here --- intelligence should be in
* connectOptions2 ...
*
* Returns true on success. On failure, returns false and sets error message.
*/
#define FILL_CONN_OPTION(dst, name) \
do \
{ \
tmp = conninfo_getval(connOptions, name); \
if (tmp) \
{ \
dst = strdup(tmp); \
if (dst == NULL) \
goto oom_error; \
} \
else \
dst = NULL; \
} while(0)
static bool
fillPGconn(PGconn *conn, PQconninfoOption *connOptions) fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
{ {
const char *tmp; const char *tmp;
/* FILL_CONN_OPTION(conn->pghostaddr, "hostaddr");
* Move option values into conn structure FILL_CONN_OPTION(conn->pghost, "host");
* FILL_CONN_OPTION(conn->pgport, "port");
* Don't put anything cute here --- intelligence should be in FILL_CONN_OPTION(conn->pgtty, "tty");
* connectOptions2 ... FILL_CONN_OPTION(conn->pgoptions, "options");
* FILL_CONN_OPTION(conn->appname, "application_name");
* XXX: probably worth checking strdup() return value here... FILL_CONN_OPTION(conn->fbappname, "fallback_application_name");
*/ FILL_CONN_OPTION(conn->dbName, "dbname");
tmp = conninfo_getval(connOptions, "hostaddr"); FILL_CONN_OPTION(conn->pguser, "user");
conn->pghostaddr = tmp ? strdup(tmp) : NULL; FILL_CONN_OPTION(conn->pgpass, "password");
tmp = conninfo_getval(connOptions, "host"); FILL_CONN_OPTION(conn->connect_timeout, "connect_timeout");
conn->pghost = tmp ? strdup(tmp) : NULL; FILL_CONN_OPTION(conn->client_encoding_initial, "client_encoding");
tmp = conninfo_getval(connOptions, "port"); FILL_CONN_OPTION(conn->keepalives, "keepalives");
conn->pgport = tmp ? strdup(tmp) : NULL; FILL_CONN_OPTION(conn->keepalives_idle, "keepalives_idle");
tmp = conninfo_getval(connOptions, "tty"); FILL_CONN_OPTION(conn->keepalives_interval, "keepalives_interval");
conn->pgtty = tmp ? strdup(tmp) : NULL; FILL_CONN_OPTION(conn->keepalives_count, "keepalives_count");
tmp = conninfo_getval(connOptions, "options"); FILL_CONN_OPTION(conn->sslmode, "sslmode");
conn->pgoptions = tmp ? strdup(tmp) : NULL; FILL_CONN_OPTION(conn->sslcompression, "sslcompression");
tmp = conninfo_getval(connOptions, "application_name"); FILL_CONN_OPTION(conn->sslkey, "sslkey");
conn->appname = tmp ? strdup(tmp) : NULL; FILL_CONN_OPTION(conn->sslcert, "sslcert");
tmp = conninfo_getval(connOptions, "fallback_application_name"); FILL_CONN_OPTION(conn->sslrootcert, "sslrootcert");
conn->fbappname = tmp ? strdup(tmp) : NULL; FILL_CONN_OPTION(conn->sslcrl, "sslcrl");
tmp = conninfo_getval(connOptions, "dbname");
conn->dbName = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "user");
conn->pguser = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "password");
conn->pgpass = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "connect_timeout");
conn->connect_timeout = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "client_encoding");
conn->client_encoding_initial = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "keepalives");
conn->keepalives = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "keepalives_idle");
conn->keepalives_idle = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "keepalives_interval");
conn->keepalives_interval = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "keepalives_count");
conn->keepalives_count = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "sslmode");
conn->sslmode = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "sslcompression");
conn->sslcompression = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "sslkey");
conn->sslkey = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "sslcert");
conn->sslcert = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "sslrootcert");
conn->sslrootcert = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "sslcrl");
conn->sslcrl = tmp ? strdup(tmp) : NULL;
#ifdef USE_SSL #ifdef USE_SSL
tmp = conninfo_getval(connOptions, "requiressl"); tmp = conninfo_getval(connOptions, "requiressl");
if (tmp && tmp[0] == '1') if (tmp && tmp[0] == '1')
@ -667,20 +663,25 @@ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
if (conn->sslmode) if (conn->sslmode)
free(conn->sslmode); free(conn->sslmode);
conn->sslmode = strdup("require"); conn->sslmode = strdup("require");
if (!conn->sslmode)
return false;
} }
#endif #endif
tmp = conninfo_getval(connOptions, "requirepeer"); FILL_CONN_OPTION(conn->requirepeer, "requirepeer");
conn->requirepeer = tmp ? strdup(tmp) : NULL;
#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI) #if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
tmp = conninfo_getval(connOptions, "krbsrvname"); FILL_CONN_OPTION(conn->krbsrvname, "krbsrvname");
conn->krbsrvname = tmp ? strdup(tmp) : NULL;
#endif #endif
#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) #if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
tmp = conninfo_getval(connOptions, "gsslib"); FILL_CONN_OPTION(conn->gsslib, "gsslib");
conn->gsslib = tmp ? strdup(tmp) : NULL;
#endif #endif
tmp = conninfo_getval(connOptions, "replication"); FILL_CONN_OPTION(conn->replication, "replication");
conn->replication = tmp ? strdup(tmp) : NULL;
return true;
oom_error:
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("out of memory\n"));
return false;
} }
/* /*
@ -713,7 +714,12 @@ connectOptions1(PGconn *conn, const char *conninfo)
/* /*
* Move option values into conn structure * Move option values into conn structure
*/ */
fillPGconn(conn, connOptions); if (!fillPGconn(conn, connOptions))
{
conn->status = CONNECTION_BAD;
PQconninfoFree(connOptions);
return false;
}
/* /*
* Free the option info - all is in conn now * Free the option info - all is in conn now
@ -743,6 +749,8 @@ connectOptions2(PGconn *conn)
if (conn->dbName) if (conn->dbName)
free(conn->dbName); free(conn->dbName);
conn->dbName = strdup(conn->pguser); conn->dbName = strdup(conn->pguser);
if (!conn->dbName)
goto oom_error;
} }
/* /*
@ -755,7 +763,12 @@ connectOptions2(PGconn *conn)
conn->pgpass = PasswordFromFile(conn->pghost, conn->pgport, conn->pgpass = PasswordFromFile(conn->pghost, conn->pgport,
conn->dbName, conn->pguser); conn->dbName, conn->pguser);
if (conn->pgpass == NULL) if (conn->pgpass == NULL)
{
conn->pgpass = strdup(DefaultPassword); conn->pgpass = strdup(DefaultPassword);
if (!conn->pgpass)
goto oom_error;
}
else else
conn->dot_pgpass_used = true; conn->dot_pgpass_used = true;
} }
@ -813,7 +826,11 @@ connectOptions2(PGconn *conn)
#endif #endif
} }
else else
{
conn->sslmode = strdup(DefaultSSLMode); conn->sslmode = strdup(DefaultSSLMode);
if (!conn->sslmode)
goto oom_error;
}
/* /*
* Resolve special "auto" client_encoding from the locale * Resolve special "auto" client_encoding from the locale
@ -823,6 +840,8 @@ connectOptions2(PGconn *conn)
{ {
free(conn->client_encoding_initial); free(conn->client_encoding_initial);
conn->client_encoding_initial = strdup(pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true))); conn->client_encoding_initial = strdup(pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true)));
if (!conn->client_encoding_initial)
goto oom_error;
} }
/* /*
@ -833,6 +852,12 @@ connectOptions2(PGconn *conn)
conn->options_valid = true; conn->options_valid = true;
return true; return true;
oom_error:
conn->status = CONNECTION_BAD;
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("out of memory\n"));
return false;
} }
/* /*
@ -926,6 +951,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
if (conn->dbName) if (conn->dbName)
free(conn->dbName); free(conn->dbName);
conn->dbName = strdup(dbName); conn->dbName = strdup(dbName);
if (!conn->dbName)
goto oom_error;
} }
} }
@ -938,6 +965,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
if (conn->pghost) if (conn->pghost)
free(conn->pghost); free(conn->pghost);
conn->pghost = strdup(pghost); conn->pghost = strdup(pghost);
if (!conn->pghost)
goto oom_error;
} }
if (pgport && pgport[0] != '\0') if (pgport && pgport[0] != '\0')
@ -945,6 +974,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
if (conn->pgport) if (conn->pgport)
free(conn->pgport); free(conn->pgport);
conn->pgport = strdup(pgport); conn->pgport = strdup(pgport);
if (!conn->pgport)
goto oom_error;
} }
if (pgoptions && pgoptions[0] != '\0') if (pgoptions && pgoptions[0] != '\0')
@ -952,6 +983,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
if (conn->pgoptions) if (conn->pgoptions)
free(conn->pgoptions); free(conn->pgoptions);
conn->pgoptions = strdup(pgoptions); conn->pgoptions = strdup(pgoptions);
if (!conn->pgoptions)
goto oom_error;
} }
if (pgtty && pgtty[0] != '\0') if (pgtty && pgtty[0] != '\0')
@ -959,6 +992,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
if (conn->pgtty) if (conn->pgtty)
free(conn->pgtty); free(conn->pgtty);
conn->pgtty = strdup(pgtty); conn->pgtty = strdup(pgtty);
if (!conn->pgtty)
goto oom_error;
} }
if (login && login[0] != '\0') if (login && login[0] != '\0')
@ -966,6 +1001,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
if (conn->pguser) if (conn->pguser)
free(conn->pguser); free(conn->pguser);
conn->pguser = strdup(login); conn->pguser = strdup(login);
if (!conn->pguser)
goto oom_error;
} }
if (pwd && pwd[0] != '\0') if (pwd && pwd[0] != '\0')
@ -973,6 +1010,8 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
if (conn->pgpass) if (conn->pgpass)
free(conn->pgpass); free(conn->pgpass);
conn->pgpass = strdup(pwd); conn->pgpass = strdup(pwd);
if (!conn->pgpass)
goto oom_error;
} }
/* /*
@ -988,6 +1027,12 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
(void) connectDBComplete(conn); (void) connectDBComplete(conn);
return conn; return conn;
oom_error:
conn->status = CONNECTION_BAD;
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("out of memory\n"));
return conn;
} }
@ -3811,7 +3856,16 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
if (strcmp(options[i].keyword, optname) == 0) if (strcmp(options[i].keyword, optname) == 0)
{ {
if (options[i].val == NULL) if (options[i].val == NULL)
{
options[i].val = strdup(optval); options[i].val = strdup(optval);
if (!options[i].val)
{
printfPQExpBuffer(errorMessage,
libpq_gettext("out of memory\n"));
free(result);
return 3;
}
}
found_keyword = true; found_keyword = true;
break; break;
} }
@ -4034,6 +4088,13 @@ parseServiceFile(const char *serviceFile,
{ {
if (options[i].val == NULL) if (options[i].val == NULL)
options[i].val = strdup(val); options[i].val = strdup(val);
if (!options[i].val)
{
printfPQExpBuffer(errorMessage,
libpq_gettext("out of memory\n"));
fclose(f);
return 3;
}
found_keyword = true; found_keyword = true;
break; break;
} }
@ -4439,6 +4500,14 @@ conninfo_array_parse(const char *const * keywords, const char *const * values,
if (options[k].val) if (options[k].val)
free(options[k].val); free(options[k].val);
options[k].val = strdup(str_option->val); options[k].val = strdup(str_option->val);
if (!options[k].val)
{
printfPQExpBuffer(errorMessage,
libpq_gettext("out of memory\n"));
PQconninfoFree(options);
PQconninfoFree(dbname_options);
return NULL;
}
break; break;
} }
} }
@ -4622,20 +4691,22 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
{ {
int prefix_len; int prefix_len;
char *p; char *p;
char *buf = strdup(uri); /* need a modifiable copy of the input char *buf;
* URI */ char *start;
char *start = buf;
char prevchar = '\0'; char prevchar = '\0';
char *user = NULL; char *user = NULL;
char *host = NULL; char *host = NULL;
bool retval = false; bool retval = false;
/* need a modifiable copy of the input URI */
buf = strdup(uri);
if (buf == NULL) if (buf == NULL)
{ {
printfPQExpBuffer(errorMessage, printfPQExpBuffer(errorMessage,
libpq_gettext("out of memory\n")); libpq_gettext("out of memory\n"));
return false; return false;
} }
start = buf;
/* Skip the URI prefix */ /* Skip the URI prefix */
prefix_len = uri_prefix_length(uri); prefix_len = uri_prefix_length(uri);
@ -4977,15 +5048,17 @@ conninfo_uri_parse_params(char *params,
static char * static char *
conninfo_uri_decode(const char *str, PQExpBuffer errorMessage) conninfo_uri_decode(const char *str, PQExpBuffer errorMessage)
{ {
char *buf = malloc(strlen(str) + 1); char *buf;
char *p = buf; char *p;
const char *q = str; const char *q = str;
buf = malloc(strlen(str) + 1);
if (buf == NULL) if (buf == NULL)
{ {
printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
return NULL; return NULL;
} }
p = buf;
for (;;) for (;;)
{ {
@ -5116,7 +5189,6 @@ conninfo_storeval(PQconninfoOption *connOptions,
else else
{ {
value_copy = strdup(value); value_copy = strdup(value);
if (value_copy == NULL) if (value_copy == NULL)
{ {
printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
@ -5632,6 +5704,12 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
ret = strdup(t); ret = strdup(t);
fclose(fp); fclose(fp);
if (!ret)
{
/* Out of memory. XXX: an error message would be nice. */
return NULL;
}
/* De-escape password. */ /* De-escape password. */
for (p1 = p2 = ret; *p1 != ':' && *p1 != '\0'; ++p1, ++p2) for (p1 = p2 = ret; *p1 != ':' && *p1 != '\0'; ++p1, ++p2)
{ {