blob: 85b6288e22c46190350847a44d42af906f504b19 [file] [log] [blame]
/*
* 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;
}