first and last commit

This commit is contained in:
Chris Nutter committed 2023-07-08 13:03:17 -07:00
commit 1bf4efd552
2612 files changed
+636500

No files matched your search

+560
View File
@@ -0,0 +1,560 @@
/*
* Copyright (C) 2007-2014 Apple Inc. All rights reserved.
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
*
* This document is the property of Apple Computer, Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Computer, Inc.
*/
#include <debug.h>
#include <list.h>
#include <lib/env.h>
#include <stdlib.h>
#include <string.h>
#include <sys/menu.h>
static struct list_node env_list = LIST_INITIAL_VALUE(env_list);
#define ENV_VAR_ENTRY_NAME_LEN 64
struct env_var_entry {
struct list_node node;
struct env_var var;
char name[ENV_VAR_ENTRY_NAME_LEN];
};
static int str2ip(const char *str, uint32_t *ip);
static int str2mac(const char *str, uint8_t addr[6]);
#define kIBootHiddenEnvDomain "com.apple.System."
#if DEBUG_BUILD
static bool nvram_whitelist_override = false;
#else
static const bool nvram_whitelist_override = false;
#endif
static bool hide_key(const char *str)
{
if (0 == strncmp(str, kIBootHiddenEnvDomain, sizeof(kIBootHiddenEnvDomain)-1))
return true;
else
return false;
}
static struct env_var_entry *find_var_entry(const char *name)
{
struct env_var_entry *entry;
list_for_every_entry(&env_list, entry, struct env_var_entry, node) {
if (!strcmp(entry->name, name))
return entry;
}
return NULL;
}
const struct env_var *env_get_etc(const char *name)
{
struct env_var_entry *entry;
entry = find_var_entry(name);
if (!entry)
return NULL;
if ((entry->var.flags & ENV_SHADOW) && !nvram_whitelist_override) {
dprintf(DEBUG_CRITICAL, "\x1b[1;5;31mBlocked read\x1b[m from shadowed variable \"%s\"\n", name);
dprintf(DEBUG_CRITICAL, "Disable NVRAM whitelist with DEBUG iBoot command envprot off\n");
return NULL;
}
return &entry->var;
}
const char *env_get(const char *name)
{
const struct env_var *var;
var = env_get_etc(name);
if (!var)
return NULL;
return var->str;
}
size_t env_get_uint(const char *name, size_t default_val)
{
const struct env_var *var;
var = env_get_etc(name);
if (!var)
return default_val;
return var->u;
}
bool env_get_bool(const char *name, bool default_val)
{
const struct env_var *var;
var = env_get_etc(name);
if (NULL == var)
return default_val;
if (!strcmp(var->str, "true"))
return true;
if (0 != var->u)
return true;
return false;
}
static int str2ip(const char *str, uint32_t *ip)
{
char numstr[16];
int num;
int field = 0;
const char *ptr;
if (str == NULL)
return -1;
*ip = 0;
ptr = str;
for (field = 0; field < 4; field++) {
char *dot = strchr(ptr, '.');
if (dot) {
strlcpy(numstr, ptr, __min((unsigned)(dot - ptr + 1), sizeof(numstr)));
} else {
strlcpy(numstr, ptr, sizeof(numstr));
}
// printf("numstr '%s'\n", numstr);
num = strtol(numstr, NULL, 0);
if (num < 0 || num > 255) {
return -1;
}
*ip |= (num << (8 * field));
if (dot)
ptr = dot + 1;
else
break;
}
return 0;
}
int env_get_ipaddr(const char *name, uint32_t *ip)
{
const char *env;
env = env_get(name);
if (!env)
return -1;
if (str2ip(env, ip) < 0)
return -1;
return 0;
}
static int str2mac(const char *str, uint8_t addr[6])
{
char numstr[16];
int num;
int field = 0;
const char *ptr;
char *endptr;
if (str == NULL)
return -1;
addr[0] = addr[1] = addr[2] = addr[3] = addr[4] = addr[5] = 0;
ptr = str;
for (field = 0; field < 6; field++) {
char *sep = strchr(ptr, ':');
if (sep) {
strlcpy(numstr, ptr, __min((unsigned)(sep - ptr + 1), sizeof(numstr)));
} else {
strlcpy(numstr, ptr, sizeof(numstr));
}
// printf("numstr '%s'\n", numstr);
num = strtol(numstr, &endptr, 16);
if (endptr == numstr) {
dprintf(DEBUG_INFO, "malformed MAC address %s\n", str);
return -1;
}
if (num < 0 || num > 255) {
dprintf(DEBUG_INFO, "makformed MAC address %s\n", str);
return -1;
}
addr[field] = num;
if (sep)
ptr = sep + 1;
else
break;
}
return 0;
}
int env_get_ethaddr(const char *name, uint8_t ethaddr[6])
{
const char *env;
env = env_get(name);
if (!env)
return -1;
if (str2mac(env, ethaddr) < 0)
return -1;
return 0;
}
int env_set(const char *name, const char *val, uint32_t flags)
{
struct env_var_entry *entry;
if (flags & ENV_PERSISTENT) {
if (env_blacklist_nvram(name))
flags |= ENV_SHADOW;
}
/* see if it already exists */
entry = find_var_entry(name);
if (entry) {
if ((flags & ENV_SHADOW) && !(entry->var.flags & ENV_SHADOW) && !nvram_whitelist_override) {
dprintf(DEBUG_CRITICAL, "\x1b[1;5;31mBlocked shadowed write\x1b[m to variable \"%s\"\n", name);
dprintf(DEBUG_CRITICAL, "Disable NVRAM whitelist with DEBUG iBoot command envprot off\n");
// If the variable exists, is not on the whitelist for NVRAM variables,
// and the value was generated internally (probably from the default environment),
// refuse to modify the existing value.
// This will result in the variable getting removed from nvram on saveenv, but
// protects us from people removing things from the default environment.
return -1;
} else {
// If the variable exists and either is on the whitelist or was not
// generated internally, delete the old value
env_unset(name);
}
}
entry = malloc(sizeof(struct env_var_entry));
strlcpy(entry->name, name, sizeof(entry->name));
entry->var.str = strdup(val);
if (entry->var.str == NULL) {
free(entry);
return -1;
}
entry->var.u = strtoul(entry->var.str, NULL, 0);
/* save the flags */
entry->var.flags = flags;
list_add_tail(&env_list, &entry->node);
return 0;
}
int env_set_uint(const char *name, size_t val, uint32_t flags)
{
char numbuf[(sizeof(size_t) * 2) + 3]; // plus '3' to store "0x" and null terminator
snprintf(numbuf, sizeof(numbuf), "0x%lx", val);
return env_set(name, numbuf, flags);
}
int env_unset(const char *name)
{
struct env_var_entry *entry;
entry = find_var_entry(name);
if (!entry)
return 0;
list_delete(&entry->node);
free(entry->var.str);
free(entry);
return 1;
}
/*
* used by the nvram code to write the environment out to nvram.
* serialize all of the persistent environment variables with a simple
* <name>=<value>NULL...
* strategy.
*/
size_t env_serialize(uint8_t *buf, size_t buf_len)
{
size_t offset;
struct env_var_entry *entry;
offset = 0;
list_for_every_entry(&env_list, entry, struct env_var_entry, node) {
if (entry->var.flags & ENV_PERSISTENT) {
/* print one var into the buffer, avoiding overflow */
offset += snprintf((void *)(buf + offset), buf_len - offset, "%s=%s", entry->name, entry->var.str) + 1;
/* effectively require there to always be at least one free byte */
if (offset >= buf_len)
return(0);
}
}
return offset;
}
/* opposite of the above */
int env_unserialize(const uint8_t *buf, size_t buf_len)
{
size_t offset;
#if 0
dprintf(DEBUG_SPEW, "serialized environment vars:\n");
dhexdump(DEBUG_SPEW, buf, buf_len);
#endif
offset = 0;
while (offset < buf_len) {
char envname[64];
char envval[256];
uint32_t var_start, var_end;
uint32_t val_start, val_end;
if (buf[offset] == 0) {
offset++;
continue;
}
/* find the start and end of the variable name */
var_start = offset;
for (var_end = var_start; buf[var_end] != '=' && buf[var_end] != 0; var_end++)
;
if (buf[var_end] == 0) // malformed variable, no '='
break;
/* find the start and end of the value component */
val_start = var_end + 1; /* skip the '=' */
for (val_end = val_start; buf[val_end] != 0; val_end++)
;
/* copy them out */
strlcpy(envname, (const char *)&buf[var_start], __min(var_end - var_start + 1, sizeof(envname)));
strlcpy(envval, (const char *)&buf[val_start], __min(val_end - val_start + 1, sizeof(envval)));
dprintf(DEBUG_SPEW, "'%s' = '%s'\n", envname, envval);
/* load the environment */
if (strlen(envname) > 0 && strlen(envval) > 0) {
uint32_t flags = ENV_PERSISTENT;
if (env_blacklist_nvram(envname))
flags |= ENV_SHADOW;
env_set(envname, envval, flags);
}
offset = val_end + 1;
}
return 0;
}
/* for unit testing testing purposes only, remove every environment
* variable
*/
void env_reset(void)
{
struct env_var_entry *entry;
struct env_var_entry *temp;
list_for_every_entry_safe(&env_list, entry, temp, struct env_var_entry, node) {
env_unset(entry->name);
}
}
/* command prompt routines */
static void dump_var(const struct env_var_entry *entry)
{
int i, binary = 0;
uint32_t flags;
/* filter out non-ascii strings in variable values */
for (i = 0; i < ENV_VAR_ENTRY_NAME_LEN; i++) {
if (entry->var.str[i] == 0)
break;
if (entry->var.str[i] < 0x20 || entry->var.str[i] > 0x7e)
binary = 1;
}
flags = entry->var.flags;
printf("%c %s = %s%s%s\n",
(flags & ENV_SHADOW) ? 'S' : ((flags & ENV_PERSISTENT) ? 'P' : ' '),
entry->name,
binary ? "" : "\"",
binary ? "<DATA>" : entry->var.str,
binary ? "" : "\"");
}
int do_printenv(int argc, struct cmd_arg *args)
{
struct env_var_entry *entry;
if (argc >= 2) {
/* display a single variable */
entry = find_var_entry(args[1].str);
if (entry) {
if (!hide_key(entry->name))
dump_var(entry);
} else {
printf("variable %s not set\n", args[1].str);
}
} else {
/* display all variables */
list_for_every_entry(&env_list, entry, struct env_var_entry, node) {
if (hide_key(entry->name)) continue;
dump_var(entry);
}
}
return 0;
}
int do_getenv(int argc, struct cmd_arg *args)
{
struct env_var_entry *entry;
if(argc != 2)
{
#if !RELEASE_BUILD
printf("%s <var>\n", args[0].str);
#endif
return -1;
}
entry = find_var_entry(args[1].str);
if(!entry)
return -1;
if(hide_key(entry->name))
return -1;
if(env_blacklist(entry->name, false))
return -1;
return env_set("cmd-results", entry->var.str, 0);
}
int do_setenv(int argc, struct cmd_arg *args)
{
if (argc < 2) {
#if !RELEASE_BUILD
printf("not enough arguments.\n");
printf("%s <var> [<value>] ...\n", args[0].str);
#endif
return -1;
}
if (hide_key(args[1].str)) return -1;
if (env_blacklist(args[1].str, true)) return -1;
if (argc < 3) {
env_unset(args[1].str);
} else {
size_t buf_size = 512;
char *envbuf = malloc(buf_size);
int i;
/* reassemble a string from all the command line args */
envbuf[0] = 0;
for (i = 2; i < argc; i++) {
strlcat(envbuf, args[i].str, buf_size);
if (i != (argc - 1))
strlcat(envbuf, " ", buf_size);
}
env_set(args[1].str, envbuf, ENV_PERSISTENT);
free(envbuf);
}
return 0;
}
int do_clearenv(int argc, struct cmd_arg *args)
{
struct env_var_entry *entry, *pentry;
if (argc < 2) {
printf("not enough arguments.\n");
printf("Usage: It's a two step process. Run the following commands to clear\n");
printf("\t\tclearenv <var>\n");
printf("\t\tclearenv 1 \t {confirms the clear}\n");
printf("ex: clearenv auto-boot boot-args\n");
printf(" clearenv 1\n");
return -1;
}
if (args[1].u == 0) {
printf("Clear pending. Run command \"clearenv 1\" to confirm\n");
return -1;
}
/* clear all persistent variables */
while (1) {
pentry = 0;
list_for_every_entry(&env_list, entry, struct env_var_entry, node) {
if (hide_key(entry->name)) continue;
if (entry->var.flags & ENV_PERSISTENT) {
pentry = entry;
break;
}
}
if (pentry == 0) break;
env_unset(pentry->name);
}
return 0;
}
#if DEBUG_BUILD
int
do_envprot(int argc, struct cmd_arg *args)
{
int result = 0;
if (argc != 2) {
result = -1;
} else {
if (strcmp(args[1].str, "off") == 0) {
nvram_whitelist_override = true;
printf("Disabled NVRAM whitelist\n");
} else if (strcmp(args[1].str, "on") == 0) {
nvram_whitelist_override = false;
printf("Enabled NVRAM whitelist\n");
} else {
result = -1;
}
}
if (result < 0)
printf("Usage: envprot {on | off}\n");
return result;
}
#endif
+27
View File
@@ -0,0 +1,27 @@
# Copyright (C) 2014 Apple Inc. All rights reserved.
#
# This document is the property of Apple Inc.
# It is considered confidential and proprietary.
#
# This document may not be reproduced or transmitted in any form,
# in whole or in part, without the express written permission of
# Apple Inc.
#
LIBENV_DIR := $(GET_LOCAL_DIR)
LIBENV_BUILD := $(call TOLIBDIR,$(LIBENV_DIR)/LIBENV.a)
COMMONLIBS += LIBENV
# Only process the remainder of the makefile if we are building libraries
#
ifeq ($(MAKEPHASE),libraries)
LIBENV_OBJS := $(LIBENV_DIR)/env.o
LIBENV_OBJS := $(call TOLIBOBJDIR,$(LIBENV_OBJS))
ALL_DEPS += $(LIBENV_OBJS:%o=%d)
$(LIBENV_BUILD): $(LIBENV_OBJS)
endif
+13
View File
@@ -0,0 +1,13 @@
# Copyright (C) 2014 Apple Inc. All rights reserved.
# Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
#
# This document is the property of Apple Inc.
# It is considered confidential and proprietary.
#
# This document may not be reproduced or transmitted in any form,
# in whole or in part, without the express written permission of
# Apple Inc.
#
OPTIONS += WITH_ENV=1
LIBRARY_MODULES += lib/env
+231
View File
@@ -0,0 +1,231 @@
#include <unittest.h>
#include <lib/env.h>
#include <stdlib.h>
#include <string.h>
extern int do_printenv(int argc, void *);
extern void env_reset(void);
/* Before each test case is called, empty out the environment */
void test_env_suite_setup(void)
{
env_reset();
}
void test_env(uintptr_t context)
{
const uint8_t source[] =
"numvar=1234567890\0"
"truevar=true\0"
"falsevar=false\0"
"zerovar=0\0"
"strvar=hi mom 123=abc-_*\0"
"ethaddr=12:34:56:78:ab:cd\0"
"ipaddr=32.1.0.100\0"
"blacklisted=nocando\0"
"com.apple.System.hideme=hidden";
uint8_t ethaddr[6];
uint32_t ipaddr;
TEST_ASSERT_EQ(env_unserialize(source, sizeof(source)), 0);
// Test getting variables as strings
TEST_ASSERT_STR_EQ(env_get("numvar"), "1234567890");
TEST_ASSERT_STR_EQ(env_get("truevar"), "true");
TEST_ASSERT_STR_EQ(env_get("falsevar"), "false");
TEST_ASSERT_STR_EQ(env_get("zerovar"), "0");
TEST_ASSERT_STR_EQ(env_get("strvar"), "hi mom 123=abc-_*");
TEST_ASSERT_STR_EQ(env_get("ethaddr"), "12:34:56:78:ab:cd");
TEST_ASSERT_STR_EQ(env_get("ipaddr"), "32.1.0.100");
TEST_ASSERT_STR_EQ(env_get("com.apple.System.hideme"), "hidden");
// Test getting variables as booleans
TEST_ASSERT_EQ(env_get_bool("truevar", false), true);
TEST_ASSERT_EQ(env_get_bool("truevar", true), true);
TEST_ASSERT_EQ(env_get_bool("falsevar", false), false);
TEST_ASSERT_EQ(env_get_bool("falsevar", true), false);
TEST_ASSERT_EQ(env_get_bool("zerovar", false), false);
TEST_ASSERT_EQ(env_get_bool("zerovar", true), false);
TEST_ASSERT_EQ(env_get_bool("numvar", false), true);
TEST_ASSERT_EQ(env_get_bool("numvar", true), true);
TEST_ASSERT_EQ(env_get_bool("strvar", false), false);
TEST_ASSERT_EQ(env_get_bool("strvar", true), false);
// Test default value for env_get_bool
TEST_ASSERT_EQ(env_get_bool("notavar", true), true);
TEST_ASSERT_EQ(env_get_bool("notavar", false), false);
// Test getting variables as numbers
TEST_ASSERT_EQ(env_get_uint("numvar", 0), 1234567890ULL);
// Test default value for env_get_uint
TEST_ASSERT_EQ(env_get_uint("notavar", 123456), 123456);
// Test getting variables as MAC addresses
TEST_ASSERT_EQ(env_get_ethaddr("ethaddr", ethaddr), 0);
TEST_ASSERT_MEM_EQ(ethaddr, TEST_ARRAY(uint8_t, 0x12, 0x34, 0x56, 0x78, 0xab, 0xcd), sizeof(ethaddr));
// Test rejecting malformed MAC addresses
TEST_ASSERT_NEQ(env_get_ethaddr("strvar", ethaddr), 0);
TEST_ASSERT_NEQ(env_get_ethaddr("numvar", ethaddr), 0);
TEST_ASSERT_NEQ(env_get_ethaddr("notavar", ethaddr), 0);
// Test getting variables as IP addresses
TEST_ASSERT_EQ(env_get_ipaddr("ipaddr", &ipaddr), 0);
TEST_ASSERT_EQ(ipaddr, 0x64000120);
// Test getting a blacklisted variable fails
TEST_ASSERT_NULL(env_get("blacklisted"));
// Test setting a non-persistent variable
TEST_ASSERT_EQ(env_set("temp", "don't save me", 0), 0);
TEST_ASSERT_STR_EQ(env_get("temp"), "don't save me");
// Test setting a persistent variable
TEST_ASSERT_EQ(env_set("persist", "save this var", ENV_PERSISTENT), 0);
TEST_ASSERT_STR_EQ(env_get("persist"), "save this var");
// Test env_set_uint
TEST_ASSERT_EQ(env_set_uint("uint", 0xabcd1234, 0), 0);
TEST_ASSERT_EQ(env_get_uint("uint", 0), 0xabcd1234);
// Test overwriting a persistent variable
TEST_ASSERT_EQ(env_set("overwrite", "will not be saved", ENV_PERSISTENT), 0);
TEST_ASSERT_STR_EQ(env_get("overwrite"), "will not be saved");
TEST_ASSERT_EQ(env_set("overwrite", "save this value", ENV_PERSISTENT), 0);
TEST_ASSERT_STR_EQ(env_get("overwrite"), "save this value");
// Test unsetting a value
TEST_ASSERT_EQ(env_set("unset", "about to unset", ENV_PERSISTENT), 0);
TEST_ASSERT_STR_EQ(env_get("unset"), "about to unset");
TEST_ASSERT_EQ(env_unset("unset"), 1);
TEST_ASSERT_NULL(env_get("unset"));
// unsetting a non-existant variable should fail
TEST_ASSERT_EQ(env_unset("unset"), 0);
// Test serializing - everything from the source should be serialized,
// plus persistent variables set with env_set
const uint8_t expected[] =
"numvar=1234567890\0"
"truevar=true\0"
"falsevar=false\0"
"zerovar=0\0"
"strvar=hi mom 123=abc-_*\0"
"ethaddr=12:34:56:78:ab:cd\0"
"ipaddr=32.1.0.100\0"
"blacklisted=nocando\0"
"com.apple.System.hideme=hidden\0"
"persist=save this var\0"
"overwrite=save this value";
uint8_t *serialized = malloc(sizeof(expected) * 2);
TEST_ASSERT_EQ(env_serialize(serialized, sizeof(expected) * 2), sizeof(expected));
TEST_ASSERT_MEM_EQ(serialized, expected, sizeof(expected));
memset(serialized, 0, sizeof(expected));
TEST_ASSERT_EQ(env_serialize(serialized, sizeof(expected) + 1), sizeof(expected));
TEST_ASSERT_MEM_EQ(serialized, expected, sizeof(expected));
// Too small buffer should fail serialization
TEST_ASSERT_EQ(env_serialize(serialized, sizeof(expected) - 1), 0);
}
void test_env_max_len_var(uintptr_t context)
{
int result;
size_t serialized_len;
const char *long_var =
"0001020304050607"
"08090a0b0c0d0e0f"
"1011121314151617"
"18191a1b1c1d1e1f"
"2021222324252627"
"28292a2b2c2d2e2f"
"3031323334353637"
"38393a3b3c3d3e3f"
"4041424344454647"
"48494a4b4c4d4e4f"
"5051525354555657"
"58595a5b5c5d5e5f"
"6061626364656667"
"68696a6b6c6d6e6f"
"7071727374757677"
"78797a7b7c7d7e_";
const uint8_t expected[] =
"testvar="
"0001020304050607"
"08090a0b0c0d0e0f"
"1011121314151617"
"18191a1b1c1d1e1f"
"2021222324252627"
"28292a2b2c2d2e2f"
"3031323334353637"
"38393a3b3c3d3e3f"
"4041424344454647"
"48494a4b4c4d4e4f"
"5051525354555657"
"58595a5b5c5d5e5f"
"6061626364656667"
"68696a6b6c6d6e6f"
"7071727374757677"
"78797a7b7c7d7e_"
"\0testvar2=abc";
// Setting the variable should succeed
result = env_set("testvar", long_var, ENV_PERSISTENT);
TEST_ASSERT_EQ(result, 0);
// Add a dummy variable after testvar for serialization test
result = env_set("testvar2", "abc", ENV_PERSISTENT);
TEST_ASSERT_EQ(result, 0);
// The variable should read back correctly
TEST_ASSERT_STR_EQ(env_get("testvar"), long_var);
// The variable should serialize correctly
uint8_t *serialized = malloc(sizeof(expected) * 2);
serialized_len = env_serialize(serialized, sizeof(expected) * 2);
TEST_ASSERT_EQ(serialized_len, sizeof(expected));
TEST_ASSERT_MEM_EQ(serialized, expected, sizeof(expected));
// Clear out the environment to prepare for deserialization
env_reset();
// Make sure the environment really was cleared
TEST_ASSERT_NULL(env_get("testvar"));
TEST_ASSERT_NULL(env_get("testvar2"));
// Now load the environment back in from the serialized buffer
result = env_unserialize(serialized, serialized_len);
TEST_ASSERT_EQ(result, 0);
// And make sure everything came through OK
TEST_ASSERT_STR_EQ(env_get("testvar"), long_var);
TEST_ASSERT_STR_EQ(env_get("testvar2"), "abc");
}
bool env_blacklist(const char *name, bool write)
{
if (strcmp(name, "set-blacklisted") == 0)
return true;
return false;
}
bool env_blacklist_nvram(const char *name)
{
if (strcmp(name, "blacklisted") == 0)
return true;
return false;
}
static struct test_suite env_test_suite = {
.name = "env",
.description = "tests the environment module",
.setup_function = test_env_suite_setup,
.test_cases = {
{ "env", test_env, 0 },
{ "env_max_len_var", test_env_max_len_var, 0 },
TEST_CASE_LAST
}
};
TEST_SUITE(env_test_suite);
+20
View File
@@ -0,0 +1,20 @@
# Copyright (C) 2014 Apple Inc. All rights reserved.
#
# This document is the property of Apple Inc.
# It is considered confidential and proprietary.
#
# This document may not be reproduced or transmitted in any form,
# in whole or in part, without the express written permission of
# Apple Inc.
#
LOCAL_DIR := $(GET_LOCAL_DIR)
TEST_NAME := env
TEST_OBJS := \
$(LOCAL_DIR)/env.o
TEST_SUPPORT_OBJS := \
tests/unittest-main.o \
$(LOCAL_DIR)/tests.o