Add itoa-digits.
[kopensolaris-gnu/glibc.git] / time / tzfile.c
index d80789d..4b891b4 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 92, 93, 95, 96 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 92, 93, 95, 96, 97 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -21,6 +21,7 @@
 #include <time.h>
 #include <string.h>
 #include <limits.h>
+#include <unistd.h>
 
 #define        NOID
 #include <tzfile.h>
@@ -42,8 +43,10 @@ struct leap
     long int change;           /* Seconds of correction to apply.  */
   };
 
-static struct ttinfo *find_transition (time_t timer);
-static void compute_tzname_max (size_t);
+extern char * __tzstring (const char *); /* Defined in tzset.c.  */
+
+static struct ttinfo *find_transition (time_t timer) internal_function;
+static void compute_tzname_max (size_t) internal_function;
 
 static size_t num_transitions;
 static time_t *transitions = NULL;
@@ -79,6 +82,7 @@ decode (const void *ptr)
 void
 __tzfile_read (const char *file)
 {
+  static const char default_tzdir[] = TZDIR;
   size_t num_isstd, num_isgmt;
   register FILE *f;
   struct tzhead tzhead;
@@ -108,21 +112,47 @@ __tzfile_read (const char *file)
     /* No user specification; use the site-wide default.  */
     file = TZDEFAULT;
   else if (*file == '\0')
-    /* User specified the empty string; use UTC explicitly.  */
-    file = "Universal";
+    /* User specified the empty string; use UTC with no leap seconds.  */
+    return;
+  else
+    {
+      /* We must not allow to read an arbitrary file in a setuid
+        program.  So we fail for any file which is not in the
+        directory hierachy starting at TZDIR
+        and which is not the system wide default TZDEFAULT.  */
+      if (__libc_enable_secure
+         && ((*file == '/'
+              && memcmp (file, TZDEFAULT, sizeof TZDEFAULT)
+              && memcmp (file, default_tzdir, sizeof (default_tzdir) - 1))
+             || strstr (file, "../") != NULL))
+       /* This test is certainly a bit too restrictive but it should
+          catch all critical cases.  */
+       return;
+    }
 
   if (*file != '/')
     {
-      static const char tzdir[] = TZDIR;
-      register const unsigned int len = strlen (file) + 1;
-      char *new = (char *) __alloca (sizeof (tzdir) + len);
-      memcpy (new, tzdir, sizeof(tzdir) - 1);
-      new[sizeof (tzdir) - 1] = '/';
-      memcpy (&new[sizeof (tzdir)], file, len);
+      const char *tzdir;
+      unsigned int len, tzdir_len;
+      char *new;
+
+      tzdir = __secure_getenv ("TZDIR");
+      if (tzdir == NULL || *tzdir == '\0')
+       {
+         tzdir = default_tzdir;
+         tzdir_len = sizeof (default_tzdir) - 1;
+       }
+      else
+       tzdir_len = strlen (tzdir);
+      len = strlen (file) + 1;
+      new = (char *) __alloca (tzdir_len + 1 + len);
+      memcpy (new, tzdir, tzdir_len);
+      new[tzdir_len] = '/';
+      memcpy (&new[tzdir_len + 1], file, len);
       file = new;
     }
 
-  f = fopen(file, "r");
+  f = fopen (file, "r");
   if (f == NULL)
     return;
 
@@ -171,6 +201,12 @@ __tzfile_read (const char *file)
       fread(type_idxs, 1, num_transitions, f) != num_transitions)
     goto lose;
 
+  /* Check for bogus indices in the data file, so we can hereafter
+     safely use type_idxs[T] as indices into `types' and never crash.  */
+  for (i = 0; i < num_transitions; ++i)
+    if (type_idxs[i] >= num_types)
+      goto lose;
+
   if (BYTE_ORDER != BIG_ENDIAN || sizeof (time_t) != 4)
     {
       /* Decode the transition times, stored as 4-byte integers in
@@ -189,6 +225,8 @@ __tzfile_read (const char *file)
          fread (&types[i].isdst, 1, 1, f) != 1 ||
          fread (&types[i].idx, 1, 1, f) != 1)
        goto lose;
+      if (types[i].idx >= chars) /* Bogus index in data file.  */
+       goto lose;
       types[i].offset = (long int) decode (x);
     }
 
@@ -208,7 +246,7 @@ __tzfile_read (const char *file)
 
   for (i = 0; i < num_isstd; ++i)
     {
-      char c = getc (f);
+      int c = getc (f);
       if (c == EOF)
        goto lose;
       types[i].isstd = c != 0;
@@ -218,7 +256,7 @@ __tzfile_read (const char *file)
 
   for (i = 0; i < num_isgmt; ++i)
     {
-      char c = getc (f);
+      int c = getc (f);
       if (c == EOF)
        goto lose;
       types[i].isgmt = c != 0;
@@ -231,9 +269,9 @@ __tzfile_read (const char *file)
   info = find_transition (0);
   for (i = 0; i < num_types && i < sizeof (__tzname) / sizeof (__tzname[0]);
        ++i)
-    __tzname[types[i].isdst] = &zone_names[types[i].idx];
+    __tzname[types[i].isdst] = __tzstring (&zone_names[types[i].idx]);
   if (info->isdst < sizeof (__tzname) / sizeof (__tzname[0]))
-    __tzname[info->isdst] = &zone_names[info->idx];
+    __tzname[info->isdst] = __tzstring (&zone_names[info->idx]);
 
   compute_tzname_max (chars);
 
@@ -249,7 +287,8 @@ __tzfile_read (const char *file)
    from the TZDEFRULES file.  */
 
 void
-__tzfile_default (char *std, char *dst, long int stdoff, long int dstoff)
+__tzfile_default (const char *std, const char *dst,
+                 long int stdoff, long int dstoff)
 {
   size_t stdlen, dstlen, i;
   long int rule_offset, rule_stdoff, rule_dstoff;
@@ -335,10 +374,15 @@ __tzfile_default (char *std, char *dst, long int stdoff, long int dstoff)
   types[1].offset = dstoff;
   types[1].isdst = 1;
 
+  /* Reset the zone names to point to the user's names.  */
+  __tzname[0] = (char *) std;
+  __tzname[1] = (char *) dst;
+
   compute_tzname_max (stdlen + dstlen);
 }
 \f
 static struct ttinfo *
+internal_function
 find_transition (time_t timer)
 {
   size_t i;
@@ -368,19 +412,23 @@ find_transition (time_t timer)
 }
 \f
 int
-__tzfile_compute (time_t timer, long int *leap_correct, int *leap_hit)
+__tzfile_compute (time_t timer, int use_localtime,
+                 long int *leap_correct, int *leap_hit)
 {
-  struct ttinfo *info;
   register size_t i;
 
-  info = find_transition (timer);
-  __daylight = info->isdst;
-  __timezone = info->offset;
-  for (i = 0; i < num_types && i < sizeof (__tzname) / sizeof (__tzname[0]);
-       ++i)
-    __tzname[types[i].isdst] = &zone_names[types[i].idx];
-  if (info->isdst < sizeof (__tzname) / sizeof (__tzname[0]))
-    __tzname[info->isdst] = &zone_names[info->idx];
+  if (use_localtime)
+    {
+      struct ttinfo *info = find_transition (timer);
+      __daylight = info->isdst;
+      __timezone = info->offset;
+      for (i = 0;
+          i < num_types && i < sizeof (__tzname) / sizeof (__tzname[0]);
+          ++i)
+       __tzname[types[i].isdst] = &zone_names[types[i].idx];
+      if (info->isdst < sizeof (__tzname) / sizeof (__tzname[0]))
+       __tzname[info->isdst] = &zone_names[info->idx];
+    }
 
   *leap_correct = 0L;
   *leap_hit = 0;
@@ -412,10 +460,11 @@ __tzfile_compute (time_t timer, long int *leap_correct, int *leap_hit)
   return 1;
 }
 \f
-void
+static void
+internal_function
 compute_tzname_max (size_t chars)
 {
-  extern size_t __tzname_cur_max; /* Defined in __tzset.c. */
+  extern size_t __tzname_cur_max; /* Defined in tzset.c. */
 
   const char *p;
 
@@ -425,7 +474,7 @@ compute_tzname_max (size_t chars)
       const char *start = p;
       while (*p != '\0')
        ++p;
-      if (p - start > __tzname_cur_max)
+      if ((size_t) (p - start) > __tzname_cur_max)
        __tzname_cur_max = p - start;
     } while (++p < &zone_names[chars]);
 }