glibc: use secure_getenv for loading locale archive
To quote `getenv(3)`:
> The GNU-specific secure_getenv() function is just like
> getenv() except that it returns NULL in cases where "secure
> execution" is required.
One of these cases is running in setuid mode. In this case, one could
make `sudo` load an arbitrary file as locale archive by modifying the
LOCALE_ARCHIVE env variable accordingly.
After consultation with the security team we came to the conclusion that
this is not exploitable by itself since it'd need another issue that
can be triggered by a maliciously crafted locale archive. Since this is
a valid issue nonetheless[1], I decided to fix it, but go through the
normal PR & staging lifecycle.
This patch also adds another fallback to
`/run/current-system/sw/lib/locale/locale-archive`: otherwise `sudo`
would fail to load any locale archive since that would be in
LOCALE_ARCHIVE/LOCALE_ARCHIVE_2_27 and thus setuid programs would
regress on translated output.
Thank you to Elliot Cameron for reporting this.
[1] glibc's internal environment filtering also prevents setuid
processes to use certain locale settings from the environment
such as LOCPATH.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
diff --git a/locale/loadarchive.c b/locale/loadarchive.c
|
||||
index 512769eaec..171dbb4ad9 100644
|
||||
index 452e3eb6e3..e2016a8aab 100644
|
||||
--- a/locale/loadarchive.c
|
||||
+++ b/locale/loadarchive.c
|
||||
@@ -123,6 +123,23 @@ calculate_head_size (const struct locarhead *h)
|
||||
@@ -123,6 +123,25 @@ calculate_head_size (const struct locarhead *h)
|
||||
return MAX (namehash_end, MAX (string_end, locrectab_end));
|
||||
}
|
||||
|
||||
@@ -10,13 +10,15 @@ index 512769eaec..171dbb4ad9 100644
|
||||
+open_locale_archive (void)
|
||||
+{
|
||||
+ int fd = -1;
|
||||
+ char *versioned_path = getenv ("LOCALE_ARCHIVE_2_27");
|
||||
+ char *path = getenv ("LOCALE_ARCHIVE");
|
||||
+ char *versioned_path = secure_getenv ("LOCALE_ARCHIVE_2_27");
|
||||
+ char *path = secure_getenv ("LOCALE_ARCHIVE");
|
||||
+ if (versioned_path)
|
||||
+ fd = __open_nocancel (versioned_path, O_RDONLY|O_LARGEFILE|O_CLOEXEC);
|
||||
+ if (path && fd < 0)
|
||||
+ fd = __open_nocancel (path, O_RDONLY|O_LARGEFILE|O_CLOEXEC);
|
||||
+ if (fd < 0)
|
||||
+ fd = __open_nocancel("/run/current-system/sw/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE|O_CLOEXEC);
|
||||
+ if (fd < 0)
|
||||
+ fd = __open_nocancel ("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE|O_CLOEXEC);
|
||||
+ if (fd < 0)
|
||||
+ fd = __open_nocancel (archfname, O_RDONLY|O_LARGEFILE|O_CLOEXEC);
|
||||
@@ -26,7 +28,7 @@ index 512769eaec..171dbb4ad9 100644
|
||||
|
||||
/* Find the locale *NAMEP in the locale archive, and return the
|
||||
internalized data structure for its CATEGORY data. If this locale has
|
||||
@@ -202,7 +219,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
|
||||
@@ -202,7 +221,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
|
||||
archmapped = &headmap;
|
||||
|
||||
/* The archive has never been opened. */
|
||||
@@ -35,7 +37,7 @@ index 512769eaec..171dbb4ad9 100644
|
||||
if (fd < 0)
|
||||
/* Cannot open the archive, for whatever reason. */
|
||||
return NULL;
|
||||
@@ -397,8 +414,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
|
||||
@@ -397,8 +416,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
|
||||
if (fd == -1)
|
||||
{
|
||||
struct __stat64_t64 st;
|
||||
@@ -46,10 +48,10 @@ index 512769eaec..171dbb4ad9 100644
|
||||
/* Cannot open the archive, for whatever reason. */
|
||||
return NULL;
|
||||
diff --git a/locale/programs/locale.c b/locale/programs/locale.c
|
||||
index ca0a95be99..e484783402 100644
|
||||
index c7ee1874e8..af20fbac3e 100644
|
||||
--- a/locale/programs/locale.c
|
||||
+++ b/locale/programs/locale.c
|
||||
@@ -633,6 +633,24 @@ nameentcmp (const void *a, const void *b)
|
||||
@@ -632,6 +632,26 @@ nameentcmp (const void *a, const void *b)
|
||||
}
|
||||
|
||||
|
||||
@@ -57,13 +59,15 @@ index ca0a95be99..e484783402 100644
|
||||
+open_locale_archive (void)
|
||||
+{
|
||||
+ int fd = -1;
|
||||
+ char *versioned_path = getenv ("LOCALE_ARCHIVE_2_27");
|
||||
+ char *path = getenv ("LOCALE_ARCHIVE");
|
||||
+ char *versioned_path = secure_getenv ("LOCALE_ARCHIVE_2_27");
|
||||
+ char *path = secure_getenv ("LOCALE_ARCHIVE");
|
||||
+ if (versioned_path)
|
||||
+ fd = open64 (versioned_path, O_RDONLY);
|
||||
+ if (path && fd < 0)
|
||||
+ fd = open64 (path, O_RDONLY);
|
||||
+ if (fd < 0)
|
||||
+ fd = open64 ("/run/current-system/sw/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE|O_CLOEXEC);
|
||||
+ if (fd < 0)
|
||||
+ fd = open64 ("/usr/lib/locale/locale-archive", O_RDONLY);
|
||||
+ if (fd < 0)
|
||||
+ fd = open64 (ARCHIVE_NAME, O_RDONLY);
|
||||
@@ -74,7 +78,7 @@ index ca0a95be99..e484783402 100644
|
||||
static int
|
||||
write_archive_locales (void **all_datap, char *linebuf)
|
||||
{
|
||||
@@ -645,7 +663,7 @@ write_archive_locales (void **all_datap, char *linebuf)
|
||||
@@ -644,7 +664,7 @@ write_archive_locales (void **all_datap, char *linebuf)
|
||||
int fd, ret = 0;
|
||||
uint32_t cnt;
|
||||
|
||||
@@ -84,10 +88,10 @@ index ca0a95be99..e484783402 100644
|
||||
return 0;
|
||||
|
||||
diff --git a/locale/programs/locarchive.c b/locale/programs/locarchive.c
|
||||
index f38e835c52..779a3199fc 100644
|
||||
index 8d79a1b6d1..93118c52e3 100644
|
||||
--- a/locale/programs/locarchive.c
|
||||
+++ b/locale/programs/locarchive.c
|
||||
@@ -117,6 +117,22 @@ prepare_address_space (int fd, size_t total, size_t *reserved, int *xflags,
|
||||
@@ -116,6 +116,22 @@ prepare_address_space (int fd, size_t total, size_t *reserved, int *xflags,
|
||||
}
|
||||
|
||||
|
||||
@@ -95,8 +99,8 @@ index f38e835c52..779a3199fc 100644
|
||||
+open_locale_archive (const char * archivefname, int flags)
|
||||
+{
|
||||
+ int fd = -1;
|
||||
+ char *versioned_path = getenv ("LOCALE_ARCHIVE_2_27");
|
||||
+ char *path = getenv ("LOCALE_ARCHIVE");
|
||||
+ char *versioned_path = secure_getenv ("LOCALE_ARCHIVE_2_27");
|
||||
+ char *path = secure_getenv ("LOCALE_ARCHIVE");
|
||||
+ if (versioned_path)
|
||||
+ fd = open64 (versioned_path, flags);
|
||||
+ if (path && fd < 0)
|
||||
@@ -110,7 +114,7 @@ index f38e835c52..779a3199fc 100644
|
||||
static void
|
||||
create_archive (const char *archivefname, struct locarhandle *ah)
|
||||
{
|
||||
@@ -578,7 +594,7 @@ open_archive (struct locarhandle *ah, bool readonly)
|
||||
@@ -577,7 +593,7 @@ open_archive (struct locarhandle *ah, bool readonly)
|
||||
while (1)
|
||||
{
|
||||
/* Open the archive. We must have exclusive write access. */
|
||||
|
||||
Reference in New Issue
Block a user