
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.
126 lines
4.0 KiB
Diff
126 lines
4.0 KiB
Diff
diff --git a/locale/loadarchive.c b/locale/loadarchive.c
|
|
index 452e3eb6e3..e2016a8aab 100644
|
|
--- a/locale/loadarchive.c
|
|
+++ b/locale/loadarchive.c
|
|
@@ -123,6 +123,25 @@ calculate_head_size (const struct locarhead *h)
|
|
return MAX (namehash_end, MAX (string_end, locrectab_end));
|
|
}
|
|
|
|
+static int
|
|
+open_locale_archive (void)
|
|
+{
|
|
+ int fd = -1;
|
|
+ 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);
|
|
+ return fd;
|
|
+}
|
|
+
|
|
|
|
/* Find the locale *NAMEP in the locale archive, and return the
|
|
internalized data structure for its CATEGORY data. If this locale has
|
|
@@ -202,7 +221,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
|
|
archmapped = &headmap;
|
|
|
|
/* The archive has never been opened. */
|
|
- fd = __open_nocancel (archfname, O_RDONLY|O_LARGEFILE|O_CLOEXEC);
|
|
+ fd = open_locale_archive ();
|
|
if (fd < 0)
|
|
/* Cannot open the archive, for whatever reason. */
|
|
return NULL;
|
|
@@ -397,8 +416,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
|
|
if (fd == -1)
|
|
{
|
|
struct __stat64_t64 st;
|
|
- fd = __open_nocancel (archfname,
|
|
- O_RDONLY|O_LARGEFILE|O_CLOEXEC);
|
|
+ fd = open_locale_archive();
|
|
if (fd == -1)
|
|
/* Cannot open the archive, for whatever reason. */
|
|
return NULL;
|
|
diff --git a/locale/programs/locale.c b/locale/programs/locale.c
|
|
index c7ee1874e8..af20fbac3e 100644
|
|
--- a/locale/programs/locale.c
|
|
+++ b/locale/programs/locale.c
|
|
@@ -632,6 +632,26 @@ nameentcmp (const void *a, const void *b)
|
|
}
|
|
|
|
|
|
+static int
|
|
+open_locale_archive (void)
|
|
+{
|
|
+ int fd = -1;
|
|
+ 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);
|
|
+ return fd;
|
|
+}
|
|
+
|
|
+
|
|
static int
|
|
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;
|
|
|
|
- fd = open64 (ARCHIVE_NAME, O_RDONLY);
|
|
+ fd = open_locale_archive ();
|
|
if (fd < 0)
|
|
return 0;
|
|
|
|
diff --git a/locale/programs/locarchive.c b/locale/programs/locarchive.c
|
|
index 8d79a1b6d1..93118c52e3 100644
|
|
--- a/locale/programs/locarchive.c
|
|
+++ b/locale/programs/locarchive.c
|
|
@@ -116,6 +116,22 @@ prepare_address_space (int fd, size_t total, size_t *reserved, int *xflags,
|
|
}
|
|
|
|
|
|
+static int
|
|
+open_locale_archive (const char * archivefname, int flags)
|
|
+{
|
|
+ int fd = -1;
|
|
+ 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)
|
|
+ fd = open64 (path, flags);
|
|
+ if (fd < 0)
|
|
+ fd = open64 (archivefname, flags);
|
|
+ return fd;
|
|
+}
|
|
+
|
|
+
|
|
static void
|
|
create_archive (const char *archivefname, struct locarhandle *ah)
|
|
{
|
|
@@ -577,7 +593,7 @@ open_archive (struct locarhandle *ah, bool readonly)
|
|
while (1)
|
|
{
|
|
/* Open the archive. We must have exclusive write access. */
|
|
- fd = open64 (archivefname, readonly ? O_RDONLY : O_RDWR);
|
|
+ fd = open_locale_archive (archivefname, readonly ? O_RDONLY : O_RDWR);
|
|
if (fd == -1)
|
|
{
|
|
/* Maybe the file does not yet exist? If we are opening
|