openjade (1.4devel1-20.1) intl/dcgettext.c

Summary

 intl/dcgettext.c |  273 ++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 203 insertions(+), 70 deletions(-)

    
download this patch

Patch contents

--- openjade-1.4devel1.orig/intl/dcgettext.c
+++ openjade-1.4devel1/intl/dcgettext.c
@@ -1,19 +1,19 @@
-/* dcgettext.c -- implemenatation of the dcgettext(3) function
-   Copyright (C) 1995 Free Software Foundation, Inc.
+/* Implementation of the dcgettext(3) function.
+   Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
 
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #ifdef HAVE_CONFIG_H
 # include <config.h>
@@ -21,10 +21,11 @@
 
 #include <sys/types.h>
 
-#ifdef __GNUC__
+#if defined __GNUC__ && !defined C_ALLOCA
 # define alloca __builtin_alloca
+# define HAVE_ALLOCA 1
 #else
-# if defined HAVE_ALLOCA_H || defined _LIBC
+# if (defined HAVE_ALLOCA_H || defined _LIBC) && !defined C_ALLOCA
 #  include <alloca.h>
 # else
 #  ifdef _AIX
@@ -41,6 +42,9 @@
 #ifndef errno
 extern int errno;
 #endif
+#ifndef __set_errno
+# define __set_errno(val) errno = (val)
+#endif
 
 #if defined STDC_HEADERS || defined _LIBC
 # include <stdlib.h>
@@ -54,6 +58,9 @@
 #endif
 
 #if defined HAVE_STRING_H || defined _LIBC
+# ifndef _GNU_SOURCE
+#  define _GNU_SOURCE	1
+# endif
 # include <string.h>
 #else
 # include <strings.h>
@@ -84,14 +91,19 @@
    because some ANSI C functions will require linking with this object
    file and the name space must not be polluted.  */
 # define getcwd __getcwd
-# define stpcpy __stpcpy
-#endif
-
-#if !defined HAVE_GETCWD && !defined _LIBC
-char *getwd ();
-# define getcwd(buf, max) getwd (buf)
+# ifndef stpcpy
+#  define stpcpy __stpcpy
+# endif
 #else
+# if !defined HAVE_GETCWD
+char *getwd ();
+#  define getcwd(buf, max) getwd (buf)
+# else
 char *getcwd ();
+# endif
+# ifndef HAVE_STPCPY
+static char *stpcpy PARAMS ((char *dest, const char *src));
+# endif
 #endif
 
 /* Amount to increase buffer size by in each try.  */
@@ -151,10 +163,48 @@
 struct binding *_nl_domain_bindings;
 
 /* Prototypes for local functions.  */
-static char *find_msg __P ((struct loaded_domain *domain, const char *msgid));
-static const char *category_to_name __P((int category));
-static const char *guess_category_value __P((int category,
-					     const char *categoryname));
+static char *find_msg PARAMS ((struct loaded_l10nfile *domain_file,
+			       const char *msgid)) internal_function;
+static const char *category_to_name PARAMS ((int category)) internal_function;
+static const char *guess_category_value PARAMS ((int category,
+						 const char *categoryname))
+     internal_function;
+
+
+/* For those loosing systems which don't have `alloca' we have to add
+   some additional code emulating it.  */
+#ifdef HAVE_ALLOCA
+/* Nothing has to be done.  */
+# define ADD_BLOCK(list, address) /* nothing */
+# define FREE_BLOCKS(list) /* nothing */
+#else
+struct block_list
+{
+  void *address;
+  struct block_list *next;
+};
+# define ADD_BLOCK(list, addr)						      \
+  do {									      \
+    struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
+    /* If we cannot get a free block we cannot add the new element to	      \
+       the list.  */							      \
+    if (newp != NULL) {							      \
+      newp->address = (addr);						      \
+      newp->next = (list);						      \
+      (list) = newp;							      \
+    }									      \
+  } while (0)
+# define FREE_BLOCKS(list)						      \
+  do {									      \
+    while (list != NULL) {						      \
+      struct block_list *old = list;					      \
+      list = list->next;						      \
+      free (old);							      \
+    }									      \
+  } while (0)
+# undef alloca
+# define alloca(size) (malloc (size))
+#endif	/* have alloca */
 
 
 /* Names for the libintl functions are a problem.  They must not clash
@@ -167,6 +217,24 @@
 # define DCGETTEXT dcgettext__
 #endif
 
+/* Checking whether the binaries runs SUID must be done and glibc provides
+   easier methods therefore we make a difference here.  */
+#ifdef _LIBC
+# define ENABLE_SECURE __libc_enable_secure
+# define DETERMINE_SECURE
+#else
+static int enable_secure;
+# define ENABLE_SECURE (enable_secure == 1)
+# define DETERMINE_SECURE \
+  if (enable_secure == 0)						      \
+    {									      \
+      if (getuid () != geteuid () || getgid () != getegid ())		      \
+	enable_secure = 1;						      \
+      else								      \
+	enable_secure = -1;						      \
+    }
+#endif
+
 /* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
    locale.  */
 char *
@@ -175,21 +243,28 @@
      const char *msgid;
      int category;
 {
-  struct loaded_domain *domain;
+#ifndef HAVE_ALLOCA
+  struct block_list *block_list = NULL;
+#endif
+  struct loaded_l10nfile *domain;
   struct binding *binding;
   const char *categoryname;
   const char *categoryvalue;
   char *dirname, *xdomainname;
   char *single_locale;
   char *retval;
+  int saved_errno = errno;
 
   /* If no real MSGID is given return NULL.  */
   if (msgid == NULL)
     return NULL;
 
+  /* See whether this is a SUID binary or not.  */
+  DETERMINE_SECURE;
+
   /* If DOMAINNAME is NULL, we are interested in the default domain.  If
      CATEGORY is not LC_MESSAGES this might not make much sense but the
-     defintion left this undefined.  */
+     definition left this undefined.  */
   if (domainname == NULL)
     domainname = _nl_current_default_domain;
 
@@ -219,34 +294,31 @@
       size_t path_max;
       char *ret;
 
-      path_max = (unsigned) PATH_MAX;
+      path_max = (unsigned int) PATH_MAX;
       path_max += 2;		/* The getcwd docs say to do this.  */
 
       dirname = (char *) alloca (path_max + dirname_len);
+      ADD_BLOCK (block_list, dirname);
 
-      errno = 0;
+      __set_errno (0);
       while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE)
 	{
 	  path_max += PATH_INCR;
 	  dirname = (char *) alloca (path_max + dirname_len);
-	  errno = 0;
+	  ADD_BLOCK (block_list, dirname);
+	  __set_errno (0);
 	}
 
       if (ret == NULL)
-	/* We cannot get the current working directory.  Don't signal an
-	   error but simply return the default string.  */
-	return (char *) msgid;
-
-      /* We don't want libintl.a to depend on any other library.  So
-	 we avoid the non-standard function stpcpy.  In GNU C Library
-	 this function is available, though.  Also allow the symbol
-	 HAVE_STPCPY to be defined.  */
-#if defined _LIBC || defined HAVE_STPCPY
+	{
+	  /* We cannot get the current working directory.  Don't signal an
+	     error but simply return the default string.  */
+	  FREE_BLOCKS (block_list);
+	  __set_errno (saved_errno);
+	  return (char *) msgid;
+	}
+
       stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
-#else
-      strcat (dirname, "/");
-      strcat (dirname, binding->dirname);
-#endif
     }
 
   /* Now determine the symbolic name of CATEGORY and its value.  */
@@ -255,27 +327,19 @@
 
   xdomainname = (char *) alloca (strlen (categoryname)
 				 + strlen (domainname) + 5);
-  /* We don't want libintl.a to depend on any other library.  So we
-     avoid the non-standard function stpcpy.  In GNU C Library this
-     function is available, though.  Also allow the symbol HAVE_STPCPY
-     to be defined.  */
-#if defined _LIBC || defined HAVE_STPCPY
+  ADD_BLOCK (block_list, xdomainname);
+
   stpcpy (stpcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
 		  domainname),
 	  ".mo");
-#else
-  strcpy (xdomainname, categoryname);
-  strcat (xdomainname, "/");
-  strcat (xdomainname, domainname);
-  strcat (xdomainname, ".mo");
-#endif
 
   /* Creating working area.  */
   single_locale = (char *) alloca (strlen (categoryvalue) + 1);
+  ADD_BLOCK (block_list, single_locale);
 
 
   /* Search for the given string.  This is a loop because we perhaps
-     got an ordered list of languages to consider for th translation.  */
+     got an ordered list of languages to consider for the translation.  */
   while (1)
     {
       /* Make CATEGORYVALUE point to the next element of the list.  */
@@ -285,7 +349,7 @@
 	{
 	  /* The whole contents of CATEGORYVALUE has been searched but
 	     no valid entry has been found.  We solve this situation
-	     by implicitely appending a "C" entry, i.e. no translation
+	     by implicitly appending a "C" entry, i.e. no translation
 	     will take place.  */
 	  single_locale[0] = 'C';
 	  single_locale[1] = '\0';
@@ -296,13 +360,26 @@
 	  while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
 	    *cp++ = *categoryvalue++;
 	  *cp = '\0';
+
+	  /* When this is a SUID binary we must not allow accessing files
+	     outside the dedicated directories.  */
+	  if (ENABLE_SECURE
+	      && (memchr (single_locale, '/',
+			  _nl_find_language (single_locale) - single_locale)
+		  != NULL))
+	    /* Ingore this entry.  */
+	    continue;
 	}
 
       /* If the current locale value is C (or POSIX) we don't load a
 	 domain.  Return the MSGID.  */
       if (strcmp (single_locale, "C") == 0
 	  || strcmp (single_locale, "POSIX") == 0)
-	return (char *) msgid;
+	{
+	  FREE_BLOCKS (block_list);
+	  __set_errno (saved_errno);
+	  return (char *) msgid;
+	}
 
 
       /* Find structure describing the message catalog matching the
@@ -327,7 +404,11 @@
 	    }
 
 	  if (retval != NULL)
-	    return retval;
+	    {
+	      FREE_BLOCKS (block_list);
+	      __set_errno (saved_errno);
+	      return retval;
+	    }
 	}
     }
   /* NOTREACHED */
@@ -340,18 +421,23 @@
 
 
 static char *
-find_msg (domain, msgid)
-     struct loaded_domain *domain;
+internal_function
+find_msg (domain_file, msgid)
+     struct loaded_l10nfile *domain_file;
      const char *msgid;
 {
-  size_t top, act, bottom;
+  size_t act = 0;
+  size_t top, bottom;
+  struct loaded_domain *domain;
 
-  if (domain->decided == 0)
-    _nl_load_domain (domain);
+  if (domain_file->decided == 0)
+    _nl_load_domain (domain_file);
 
-  if (domain->data == NULL)
+  if (domain_file->data == NULL)
     return NULL;
 
+  domain = (struct loaded_domain *) domain_file->data;
+
   /* Locate the MSGID and its translation.  */
   if (domain->hash_size > 2 && domain->hash_tab != NULL)
     {
@@ -375,8 +461,8 @@
 
       while (1)
 	{
-	  if (idx >= W (domain->must_swap, domain->hash_size) - incr)
-	    idx -= W (domain->must_swap, domain->hash_size) - incr;
+	  if (idx >= domain->hash_size - incr)
+	    idx -= domain->hash_size - incr;
 	  else
 	    idx += incr;
 
@@ -424,7 +510,9 @@
 
 
 /* Return string representation of locale CATEGORY.  */
-static const char *category_to_name (category)
+static const char *
+internal_function
+category_to_name (category)
      int category;
 {
   const char *retval;
@@ -482,7 +570,9 @@
 }
 
 /* Guess value of current locale from value of the environment variables.  */
-static const char *guess_category_value (category, categoryname)
+static const char *
+internal_function
+guess_category_value (category, categoryname)
      int category;
      const char *categoryname;
 {
@@ -520,3 +610,46 @@
   return "C";
 #endif
 }
+
+/* @@ begin of epilog @@ */
+
+/* We don't want libintl.a to depend on any other library.  So we
+   avoid the non-standard function stpcpy.  In GNU C Library this
+   function is available, though.  Also allow the symbol HAVE_STPCPY
+   to be defined.  */
+#if !_LIBC && !HAVE_STPCPY
+static char *
+stpcpy (dest, src)
+     char *dest;
+     const char *src;
+{
+  while ((*dest++ = *src++) != '\0')
+    /* Do nothing. */ ;
+  return dest - 1;
+}
+#endif
+
+
+#ifdef _LIBC
+/* If we want to free all resources we have to do some work at
+   program's end.  */
+static void __attribute__ ((unused))
+free_mem (void)
+{
+  struct binding *runp;
+
+  for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next)
+    {
+      free (runp->domainname);
+      if (runp->dirname != _nl_default_dirname)
+	/* Yes, this is a pointer comparison.  */
+	free (runp->dirname);
+    }
+
+  if (_nl_current_default_domain != _nl_default_default_domain)
+    /* Yes, again a pointer comparison.  */
+    free ((char *) _nl_current_default_domain);
+}
+
+text_set_element (__libc_subfreeres, free_mem);
+#endif