/* * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. * 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. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "securec.h" #include "basic.h" #include "ns.h" #include "u_mount.h" #include "cgrp.h" #include "options.h" #include "utils.h" #include "logger.h" #define DECIMAL 10 #define MAX_ARGC 1024 #define MAX_ARG_LEN 1024 bool g_allowLink = false; struct CmdArgs { char rootfs[BUF_SIZE]; long pid; char options[BUF_SIZE]; struct MountList files; struct MountList dirs; }; static struct option g_cmdOpts[] = { {"allow-link", required_argument, 0, 'l'}, {"devices", required_argument, 0, 'd'}, {"pid", required_argument, 0, 'p'}, {"rootfs", required_argument, 0, 'r'}, {"options", required_argument, 0, 'o'}, {"mount-file", required_argument, 0, 'f'}, {"mount-dir", required_argument, 0, 'i'}, {0, 0, 0, 0} }; typedef bool (*CmdArgParser)(struct CmdArgs *args, const char *arg); static bool PidCmdArgParser(struct CmdArgs *args, const char *arg) { char buff[PATH_MAX] = {0}; if (args == NULL || arg == NULL) { Logger("args, arg pointer is null!", LEVEL_ERROR, SCREEN_YES); return false; } args->pid = strtol(arg, NULL, DECIMAL); const char* pidMax = "/proc/sys/kernel/pid_max"; const size_t maxFileSzieMb = 10; // max 10MB if (!CheckExternalFile(pidMax, strlen(pidMax), maxFileSzieMb, true)) { Logger("failed to check pid_max path.", LEVEL_ERROR, SCREEN_YES); return false; } errno = 0; FILE* pFile = NULL; pFile = fopen(pidMax, "r"); if ((pFile == NULL) || (fgets(buff, PATH_MAX, pFile) == NULL)) { if (pFile != NULL) { (void)fclose(pFile); } pFile = NULL; Logger("failed to get pid_max buff.", LEVEL_ERROR, SCREEN_YES); return false; } (void)fclose(pFile); if ((strlen(buff) > 0) && (buff[strlen(buff) -1] == '\n')) { buff[strlen(buff) -1] = '\0'; } for (size_t iLoop = 0; iLoop < strlen(buff); iLoop++) { if (isdigit(buff[iLoop]) == 0) { Logger("failed to get pid_max value.", LEVEL_ERROR, SCREEN_YES); return false; } } if ((args->pid < 0) || (args->pid >= strtol(buff, NULL, DECIMAL))) { Logger("The PID out of bounds.", LEVEL_ERROR, SCREEN_YES); return false; } if (errno != 0) { char* str = FormatLogMessage("failed to convert pid string from cmd args, pid string: %s.", arg); Logger(str, LEVEL_ERROR, SCREEN_YES); free(str); return false; } return true; } static bool CheckFileLegality(const char* filePath, const size_t filePathLen, const size_t maxFileSzieMb) { if ((filePathLen > PATH_MAX) || (filePathLen <= 0)) { // 长度越界 Logger("filePathLen out of bounds!", LEVEL_ERROR, SCREEN_YES); return false; } for (size_t iLoop = 0; iLoop < filePathLen; iLoop++) { if (!IsValidChar(filePath[iLoop])) { // 非法字符 Logger("filePath has an illegal character!", LEVEL_ERROR, SCREEN_YES); return false; } } char resolvedPath[PATH_MAX] = {0}; if (realpath(filePath, resolvedPath) == NULL && errno != ENOENT) { Logger("realpath failed!", LEVEL_ERROR, SCREEN_YES); return false; } if (!g_allowLink && strcmp(resolvedPath, filePath) != 0) { // 存在软链接 Logger("filePath has a soft link!", LEVEL_ERROR, SCREEN_YES); return false; } return true; } static bool RootfsCmdArgParser(struct CmdArgs *args, const char *arg) { if (args == NULL || arg == NULL) { Logger("args, arg pointer is null!", LEVEL_ERROR, SCREEN_YES); return false; } errno_t err = strcpy_s(args->rootfs, BUF_SIZE, arg); if (err != EOK) { Logger("failed to get rootfs path from cmd args", LEVEL_ERROR, SCREEN_YES); return false; } const size_t maxFileSzieMb = 50; // max 50MB if (!CheckFileLegality(args->rootfs, strlen(args->rootfs), maxFileSzieMb)) { Logger("failed to check rootf.", LEVEL_ERROR, SCREEN_YES); return false; } return true; } static bool OptionsCmdArgParser(struct CmdArgs *args, const char *arg) { if (args == NULL || arg == NULL) { Logger("args, arg pointer is null!", LEVEL_ERROR, SCREEN_YES); return false; } errno_t err = strcpy_s(args->options, BUF_SIZE, arg); if (err != EOK) { Logger("failed to get options string from cmd args", LEVEL_ERROR, SCREEN_YES); return false; } if ((strcmp(args->options, "NODRV,VIRTUAL") != 0) && (strcmp(args->options, "NODRV") != 0) && (strcmp(args->options, "VIRTUAL") != 0)) { Logger("Whitelist check failed.", LEVEL_ERROR, SCREEN_YES); return false; } return true; } static bool CheckWhiteList(const char* fileName) { if (fileName == NULL) { return false; } bool fileExists = false; const char mountWhiteList[WHITE_LIST_NUM][PATH_MAX] = {{"/usr/local/Ascend/driver/lib64"}, {"/usr/local/Ascend/driver/include"}, {"/usr/local/dcmi"}, {"/usr/local/bin/npu-smi"}, {"/home/data/miniD/driver/lib64"}, {"/usr/local/sbin/npu-smi"}, {"/usr/local/Ascend/driver/tools"}, {"/etc/hdcBasic.cfg"}, {"/etc/sys_version.conf"}, {"/etc/ld.so.conf.d/mind_so.conf"}, {"/etc/slog.conf"}, {"/var/dmp_daemon"}, {"/var/slogd"}, {"/usr/lib64/libsemanage.so.2"}, {"/usr/lib64/libmmpa.so"}, {"/usr/lib64/libcrypto.so.1.1"}, {"/usr/lib64/libdrvdsmi.so"}, {"/usr/lib64/libdcmi.so"}, {"/usr/lib64/libstackcore.so"}, {"/usr/lib64/libmpi_dvpp_adapter.so"}, {"/usr/lib64/libaicpu_scheduler.so"}, {"/usr/lib64/libaicpu_processer.so"}, {"/usr/lib64/libaicpu_prof.so"}, {"/usr/lib64/libaicpu_sharder.so"}, {"/usr/lib64/libadump.so"}, {"/usr/lib64/libtsd_eventclient.so"}, {"/usr/lib64/aicpu_kernels"}, {"/usr/lib64/libyaml-0.so.2"} }; for (size_t iLoop = 0; iLoop < WHITE_LIST_NUM; iLoop++) { if (strcmp(mountWhiteList[iLoop], fileName) == 0) { fileExists = true; break; } } if (!fileExists) { char* str = FormatLogMessage("failed to check whiteList value: %s.", fileName); Logger(str, LEVEL_ERROR, SCREEN_YES); free(str); return false; } return true; } static bool MountFileCmdArgParser(struct CmdArgs *args, const char *arg) { if (args == NULL || arg == NULL) { Logger("args, arg pointer is null!", LEVEL_ERROR, SCREEN_YES); return false; } if (args->files.count >= MAX_MOUNT_NR) { char* str = FormatLogMessage("too many files to mount, max number is %u", MAX_MOUNT_NR); Logger(str, LEVEL_ERROR, SCREEN_YES); free(str); return false; } char *dst = &args->files.list[args->files.count++][0]; errno_t err = strcpy_s(dst, PATH_MAX, arg); if (err != EOK) { char* str = FormatLogMessage("failed to copy mount file path: %s", arg); Logger(str, LEVEL_ERROR, SCREEN_YES); free(str); return false; } const size_t maxFileSzieMb = 50; // max 50MB if (!CheckFileLegality(dst, strlen(dst), maxFileSzieMb)) { char* str = FormatLogMessage("failed to check files: %s", dst); Logger(str, LEVEL_ERROR, SCREEN_YES); free(str); return false; } return CheckWhiteList(dst) ? true : false; } static bool MountDirCmdArgParser(struct CmdArgs *args, const char *arg) { if (args == NULL || arg == NULL) { Logger("args, arg pointer is null!", LEVEL_ERROR, SCREEN_YES); return false; } if (args->dirs.count >= MAX_MOUNT_NR) { char* str = FormatLogMessage("too many directories to mount, max number is %u", MAX_MOUNT_NR); Logger(str, LEVEL_ERROR, SCREEN_YES); free(str); return false; } char *dst = &args->dirs.list[args->dirs.count++][0]; errno_t err = strcpy_s(dst, PATH_MAX, arg); if (err != EOK) { char* str = FormatLogMessage("error: failed to copy mount directory path: %s", arg); Logger(str, LEVEL_ERROR, SCREEN_YES); free(str); return false; } const size_t maxFileSzieMb = 50; // max 50MB if (!CheckFileLegality(dst, strlen(dst), maxFileSzieMb)) { Logger("failed to check dir.", LEVEL_ERROR, SCREEN_YES); return false; } return CheckWhiteList(dst) ? true : false; } static bool LinkCheckCmdArgParser(const char *argv) { if (argv == NULL) { Logger("link arg pointer is null!", LEVEL_ERROR, SCREEN_YES); return false; } if (strcmp(argv, "True") == 0) { g_allowLink = true; return true; } if (strcmp(argv, "False") == 0) { g_allowLink = false; return true; } Logger("invalid link check value!", LEVEL_ERROR, SCREEN_YES); return false; } #define NUM_OF_CMD_ARGS 6 static struct { const char c; CmdArgParser parser; } g_cmdArgParsers[NUM_OF_CMD_ARGS] = { {'l', LinkCheckCmdArgParser}, {'p', PidCmdArgParser}, {'r', RootfsCmdArgParser}, {'o', OptionsCmdArgParser}, {'f', MountFileCmdArgParser}, {'i', MountDirCmdArgParser} }; static int ParseOneCmdArg(struct CmdArgs *args, char indicator, const char *value) { if (args == NULL || value == NULL) { Logger("args, value pointer is null!", LEVEL_ERROR, SCREEN_YES); return -1; } int i; for (i = 0; i < NUM_OF_CMD_ARGS; i++) { if (g_cmdArgParsers[i].c == indicator) { break; } } bool isOK; if (i == 0) { isOK = LinkCheckCmdArgParser(value); } else { isOK = g_cmdArgParsers[i].parser(args, value); } if (!isOK) { char* str = FormatLogMessage("failed while parsing cmd arg, indicate char: %c, value: %s.", indicator, value); Logger(str, LEVEL_ERROR, SCREEN_YES); free(str); return -1; } return 0; } static inline bool IsCmdArgsValid(const struct CmdArgs *args) { if (args == NULL) { Logger("args pointer is null!", LEVEL_ERROR, SCREEN_YES); return false; } return (strlen(args->rootfs) > 0) && (args->pid > 0); } int DoPrepare(const struct CmdArgs *args, struct ParsedConfig *config) { if (args == NULL || config == NULL) { Logger("args, config pointer is null!", LEVEL_ERROR, SCREEN_YES); return -1; } int ret; errno_t err; err = strcpy_s(config->rootfs, BUF_SIZE, args->rootfs); if (err != EOK) { Logger("failed to copy rootfs path to parsed config.", LEVEL_ERROR, SCREEN_YES); return -1; } ret = GetNsPath(args->pid, "mnt", config->containerNsPath, BUF_SIZE); if (ret < 0) { char* str = FormatLogMessage("failed to get container mnt ns path: pid(%d).", args->pid); Logger(str, LEVEL_ERROR, SCREEN_YES); free(str); return -1; } char originNsPath[BUF_SIZE] = {0}; ret = GetSelfNsPath("mnt", originNsPath, BUF_SIZE); if (ret < 0) { Logger("failed to get self ns path.", LEVEL_ERROR, SCREEN_YES); return -1; } config->originNsFd = open((const char *)originNsPath, O_RDONLY); // proc接口,非外部输入 if (config->originNsFd < 0) { char* str = FormatLogMessage("failed to get self ns fd: %s.", originNsPath); Logger(str, LEVEL_ERROR, SCREEN_YES); free(str); return -1; } config->files = (const struct MountList *)&args->files; config->dirs = (const struct MountList *)&args->dirs; return 0; } int SetupContainer(struct CmdArgs *args) { if (args == NULL) { Logger("args pointer is null!", LEVEL_ERROR, SCREEN_YES); return -1; } int ret; struct ParsedConfig config; Logger("prepare necessary config", LEVEL_INFO, SCREEN_YES); ret = DoPrepare(args, &config); if (ret < 0) { Logger("failed to prepare nesessary config.", LEVEL_ERROR, SCREEN_YES); return -1; } // enter container's mount namespace Logger("enter container's mount namespace", LEVEL_INFO, SCREEN_YES); ret = EnterNsByPath((const char *)config.containerNsPath, CLONE_NEWNS); if (ret < 0) { char* str = FormatLogMessage("failed to set to container ns: %s.", config.containerNsPath); Logger(str, LEVEL_ERROR, SCREEN_YES); free(str); close(config.originNsFd); return -1; } Logger("do mounting", LEVEL_INFO, SCREEN_YES); ret = DoMounting(&config); if (ret < 0) { Logger("failed to do mounting.", LEVEL_ERROR, SCREEN_YES); close(config.originNsFd); return -1; } // back to original namespace Logger("back to original namespace", LEVEL_INFO, SCREEN_YES); ret = EnterNsByFd(config.originNsFd, CLONE_NEWNS); if (ret < 0) { Logger("failed to set ns back.", LEVEL_ERROR, SCREEN_YES); close(config.originNsFd); return -1; } close(config.originNsFd); return 0; } int Process(int argc, char **argv) { if (argv == NULL) { Logger("argv pointer is null!", LEVEL_ERROR, SCREEN_YES); return -1; } if (argc > MAX_ARGC) { Logger("too many arguments!", LEVEL_ERROR, SCREEN_YES); return -1; } int c; int ret; struct CmdArgs args = {0}; Logger("runc start prestart-hook ...", LEVEL_INFO, SCREEN_YES); while ((c = getopt_long(argc, argv, "l:p:r:o:f:i", g_cmdOpts, NULL)) != -1) { ret = ParseOneCmdArg(&args, (char)c, optarg); if (ret < 0) { Logger("failed to parse cmd args.", LEVEL_ERROR, SCREEN_YES); return -1; } } Logger("verify parameters valid and parse runtime options", LEVEL_INFO, SCREEN_YES); if (!IsCmdArgsValid(&args)) { Logger("information not completed or valid.", LEVEL_ERROR, SCREEN_YES); return -1; } ParseRuntimeOptions(args.options); Logger("setup container config ...", LEVEL_INFO, SCREEN_YES); ret = SetupContainer(&args); if (ret < 0) { Logger("failed to setup container.", LEVEL_ERROR, SCREEN_YES); return ret; } Logger("prestart-hook setup container successful.", LEVEL_INFO, SCREEN_YES); return 0; } #ifdef gtest int _main(int argc, char **argv) { #else int main(int argc, char **argv) { #endif int ret = Process(argc, argv); return ret; }