* Copyright (c) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string>
#include <fstream>
#include <climits>
#include <sys/ioctl.h>
#include "securec.h"
#include "appspawn_utils.h"
#include "appspawn_msg.h"
#include "appspawn_hook.h"
#include "appspawn_manager.h"
constexpr const char *ENV_FILE_PATH = "/etc/environment";
constexpr size_t MIN_QUOTED_VALUE_LENGTH = 2;
constexpr size_t QUOTE_LENGTH = 1;
constexpr size_t TWO_QUOTE_LENGTH = 2 * QUOTE_LENGTH;
constexpr size_t MAX_ENV_LINE_LENGTH = 4096;
constexpr size_t MAX_ENV_FILE_LINES = 1000;
APPSPAWN_STATIC std::string_view TrimWhitespaceView(std::string_view str)
{
const char *ws = " \t\n\r";
auto start = str.find_first_not_of(ws);
if (start == std::string::npos) {
return std::string_view();
}
auto end = str.find_last_not_of(ws);
return std::string_view(str.data() + start, end - start + 1);
}
APPSPAWN_STATIC int32_t ParseEnvLine(const std::string &line, std::string &envName, std::string &envValue)
{
if (line.length() > MAX_ENV_LINE_LENGTH) {
APPSPAWN_LOGW("Environment line too long: %{public}zu bytes (max: %{public}zu)",
line.length(), MAX_ENV_LINE_LENGTH);
return APPSPAWN_ENV_FILE_FORMAT_ERROR;
}
std::string_view lineView(line);
lineView = TrimWhitespaceView(lineView);
if (lineView.empty() || lineView[0] == '#') {
return APPSPAWN_ENV_FILE_EMPTY;
}
size_t equalsPos = lineView.find('=');
if (equalsPos == std::string_view::npos) {
APPSPAWN_LOGW("Invalid environment variable format: %{public}s (missing '=')", line.c_str());
return APPSPAWN_ENV_FILE_FORMAT_ERROR;
}
std::string_view rawName = lineView.substr(0, equalsPos);
envName = std::string(TrimWhitespaceView(rawName));
if (envName.empty()) {
APPSPAWN_LOGW("Invalid environment variable: empty name");
return APPSPAWN_ENV_FILE_FORMAT_ERROR;
}
std::string_view rawValue = lineView.substr(equalsPos + 1);
std::string trimmedValue = std::string(TrimWhitespaceView(rawValue));
size_t valueLen = trimmedValue.length();
if (valueLen >= MIN_QUOTED_VALUE_LENGTH && (trimmedValue[0] == '"' || trimmedValue[0] == '\'') &&
trimmedValue[valueLen - 1] == trimmedValue[0]) {
envValue = trimmedValue.substr(QUOTE_LENGTH, valueLen - TWO_QUOTE_LENGTH);
} else {
envValue = trimmedValue;
}
return APPSPAWN_OK;
}
APPSPAWN_STATIC int32_t LoadCustomSandboxEnv(const char* envFilePath)
{
APPSPAWN_CHECK(envFilePath != nullptr, return APPSPAWN_ARG_INVALID, "envFilePath is nullptr.");
char normalizedPath[PATH_MAX] = {0};
int savedErrno = 0;
char *tmpPath = realpath(envFilePath, normalizedPath);
savedErrno = (tmpPath == nullptr) ? errno : 0;
APPSPAWN_CHECK(savedErrno != ENOENT, return APPSPAWN_OK, "Environment file %{public}s does not exist.",
envFilePath);
APPSPAWN_CHECK(tmpPath, return APPSPAWN_ARG_INVALID, "Failed to normalize path %{public}s, errno: %{public}d",
envFilePath, savedErrno);
std::ifstream file(normalizedPath);
bool isOpen = file.is_open();
savedErrno = (isOpen == false) ? errno : 0;
APPSPAWN_CHECK(savedErrno != ENOENT, return APPSPAWN_OK, "Environment file %{public}s does not exist.",
normalizedPath);
APPSPAWN_CHECK(isOpen, return APPSPAWN_ENV_FILE_READ_ERROR, "Failed to open %{public}s, errno: %{public}d",
normalizedPath, savedErrno);
std::string line;
std::string envName;
std::string envValue;
size_t lineCount = 0;
while (std::getline(file, line)) {
if (++lineCount > MAX_ENV_FILE_LINES) {
APPSPAWN_LOGE("Environment file exceeds maximum line count: %{public}zu (max: %{public}zu)",
lineCount, MAX_ENV_FILE_LINES);
return APPSPAWN_ENV_FILE_READ_ERROR;
}
int32_t parseRet = ParseEnvLine(line, envName, envValue);
if (parseRet != APPSPAWN_OK) {
continue;
}
int ret = setenv(envName.c_str(), envValue.c_str(), 1);
APPSPAWN_CHECK_LOGV(ret == 0, continue, "Failed to setenv %{public}s=%{public}s, errno: %{public}d",
envName.c_str(), envValue.c_str(), errno);
}
APPSPAWN_CHECK(!(file.fail() && !file.eof()), return APPSPAWN_ENV_FILE_READ_ERROR,
"Error reading %{public}s, errno: %{public}d", normalizedPath, errno);
return APPSPAWN_OK;
}
APPSPAWN_STATIC int SpawnSetCustomSandboxEnv(AppSpawnMgr *content, AppSpawningCtx *property)
{
APPSPAWN_LOGV("Spawning: set SpawnSetCustomSandboxEnv.");
if (CheckAppMsgFlagsSet(property, APP_FLAGS_CUSTOM_SANDBOX)) {
int32_t ret = LoadCustomSandboxEnv(ENV_FILE_PATH);
APPSPAWN_LOGV("Finished LoadCustomSandboxEnv result: %{public}d", ret);
}
return APPSPAWN_OK;
}
#define HM_ACCESS_TOKEN_ID_IOCTL_BASE 'A'
enum {
HM_GET_TOKEN_ID = 1,
HM_SET_TOKEN_ID,
HM_GET_FTOKEN_ID,
HM_SET_FTOKEN_ID,
HM_ADD_PERMISSION,
HM_REMOVE_PERMISSION,
HM_GET_PERMISSION,
HM_SET_PERMISSION,
HM_GET_CLOSEST_HAP_TOKENID,
HM_GET_FAMILY_TOKENIDS,
HM_GET_BINARY_PERMISSIONS,
HM_SET_USERID,
HM_GET_USERID,
HM_ACCESS_TOKENID_MAX_NR
};
#define ACCESS_TOKENID_SET_USERID _IOW(HM_ACCESS_TOKEN_ID_IOCTL_BASE, HM_SET_USERID, uint32_t)
#define ACCESS_TOKENID_GET_USERID _IOR(HM_ACCESS_TOKEN_ID_IOCTL_BASE, HM_GET_USERID, uint32_t)
constexpr const char* HDAC_DEV = "/dev/access_token_id";
#ifdef __cplusplus
extern "C" {
#endif
int SetUserId(char *userIdStr)
{
APPSPAWN_CHECK(userIdStr != nullptr, return -1, "userIdStr == nullptr.");
APPSPAWN_LOGV("SetUserId called with userId=%{public}s", userIdStr);
char *endPtr = nullptr;
errno = 0;
const int numberBase = 10;
long long tmpUserId = strtoll(userIdStr, &endPtr, numberBase);
APPSPAWN_CHECK(endPtr != userIdStr, return -1,
"Failed to convert userId string '%s': no valid digits", userIdStr);
APPSPAWN_CHECK(*endPtr == '\0', return -1,
"Failed to convert userId string '%s': extra characters '%s'", userIdStr, endPtr);
APPSPAWN_CHECK(errno != ERANGE, return -1,
"UserId string '%s' causes long integer overflow/underflow", userIdStr);
if (tmpUserId < 0 || tmpUserId > UINT32_MAX) {
APPSPAWN_LOGE("UserId value %lld from string '%s' is out of uint32_t range [0, %u]",
tmpUserId, userIdStr, UINT32_MAX);
return -1;
}
uint32_t userId = static_cast<uint32_t>(tmpUserId);
int fdIoctl = open(HDAC_DEV, O_WRONLY);
APPSPAWN_CHECK(fdIoctl >= 0, return -1,
"Failed to open %{public}s: %{public}s", HDAC_DEV, strerror(errno));
int rc = ioctl(fdIoctl, ACCESS_TOKENID_SET_USERID, &userId);
if (rc < 0) {
APPSPAWN_LOGE("Failed to set userId=%{public}u: %{public}s", userId, strerror(errno));
}
close(fdIoctl);
return rc;
}
int GetUserId(uint32_t *userId)
{
APPSPAWN_CHECK(userId != nullptr, return -1, "userId == nullptr.");
errno = 0;
int fdIoctl = open(HDAC_DEV, O_RDONLY);
APPSPAWN_CHECK(fdIoctl >= 0, return -1,
"Failed to open %{public}s: %{public}s", HDAC_DEV, strerror(errno));
uint32_t userIdTmp = 0;
int rc = ioctl(fdIoctl, ACCESS_TOKENID_GET_USERID, &userIdTmp);
if (rc < 0) {
close(fdIoctl);
APPSPAWN_LOGE("Failed to get userId: %{public}s", strerror(errno));
return -1;
}
close(fdIoctl);
*userId = userIdTmp;
return rc;
}
#ifdef __cplusplus
}
#endif
MODULE_CONSTRUCTOR(void)
{
const int32_t priority = HOOK_PRIO_COMMON + 2;
AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, priority, SpawnSetCustomSandboxEnv);
}