From 2f4c74906d9e0d3c32a3a4e1db38f20a2c1501ad Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Fri, 16 May 2025 15:06:37 +0200 Subject: [PATCH] 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. --- .../libraries/glibc/nix-locale-archive.patch | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/pkgs/development/libraries/glibc/nix-locale-archive.patch b/pkgs/development/libraries/glibc/nix-locale-archive.patch index 9519b5f5fdf5..15d679d3cf2e 100644 --- a/pkgs/development/libraries/glibc/nix-locale-archive.patch +++ b/pkgs/development/libraries/glibc/nix-locale-archive.patch @@ -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. */