Coverage Report

Created: 2025-11-09 06:38

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