1285 lines
34 KiB
C
1285 lines
34 KiB
C
/******************************************************************************
|
|
* Copyright (c) Huawei Technologies Co., Ltd. 2017-2019. All rights reserved.
|
|
* iSulad licensed under the Mulan PSL v1.
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v1.
|
|
* You may obtain a copy of Mulan PSL v1 at:
|
|
* http://license.coscl.org.cn/MulanPSL
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
* PURPOSE.
|
|
* See the Mulan PSL v1 for more details.
|
|
* Author: tanyifeng
|
|
* Create: 2017-11-22
|
|
* Description: provide container specs functions
|
|
******************************************************************************/
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <stdbool.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
#include <sys/utsname.h>
|
|
#include <sched.h>
|
|
#include <ctype.h>
|
|
|
|
#include "error.h"
|
|
#include "log.h"
|
|
#include "oci_runtime_spec.h"
|
|
#include "host_config.h"
|
|
#include "container_custom_config.h"
|
|
#include "utils.h"
|
|
#include "config.h"
|
|
#include "path.h"
|
|
#include "lcrd_config.h"
|
|
#include "specs_extend.h"
|
|
|
|
#define MINUID 0
|
|
#define MAXUID (((1LL << 31) - 1))
|
|
#define DEFAULT_UID 0
|
|
|
|
#define UnixPasswdPath "/etc/passwd"
|
|
#define UnixGroupPath "/etc/group"
|
|
|
|
#define MERGE_HOOKS_ITEM_DEF(item) \
|
|
int merge_##item##_conf(oci_runtime_spec_hooks *dest, oci_runtime_spec_hooks * src) \
|
|
{\
|
|
size_t old_size = 0; \
|
|
size_t new_size = 0; \
|
|
int ret = 0; \
|
|
size_t i = 0; \
|
|
if (src->item##_len) { \
|
|
defs_hook **item = NULL; \
|
|
if (dest->item##_len > (LIST_SIZE_MAX - src->item##_len) - 1) { \
|
|
ERROR("the length of item element is too long!"); \
|
|
ret = -1; \
|
|
goto out; \
|
|
} \
|
|
old_size = dest->item##_len * sizeof(defs_hook *); \
|
|
new_size = (dest->item##_len + src->item##_len + 1) * sizeof(defs_hook *); \
|
|
ret = mem_realloc((void **)&(item), new_size, dest->item, old_size); \
|
|
if (ret != 0) { \
|
|
ERROR("Failed to realloc memory for hooks_"#item" variables"); \
|
|
ret = -1; \
|
|
goto out; \
|
|
}\
|
|
dest->item = item; \
|
|
for (; i < src->item##_len; i++) { \
|
|
dest->item[dest->item##_len] = src->item[i]; \
|
|
dest->item##_len++; \
|
|
src->item[i] = NULL; \
|
|
} \
|
|
src->item##_len = 0; \
|
|
free(src->item); \
|
|
src->item = NULL; \
|
|
} \
|
|
out: \
|
|
return ret; \
|
|
}
|
|
|
|
MERGE_HOOKS_ITEM_DEF(prestart)
|
|
MERGE_HOOKS_ITEM_DEF(poststart)
|
|
MERGE_HOOKS_ITEM_DEF(poststop)
|
|
|
|
|
|
int merge_hooks(oci_runtime_spec_hooks *dest, oci_runtime_spec_hooks *src)
|
|
{
|
|
if (merge_prestart_conf(dest, src) || merge_poststart_conf(dest, src) || merge_poststop_conf(dest, src)) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int merge_global_hook(oci_runtime_spec *oci_spec)
|
|
{
|
|
int ret = 0;
|
|
oci_runtime_spec_hooks *hooks = NULL;
|
|
oci_runtime_spec_hooks *tmp = NULL;
|
|
|
|
if (conf_get_lcrd_hooks(&hooks)) {
|
|
ERROR("Failed to get lcrd hooks");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (oci_spec->hooks != NULL) {
|
|
if (hooks != NULL) {
|
|
if (merge_hooks(hooks, oci_spec->hooks)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
tmp = hooks;
|
|
hooks = oci_spec->hooks;
|
|
oci_spec->hooks = tmp;
|
|
}
|
|
} else {
|
|
oci_spec->hooks = hooks;
|
|
hooks = NULL;
|
|
}
|
|
out:
|
|
free_oci_runtime_spec_hooks(hooks);
|
|
return ret;
|
|
}
|
|
|
|
static int make_one_id_mapping(defs_id_mapping ***mappings, unsigned int id, unsigned int size)
|
|
{
|
|
*mappings = util_common_calloc_s(sizeof(defs_id_mapping *));
|
|
if (*mappings == NULL) {
|
|
return -1;
|
|
}
|
|
(*mappings)[0] = util_common_calloc_s(sizeof(defs_id_mapping));
|
|
if ((*mappings)[0] == NULL) {
|
|
return -1;
|
|
}
|
|
(*mappings)[0]->host_id = id;
|
|
(*mappings)[0]->container_id = 0;
|
|
(*mappings)[0]->size = size;
|
|
return 0;
|
|
}
|
|
|
|
static int make_linux_uid_gid_mappings(oci_runtime_spec *container,
|
|
unsigned int host_uid,
|
|
unsigned int host_gid,
|
|
unsigned int size)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = make_sure_oci_spec_linux(container);
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
|
|
if (container->linux->uid_mappings == NULL) {
|
|
ret = make_one_id_mapping(&(container->linux->uid_mappings), host_uid, size);
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
container->linux->uid_mappings_len++;
|
|
}
|
|
if (container->linux->gid_mappings == NULL) {
|
|
ret = make_one_id_mapping(&(container->linux->gid_mappings), host_gid, size);
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
container->linux->gid_mappings_len++;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int make_userns_remap(oci_runtime_spec *container, const char *user_remap)
|
|
{
|
|
int ret = 0;
|
|
unsigned int host_uid = 0;
|
|
unsigned int host_gid = 0;
|
|
unsigned int size = 0;
|
|
|
|
if (user_remap == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
ret = util_parse_user_remap(user_remap, &host_uid, &host_gid, &size);
|
|
if (ret) {
|
|
ERROR("User remap '%s' format error", user_remap);
|
|
return ret;
|
|
}
|
|
if (host_uid == 0 && host_gid == 0) {
|
|
return 0;
|
|
}
|
|
ret = make_linux_uid_gid_mappings(container, host_uid, host_gid, size);
|
|
if (ret) {
|
|
ERROR("Make linux uid and gid mappings failed");
|
|
return ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int generate_env_map_from_file(FILE *fp, json_map_string_string *env_map)
|
|
{
|
|
int ret = 0;
|
|
char *key = NULL;
|
|
char *value = NULL;
|
|
char *pline = NULL;
|
|
size_t length = 0;
|
|
char *saveptr = NULL;
|
|
|
|
while (getline(&pline, &length, fp) != -1) {
|
|
util_trim_newline(pline);
|
|
pline = util_trim_space(pline);
|
|
if (pline == NULL || pline[0] == '#') {
|
|
continue;
|
|
}
|
|
key = strtok_r(pline, "=", &saveptr);
|
|
value = strtok_r(NULL, "=", &saveptr);
|
|
if (key != NULL && value != NULL) {
|
|
key = util_trim_space(key);
|
|
value = util_trim_space(value);
|
|
if ((size_t)(MAX_BUFFER_SIZE - 1) - strlen(key) < strlen(value)) {
|
|
ERROR("env length exceed %lld bytes", MAX_BUFFER_SIZE);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
ret = append_json_map_string_string(env_map, key, value);
|
|
if (ret < 0) {
|
|
ERROR("append env to map failed");
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
out:
|
|
free(pline);
|
|
return ret;
|
|
}
|
|
|
|
static json_map_string_string *parse_env_target_file(const char *env_path)
|
|
{
|
|
int ret = 0;
|
|
FILE *fp = NULL;
|
|
json_map_string_string *env_map = (json_map_string_string *)util_common_calloc_s(sizeof(json_map_string_string));
|
|
|
|
if (env_map == NULL) {
|
|
ERROR("Out of memory");
|
|
return NULL;
|
|
}
|
|
fp = util_fopen(env_path, "r");
|
|
if (fp == NULL) {
|
|
SYSERROR("Failed to open env target file '%s'", env_path);
|
|
goto out;
|
|
}
|
|
ret = generate_env_map_from_file(fp, env_map);
|
|
if (ret != 0) {
|
|
ERROR("Failed to generate env map from file");
|
|
goto out;
|
|
}
|
|
fclose(fp);
|
|
return env_map;
|
|
out:
|
|
if (fp != NULL) {
|
|
fclose(fp);
|
|
}
|
|
free_json_map_string_string(env_map);
|
|
return NULL;
|
|
}
|
|
|
|
static int do_append_env(char ***env, size_t *env_len, const char *key, const char *value)
|
|
{
|
|
char *tmp_env = NULL;
|
|
size_t tmp_env_len = 0;
|
|
|
|
if (strlen(value) > ((SIZE_MAX - 2) - strlen(key))) {
|
|
ERROR("env value length too big");
|
|
return -1;
|
|
}
|
|
|
|
tmp_env_len = strlen(key) + strlen(value) + 2;
|
|
|
|
tmp_env = util_common_calloc_s(tmp_env_len);
|
|
if (tmp_env == NULL) {
|
|
ERROR("Out of memory");
|
|
return -1;
|
|
}
|
|
int nret = snprintf(tmp_env, tmp_env_len, "%s=%s", key, value);
|
|
if (nret < 0 || (size_t)nret >= tmp_env_len) {
|
|
ERROR("Out of memory");
|
|
free(tmp_env);
|
|
return -1;
|
|
}
|
|
if (util_array_append(env, tmp_env) < 0) {
|
|
ERROR("Failed to append env");
|
|
free(tmp_env);
|
|
return -1;
|
|
}
|
|
free(tmp_env);
|
|
(*env_len)++;
|
|
return 0;
|
|
}
|
|
|
|
static int check_env_need_append(const oci_runtime_spec *oci_spec, const char *env_key, bool *is_append)
|
|
{
|
|
size_t i = 0;
|
|
char *key = NULL;
|
|
char *value = NULL;
|
|
char *saveptr = NULL;
|
|
|
|
for (i = 0; i < oci_spec->process->env_len; i++) {
|
|
char *tmp_env = NULL;
|
|
tmp_env = util_strdup_s(oci_spec->process->env[i]);
|
|
key = strtok_r(tmp_env, "=", &saveptr);
|
|
value = strtok_r(NULL, "=", &saveptr);
|
|
if (key == NULL || value == NULL) {
|
|
ERROR("Bad env format");
|
|
free(tmp_env);
|
|
tmp_env = NULL;
|
|
return -1;
|
|
}
|
|
if (strcmp(key, env_key) == 0) {
|
|
*is_append = false;
|
|
free(tmp_env);
|
|
tmp_env = NULL;
|
|
return 0;
|
|
}
|
|
free(tmp_env);
|
|
tmp_env = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int do_merge_env_target(oci_runtime_spec *oci_spec, const json_map_string_string *env_map)
|
|
{
|
|
int ret = 0;
|
|
size_t i = 0;
|
|
char **env = NULL;
|
|
size_t env_len = 0;
|
|
|
|
env = (char **)util_common_calloc_s(sizeof(char *));
|
|
if (env == NULL) {
|
|
ERROR("Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < env_map->len; i++) {
|
|
bool is_append = true;
|
|
ret = check_env_need_append(oci_spec, env_map->keys[i], &is_append);
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
if (!is_append) {
|
|
continue;
|
|
}
|
|
if (do_append_env(&env, &env_len,
|
|
(const char *)env_map->keys[i],
|
|
(const char *)env_map->values[i]) < 0) {
|
|
ERROR("Failed to append env");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
ret = merge_env(oci_spec, (const char **)env, env_len);
|
|
out:
|
|
util_free_array(env);
|
|
return ret;
|
|
}
|
|
|
|
static char *get_env_abs_file_path(const oci_runtime_spec *oci_spec, const char *env_target_file)
|
|
{
|
|
char *env_path = NULL;
|
|
int64_t file_size = 0;
|
|
|
|
if (oci_spec->root == NULL || oci_spec->root->path == NULL) {
|
|
return NULL;
|
|
}
|
|
if (realpath_in_scope(oci_spec->root->path, env_target_file, &env_path) < 0) {
|
|
ERROR("env target file '%s' real path must be under rootfs '%s'", env_target_file, oci_spec->root->path);
|
|
goto out;
|
|
}
|
|
if (!util_file_exists(env_path)) {
|
|
return env_path;
|
|
}
|
|
file_size = util_file_size(env_path);
|
|
if (file_size > REGULAR_FILE_SIZE) {
|
|
ERROR("env target file %s, size exceed limit: %lld", env_target_file, REGULAR_FILE_SIZE);
|
|
goto out;
|
|
}
|
|
return env_path;
|
|
out:
|
|
free(env_path);
|
|
return NULL;
|
|
}
|
|
|
|
int merge_env_target_file(oci_runtime_spec *oci_spec, const char *env_target_file)
|
|
{
|
|
int ret = 0;
|
|
char *env_path = NULL;
|
|
json_map_string_string *env_map = NULL;
|
|
|
|
if (env_target_file == NULL) {
|
|
return 0;
|
|
}
|
|
env_path = get_env_abs_file_path(oci_spec, env_target_file);
|
|
if (env_path == NULL) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (!util_file_exists(env_path)) {
|
|
goto out;
|
|
}
|
|
env_map = parse_env_target_file(env_path);
|
|
if (env_map == NULL) {
|
|
ERROR("Failed to parse env target file");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
ret = do_merge_env_target(oci_spec, (const json_map_string_string *)env_map);
|
|
if (ret != 0) {
|
|
ERROR("Failed to merge env target file");
|
|
goto out;
|
|
}
|
|
out:
|
|
free(env_path);
|
|
free_json_map_string_string(env_map);
|
|
return ret;
|
|
}
|
|
|
|
int merge_env(oci_runtime_spec *oci_spec, const char **env, size_t env_len)
|
|
{
|
|
int ret = 0;
|
|
size_t new_size = 0;
|
|
size_t old_size = 0;
|
|
size_t i = 0;
|
|
char **temp = NULL;
|
|
|
|
if (env_len == 0 || env == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
ret = make_sure_oci_spec_process(oci_spec);
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
|
|
if (env_len > LIST_ENV_SIZE_MAX - oci_spec->process->env_len) {
|
|
ERROR("The length of envionment variables is too long, the limit is %d", LIST_ENV_SIZE_MAX);
|
|
lcrd_set_error_message("The length of envionment variables is too long, the limit is %d", LIST_ENV_SIZE_MAX);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
new_size = (oci_spec->process->env_len + env_len) * sizeof(char *);
|
|
old_size = oci_spec->process->env_len * sizeof(char *);
|
|
ret = mem_realloc((void **)&temp, new_size, oci_spec->process->env, old_size);
|
|
if (ret != 0) {
|
|
ERROR("Failed to realloc memory for envionment variables");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
oci_spec->process->env = temp;
|
|
for (i = 0; i < env_len; i++) {
|
|
oci_spec->process->env[oci_spec->process->env_len] = util_strdup_s(env[i]);
|
|
oci_spec->process->env_len++;
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int read_user_file(const char *basefs, const char *user_path, FILE **stream)
|
|
{
|
|
int ret = 0;
|
|
int64_t filesize = 0;
|
|
char *real_path = NULL;
|
|
|
|
if (realpath_in_scope(basefs, user_path, &real_path) < 0) {
|
|
ERROR("user target file '%s' real path must be under '%s'", user_path, basefs);
|
|
lcrd_set_error_message("user target file '%s' real path must be under '%s'", user_path, basefs);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
filesize = util_file_size(real_path);
|
|
if (filesize > REGULAR_FILE_SIZE) {
|
|
ERROR("File %s is more than %lld", real_path, (long long)REGULAR_FILE_SIZE);
|
|
lcrd_set_error_message("File %s is more than %lld", real_path, (long long)REGULAR_FILE_SIZE);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
*stream = util_fopen(real_path, "r");
|
|
if (*stream == NULL) {
|
|
ERROR("Failed to open %s: %s", real_path, strerror(errno));
|
|
ret = 0;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
free(real_path);
|
|
return ret;
|
|
}
|
|
|
|
static void parse_user_group(const char *username, char **user, char **group, char **tmp_dup)
|
|
{
|
|
char *tmp = NULL;
|
|
char *pdot = NULL;
|
|
|
|
if (user == NULL || group == NULL || tmp_dup == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (username != NULL) {
|
|
tmp = util_strdup_s(username);
|
|
|
|
// for free tmp in caller
|
|
*tmp_dup = tmp;
|
|
|
|
pdot = strstr(tmp, ":");
|
|
if (pdot != NULL) {
|
|
*pdot = '\0';
|
|
if (pdot != tmp) {
|
|
// User found
|
|
*user = tmp;
|
|
}
|
|
if (*(pdot + 1) != '\0') {
|
|
// group found
|
|
*group = pdot + 1;
|
|
}
|
|
} else {
|
|
// No : found
|
|
if (*tmp != '\0') {
|
|
*user = tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void uids_gids_range_err_log()
|
|
{
|
|
ERROR("uids and gids must be in range 0-%d", MAXUID);
|
|
lcrd_set_error_message("uids and gids must be in range 0-%d", MAXUID);
|
|
return;
|
|
}
|
|
|
|
static bool b_user_found(const char *user, const struct passwd *pwbufp)
|
|
{
|
|
int uret = -1;
|
|
long long n_user = 0;
|
|
bool userfound = false;
|
|
|
|
if (pwbufp == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (user != NULL) {
|
|
uret = util_safe_llong(user, &n_user);
|
|
}
|
|
|
|
if (user == NULL && pwbufp->pw_uid == DEFAULT_UID) {
|
|
userfound = true;
|
|
}
|
|
// Treat numeric usename as valid UID
|
|
if (uret == 0 && (long long)pwbufp->pw_uid == n_user) {
|
|
userfound = true;
|
|
}
|
|
if (uret != 0 && user != NULL && strcmp(user, pwbufp->pw_name) == 0) {
|
|
userfound = true;
|
|
}
|
|
|
|
return userfound;
|
|
}
|
|
|
|
|
|
static int proc_by_fpasswd(FILE *f_passwd, const char *user, oci_runtime_spec_process_user *puser,
|
|
char **matched_username)
|
|
{
|
|
int ret = 0;
|
|
int errval = 0;
|
|
int uret = -1;
|
|
bool userfound = false;
|
|
long long n_user = 0;
|
|
char buf[BUFSIZ];
|
|
struct passwd pw, *pwbufp = NULL;
|
|
|
|
if (f_passwd != NULL) {
|
|
errval = fgetpwent_r(f_passwd, &pw, buf, sizeof(buf), &pwbufp);
|
|
|
|
while (errval == 0 && pwbufp != NULL) {
|
|
userfound = b_user_found(user, pwbufp);
|
|
// Take the first match as valid user
|
|
if (userfound) {
|
|
free(puser->username);
|
|
puser->username = util_strdup_s(pwbufp->pw_name);
|
|
puser->uid = pwbufp->pw_uid;
|
|
puser->gid = pwbufp->pw_gid;
|
|
*matched_username = puser->username;
|
|
break;
|
|
}
|
|
errval = fgetpwent_r(f_passwd, &pw, buf, sizeof(buf), &pwbufp);
|
|
}
|
|
}
|
|
|
|
if (errval != 0 && errval != ENOENT) {
|
|
ERROR("Failed to parse passwd file: Insufficient buffer space supplied");
|
|
lcrd_set_error_message("Failed to parse passwd file: Insufficient buffer space supplied");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (!userfound && user != NULL) {
|
|
uret = util_safe_llong(user, &n_user);
|
|
// user is not a valid numeric UID
|
|
if (uret != 0) {
|
|
ERROR("Unable to find user '%s'", user);
|
|
lcrd_set_error_message("Unable to find user '%s': no matching entries in passwd file", user);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (n_user < MINUID || n_user > MAXUID) {
|
|
uids_gids_range_err_log();
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
puser->uid = (uid_t)n_user;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int append_additional_gids(gid_t gid, gid_t **additional_gids, size_t *len)
|
|
{
|
|
int ret = 0;
|
|
size_t new_len = 0;
|
|
size_t i;
|
|
gid_t *new_gids = NULL;
|
|
|
|
if (*len > (SIZE_MAX / sizeof(gid_t)) - 1) {
|
|
ERROR("Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
new_len = *len + 1;
|
|
|
|
for (i = 0; i < *len; i++) {
|
|
if ((*additional_gids)[i] == gid) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ret = mem_realloc((void **)&new_gids, new_len * sizeof(gid_t), *additional_gids, (*len) * sizeof(gid_t));
|
|
if (ret != 0) {
|
|
ERROR("Out of memory");
|
|
return -1;
|
|
}
|
|
*additional_gids = new_gids;
|
|
(*additional_gids)[*len] = gid;
|
|
*len = new_len;
|
|
return 0;
|
|
}
|
|
|
|
static int search_group_list(struct group *gbufp, const char *username, oci_runtime_spec_process_user *puser)
|
|
{
|
|
char **username_list = gbufp->gr_mem;
|
|
while (username_list != NULL && *username_list != NULL) {
|
|
if (strcmp(*username_list, username) == 0) {
|
|
if (append_additional_gids(gbufp->gr_gid, &puser->additional_gids, &puser->additional_gids_len)) {
|
|
ERROR("Failed to append additional groups");
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
username_list++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool check_group_found(const char *group, const struct group *gbufp)
|
|
{
|
|
int gret = -1;
|
|
long long n_grp = 0;
|
|
|
|
if (group != NULL) {
|
|
gret = util_safe_llong(group, &n_grp);
|
|
}
|
|
|
|
if (gret == 0 && n_grp == (long long)gbufp->gr_gid) {
|
|
return true;
|
|
}
|
|
if (gret != 0 && group != NULL && strcmp(group, gbufp->gr_name) == 0) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int do_proc_by_froup(FILE *f_group, const char *group, oci_runtime_spec_process_user *puser,
|
|
const char *matched_username, int *groupcnt)
|
|
{
|
|
int errval = 0;
|
|
char buf[BUFSIZ] = { 0 };
|
|
bool groupfound = false;
|
|
struct group grp, *gbufp = NULL;
|
|
|
|
if (f_group == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
errval = fgetgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp);
|
|
while (errval == 0 && gbufp != NULL) {
|
|
// Treat numeric group as valid GID
|
|
if (group == NULL) {
|
|
if (search_group_list(gbufp, matched_username, puser) != 0) {
|
|
return -1;
|
|
}
|
|
errval = fgetgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp);
|
|
continue;
|
|
}
|
|
|
|
groupfound = check_group_found(group, gbufp);
|
|
if (groupfound && *groupcnt != 1) {
|
|
// Continue search group list, but only take first found group
|
|
puser->gid = gbufp->gr_gid;
|
|
*groupcnt = 1;
|
|
}
|
|
errval = fgetgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int proc_by_fgroup(FILE *f_group, const char *group, oci_runtime_spec_process_user *puser,
|
|
const char *matched_username)
|
|
{
|
|
int ret = 0;
|
|
int gret = -1;
|
|
int groupcnt = 0;
|
|
long long n_grp = 0;
|
|
|
|
if (group != NULL || matched_username != NULL) {
|
|
if (do_proc_by_froup(f_group, group, puser, matched_username, &groupcnt) != 0) {
|
|
goto out;
|
|
}
|
|
|
|
if (group != NULL && groupcnt == 0) {
|
|
gret = util_safe_llong(group, &n_grp);
|
|
// group is not a valid numeric GID
|
|
if (gret != 0) {
|
|
ERROR("Unable to find group '%s'", group);
|
|
lcrd_set_error_message("Unable to find group '%s': no matching entries in group file", group);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (n_grp < MINUID || n_grp > MAXUID) {
|
|
uids_gids_range_err_log();
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
puser->gid = (gid_t)n_grp;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int get_exec_user(const char *username, FILE *f_passwd, FILE *f_group, oci_runtime_spec_process_user *puser)
|
|
{
|
|
int ret = 0;
|
|
char *tmp = NULL;
|
|
char *user = NULL;
|
|
char *group = NULL;
|
|
char *matched_username = NULL;
|
|
|
|
// parse user and group by username
|
|
parse_user_group(username, &user, &group, &tmp);
|
|
|
|
// proc by f_passwd
|
|
ret = proc_by_fpasswd(f_passwd, user, puser, &matched_username);
|
|
if (ret != 0) {
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
// proc by f_group
|
|
ret = proc_by_fgroup(f_group, group, puser, matched_username);
|
|
if (ret != 0) {
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
free(tmp);
|
|
return ret;
|
|
}
|
|
|
|
static int append_additional_groups(const struct group *grp, struct group **groups, size_t *len)
|
|
{
|
|
int ret = 0;
|
|
struct group *new_groups = NULL;
|
|
size_t new_len = *len + 1;
|
|
|
|
ret = mem_realloc((void **)&new_groups, new_len * sizeof(struct group), *groups, (*len) * sizeof(struct group));
|
|
if (ret != 0) {
|
|
ERROR("Out of memory");
|
|
return -1;
|
|
}
|
|
*groups = new_groups;
|
|
(*groups)[*len].gr_name = util_strdup_s(grp->gr_name);
|
|
(*groups)[*len].gr_gid = grp->gr_gid;
|
|
*len = new_len;
|
|
return 0;
|
|
}
|
|
|
|
static bool group_matched(const char *group, const struct group *gbufp)
|
|
{
|
|
bool matched = false;
|
|
long long n_gid = 0;
|
|
int gret = -1;
|
|
|
|
if (group == NULL || gbufp == NULL) {
|
|
return false;
|
|
}
|
|
|
|
gret = util_safe_llong(group, &n_gid);
|
|
if (strcmp(group, gbufp->gr_name) == 0 || (gret == 0 && n_gid == gbufp->gr_gid)) {
|
|
matched = true;
|
|
}
|
|
|
|
return matched;
|
|
}
|
|
|
|
static int get_one_additional_group(const char *additional_group, struct group *groups, size_t groups_len,
|
|
oci_runtime_spec_process_user *puser)
|
|
{
|
|
int ret = 0;
|
|
int gret = -1;
|
|
long long n_gid = 0;
|
|
bool found = false;
|
|
size_t j;
|
|
|
|
for (j = 0; groups != NULL && j < groups_len; j++) {
|
|
// Only take the first founded group
|
|
if (group_matched(additional_group, &groups[j])) {
|
|
found = true;
|
|
if (append_additional_gids(groups[j].gr_gid, &puser->additional_gids, &puser->additional_gids_len)) {
|
|
ERROR("Failed to append additional groups");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
gret = util_safe_llong(additional_group, &n_gid);
|
|
if (gret != 0) {
|
|
ERROR("Unable to find group %s", additional_group);
|
|
lcrd_set_error_message("Unable to find group %s", additional_group);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (n_gid < MINUID || n_gid > MAXUID) {
|
|
uids_gids_range_err_log();
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (append_additional_gids((gid_t)n_gid, &puser->additional_gids, &puser->additional_gids_len)) {
|
|
ERROR("Failed to append additional groups");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
|
|
int get_additional_groups(char **additional_groups, size_t additional_groups_len,
|
|
FILE *f_group, oci_runtime_spec_process_user *puser)
|
|
{
|
|
int ret = 0;
|
|
size_t i;
|
|
size_t groups_len = 0;
|
|
char buf[BUFSIZ] = { 0 };
|
|
struct group grp;
|
|
struct group *gbufp = NULL;
|
|
struct group *groups = NULL;
|
|
|
|
while (f_group != NULL && fgetgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp) == 0) {
|
|
for (i = 0; i < additional_groups_len; i++) {
|
|
if (!group_matched(additional_groups[i], gbufp)) {
|
|
continue;
|
|
}
|
|
if (append_additional_groups(gbufp, &groups, &groups_len)) {
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < additional_groups_len; i++) {
|
|
ret = get_one_additional_group(additional_groups[i], groups, groups_len, puser);
|
|
if (ret != 0) {
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
cleanup:
|
|
for (i = 0; groups != NULL && i < groups_len; i++) {
|
|
free(groups[i].gr_name);
|
|
}
|
|
free(groups);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int resolve_basefs(const char *basefs, char **resolved_basefs)
|
|
{
|
|
struct stat s;
|
|
char real_path[PATH_MAX + 1] = { 0 };
|
|
|
|
if (strlen(basefs) > PATH_MAX || !realpath(basefs, real_path)) {
|
|
ERROR("invalid file path %s", basefs);
|
|
return -1;
|
|
}
|
|
|
|
if (stat(real_path, &s) < 0) {
|
|
ERROR("stat failed, error: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if ((s.st_mode & S_IFMT) == S_IFDIR) {
|
|
*resolved_basefs = util_strdup_s(real_path);
|
|
} else {
|
|
*resolved_basefs = util_strdup_s("/");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int get_user(const char *basefs, const host_config *hc, const char *userstr, oci_runtime_spec_process_user *puser)
|
|
{
|
|
int ret = 0;
|
|
FILE *f_passwd = NULL;
|
|
FILE *f_group = NULL;
|
|
char *resolved_basefs = NULL;
|
|
|
|
if (basefs == NULL || puser == NULL || hc == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
ret = resolve_basefs(basefs, &resolved_basefs);
|
|
if (ret != 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = read_user_file(resolved_basefs, UnixPasswdPath, &f_passwd);
|
|
if (ret != 0) {
|
|
goto cleanup;
|
|
}
|
|
ret = read_user_file(resolved_basefs, UnixGroupPath, &f_group);
|
|
if (ret != 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = get_exec_user(userstr, f_passwd, f_group, puser);
|
|
if (ret != 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (hc->group_add != NULL && hc->group_add_len > 0) {
|
|
if (hc->group_add_len > LIST_SIZE_MAX) {
|
|
ERROR("Too many groups to add, the limit is %d", LIST_SIZE_MAX);
|
|
lcrd_set_error_message("Too many groups to add, the limit is %d", LIST_SIZE_MAX);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
// Rewind f_group to serach from beginning again.
|
|
if (f_group != NULL) {
|
|
rewind(f_group);
|
|
}
|
|
ret = get_additional_groups(hc->group_add, hc->group_add_len, f_group, puser);
|
|
if (ret != 0) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
if (f_passwd != NULL) {
|
|
fclose(f_passwd);
|
|
}
|
|
if (f_group != NULL) {
|
|
fclose(f_group);
|
|
}
|
|
|
|
free(resolved_basefs);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int make_sure_oci_spec_porcess_user(oci_runtime_spec *oci_spec)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = make_sure_oci_spec_process(oci_spec);
|
|
if (ret < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (oci_spec->process->user == NULL) {
|
|
oci_spec->process->user = util_common_calloc_s(sizeof(oci_runtime_spec_process_user));
|
|
if (oci_spec->process->user == NULL) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int merge_user(const char *basefs, oci_runtime_spec *oci_spec, const host_config *hc, const char *user)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = make_sure_oci_spec_porcess_user(oci_spec);
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
|
|
if (get_user(basefs, hc, user, oci_spec->process->user)) {
|
|
ERROR("Failed to get user with '%s'", user ? user : "");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
char *oci_container_get_env(const oci_runtime_spec *oci_spec, const char *key)
|
|
{
|
|
const oci_runtime_spec_process *op = NULL;
|
|
|
|
if (oci_spec == NULL) {
|
|
ERROR("nil oci_spec");
|
|
return NULL;
|
|
}
|
|
if (oci_spec->process == NULL) {
|
|
ERROR("nil oci_spec->process");
|
|
return NULL;
|
|
}
|
|
|
|
op = oci_spec->process;
|
|
return util_env_get_val(op->env, op->env_len, key, strlen(key));
|
|
}
|
|
|
|
int make_sure_oci_spec_linux(oci_runtime_spec *oci_spec)
|
|
{
|
|
if (oci_spec->linux == NULL) {
|
|
oci_spec->linux = util_common_calloc_s(sizeof(oci_runtime_config_linux));
|
|
if (oci_spec->linux == NULL) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int make_sure_oci_spec_process(oci_runtime_spec *oci_spec)
|
|
{
|
|
if (oci_spec->process == NULL) {
|
|
oci_spec->process = util_common_calloc_s(sizeof(oci_runtime_spec_process));
|
|
if (oci_spec->process == NULL) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int make_sure_oci_spec_linux_resources(oci_runtime_spec *oci_spec)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = make_sure_oci_spec_linux(oci_spec);
|
|
if (ret < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (oci_spec->linux->resources == NULL) {
|
|
oci_spec->linux->resources = util_common_calloc_s(sizeof(oci_runtime_config_linux_resources));
|
|
if (oci_spec->linux->resources == NULL) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int make_sure_oci_spec_linux_resources_blkio(oci_runtime_spec *oci_spec)
|
|
{
|
|
int ret;
|
|
|
|
ret = make_sure_oci_spec_linux_resources(oci_spec);
|
|
if (ret < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (oci_spec->linux->resources->block_io == NULL) {
|
|
oci_spec->linux->resources->block_io =
|
|
util_common_calloc_s(sizeof(oci_runtime_config_linux_resources_block_io));
|
|
if (oci_spec->linux->resources->block_io == NULL) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int merge_ulimits_pre(oci_runtime_spec *oci_spec, size_t host_ulimits_len)
|
|
{
|
|
int ret;
|
|
size_t new_size, old_size, tmp;
|
|
oci_runtime_spec_process_rlimits_element **rlimits_temp = NULL;
|
|
|
|
ret = make_sure_oci_spec_process(oci_spec);
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
|
|
tmp = SIZE_MAX / sizeof(oci_runtime_spec_process_rlimits_element *) - oci_spec->process->rlimits_len;
|
|
if (host_ulimits_len > tmp) {
|
|
ERROR("Too many rlimits to merge!");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
old_size = oci_spec->process->rlimits_len * sizeof(oci_runtime_spec_process_rlimits_element *);
|
|
new_size = (oci_spec->process->rlimits_len + host_ulimits_len) * sizeof(oci_runtime_spec_process_rlimits_element *);
|
|
ret = mem_realloc((void **)&rlimits_temp, new_size, oci_spec->process->rlimits, old_size);
|
|
if (ret != 0) {
|
|
ERROR("Failed to realloc memory for rlimits");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
oci_spec->process->rlimits = rlimits_temp;
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int trans_ulimit_to_rlimit(oci_runtime_spec_process_rlimits_element **rlimit_dst,
|
|
const host_config_ulimits_element *ulimit)
|
|
{
|
|
#define RLIMIT_PRE "RLIMIT_"
|
|
int ret = 0;
|
|
size_t j, namelen;
|
|
char *typename = NULL;
|
|
oci_runtime_spec_process_rlimits_element *rlimit = NULL;
|
|
|
|
// name + "RLIMIT_" + '\0'
|
|
if (strlen(ulimit->name) > ((SIZE_MAX - strlen(RLIMIT_PRE)) - 1)) {
|
|
ERROR("Invalid ulimit name");
|
|
return -1;
|
|
}
|
|
namelen = strlen(ulimit->name) + strlen(RLIMIT_PRE) + 1;
|
|
typename = util_common_calloc_s(namelen);
|
|
if (typename == NULL) {
|
|
ERROR("Out of memory");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
(void)strcat(typename, RLIMIT_PRE);
|
|
|
|
for (j = 0; j < strlen(ulimit->name); j++) {
|
|
typename[j + strlen(RLIMIT_PRE)] = (char)toupper((int)(ulimit->name[j]));
|
|
}
|
|
|
|
rlimit = util_common_calloc_s(sizeof(oci_runtime_spec_process_rlimits_element));
|
|
if (rlimit == NULL) {
|
|
ERROR("Failed to malloc memory for rlimit");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
rlimit->type = typename;
|
|
rlimit->soft = (uint64_t)ulimit->soft;
|
|
rlimit->hard = (uint64_t)ulimit->hard;
|
|
|
|
*rlimit_dst = rlimit;
|
|
out:
|
|
if (ret < 0) {
|
|
free(typename);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int do_merge_one_ulimit(const oci_runtime_spec *oci_spec, oci_runtime_spec_process_rlimits_element *rlimit)
|
|
{
|
|
size_t j;
|
|
bool exists = false;
|
|
|
|
for (j = 0; j < oci_spec->process->rlimits_len; j++) {
|
|
if (oci_spec->process->rlimits[j]->type == NULL) {
|
|
ERROR("rlimit type is empty");
|
|
UTIL_FREE_AND_SET_NULL(rlimit->type);
|
|
free(rlimit);
|
|
return -1;
|
|
}
|
|
if (strcmp(oci_spec->process->rlimits[j]->type, rlimit->type) == 0) {
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
if (exists) {
|
|
/* ulimit exist, discard default ulimit */
|
|
UTIL_FREE_AND_SET_NULL(rlimit->type);
|
|
free(rlimit);
|
|
} else {
|
|
oci_spec->process->rlimits[oci_spec->process->rlimits_len] = rlimit;
|
|
oci_spec->process->rlimits_len++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int merge_one_ulimit(const oci_runtime_spec *oci_spec, const host_config_ulimits_element *ulimit)
|
|
{
|
|
oci_runtime_spec_process_rlimits_element *rlimit = NULL;
|
|
|
|
if (trans_ulimit_to_rlimit(&rlimit, ulimit) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return do_merge_one_ulimit(oci_spec, rlimit);
|
|
}
|
|
|
|
static int merge_ulimits(oci_runtime_spec *oci_spec, host_config_ulimits_element **ulimits, size_t ulimits_len)
|
|
{
|
|
int ret = 0;
|
|
size_t i = 0;
|
|
|
|
if (oci_spec == NULL || ulimits == NULL || ulimits_len == 0) {
|
|
return -1;
|
|
}
|
|
|
|
ret = merge_ulimits_pre(oci_spec, ulimits_len);
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < ulimits_len; i++) {
|
|
ret = merge_one_ulimit(oci_spec, ulimits[i]);
|
|
if (ret != 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int merge_global_ulimit(oci_runtime_spec *oci_spec)
|
|
{
|
|
int ret = 0;
|
|
host_config_ulimits_element **ulimits = NULL;
|
|
size_t ulimits_len;
|
|
|
|
if (conf_get_lcrd_default_ulimit(&ulimits) != 0) {
|
|
ERROR("Failed to get lcrd default ulimit");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (ulimits != NULL) {
|
|
ulimits_len = ulimit_array_len(ulimits);
|
|
if (merge_ulimits(oci_spec, ulimits, ulimits_len)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
free_default_ulimit(ulimits);
|
|
return ret;
|
|
}
|
|
|
|
|