Coverage Report

Created: 2025-07-23 06:42

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