/* * ssexec_tree_admin.c * * Copyright (c) 2018-2024 Eric Vidal <eric@obarun.org> * * All rights reserved. * * This file is part of Obarun. It is subject to the license terms in * the LICENSE file found in the top-level directory of this * distribution. * This file may not be copied, modified, propagated, or distributed * except according to the terms contained in the LICENSE file./ */ #include <66/tree.h> #include <string.h> #include <stdint.h>//uintx_t #include <sys/stat.h> #include <stdio.h>//rename #include <pwd.h> #include <stdlib.h>//free #include <oblibs/log.h> #include <oblibs/types.h> #include <oblibs/directory.h> #include <oblibs/files.h> #include <oblibs/string.h> #include <oblibs/sastr.h> #include <oblibs/graph.h> #include <skalibs/sgetopt.h> #include <skalibs/stralloc.h> #include <skalibs/djbunix.h> #include <skalibs/bytestr.h>//byte_count #include <skalibs/posixplz.h>//unlink_void #include <66/config.h> #include <66/utils.h> #include <66/constants.h> #include <66/enum.h> #include <66/state.h> #include <66/service.h> #include <66/resolve.h> #include <66/graph.h> #include <66/sanitize.h> #include <s6/supervise.h> #define TREE_COLON_DELIM ':' #define TREE_COMMA_DELIM ',' #define TREE_MAXOPTS 9 #define tree_checkopts(n) if (n >= TREE_MAXOPTS) log_die(LOG_EXIT_USER, "too many -o options") typedef struct tree_opts_map_s tree_opts_map_t ; struct tree_opts_map_s { char const *str ; int const id ; } ; typedef enum enum_tree_opts_e enum_tree_opts_t, *enum_tree_opts_t_ref ; enum enum_ns_opts_e { TREE_OPTS_DEPENDS = 0, TREE_OPTS_REQUIREDBY, TREE_OPTS_RENAME, TREE_OPTS_GROUPS, TREE_OPTS_NOSEED, TREE_OPTS_ALLOW, TREE_OPTS_DENY, TREE_OPTS_CLONE, TREE_OPTS_ENDOFKEY } ; tree_opts_map_t const tree_opts_table[] = { { .str = "depends", .id = TREE_OPTS_DEPENDS }, { .str = "requiredby", .id = TREE_OPTS_REQUIREDBY }, { .str = "rename", .id = TREE_OPTS_RENAME }, { .str = "groups", .id = TREE_OPTS_GROUPS }, { .str = "noseed", .id = TREE_OPTS_NOSEED }, { .str = "allow", .id = TREE_OPTS_ALLOW }, { .str = "deny", .id = TREE_OPTS_DENY }, { .str = "clone", .id = TREE_OPTS_CLONE }, { .str = 0 } } ; typedef struct tree_what_s tree_what_t, *tree_what_t_ref ; struct tree_what_s { uint8_t create ; uint8_t depends ; uint8_t requiredby ; uint8_t allow ; uint8_t deny ; uint8_t enable ; uint8_t disable ; uint8_t remove ; uint8_t clone ; uint8_t groups ; uint8_t current ; uint8_t rename ; uint8_t noseed ; char gr[6] ; char sclone[100] ; uid_t auids[256] ; uid_t duids[256] ; uint8_t ndepends ; // only used if the term none is passed as dependencies uint8_t nrequiredby ; // only used if the term none is passed as dependencies uint8_t nopts ; } ; #define TREE_WHAT_ZERO { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, { 0 }, { 0 }, { 0 }, { 0 }, 1, 1, 0 } tree_what_t what_init(void) { log_flow() ; tree_what_t what = TREE_WHAT_ZERO ; memset(what.gr, 0, 6 * sizeof(char)); ; memset(what.auids, 0, 256 * sizeof(uid_t)); memset(what.duids, 0, 256 * sizeof(uid_t)) ; return what ; } void tree_enable_disable(graph_t *g, char const *base, char const *treename, uint8_t action) ; static void check_identifier(char const *name) { if (!memcmp(name, SS_MASTER + 1, 6)) log_die(LOG_EXIT_USER,"tree name: ",name,": starts with reserved prefix Master") ; char str[UINT_FMT] ; str[uint_fmt(str, SS_MAX_TREENAME)] = 0 ; if (strlen(name) > SS_MAX_TREENAME) log_die(LOG_EXIT_USER,"tree name is too long -- it can not exceed ", str) ; } static ssize_t tree_get_key(char *table,char const *str) { ssize_t pos = -1 ; pos = get_len_until(str,'=') ; if (pos == -1) return -1 ; auto_strings(table,str) ; table[pos] = 0 ; pos++ ; // remove '=' return pos ; } static void tree_parse_options_groups(char *store, char const *str) { log_flow() ; uid_t uid = getuid() ; if (strcmp(str, TREE_GROUPS_BOOT) && strcmp(str, TREE_GROUPS_ADM) && strcmp(str, TREE_GROUPS_USER) && strcmp(str, "none")) log_die(LOG_EXIT_SYS, "invalid group: ", str) ; if (!uid && (!strcmp(str, TREE_GROUPS_USER))) log_die(LOG_EXIT_SYS, "Only regular user can use this group") ; else if (uid && (!strcmp(str, TREE_GROUPS_ADM) || !strcmp(str, TREE_GROUPS_BOOT))) log_die(LOG_EXIT_SYS, "Only root user can use this group") ; auto_strings(store, str) ; } void tree_parse_uid_list(uid_t *uids, char const *str) { log_flow() ; size_t pos = 0 ; stralloc sa = STRALLOC_ZERO ; if (!sastr_clean_string_wdelim(&sa, str, TREE_COMMA_DELIM)) log_dieu(LOG_EXIT_SYS,"parse uid list") ; uid_t owner = getuid() ; /** special case, we don't know which user want to use * the tree, we need a general name to allow all users. * The term "user" is took here to allow the current user*/ ssize_t p = sastr_cmp(&sa, "user") ; FOREACH_SASTR(&sa, pos) { if (pos == (size_t)p) { struct passwd *pw = getpwuid(owner); if (!pw) { if (!errno) errno = ESRCH ; log_dieu(LOG_EXIT_SYS,"get user name") ; } if (!scan_uidlist(pw->pw_name, uids)) log_dieu(LOG_EXIT_USER,"scan account: ",pw->pw_name) ; continue ; } if (!scan_uidlist(sa.s + pos, uids)) log_dieu(LOG_EXIT_USER,"scan account: ",sa.s + pos) ; } stralloc_free(&sa) ; } static void tree_parse_options_depends(graph_t *g, ssexec_t *info, char const *str, uint8_t requiredby, tree_what_t *what) { log_flow() ; if (!*str) return ; int r ; size_t pos = 0 ; char *name = 0 ; stralloc sa = STRALLOC_ZERO ; if (!sastr_clean_string_wdelim(&sa, str, TREE_COMMA_DELIM)) log_dieu(LOG_EXIT_SYS,"clean sub options") ; if (sastr_cmp(&sa, "none") >= 0) { if (!requiredby) what->ndepends = 0 ; else what->nrequiredby = 0 ; stralloc_free(&sa) ; return ; } FOREACH_SASTR(&sa, pos) { name = sa.s + pos ; r = tree_isvalid(info->base.s, name) ; if (r < 0) log_diesys(LOG_EXIT_SYS, "invalid treename") ; /** We only creates trees declared as dependency. * TreeA depends on TreeB, we create TreeB * if it doesn't exist yet */ if (!r && !requiredby) { ssexec_t newinfo = SSEXEC_ZERO ; if (!auto_stra(&newinfo.base, info->base.s) || !auto_stra(&newinfo.treename, name)) log_die_nomem("stralloc") ; newinfo.owner = info->owner ; newinfo.prog = info->prog ; newinfo.help = info->help ; newinfo.usage = info->usage ; newinfo.opt_color = info->opt_color ; newinfo.opt_tree = info->opt_tree ; int nwhat = what->noseed ? 2 : 0 ; int nargc = 3 + nwhat ; char const *prog = PROG ; char const *newargv[nargc] ; uint8_t m = 0 ; newargv[m++] = "tree" ; if (nwhat) { newargv[m++] = "-o" ; newargv[m++] = "noseed" ; } newargv[m++] = name ; newargv[m++] = 0 ; log_trace("launch 66 tree sub-process for tree: ", name) ; PROG = "tree (child)" ; if (ssexec_tree_admin(nargc, newargv, &newinfo)) log_dieusys(LOG_EXIT_SYS, "create tree: ", name) ; PROG = prog ; ssexec_free(&newinfo) ; r = 1 ; } if (!requiredby) { if (!graph_vertex_add_with_edge(g, info->treename.s, name)) log_die(LOG_EXIT_SYS,"add edge: ", name, " to vertex: ", info->treename.s) ; } else if (r) { /** if TreeA is requiredby TreeB, we don't want to create TreeB. * We only manages it if it exist yet */ if (!graph_vertex_add_with_requiredby(g, info->treename.s, name)) log_die(LOG_EXIT_SYS,"add requiredby: ", name, " to: ", info->treename.s) ; } } stralloc_free(&sa) ; } static void tree_parse_options(graph_t *g, char const *str, ssexec_t *info, tree_what_t *what) { log_flow() ; if (!*str) return ; size_t pos = 0, len = 0 ; ssize_t r ; char *line = 0, *key = 0, *val = 0 ; stralloc sa = STRALLOC_ZERO ; tree_opts_map_t const *t ; if (!sastr_clean_string_wdelim(&sa, str, TREE_COLON_DELIM)) log_dieu(LOG_EXIT_SYS,"clean options") ; unsigned int n = sastr_len(&sa), nopts = 0 , old ; tree_checkopts(n) ; FOREACH_SASTR(&sa, pos) { line = sa.s + pos ; t = tree_opts_table ; old = nopts ; for (; t->str ; t++) { len = strlen(line) ; char tmp[len + 1] ; r = tree_get_key(tmp,line) ; if (r == -1 && strcmp(line, "noseed")) log_die(LOG_EXIT_USER,"invalid key: ", line) ; if (!strcmp(line, "noseed")) { key = line ; } else { key = tmp ; val = line + r ; } if (!strcmp(key, t->str)) { switch(t->id) { case TREE_OPTS_DEPENDS : tree_parse_options_depends(g, info, val, 0, what) ; what->depends = 1 ; break ; case TREE_OPTS_REQUIREDBY : tree_parse_options_depends(g, info, val, 1, what) ; what->requiredby = 1 ; break ; case TREE_OPTS_RENAME: what->rename = 1 ; break ; case TREE_OPTS_GROUPS: tree_parse_options_groups(what->gr, val) ; what->groups = 1 ; break ; case TREE_OPTS_NOSEED: what->noseed = 1 ; break ; case TREE_OPTS_ALLOW: tree_parse_uid_list(what->auids, val) ; what->allow = 1 ; break ; case TREE_OPTS_DENY: tree_parse_uid_list(what->duids, val) ; what->deny = 1 ; break ; case TREE_OPTS_CLONE: if (strlen(val) > 99) log_die(LOG_EXIT_USER, "clone name cannot exceed 100 characters") ; auto_strings(what->sclone, val) ; what->clone = 1 ; break ; default : break ; } nopts++ ; } } if (old == nopts) log_die(LOG_EXIT_SYS,"invalid option: ",line) ; } stralloc_free(&sa) ; } void tree_parse_seed(char const *treename, tree_seed_t *seed, tree_what_t *what) { log_flow() ; log_trace("checking seed file: ", treename, "..." ) ; if (tree_seed_isvalid(treename)) { if (!tree_seed_setseed(seed, treename)) log_dieu(LOG_EXIT_SYS, "parse seed file: ", treename) ; if (seed->depends) what->depends = 1 ; if (seed->requiredby) what->requiredby = 1 ; if (seed->disen) what->enable = 1 ; if (seed->allow > 0) { tree_parse_uid_list(what->auids, seed->sa.s + seed->allow) ; what->allow = 1 ; } if (seed->deny > 0) { tree_parse_uid_list(what->duids, seed->sa.s + seed->deny) ; what->deny = 1 ; } if (seed->current) what->current = 1 ; if (seed->groups) { tree_parse_options_groups(what->gr, seed->sa.s + seed->groups) ; what->groups = 1 ; } } } void tree_groups(graph_t *graph, char const *base, char const *treename, char const *value) { log_flow() ; resolve_tree_t tres = RESOLVE_TREE_ZERO ; resolve_wrapper_t_ref wres = resolve_set_struct(DATA_TREE, &tres) ; size_t nb = 0 ; char pack[UINT_FMT] ; char const *val ; log_trace("set: ", treename," to group ..." ) ; if (!strcmp(value, "none")) { val = "" ; goto write ; } else if (!strcmp(value, "boot")) { /** a tree on groups boot cannot be enabled */ tree_enable_disable(graph, base, treename, 0) ; } nb = 1 ; val = value ; write: uint_pack(pack, nb) ; pack[uint_fmt(pack, nb)] = 0 ; if (resolve_read_g(wres, base, treename) <= 0) log_dieusys(LOG_EXIT_SYS, "read resolve file of: ", treename) ; if (!resolve_modify_field(wres, E_RESOLVE_TREE_GROUPS, val) || !resolve_modify_field(wres, E_RESOLVE_TREE_NGROUPS, pack)) log_dieusys(LOG_EXIT_SYS, "modify resolve file of: ", treename) ; if (!resolve_write_g(wres, base, treename)) log_dieusys(LOG_EXIT_SYS, "write resolve file of: ", treename) ; resolve_free(wres) ; log_info("Set successfully: ", treename, " to group: ", value) ; } void tree_master_modify_contents(char const *base) { log_flow() ; stralloc sa = STRALLOC_ZERO ; resolve_tree_master_t mres = RESOLVE_TREE_MASTER_ZERO ; resolve_wrapper_t_ref wres = resolve_set_struct(DATA_TREE_MASTER, &mres) ; size_t baselen = strlen(base) ; char solve[baselen + SS_SYSTEM_LEN + SS_RESOLVE_LEN + 1] ; char const *exclude[2] = { SS_MASTER + 1, 0 } ; log_trace("modify field contents of resolve Master file of trees") ; auto_strings(solve, base, SS_SYSTEM, SS_RESOLVE) ; if (!sastr_dir_get(&sa, solve, exclude, S_IFREG)) log_dieu(LOG_EXIT_SYS, "get resolve files of trees") ; size_t ncontents = sa.len ? sastr_nelement(&sa) : 0 ; if (ncontents) if (!sastr_rebuild_in_oneline(&sa)) log_dieu(LOG_EXIT_SYS, "rebuild stralloc") ; if (resolve_read_g(wres, base, SS_MASTER + 1) <= 0) log_dieusys(LOG_EXIT_SYS, "read resolve Master file of trees") ; mres.ncontents = (uint32_t)ncontents ; if (ncontents) mres.contents = resolve_add_string(wres, sa.s) ; else mres.contents = resolve_add_string(wres, "") ; if (!resolve_write_g(wres, base, SS_MASTER + 1)) log_dieusys(LOG_EXIT_SYS, "write resolve Master file of trees") ; stralloc_free(&sa) ; resolve_free(wres) ; } void tree_create(graph_t *g, ssexec_t *info, tree_what_t *what) { log_flow() ; resolve_tree_t tres = RESOLVE_TREE_ZERO ; resolve_wrapper_t_ref wres = resolve_set_struct(DATA_TREE, &tres) ; tree_seed_t seed = TREE_SEED_ZERO ; resolve_init(wres) ; /** check seed file */ if (!what->noseed) tree_parse_seed(info->treename.s, &seed, what) ; log_trace("creating: ", info->treename.s, "..." ) ; // set permissions what->allow = 1 ; tres.name = resolve_add_string(wres, info->treename.s) ; tres.groups = resolve_add_string(wres, info->owner ? TREE_GROUPS_USER : TREE_GROUPS_ADM) ; tres.ngroups = 1 ; log_trace("write resolve file of: ", info->treename.s) ; if (!resolve_write_g(wres, info->base.s, info->treename.s)) log_dieu(LOG_EXIT_SYS, "write resolve file of: ", info->treename.s) ; /** Check the length of seed.sa.len: If the seed file is not parsed at this point, * seed.sa.s + seed.depends is empty, which can lead to a segmentation fault * when the -o option is passed at the command line. However, we have already gone * through the tree_parse_options_depends in such cases. */ if (what->depends && seed.sa.len) tree_parse_options_depends(g, info, seed.sa.s + seed.depends, 0, what) ; if (what->requiredby && seed.sa.len) tree_parse_options_depends(g, info, seed.sa.s + seed.requiredby, 1, what) ; tree_master_modify_contents(info->base.s) ; resolve_free(wres) ; tree_seed_free(&seed) ; log_info("Created successfully tree: ", info->treename.s) ; } void tree_enable_disable_deps(graph_t *g,char const *base, char const *treename, uint8_t action) { log_flow() ; size_t pos = 0, element = 0 ; stralloc sa = STRALLOC_ZERO ; if (graph_matrix_get_edge_g_sa(&sa, g, treename, action ? 0 : 1, 0) < 0) log_dieu(LOG_EXIT_SYS, "get ", action ? "dependencies" : "required by" ," of: ", treename) ; size_t len = sastr_nelement(&sa) ; unsigned int v[len + 1] ; memset(v, 0, (len + 1) * sizeof(unsigned int)) ; if (sa.len) { FOREACH_SASTR(&sa, pos) { if (!v[element]) { char *name = sa.s + pos ; tree_enable_disable(g, base, name, action) ; v[element] = 1 ; } element++ ; } } stralloc_free(&sa) ; } /** @action -> 0 disable * @action -> 1 enable */ void tree_enable_disable(graph_t *g, char const *base, char const *treename, uint8_t action) { log_flow() ; resolve_tree_t tres = RESOLVE_TREE_ZERO ; resolve_wrapper_t_ref wres = resolve_set_struct(DATA_TREE, &tres) ; if (resolve_read_g(wres, base, treename) <= 0) log_dieusys(LOG_EXIT_SYS, "read resolve file of: ", treename) ; uint8_t disen = tres.enabled ; if ((disen && !action) || (!disen && action)){ log_trace(!action ? "disable " : "enable ", treename, "...") ; if (tree_ongroups(base, treename, TREE_GROUPS_BOOT) && action) { log_1_warn(treename," is a part of group ", TREE_GROUPS_BOOT," -- ignoring enable request") ; return ; } tres.enabled = action ; if (!resolve_write_g(wres, base, treename)) log_dieusys(LOG_EXIT_SYS, "write resolve file of: ", treename) ; tree_enable_disable_deps(g, base, treename, action) ; log_info(!action ? "Disabled" : "Enabled"," successfully tree: ", treename) ; } else { log_info("Already ",!action ? "disabled" : "enabled"," tree: ",treename) ; } resolve_free(wres) ; } /* !deps -> add * deps -> remove */ void tree_depends_requiredby(graph_t *g, char const *base, char const *treename, uint8_t requiredby, uint8_t none, char const *deps) { log_flow() ; resolve_tree_t tres = RESOLVE_TREE_ZERO ; resolve_wrapper_t_ref wres = resolve_set_struct(DATA_TREE, &tres) ; size_t pos = 0, len = 0, nb = 0, element = 0 ; uint8_t ewhat = !requiredby ? E_RESOLVE_TREE_DEPENDS : E_RESOLVE_TREE_REQUIREDBY ; uint8_t nwhat = !requiredby ? E_RESOLVE_TREE_NDEPENDS : E_RESOLVE_TREE_NREQUIREDBY ; stralloc sa = STRALLOC_ZERO ; char pack[UINT_FMT] ; log_trace("manage ", !requiredby ? "dependencies" : "required by", " for tree: ", treename, "..." ) ; if (graph_matrix_get_edge_g_sorted_sa(&sa, g, treename, requiredby, 0) < 0) log_dieu(LOG_EXIT_SYS,"get sorted ", requiredby ? "required by" : "dependency", " list of tree: ", treename) ; size_t vlen = sastr_nelement(&sa) ; unsigned int v[vlen + 1] ; memset(v, 0, (vlen + 1) * sizeof(unsigned int)) ; len = sa.len ; { char t[len + 1] ; sastr_to_char(t, &sa) ; sa.len = 0 ; for(; pos < len ; pos += strlen(t + pos) + 1, element++) { if (!v[element]) { char *name = t + pos ; if (!none) { if (!graph_edge_remove_g(g, treename, name)) log_dieu(LOG_EXIT_SYS,"remove edge: ", name, " from vertex: ", treename); } else { if (deps) { if (!strcmp(name, deps)) { v[element] = 1 ; continue ; } } if (!auto_stra(&sa, name, " ")) log_die_nomem("stralloc") ; nb++ ; } v[element] = 1 ; } } } if (sa.len) sa.len-- ; //remove last " " if (!stralloc_0(&sa)) log_die_nomem("stralloc") ; uint_pack(pack, nb) ; pack[uint_fmt(pack, nb)] = 0 ; if (resolve_read_g(wres, base, treename) <= 0) log_dieusys(LOG_EXIT_SYS, "read resolve file of: ", treename) ; if (!resolve_modify_field(wres, ewhat, sa.s) || !resolve_modify_field(wres, nwhat, pack)) log_dieusys(LOG_EXIT_SYS, "modify resolve file of: ", treename) ; if (!resolve_write_g(wres, base, treename)) log_dieusys(LOG_EXIT_SYS, "write resolve file of: ", treename) ; if (!none) { graph_free_matrix(g) ; graph_free_sort(g) ; if (!graph_matrix_build(g)) log_die(LOG_EXIT_SYS, "build the graph") ; if (!graph_matrix_analyze_cycle(g)) log_die(LOG_EXIT_SYS, "found cycle") ; if (!graph_matrix_sort(g)) log_die(LOG_EXIT_SYS, "sort the graph") ; } stralloc_free(&sa) ; resolve_free(wres) ; log_info(requiredby ? "Required by " : "Dependencies ", "successfully managed for tree: ", treename) ; } void tree_depends_requiredby_deps(graph_t *g, char const *base, char const *treename, uint8_t requiredby, uint8_t none, char const *deps) { log_flow() ; size_t baselen = strlen(base), pos = 0, len = 0, element = 0 ; stralloc sa = STRALLOC_ZERO ; char solve[baselen + SS_SYSTEM_LEN + 1] ; if (graph_matrix_get_edge_g_sorted_sa(&sa, g, treename, requiredby, 0) < 0) log_dieu(LOG_EXIT_SYS,"get sorted ", requiredby ? "required by" : "dependency", " list of tree: ", treename) ; size_t vlen = sastr_nelement(&sa) ; unsigned int v[vlen + 1] ; memset(v, 0, (vlen + 1) * sizeof(unsigned int)) ; auto_strings(solve, base, SS_SYSTEM) ; len = sa.len ; char t[len + 1] ; sastr_to_char(t, &sa) ; for(; pos < len ; pos += strlen(t + pos) + 1, element++) { if (!v[element]) { char *name = t + pos ; tree_depends_requiredby(g, base, name, !requiredby, none, deps) ; v[element] = 1 ; } } stralloc_free(&sa) ; } /** @what -> 0 deny * @what -> 1 allow */ void tree_rules(char const *base, char const *treename, uid_t *uids, uint8_t what) { log_flow() ; int r ; size_t uidn = uids[0], pos = 0 ; uid_t owner = MYUID ; char pack[256] ; stralloc sa = STRALLOC_ZERO ; resolve_tree_t tres = RESOLVE_TREE_ZERO ; resolve_wrapper_t_ref wres = resolve_set_struct(DATA_TREE, &tres) ; log_trace("set ", !what ? "denied" : "allowed", " user for tree: ", treename, "..." ) ; if (resolve_read_g(wres, base, treename) <= 0) log_dieusys(LOG_EXIT_SYS, "read resolve file of: ", treename) ; if (tres.nallow) if (!sastr_clean_string(&sa, tres.sa.s + tres.allow)) log_dieu(LOG_EXIT_SYS, "clean string") ; /** fresh creation of the tree */ if (!tres.nallow) { if (!uids[0]) { uids[0] = 1 ; uids[1] = owner ; } else { /** command can be 66 tree -a <account> <tree> * where <tree> doesn't exist yet. * Keep the -a option value and append the owner * of the process at the end of the list. */ uids[0]++ ; uids[uidn + 1] = owner ; } uidn++ ; } for (; pos < uidn ; pos++) { uint32_pack(pack,uids[pos+1]) ; pack[uint_fmt(pack,uids[pos+1])] = 0 ; r = sastr_cmp(&sa, pack) ; if (r < 0 && what) { if (!sastr_add_string(&sa, pack)) log_die_nomem("stralloc") ; tres.nallow++ ; log_trace("user: ", pack, " is allowed for tree: ", treename) ; } else if (r >= 0 && !what) { if (owner == uids[pos+1]) { log_1_warn("you cannot deny yourself -- ignoring request") ; continue ; } if (!sastr_remove_element(&sa, pack)) log_dieu(LOG_EXIT_SYS, "remove: ", pack, " from list") ; tres.nallow-- ; log_trace("user: ", pack, " is denied for tree: ", treename) ; } } if (!sastr_rebuild_in_oneline(&sa)) log_dieu(LOG_EXIT_SYS, "rebuild string") ; if (!resolve_modify_field(wres, E_RESOLVE_TREE_ALLOW, sa.s)) log_dieusys(LOG_EXIT_SYS, "modify resolve file of: ", treename) ; if (!resolve_write_g(wres, base, treename)) log_dieusys(LOG_EXIT_SYS, "write resolve file of: ", treename) ; stralloc_free(&sa) ; resolve_free(wres) ; log_info("Permissions rules set successfully for tree: ", treename) ; } static void tree_service_switch_contents(char const *base, char const *treesrc, char const *treedst, ssexec_t *info) { log_flow() ; size_t pos = 0 ; resolve_tree_t tres = RESOLVE_TREE_ZERO ; resolve_wrapper_t_ref wres = resolve_set_struct(DATA_TREE, &tres) ; resolve_service_t res = RESOLVE_SERVICE_ZERO ; resolve_wrapper_t_ref swres = resolve_set_struct(DATA_SERVICE, &res) ; stralloc sa = STRALLOC_ZERO ; if (!resolve_get_field_tosa_g(&sa, base, treesrc, DATA_TREE, E_RESOLVE_TREE_CONTENTS)) log_dieu(LOG_EXIT_SYS, "get contents list of tree: ", treesrc) ; FOREACH_SASTR(&sa, pos) { log_trace("switch service: ", sa.s + pos, " to tree: ", treedst) ; tree_service_add(treedst, sa.s + pos, info) ; if (!resolve_modify_field_g(swres, base, sa.s + pos, E_RESOLVE_SERVICE_TREENAME, treedst)) log_dieu(LOG_EXIT_SYS, "modify resolve file of: ", sa.s + pos) ; } stralloc_free(&sa) ; resolve_free(wres) ; resolve_free(swres) ; } void tree_remove(graph_t *g, char const *base, char const *treename, ssexec_t *info) { log_flow() ; int r ; char tree[SS_MAX_TREENAME + 1] ; char *current = SS_DEFAULT_TREENAME ; log_trace("delete: ", treename, "..." ) ; tree_enable_disable(g, base, treename, 0) ; /** depends */ tree_depends_requiredby_deps(g, base, treename, 0, 1, treename) ; /** requiredby */ tree_depends_requiredby_deps(g, base, treename, 1, 1, treename) ; if (tree_iscurrent(base, treename)) { /** This symlink must be valid in any case to avoid crashing the sanitize_system process. * If it is not valid, at least point it to the SS_DEFAULT_TREENAME, * as this tree is automatically created at every 66 command invocation * if it does not exist yet. */ log_warn("tree ",treename, " is marked as default -- switch default to: ", SS_DEFAULT_TREENAME) ; if (!tree_switch_current(base, SS_DEFAULT_TREENAME)) log_dieusys(LOG_EXIT_SYS,"set: ", SS_DEFAULT_TREENAME, " as default") ; log_info("Set successfully: ", SS_DEFAULT_TREENAME," as default") ; } else { r = tree_find_current(tree, base) ; if (r < 0) log_dieu(LOG_EXIT_SYS, "find default tree") ; if (r) current = tree ; else current = SS_DEFAULT_TREENAME ; } tree_service_switch_contents(base, treename, current, info) ; log_trace("remove resolve file of tree: ", treename) ; resolve_remove_g(base, treename, DATA_TREE) ; tree_master_modify_contents(base) ; log_info("Deleted successfully: ", treename) ; } void tree_current(ssexec_t *info) { log_trace("mark: ", info->treename.s," as default ..." ) ; if (!tree_switch_current(info->base.s, info->treename.s)) log_dieusys(LOG_EXIT_SYS,"set: ", info->treename.s, " as default") ; log_info("Set successfully: ", info->treename.s," as default") ; } void tree_clone(char const *clone, ssexec_t *info) { log_flow() ; struct stat st ; resolve_tree_t tres = RESOLVE_TREE_ZERO ; resolve_wrapper_t_ref wres = resolve_set_struct(DATA_TREE, &tres) ; size_t syslen = info->base.len + SS_SYSTEM_LEN, clonelen = strlen(clone) ; if (resolve_check_g(wres, info->base.s, clone)) log_die(LOG_EXIT_USER, clone, ": already exist") ; /** copy tree resolve file */ char src[syslen + SS_RESOLVE_LEN + 1 + info->treename.len + 1] ; char dst[syslen + SS_RESOLVE_LEN + 1 + clonelen + 1] ; auto_strings(src, info->base.s, SS_SYSTEM, SS_RESOLVE, "/", info->treename.s) ; auto_strings(dst, info->base.s, SS_SYSTEM, SS_RESOLVE, "/", clone) ; if (stat(src, &st) < 0) log_dieusys(LOG_EXIT_SYS, "stat: ", src) ; if (!filecopy_unsafe(src, dst, st.st_mode)) log_dieusys(LOG_EXIT_SYS, "copy: ", src, " to: ", dst) ; if (lchown(dst, st.st_uid, st.st_gid) < 0) log_dieusys(LOG_EXIT_SYS, "chown: ", dst) ; if (resolve_read_g(wres, info->base.s, clone) <= 0) log_dieu(LOG_EXIT_SYS, "read resolve file of tree: ", clone) ; if (!resolve_modify_field(wres, E_RESOLVE_TREE_INIT, 0) || !resolve_modify_field(wres, E_RESOLVE_TREE_SUPERVISED, 0) || !resolve_modify_field(wres, E_RESOLVE_TREE_CONTENTS, "") || !resolve_modify_field(wres, E_RESOLVE_TREE_NCONTENTS, 0) || !resolve_modify_field(wres, E_RESOLVE_TREE_ENABLED, 0) || !resolve_modify_field(wres, E_RESOLVE_TREE_NAME, clone)) log_dieusys(LOG_EXIT_SYS, "modify resolve file of tree: ", clone) ; if (!resolve_write_g(wres, info->base.s, clone)) log_dieusys(LOG_EXIT_SYS, "write resolve file of tree: ", clone) ; resolve_free(wres) ; /** tree Master resolve file */ tree_master_modify_contents(info->base.s) ; log_info("Cloned successfully: ", info->treename.s, " to: ", clone) ; } int ssexec_tree_admin(int argc, char const *const *argv, ssexec_t *info) { log_flow(); int r ; /** We can arrive here from other ssexec_xxx functions that * already define the tree name. It will be overwritten, * correcting the info structure. * Therefore, retrieve the original name at the end of the process. */ char oldtree[SS_MAX_TREENAME + 1] ; stralloc sa = STRALLOC_ZERO ; graph_t graph = GRAPH_ZERO ; struct resolve_hash_tree_s *htres = NULL ; tree_what_t what = what_init() ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc, argv, OPTS_TREE_ADMIN, &l) ; if (opt == -1) break ; switch (opt) { case 'c' : what.current = 1 ; what.nopts++ ; break ; case 'o' : if (!auto_stra(&sa, l.arg)) log_die_nomem("stralloc") ; what.nopts++ ; break ; case 'E' : what.enable = 1 ; what.nopts++ ; break ; case 'D' : what.disable = 1 ; what.nopts++ ; break ; case 'R' : what.remove = 1 ; what.create = 0 ; what.nopts++ ; break ; case 'n': log_1_warn("deprecated option -n -- use '66 tree create <treename>' instead") ; break ; case 'a' : log_1_warn("deprecated option -a -- see allow field at -o options") ; break ; case 'd' : log_1_warn("deprecated option -d -- see deny field at -o options") ; break ; case 'C' : log_1_warn("deprecated option -C -- see clone field at -o options") ; break ; case 'S' : log_1_warn("deprecated option -S -- see depends/requiredby fields at -o options") ; break ; default : log_usage(info->usage, "\n", info->help) ; } } argc -= l.ind ; argv += l.ind ; } if (argc < 1) log_usage(info->usage, "\n", info->help) ; check_identifier(argv[0]) ; if (info->opt_tree) { auto_strings(oldtree, info->treename.s) ; } else { /** avoid empty string */ auto_strings(oldtree, argv[0]) ; } info->treename.len = 0 ; if (!auto_stra(&info->treename, argv[0])) log_die_nomem("stralloc") ; r = tree_isvalid(info->base.s, info->treename.s) ; if (r < 0) log_diesys(LOG_EXIT_SYS, "invalid tree directory") ; if (sa.len) tree_parse_options(&graph, sa.s, info, &what) ; /** create is the option by default * mark it false if the tree already exist */ if (r) { if (!what.nopts) { log_warn(info->treename.s, ": already exist") ; goto freed ; } what.create = 0 ; } if(!r && what.create) tree_create(&graph, info, &what) ; if (!r && what.remove) log_dieusys(LOG_EXIT_SYS,"find tree: ", info->treename.s) ; graph_build_tree(&graph, &htres, info->base.s, E_RESOLVE_TREE_MASTER_CONTENTS) ; if (what.remove) { tree_remove(&graph, info->base.s, info->treename.s, info) ; goto freed ; } /** groups have influence on enable. Apply it first. */ if (what.groups) tree_groups(&graph, info->base.s, info->treename.s, what.gr) ; if (what.depends) { tree_depends_requiredby(&graph, info->base.s, info->treename.s, 0, what.ndepends, 0) ; tree_depends_requiredby_deps(&graph, info->base.s, info->treename.s, 0, what.ndepends, 0) ; sa.len = 0 ; size_t pos = 0 ; if (graph_matrix_get_edge_g_sorted_sa(&sa, &graph, info->treename.s, 0, 0) < 0) log_dieu(LOG_EXIT_SYS,"get sorted dependency list of tree: ", info->treename.s) ; if (tree_isenabled(info->base.s, info->treename.s)) { FOREACH_SASTR(&sa, pos) tree_enable_disable(&graph, info->base.s, sa.s + pos, 1) ; } } if (what.requiredby) { tree_depends_requiredby(&graph, info->base.s, info->treename.s, 1, what.nrequiredby, 0) ; tree_depends_requiredby_deps(&graph, info->base.s, info->treename.s, 1, what.nrequiredby, 0) ; sa.len = 0 ; size_t pos = 0 ; if (graph_matrix_get_edge_g_sorted_sa(&sa, &graph, info->treename.s, 1, 0) < 0) log_dieu(LOG_EXIT_SYS,"get sorted dependency list of tree: ", info->treename.s) ; if (!tree_isenabled(info->base.s, info->treename.s)) { FOREACH_SASTR(&sa, pos) tree_enable_disable(&graph, info->base.s, sa.s + pos, 0) ; } } if (what.enable) tree_enable_disable(&graph, info->base.s, info->treename.s, 1) ; if (what.disable) tree_enable_disable(&graph, info->base.s, info->treename.s, 0) ; if (what.allow) tree_rules(info->base.s, info->treename.s, what.auids, 1) ; if (what.deny) tree_rules(info->base.s, info->treename.s, what.duids, 0) ; if (what.current) tree_current(info) ; if (what.clone) tree_clone(what.sclone, info) ; freed: info->treename.len = 0 ; if (!auto_stra(&info->treename, oldtree)) log_die_nomem("stralloc") ; stralloc_free(&sa) ; graph_free_all(&graph) ; hash_free_tree(&htres) ; return 0 ; }