harden locale name handling and prevent slashes in LC_MESSAGES
authorRich Felker <dalias@aerifal.cx>
Thu, 31 Jul 2014 16:05:25 +0000 (12:05 -0400)
committerRich Felker <dalias@aerifal.cx>
Thu, 31 Jul 2014 16:05:25 +0000 (12:05 -0400)
the code which loads locale files was already rejecting locale names
containing slashes. however, LC_MESSAGES records a locale name even if
libc does not have a matching locale file, so that gettext or
application code can use the recorded locale name for message
translations to languages that libc does not support. this recorded
name was not being checked for slashes, meaning that such code could
potentially be tricked into directory traversal.

in addition, since the value of a locale category is sometimes used as
a pathname component by callers, the improved code rejects any value
beginning with a dot. this prevents traversal to the parent directory
via "..", use of the top-level locale directory via ".", and also
avoids "hidden" directories as a side effect.

finally, overly long locale names are now rejected (treated as an
unrecognized name and thus as an alias for C.UTF-8) rather than being
truncated.

src/locale/__setlocalecat.c

index 44385e02c6584e10a393b5ee916db513023529cc..1c894d9ca459d9f659cb59e5ac1f3229700d1668 100644 (file)
@@ -28,8 +28,6 @@ static struct __locale_map *findlocale(const char *name, size_t n)
        for (p=loc_head; p; p=p->next)
                if (!strcmp(name, p->name)) return p;
 
-       if (strchr(name, '/')) return 0;
-
        if (!libc.secure) path = getenv("MUSL_LOCPATH");
        /* FIXME: add a default path? */
        if (!path) return 0;
@@ -81,7 +79,9 @@ int __setlocalecat(locale_t loc, int cat, const char *val)
                (val = "C.UTF-8");
        }
 
-       size_t n = strnlen(val, LOCALE_NAME_MAX);
+       size_t n;
+       for (n=0; n<LOCALE_NAME_MAX && val[n] && val[n]!='/'; n++);
+       if (val[0]=='.' || val[n]) val = "C.UTF-8";
        int builtin = (val[0]=='C' && !val[1])
                || !strcmp(val, "C.UTF-8")
                || !strcmp(val, "POSIX");