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

Commit c669915

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 34d21e7 commit c669915

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
@@ -1029,15 +1029,16 @@ pg_GSS_recvauth(Port *port)
10291029
size_t kt_len = strlen(pg_krb_server_keyfile) + 14;
10301030
char *kt_path = malloc(kt_len);
10311031

1032-
if (!kt_path)
1032+
if (!kt_path ||
1033+
snprintf(kt_path, kt_len, "KRB5_KTNAME=%s",
1034+
pg_krb_server_keyfile) != kt_len - 2 ||
1035+
putenv(kt_path) != 0)
10331036
{
10341037
ereport(LOG,
10351038
(errcode(ERRCODE_OUT_OF_MEMORY),
10361039
errmsg("out of memory")));
10371040
return STATUS_ERROR;
10381041
}
1039-
snprintf(kt_path, kt_len, "KRB5_KTNAME=%s", pg_krb_server_keyfile);
1040-
putenv(kt_path);
10411042
}
10421043
}
10431044

src/backend/utils/adt/pg_locale.c

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -559,24 +559,34 @@ PGLC_localeconv(void)
559559
* pg_strftime(), which isn't locale-aware and does not need to be replaced.
560560
*/
561561
static size_t
562-
strftime_win32(char *dst, size_t dstlen, const wchar_t *format, const struct tm * tm)
562+
strftime_win32(char *dst, size_t dstlen,
563+
const char *format, const struct tm * tm)
563564
{
564565
size_t len;
566+
wchar_t wformat[8]; /* formats used below need 3 bytes */
565567
wchar_t wbuf[MAX_L10N_DATA];
566568
int encoding;
567569

568570
encoding = GetDatabaseEncoding();
569571

570-
len = wcsftime(wbuf, MAX_L10N_DATA, format, tm);
572+
/* get a wchar_t version of the format string */
573+
len = MultiByteToWideChar(CP_UTF8, 0, format, -1,
574+
wformat, lengthof(wformat));
575+
if (len == 0)
576+
elog(ERROR, "could not convert format string from UTF-8: error code %lu",
577+
GetLastError());
578+
579+
len = wcsftime(wbuf, MAX_L10N_DATA, wformat, tm);
571580
if (len == 0)
572581

573582
/*
574-
* strftime call failed - return 0 with the contents of dst
575-
* unspecified
583+
* strftime failed, possibly because the result would not fit in
584+
* MAX_L10N_DATA. Return 0 with the contents of dst unspecified.
576585
*/
577586
return 0;
578587

579-
len = WideCharToMultiByte(CP_UTF8, 0, wbuf, len, dst, dstlen, NULL, NULL);
588+
len = WideCharToMultiByte(CP_UTF8, 0, wbuf, len, dst, dstlen - 1,
589+
NULL, NULL);
580590
if (len == 0)
581591
elog(ERROR,
582592
"could not convert string to UTF-8: error code %lu", GetLastError());
@@ -599,9 +609,33 @@ strftime_win32(char *dst, size_t dstlen, const wchar_t *format, const struct tm
599609
}
600610

601611
/* redefine strftime() */
602-
#define strftime(a,b,c,d) strftime_win32(a,b,L##c,d)
612+
#define strftime(a,b,c,d) strftime_win32(a,b,c,d)
603613
#endif /* WIN32 */
604614

615+
/* Subroutine for cache_locale_time(). */
616+
static void
617+
cache_single_time(char **dst, const char *format, const struct tm * tm)
618+
{
619+
char buf[MAX_L10N_DATA];
620+
char *ptr;
621+
622+
/*
623+
* MAX_L10N_DATA is sufficient buffer space for every known locale, and
624+
* POSIX defines no strftime() errors. (Buffer space exhaustion is not an
625+
* error.) An implementation might report errors (e.g. ENOMEM) by
626+
* returning 0 (or, less plausibly, a negative value) and setting errno.
627+
* Report errno just in case the implementation did that, but clear it in
628+
* advance of the call so we don't emit a stale, unrelated errno.
629+
*/
630+
errno = 0;
631+
if (strftime(buf, MAX_L10N_DATA, format, tm) <= 0)
632+
elog(ERROR, "strftime(%s) failed: %m", format);
633+
634+
ptr = MemoryContextStrdup(TopMemoryContext, buf);
635+
if (*dst)
636+
pfree(*dst);
637+
*dst = ptr;
638+
}
605639

606640
/*
607641
* Update the lc_time localization cache variables if needed.
@@ -612,8 +646,6 @@ cache_locale_time(void)
612646
char *save_lc_time;
613647
time_t timenow;
614648
struct tm *timeinfo;
615-
char buf[MAX_L10N_DATA];
616-
char *ptr;
617649
int i;
618650

619651
#ifdef WIN32
@@ -660,35 +692,17 @@ cache_locale_time(void)
660692
for (i = 0; i < 7; i++)
661693
{
662694
timeinfo->tm_wday = i;
663-
strftime(buf, MAX_L10N_DATA, "%a", timeinfo);
664-
ptr = MemoryContextStrdup(TopMemoryContext, buf);
665-
if (localized_abbrev_days[i])
666-
pfree(localized_abbrev_days[i]);
667-
localized_abbrev_days[i] = ptr;
668-
669-
strftime(buf, MAX_L10N_DATA, "%A", timeinfo);
670-
ptr = MemoryContextStrdup(TopMemoryContext, buf);
671-
if (localized_full_days[i])
672-
pfree(localized_full_days[i]);
673-
localized_full_days[i] = ptr;
695+
cache_single_time(&localized_abbrev_days[i], "%a", timeinfo);
696+
cache_single_time(&localized_full_days[i], "%A", timeinfo);
674697
}
675698

676699
/* localized months */
677700
for (i = 0; i < 12; i++)
678701
{
679702
timeinfo->tm_mon = i;
680703
timeinfo->tm_mday = 1; /* make sure we don't have invalid date */
681-
strftime(buf, MAX_L10N_DATA, "%b", timeinfo);
682-
ptr = MemoryContextStrdup(TopMemoryContext, buf);
683-
if (localized_abbrev_months[i])
684-
pfree(localized_abbrev_months[i]);
685-
localized_abbrev_months[i] = ptr;
686-
687-
strftime(buf, MAX_L10N_DATA, "%B", timeinfo);
688-
ptr = MemoryContextStrdup(TopMemoryContext, buf);
689-
if (localized_full_months[i])
690-
pfree(localized_full_months[i]);
691-
localized_full_months[i] = ptr;
704+
cache_single_time(&localized_abbrev_months[i], "%b", timeinfo);
705+
cache_single_time(&localized_full_months[i], "%B", timeinfo);
692706
}
693707

694708
/* try to restore internal settings */

0 commit comments

Comments
 (0)