Coverage Report

Created: 2026-01-09 06:37

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
0
{
37
0
    struct privilege *priv;
38
0
    int nfound = 0;
39
0
    debug_decl(display_priv_short, SUDOERS_DEBUG_PARSER);
40
41
0
    TAILQ_FOREACH(priv, &us->privileges, entries) {
42
0
  struct cmndspec *cs;
43
0
  struct cmndtag tags;
44
45
0
  if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW)
46
0
      continue;
47
48
0
  sudoers_defaults_list_to_tags(&priv->defaults, &tags);
49
0
  TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
50
0
      struct cmndspec *prev_cs = TAILQ_PREV(cs, cmndspec_list, entries);
51
52
0
      if (prev_cs == NULL || RUNAS_CHANGED(cs, prev_cs)) {
53
0
    struct member *m;
54
55
    /* Start new line, first entry or RunAs changed. */
56
0
    if (prev_cs != NULL)
57
0
        sudo_lbuf_append(lbuf, "\n");
58
0
    sudo_lbuf_append(lbuf, "    (");
59
0
    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
0
    } else if (cs->runasgrouplist == NULL) {
67
0
        sudo_lbuf_append(lbuf, "%s", def_runas_default);
68
0
    } else {
69
0
        sudo_lbuf_append(lbuf, "%s", pw->pw_name);
70
0
    }
71
0
    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
0
    sudo_lbuf_append(lbuf, ") ");
81
0
    sudoers_format_cmndspec(lbuf, parse_tree, cs, NULL,
82
0
        tags, true);
83
0
      } 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
0
      nfound++;
90
0
  }
91
0
  sudo_lbuf_append(lbuf, "\n");
92
0
    }
93
0
    debug_return_int(nfound);
94
0
}
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
0
{
104
0
    debug_decl(new_long_entry, SUDOERS_DEBUG_PARSER);
105
106
0
    if (prev_cs == NULL)
107
0
  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
0
{
139
0
    const struct defaults *d;
140
0
    const struct member *m;
141
0
    debug_decl(display_cmndspec_long, SUDOERS_DEBUG_PARSER);
142
143
0
    if (new_long_entry(cs, prev_cs)) {
144
0
  unsigned int olen;
145
146
0
  if (prev_cs != NULL)
147
0
      sudo_lbuf_append(lbuf, "\n");
148
0
  if (priv->ldap_role != NULL) {
149
0
      sudo_lbuf_append(lbuf, _("LDAP Role: %s\n"),
150
0
    priv->ldap_role);
151
0
  } else {
152
0
      sudo_lbuf_append(lbuf, _("Sudoers entry: %s\n"),
153
0
    us->file);
154
0
  }
155
0
  sudo_lbuf_append(lbuf, "%s", _("    RunAsUsers: "));
156
0
  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
0
  } else if (cs->runasgrouplist == NULL) {
164
0
      sudo_lbuf_append(lbuf, "%s", def_runas_default);
165
0
  } else {
166
0
      sudo_lbuf_append(lbuf, "%s", pw->pw_name);
167
0
  }
168
0
  sudo_lbuf_append(lbuf, "\n");
169
0
  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
0
  olen = lbuf->len;
180
0
  sudo_lbuf_append(lbuf, "%s", _("    Options: "));
181
0
  TAILQ_FOREACH(d, &priv->defaults, entries) {
182
0
      sudoers_format_default(lbuf, d);
183
0
      sudo_lbuf_append(lbuf, ", ");
184
0
  }
185
0
  if (TAG_SET(cs->tags.setenv))
186
0
      sudo_lbuf_append(lbuf, "%ssetenv, ", cs->tags.setenv ? "" : "!");
187
0
  if (TAG_SET(cs->tags.noexec))
188
0
      sudo_lbuf_append(lbuf, "%snoexec, ", cs->tags.noexec ? "" : "!");
189
0
  if (TAG_SET(cs->tags.intercept))
190
0
      sudo_lbuf_append(lbuf, "%sintercept, ", cs->tags.intercept ? "" : "!");
191
0
  if (TAG_SET(cs->tags.nopasswd))
192
0
      sudo_lbuf_append(lbuf, "%sauthenticate, ", cs->tags.nopasswd ? "!" : "");
193
0
  if (TAG_SET(cs->tags.log_input))
194
0
      sudo_lbuf_append(lbuf, "%slog_input, ", cs->tags.log_input ? "" : "!");
195
0
  if (TAG_SET(cs->tags.log_output))
196
0
      sudo_lbuf_append(lbuf, "%slog_output, ", cs->tags.log_output ? "" : "!");
197
0
  if (lbuf->buf[lbuf->len - 2] == ',') {
198
0
      lbuf->len -= 2; /* remove trailing ", " */
199
0
      sudo_lbuf_append(lbuf, "\n");
200
0
  } else {
201
0
      lbuf->len = olen; /* no options */
202
0
  }
203
0
  if (cs->apparmor_profile != NULL) {
204
0
      sudo_lbuf_append(lbuf, "    ApparmorProfile: %s\n",
205
0
    cs->apparmor_profile);
206
0
  }
207
0
  if (cs->privs != NULL)
208
0
      sudo_lbuf_append(lbuf, "    Privs: %s\n", cs->privs);
209
0
  if (cs->limitprivs != NULL)
210
0
      sudo_lbuf_append(lbuf, "    Limitprivs: %s\n", cs->limitprivs);
211
0
  if (cs->role != NULL)
212
0
      sudo_lbuf_append(lbuf, "    Role: %s\n", cs->role);
213
0
  if (cs->type != NULL)
214
0
      sudo_lbuf_append(lbuf, "    Type: %s\n", cs->type);
215
0
  if (cs->runchroot != NULL)
216
0
      sudo_lbuf_append(lbuf, "    Chroot: %s\n", cs->runchroot);
217
0
  if (cs->runcwd != NULL)
218
0
      sudo_lbuf_append(lbuf, "    Cwd: %s\n", cs->runcwd);
219
0
  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
0
  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
0
  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
0
  sudo_lbuf_append(lbuf, "%s", _("    Commands:\n"));
245
0
    }
246
0
    sudo_lbuf_append(lbuf, "\t");
247
0
    sudoers_format_member(lbuf, parse_tree, cs->cmnd, "\n\t",
248
0
  CMNDALIAS);
249
0
    sudo_lbuf_append(lbuf, "\n");
250
251
0
    debug_return;
252
0
}
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
0
{
258
0
    const struct privilege *priv;
259
0
    int nfound = 0;
260
0
    debug_decl(display_priv_long, SUDOERS_DEBUG_PARSER);
261
262
0
    TAILQ_FOREACH(priv, &us->privileges, entries) {
263
0
  const struct cmndspec *cs, *prev_cs;
264
265
0
  if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW)
266
0
      continue;
267
0
  prev_cs = NULL;
268
0
  sudo_lbuf_append(lbuf, "\n");
269
0
  TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
270
0
      display_cmndspec_long(parse_tree, pw, us, priv, cs, prev_cs,
271
0
    lbuf);
272
0
      prev_cs = cs;
273
0
      nfound++;
274
0
  }
275
0
    }
276
0
    debug_return_int(nfound);
277
0
}
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
8
{
283
8
    const struct userspec *us;
284
8
    int nfound = 0;
285
8
    debug_decl(sudo_display_userspecs, SUDOERS_DEBUG_PARSER);
286
287
8
    TAILQ_FOREACH(us, &parse_tree->userspecs, entries) {
288
0
  if (userlist_matches(parse_tree, pw, &us->users) != ALLOW)
289
0
      continue;
290
291
0
  if (verbose)
292
0
      nfound += display_priv_long(parse_tree, pw, us, lbuf);
293
0
  else
294
0
      nfound += display_priv_short(parse_tree, pw, us, lbuf);
295
0
    }
296
8
    if (sudo_lbuf_error(lbuf))
297
0
  debug_return_int(-1);
298
8
    debug_return_int(nfound);
299
8
}
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
8
{
308
8
    const struct defaults *d;
309
8
    const char *prefix;
310
8
    int nfound = 0;
311
8
    debug_decl(display_defaults, SUDOERS_DEBUG_PARSER);
312
313
8
    if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
314
8
  prefix = "    ";
315
0
    else
316
0
  prefix = ", ";
317
318
8
    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
319
0
  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
0
      case DEFAULTS_CMND:
330
0
    continue;
331
0
  }
332
0
  sudo_lbuf_append(lbuf, "%s", prefix);
333
0
  sudoers_format_default(lbuf, d);
334
0
  prefix = ", ";
335
0
  nfound++;
336
0
    }
337
8
    if (sudo_lbuf_error(lbuf))
338
0
  debug_return_int(-1);
339
8
    debug_return_int(nfound);
340
8
}
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
16
{
349
16
    const struct defaults *d;
350
16
    const struct defaults_binding *binding = NULL;
351
16
    const struct member *m;
352
16
    const char *dsep;
353
16
    short atype;
354
16
    int nfound = 0;
355
16
    debug_decl(display_bound_defaults_by_type, SUDOERS_DEBUG_PARSER);
356
357
16
    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
8
  case DEFAULTS_RUNAS:
367
8
      atype = RUNASALIAS;
368
8
      dsep = ">";
369
8
      break;
370
8
  case DEFAULTS_CMND:
371
8
      atype = CMNDALIAS;
372
8
      dsep = "!";
373
8
      break;
374
0
  default:
375
0
      debug_return_int(-1);
376
16
    }
377
16
    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
378
0
  if (d->type != deftype)
379
0
      continue;
380
381
0
  nfound++;
382
0
  if (binding != d->binding) {
383
0
      binding = d->binding;
384
0
      if (nfound != 1)
385
0
    sudo_lbuf_append(lbuf, "\n");
386
0
      sudo_lbuf_append(lbuf, "    Defaults%s", dsep);
387
0
      TAILQ_FOREACH(m, &binding->members, entries) {
388
0
    if (m != TAILQ_FIRST(&binding->members))
389
0
        sudo_lbuf_append(lbuf, ", ");
390
0
    sudoers_format_member(lbuf, parse_tree, m, ", ", atype);
391
0
      }
392
0
      sudo_lbuf_append(lbuf, " ");
393
0
  } else
394
0
      sudo_lbuf_append(lbuf, ", ");
395
0
  sudoers_format_default(lbuf, d);
396
0
    }
397
398
16
    if (sudo_lbuf_error(lbuf))
399
0
  debug_return_int(-1);
400
16
    debug_return_int(nfound);
401
16
}
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
8
{
410
8
    int nfound = 0;
411
8
    debug_decl(display_bound_defaults, SUDOERS_DEBUG_PARSER);
412
413
    /* XXX - should only print ones that match what the user can do. */
414
8
    nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_RUNAS,
415
8
  lbuf);
416
8
    nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_CMND,
417
8
  lbuf);
418
419
8
    if (sudo_lbuf_error(lbuf))
420
0
  debug_return_int(-1);
421
8
    debug_return_int(nfound);
422
8
}
423
424
static int
425
output(const char *buf)
426
8
{
427
8
    struct sudo_conv_message msg;
428
8
    struct sudo_conv_reply repl;
429
8
    debug_decl(output, SUDOERS_DEBUG_NSS);
430
431
    /* Call conversation function */
432
8
    memset(&msg, 0, sizeof(msg));
433
8
    msg.msg_type = SUDO_CONV_INFO_MSG;
434
8
    msg.msg = buf;
435
8
    memset(&repl, 0, sizeof(repl));
436
8
    if (sudo_conv(1, &msg, &repl, NULL) == -1)
437
0
  debug_return_int(0);
438
8
    debug_return_int((int)strlen(buf));
439
8
}
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
8
{
449
8
    const struct sudo_nss *nss;
450
8
    struct sudo_lbuf def_buf, priv_buf;
451
8
    int cols, count, n;
452
8
    unsigned int olen;
453
8
    struct stat sb;
454
8
    debug_decl(display_privs, SUDOERS_DEBUG_PARSER);
455
456
8
    if (verbose < 0) {
457
  /* Nothing to display. */
458
0
  debug_return_int(true);
459
0
    }
460
461
8
    cols = ctx->user.cols;
462
8
    if (fstat(STDOUT_FILENO, &sb) == 0 && S_ISFIFO(sb.st_mode))
463
0
  cols = 0;
464
8
    sudo_lbuf_init(&def_buf, output, 4, NULL, cols);
465
8
    sudo_lbuf_init(&priv_buf, output, 8, NULL, cols);
466
467
8
    sudo_lbuf_append(&def_buf, _("Matching Defaults entries for %s on %s:\n"),
468
8
  pw->pw_name, ctx->runas.shost);
469
8
    count = 0;
470
8
    TAILQ_FOREACH(nss, snl, entries) {
471
8
  n = display_defaults(nss->parse_tree, pw, &def_buf);
472
8
  if (n == -1)
473
0
      goto bad;
474
8
  count += n;
475
8
    }
476
8
    if (count != 0) {
477
0
  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
8
    olen = def_buf.len;
485
8
    sudo_lbuf_append(&def_buf, _("Runas and Command-specific defaults for %s:\n"),
486
8
  pw->pw_name);
487
8
    count = 0;
488
8
    TAILQ_FOREACH(nss, snl, entries) {
489
8
  n = display_bound_defaults(nss->parse_tree, pw, &def_buf);
490
8
  if (n == -1)
491
0
      goto bad;
492
8
  count += n;
493
8
    }
494
8
    if (count != 0) {
495
0
  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
8
    sudo_lbuf_append(&priv_buf,
503
8
  _("User %s may run the following commands on %s:\n"),
504
8
  pw->pw_name, ctx->runas.shost);
505
8
    count = 0;
506
8
    TAILQ_FOREACH(nss, snl, entries) {
507
8
  if (nss->query(ctx, nss, pw) != -1) {
508
8
      n = sudo_display_userspecs(nss->parse_tree, pw, &priv_buf,
509
8
    verbose);
510
8
      if (n == -1)
511
0
    goto bad;
512
8
      count += n;
513
8
  }
514
8
    }
515
8
    if (count == 0) {
516
8
  def_buf.len = 0;
517
8
  priv_buf.len = 0;
518
8
  sudo_lbuf_append(&priv_buf,
519
8
      _("User %s is not allowed to run sudo on %s.\n"),
520
8
      pw->pw_name, ctx->runas.shost);
521
8
    }
522
8
    if (sudo_lbuf_error(&def_buf) || sudo_lbuf_error(&priv_buf))
523
0
  goto bad;
524
525
8
    sudo_lbuf_print(&def_buf);
526
8
    sudo_lbuf_print(&priv_buf);
527
528
8
    sudo_lbuf_destroy(&def_buf);
529
8
    sudo_lbuf_destroy(&priv_buf);
530
531
8
    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
}