"Fossies" - the Fresh Open Source Software Archive

Member "sudo-1.9.11p3/plugins/sudoers/alias.c" (12 Jun 2022, 9988 Bytes) of package /linux/misc/sudo-1.9.11p3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "alias.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * SPDX-License-Identifier: ISC
    3  *
    4  * Copyright (c) 2004-2005, 2007-2019
    5  *  Todd C. Miller <Todd.Miller@sudo.ws>
    6  *
    7  * Permission to use, copy, modify, and distribute this software for any
    8  * purpose with or without fee is hereby granted, provided that the above
    9  * copyright notice and this permission notice appear in all copies.
   10  *
   11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   18  */
   19 
   20 /*
   21  * This is an open source non-commercial project. Dear PVS-Studio, please check it.
   22  * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
   23  */
   24 
   25 /*
   26  * This is an open source non-commercial project. Dear PVS-Studio, please check it.
   27  * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
   28  */
   29 
   30 #include <config.h>
   31 
   32 #include <stdio.h>
   33 #include <stdlib.h>
   34 #include <string.h>
   35 #include <errno.h>
   36 
   37 #include "sudoers.h"
   38 #include "redblack.h"
   39 #include <gram.h>
   40 
   41 /*
   42  * Comparison function for the red-black tree.
   43  * Aliases are sorted by name with the type used as a tie-breaker.
   44  */
   45 static int
   46 alias_compare(const void *v1, const void *v2)
   47 {
   48     const struct alias *a1 = (const struct alias *)v1;
   49     const struct alias *a2 = (const struct alias *)v2;
   50     int res;
   51     debug_decl(alias_compare, SUDOERS_DEBUG_ALIAS);
   52 
   53     if (a1 == NULL)
   54     res = -1;
   55     else if (a2 == NULL)
   56     res = 1;
   57     else if ((res = strcmp(a1->name, a2->name)) == 0)
   58     res = a1->type - a2->type;
   59     debug_return_int(res);
   60 }
   61 
   62 /*
   63  * Search the tree for an alias with the specified name and type.
   64  * Returns a pointer to the alias structure or NULL if not found.
   65  * Caller is responsible for calling alias_put() on the returned
   66  * alias to mark it as unused.
   67  */
   68 struct alias *
   69 alias_get(struct sudoers_parse_tree *parse_tree, const char *name, int type)
   70 {
   71     struct alias key;
   72     struct rbnode *node;
   73     struct alias *a = NULL;
   74     debug_decl(alias_get, SUDOERS_DEBUG_ALIAS);
   75 
   76     if (parse_tree->aliases == NULL)
   77     debug_return_ptr(NULL);
   78 
   79     key.name = (char *)name;
   80     key.type = type;
   81     if ((node = rbfind(parse_tree->aliases, &key)) != NULL) {
   82     /*
   83      * Check whether this alias is already in use.
   84      * If so, we've detected a loop.  If not, set the flag,
   85      * which the caller should clear with a call to alias_put().
   86      */
   87     a = node->data;
   88     if (a->used) {
   89         errno = ELOOP;
   90         debug_return_ptr(NULL);
   91     }
   92     a->used = true;
   93     } else {
   94     errno = ENOENT;
   95     }
   96     debug_return_ptr(a);
   97 }
   98 
   99 /*
  100  * Clear the "used" flag in an alias once the caller is done with it.
  101  */
  102 void
  103 alias_put(struct alias *a)
  104 {
  105     debug_decl(alias_put, SUDOERS_DEBUG_ALIAS);
  106     a->used = false;
  107     debug_return;
  108 }
  109 
  110 /*
  111  * Add an alias to the aliases redblack tree.
  112  * Note that "file" must be a reference-counted string.
  113  * Returns true on success and false on failure, setting errno.
  114  */
  115 bool
  116 alias_add(struct sudoers_parse_tree *parse_tree, char *name, int type,
  117     char *file, int line, int column, struct member *members)
  118 {
  119     struct alias *a;
  120     debug_decl(alias_add, SUDOERS_DEBUG_ALIAS);
  121 
  122     if (parse_tree->aliases == NULL) {
  123     if ((parse_tree->aliases = alloc_aliases()) == NULL)
  124         debug_return_bool(false);
  125     }
  126 
  127     a = calloc(1, sizeof(*a));
  128     if (a == NULL)
  129     debug_return_bool(false);
  130 
  131     /* Only set elements used by alias_compare() in case there is a dupe. */
  132     a->name = name;
  133     a->type = type;
  134     switch (rbinsert(parse_tree->aliases, a, NULL)) {
  135     case 1:
  136     free(a);
  137     errno = EEXIST;
  138     debug_return_bool(false);
  139     case -1:
  140     free(a);
  141     debug_return_bool(false);
  142     }
  143 
  144     /*
  145      * It is now safe to fill in the rest of the alias.  We do this last
  146      * since it modifies "file" (adds a ref) and "members" (tailq conversion).
  147      */
  148     /* a->used = false; */
  149     a->file = sudo_rcstr_addref(file);
  150     a->line = line;
  151     a->column = column;
  152     HLTQ_TO_TAILQ(&a->members, members, entries);
  153     debug_return_bool(true);
  154 }
  155 
  156 /*
  157  * Closure to adapt 2-arg rbapply() to 3-arg alias_apply().
  158  */
  159 struct alias_apply_closure {
  160     struct sudoers_parse_tree *parse_tree;
  161     int (*func)(struct sudoers_parse_tree *, struct alias *, void *);
  162     void *cookie;
  163 };
  164 
  165 /* Adapt rbapply() to alias_apply() calling convention. */
  166 static int
  167 alias_apply_func(void *v1, void *v2)
  168 {
  169     struct alias *a = v1;
  170     struct alias_apply_closure *closure = v2;
  171 
  172     return closure->func(closure->parse_tree, a, closure->cookie);
  173 }
  174 
  175 /*
  176  * Apply a function to each alias entry and pass in a cookie.
  177  */
  178 void
  179 alias_apply(struct sudoers_parse_tree *parse_tree,
  180     int (*func)(struct sudoers_parse_tree *, struct alias *, void *),
  181     void *cookie)
  182 {
  183     struct alias_apply_closure closure;
  184     debug_decl(alias_apply, SUDOERS_DEBUG_ALIAS);
  185 
  186     if (parse_tree->aliases != NULL) {
  187     closure.parse_tree = parse_tree;
  188     closure.func = func;
  189     closure.cookie = cookie;
  190     rbapply(parse_tree->aliases, alias_apply_func, &closure, inorder);
  191     }
  192 
  193     debug_return;
  194 }
  195 
  196 /*
  197  * Returns true if there are no aliases in the parse_tree, else false.
  198  */
  199 bool
  200 no_aliases(struct sudoers_parse_tree *parse_tree)
  201 {
  202     debug_decl(no_aliases, SUDOERS_DEBUG_ALIAS);
  203     debug_return_bool(parse_tree->aliases == NULL ||
  204     rbisempty(parse_tree->aliases));
  205 }
  206 
  207 /*
  208  * Free memory used by an alias struct and its members.
  209  */
  210 void
  211 alias_free(void *v)
  212 {
  213     struct alias *a = (struct alias *)v;
  214     debug_decl(alias_free, SUDOERS_DEBUG_ALIAS);
  215 
  216     if (a != NULL) {
  217     free(a->name);
  218     sudo_rcstr_delref(a->file);
  219     free_members(&a->members);
  220     free(a);
  221     }
  222 
  223     debug_return;
  224 }
  225 
  226 /*
  227  * Find the named alias, remove it from the tree and return it.
  228  */
  229 struct alias *
  230 alias_remove(struct sudoers_parse_tree *parse_tree, const char *name, int type)
  231 {
  232     struct rbnode *node;
  233     struct alias key;
  234     debug_decl(alias_remove, SUDOERS_DEBUG_ALIAS);
  235 
  236     if (parse_tree->aliases != NULL) {
  237     key.name = (char *)name;
  238     key.type = type;
  239     if ((node = rbfind(parse_tree->aliases, &key)) != NULL)
  240         debug_return_ptr(rbdelete(parse_tree->aliases, node));
  241     }
  242     errno = ENOENT;
  243     debug_return_ptr(NULL);
  244 }
  245 
  246 struct rbtree *
  247 alloc_aliases(void)
  248 {
  249     debug_decl(alloc_aliases, SUDOERS_DEBUG_ALIAS);
  250 
  251     debug_return_ptr(rbcreate(alias_compare));
  252 }
  253 
  254 void
  255 free_aliases(struct rbtree *aliases)
  256 {
  257     debug_decl(free_aliases, SUDOERS_DEBUG_ALIAS);
  258 
  259     if (aliases != NULL)
  260     rbdestroy(aliases, alias_free);
  261 }
  262 
  263 const char *
  264 alias_type_to_string(int alias_type)
  265 {
  266     return alias_type == HOSTALIAS ? "Host_Alias" :
  267     alias_type == CMNDALIAS ? "Cmnd_Alias" :
  268     alias_type == USERALIAS ? "User_Alias" :
  269     alias_type == RUNASALIAS ? "Runas_Alias" :
  270     "Invalid_Alias";
  271 }
  272 
  273 /*
  274  * Remove the alias of the specified type as well as any other aliases
  275  * referenced by that alias.  Stores removed aliases in a freelist.
  276  */
  277 static bool
  278 alias_remove_recursive(struct sudoers_parse_tree *parse_tree, char *name,
  279     int type, struct rbtree *freelist)
  280 {
  281     struct member *m;
  282     struct alias *a;
  283     bool ret = true;
  284     debug_decl(alias_remove_recursive, SUDOERS_DEBUG_ALIAS);
  285 
  286     if ((a = alias_remove(parse_tree, name, type)) != NULL) {
  287     TAILQ_FOREACH(m, &a->members, entries) {
  288         if (m->type == ALIAS) {
  289         if (!alias_remove_recursive(parse_tree, m->name, type, freelist))
  290             ret = false;
  291         }
  292     }
  293     if (rbinsert(freelist, a, NULL) != 0)
  294         ret = false;
  295     }
  296     debug_return_bool(ret);
  297 }
  298 
  299 static int
  300 alias_find_used_members(struct sudoers_parse_tree *parse_tree,
  301     struct member_list *members, int atype, struct rbtree *used_aliases)
  302 {
  303     struct member *m;
  304     int errors = 0;
  305     debug_decl(alias_find_used_members, SUDOERS_DEBUG_ALIAS);
  306 
  307     if (members != NULL) {
  308     TAILQ_FOREACH(m, members, entries) {
  309         if (m->type != ALIAS)
  310         continue;
  311         if (!alias_remove_recursive(parse_tree, m->name, atype, used_aliases))
  312         errors++;
  313     }
  314     }
  315 
  316     debug_return_int(errors);
  317 }
  318 
  319 /*
  320  * Move all aliases referenced by userspecs to used_aliases.
  321  */
  322 bool
  323 alias_find_used(struct sudoers_parse_tree *parse_tree, struct rbtree *used_aliases)
  324 {
  325     struct privilege *priv;
  326     struct userspec *us;
  327     struct cmndspec *cs;
  328     struct defaults *d;
  329     struct member *m;
  330     int errors = 0;
  331     debug_decl(alias_find_used, SUDOERS_DEBUG_ALIAS);
  332 
  333     /* Move referenced aliases to used_aliases. */
  334     TAILQ_FOREACH(us, &parse_tree->userspecs, entries) {
  335     errors += alias_find_used_members(parse_tree, &us->users,
  336         USERALIAS, used_aliases);
  337     TAILQ_FOREACH(priv, &us->privileges, entries) {
  338         errors += alias_find_used_members(parse_tree, &priv->hostlist,
  339         HOSTALIAS, used_aliases);
  340         TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
  341         errors += alias_find_used_members(parse_tree, cs->runasuserlist,
  342             RUNASALIAS, used_aliases);
  343         errors += alias_find_used_members(parse_tree, cs->runasgrouplist,
  344             RUNASALIAS, used_aliases);
  345         if ((m = cs->cmnd)->type == ALIAS) {
  346             if (!alias_remove_recursive(parse_tree, m->name, CMNDALIAS,
  347             used_aliases))
  348             errors++;
  349         }
  350         }
  351     }
  352     }
  353     TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
  354     switch (d->type) {
  355         case DEFAULTS_HOST:
  356         errors += alias_find_used_members(parse_tree,
  357             &d->binding->members, HOSTALIAS, used_aliases);
  358         break;
  359         case DEFAULTS_USER:
  360         errors += alias_find_used_members(parse_tree,
  361             &d->binding->members, USERALIAS, used_aliases);
  362         break;
  363         case DEFAULTS_RUNAS:
  364         errors += alias_find_used_members(parse_tree,
  365             &d->binding->members, RUNASALIAS, used_aliases);
  366         break;
  367         case DEFAULTS_CMND:
  368         errors += alias_find_used_members(parse_tree,
  369             &d->binding->members, CMNDALIAS, used_aliases);
  370         break;
  371         default:
  372         break;
  373     }
  374     }
  375 
  376     debug_return_int(errors ? false : true);
  377 }