Coverage Report

Created: 2023-06-07 06:47

/src/sudo/plugins/sudoers/parse.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 2004-2005, 2007-2023 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
runas_matches_pw(struct sudoers_parse_tree *parse_tree,
40
    const struct cmndspec *cs, const struct passwd *pw)
41
0
{
42
0
    debug_decl(runas_matches_pw, SUDOERS_DEBUG_PARSER);
43
44
0
    if (cs->runasuserlist != NULL)
45
0
  debug_return_int(userlist_matches(parse_tree, pw, cs->runasuserlist));
46
47
0
    if (cs->runasgrouplist == NULL) {
48
  /* No explicit runas user or group, use default. */
49
0
  if (userpw_matches(def_runas_default, pw->pw_name, pw))
50
0
      debug_return_int(ALLOW);
51
0
    }
52
0
    debug_return_int(UNSPEC);
53
0
}
54
55
/*
56
 * Look up the user in the sudoers parse tree for pseudo-commands like
57
 * list, verify and kill.
58
 */
59
static int
60
sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw, int pwflag)
61
16
{
62
16
    char *saved_runchroot;
63
16
    struct passwd *root_pw = NULL;
64
16
    struct sudo_nss *nss;
65
16
    struct cmndspec *cs;
66
16
    struct privilege *priv;
67
16
    struct userspec *us;
68
16
    struct defaults *def;
69
16
    int cmnd_match, nopass, match = DENY;
70
16
    int validated = 0;
71
16
    enum def_tuple pwcheck;
72
16
    debug_decl(sudoers_lookup_pseudo, SUDOERS_DEBUG_PARSER);
73
74
16
    pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
75
16
    nopass = (pwcheck == never || pwcheck == all) ? true : false;
76
77
16
    if (list_pw != NULL) {
78
0
  root_pw = sudo_getpwuid(ROOT_UID);
79
0
  if (root_pw == NULL)
80
0
      log_warningx(SLOG_SEND_MAIL, N_("unknown uid %u"), ROOT_UID);
81
16
    } else {
82
16
  SET(validated, FLAG_NO_CHECK);
83
16
    }
84
85
    /* Don't use chroot setting for pseudo-commands. */
86
16
    saved_runchroot = def_runchroot;
87
16
    def_runchroot = NULL;
88
89
16
    TAILQ_FOREACH(nss, snl, entries) {
90
16
  if (nss->query(nss, pw) == -1) {
91
      /* The query function should have printed an error message. */
92
0
      SET(validated, VALIDATE_ERROR);
93
0
      break;
94
0
  }
95
16
  TAILQ_FOREACH(us, &nss->parse_tree->userspecs, entries) {
96
8
      if (userlist_matches(nss->parse_tree, pw, &us->users) != ALLOW)
97
6
    continue;
98
2
      TAILQ_FOREACH(priv, &us->privileges, entries) {
99
2
    int priv_nopass = UNSPEC;
100
101
2
    if (hostlist_matches(nss->parse_tree, pw, &priv->hostlist) != ALLOW)
102
0
        continue;
103
2
    TAILQ_FOREACH(def, &priv->defaults, entries) {
104
0
        if (strcmp(def->var, "authenticate") == 0)
105
0
      priv_nopass = !def->op;
106
0
    }
107
4
    TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
108
4
        if (pwcheck == any) {
109
0
      if (cs->tags.nopasswd == true || priv_nopass == true)
110
0
          nopass = true;
111
4
        } else if (pwcheck == all) {
112
0
      if (cs->tags.nopasswd != true && priv_nopass != true)
113
0
          nopass = false;
114
0
        }
115
4
        if (match == ALLOW)
116
2
      continue;
117
118
        /*
119
         * Root can list any user's privileges.
120
         * A user may always list their own privileges.
121
         */
122
2
        if (user_uid == 0 || list_pw == NULL ||
123
2
          user_uid == list_pw->pw_uid) {
124
2
      match = ALLOW;
125
2
      continue;
126
2
        }
127
128
        /*
129
         * To list another user's prilileges, the runas
130
         * user must match the list user or root.
131
         */
132
0
        switch (runas_matches_pw(nss->parse_tree, cs, list_pw)) {
133
0
        case DENY:
134
0
      break;
135
0
        case ALLOW:
136
      /*
137
       * RunAs user matches list user.
138
       * Match on command "list" or ALL.
139
       */
140
0
      cmnd_match = cmnd_matches(nss->parse_tree,
141
0
          cs->cmnd, cs->runchroot, NULL);
142
0
      if (cmnd_match != UNSPEC) {
143
0
          match = cmnd_match;
144
0
          goto done;
145
0
      }
146
0
      break;
147
0
        default:
148
      /*
149
       * RunAs user doesn't match list user.  Only allow
150
       * listing if the user has "sudo ALL" for root.
151
       */
152
0
      if (root_pw != NULL && runas_matches_pw(nss->parse_tree,
153
0
        cs, root_pw) == ALLOW) {
154
0
          cmnd_match = cmnd_matches_all(nss->parse_tree,
155
0
        cs->cmnd, cs->runchroot, NULL);
156
0
          if (cmnd_match != UNSPEC) {
157
0
        match = cmnd_match;
158
0
        goto done;
159
0
          }
160
0
      }
161
0
      break;
162
0
        }
163
0
    }
164
2
      }
165
2
  }
166
16
    }
167
16
done:
168
16
    if (root_pw != NULL)
169
0
  sudo_pw_delref(root_pw);
170
16
    if (match == ALLOW || user_uid == 0) {
171
  /* User has an entry for this host. */
172
16
  SET(validated, VALIDATE_SUCCESS);
173
16
    } else if (match == DENY)
174
0
  SET(validated, VALIDATE_FAILURE);
175
16
    if (pwcheck == always && def_authenticate)
176
0
  SET(validated, FLAG_CHECK_USER);
177
16
    else if (nopass == true)
178
0
  def_authenticate = false;
179
180
    /* Restore original def_runchroot. */
181
16
    def_runchroot = saved_runchroot;
182
183
16
    debug_return_int(validated);
184
16
}
185
186
static void
187
init_cmnd_info(struct cmnd_info *info)
188
17
{
189
17
    memset(info, 0, sizeof(*info));
190
17
    if (def_intercept || ISSET(sudo_mode, MODE_POLICY_INTERCEPTED))
191
0
  info->intercepted = true;
192
17
}
193
194
static int
195
sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw,
196
    int *validated, struct cmnd_info *info, struct cmndspec **matching_cs,
197
    struct defaults_list **defs, time_t now)
198
16
{
199
16
    int host_match, runas_match, cmnd_match;
200
16
    struct cmndspec *cs;
201
16
    struct privilege *priv;
202
16
    struct userspec *us;
203
16
    struct member *matching_user;
204
16
    debug_decl(sudoers_lookup_check, SUDOERS_DEBUG_PARSER);
205
206
16
    init_cmnd_info(info);
207
208
16
    TAILQ_FOREACH_REVERSE(us, &nss->parse_tree->userspecs, userspec_list, entries) {
209
8
  if (userlist_matches(nss->parse_tree, pw, &us->users) != ALLOW)
210
6
      continue;
211
2
  CLR(*validated, FLAG_NO_USER);
212
2
  TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) {
213
2
      host_match = hostlist_matches(nss->parse_tree, pw, &priv->hostlist);
214
2
      if (host_match == ALLOW)
215
2
    CLR(*validated, FLAG_NO_HOST);
216
0
      else
217
0
    continue;
218
2
      TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) {
219
2
    if (cs->notbefore != UNSPEC) {
220
0
        if (now < cs->notbefore)
221
0
      continue;
222
0
    }
223
2
    if (cs->notafter != UNSPEC) {
224
0
        if (now > cs->notafter)
225
0
      continue;
226
0
    }
227
2
    matching_user = NULL;
228
2
    runas_match = runaslist_matches(nss->parse_tree,
229
2
        cs->runasuserlist, cs->runasgrouplist, &matching_user,
230
2
        NULL);
231
2
    if (runas_match == ALLOW) {
232
2
        cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd,
233
2
      cs->runchroot, info);
234
2
        if (cmnd_match != UNSPEC) {
235
      /*
236
       * If user is running command as himself,
237
       * set runas_pw = sudo_user.pw.
238
       * XXX - hack, want more general solution
239
       */
240
1
      if (matching_user && matching_user->type == MYSELF) {
241
0
          sudo_pw_delref(runas_pw);
242
0
          sudo_pw_addref(sudo_user.pw);
243
0
          runas_pw = sudo_user.pw;
244
0
      }
245
1
      *matching_cs = cs;
246
1
      *defs = &priv->defaults;
247
1
      sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
248
1
          "userspec matched @ %s:%d:%d: %s",
249
1
          us->file ? us->file : "???", us->line, us->column,
250
1
          cmnd_match ? "allowed" : "denied");
251
1
      debug_return_int(cmnd_match);
252
1
        }
253
1
        free(info->cmnd_path);
254
1
        init_cmnd_info(info);
255
1
    }
256
2
      }
257
2
  }
258
2
    }
259
15
    debug_return_int(UNSPEC);
260
15
}
261
262
/*
263
 * Apply cmndspec-specific settings including SELinux role/type,
264
 * Solaris privs, and command tags.
265
 */
266
static bool
267
apply_cmndspec(struct cmndspec *cs)
268
1
{
269
1
    debug_decl(apply_cmndspec, SUDOERS_DEBUG_PARSER);
270
271
1
    if (cs != NULL) {
272
#ifdef HAVE_SELINUX
273
  /* Set role and type if not specified on command line. */
274
  if (user_role == NULL) {
275
      if (cs->role != NULL) {
276
    user_role = strdup(cs->role);
277
    if (user_role == NULL) {
278
        sudo_warnx(U_("%s: %s"), __func__,
279
      U_("unable to allocate memory"));
280
        debug_return_bool(false);
281
    }
282
      } else {
283
    user_role = def_role;
284
    def_role = NULL;
285
      }
286
      if (user_role != NULL) {
287
    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
288
        "user_role -> %s", user_role);
289
      }
290
  }
291
  if (user_type == NULL) {
292
      if (cs->type != NULL) {
293
    user_type = strdup(cs->type);
294
    if (user_type == NULL) {
295
        sudo_warnx(U_("%s: %s"), __func__,
296
      U_("unable to allocate memory"));
297
        debug_return_bool(false);
298
    }
299
      } else {
300
    user_type = def_type;
301
    def_type = NULL;
302
      }
303
      if (user_type != NULL) {
304
    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
305
        "user_type -> %s", user_type);
306
      }
307
  }
308
#endif /* HAVE_SELINUX */
309
#ifdef HAVE_APPARMOR
310
  /* Set AppArmor profile, if specified */
311
  if (cs->apparmor_profile != NULL) {
312
      user_apparmor_profile = strdup(cs->apparmor_profile);
313
      if (user_apparmor_profile == NULL) {
314
    sudo_warnx(U_("%s: %s"), __func__,
315
        U_("unable to allocate memory"));
316
    debug_return_bool(false);
317
      }
318
  } else {
319
      user_apparmor_profile = def_apparmor_profile;
320
      def_apparmor_profile = NULL;
321
  }
322
  if (user_apparmor_profile != NULL) {
323
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
324
    "user_apparmor_profile -> %s", user_apparmor_profile);
325
  }
326
#endif
327
#ifdef HAVE_PRIV_SET
328
  /* Set Solaris privilege sets */
329
  if (runas_privs == NULL) {
330
      if (cs->privs != NULL) {
331
    runas_privs = strdup(cs->privs);
332
    if (runas_privs == NULL) {
333
        sudo_warnx(U_("%s: %s"), __func__,
334
      U_("unable to allocate memory"));
335
        debug_return_bool(false);
336
    }
337
      } else {
338
    runas_privs = def_privs;
339
    def_privs = NULL;
340
      }
341
      if (runas_privs != NULL) {
342
    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
343
        "runas_privs -> %s", runas_privs);
344
      }
345
  }
346
  if (runas_limitprivs == NULL) {
347
      if (cs->limitprivs != NULL) {
348
    runas_limitprivs = strdup(cs->limitprivs);
349
    if (runas_limitprivs == NULL) {
350
        sudo_warnx(U_("%s: %s"), __func__,
351
      U_("unable to allocate memory"));
352
        debug_return_bool(false);
353
    }
354
      } else {
355
    runas_limitprivs = def_limitprivs;
356
    def_limitprivs = NULL;
357
      }
358
      if (runas_limitprivs != NULL) {
359
    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
360
        "runas_limitprivs -> %s", runas_limitprivs);
361
      }
362
  }
363
#endif /* HAVE_PRIV_SET */
364
1
  if (cs->timeout > 0) {
365
0
      def_command_timeout = cs->timeout;
366
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
367
0
    "def_command_timeout -> %d", def_command_timeout);
368
0
  }
369
1
  if (cs->runcwd != NULL) {
370
0
      free(def_runcwd);
371
0
      def_runcwd = strdup(cs->runcwd);
372
0
      if (def_runcwd == NULL) {
373
0
    sudo_warnx(U_("%s: %s"), __func__,
374
0
        U_("unable to allocate memory"));
375
0
    debug_return_bool(false);
376
0
      }
377
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
378
0
    "def_runcwd -> %s", def_runcwd);
379
0
  }
380
1
  if (cs->runchroot != NULL) {
381
0
      free(def_runchroot);
382
0
      def_runchroot = strdup(cs->runchroot);
383
0
      if (def_runchroot == NULL) {
384
0
    sudo_warnx(U_("%s: %s"), __func__,
385
0
        U_("unable to allocate memory"));
386
0
    debug_return_bool(false);
387
0
      }
388
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
389
0
    "def_runchroot -> %s", def_runchroot);
390
0
  }
391
1
  if (cs->tags.nopasswd != UNSPEC) {
392
0
      def_authenticate = !cs->tags.nopasswd;
393
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
394
0
    "def_authenticate -> %s", def_authenticate ? "true" : "false");
395
0
  }
396
1
  if (cs->tags.noexec != UNSPEC) {
397
0
      def_noexec = cs->tags.noexec;
398
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
399
0
    "def_noexec -> %s", def_noexec ? "true" : "false");
400
0
  }
401
1
  if (cs->tags.intercept != UNSPEC) {
402
0
      def_intercept = cs->tags.intercept;
403
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
404
0
    "def_intercept -> %s", def_intercept ? "true" : "false");
405
0
  }
406
1
  if (cs->tags.setenv != UNSPEC) {
407
0
      def_setenv = cs->tags.setenv;
408
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
409
0
    "def_setenv -> %s", def_setenv ? "true" : "false");
410
0
  }
411
1
  if (cs->tags.log_input != UNSPEC) {
412
0
      def_log_input = cs->tags.log_input;
413
0
      cb_log_input(NULL, 0, 0, NULL, cs->tags.log_input);
414
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
415
0
    "def_log_input -> %s", def_log_input ? "true" : "false");
416
0
  }
417
1
  if (cs->tags.log_output != UNSPEC) {
418
0
      def_log_output = cs->tags.log_output;
419
0
      cb_log_output(NULL, 0, 0, NULL, cs->tags.log_output);
420
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
421
0
    "def_log_output -> %s", def_log_output ? "true" : "false");
422
0
  }
423
1
  if (cs->tags.send_mail != UNSPEC) {
424
0
      if (cs->tags.send_mail) {
425
0
    def_mail_all_cmnds = true;
426
0
    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
427
0
        "def_mail_all_cmnds -> true");
428
0
      } else {
429
0
    def_mail_all_cmnds = false;
430
0
    def_mail_always = false;
431
0
    def_mail_no_perms = false;
432
0
    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
433
0
        "def_mail_all_cmnds -> false, def_mail_always -> false, "
434
0
        "def_mail_no_perms -> false");
435
0
      }
436
0
  }
437
1
  if (cs->tags.follow != UNSPEC) {
438
0
      def_sudoedit_follow = cs->tags.follow;
439
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
440
0
    "def_sudoedit_follow -> %s", def_sudoedit_follow ? "true" : "false");
441
0
  }
442
1
    }
443
444
1
    debug_return_bool(true);
445
1
}
446
447
/*
448
 * Look up the user in the sudoers parse tree and check to see if they are
449
 * allowed to run the specified command on this host as the target user.
450
 */
451
int
452
sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int *cmnd_status,
453
    int pwflag)
454
32
{
455
32
    struct defaults_list *defs = NULL;
456
32
    struct sudoers_parse_tree *parse_tree = NULL;
457
32
    struct cmndspec *cs = NULL;
458
32
    struct sudo_nss *nss;
459
32
    struct cmnd_info info;
460
32
    int validated = FLAG_NO_USER | FLAG_NO_HOST;
461
32
    int m, match = UNSPEC;
462
32
    time_t now;
463
32
    debug_decl(sudoers_lookup, SUDOERS_DEBUG_PARSER);
464
465
    /*
466
     * Special case checking the "validate", "list" and "kill" pseudo-commands.
467
     */
468
32
    if (pwflag)
469
16
  debug_return_int(sudoers_lookup_pseudo(snl, pw, pwflag));
470
471
    /* Need to be runas user while stat'ing things. */
472
16
    if (!set_perms(PERM_RUNAS))
473
0
  debug_return_int(validated);
474
475
    /* Query each sudoers source and check the user. */
476
16
    time(&now);
477
16
    TAILQ_FOREACH(nss, snl, entries) {
478
16
  if (nss->query(nss, pw) == -1) {
479
      /* The query function should have printed an error message. */
480
0
      SET(validated, VALIDATE_ERROR);
481
0
      break;
482
0
  }
483
484
16
  m = sudoers_lookup_check(nss, pw, &validated, &info, &cs, &defs, now);
485
16
  if (m != UNSPEC) {
486
1
      match = m;
487
1
      parse_tree = nss->parse_tree;
488
1
  }
489
490
16
  if (!sudo_nss_can_continue(nss, m))
491
0
      break;
492
16
    }
493
16
    if (match != UNSPEC) {
494
1
  if (info.cmnd_path != NULL) {
495
      /* Update user_cmnd, user_stat, cmnd_status from matching entry. */
496
0
      free(user_cmnd);
497
0
      user_cmnd = info.cmnd_path;
498
0
      if (user_stat != NULL)
499
0
    *user_stat = info.cmnd_stat;
500
0
      *cmnd_status = info.status;
501
0
  }
502
1
  if (defs != NULL)
503
1
      (void)update_defaults(parse_tree, defs, SETDEF_GENERIC, false);
504
1
  if (!apply_cmndspec(cs))
505
0
      SET(validated, VALIDATE_ERROR);
506
1
  else if (match == ALLOW)
507
0
      SET(validated, VALIDATE_SUCCESS);
508
1
  else
509
1
      SET(validated, VALIDATE_FAILURE);
510
1
    }
511
16
    if (!restore_perms())
512
0
  SET(validated, VALIDATE_ERROR);
513
16
    debug_return_int(validated);
514
16
}
515
516
static int
517
display_priv_short(struct sudoers_parse_tree *parse_tree, struct passwd *pw,
518
    struct userspec *us, struct sudo_lbuf *lbuf)
519
2
{
520
2
    struct privilege *priv;
521
2
    int nfound = 0;
522
2
    debug_decl(display_priv_short, SUDOERS_DEBUG_PARSER);
523
524
2
    TAILQ_FOREACH(priv, &us->privileges, entries) {
525
2
  struct cmndspec *cs;
526
2
  struct cmndtag tags;
527
528
2
  if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW)
529
0
      continue;
530
531
2
  sudoers_defaults_list_to_tags(&priv->defaults, &tags);
532
4
  TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
533
4
      struct cmndspec *prev_cs = TAILQ_PREV(cs, cmndspec_list, entries);
534
535
4
      if (prev_cs == NULL || RUNAS_CHANGED(cs, prev_cs)) {
536
2
    struct member *m;
537
538
    /* Start new line, first entry or RunAs changed. */
539
2
    if (prev_cs != NULL)
540
0
        sudo_lbuf_append(lbuf, "\n");
541
2
    sudo_lbuf_append(lbuf, "    (");
542
2
    if (cs->runasuserlist != NULL) {
543
0
        TAILQ_FOREACH(m, cs->runasuserlist, entries) {
544
0
      if (m != TAILQ_FIRST(cs->runasuserlist))
545
0
          sudo_lbuf_append(lbuf, ", ");
546
0
      sudoers_format_member(lbuf, parse_tree, m, ", ",
547
0
          RUNASALIAS);
548
0
        }
549
2
    } else if (cs->runasgrouplist == NULL) {
550
2
        sudo_lbuf_append(lbuf, "%s", def_runas_default);
551
2
    } else {
552
0
        sudo_lbuf_append(lbuf, "%s", pw->pw_name);
553
0
    }
554
2
    if (cs->runasgrouplist != NULL) {
555
0
        sudo_lbuf_append(lbuf, " : ");
556
0
        TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
557
0
      if (m != TAILQ_FIRST(cs->runasgrouplist))
558
0
          sudo_lbuf_append(lbuf, ", ");
559
0
      sudoers_format_member(lbuf, parse_tree, m, ", ",
560
0
          RUNASALIAS);
561
0
        }
562
0
    }
563
2
    sudo_lbuf_append(lbuf, ") ");
564
2
    sudoers_format_cmndspec(lbuf, parse_tree, cs, NULL, tags, true);
565
2
      } else {
566
    /* Continue existing line. */
567
2
    sudo_lbuf_append(lbuf, ", ");
568
2
    sudoers_format_cmndspec(lbuf, parse_tree, cs, prev_cs, tags,
569
2
        true);
570
2
      }
571
4
      nfound++;
572
4
  }
573
2
  sudo_lbuf_append(lbuf, "\n");
574
2
    }
575
2
    debug_return_int(nfound);
576
2
}
577
578
/*
579
 * Compare the current cmndspec with the previous one to determine
580
 * whether we need to start a new long entry for "sudo -ll".
581
 * Returns true if we should start a new long entry, else false.
582
 */
583
static bool
584
new_long_entry(struct cmndspec *cs, struct cmndspec *prev_cs)
585
4
{
586
4
    debug_decl(new_long_entry, SUDOERS_DEBUG_PARSER);
587
588
4
    if (prev_cs == NULL)
589
2
  debug_return_bool(true);
590
2
    if (RUNAS_CHANGED(cs, prev_cs) || TAGS_CHANGED(prev_cs->tags, cs->tags))
591
0
  debug_return_bool(true);
592
#ifdef HAVE_PRIV_SET
593
    if (cs->privs && (!prev_cs->privs || strcmp(cs->privs, prev_cs->privs) != 0))
594
  debug_return_bool(true);
595
    if (cs->limitprivs && (!prev_cs->limitprivs || strcmp(cs->limitprivs, prev_cs->limitprivs) != 0))
596
  debug_return_bool(true);
597
#endif /* HAVE_PRIV_SET */
598
#ifdef HAVE_SELINUX
599
    if (cs->role && (!prev_cs->role || strcmp(cs->role, prev_cs->role) != 0))
600
  debug_return_bool(true);
601
    if (cs->type && (!prev_cs->type || strcmp(cs->type, prev_cs->type) != 0))
602
  debug_return_bool(true);
603
#endif /* HAVE_SELINUX */
604
#ifdef HAVE_APPARMOR
605
    if (cs->apparmor_profile && (!prev_cs->apparmor_profile || strcmp(cs->apparmor_profile, prev_cs->apparmor_profile) != 0))
606
  debug_return_bool(true);
607
#endif /* HAVE_APPARMOR */
608
2
    if (cs->runchroot && (!prev_cs->runchroot || strcmp(cs->runchroot, prev_cs->runchroot) != 0))
609
0
  debug_return_bool(true);
610
2
    if (cs->runcwd && (!prev_cs->runcwd || strcmp(cs->runcwd, prev_cs->runcwd) != 0))
611
0
  debug_return_bool(true);
612
2
    if (cs->timeout != prev_cs->timeout)
613
0
  debug_return_bool(true);
614
2
    if (cs->notbefore != prev_cs->notbefore)
615
0
  debug_return_bool(true);
616
2
    if (cs->notafter != prev_cs->notafter)
617
0
  debug_return_bool(true);
618
2
    debug_return_bool(false);
619
2
}
620
621
static int
622
display_priv_long(struct sudoers_parse_tree *parse_tree, struct passwd *pw,
623
    struct userspec *us, struct sudo_lbuf *lbuf)
624
2
{
625
2
    struct privilege *priv;
626
2
    int nfound = 0;
627
2
    debug_decl(display_priv_long, SUDOERS_DEBUG_PARSER);
628
629
2
    TAILQ_FOREACH(priv, &us->privileges, entries) {
630
2
  struct cmndspec *cs, *prev_cs;
631
632
2
  if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW)
633
0
      continue;
634
2
  prev_cs = NULL;
635
4
  TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
636
4
      struct defaults *d;
637
4
      struct member *m;
638
639
4
      if (new_long_entry(cs, prev_cs)) {
640
2
    int olen;
641
642
2
    if (priv->ldap_role != NULL) {
643
0
        sudo_lbuf_append(lbuf, _("\nLDAP Role: %s\n"),
644
0
      priv->ldap_role);
645
2
    } else {
646
2
        sudo_lbuf_append(lbuf, "%s", _("\nSudoers entry:\n"));
647
2
    }
648
2
    sudo_lbuf_append(lbuf, "%s", _("    RunAsUsers: "));
649
2
    if (cs->runasuserlist != NULL) {
650
0
        TAILQ_FOREACH(m, cs->runasuserlist, entries) {
651
0
      if (m != TAILQ_FIRST(cs->runasuserlist))
652
0
          sudo_lbuf_append(lbuf, ", ");
653
0
      sudoers_format_member(lbuf, parse_tree, m, ", ",
654
0
          RUNASALIAS);
655
0
        }
656
2
    } else if (cs->runasgrouplist == NULL) {
657
2
        sudo_lbuf_append(lbuf, "%s", def_runas_default);
658
2
    } else {
659
0
        sudo_lbuf_append(lbuf, "%s", pw->pw_name);
660
0
    }
661
2
    sudo_lbuf_append(lbuf, "\n");
662
2
    if (cs->runasgrouplist != NULL) {
663
0
        sudo_lbuf_append(lbuf, "%s", _("    RunAsGroups: "));
664
0
        TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
665
0
      if (m != TAILQ_FIRST(cs->runasgrouplist))
666
0
          sudo_lbuf_append(lbuf, ", ");
667
0
      sudoers_format_member(lbuf, parse_tree, m, ", ",
668
0
          RUNASALIAS);
669
0
        }
670
0
        sudo_lbuf_append(lbuf, "\n");
671
0
    }
672
2
    olen = lbuf->len;
673
2
    sudo_lbuf_append(lbuf, "%s", _("    Options: "));
674
2
    TAILQ_FOREACH(d, &priv->defaults, entries) {
675
0
        sudoers_format_default(lbuf, d);
676
0
        sudo_lbuf_append(lbuf, ", ");
677
0
    }
678
2
    if (TAG_SET(cs->tags.setenv))
679
0
        sudo_lbuf_append(lbuf, "%ssetenv, ", cs->tags.setenv ? "" : "!");
680
2
    if (TAG_SET(cs->tags.noexec))
681
0
        sudo_lbuf_append(lbuf, "%snoexec, ", cs->tags.noexec ? "" : "!");
682
2
    if (TAG_SET(cs->tags.intercept))
683
0
        sudo_lbuf_append(lbuf, "%sintercept, ", cs->tags.intercept ? "" : "!");
684
2
    if (TAG_SET(cs->tags.nopasswd))
685
0
        sudo_lbuf_append(lbuf, "%sauthenticate, ", cs->tags.nopasswd ? "!" : "");
686
2
    if (TAG_SET(cs->tags.log_input))
687
0
        sudo_lbuf_append(lbuf, "%slog_input, ", cs->tags.log_input ? "" : "!");
688
2
    if (TAG_SET(cs->tags.log_output))
689
0
        sudo_lbuf_append(lbuf, "%slog_output, ", cs->tags.log_output ? "" : "!");
690
2
    if (lbuf->buf[lbuf->len - 2] == ',') {
691
0
        lbuf->len -= 2; /* remove trailing ", " */
692
0
        sudo_lbuf_append(lbuf, "\n");
693
2
    } else {
694
2
        lbuf->len = olen; /* no options */
695
2
    }
696
#ifdef HAVE_PRIV_SET
697
    if (cs->privs)
698
        sudo_lbuf_append(lbuf, "    Privs: %s\n", cs->privs);
699
    if (cs->limitprivs)
700
        sudo_lbuf_append(lbuf, "    Limitprivs: %s\n", cs->limitprivs);
701
#endif /* HAVE_PRIV_SET */
702
#ifdef HAVE_SELINUX
703
    if (cs->role)
704
        sudo_lbuf_append(lbuf, "    Role: %s\n", cs->role);
705
    if (cs->type)
706
        sudo_lbuf_append(lbuf, "    Type: %s\n", cs->type);
707
#endif /* HAVE_SELINUX */
708
2
    if (cs->runchroot != NULL)
709
0
        sudo_lbuf_append(lbuf, "    Chroot: %s\n", cs->runchroot);
710
2
    if (cs->runcwd != NULL)
711
0
        sudo_lbuf_append(lbuf, "    Cwd: %s\n", cs->runcwd);
712
2
    if (cs->timeout > 0) {
713
0
        char numbuf[(((sizeof(int) * 8) + 2) / 3) + 2];
714
0
        (void)snprintf(numbuf, sizeof(numbuf), "%d", cs->timeout);
715
0
        sudo_lbuf_append(lbuf, "    Timeout: %s\n", numbuf);
716
0
    }
717
2
    if (cs->notbefore != UNSPEC) {
718
0
        char buf[sizeof("CCYYMMDDHHMMSSZ")] = "";
719
0
        struct tm gmt;
720
0
        int len;
721
0
        if (gmtime_r(&cs->notbefore, &gmt) != NULL) {
722
0
      len = strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &gmt);
723
0
      if (len != 0 && buf[sizeof(buf) - 1] == '\0')
724
0
          sudo_lbuf_append(lbuf, "    NotBefore: %s\n", buf);
725
0
        }
726
0
    }
727
2
    if (cs->notafter != UNSPEC) {
728
0
        char buf[sizeof("CCYYMMDDHHMMSSZ")] = "";
729
0
        struct tm gmt;
730
0
        int len;
731
0
        if (gmtime_r(&cs->notafter, &gmt) != NULL) {
732
0
      len = strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &gmt);
733
0
      if (len != 0 && buf[sizeof(buf) - 1] == '\0')
734
0
          sudo_lbuf_append(lbuf, "    NotAfter: %s\n", buf);
735
0
        }
736
0
    }
737
2
    sudo_lbuf_append(lbuf, "%s", _("    Commands:\n"));
738
2
      }
739
4
      sudo_lbuf_append(lbuf, "\t");
740
4
      sudoers_format_member(lbuf, parse_tree, cs->cmnd, "\n\t",
741
4
    CMNDALIAS);
742
4
      sudo_lbuf_append(lbuf, "\n");
743
4
      prev_cs = cs;
744
4
      nfound++;
745
4
  }
746
2
    }
747
2
    debug_return_int(nfound);
748
2
}
749
750
static int
751
sudo_display_userspecs(struct sudoers_parse_tree *parse_tree, struct passwd *pw,
752
    struct sudo_lbuf *lbuf, bool verbose)
753
32
{
754
32
    struct userspec *us;
755
32
    int nfound = 0;
756
32
    debug_decl(sudo_display_userspecs, SUDOERS_DEBUG_PARSER);
757
758
32
    TAILQ_FOREACH(us, &parse_tree->userspecs, entries) {
759
16
  if (userlist_matches(parse_tree, pw, &us->users) != ALLOW)
760
12
      continue;
761
762
4
  if (verbose)
763
2
      nfound += display_priv_long(parse_tree, pw, us, lbuf);
764
2
  else
765
2
      nfound += display_priv_short(parse_tree, pw, us, lbuf);
766
4
    }
767
32
    if (sudo_lbuf_error(lbuf))
768
0
  debug_return_int(-1);
769
32
    debug_return_int(nfound);
770
32
}
771
772
/*
773
 * Display matching Defaults entries for the given user on this host.
774
 */
775
static int
776
display_defaults(struct sudoers_parse_tree *parse_tree, struct passwd *pw,
777
    struct sudo_lbuf *lbuf)
778
32
{
779
32
    struct defaults *d;
780
32
    const char *prefix;
781
32
    int nfound = 0;
782
32
    debug_decl(display_defaults, SUDOERS_DEBUG_PARSER);
783
784
32
    if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
785
32
  prefix = "    ";
786
0
    else
787
0
  prefix = ", ";
788
789
32
    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
790
8
  switch (d->type) {
791
0
      case DEFAULTS_HOST:
792
0
    if (hostlist_matches(parse_tree, pw, &d->binding->members) != ALLOW)
793
0
        continue;
794
0
    break;
795
0
      case DEFAULTS_USER:
796
0
    if (userlist_matches(parse_tree, pw, &d->binding->members) != ALLOW)
797
0
        continue;
798
0
    break;
799
0
      case DEFAULTS_RUNAS:
800
0
      case DEFAULTS_CMND:
801
0
    continue;
802
8
  }
803
8
  sudo_lbuf_append(lbuf, "%s", prefix);
804
8
  sudoers_format_default(lbuf, d);
805
8
  prefix = ", ";
806
8
  nfound++;
807
8
    }
808
32
    if (sudo_lbuf_error(lbuf))
809
0
  debug_return_int(-1);
810
32
    debug_return_int(nfound);
811
32
}
812
813
/*
814
 * Display Defaults entries of the given type.
815
 */
816
static int
817
display_bound_defaults_by_type(struct sudoers_parse_tree *parse_tree,
818
    int deftype, struct sudo_lbuf *lbuf)
819
64
{
820
64
    struct defaults *d;
821
64
    struct defaults_binding *binding = NULL;
822
64
    struct member *m;
823
64
    const char *dsep;
824
64
    int atype, nfound = 0;
825
64
    debug_decl(display_bound_defaults_by_type, SUDOERS_DEBUG_PARSER);
826
827
64
    switch (deftype) {
828
0
  case DEFAULTS_HOST:
829
0
      atype = HOSTALIAS;
830
0
      dsep = "@";
831
0
      break;
832
0
  case DEFAULTS_USER:
833
0
      atype = USERALIAS;
834
0
      dsep = ":";
835
0
      break;
836
32
  case DEFAULTS_RUNAS:
837
32
      atype = RUNASALIAS;
838
32
      dsep = ">";
839
32
      break;
840
32
  case DEFAULTS_CMND:
841
32
      atype = CMNDALIAS;
842
32
      dsep = "!";
843
32
      break;
844
0
  default:
845
0
      debug_return_int(-1);
846
64
    }
847
64
    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
848
16
  if (d->type != deftype)
849
16
      continue;
850
851
0
  nfound++;
852
0
  if (binding != d->binding) {
853
0
      binding = d->binding;
854
0
      if (nfound != 1)
855
0
    sudo_lbuf_append(lbuf, "\n");
856
0
      sudo_lbuf_append(lbuf, "    Defaults%s", dsep);
857
0
      TAILQ_FOREACH(m, &binding->members, entries) {
858
0
    if (m != TAILQ_FIRST(&binding->members))
859
0
        sudo_lbuf_append(lbuf, ", ");
860
0
    sudoers_format_member(lbuf, parse_tree, m, ", ", atype);
861
0
      }
862
0
      sudo_lbuf_append(lbuf, " ");
863
0
  } else
864
0
      sudo_lbuf_append(lbuf, ", ");
865
0
  sudoers_format_default(lbuf, d);
866
0
    }
867
868
64
    if (sudo_lbuf_error(lbuf))
869
0
  debug_return_int(-1);
870
64
    debug_return_int(nfound);
871
64
}
872
873
/*
874
 * Display Defaults entries that are per-runas or per-command
875
 */
876
static int
877
display_bound_defaults(struct sudoers_parse_tree *parse_tree,
878
    struct passwd *pw, struct sudo_lbuf *lbuf)
879
32
{
880
32
    int nfound = 0;
881
32
    debug_decl(display_bound_defaults, SUDOERS_DEBUG_PARSER);
882
883
    /* XXX - should only print ones that match what the user can do. */
884
32
    nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_RUNAS, lbuf);
885
32
    nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_CMND, lbuf);
886
887
32
    if (sudo_lbuf_error(lbuf))
888
0
  debug_return_int(-1);
889
32
    debug_return_int(nfound);
890
32
}
891
892
static int
893
output(const char *buf)
894
32
{
895
32
    struct sudo_conv_message msg;
896
32
    struct sudo_conv_reply repl;
897
32
    debug_decl(output, SUDOERS_DEBUG_NSS);
898
899
    /* Call conversation function */
900
32
    memset(&msg, 0, sizeof(msg));
901
32
    msg.msg_type = SUDO_CONV_INFO_MSG;
902
32
    msg.msg = buf;
903
32
    memset(&repl, 0, sizeof(repl));
904
32
    if (sudo_conv(1, &msg, &repl, NULL) == -1)
905
0
  debug_return_int(0);
906
32
    debug_return_int(strlen(buf));
907
32
}
908
909
/*
910
 * Print out privileges for the specified user.
911
 * Returns true on success or -1 on error.
912
 */
913
int
914
display_privs(struct sudo_nss_list *snl, struct passwd *pw, bool verbose)
915
32
{
916
32
    struct sudo_nss *nss;
917
32
    struct sudo_lbuf def_buf, priv_buf;
918
32
    struct stat sb;
919
32
    int cols, count, olen, n;
920
32
    debug_decl(display_privs, SUDOERS_DEBUG_PARSER);
921
922
32
    cols = sudo_user.cols;
923
32
    if (fstat(STDOUT_FILENO, &sb) == 0 && S_ISFIFO(sb.st_mode))
924
0
  cols = 0;
925
32
    sudo_lbuf_init(&def_buf, output, 4, NULL, cols);
926
32
    sudo_lbuf_init(&priv_buf, output, 8, NULL, cols);
927
928
32
    sudo_lbuf_append(&def_buf, _("Matching Defaults entries for %s on %s:\n"),
929
32
  pw->pw_name, user_srunhost);
930
32
    count = 0;
931
32
    TAILQ_FOREACH(nss, snl, entries) {
932
32
  n = display_defaults(nss->parse_tree, pw, &def_buf);
933
32
  if (n == -1)
934
0
      goto bad;
935
32
  count += n;
936
32
    }
937
32
    if (count != 0) {
938
8
  sudo_lbuf_append(&def_buf, "\n\n");
939
24
    } else {
940
  /* Undo Defaults header. */
941
24
  def_buf.len = 0;
942
24
    }
943
944
    /* Display Runas and Cmnd-specific defaults. */
945
32
    olen = def_buf.len;
946
32
    sudo_lbuf_append(&def_buf, _("Runas and Command-specific defaults for %s:\n"),
947
32
  pw->pw_name);
948
32
    count = 0;
949
32
    TAILQ_FOREACH(nss, snl, entries) {
950
32
  n = display_bound_defaults(nss->parse_tree, pw, &def_buf);
951
32
  if (n == -1)
952
0
      goto bad;
953
32
  count += n;
954
32
    }
955
32
    if (count != 0) {
956
0
  sudo_lbuf_append(&def_buf, "\n\n");
957
32
    } else {
958
  /* Undo Defaults header. */
959
32
  def_buf.len = olen;
960
32
    }
961
962
    /* Display privileges from all sources. */
963
32
    sudo_lbuf_append(&priv_buf,
964
32
  _("User %s may run the following commands on %s:\n"),
965
32
  pw->pw_name, user_srunhost);
966
32
    count = 0;
967
32
    TAILQ_FOREACH(nss, snl, entries) {
968
32
  if (nss->query(nss, pw) != -1) {
969
32
      n = sudo_display_userspecs(nss->parse_tree, pw, &priv_buf, verbose);
970
32
      if (n == -1)
971
0
    goto bad;
972
32
      count += n;
973
32
  }
974
32
    }
975
32
    if (count == 0) {
976
28
  def_buf.len = 0;
977
28
  priv_buf.len = 0;
978
28
  sudo_lbuf_append(&priv_buf,
979
28
      _("User %s is not allowed to run sudo on %s.\n"),
980
28
      pw->pw_name, user_srunhost);
981
28
    }
982
32
    if (sudo_lbuf_error(&def_buf) || sudo_lbuf_error(&priv_buf))
983
0
  goto bad;
984
985
32
    sudo_lbuf_print(&def_buf);
986
32
    sudo_lbuf_print(&priv_buf);
987
988
32
    sudo_lbuf_destroy(&def_buf);
989
32
    sudo_lbuf_destroy(&priv_buf);
990
991
32
    debug_return_int(true);
992
0
bad:
993
0
    sudo_lbuf_destroy(&def_buf);
994
0
    sudo_lbuf_destroy(&priv_buf);
995
996
0
    debug_return_int(-1);
997
0
}
998
999
static int
1000
display_cmnd_check(struct sudoers_parse_tree *parse_tree, struct passwd *pw,
1001
    time_t now)
1002
0
{
1003
0
    int host_match, runas_match, cmnd_match = UNSPEC;
1004
0
    char *saved_user_cmnd, *saved_user_base;
1005
0
    struct cmndspec *cs;
1006
0
    struct privilege *priv;
1007
0
    struct userspec *us;
1008
0
    debug_decl(display_cmnd_check, SUDOERS_DEBUG_PARSER);
1009
1010
    /*
1011
     * For "sudo -l command", user_cmnd is "list" and the actual
1012
     * command we are checking is in list_cmnd.
1013
     */
1014
0
    saved_user_cmnd = user_cmnd;
1015
0
    saved_user_base = user_base;
1016
0
    user_cmnd = list_cmnd;
1017
0
    user_base = sudo_basename(user_cmnd);
1018
1019
0
    TAILQ_FOREACH_REVERSE(us, &parse_tree->userspecs, userspec_list, entries) {
1020
0
  if (userlist_matches(parse_tree, pw, &us->users) != ALLOW)
1021
0
      continue;
1022
0
  TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) {
1023
0
      host_match = hostlist_matches(parse_tree, pw, &priv->hostlist);
1024
0
      if (host_match != ALLOW)
1025
0
    continue;
1026
0
      TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) {
1027
0
    if (cs->notbefore != UNSPEC) {
1028
0
        if (now < cs->notbefore)
1029
0
      continue;
1030
0
    }
1031
0
    if (cs->notafter != UNSPEC) {
1032
0
        if (now > cs->notafter)
1033
0
      continue;
1034
0
    }
1035
0
    runas_match = runaslist_matches(parse_tree, cs->runasuserlist,
1036
0
        cs->runasgrouplist, NULL, NULL);
1037
0
    if (runas_match == ALLOW) {
1038
0
        cmnd_match = cmnd_matches(parse_tree, cs->cmnd,
1039
0
      cs->runchroot, NULL);
1040
0
        if (cmnd_match != UNSPEC)
1041
0
      goto done;
1042
0
    }
1043
0
      }
1044
0
  }
1045
0
    }
1046
0
done:
1047
0
    user_cmnd = saved_user_cmnd;
1048
0
    user_base = saved_user_base;
1049
0
    debug_return_int(cmnd_match);
1050
0
}
1051
1052
/*
1053
 * Check user_cmnd against sudoers and print the matching entry if the
1054
 * command is allowed.
1055
 * Returns true if the command is allowed, false if not or -1 on error.
1056
 */
1057
int
1058
display_cmnd(struct sudo_nss_list *snl, struct passwd *pw)
1059
0
{
1060
0
    struct sudo_nss *nss;
1061
0
    int m, match = UNSPEC;
1062
0
    int ret = false;
1063
0
    time_t now;
1064
0
    debug_decl(display_cmnd, SUDOERS_DEBUG_PARSER);
1065
1066
    /* Iterate over each source, checking for the command. */
1067
0
    time(&now);
1068
0
    TAILQ_FOREACH(nss, snl, entries) {
1069
0
  if (nss->query(nss, pw) == -1) {
1070
      /* The query function should have printed an error message. */
1071
0
      debug_return_int(-1);
1072
0
  }
1073
1074
0
  m = display_cmnd_check(nss->parse_tree, pw, now);
1075
0
  if (m != UNSPEC)
1076
0
      match = m;
1077
1078
0
  if (!sudo_nss_can_continue(nss, m))
1079
0
      break;
1080
0
    }
1081
0
    if (match == ALLOW) {
1082
0
  const int len = sudo_printf(SUDO_CONV_INFO_MSG, "%s%s%s\n",
1083
0
      list_cmnd, user_args ? " " : "", user_args ? user_args : "");
1084
0
  ret = len < 0 ? -1 : true;
1085
0
    }
1086
0
    debug_return_int(ret);
1087
0
}