Coverage Report

Created: 2025-11-11 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sudo/plugins/sudoers/display.c
Line
Count
Source
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 2004-2005, 2007-2024 Todd C. Miller <Todd.Miller@sudo.ws>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <config.h>
20
21
#include <sys/stat.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <unistd.h>
26
#include <ctype.h>
27
#include <pwd.h>
28
29
#include <sudoers.h>
30
#include <sudo_lbuf.h>
31
#include <gram.h>
32
33
static int
34
display_priv_short(const struct sudoers_parse_tree *parse_tree,
35
    const struct passwd *pw, const struct userspec *us, struct sudo_lbuf *lbuf)
36
1.15k
{
37
1.15k
    struct privilege *priv;
38
1.15k
    int nfound = 0;
39
1.15k
    debug_decl(display_priv_short, SUDOERS_DEBUG_PARSER);
40
41
1.17k
    TAILQ_FOREACH(priv, &us->privileges, entries) {
42
1.17k
  struct cmndspec *cs;
43
1.17k
  struct cmndtag tags;
44
45
1.17k
  if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW)
46
1.05k
      continue;
47
48
127
  sudoers_defaults_list_to_tags(&priv->defaults, &tags);
49
156
  TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
50
156
      struct cmndspec *prev_cs = TAILQ_PREV(cs, cmndspec_list, entries);
51
52
156
      if (prev_cs == NULL || RUNAS_CHANGED(cs, prev_cs)) {
53
127
    struct member *m;
54
55
    /* Start new line, first entry or RunAs changed. */
56
127
    if (prev_cs != NULL)
57
0
        sudo_lbuf_append(lbuf, "\n");
58
127
    sudo_lbuf_append(lbuf, "    (");
59
127
    if (cs->runasuserlist != NULL) {
60
979
        TAILQ_FOREACH(m, cs->runasuserlist, entries) {
61
979
      if (m != TAILQ_FIRST(cs->runasuserlist))
62
909
          sudo_lbuf_append(lbuf, ", ");
63
979
      sudoers_format_member(lbuf, parse_tree, m, ", ",
64
979
          RUNASALIAS);
65
979
        }
66
70
    } else if (cs->runasgrouplist == NULL) {
67
57
        sudo_lbuf_append(lbuf, "%s", def_runas_default);
68
57
    } else {
69
0
        sudo_lbuf_append(lbuf, "%s", pw->pw_name);
70
0
    }
71
127
    if (cs->runasgrouplist != NULL) {
72
0
        sudo_lbuf_append(lbuf, " : ");
73
0
        TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
74
0
      if (m != TAILQ_FIRST(cs->runasgrouplist))
75
0
          sudo_lbuf_append(lbuf, ", ");
76
0
      sudoers_format_member(lbuf, parse_tree, m, ", ",
77
0
          RUNASALIAS);
78
0
        }
79
0
    }
80
127
    sudo_lbuf_append(lbuf, ") ");
81
127
    sudoers_format_cmndspec(lbuf, parse_tree, cs, NULL,
82
127
        tags, true);
83
127
      } else {
84
    /* Continue existing line. */
85
29
    sudo_lbuf_append(lbuf, ", ");
86
29
    sudoers_format_cmndspec(lbuf, parse_tree, cs, prev_cs,
87
29
        tags, true);
88
29
      }
89
156
      nfound++;
90
156
  }
91
127
  sudo_lbuf_append(lbuf, "\n");
92
127
    }
93
1.15k
    debug_return_int(nfound);
94
1.15k
}
95
96
/*
97
 * Compare the current cmndspec with the previous one to determine
98
 * whether we need to start a new long entry for "sudo -ll".
99
 * Returns true if we should start a new long entry, else false.
100
 */
101
static bool
102
new_long_entry(const struct cmndspec *cs, const struct cmndspec *prev_cs)
103
156
{
104
156
    debug_decl(new_long_entry, SUDOERS_DEBUG_PARSER);
105
106
156
    if (prev_cs == NULL)
107
127
  debug_return_bool(true);
108
29
    if (RUNAS_CHANGED(cs, prev_cs) || TAGS_CHANGED(prev_cs->tags, cs->tags))
109
9
  debug_return_bool(true);
110
20
    if (cs->privs && (!prev_cs->privs || strcmp(cs->privs, prev_cs->privs) != 0))
111
0
  debug_return_bool(true);
112
20
    if (cs->limitprivs && (!prev_cs->limitprivs || strcmp(cs->limitprivs, prev_cs->limitprivs) != 0))
113
0
  debug_return_bool(true);
114
20
    if (cs->role && (!prev_cs->role || strcmp(cs->role, prev_cs->role) != 0))
115
0
  debug_return_bool(true);
116
20
    if (cs->type && (!prev_cs->type || strcmp(cs->type, prev_cs->type) != 0))
117
0
  debug_return_bool(true);
118
20
    if (cs->apparmor_profile && (!prev_cs->apparmor_profile || strcmp(cs->apparmor_profile, prev_cs->apparmor_profile) != 0))
119
0
  debug_return_bool(true);
120
20
    if (cs->runchroot && (!prev_cs->runchroot || strcmp(cs->runchroot, prev_cs->runchroot) != 0))
121
0
  debug_return_bool(true);
122
20
    if (cs->runcwd && (!prev_cs->runcwd || strcmp(cs->runcwd, prev_cs->runcwd) != 0))
123
0
  debug_return_bool(true);
124
20
    if (cs->timeout != prev_cs->timeout)
125
0
  debug_return_bool(true);
126
20
    if (cs->notbefore != prev_cs->notbefore)
127
11
  debug_return_bool(true);
128
9
    if (cs->notafter != prev_cs->notafter)
129
0
  debug_return_bool(true);
130
9
    debug_return_bool(false);
131
9
}
132
133
static void
134
display_cmndspec_long(const struct sudoers_parse_tree *parse_tree,
135
    const struct passwd *pw, const struct userspec *us,
136
    const struct privilege *priv, const struct cmndspec *cs,
137
    const struct cmndspec *prev_cs, struct sudo_lbuf *lbuf)
138
156
{
139
156
    const struct defaults *d;
140
156
    const struct member *m;
141
156
    debug_decl(display_cmndspec_long, SUDOERS_DEBUG_PARSER);
142
143
156
    if (new_long_entry(cs, prev_cs)) {
144
147
  unsigned int olen;
145
146
147
  if (prev_cs != NULL)
147
20
      sudo_lbuf_append(lbuf, "\n");
148
147
  if (priv->ldap_role != NULL) {
149
0
      sudo_lbuf_append(lbuf, _("LDAP Role: %s\n"),
150
0
    priv->ldap_role);
151
147
  } else {
152
147
      sudo_lbuf_append(lbuf, _("Sudoers entry: %s\n"),
153
147
    us->file);
154
147
  }
155
147
  sudo_lbuf_append(lbuf, "%s", _("    RunAsUsers: "));
156
147
  if (cs->runasuserlist != NULL) {
157
3.34k
      TAILQ_FOREACH(m, cs->runasuserlist, entries) {
158
3.34k
    if (m != TAILQ_FIRST(cs->runasuserlist))
159
3.25k
        sudo_lbuf_append(lbuf, ", ");
160
3.34k
    sudoers_format_member(lbuf, parse_tree, m, ", ",
161
3.34k
        RUNASALIAS);
162
3.34k
      }
163
90
  } else if (cs->runasgrouplist == NULL) {
164
57
      sudo_lbuf_append(lbuf, "%s", def_runas_default);
165
57
  } else {
166
0
      sudo_lbuf_append(lbuf, "%s", pw->pw_name);
167
0
  }
168
147
  sudo_lbuf_append(lbuf, "\n");
169
147
  if (cs->runasgrouplist != NULL) {
170
0
      sudo_lbuf_append(lbuf, "%s", _("    RunAsGroups: "));
171
0
      TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
172
0
    if (m != TAILQ_FIRST(cs->runasgrouplist))
173
0
        sudo_lbuf_append(lbuf, ", ");
174
0
    sudoers_format_member(lbuf, parse_tree, m, ", ",
175
0
        RUNASALIAS);
176
0
      }
177
0
      sudo_lbuf_append(lbuf, "\n");
178
0
  }
179
147
  olen = lbuf->len;
180
147
  sudo_lbuf_append(lbuf, "%s", _("    Options: "));
181
147
  TAILQ_FOREACH(d, &priv->defaults, entries) {
182
0
      sudoers_format_default(lbuf, d);
183
0
      sudo_lbuf_append(lbuf, ", ");
184
0
  }
185
147
  if (TAG_SET(cs->tags.setenv))
186
0
      sudo_lbuf_append(lbuf, "%ssetenv, ", cs->tags.setenv ? "" : "!");
187
147
  if (TAG_SET(cs->tags.noexec))
188
0
      sudo_lbuf_append(lbuf, "%snoexec, ", cs->tags.noexec ? "" : "!");
189
147
  if (TAG_SET(cs->tags.intercept))
190
0
      sudo_lbuf_append(lbuf, "%sintercept, ", cs->tags.intercept ? "" : "!");
191
147
  if (TAG_SET(cs->tags.nopasswd))
192
20
      sudo_lbuf_append(lbuf, "%sauthenticate, ", cs->tags.nopasswd ? "!" : "");
193
147
  if (TAG_SET(cs->tags.log_input))
194
0
      sudo_lbuf_append(lbuf, "%slog_input, ", cs->tags.log_input ? "" : "!");
195
147
  if (TAG_SET(cs->tags.log_output))
196
0
      sudo_lbuf_append(lbuf, "%slog_output, ", cs->tags.log_output ? "" : "!");
197
147
  if (lbuf->buf[lbuf->len - 2] == ',') {
198
20
      lbuf->len -= 2; /* remove trailing ", " */
199
20
      sudo_lbuf_append(lbuf, "\n");
200
127
  } else {
201
127
      lbuf->len = olen; /* no options */
202
127
  }
203
147
  if (cs->apparmor_profile != NULL) {
204
0
      sudo_lbuf_append(lbuf, "    ApparmorProfile: %s\n",
205
0
    cs->apparmor_profile);
206
0
  }
207
147
  if (cs->privs != NULL)
208
0
      sudo_lbuf_append(lbuf, "    Privs: %s\n", cs->privs);
209
147
  if (cs->limitprivs != NULL)
210
0
      sudo_lbuf_append(lbuf, "    Limitprivs: %s\n", cs->limitprivs);
211
147
  if (cs->role != NULL)
212
0
      sudo_lbuf_append(lbuf, "    Role: %s\n", cs->role);
213
147
  if (cs->type != NULL)
214
0
      sudo_lbuf_append(lbuf, "    Type: %s\n", cs->type);
215
147
  if (cs->runchroot != NULL)
216
0
      sudo_lbuf_append(lbuf, "    Chroot: %s\n", cs->runchroot);
217
147
  if (cs->runcwd != NULL)
218
2
      sudo_lbuf_append(lbuf, "    Cwd: %s\n", cs->runcwd);
219
147
  if (cs->timeout > 0) {
220
0
      char numbuf[STRLEN_MAX_SIGNED(int) + 1];
221
0
      (void)snprintf(numbuf, sizeof(numbuf), "%d", cs->timeout);
222
0
      sudo_lbuf_append(lbuf, "    Timeout: %s\n", numbuf);
223
0
  }
224
147
  if (cs->notbefore != UNSPEC) {
225
11
      char buf[sizeof("CCYYMMDDHHMMSSZ")] = "";
226
11
      struct tm gmt;
227
11
      size_t len;
228
11
      if (gmtime_r(&cs->notbefore, &gmt) != NULL) {
229
11
    len = strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &gmt);
230
11
    if (len != 0 && buf[sizeof(buf) - 1] == '\0')
231
11
        sudo_lbuf_append(lbuf, "    NotBefore: %s\n", buf);
232
11
      }
233
11
  }
234
147
  if (cs->notafter != UNSPEC) {
235
0
      char buf[sizeof("CCYYMMDDHHMMSSZ")] = "";
236
0
      struct tm gmt;
237
0
      size_t len;
238
0
      if (gmtime_r(&cs->notafter, &gmt) != NULL) {
239
0
    len = strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &gmt);
240
0
    if (len != 0 && buf[sizeof(buf) - 1] == '\0')
241
0
        sudo_lbuf_append(lbuf, "    NotAfter: %s\n", buf);
242
0
      }
243
0
  }
244
147
  sudo_lbuf_append(lbuf, "%s", _("    Commands:\n"));
245
147
    }
246
156
    sudo_lbuf_append(lbuf, "\t");
247
156
    sudoers_format_member(lbuf, parse_tree, cs->cmnd, "\n\t",
248
156
  CMNDALIAS);
249
156
    sudo_lbuf_append(lbuf, "\n");
250
251
156
    debug_return;
252
156
}
253
254
static int
255
display_priv_long(const struct sudoers_parse_tree *parse_tree,
256
    const struct passwd *pw, const struct userspec *us, struct sudo_lbuf *lbuf)
257
1.15k
{
258
1.15k
    const struct privilege *priv;
259
1.15k
    int nfound = 0;
260
1.15k
    debug_decl(display_priv_long, SUDOERS_DEBUG_PARSER);
261
262
1.17k
    TAILQ_FOREACH(priv, &us->privileges, entries) {
263
1.17k
  const struct cmndspec *cs, *prev_cs;
264
265
1.17k
  if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW)
266
1.05k
      continue;
267
127
  prev_cs = NULL;
268
127
  sudo_lbuf_append(lbuf, "\n");
269
156
  TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
270
156
      display_cmndspec_long(parse_tree, pw, us, priv, cs, prev_cs,
271
156
    lbuf);
272
156
      prev_cs = cs;
273
156
      nfound++;
274
156
  }
275
127
    }
276
1.15k
    debug_return_int(nfound);
277
1.15k
}
278
279
static int
280
sudo_display_userspecs(struct sudoers_parse_tree *parse_tree,
281
    const struct passwd *pw, struct sudo_lbuf *lbuf, bool verbose)
282
24
{
283
24
    const struct userspec *us;
284
24
    int nfound = 0;
285
24
    debug_decl(sudo_display_userspecs, SUDOERS_DEBUG_PARSER);
286
287
6.78k
    TAILQ_FOREACH(us, &parse_tree->userspecs, entries) {
288
6.78k
  if (userlist_matches(parse_tree, pw, &us->users) != ALLOW)
289
4.47k
      continue;
290
291
2.31k
  if (verbose)
292
1.15k
      nfound += display_priv_long(parse_tree, pw, us, lbuf);
293
1.15k
  else
294
1.15k
      nfound += display_priv_short(parse_tree, pw, us, lbuf);
295
2.31k
    }
296
24
    if (sudo_lbuf_error(lbuf))
297
0
  debug_return_int(-1);
298
24
    debug_return_int(nfound);
299
24
}
300
301
/*
302
 * Display matching Defaults entries for the given user on this host.
303
 */
304
static int
305
display_defaults(const struct sudoers_parse_tree *parse_tree,
306
    const struct passwd *pw, struct sudo_lbuf *lbuf)
307
24
{
308
24
    const struct defaults *d;
309
24
    const char *prefix;
310
24
    int nfound = 0;
311
24
    debug_decl(display_defaults, SUDOERS_DEBUG_PARSER);
312
313
24
    if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
314
24
  prefix = "    ";
315
0
    else
316
0
  prefix = ", ";
317
318
27.0k
    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
319
27.0k
  switch (d->type) {
320
0
      case DEFAULTS_HOST:
321
0
    if (hostlist_matches(parse_tree, pw, &d->binding->members) != ALLOW)
322
0
        continue;
323
0
    break;
324
648
      case DEFAULTS_USER:
325
648
    if (userlist_matches(parse_tree, pw, &d->binding->members) != ALLOW)
326
486
        continue;
327
162
    break;
328
576
      case DEFAULTS_RUNAS:
329
1.05k
      case DEFAULTS_CMND:
330
1.05k
    continue;
331
27.0k
  }
332
25.4k
  sudo_lbuf_append(lbuf, "%s", prefix);
333
25.4k
  sudoers_format_default(lbuf, d);
334
25.4k
  prefix = ", ";
335
25.4k
  nfound++;
336
25.4k
    }
337
24
    if (sudo_lbuf_error(lbuf))
338
0
  debug_return_int(-1);
339
24
    debug_return_int(nfound);
340
24
}
341
342
/*
343
 * Display Defaults entries of the given type.
344
 */
345
static int
346
display_bound_defaults_by_type(const struct sudoers_parse_tree *parse_tree,
347
    int deftype, struct sudo_lbuf *lbuf)
348
48
{
349
48
    const struct defaults *d;
350
48
    const struct defaults_binding *binding = NULL;
351
48
    const struct member *m;
352
48
    const char *dsep;
353
48
    short atype;
354
48
    int nfound = 0;
355
48
    debug_decl(display_bound_defaults_by_type, SUDOERS_DEBUG_PARSER);
356
357
48
    switch (deftype) {
358
0
  case DEFAULTS_HOST:
359
0
      atype = HOSTALIAS;
360
0
      dsep = "@";
361
0
      break;
362
0
  case DEFAULTS_USER:
363
0
      atype = USERALIAS;
364
0
      dsep = ":";
365
0
      break;
366
24
  case DEFAULTS_RUNAS:
367
24
      atype = RUNASALIAS;
368
24
      dsep = ">";
369
24
      break;
370
24
  case DEFAULTS_CMND:
371
24
      atype = CMNDALIAS;
372
24
      dsep = "!";
373
24
      break;
374
0
  default:
375
0
      debug_return_int(-1);
376
48
    }
377
54.0k
    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
378
54.0k
  if (d->type != deftype)
379
53.0k
      continue;
380
381
1.05k
  nfound++;
382
1.05k
  if (binding != d->binding) {
383
528
      binding = d->binding;
384
528
      if (nfound != 1)
385
512
    sudo_lbuf_append(lbuf, "\n");
386
528
      sudo_lbuf_append(lbuf, "    Defaults%s", dsep);
387
760
      TAILQ_FOREACH(m, &binding->members, entries) {
388
760
    if (m != TAILQ_FIRST(&binding->members))
389
232
        sudo_lbuf_append(lbuf, ", ");
390
760
    sudoers_format_member(lbuf, parse_tree, m, ", ", atype);
391
760
      }
392
528
      sudo_lbuf_append(lbuf, " ");
393
528
  } else
394
528
      sudo_lbuf_append(lbuf, ", ");
395
1.05k
  sudoers_format_default(lbuf, d);
396
1.05k
    }
397
398
48
    if (sudo_lbuf_error(lbuf))
399
0
  debug_return_int(-1);
400
48
    debug_return_int(nfound);
401
48
}
402
403
/*
404
 * Display Defaults entries that are per-runas or per-command
405
 */
406
static int
407
display_bound_defaults(const struct sudoers_parse_tree *parse_tree,
408
    const struct passwd *pw, struct sudo_lbuf *lbuf)
409
24
{
410
24
    int nfound = 0;
411
24
    debug_decl(display_bound_defaults, SUDOERS_DEBUG_PARSER);
412
413
    /* XXX - should only print ones that match what the user can do. */
414
24
    nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_RUNAS,
415
24
  lbuf);
416
24
    nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_CMND,
417
24
  lbuf);
418
419
24
    if (sudo_lbuf_error(lbuf))
420
0
  debug_return_int(-1);
421
24
    debug_return_int(nfound);
422
24
}
423
424
static int
425
output(const char *buf)
426
32
{
427
32
    struct sudo_conv_message msg;
428
32
    struct sudo_conv_reply repl;
429
32
    debug_decl(output, SUDOERS_DEBUG_NSS);
430
431
    /* Call conversation function */
432
32
    memset(&msg, 0, sizeof(msg));
433
32
    msg.msg_type = SUDO_CONV_INFO_MSG;
434
32
    msg.msg = buf;
435
32
    memset(&repl, 0, sizeof(repl));
436
32
    if (sudo_conv(1, &msg, &repl, NULL) == -1)
437
0
  debug_return_int(0);
438
32
    debug_return_int((int)strlen(buf));
439
32
}
440
441
/*
442
 * Print out privileges for the specified user.
443
 * Returns true on success or -1 on error.
444
 */
445
int
446
display_privs(struct sudoers_context *ctx, const struct sudo_nss_list *snl,
447
    struct passwd *pw, int verbose)
448
24
{
449
24
    const struct sudo_nss *nss;
450
24
    struct sudo_lbuf def_buf, priv_buf;
451
24
    int cols, count, n;
452
24
    unsigned int olen;
453
24
    struct stat sb;
454
24
    debug_decl(display_privs, SUDOERS_DEBUG_PARSER);
455
456
24
    if (verbose < 0) {
457
  /* Nothing to display. */
458
0
  debug_return_int(true);
459
0
    }
460
461
24
    cols = ctx->user.cols;
462
24
    if (fstat(STDOUT_FILENO, &sb) == 0 && S_ISFIFO(sb.st_mode))
463
0
  cols = 0;
464
24
    sudo_lbuf_init(&def_buf, output, 4, NULL, cols);
465
24
    sudo_lbuf_init(&priv_buf, output, 8, NULL, cols);
466
467
24
    sudo_lbuf_append(&def_buf, _("Matching Defaults entries for %s on %s:\n"),
468
24
  pw->pw_name, ctx->runas.shost);
469
24
    count = 0;
470
24
    TAILQ_FOREACH(nss, snl, entries) {
471
24
  n = display_defaults(nss->parse_tree, pw, &def_buf);
472
24
  if (n == -1)
473
0
      goto bad;
474
24
  count += n;
475
24
    }
476
24
    if (count != 0) {
477
8
  sudo_lbuf_append(&def_buf, "\n\n");
478
16
    } else {
479
  /* Undo Defaults header. */
480
16
  def_buf.len = 0;
481
16
    }
482
483
    /* Display Runas and Cmnd-specific defaults. */
484
24
    olen = def_buf.len;
485
24
    sudo_lbuf_append(&def_buf, _("Runas and Command-specific defaults for %s:\n"),
486
24
  pw->pw_name);
487
24
    count = 0;
488
24
    TAILQ_FOREACH(nss, snl, entries) {
489
24
  n = display_bound_defaults(nss->parse_tree, pw, &def_buf);
490
24
  if (n == -1)
491
0
      goto bad;
492
24
  count += n;
493
24
    }
494
24
    if (count != 0) {
495
8
  sudo_lbuf_append(&def_buf, "\n\n");
496
16
    } else {
497
  /* Undo Defaults header. */
498
16
  def_buf.len = olen;
499
16
    }
500
501
    /* Display privileges from all sources. */
502
24
    sudo_lbuf_append(&priv_buf,
503
24
  _("User %s may run the following commands on %s:\n"),
504
24
  pw->pw_name, ctx->runas.shost);
505
24
    count = 0;
506
24
    TAILQ_FOREACH(nss, snl, entries) {
507
24
  if (nss->query(ctx, nss, pw) != -1) {
508
24
      n = sudo_display_userspecs(nss->parse_tree, pw, &priv_buf,
509
24
    verbose);
510
24
      if (n == -1)
511
0
    goto bad;
512
24
      count += n;
513
24
  }
514
24
    }
515
24
    if (count == 0) {
516
16
  def_buf.len = 0;
517
16
  priv_buf.len = 0;
518
16
  sudo_lbuf_append(&priv_buf,
519
16
      _("User %s is not allowed to run sudo on %s.\n"),
520
16
      pw->pw_name, ctx->runas.shost);
521
16
    }
522
24
    if (sudo_lbuf_error(&def_buf) || sudo_lbuf_error(&priv_buf))
523
0
  goto bad;
524
525
24
    sudo_lbuf_print(&def_buf);
526
24
    sudo_lbuf_print(&priv_buf);
527
528
24
    sudo_lbuf_destroy(&def_buf);
529
24
    sudo_lbuf_destroy(&priv_buf);
530
531
24
    debug_return_int(true);
532
0
bad:
533
0
    sudo_lbuf_destroy(&def_buf);
534
0
    sudo_lbuf_destroy(&priv_buf);
535
536
0
    debug_return_int(-1);
537
0
}
538
539
static int
540
display_cmnd_check(struct sudoers_context *ctx,
541
    const struct sudoers_parse_tree *parse_tree, const struct passwd *pw,
542
    time_t now, struct sudoers_match_info *match_info)
543
0
{
544
0
    int host_match, runas_match, cmnd_match = UNSPEC;
545
0
    char *saved_user_cmnd, *saved_user_base;
546
0
    const struct privilege *priv;
547
0
    const struct userspec *us;
548
0
    const struct cmndspec *cs;
549
0
    debug_decl(display_cmnd_check, SUDOERS_DEBUG_PARSER);
550
551
    /*
552
     * For "sudo -l command", ctx->user.cmnd is "list" and the actual
553
     * command we are checking is in ctx->user.cmnd_list.
554
     */
555
0
    saved_user_cmnd = ctx->user.cmnd;
556
0
    saved_user_base = ctx->user.cmnd_base;
557
0
    ctx->user.cmnd = ctx->user.cmnd_list;
558
0
    ctx->user.cmnd_base = sudo_basename(ctx->user.cmnd);
559
560
0
    TAILQ_FOREACH_REVERSE(us, &parse_tree->userspecs, userspec_list, entries) {
561
0
  if (userlist_matches(parse_tree, pw, &us->users) != ALLOW)
562
0
      continue;
563
0
  TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) {
564
0
      host_match = hostlist_matches(parse_tree, pw, &priv->hostlist);
565
0
      if (host_match != ALLOW)
566
0
    continue;
567
0
      TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) {
568
0
    if (cs->notbefore != UNSPEC) {
569
0
        if (now < cs->notbefore)
570
0
      continue;
571
0
    }
572
0
    if (cs->notafter != UNSPEC) {
573
0
        if (now > cs->notafter)
574
0
      continue;
575
0
    }
576
0
    runas_match = runaslist_matches(parse_tree, cs->runasuserlist,
577
0
        cs->runasgrouplist);
578
0
    if (runas_match == ALLOW) {
579
0
        cmnd_match = cmnd_matches(parse_tree, cs->cmnd,
580
0
      cs->runchroot, NULL);
581
0
        if (cmnd_match != UNSPEC) {
582
0
      match_info->parse_tree = parse_tree;
583
0
      match_info->us = us;
584
0
      match_info->priv = priv;
585
0
      match_info->cs = cs;
586
0
      goto done;
587
0
        }
588
0
    }
589
0
      }
590
0
  }
591
0
    }
592
0
done:
593
0
    ctx->user.cmnd = saved_user_cmnd;
594
0
    ctx->user.cmnd_base = saved_user_base;
595
0
    debug_return_int(cmnd_match);
596
0
}
597
598
/*
599
 * Check ctx->user.cmnd against sudoers and print the matching entry if the
600
 * command is allowed.
601
 * Returns true if the command is allowed, false if not or -1 on error.
602
 */
603
int
604
display_cmnd(struct sudoers_context *ctx, const struct sudo_nss_list *snl,
605
    struct passwd *pw, int verbose)
606
0
{
607
0
    struct sudoers_match_info match_info = { NULL };
608
0
    struct sudo_lbuf lbuf;
609
0
    struct sudo_nss *nss;
610
0
    int m, match = UNSPEC;
611
0
    int ret = false;
612
0
    time_t now;
613
0
    debug_decl(display_cmnd, SUDOERS_DEBUG_PARSER);
614
615
    /* Iterate over each source, checking for the command. */
616
0
    time(&now);
617
0
    sudo_lbuf_init(&lbuf, output, 0, NULL, 0);
618
0
    TAILQ_FOREACH(nss, snl, entries) {
619
0
  if (nss->query(ctx, nss, pw) == -1) {
620
      /* The query function should have printed an error message. */
621
0
      debug_return_int(-1);
622
0
  }
623
624
0
  m = display_cmnd_check(ctx, nss->parse_tree, pw, now, &match_info);
625
0
  if (m != UNSPEC)
626
0
      match = m;
627
628
0
  if (!sudo_nss_can_continue(nss, m))
629
0
      break;
630
0
    }
631
0
    if (match == ALLOW) {
632
0
  if (verbose < 0) {
633
      /* Nothing to display. */
634
0
      debug_return_int(true);
635
0
  }
636
0
  if (verbose) {
637
      /* Append matching sudoers rule (long form). */
638
0
      display_cmndspec_long(match_info.parse_tree, pw, match_info.us,
639
0
    match_info.priv, match_info.cs, NULL, &lbuf);
640
0
      sudo_lbuf_append(&lbuf, "    Matched: ");
641
0
  }
642
0
  sudo_lbuf_append(&lbuf, "%s%s%s\n", ctx->user.cmnd_list,
643
0
      ctx->user.cmnd_args ? " " : "",
644
0
      ctx->user.cmnd_args ? ctx->user.cmnd_args : "");
645
0
  sudo_lbuf_print(&lbuf);
646
0
  ret = sudo_lbuf_error(&lbuf) ? -1 : true;
647
0
  sudo_lbuf_destroy(&lbuf);
648
0
    }
649
0
    debug_return_int(ret);
650
0
}