Skip to content

Commit 2cb9f2c

Browse files
committed
Check return values of sensitive system library calls.
PostgreSQL already checked the vast majority of these, missing this handful that nearly cannot fail. If putenv() failed with ENOMEM in pg_GSS_recvauth(), authentication would proceed with the wrong keytab file. If strftime() returned zero in cache_locale_time(), using the unspecified buffer contents could lead to information exposure or a crash. Back-patch to 9.0 (all supported versions). Other unchecked calls to these functions, especially those in frontend code, pose negligible security concern. This patch does not address them. Nonetheless, it is always better to check return values whose specification provides for indicating an error. In passing, fix an off-by-one error in strftime_win32()'s invocation of WideCharToMultiByte(). Upon retrieving a value of exactly MAX_L10N_DATA bytes, strftime_win32() would overrun the caller's buffer by one byte. MAX_L10N_DATA is chosen to exceed the length of every possible value, so the vulnerable scenario probably does not arise. Security: CVE-2015-3166
1 parent e58f042 commit 2cb9f2c

File tree

2 files changed

+48
-33
lines changed

2 files changed

+48
-33
lines changed

src/backend/libpq/auth.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,15 +1026,16 @@ pg_GSS_recvauth(Port *port)
10261026
size_t kt_len = strlen(pg_krb_server_keyfile) + 14;
10271027
char *kt_path = malloc(kt_len);
10281028

1029-
if (!kt_path)
1029+
if (!kt_path ||
1030+
snprintf(kt_path, kt_len, "KRB5_KTNAME=%s",
1031+
pg_krb_server_keyfile) != kt_len - 2 ||
1032+
putenv(kt_path) != 0)
10301033
{
10311034
ereport(LOG,
10321035
(errcode(ERRCODE_OUT_OF_MEMORY),
10331036
errmsg("out of memory")));
10341037
return STATUS_ERROR;
10351038
}
1036-
snprintf(kt_path, kt_len, "KRB5_KTNAME=%s", pg_krb_server_keyfile);
1037-
putenv(kt_path);
10381039
}
10391040
}
10401041

src/backend/utils/adt/pg_locale.c

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -544,24 +544,34 @@ PGLC_localeconv(void)
544544
* pg_strftime(), which isn't locale-aware and does not need to be replaced.
545545
*/
546546
static size_t
547-
strftime_win32(char *dst, size_t dstlen, const wchar_t *format, const struct tm * tm)
547+
strftime_win32(char *dst, size_t dstlen,
548+
const char *format, const struct tm * tm)
548549
{
549550
size_t len;
551+
wchar_t wformat[8]; /* formats used below need 3 bytes */
550552
wchar_t wbuf[MAX_L10N_DATA];
551553
int encoding;
552554

553555
encoding = GetDatabaseEncoding();
554556

555-
len = wcsftime(wbuf, MAX_L10N_DATA, format, tm);
557+
/* get a wchar_t version of the format string */
558+
len = MultiByteToWideChar(CP_UTF8, 0, format, -1,
559+
wformat, lengthof(wformat));
560+
if (len == 0)
561+
elog(ERROR, "could not convert format string from UTF-8: error code %lu",
562+
GetLastError());
563+
564+
len = wcsftime(wbuf, MAX_L10N_DATA, wformat, tm);
556565
if (len == 0)
557566

558567
/*
559-
* strftime call failed - return 0 with the contents of dst
560-
* unspecified
568+
* strftime failed, possibly because the result would not fit in
569+
* MAX_L10N_DATA. Return 0 with the contents of dst unspecified.
561570
*/
562571
return 0;
563572

564-
len = WideCharToMultiByte(CP_UTF8, 0, wbuf, len, dst, dstlen, NULL, NULL);
573+
len = WideCharToMultiByte(CP_UTF8, 0, wbuf, len, dst, dstlen - 1,
574+
NULL, NULL);
565575
if (len == 0)
566576
elog(ERROR,
567577
"could not convert string to UTF-8:error %lu", GetLastError());
@@ -584,9 +594,33 @@ strftime_win32(char *dst, size_t dstlen, const wchar_t *format, const struct tm
584594
}
585595

586596
/* redefine strftime() */
587-
#define strftime(a,b,c,d) strftime_win32(a,b,L##c,d)
597+
#define strftime(a,b,c,d) strftime_win32(a,b,c,d)
588598
#endif /* WIN32 */
589599

600+
/* Subroutine for cache_locale_time(). */
601+
static void
602+
cache_single_time(char **dst, const char *format, const struct tm * tm)
603+
{
604+
char buf[MAX_L10N_DATA];
605+
char *ptr;
606+
607+
/*
608+
* MAX_L10N_DATA is sufficient buffer space for every known locale, and
609+
* POSIX defines no strftime() errors. (Buffer space exhaustion is not an
610+
* error.) An implementation might report errors (e.g. ENOMEM) by
611+
* returning 0 (or, less plausibly, a negative value) and setting errno.
612+
* Report errno just in case the implementation did that, but clear it in
613+
* advance of the call so we don't emit a stale, unrelated errno.
614+
*/
615+
errno = 0;
616+
if (strftime(buf, MAX_L10N_DATA, format, tm) <= 0)
617+
elog(ERROR, "strftime(%s) failed: %m", format);
618+
619+
ptr = MemoryContextStrdup(TopMemoryContext, buf);
620+
if (*dst)
621+
pfree(*dst);
622+
*dst = ptr;
623+
}
590624

591625
/*
592626
* Update the lc_time localization cache variables if needed.
@@ -597,8 +631,6 @@ cache_locale_time(void)
597631
char *save_lc_time;
598632
time_t timenow;
599633
struct tm *timeinfo;
600-
char buf[MAX_L10N_DATA];
601-
char *ptr;
602634
int i;
603635

604636
#ifdef WIN32
@@ -645,35 +677,17 @@ cache_locale_time(void)
645677
for (i = 0; i < 7; i++)
646678
{
647679
timeinfo->tm_wday = i;
648-
strftime(buf, MAX_L10N_DATA, "%a", timeinfo);
649-
ptr = MemoryContextStrdup(TopMemoryContext, buf);
650-
if (localized_abbrev_days[i])
651-
pfree(localized_abbrev_days[i]);
652-
localized_abbrev_days[i] = ptr;
653-
654-
strftime(buf, MAX_L10N_DATA, "%A", timeinfo);
655-
ptr = MemoryContextStrdup(TopMemoryContext, buf);
656-
if (localized_full_days[i])
657-
pfree(localized_full_days[i]);
658-
localized_full_days[i] = ptr;
680+
cache_single_time(&localized_abbrev_days[i], "%a", timeinfo);
681+
cache_single_time(&localized_full_days[i], "%A", timeinfo);
659682
}
660683

661684
/* localized months */
662685
for (i = 0; i < 12; i++)
663686
{
664687
timeinfo->tm_mon = i;
665688
timeinfo->tm_mday = 1; /* make sure we don't have invalid date */
666-
strftime(buf, MAX_L10N_DATA, "%b", timeinfo);
667-
ptr = MemoryContextStrdup(TopMemoryContext, buf);
668-
if (localized_abbrev_months[i])
669-
pfree(localized_abbrev_months[i]);
670-
localized_abbrev_months[i] = ptr;
671-
672-
strftime(buf, MAX_L10N_DATA, "%B", timeinfo);
673-
ptr = MemoryContextStrdup(TopMemoryContext, buf);
674-
if (localized_full_months[i])
675-
pfree(localized_full_months[i]);
676-
localized_full_months[i] = ptr;
689+
cache_single_time(&localized_abbrev_months[i], "%b", timeinfo);
690+
cache_single_time(&localized_full_months[i], "%B", timeinfo);
677691
}
678692

679693
/* try to restore internal settings */

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