Allow TZ to contain absolutes paths starting with /usr/share/zoneinfo/
authormillert <millert@openbsd.org>
Mon, 3 Oct 2022 15:34:39 +0000 (15:34 +0000)
committermillert <millert@openbsd.org>
Mon, 3 Oct 2022 15:34:39 +0000 (15:34 +0000)
Other absolutes paths are still rejected.

lib/libc/time/localtime.c

index 6f1c207..2e16411 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: localtime.c,v 1.64 2022/09/23 17:29:22 millert Exp $ */
+/*     $OpenBSD: localtime.c,v 1.65 2022/10/03 15:34:39 millert Exp $ */
 /*
 ** This file is in the public domain, so clarified as of
 ** 1996-06-05 by Arthur David Olson.
@@ -296,27 +296,25 @@ differ_by_repeat(time_t t1, time_t t0)
 }
 
 static int
-tzload(const char *name, struct state *sp, int doextend)
+tzpath_ok(const char *name)
 {
-       const char *            p;
-       int                     i;
-       int                     fid;
-       int                     stored;
-       int                     nread;
-       typedef union {
-               struct tzhead   tzhead;
-               char            buf[2 * sizeof(struct tzhead) +
-                                   2 * sizeof *sp +
-                                   4 * TZ_MAX_TIMES];
-       } u_t;
-       u_t *                   up;
-       char                    fullname[PATH_MAX];
+       /* Reject absolute paths that don't start with TZDIR.  */
+       if (name[0] == '/' && (strncmp(name, TZDIR, sizeof(TZDIR) - 1) != 0 ||
+           name[sizeof(TZDIR) - 1] != '/'))
+               return 0;
 
-       up = calloc(1, sizeof *up);
-       if (up == NULL)
-               return -1;
+       /* Reject paths that contain "../". */
+       if (strstr(name, "../") != NULL)
+               return 0;
 
-       sp->goback = sp->goahead = FALSE;
+       return 1;
+}
+
+static int
+open_tzfile(const char *name)
+{
+       char fullname[PATH_MAX];
+       int i;
 
        if (name != NULL) {
                /*
@@ -325,22 +323,53 @@ tzload(const char *name, struct state *sp, int doextend)
                 */
                if (name[0] == ':')
                        name++;
-               /* Ignore absolute paths or names that contain "../". */
-               if (name[0] == '/' || strstr(name, "../") != NULL)
+
+               /*
+                * Ignore absolute paths that don't start with TZDIR
+                * or that contain "../".
+                */
+               if (!tzpath_ok(name))
                        name = NULL;
        }
+
        if (name == NULL) {
                name = TZDEFAULT;
-       } else {
+       } else if (name[0] != '/') {
                /* Time zone data path is relative to TZDIR. */
                i = snprintf(fullname, sizeof(fullname), "%s/%s", TZDIR, name);
                if (i < 0 || i >= sizeof(fullname)) {
                        errno = ENAMETOOLONG;
-                       goto oops;
+                       return -1;
                }
                name = fullname;
        }
-       if ((fid = open(name, O_RDONLY)) == -1) {
+
+       return open(name, O_RDONLY);
+}
+
+static int
+tzload(const char *name, struct state *sp, int doextend)
+{
+       const char *            p;
+       int                     i;
+       int                     fid;
+       int                     stored;
+       int                     nread;
+       typedef union {
+               struct tzhead   tzhead;
+               char            buf[2 * sizeof(struct tzhead) +
+                                   2 * sizeof *sp +
+                                   4 * TZ_MAX_TIMES];
+       } u_t;
+       u_t *                   up;
+
+       up = calloc(1, sizeof *up);
+       if (up == NULL)
+               return -1;
+
+       sp->goback = sp->goahead = FALSE;
+
+       if ((fid = open_tzfile(name)) == -1) {
                /* Could be a POSIX section 8-style TZ string. */
                goto oops;
        }