Coverage Report

Created: 2026-05-16 07:05

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
15
{
37
15
    struct privilege *priv;
38
15
    int nfound = 0;
39
15
    debug_decl(display_priv_short, SUDOERS_DEBUG_PARSER);
40
41
15
    TAILQ_FOREACH(priv, &us->privileges, entries) {
42
15
  struct cmndspec *cs;
43
15
  struct cmndtag tags;
44
45
15
  if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW)
46
0
      continue;
47
48
15
  sudoers_defaults_list_to_tags(&priv->defaults, &tags);
49
15
  TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
50
15
      struct cmndspec *prev_cs = TAILQ_PREV(cs, cmndspec_list, entries);
51
52
15
      if (prev_cs == NULL || RUNAS_CHANGED(cs, prev_cs)) {
53
15
    struct member *m;
54
55
    /* Start new line, first entry or RunAs changed. */
56
15
    if (prev_cs != NULL)
57
0
        sudo_lbuf_append(lbuf, "\n");
58
15
    sudo_lbuf_append(lbuf, "    (");
59
15
    if (cs->runasuserlist != NULL) {
60
0
        TAILQ_FOREACH(m, cs->runasuserlist, entries) {
61
0
      if (m != TAILQ_FIRST(cs->runasuserlist))
62
0
          sudo_lbuf_append(lbuf, ", ");
63
0
      sudoers_format_member(lbuf, parse_tree, m, ", ",
64
0
          RUNASALIAS);
65
0
        }
66
15
    } else if (cs->runasgrouplist == NULL) {
67
15
        sudo_lbuf_append(lbuf, "%s", def_runas_default);
68
15
    } else {
69
0
        sudo_lbuf_append(lbuf, "%s", pw->pw_name);
70
0
    }
71
15
    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
15
    sudo_lbuf_append(lbuf, ") ");
81
15
    sudoers_format_cmndspec(lbuf, parse_tree, cs, NULL,
82
15
        tags, true);
83
15
      } else {
84
    /* Continue existing line. */
85
0
    sudo_lbuf_append(lbuf, ", ");
86
0
    sudoers_format_cmndspec(lbuf, parse_tree, cs, prev_cs,
87
0
        tags, true);
88
0
      }
89
15
      nfound++;
90
15
  }
91
15
  sudo_lbuf_append(lbuf, "\n");
92
15
    }
93
15
    debug_return_int(nfound);
94
15
}
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
15
{
104
15
    debug_decl(new_long_entry, SUDOERS_DEBUG_PARSER);
105
106
15
    if (prev_cs == NULL)
107
15
  debug_return_bool(true);
108
0
    if (RUNAS_CHANGED(cs, prev_cs) || TAGS_CHANGED(prev_cs->tags, cs->tags))
109
0
  debug_return_bool(true);
110
0
    if (cs->privs && (!prev_cs->privs || strcmp(cs->privs, prev_cs->privs) != 0))
111
0
  debug_return_bool(true);
112
0
    if (cs->limitprivs && (!prev_cs->limitprivs || strcmp(cs->limitprivs, prev_cs->limitprivs) != 0))
113
0
  debug_return_bool(true);
114
0
    if (cs->role && (!prev_cs->role || strcmp(cs->role, prev_cs->role) != 0))
115
0
  debug_return_bool(true);
116
0
    if (cs->type && (!prev_cs->type || strcmp(cs->type, prev_cs->type) != 0))
117
0
  debug_return_bool(true);
118
0
    if (cs->apparmor_profile && (!prev_cs->apparmor_profile || strcmp(cs->apparmor_profile, prev_cs->apparmor_profile) != 0))
119
0
  debug_return_bool(true);
120
0
    if (cs->runchroot && (!prev_cs->runchroot || strcmp(cs->runchroot, prev_cs->runchroot) != 0))
121
0
  debug_return_bool(true);
122
0
    if (cs->runcwd && (!prev_cs->runcwd || strcmp(cs->runcwd, prev_cs->runcwd) != 0))
123
0
  debug_return_bool(true);
124
0
    if (cs->timeout != prev_cs->timeout)
125
0
  debug_return_bool(true);
126
0
    if (cs->notbefore != prev_cs->notbefore)
127
0
  debug_return_bool(true);
128
0
    if (cs->notafter != prev_cs->notafter)
129
0
  debug_return_bool(true);
130
0
    debug_return_bool(false);
131
0
}
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
15
{
139
15
    const struct defaults *d;
140
15
    const struct member *m;
141
15
    debug_decl(display_cmndspec_long, SUDOERS_DEBUG_PARSER);
142
143
15
    if (new_long_entry(cs, prev_cs)) {
144
15
  unsigned int olen;
145
146
15
  if (prev_cs != NULL)
147
0
      sudo_lbuf_append(lbuf, "\n");
148
15
  if (priv->ldap_role != NULL) {
149
0
      sudo_lbuf_append(lbuf, _("LDAP Role: %s\n"),
150
0
    priv->ldap_role);
151
15
  } else {
152
15
      sudo_lbuf_append(lbuf, _("Sudoers entry: %s\n"),
153
15
    us->file);
154
15
  }
155
15
  sudo_lbuf_append(lbuf, "%s", _("    RunAsUsers: "));
156
15
  if (cs->runasuserlist != NULL) {
157
0
      TAILQ_FOREACH(m, cs->runasuserlist, entries) {
158
0
    if (m != TAILQ_FIRST(cs->runasuserlist))
159
0
        sudo_lbuf_append(lbuf, ", ");
160
0
    sudoers_format_member(lbuf, parse_tree, m, ", ",
161
0
        RUNASALIAS);
162
0
      }
163
15
  } else if (cs->runasgrouplist == NULL) {
164
15
      sudo_lbuf_append(lbuf, "%s", def_runas_default);
165
15
  } else {
166
0
      sudo_lbuf_append(lbuf, "%s", pw->pw_name);
167
0
  }
168
15
  sudo_lbuf_append(lbuf, "\n");
169
15
  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
15
  olen = lbuf->len;
180
15
  sudo_lbuf_append(lbuf, "%s", _("    Options: "));
181
15
  TAILQ_FOREACH(d, &priv->defaults, entries) {
182
0
      sudoers_format_default(lbuf, d);
183
0
      sudo_lbuf_append(lbuf, ", ");
184
0
  }
185
15
  if (TAG_SET(cs->tags.setenv))
186
0
      sudo_lbuf_append(lbuf, "%ssetenv, ", cs->tags.setenv ? "" : "!");
187
15
  if (TAG_SET(cs->tags.noexec))
188
0
      sudo_lbuf_append(lbuf, "%snoexec, ", cs->tags.noexec ? "" : "!");
189
15
  if (TAG_SET(cs->tags.intercept))
190
0
      sudo_lbuf_append(lbuf, "%sintercept, ", cs->tags.intercept ? "" : "!");
191
15
  if (TAG_SET(cs->tags.nopasswd))
192
0
      sudo_lbuf_append(lbuf, "%sauthenticate, ", cs->tags.nopasswd ? "!" : "");
193
15
  if (TAG_SET(cs->tags.log_input))
194
0
      sudo_lbuf_append(lbuf, "%slog_input, ", cs->tags.log_input ? "" : "!");
195
15
  if (TAG_SET(cs->tags.log_output))
196
0
      sudo_lbuf_append(lbuf, "%slog_output, ", cs->tags.log_output ? "" : "!");
197
15
  if (lbuf->buf[lbuf->len - 2] == ',') {
198
0
      lbuf->len -= 2; /* remove trailing ", " */
199
0
      sudo_lbuf_append(lbuf, "\n");
200
15
  } else {
201
15
      lbuf->len = olen; /* no options */
202
15
  }
203
15
  if (cs->apparmor_profile != NULL) {
204
0
      sudo_lbuf_append(lbuf, "    ApparmorProfile: %s\n",
205
0
    cs->apparmor_profile);
206
0
  }
207
15
  if (cs->privs != NULL)
208
0
      sudo_lbuf_append(lbuf, "    Privs: %s\n", cs->privs);
209
15
  if (cs->limitprivs != NULL)
210
0
      sudo_lbuf_append(lbuf, "    Limitprivs: %s\n", cs->limitprivs);
211
15
  if (cs->role != NULL)
212
0
      sudo_lbuf_append(lbuf, "    Role: %s\n", cs->role);
213
15
  if (cs->type != NULL)
214
0
      sudo_lbuf_append(lbuf, "    Type: %s\n", cs->type);
215
15
  if (cs->runchroot != NULL)
216
0
      sudo_lbuf_append(lbuf, "    Chroot: %s\n", cs->runchroot);
217
15
  if (cs->runcwd != NULL)
218
0
      sudo_lbuf_append(lbuf, "    Cwd: %s\n", cs->runcwd);
219
15
  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
15
  if (cs->notbefore != UNSPEC) {
225
0
      char buf[sizeof("CCYYMMDDHHMMSSZ")] = "";
226
0
      struct tm gmt;
227
0
      size_t len;
228
0
      if (gmtime_r(&cs->notbefore, &gmt) != NULL) {
229
0
    len = strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &gmt);
230
0
    if (len != 0 && buf[sizeof(buf) - 1] == '\0')
231
0
        sudo_lbuf_append(lbuf, "    NotBefore: %s\n", buf);
232
0
      }
233
0
  }
234
15
  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
15
  sudo_lbuf_append(lbuf, "%s", _("    Commands:\n"));
245
15
    }
246
15
    sudo_lbuf_append(lbuf, "\t");
247
15
    sudoers_format_member(lbuf, parse_tree, cs->cmnd, "\n\t",
248
15
  CMNDALIAS);
249
15
    sudo_lbuf_append(lbuf, "\n");
250
251
15
    debug_return;
252
15
}
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
15
{
258
15
    const struct privilege *priv;
259
15
    int nfound = 0;
260
15
    debug_decl(display_priv_long, SUDOERS_DEBUG_PARSER);
261
262
15
    TAILQ_FOREACH(priv, &us->privileges, entries) {
263
15
  const struct cmndspec *cs, *prev_cs;
264
265
15
  if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW)
266
0
      continue;
267
15
  prev_cs = NULL;
268
15
  sudo_lbuf_append(lbuf, "\n");
269
15
  TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
270
15
      display_cmndspec_long(parse_tree, pw, us, priv, cs, prev_cs,
271
15
    lbuf);
272
15
      prev_cs = cs;
273
15
      nfound++;
274
15
  }
275
15
    }
276
15
    debug_return_int(nfound);
277
15
}
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
16
{
283
16
    const struct userspec *us;
284
16
    int nfound = 0;
285
16
    debug_decl(sudo_display_userspecs, SUDOERS_DEBUG_PARSER);
286
287
40
    TAILQ_FOREACH(us, &parse_tree->userspecs, entries) {
288
40
  if (userlist_matches(parse_tree, pw, &us->users) != ALLOW)
289
10
      continue;
290
291
30
  if (verbose)
292
15
      nfound += display_priv_long(parse_tree, pw, us, lbuf);
293
15
  else
294
15
      nfound += display_priv_short(parse_tree, pw, us, lbuf);
295
30
    }
296
16
    if (sudo_lbuf_error(lbuf))
297
0
  debug_return_int(-1);
298
16
    debug_return_int(nfound);
299
16
}
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
16
{
308
16
    const struct defaults *d;
309
16
    const char *prefix;
310
16
    int nfound = 0;
311
16
    debug_decl(display_defaults, SUDOERS_DEBUG_PARSER);
312
313
16
    if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
314
16
  prefix = "    ";
315
0
    else
316
0
  prefix = ", ";
317
318
48
    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
319
48
  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
0
      case DEFAULTS_USER:
325
0
    if (userlist_matches(parse_tree, pw, &d->binding->members) != ALLOW)
326
0
        continue;
327
0
    break;
328
0
      case DEFAULTS_RUNAS:
329
8
      case DEFAULTS_CMND:
330
8
    continue;
331
48
  }
332
40
  sudo_lbuf_append(lbuf, "%s", prefix);
333
40
  sudoers_format_default(lbuf, d);
334
40
  prefix = ", ";
335
40
  nfound++;
336
40
    }
337
16
    if (sudo_lbuf_error(lbuf))
338
0
  debug_return_int(-1);
339
16
    debug_return_int(nfound);
340
16
}
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
32
{
349
32
    const struct defaults *d;
350
32
    const struct defaults_binding *binding = NULL;
351
32
    const struct member *m;
352
32
    const char *dsep;
353
32
    short atype;
354
32
    int nfound = 0;
355
32
    debug_decl(display_bound_defaults_by_type, SUDOERS_DEBUG_PARSER);
356
357
32
    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
16
  case DEFAULTS_RUNAS:
367
16
      atype = RUNASALIAS;
368
16
      dsep = ">";
369
16
      break;
370
16
  case DEFAULTS_CMND:
371
16
      atype = CMNDALIAS;
372
16
      dsep = "!";
373
16
      break;
374
0
  default:
375
0
      debug_return_int(-1);
376
32
    }
377
96
    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
378
96
  if (d->type != deftype)
379
88
      continue;
380
381
8
  nfound++;
382
8
  if (binding != d->binding) {
383
8
      binding = d->binding;
384
8
      if (nfound != 1)
385
0
    sudo_lbuf_append(lbuf, "\n");
386
8
      sudo_lbuf_append(lbuf, "    Defaults%s", dsep);
387
16
      TAILQ_FOREACH(m, &binding->members, entries) {
388
16
    if (m != TAILQ_FIRST(&binding->members))
389
8
        sudo_lbuf_append(lbuf, ", ");
390
16
    sudoers_format_member(lbuf, parse_tree, m, ", ", atype);
391
16
      }
392
8
      sudo_lbuf_append(lbuf, " ");
393
8
  } else
394
0
      sudo_lbuf_append(lbuf, ", ");
395
8
  sudoers_format_default(lbuf, d);
396
8
    }
397
398
32
    if (sudo_lbuf_error(lbuf))
399
0
  debug_return_int(-1);
400
32
    debug_return_int(nfound);
401
32
}
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
16
{
410
16
    int nfound = 0;
411
16
    debug_decl(display_bound_defaults, SUDOERS_DEBUG_PARSER);
412
413
    /* XXX - should only print ones that match what the user can do. */
414
16
    nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_RUNAS,
415
16
  lbuf);
416
16
    nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_CMND,
417
16
  lbuf);
418
419
16
    if (sudo_lbuf_error(lbuf))
420
0
  debug_return_int(-1);
421
16
    debug_return_int(nfound);
422
16
}
423
424
static int
425
output(const char *buf)
426
22
{
427
22
    struct sudo_conv_message msg;
428
22
    struct sudo_conv_reply repl;
429
22
    debug_decl(output, SUDOERS_DEBUG_NSS);
430
431
    /* Call conversation function */
432
22
    memset(&msg, 0, sizeof(msg));
433
22
    msg.msg_type = SUDO_CONV_INFO_MSG;
434
22
    msg.msg = buf;
435
22
    memset(&repl, 0, sizeof(repl));
436
22
    if (sudo_conv(1, &msg, &repl, NULL) == -1)
437
0
  debug_return_int(0);
438
22
    debug_return_int((int)strlen(buf));
439
22
}
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
16
{
449
16
    const struct sudo_nss *nss;
450
16
    struct sudo_lbuf def_buf, priv_buf;
451
16
    int cols, count, n;
452
16
    unsigned int olen;
453
16
    struct stat sb;
454
16
    debug_decl(display_privs, SUDOERS_DEBUG_PARSER);
455
456
16
    if (verbose < 0) {
457
  /* Nothing to display. */
458
0
  debug_return_int(true);
459
0
    }
460
461
16
    cols = ctx->user.cols;
462
16
    if (!sudo_isatty(STDOUT_FILENO, &sb))
463
16
  cols = 0;
464
16
    sudo_lbuf_init(&def_buf, output, 4, NULL, cols);
465
16
    sudo_lbuf_init(&priv_buf, output, 8, NULL, cols);
466
467
16
    sudo_lbuf_append(&def_buf, _("Matching Defaults entries for %s on %s:\n"),
468
16
  pw->pw_name, ctx->runas.shost);
469
16
    count = 0;
470
16
    TAILQ_FOREACH(nss, snl, entries) {
471
16
  n = display_defaults(nss->parse_tree, pw, &def_buf);
472
16
  if (n == -1)
473
0
      goto bad;
474
16
  count += n;
475
16
    }
476
16
    if (count != 0) {
477
8
  sudo_lbuf_append(&def_buf, "\n\n");
478
8
    } else {
479
  /* Undo Defaults header. */
480
8
  def_buf.len = 0;
481
8
    }
482
483
    /* Display Runas and Cmnd-specific defaults. */
484
16
    olen = def_buf.len;
485
16
    sudo_lbuf_append(&def_buf, _("Runas and Command-specific defaults for %s:\n"),
486
16
  pw->pw_name);
487
16
    count = 0;
488
16
    TAILQ_FOREACH(nss, snl, entries) {
489
16
  n = display_bound_defaults(nss->parse_tree, pw, &def_buf);
490
16
  if (n == -1)
491
0
      goto bad;
492
16
  count += n;
493
16
    }
494
16
    if (count != 0) {
495
8
  sudo_lbuf_append(&def_buf, "\n\n");
496
8
    } else {
497
  /* Undo Defaults header. */
498
8
  def_buf.len = olen;
499
8
    }
500
501
    /* Display privileges from all sources. */
502
16
    sudo_lbuf_append(&priv_buf,
503
16
  _("User %s may run the following commands on %s:\n"),
504
16
  pw->pw_name, ctx->runas.shost);
505
16
    count = 0;
506
16
    TAILQ_FOREACH(nss, snl, entries) {
507
16
  if (nss->query(ctx, nss, pw) != -1) {
508
16
      n = sudo_display_userspecs(nss->parse_tree, pw, &priv_buf,
509
16
    verbose);
510
16
      if (n == -1)
511
0
    goto bad;
512
16
      count += n;
513
16
  }
514
16
    }
515
16
    if (count == 0) {
516
10
  def_buf.len = 0;
517
10
  priv_buf.len = 0;
518
10
  sudo_lbuf_append(&priv_buf,
519
10
      _("User %s is not allowed to run sudo on %s.\n"),
520
10
      pw->pw_name, ctx->runas.shost);
521
10
    }
522
16
    if (sudo_lbuf_error(&def_buf) || sudo_lbuf_error(&priv_buf))
523
0
  goto bad;
524
525
16
    sudo_lbuf_print(&def_buf);
526
16
    sudo_lbuf_print(&priv_buf);
527
528
16
    sudo_lbuf_destroy(&def_buf);
529
16
    sudo_lbuf_destroy(&priv_buf);
530
531
16
    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
{
607
    struct sudoers_match_info match_info = { NULL };
608
    struct sudo_lbuf lbuf;
609
    struct sudo_nss *nss;
610
    int m, match = UNSPEC;
611
    int ret = false;
612
    time_t now;
613
    debug_decl(display_cmnd, SUDOERS_DEBUG_PARSER);
614
615
    /* Iterate over each source, checking for the command. */
616
    time(&now);
617
    sudo_lbuf_init(&lbuf, output, 0, NULL, 0);
618
    TAILQ_FOREACH(nss, snl, entries) {
619
  if (nss->query(ctx, nss, pw) == -1) {
620
      /* The query function should have printed an error message. */
621
      debug_return_int(-1);
622
  }
623
624
  m = display_cmnd_check(ctx, nss->parse_tree, pw, now, &match_info);
625
  if (m != UNSPEC)
626
      match = m;
627
628
  if (!sudo_nss_can_continue(nss, m))
629
      break;
630
    }
631
    if (match == ALLOW) {
632
  if (verbose < 0) {
633
      /* Nothing to display. */
634
      debug_return_int(true);
635
  }
636
  if (verbose) {
637
      /* Append matching sudoers rule (long form). */
638
      display_cmndspec_long(match_info.parse_tree, pw, match_info.us,
639
    match_info.priv, match_info.cs, NULL, &lbuf);
640
      sudo_lbuf_append(&lbuf, "    Matched: ");
641
  }
642
  sudo_lbuf_append(&lbuf, "%s%s%s\n", ctx->user.cmnd_list,
643
      ctx->user.cmnd_args ? " " : "",
644
      ctx->user.cmnd_args ? ctx->user.cmnd_args : "");
645
  sudo_lbuf_print(&lbuf);
646
  ret = sudo_lbuf_error(&lbuf) ? -1 : true;
647
  sudo_lbuf_destroy(&lbuf);
648
    }
649
    debug_return_int(ret);
650
}