Coverage Report

Created: 2025-11-05 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sudo/plugins/sudoers/lookup.c
Line
Count
Source
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 2004-2005, 2007-2025 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 <stdio.h>
22
#include <stdlib.h>
23
#include <string.h>
24
#include <unistd.h>
25
#include <pwd.h>
26
27
#include <sudoers.h>
28
#include <gram.h>
29
30
static int
31
runas_matches_pw(struct sudoers_parse_tree *parse_tree,
32
    const struct cmndspec *cs, const struct passwd *pw)
33
0
{
34
0
    debug_decl(runas_matches_pw, SUDOERS_DEBUG_PARSER);
35
36
0
    if (cs->runasuserlist != NULL)
37
0
  debug_return_int(userlist_matches(parse_tree, pw, cs->runasuserlist));
38
39
0
    if (cs->runasgrouplist == NULL) {
40
  /* No explicit runas user or group, use default. */
41
0
  if (userpw_matches(def_runas_default, pw->pw_name, pw) == ALLOW)
42
0
      debug_return_int(ALLOW);
43
0
    }
44
0
    debug_return_int(UNSPEC);
45
0
}
46
47
/*
48
 * Look up the user in the sudoers parse tree for pseudo-commands like
49
 * list, verify and kill.
50
 */
51
static unsigned int
52
sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct sudoers_context *ctx,
53
    time_t now, sudoers_lookup_callback_fn_t callback, void *cb_data,
54
    int pwflag)
55
8
{
56
8
    char *saved_runchroot;
57
8
    struct passwd *root_pw = NULL;
58
8
    struct sudo_nss *nss;
59
8
    struct cmndspec *cs;
60
8
    struct privilege *priv;
61
8
    struct userspec *us;
62
8
    struct defaults *def;
63
8
    int nopass, match = UNSPEC;
64
8
    unsigned int validated = 0;
65
8
    enum def_tuple pwcheck;
66
8
    debug_decl(sudoers_lookup_pseudo, SUDOERS_DEBUG_PARSER);
67
68
8
    pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
69
8
    nopass = (pwcheck == never || pwcheck == all) ? true : false;
70
71
8
    if (ctx->runas.list_pw != NULL) {
72
0
  root_pw = sudo_getpwuid(ROOT_UID);
73
0
  if (root_pw == NULL)
74
0
      sudo_warnx(U_("unknown uid %u"), ROOT_UID);
75
8
    } else {
76
8
  SET(validated, FLAG_NO_CHECK);
77
8
    }
78
79
    /* Don't use chroot setting for pseudo-commands. */
80
8
    saved_runchroot = def_runchroot;
81
8
    def_runchroot = NULL;
82
83
8
    TAILQ_FOREACH(nss, snl, entries) {
84
8
  if (nss->query(ctx, nss, ctx->user.pw) == -1) {
85
      /* The query function should have printed an error message. */
86
0
      SET(validated, VALIDATE_ERROR);
87
0
      break;
88
0
  }
89
90
  /*
91
   * We have to traverse the policy forwards, not in reverse,
92
   * to support the "pwcheck == all" case.
93
   */
94
8
  TAILQ_FOREACH(us, &nss->parse_tree->userspecs, entries) {
95
0
      const int user_match = userlist_matches(nss->parse_tree,
96
0
    ctx->user.pw, &us->users);
97
0
      if (user_match != ALLOW) {
98
0
    if (callback != NULL && user_match == DENY) {
99
0
        callback(nss->parse_tree, us, user_match, NULL, UNSPEC,
100
0
      NULL, UNSPEC, UNSPEC, UNSPEC, cb_data);
101
0
    }
102
0
    continue;
103
0
      }
104
0
      TAILQ_FOREACH(priv, &us->privileges, entries) {
105
0
    int priv_nopass = UNSPEC;
106
0
    const int host_match = hostlist_matches(nss->parse_tree,
107
0
        ctx->user.pw, &priv->hostlist);
108
0
    if (host_match != ALLOW) {
109
0
        if (callback != NULL) {
110
0
      callback(nss->parse_tree, us, user_match, priv,
111
0
          host_match, NULL, UNSPEC, UNSPEC, UNSPEC, cb_data);
112
0
        }
113
0
        continue;
114
0
    }
115
0
    TAILQ_FOREACH(def, &priv->defaults, entries) {
116
0
        if (strcmp(def->var, "authenticate") == 0) {
117
0
      priv_nopass = !def->op;
118
0
      break;
119
0
        }
120
0
    }
121
0
    TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
122
0
        int cmnd_match = UNSPEC;
123
0
        int date_match = UNSPEC;
124
0
        int runas_match = UNSPEC;
125
126
0
        if (cs->notbefore != UNSPEC) {
127
0
      date_match = now < cs->notbefore ? DENY : ALLOW;
128
0
        }
129
0
        if (cs->notafter != UNSPEC) {
130
0
      date_match = now > cs->notafter ? DENY : ALLOW;
131
0
        }
132
        /*
133
         * Root can list any user's privileges.
134
         * A user may always list their own privileges.
135
         */
136
0
        if (ctx->user.uid == 0 || ctx->runas.list_pw == NULL ||
137
0
          ctx->user.uid == ctx->runas.list_pw->pw_uid) {
138
0
      cmnd_match = ALLOW;
139
0
      runas_match = ALLOW;
140
0
        } else if (date_match != DENY) {
141
      /*
142
       * To list another user's privileges, the runas
143
       * user must match the list user or root.
144
       */
145
0
      runas_match = runas_matches_pw(nss->parse_tree, cs,
146
0
          ctx->runas.list_pw);
147
0
      switch (runas_match) {
148
0
      case DENY:
149
0
          break;
150
0
      case ALLOW:
151
          /*
152
           * RunAs user matches list user.
153
           * Match on command "list" or ALL.
154
           */
155
0
          cmnd_match = cmnd_matches(nss->parse_tree,
156
0
        cs->cmnd, cs->runchroot, NULL);
157
0
          break;
158
0
      default:
159
          /*
160
           * RunAs user doesn't match list user.
161
           * Only allow listing if the user has
162
           * "sudo ALL" for root.
163
           */
164
0
          if (root_pw != NULL &&
165
0
            runas_matches_pw(nss->parse_tree, cs,
166
0
            root_pw) == ALLOW) {
167
0
        runas_match = ALLOW;
168
0
        cmnd_match = cmnd_matches_all(nss->parse_tree,
169
0
            cs->cmnd, cs->runchroot, NULL);
170
0
          }
171
0
          break;
172
0
      }
173
0
        }
174
175
        /*
176
         * Apply the NOPASSWD tag if the entry matched.
177
         * This is relevant for "sudo -U otheruser -l".
178
         */
179
0
        if (cmnd_match == ALLOW && runas_match == ALLOW &&
180
0
          date_match != DENY) {
181
0
      if (pwcheck == any) {
182
0
          if (cs->tags.nopasswd == true || priv_nopass == true)
183
0
        nopass = true;
184
0
      } else if (pwcheck == all) {
185
0
          if (cs->tags.nopasswd != true && priv_nopass != true)
186
0
        nopass = false;
187
0
      }
188
0
        }
189
190
0
        if (callback != NULL) {
191
0
      callback(nss->parse_tree, us, user_match, priv,
192
0
          host_match, cs, date_match, runas_match,
193
0
          cmnd_match, cb_data);
194
0
        }
195
0
        if (SPECIFIED(cmnd_match)) {
196
      /*
197
       * We take the last match but must process
198
       * the entire policy for pwcheck == all.
199
       */
200
0
      match = cmnd_match;
201
0
        }
202
0
    }
203
0
      }
204
0
  }
205
8
  if (!sudo_nss_can_continue(nss, match))
206
0
      break;
207
8
    }
208
8
    if (root_pw != NULL)
209
0
  sudo_pw_delref(root_pw);
210
8
    if (match == ALLOW || ctx->user.uid == 0) {
211
  /* User has an entry for this host. */
212
8
  SET(validated, VALIDATE_SUCCESS);
213
8
    } else {
214
  /* No entry or user is not allowed to list other users. */
215
0
  SET(validated, VALIDATE_FAILURE);
216
0
    }
217
8
    if (pwcheck == always && def_authenticate)
218
0
  SET(validated, FLAG_CHECK_USER);
219
8
    else if (nopass == true)
220
0
  def_authenticate = false;
221
222
    /* Restore original def_runchroot. */
223
8
    def_runchroot = saved_runchroot;
224
225
8
    debug_return_uint(validated);
226
8
}
227
228
static int
229
sudoers_lookup_check(struct sudo_nss *nss, struct sudoers_context *ctx,
230
    unsigned int *validated, struct cmnd_info *info, time_t now,
231
    sudoers_lookup_callback_fn_t callback, void *cb_data,
232
    struct cmndspec **matching_cs, struct defaults_list **defs)
233
8
{
234
8
    struct cmndspec *cs;
235
8
    struct privilege *priv;
236
8
    struct userspec *us;
237
8
    debug_decl(sudoers_lookup_check, SUDOERS_DEBUG_PARSER);
238
239
8
    memset(info, 0, sizeof(*info));
240
241
8
    TAILQ_FOREACH_REVERSE(us, &nss->parse_tree->userspecs, userspec_list, entries) {
242
0
  const int user_match = userlist_matches(nss->parse_tree, ctx->user.pw,
243
0
      &us->users);
244
0
  if (user_match != ALLOW) {
245
0
      if (callback != NULL && user_match == DENY) {
246
0
    callback(nss->parse_tree, us, user_match, NULL, UNSPEC, NULL,
247
0
        UNSPEC, UNSPEC, UNSPEC, cb_data);
248
0
      }
249
0
      continue;
250
0
  }
251
0
  CLR(*validated, FLAG_NO_USER);
252
0
  TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) {
253
0
      const int host_match = hostlist_matches(nss->parse_tree,
254
0
    ctx->user.pw, &priv->hostlist);
255
0
      if (host_match == ALLOW) {
256
0
    CLR(*validated, FLAG_NO_HOST);
257
0
      } else {
258
0
    if (callback != NULL) {
259
0
        callback(nss->parse_tree, us, user_match, priv, host_match,
260
0
      NULL, UNSPEC, UNSPEC, UNSPEC, cb_data);
261
0
    }
262
0
    continue;
263
0
      }
264
0
      TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) {
265
0
    int cmnd_match = UNSPEC;
266
0
    int date_match = UNSPEC;
267
0
    int runas_match = UNSPEC;
268
269
0
    if (cs->notbefore != UNSPEC) {
270
0
        date_match = now < cs->notbefore ? DENY : ALLOW;
271
0
    }
272
0
    if (cs->notafter != UNSPEC) {
273
0
        date_match = now > cs->notafter ? DENY : ALLOW;
274
0
    }
275
0
    if (date_match != DENY) {
276
0
        runas_match = runaslist_matches(nss->parse_tree,
277
0
      cs->runasuserlist, cs->runasgrouplist);
278
0
        if (runas_match == ALLOW) {
279
0
      cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd,
280
0
          cs->runchroot, info);
281
0
        }
282
0
    }
283
0
    if (callback != NULL) {
284
0
        callback(nss->parse_tree, us, user_match, priv, host_match,
285
0
      cs, date_match, runas_match, cmnd_match, cb_data);
286
0
    }
287
288
0
    if (SPECIFIED(cmnd_match)) {
289
0
        *matching_cs = cs;
290
0
        *defs = &priv->defaults;
291
0
        sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
292
0
      "userspec matched @ %s:%d:%d: %s",
293
0
      us->file ? us->file : "???", us->line, us->column,
294
0
      cmnd_match ? "allowed" : "denied");
295
0
        debug_return_int(cmnd_match);
296
0
    }
297
0
    free(info->cmnd_path);
298
0
    memset(info, 0, sizeof(*info));
299
0
      }
300
0
  }
301
0
    }
302
8
    debug_return_int(UNSPEC);
303
8
}
304
305
/*
306
 * Apply cmndspec-specific settings including SELinux role/type,
307
 * AppArmor profile, Solaris privs, and command tags.
308
 */
309
static bool
310
apply_cmndspec(struct sudoers_context *ctx, struct cmndspec *cs)
311
0
{
312
0
    debug_decl(apply_cmndspec, SUDOERS_DEBUG_PARSER);
313
314
0
    if (cs != NULL) {
315
  /* Set role and type if not specified on command line. */
316
0
  if (ctx->runas.role == NULL) {
317
0
      if (cs->role != NULL) {
318
0
    ctx->runas.role = strdup(cs->role);
319
0
    if (ctx->runas.role == NULL) {
320
0
        sudo_warnx(U_("%s: %s"), __func__,
321
0
      U_("unable to allocate memory"));
322
0
        debug_return_bool(false);
323
0
    }
324
0
      } else {
325
0
    ctx->runas.role = def_role;
326
0
    def_role = NULL;
327
0
      }
328
0
      if (ctx->runas.role != NULL) {
329
0
    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
330
0
        "ctx->runas.role -> %s", ctx->runas.role);
331
0
      }
332
0
  }
333
0
  if (ctx->runas.type == NULL) {
334
0
      if (cs->type != NULL) {
335
0
    ctx->runas.type = strdup(cs->type);
336
0
    if (ctx->runas.type == NULL) {
337
0
        sudo_warnx(U_("%s: %s"), __func__,
338
0
      U_("unable to allocate memory"));
339
0
        debug_return_bool(false);
340
0
    }
341
0
      } else {
342
0
    ctx->runas.type = def_type;
343
0
    def_type = NULL;
344
0
      }
345
0
      if (ctx->runas.type != NULL) {
346
0
    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
347
0
        "ctx->runas.type -> %s", ctx->runas.type);
348
0
      }
349
0
  }
350
  /* Set AppArmor profile, if specified */
351
0
  if (cs->apparmor_profile != NULL) {
352
0
      free(ctx->runas.apparmor_profile);
353
0
      ctx->runas.apparmor_profile = strdup(cs->apparmor_profile);
354
0
      if (ctx->runas.apparmor_profile == NULL) {
355
0
    sudo_warnx(U_("%s: %s"), __func__,
356
0
        U_("unable to allocate memory"));
357
0
    debug_return_bool(false);
358
0
      }
359
0
  } else {
360
0
      free(ctx->runas.apparmor_profile);
361
0
      ctx->runas.apparmor_profile = def_apparmor_profile;
362
0
      def_apparmor_profile = NULL;
363
0
  }
364
0
  if (ctx->runas.apparmor_profile != NULL) {
365
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
366
0
    "ctx->runas.apparmor_profile -> %s", ctx->runas.apparmor_profile);
367
0
  }
368
  /* Set Solaris privilege sets */
369
0
  if (cs->privs != NULL) {
370
0
      free(ctx->runas.privs);
371
0
      ctx->runas.privs = strdup(cs->privs);
372
0
      if (ctx->runas.privs == 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
  } else {
378
0
      free(ctx->runas.privs);
379
0
      ctx->runas.privs = def_privs;
380
0
      def_privs = NULL;
381
0
  }
382
0
  if (ctx->runas.privs != NULL) {
383
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
384
0
    "ctx->runas.privs -> %s", ctx->runas.privs);
385
0
  }
386
0
  if (cs->limitprivs != NULL) {
387
0
      free(ctx->runas.limitprivs);
388
0
      ctx->runas.limitprivs = strdup(cs->limitprivs);
389
0
      if (ctx->runas.limitprivs == NULL) {
390
0
    sudo_warnx(U_("%s: %s"), __func__,
391
0
        U_("unable to allocate memory"));
392
0
    debug_return_bool(false);
393
0
      }
394
0
  } else {
395
0
      free(ctx->runas.limitprivs);
396
0
      ctx->runas.limitprivs = def_limitprivs;
397
0
      def_limitprivs = NULL;
398
0
  }
399
0
  if (ctx->runas.limitprivs != NULL) {
400
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
401
0
    "ctx->runas.limitprivs -> %s", ctx->runas.limitprivs);
402
0
  }
403
0
  if (cs->timeout > 0) {
404
0
      def_command_timeout = cs->timeout;
405
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
406
0
    "def_command_timeout -> %d", def_command_timeout);
407
0
  }
408
0
  if (cs->runcwd != NULL) {
409
0
      free(def_runcwd);
410
0
      def_runcwd = strdup(cs->runcwd);
411
0
      if (def_runcwd == NULL) {
412
0
    sudo_warnx(U_("%s: %s"), __func__,
413
0
        U_("unable to allocate memory"));
414
0
    debug_return_bool(false);
415
0
      }
416
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
417
0
    "def_runcwd -> %s", def_runcwd);
418
0
  }
419
0
  if (cs->runchroot != NULL) {
420
0
      free(def_runchroot);
421
0
      def_runchroot = strdup(cs->runchroot);
422
0
      if (def_runchroot == NULL) {
423
0
    sudo_warnx(U_("%s: %s"), __func__,
424
0
        U_("unable to allocate memory"));
425
0
    debug_return_bool(false);
426
0
      }
427
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
428
0
    "def_runchroot -> %s", def_runchroot);
429
0
  }
430
0
  if (cs->tags.nopasswd != UNSPEC) {
431
0
      def_authenticate = !cs->tags.nopasswd;
432
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
433
0
    "def_authenticate -> %s", def_authenticate ? "true" : "false");
434
0
  }
435
0
  if (cs->tags.noexec != UNSPEC) {
436
0
      def_noexec = cs->tags.noexec;
437
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
438
0
    "def_noexec -> %s", def_noexec ? "true" : "false");
439
0
  }
440
0
  if (cs->tags.intercept != UNSPEC) {
441
0
      def_intercept = cs->tags.intercept;
442
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
443
0
    "def_intercept -> %s", def_intercept ? "true" : "false");
444
0
  }
445
0
  if (cs->tags.setenv != UNSPEC) {
446
0
      def_setenv = cs->tags.setenv;
447
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
448
0
    "def_setenv -> %s", def_setenv ? "true" : "false");
449
0
  }
450
0
  if (cs->tags.log_input != UNSPEC) {
451
0
      def_log_input = cs->tags.log_input;
452
0
      cb_log_input(ctx, NULL, 0, 0, NULL, cs->tags.log_input);
453
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
454
0
    "def_log_input -> %s", def_log_input ? "true" : "false");
455
0
  }
456
0
  if (cs->tags.log_output != UNSPEC) {
457
0
      def_log_output = cs->tags.log_output;
458
0
      cb_log_output(ctx, NULL, 0, 0, NULL, cs->tags.log_output);
459
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
460
0
    "def_log_output -> %s", def_log_output ? "true" : "false");
461
0
  }
462
0
  if (cs->tags.send_mail != UNSPEC) {
463
0
      if (cs->tags.send_mail) {
464
0
    def_mail_all_cmnds = true;
465
0
    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
466
0
        "def_mail_all_cmnds -> true");
467
0
      } else {
468
0
    def_mail_all_cmnds = false;
469
0
    def_mail_always = false;
470
0
    def_mail_no_perms = false;
471
0
    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
472
0
        "def_mail_all_cmnds -> false, def_mail_always -> false, "
473
0
        "def_mail_no_perms -> false");
474
0
      }
475
0
  }
476
0
  if (cs->tags.follow != UNSPEC) {
477
0
      def_sudoedit_follow = cs->tags.follow;
478
0
      sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
479
0
    "def_sudoedit_follow -> %s", def_sudoedit_follow ? "true" : "false");
480
0
  }
481
0
    }
482
483
0
    debug_return_bool(true);
484
0
}
485
486
/*
487
 * Look up the user in the sudoers parse tree and check to see if they are
488
 * allowed to run the specified command on this host as the target user.
489
 */
490
unsigned int
491
sudoers_lookup(struct sudo_nss_list *snl, struct sudoers_context *ctx,
492
    time_t now, sudoers_lookup_callback_fn_t callback, void *cb_data,
493
    int *cmnd_status, int pwflag)
494
16
{
495
16
    struct defaults_list *defs = NULL;
496
16
    struct sudoers_parse_tree *parse_tree = NULL;
497
16
    struct cmndspec *cs = NULL;
498
16
    struct sudo_nss *nss;
499
16
    struct cmnd_info info;
500
16
    unsigned int validated = FLAG_NO_USER | FLAG_NO_HOST;
501
16
    int m, match = UNSPEC;
502
16
    debug_decl(sudoers_lookup, SUDOERS_DEBUG_PARSER);
503
504
    /*
505
     * Special case checking the "validate", "list" and "kill" pseudo-commands.
506
     */
507
16
    if (pwflag) {
508
8
  debug_return_uint(sudoers_lookup_pseudo(snl, ctx, now, callback,
509
8
      cb_data, pwflag));
510
8
    }
511
512
    /* Need to be runas user while stat'ing things. */
513
8
    if (!set_perms(ctx, PERM_RUNAS))
514
0
  debug_return_uint(validated);
515
516
    /* Query each sudoers source and check the user. */
517
8
    TAILQ_FOREACH(nss, snl, entries) {
518
8
  if (nss->query(ctx, nss, ctx->user.pw) == -1) {
519
      /* The query function should have printed an error message. */
520
0
      SET(validated, VALIDATE_ERROR);
521
0
      break;
522
0
  }
523
524
8
  m = sudoers_lookup_check(nss, ctx, &validated, &info, now, callback,
525
8
      cb_data, &cs, &defs);
526
8
  if (SPECIFIED(m)) {
527
0
      match = m;
528
0
      parse_tree = nss->parse_tree;
529
0
  }
530
531
8
  if (!sudo_nss_can_continue(nss, m))
532
0
      break;
533
8
    }
534
8
    if (SPECIFIED(match)) {
535
0
  if (info.cmnd_path != NULL) {
536
      /* Update cmnd, cmnd_stat, cmnd_status from matching entry. */
537
0
      free(ctx->user.cmnd);
538
0
      ctx->user.cmnd = info.cmnd_path;
539
0
      if (ctx->user.cmnd_stat != NULL)
540
0
    *ctx->user.cmnd_stat = info.cmnd_stat;
541
0
      *cmnd_status = info.status;
542
0
  }
543
0
  if (defs != NULL)
544
0
      (void)update_defaults(ctx, parse_tree, defs, SETDEF_GENERIC, false);
545
0
  if (!apply_cmndspec(ctx, cs))
546
0
      SET(validated, VALIDATE_ERROR);
547
0
  else if (match == ALLOW)
548
0
      SET(validated, VALIDATE_SUCCESS);
549
0
  else
550
0
      SET(validated, VALIDATE_FAILURE);
551
0
    }
552
8
    if (!restore_perms())
553
0
  SET(validated, VALIDATE_ERROR);
554
8
    debug_return_uint(validated);
555
8
}