| /* |
| * Copyright (c) 2011 The Native Client Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "native_client/src/include/portability.h" |
| #include "native_client/src/include/nacl_macros.h" |
| |
| #include "native_client/src/trusted/service_runtime/env_cleanser.h" |
| #include "native_client/src/trusted/service_runtime/env_cleanser_test.h" |
| |
| /* |
| * Everything that starts with this prefix is allowed (but the prefix is |
| * stripped away). |
| */ |
| #define NACL_ENV_PREFIX "NACLENV_" |
| #define NACL_ENV_PREFIX_LENGTH 8 |
| |
| void NaClEnvCleanserCtor(struct NaClEnvCleanser *self, int with_whitelist) { |
| self->with_whitelist = with_whitelist; |
| self->cleansed_environ = (char const **) NULL; |
| } |
| |
| /* |
| * Environment variables names are from IEEE Std 1003.1-2001, with |
| * additional ones from locale(7) from glibc / linux. The entries |
| * must be sorted, in ASCII order, for the bsearch to run correctly. |
| */ |
| /* static -- not static for testing */ |
| char const *const kNaClEnvWhitelist[] = { |
| "LANG", |
| "LC_ADDRESS", |
| "LC_ALL", |
| "LC_COLLATE", |
| "LC_CTYPE", |
| "LC_IDENTIFICATION", |
| "LC_MEASUREMENT", |
| "LC_MESSAGES", |
| "LC_MONETARY", |
| "LC_NAME", |
| "LC_NUMERIC", |
| "LC_PAPER", |
| "LC_TELEPHONE", |
| "LC_TIME", |
| "NACLVERBOSITY", |
| "NACL_PLUGIN_DEBUG", /* Chromium's plugin/utility.cc */ |
| "NACL_SRPC_DEBUG", /* src/shared/srpc/utility.c */ |
| NULL, |
| }; |
| |
| /* left arg is key, right arg is table entry */ |
| static int EnvCmp(void const *vleft, void const *vright) { |
| char const *left = *(char const *const *) vleft; |
| char const *right = *(char const *const *) vright; |
| char cleft, cright; |
| |
| while ((cleft = *left) == (cright = *right) |
| && '\0' != cleft |
| && '\0' != cright) { |
| ++left; |
| ++right; |
| } |
| if ('=' == cleft && '\0' == cright) { |
| return 0; |
| } |
| return (0xff & cleft) - (0xff & cright); |
| } |
| |
| int NaClEnvIsPassThroughVar(char const *env_entry) { |
| return strlen(env_entry) > NACL_ENV_PREFIX_LENGTH && |
| 0 == strncmp(env_entry, NACL_ENV_PREFIX, |
| NACL_ENV_PREFIX_LENGTH); |
| } |
| |
| int NaClEnvInWhitelist(char const *env_entry) { |
| return NULL != bsearch((void const *) &env_entry, |
| (void const *) kNaClEnvWhitelist, |
| NACL_ARRAY_SIZE(kNaClEnvWhitelist) - 1, /* NULL */ |
| sizeof kNaClEnvWhitelist[0], |
| EnvCmp); |
| } |
| |
| /* PRE: sizeof(char *) is a power of 2 */ |
| |
| /* |
| * Initializes the object with a filtered environment. |
| * |
| * May return false on errors, e.g., out-of-memory. |
| */ |
| int NaClEnvCleanserInit(struct NaClEnvCleanser *self, char const *const *envp, |
| char const * const *extra_env) { |
| char const *const *p; |
| size_t num_env = 0; |
| size_t ptr_bytes = 0; |
| const size_t kMaxSize = ~(size_t) 0; |
| const size_t ptr_size_mult_overflow_mask = ~(kMaxSize / sizeof *envp); |
| char const **ptr_tbl; |
| size_t env; |
| |
| /* |
| * let n be a size_t. if n & ptr_size_mult_overflow_mask is set, |
| * then n*sizeof(void *) will have an arithmetic overflow. |
| */ |
| |
| if (NULL == envp || NULL == *envp) { |
| self->cleansed_environ = NULL; |
| return 1; |
| } |
| for (p = envp; NULL != *p; ++p) { |
| if (!(self->with_whitelist && NaClEnvInWhitelist(*p)) && |
| !NaClEnvIsPassThroughVar(*p)) { |
| continue; |
| } |
| if (num_env == kMaxSize) { |
| /* would overflow */ |
| return 0; |
| } |
| ++num_env; |
| } |
| |
| if (extra_env) { |
| for (p = extra_env; NULL != *p; ++p) { |
| if (num_env == kMaxSize) { |
| /* would overflow */ |
| return 0; |
| } |
| ++num_env; |
| } |
| } |
| |
| /* pointer table -- NULL pointer terminated */ |
| if (0 != ((1 + num_env) & ptr_size_mult_overflow_mask)) { |
| return 0; |
| } |
| ptr_bytes = (1 + num_env) * sizeof(*envp); |
| |
| ptr_tbl = (char const **) malloc(ptr_bytes); |
| if (NULL == ptr_tbl) { |
| return 0; |
| } |
| |
| /* this assumes no other thread is tweaking envp */ |
| for (env = 0, p = envp; NULL != *p; ++p) { |
| if (NaClEnvIsPassThroughVar(*p)) { |
| ptr_tbl[env] = *p + NACL_ENV_PREFIX_LENGTH; |
| } else if (self->with_whitelist && NaClEnvInWhitelist(*p)) { |
| ptr_tbl[env] = *p; |
| } else { |
| continue; |
| } |
| ++env; |
| } |
| if (extra_env) { |
| for (p = extra_env; NULL != *p; ++p) { |
| ptr_tbl[env] = *p; |
| ++env; |
| } |
| } |
| if (num_env != env) { |
| free((void *) ptr_tbl); |
| return 0; |
| } |
| ptr_tbl[env] = NULL; |
| self->cleansed_environ = ptr_tbl; |
| |
| return 1; |
| } |
| |
| char const *const *NaClEnvCleanserEnvironment(struct NaClEnvCleanser *self) { |
| return (char const *const *) self->cleansed_environ; |
| } |
| |
| void NaClEnvCleanserDtor(struct NaClEnvCleanser *self) { |
| free((void *) self->cleansed_environ); |
| self->cleansed_environ = NULL; |
| } |