Coverage Report

Created: 2025-07-12 06:27

/src/sudo/plugins/sudoers/check_aliases.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 2004-2005, 2007-2018, 2021-2023
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
#include <config.h>
26
27
#include <stdio.h>
28
#include <stdlib.h>
29
#include <string.h>
30
#include <errno.h>
31
32
#include <sudoers.h>
33
#include <gram.h>
34
35
struct alias_warned {
36
    SLIST_ENTRY(alias_warned) entries;
37
    const char *name;
38
};
39
SLIST_HEAD(alias_warned_list, alias_warned);
40
41
static bool
42
alias_warned(struct alias_warned_list *warned, char *name)
43
0
{
44
0
    struct alias_warned *w;
45
0
    debug_decl(alias_warned, SUDOERS_DEBUG_ALIAS);
46
47
0
    SLIST_FOREACH(w, warned, entries) {
48
0
  if (strcmp(w->name, name) == 0)
49
0
      debug_return_bool(true);
50
0
    }
51
52
0
    debug_return_bool(false);
53
0
}
54
55
static void
56
alias_warned_add(struct alias_warned_list *warned, char *name)
57
0
{
58
0
    struct alias_warned *w;
59
0
    debug_decl(alias_warned_add, SUDOERS_DEBUG_ALIAS);
60
61
0
    w = malloc(sizeof(*w));
62
0
    if (w != NULL) {
63
0
  w->name = name;
64
0
  SLIST_INSERT_HEAD(warned, w, entries);
65
0
    }
66
67
0
    debug_return;
68
0
}
69
70
static int
71
check_alias(struct sudoers_parse_tree *parse_tree,
72
    struct alias_warned_list *warned, char *name, short type,
73
    char *file, int line, int column, bool strict, bool quiet)
74
0
{
75
0
    struct member *m;
76
0
    struct alias *a;
77
0
    int errors = 0;
78
0
    debug_decl(check_alias, SUDOERS_DEBUG_ALIAS);
79
80
0
    if ((a = alias_get(parse_tree, name, type)) != NULL) {
81
  /* check alias contents */
82
0
  TAILQ_FOREACH(m, &a->members, entries) {
83
0
      if (m->type != ALIAS)
84
0
    continue;
85
0
      errors += check_alias(parse_tree, warned, m->name, type,
86
0
    a->file, a->line, a->column, strict, quiet);
87
0
  }
88
0
  alias_put(a);
89
0
    } else {
90
0
  if (!alias_warned(warned, name)) {
91
0
      if (errno == ELOOP) {
92
0
    parser_warnx(parse_tree->ctx, file, line, column, strict, quiet,
93
0
        N_("cycle in %s \"%s\""), alias_type_to_string(type), name);
94
0
      } else {
95
0
    parser_warnx(parse_tree->ctx, file, line, column, strict, quiet,
96
0
        N_("%s \"%s\" referenced but not defined"),
97
0
        alias_type_to_string(type), name);
98
0
      }
99
0
      alias_warned_add(warned, name);
100
0
  }
101
0
  errors++;
102
0
    }
103
104
0
    debug_return_int(errors);
105
0
}
106
107
/*
108
 * Iterate through the sudoers datastructures looking for undefined
109
 * aliases or unused aliases.
110
 * In strict mode, returns the number of errors, else 0.
111
 */
112
int
113
check_aliases(struct sudoers_parse_tree *parse_tree, bool strict, bool quiet,
114
    int (*cb_unused)(struct sudoers_parse_tree *, struct alias *, void *))
115
1
{
116
1
    struct alias_warned_list warned = SLIST_HEAD_INITIALIZER(warned);
117
1
    struct rbtree *used_aliases;
118
1
    struct alias_warned *w;
119
1
    struct cmndspec *cs;
120
1
    struct member *m;
121
1
    struct privilege *priv;
122
1
    struct userspec *us;
123
1
    int errors = 0;
124
1
    debug_decl(check_aliases, SUDOERS_DEBUG_ALIAS);
125
126
1
    used_aliases = alloc_aliases();
127
1
    if (used_aliases == NULL) {
128
0
  sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
129
0
  debug_return_int(-1);
130
0
    }
131
132
    /* Forward check. */
133
1
    TAILQ_FOREACH(us, &parse_tree->userspecs, entries) {
134
0
  TAILQ_FOREACH(m, &us->users, entries) {
135
0
      if (m->type == ALIAS) {
136
0
    errors += check_alias(parse_tree, &warned, m->name, USERALIAS,
137
0
        us->file, us->line, us->column, strict, quiet);
138
0
      }
139
0
  }
140
0
  TAILQ_FOREACH(priv, &us->privileges, entries) {
141
0
      TAILQ_FOREACH(m, &priv->hostlist, entries) {
142
0
    if (m->type == ALIAS) {
143
0
        errors += check_alias(parse_tree, &warned, m->name, HOSTALIAS,
144
0
      us->file, us->line, us->column, strict, quiet);
145
0
    }
146
0
      }
147
0
      TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
148
0
    if (cs->runasuserlist != NULL) {
149
0
        TAILQ_FOREACH(m, cs->runasuserlist, entries) {
150
0
      if (m->type == ALIAS) {
151
0
          errors += check_alias(parse_tree, &warned, m->name, RUNASALIAS,
152
0
        us->file, us->line, us->column, strict, quiet);
153
0
      }
154
0
        }
155
0
    }
156
0
    if (cs->runasgrouplist != NULL) {
157
0
        TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
158
0
      if (m->type == ALIAS) {
159
0
          errors += check_alias(parse_tree, &warned, m->name, RUNASALIAS,
160
0
        us->file, us->line, us->column, strict, quiet);
161
0
      }
162
0
        }
163
0
    }
164
0
    if ((m = cs->cmnd)->type == ALIAS) {
165
0
        errors += check_alias(parse_tree, &warned, m->name, CMNDALIAS,
166
0
      us->file, us->line, us->column, strict, quiet);
167
0
    }
168
0
      }
169
0
  }
170
0
    }
171
1
    while ((w = SLIST_FIRST(&warned)) != NULL) {
172
0
  SLIST_REMOVE_HEAD(&warned, entries);
173
0
  free(w);
174
0
    }
175
176
    /* Reverse check (destructive) */
177
1
    if (!alias_find_used(parse_tree, used_aliases))
178
0
  errors++;
179
1
    free_aliases(used_aliases);
180
181
    /* If all aliases were referenced we will have an empty tree. */
182
1
    if (!no_aliases(parse_tree)) {
183
0
  if (!alias_apply(parse_tree, cb_unused, &quiet))
184
0
      errors++;
185
0
    }
186
187
1
    debug_return_int(strict ? errors : 0);
188
1
}