/* File checkenv.c
 *
 * Copyright (C) 2001 Jean-Jacques Sarton jj.sarton@t-online.de
 *
 * 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 of the License, 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.
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
 
/* List of environment variables we accept */
static char *envVars[] =
{
   "PATH=",
   "HOME=",
   "USER=",
   "LANG=",
   "LOGNAME=",
   "DISPLAY=",
   "SHELL=",
   "XAUTHORITY=",
   NULL
};


/* linked list for storing the variable we will unset */
typedef struct envList_s
{
    char             *name;
    struct envList_s *next;
} envList_t;

static envList_t *list = NULL;

/*******************************************************************/
/* Function addElem(char *e)                                       */
/*                                                                 */
/*   char *e contain "VAR=value" from the environment              */
/*   we extract the name (VAR) and create an envList_telement      */
/*   will be added to our linked list                              */
/*                                                                 */
/*  return 0 on error 1 if all is OK                               */
/*******************************************************************/

static int addElem(char *e)
{
   envList_t *l;
   char *s = e;
   char *n;

   /* search end of name and alloc memory for storing it */
   while(*s && *s != '=')
   {
      s++;
   }
   n = calloc(s-e+2,1);

   if ( n != NULL )
   {
      strncpy(n, e, s-e);
      /* alloc a list element build the linked list */
      if ( list == NULL )
      {
         list = (envList_t*)calloc(sizeof(envList_t),1);
         if ( list == NULL )
         {
            return 0;
         }
         list->name = n;
      }
      else
      {
         l = list;
         while (l->next != NULL )
         {
            l = l->next;
         }
         l->next = (envList_t*)calloc(sizeof(envList_t),1);
         if ( l->next == NULL )
         {
             return 0;
         }
         l = l->next;
         l->name = n;
         return 1;
      }
   }
   else
   { 
      return 0;
   }
   return 1;
}

/*******************************************************************/
/* Function unsetEnv(void)                                         */
/*                                                                 */
/*  unset the variable listed into our list and free the list      */
/*                                                                 */
/*******************************************************************/

static void unsetEnv()
{
   envList_t *l = list;
   envList_t *n;
   while ( l != NULL )
   {
      unsetenv(l->name);
      free(l->name);
      n = l->next;
      free(l);
      l = n;
   }
   list = NULL;
}

/*******************************************************************/
/* Function checkPath(void)                                        */
/*                                                                 */
/* check the length of each element contained into our PATH        */
/* variable                                                        */
/*                                                                 */
/*  return 0 on error 1 if all is OK                               */
/*******************************************************************/

static int checkPath(void)
{
   char *p = getenv("PATH");
   char *e;

   if ( p )
   {
      while(*p)
      {
         e = strchr(p, ':');
         if ( e && (e - p) > 800 )
         {
            return 0;
         }
         else if ( e )
         {
            p = e+1; 
         }
         else
         {
            break;
         }
      }
   }
   return 1;
}

/*******************************************************************/
/* Function checkEnv(char **env)                                   */
/*                                                                 */
/* char **env is the third argument passed to main()               */ 
/*                                                                 */
/* Check the environment against possibly corrupted variable.      */
/* Not needed variable will be deleted, the remaining variables    */
/* will be checked (length) so we avoid bufferoverflow from        */
/* buggy libraries                                                 */
/*                                                                 */
/*  return 0 on error 1 if all is OK                               */
/*******************************************************************/

int checkEnv(char **env)
{
   char **e = env;
   char *n  = 0;
   char *m;
   char *u;
   char **envName = NULL;

   /* check the environment variable against the allowed */
   /* variable, if the variable is not into our array of */
   /* allowed variable remember the name so we can unset */
   /* the variable later                                 */
   while ( e && *e )
   {
      envName = envVars;
      while ( *envName )
      {
         if ( strncmp(*e, *envName, strlen(*envName)) == 0 )
         {
            /* variable is known */
            break;
         }
          envName++;
      }
      if ( *envName == NULL ) /* we don't need this */
      {
         if ( addElem(*e) == 0 )
         {
            return 0;
         }
      }
      e++;
   }
   
   /* unset all not allowed/needed variables */
   unsetEnv();

   /* check the remaining environment variables */
   n = getenv("HOME");
   if ( n && strlen(n) > 900 )
   {
      return 0;
   }

   /* check the LOGNAME and USER variables */
   m = getenv("LOGNAME");
   u = getenv("USER");
   if ( m != NULL && u != NULL )
   {
      unsetenv("LOGNAME");
   }
   else if ( m != NULL )
   {
      m = u;
   }
   if ( strlen(m) > 256 )
   {
      return 0;
   }
   while ( *m )
   {
      if ( !isprint(*m) || isspace(*m) )
         return 0;
      m++;
   }
   
   /* check the PATH variable */
   if ( checkPath() == 0 )
   {
      return 0;
   }
   
   /* check LANG */
   if ( (m = getenv("LANG")) != NULL )
   {
      if ( strlen(m) > 20 )
      {
         return 0;
      }
   }
   
   /* check SHELL */
   if ( (m = getenv("SHELL")) != NULL )
   {
      /* path very long -> error */
      if ( strlen(m) > 1023 )
         return 0;

      /* check all characters, allow only printable
       * non space character.
       */
      while ( *m )
      {
         if ( !isprint(*m) || isspace(*m) )
            return 0;
         m++;
      }
   }

   /* DISPLAY */
   m = getenv("DISPLAY");
   if ( m != NULL )
   {
      n = strchr(m, ':');
      if ( n == NULL )
      {
         return 0;
      }
      if ( strlen(n) > 6 )
      {
         return 0;
      }
      if ( n-m > 14 )
      {
         return 0;
      }
   }

   /* XAUTHORITY */
   m = getenv("XAUTHORITY");
   if ( m!= NULL )
   {
      if ( strlen(m) > 1023 )
      {
         return 0;
      }
   }
   return 1;
}
