timezone: Correctly resolve symlink from /etc/localtime

The return value of g_file_read_link ("/etc/localtime") can
be a relative path in the form of "../usr/share/zoneinfo".
This breaks the prefix check that is performed, and makes
the timezone identifier be "../usr/share/zoneinfo/America/Sao_Paulo",
for example, which breaks other parts of the system.

Fix that by canonicalizing the symlink path if we detect
is it a relative path.

(Tweaked by Philip Withnall <withnall@endlessm.com> to remove a
conditional which was unnecessary.)

https://bugzilla.gnome.org/show_bug.cgi?id=111848
This commit is contained in:
Georges Basile Stavracas Neto 2018-04-27 13:02:21 -03:00 committed by Philip Withnall
parent 0f37af7e9b
commit cb32614382

View File

@ -433,6 +433,7 @@ zone_info_unix (const gchar *identifier,
else else
{ {
gsize prefix_len = 0; gsize prefix_len = 0;
gchar *canonical_path = NULL;
filename = g_strdup ("/etc/localtime"); filename = g_strdup ("/etc/localtime");
@ -446,6 +447,11 @@ zone_info_unix (const gchar *identifier,
return NULL; return NULL;
} }
/* Resolve relative path */
canonical_path = g_canonicalize_filename (resolved_identifier, "/etc");
g_free (resolved_identifier);
resolved_identifier = g_steal_pointer (&canonical_path);
/* Strip the prefix and slashes if possible. */ /* Strip the prefix and slashes if possible. */
if (g_str_has_prefix (resolved_identifier, tzdir)) if (g_str_has_prefix (resolved_identifier, tzdir))
{ {
@ -457,6 +463,8 @@ zone_info_unix (const gchar *identifier,
if (prefix_len > 0) if (prefix_len > 0)
memmove (resolved_identifier, resolved_identifier + prefix_len, memmove (resolved_identifier, resolved_identifier + prefix_len,
strlen (resolved_identifier) - prefix_len + 1 /* nul terminator */); strlen (resolved_identifier) - prefix_len + 1 /* nul terminator */);
g_free (canonical_path);
} }
file = g_mapped_file_new (filename, FALSE, NULL); file = g_mapped_file_new (filename, FALSE, NULL);