tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

env.c (7205B)


      1 /* Copyright (c) 2003-2004, Roger Dingledine
      2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      4 /* See LICENSE for licensing information */
      5 
      6 /**
      7 * \file env.c
      8 * \brief Inspect and manipulate the environment variables.
      9 **/
     10 
     11 #include "orconfig.h"
     12 #include "lib/process/env.h"
     13 
     14 #include "lib/malloc/malloc.h"
     15 #include "lib/ctime/di_ops.h"
     16 #include "lib/container/smartlist.h"
     17 #include "lib/log/util_bug.h"
     18 #include "lib/log/log.h"
     19 
     20 #ifdef HAVE_UNISTD_H
     21 #include <unistd.h>
     22 #endif
     23 #include <stdlib.h>
     24 #include <string.h>
     25 #ifdef HAVE_CRT_EXTERNS_H
     26 /* For _NSGetEnviron on macOS */
     27 #include <crt_externs.h>
     28 #endif
     29 
     30 #ifndef HAVE__NSGETENVIRON
     31 #ifndef HAVE_EXTERN_ENVIRON_DECLARED
     32 /* Some platforms declare environ under some circumstances, others don't. */
     33 #ifndef RUNNING_DOXYGEN
     34 extern char **environ;
     35 #endif
     36 #endif /* !defined(HAVE_EXTERN_ENVIRON_DECLARED) */
     37 #endif /* !defined(HAVE__NSGETENVIRON) */
     38 
     39 /** Return the current environment. This is a portable replacement for
     40 * 'environ'. */
     41 char **
     42 get_environment(void)
     43 {
     44 #ifdef HAVE__NSGETENVIRON
     45  /* This is for compatibility between OSX versions.  Otherwise (for example)
     46   * when we do a mostly-static build on OSX 10.7, the resulting binary won't
     47   * work on OSX 10.6. */
     48  return *_NSGetEnviron();
     49 #else /* !defined(HAVE__NSGETENVIRON) */
     50  return environ;
     51 #endif /* defined(HAVE__NSGETENVIRON) */
     52 }
     53 
     54 /** Helper: return the number of characters in <b>s</b> preceding the first
     55 * occurrence of <b>ch</b>. If <b>ch</b> does not occur in <b>s</b>, return
     56 * the length of <b>s</b>. Should be equivalent to strspn(s, "ch"). */
     57 static inline size_t
     58 str_num_before(const char *s, char ch)
     59 {
     60  const char *cp = strchr(s, ch);
     61  if (cp)
     62    return cp - s;
     63  else
     64    return strlen(s);
     65 }
     66 
     67 /** Return non-zero iff getenv would consider <b>s1</b> and <b>s2</b>
     68 * to have the same name as strings in a process's environment. */
     69 int
     70 environment_variable_names_equal(const char *s1, const char *s2)
     71 {
     72  size_t s1_name_len = str_num_before(s1, '=');
     73  size_t s2_name_len = str_num_before(s2, '=');
     74 
     75  return (s1_name_len == s2_name_len &&
     76          tor_memeq(s1, s2, s1_name_len));
     77 }
     78 
     79 /** Free <b>env</b> (assuming it was produced by
     80 * process_environment_make). */
     81 void
     82 process_environment_free_(process_environment_t *env)
     83 {
     84  if (env == NULL) return;
     85 
     86  /* As both an optimization hack to reduce consing on Unixoid systems
     87   * and a nice way to ensure that some otherwise-Windows-specific
     88   * code will always get tested before changes to it get merged, the
     89   * strings which env->unixoid_environment_block points to are packed
     90   * into env->windows_environment_block. */
     91  tor_free(env->unixoid_environment_block);
     92  tor_free(env->windows_environment_block);
     93 
     94  tor_free(env);
     95 }
     96 
     97 /** Make a process_environment_t containing the environment variables
     98 * specified in <b>env_vars</b> (as C strings of the form
     99 * "NAME=VALUE"). */
    100 process_environment_t *
    101 process_environment_make(struct smartlist_t *env_vars)
    102 {
    103  process_environment_t *env = tor_malloc_zero(sizeof(process_environment_t));
    104  int n_env_vars = smartlist_len(env_vars);
    105  int i;
    106  size_t total_env_length;
    107  smartlist_t *env_vars_sorted;
    108 
    109  tor_assert(n_env_vars + 1 != 0);
    110  env->unixoid_environment_block = tor_calloc(n_env_vars + 1, sizeof(char *));
    111  /* env->unixoid_environment_block is already NULL-terminated,
    112   * because we assume that NULL == 0 (and check that during compilation). */
    113 
    114  total_env_length = 1; /* terminating NUL of terminating empty string */
    115  for (i = 0; i < n_env_vars; ++i) {
    116    const char *s = smartlist_get(env_vars, (int)i);
    117    size_t slen = strlen(s);
    118 
    119    tor_assert(slen + 1 != 0);
    120    tor_assert(slen + 1 < SIZE_MAX - total_env_length);
    121    total_env_length += slen + 1;
    122  }
    123 
    124  env->windows_environment_block = tor_malloc_zero(total_env_length);
    125  /* env->windows_environment_block is already
    126   * (NUL-terminated-empty-string)-terminated. */
    127 
    128  /* Some versions of Windows supposedly require that environment
    129   * blocks be sorted.  Or maybe some Windows programs (or their
    130   * runtime libraries) fail to look up strings in non-sorted
    131   * environment blocks.
    132   *
    133   * Also, sorting strings makes it easy to find duplicate environment
    134   * variables and environment-variable strings without an '=' on all
    135   * OSes, and they can cause badness.  Let's complain about those. */
    136  env_vars_sorted = smartlist_new();
    137  smartlist_add_all(env_vars_sorted, env_vars);
    138  smartlist_sort_strings(env_vars_sorted);
    139 
    140  /* Now copy the strings into the environment blocks. */
    141  {
    142    char *cp = env->windows_environment_block;
    143    const char *prev_env_var = NULL;
    144 
    145    for (i = 0; i < n_env_vars; ++i) {
    146      const char *s = smartlist_get(env_vars_sorted, (int)i);
    147      size_t slen = strlen(s);
    148      size_t s_name_len = str_num_before(s, '=');
    149 
    150      if (s_name_len == slen) {
    151        log_warn(LD_GENERAL,
    152                 "Preparing an environment containing a variable "
    153                 "without a value: %s",
    154                 s);
    155      }
    156      if (prev_env_var != NULL &&
    157          environment_variable_names_equal(s, prev_env_var)) {
    158        log_warn(LD_GENERAL,
    159                 "Preparing an environment containing two variables "
    160                 "with the same name: %s and %s",
    161                 prev_env_var, s);
    162      }
    163 
    164      prev_env_var = s;
    165 
    166      /* Actually copy the string into the environment. */
    167      memcpy(cp, s, slen+1);
    168      env->unixoid_environment_block[i] = cp;
    169      cp += slen+1;
    170    }
    171 
    172    tor_assert(cp == env->windows_environment_block + total_env_length - 1);
    173  }
    174 
    175  smartlist_free(env_vars_sorted);
    176 
    177  return env;
    178 }
    179 
    180 /** Return a newly allocated smartlist containing every variable in
    181 * this process's environment, as a NUL-terminated string of the form
    182 * "NAME=VALUE".  Note that on some/many/most/all OSes, the parent
    183 * process can put strings not of that form in our environment;
    184 * callers should try to not get crashed by that.
    185 *
    186 * The returned strings are heap-allocated, and must be freed by the
    187 * caller. */
    188 struct smartlist_t *
    189 get_current_process_environment_variables(void)
    190 {
    191  smartlist_t *sl = smartlist_new();
    192 
    193  char **environ_tmp; /* Not const char ** ? Really? */
    194  for (environ_tmp = get_environment(); *environ_tmp; ++environ_tmp) {
    195    smartlist_add_strdup(sl, *environ_tmp);
    196  }
    197 
    198  return sl;
    199 }
    200 
    201 /** For each string s in <b>env_vars</b> such that
    202 * environment_variable_names_equal(s, <b>new_var</b>), remove it; if
    203 * <b>free_p</b> is non-zero, call <b>free_old</b>(s).  If
    204 * <b>new_var</b> contains '=', insert it into <b>env_vars</b>. */
    205 void
    206 set_environment_variable_in_smartlist(struct smartlist_t *env_vars,
    207                                      const char *new_var,
    208                                      void (*free_old)(void*),
    209                                      int free_p)
    210 {
    211  SMARTLIST_FOREACH_BEGIN(env_vars, const char *, s) {
    212    if (environment_variable_names_equal(s, new_var)) {
    213      SMARTLIST_DEL_CURRENT(env_vars, s);
    214      if (free_p) {
    215        free_old((void *)s);
    216      }
    217    }
    218  } SMARTLIST_FOREACH_END(s);
    219 
    220  if (strchr(new_var, '=') != NULL) {
    221    smartlist_add(env_vars, (void *)new_var);
    222  }
    223 }