Skip to content

Commit 886f141

Browse files
committed
Move the check for running setid commands in intercept mode to later.
Checking for setid commands in intercept mode after command matching allows us to log a proper error message. Previously, we simply ignored setid commands when matching and the only indication of why was in the debug logs.
1 parent 45e3c0d commit 886f141

6 files changed

Lines changed: 43 additions & 66 deletions

File tree

plugins/sudoers/logging.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,8 @@ log_denial(const struct sudoers_context *ctx, unsigned int status,
295295
message = N_("user NOT in sudoers");
296296
else if (ISSET(status, FLAG_NO_HOST))
297297
message = N_("user NOT authorized on host");
298+
else if (ISSET(status, FLAG_INTERCEPT_SETID))
299+
message = N_("setid command rejected in intercept mode");
298300
else
299301
message = N_("command not allowed");
300302

@@ -322,6 +324,9 @@ log_denial(const struct sudoers_context *ctx, unsigned int status,
322324
} else if (ISSET(status, FLAG_NO_HOST)) {
323325
sudo_printf(SUDO_CONV_ERROR_MSG, _("%s is not allowed to run sudo "
324326
"on %s.\n"), ctx->user.name, ctx->runas.shost);
327+
} else if (ISSET(status, FLAG_INTERCEPT_SETID)) {
328+
sudo_printf(SUDO_CONV_ERROR_MSG, _("%s: %s\n"), getprogname(),
329+
_("setid commands are not permitted in intercept mode"));
325330
} else if (ISSET(status, FLAG_NO_CHECK)) {
326331
sudo_printf(SUDO_CONV_ERROR_MSG, _("Sorry, user %s may not run "
327332
"sudo on %s.\n"), ctx->user.name, ctx->runas.shost);

plugins/sudoers/lookup.c

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -220,14 +220,6 @@ sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct sudoers_context *ctx,
220220
debug_return_uint(validated);
221221
}
222222

223-
static void
224-
init_cmnd_info(struct sudoers_context *ctx, struct cmnd_info *info)
225-
{
226-
memset(info, 0, sizeof(*info));
227-
if (def_intercept || ISSET(ctx->mode, MODE_POLICY_INTERCEPTED))
228-
info->intercepted = true;
229-
}
230-
231223
static int
232224
sudoers_lookup_check(struct sudo_nss *nss, struct sudoers_context *ctx,
233225
unsigned int *validated, struct cmnd_info *info, time_t now,
@@ -240,7 +232,7 @@ sudoers_lookup_check(struct sudo_nss *nss, struct sudoers_context *ctx,
240232
struct member *matching_user;
241233
debug_decl(sudoers_lookup_check, SUDOERS_DEBUG_PARSER);
242234

243-
init_cmnd_info(ctx, info);
235+
memset(info, 0, sizeof(*info));
244236

245237
TAILQ_FOREACH_REVERSE(us, &nss->parse_tree->userspecs, userspec_list, entries) {
246238
const int user_match = userlist_matches(nss->parse_tree, ctx->user.pw,
@@ -311,7 +303,7 @@ sudoers_lookup_check(struct sudo_nss *nss, struct sudoers_context *ctx,
311303
debug_return_int(cmnd_match);
312304
}
313305
free(info->cmnd_path);
314-
init_cmnd_info(ctx, info);
306+
memset(info, 0, sizeof(*info));
315307
}
316308
}
317309
}

plugins/sudoers/match_command.c

Lines changed: 18 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -134,25 +134,6 @@ do_stat(int fd, const char *path, struct stat *sb)
134134
}
135135
debug_return_bool(ret);
136136
}
137-
138-
/*
139-
* Perform intercept-specific checks.
140-
* Returns true if allowed, else false.
141-
*/
142-
static bool
143-
intercept_ok(const char *path, bool intercepted, struct stat *sb)
144-
{
145-
debug_decl(intercept_ok, SUDOERS_DEBUG_MATCH);
146-
147-
if (intercepted) {
148-
if (!def_intercept_allow_setid && ISSET(sb->st_mode, S_ISUID|S_ISGID)) {
149-
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
150-
"rejecting setid command %s", path);
151-
debug_return_bool(false);
152-
}
153-
}
154-
debug_return_bool(true);
155-
}
156137
#endif /* SUDOERS_NAME_MATCH */
157138

158139
/*
@@ -257,8 +238,7 @@ set_cmnd_fd(struct sudoers_context *ctx, int fd, int real_root)
257238
*/
258239
static int
259240
command_matches_dir(struct sudoers_context *ctx, const char *sudoers_dir,
260-
size_t dlen, int real_root, bool intercepted,
261-
const struct command_digest_list *digests)
241+
size_t dlen, int real_root, const struct command_digest_list *digests)
262242
{
263243
struct stat sudoers_stat;
264244
char path[PATH_MAX];
@@ -288,8 +268,6 @@ command_matches_dir(struct sudoers_context *ctx, const char *sudoers_dir,
288268
goto done;
289269
if (!do_stat(fd, path, &sudoers_stat))
290270
goto done;
291-
if (!intercept_ok(path, intercepted, &sudoers_stat))
292-
goto done;
293271

294272
if (ctx->user.cmnd_stat == NULL ||
295273
(ctx->user.cmnd_stat->st_dev == sudoers_stat.st_dev &&
@@ -317,8 +295,7 @@ command_matches_dir(struct sudoers_context *ctx, const char *sudoers_dir,
317295
*/
318296
static int
319297
command_matches_dir(struct sudoers_context *ctx, const char *sudoers_dir,
320-
size_t dlen, int real_root, bool intercepted,
321-
const struct command_digest_list *digests)
298+
size_t dlen, int real_root, const struct command_digest_list *digests)
322299
{
323300
int fd = -1;
324301
debug_decl(command_matches_dir, SUDOERS_DEBUG_MATCH);
@@ -348,7 +325,7 @@ command_matches_dir(struct sudoers_context *ctx, const char *sudoers_dir,
348325

349326
static int
350327
command_matches_all(struct sudoers_context *ctx, int real_root,
351-
bool intercepted, const struct command_digest_list *digests)
328+
const struct command_digest_list *digests)
352329
{
353330
#ifndef SUDOERS_NAME_MATCH
354331
struct stat sb;
@@ -367,8 +344,6 @@ command_matches_all(struct sudoers_context *ctx, int real_root,
367344
/* File exists but we couldn't open it above? */
368345
goto bad;
369346
}
370-
if (!intercept_ok(ctx->user.cmnd, intercepted, &sb))
371-
goto bad;
372347
}
373348
#else
374349
/* Open the file for fdexec or for digest matching. */
@@ -391,7 +366,7 @@ command_matches_all(struct sudoers_context *ctx, int real_root,
391366

392367
static int
393368
command_matches_fnmatch(struct sudoers_context *ctx, const char *sudoers_cmnd,
394-
const char *sudoers_args, int real_root, bool intercepted,
369+
const char *sudoers_args, int real_root,
395370
const struct command_digest_list *digests)
396371
{
397372
const char *cmnd = ctx->user.cmnd;
@@ -430,8 +405,6 @@ command_matches_fnmatch(struct sudoers_context *ctx, const char *sudoers_cmnd,
430405
#ifndef SUDOERS_NAME_MATCH
431406
if (!do_stat(fd, cmnd, &sb))
432407
goto bad;
433-
if (!intercept_ok(cmnd, intercepted, &sb))
434-
goto bad;
435408
#endif
436409
/* Check digest of cmnd since sudoers_cmnd is a pattern. */
437410
if (digest_matches(fd, cmnd, digests) != ALLOW)
@@ -449,7 +422,7 @@ command_matches_fnmatch(struct sudoers_context *ctx, const char *sudoers_cmnd,
449422

450423
static int
451424
command_matches_regex(struct sudoers_context *ctx, const char *sudoers_cmnd,
452-
const char *sudoers_args, int real_root, bool intercepted,
425+
const char *sudoers_args, int real_root,
453426
const struct command_digest_list *digests)
454427
{
455428
const char *cmnd = ctx->user.cmnd;
@@ -488,8 +461,6 @@ command_matches_regex(struct sudoers_context *ctx, const char *sudoers_cmnd,
488461
#ifndef SUDOERS_NAME_MATCH
489462
if (!do_stat(fd, cmnd, &sb))
490463
goto bad;
491-
if (!intercept_ok(cmnd, intercepted, &sb))
492-
goto bad;
493464
#endif
494465
/* Check digest of cmnd since sudoers_cmnd is a pattern. */
495466
if (digest_matches(fd, cmnd, digests) != ALLOW)
@@ -508,7 +479,7 @@ command_matches_regex(struct sudoers_context *ctx, const char *sudoers_cmnd,
508479
#ifndef SUDOERS_NAME_MATCH
509480
static int
510481
command_matches_glob(struct sudoers_context *ctx, const char *sudoers_cmnd,
511-
const char *sudoers_args, int real_root, bool intercepted,
482+
const char *sudoers_args, int real_root,
512483
const struct command_digest_list *digests)
513484
{
514485
struct stat sudoers_stat;
@@ -558,8 +529,6 @@ command_matches_glob(struct sudoers_context *ctx, const char *sudoers_cmnd,
558529
continue;
559530
if (!do_stat(fd, cp, &sudoers_stat))
560531
continue;
561-
if (!intercept_ok(cp, intercepted, &sudoers_stat))
562-
continue;
563532
if (ctx->user.cmnd_stat == NULL ||
564533
(ctx->user.cmnd_stat->st_dev == sudoers_stat.st_dev &&
565534
ctx->user.cmnd_stat->st_ino == sudoers_stat.st_ino)) {
@@ -592,8 +561,7 @@ command_matches_glob(struct sudoers_context *ctx, const char *sudoers_cmnd,
592561
/* If it ends in '/' it is a directory spec. */
593562
dlen = strlen(cp);
594563
if (cp[dlen - 1] == '/') {
595-
if (command_matches_dir(ctx, cp, dlen, real_root, intercepted,
596-
digests) == ALLOW) {
564+
if (command_matches_dir(ctx, cp, dlen, real_root, digests) == ALLOW) {
597565
globfree(&gl);
598566
debug_return_int(ALLOW);
599567
}
@@ -628,8 +596,6 @@ command_matches_glob(struct sudoers_context *ctx, const char *sudoers_cmnd,
628596
continue;
629597
if (!do_stat(fd, cp, &sudoers_stat))
630598
continue;
631-
if (!intercept_ok(cp, intercepted, &sudoers_stat))
632-
continue;
633599
if (ctx->user.cmnd_stat == NULL ||
634600
(ctx->user.cmnd_stat->st_dev == sudoers_stat.st_dev &&
635601
ctx->user.cmnd_stat->st_ino == sudoers_stat.st_ino)) {
@@ -661,7 +627,7 @@ command_matches_glob(struct sudoers_context *ctx, const char *sudoers_cmnd,
661627

662628
static int
663629
command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
664-
const char *sudoers_args, int real_root, bool intercepted,
630+
const char *sudoers_args, int real_root,
665631
const struct command_digest_list *digests)
666632
{
667633
struct stat sudoers_stat;
@@ -674,7 +640,7 @@ command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
674640
dlen = strlen(sudoers_cmnd);
675641
if (sudoers_cmnd[dlen - 1] == '/') {
676642
debug_return_int(command_matches_dir(ctx, sudoers_cmnd, dlen,
677-
real_root, intercepted, digests));
643+
real_root, digests));
678644
}
679645

680646
/* Only proceed if ctx->user.cmnd_base and basename(sudoers_cmnd) match */
@@ -716,8 +682,6 @@ command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
716682
* d) there is a digest and it matches
717683
*/
718684
if (ctx->user.cmnd_stat != NULL && do_stat(fd, sudoers_cmnd, &sudoers_stat)) {
719-
if (!intercept_ok(sudoers_cmnd, intercepted, &sudoers_stat))
720-
goto bad;
721685
if (ctx->user.cmnd_stat->st_dev != sudoers_stat.st_dev ||
722686
ctx->user.cmnd_stat->st_ino != sudoers_stat.st_ino)
723687
goto bad;
@@ -747,16 +711,16 @@ command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
747711
#else /* SUDOERS_NAME_MATCH */
748712
static int
749713
command_matches_glob(struct sudoers_context *ctx, const char *sudoers_cmnd,
750-
const char *sudoers_args, int real_root, bool intercepted,
714+
const char *sudoers_args, int real_root,
751715
const struct command_digest_list *digests)
752716
{
753717
return command_matches_fnmatch(ctx, sudoers_cmnd, sudoers_args, real_root,
754-
intercepted, digests);
718+
digests);
755719
}
756720

757721
static int
758722
command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
759-
const char *sudoers_args, int real_root, bool intercepted,
723+
const char *sudoers_args, int real_root,
760724
const struct command_digest_list *digests)
761725
{
762726
size_t dlen;
@@ -767,7 +731,7 @@ command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
767731
dlen = strlen(sudoers_cmnd);
768732
if (sudoers_cmnd[dlen - 1] == '/') {
769733
debug_return_int(command_matches_dir(ctx, sudoers_cmnd, dlen, real_root,
770-
intercepted, digests));
734+
digests));
771735
}
772736

773737
if (strcmp(ctx->user.cmnd, sudoers_cmnd) == 0) {
@@ -806,7 +770,6 @@ command_matches(struct sudoers_context *ctx, const char *sudoers_cmnd,
806770
const char *sudoers_args, const char *runchroot, struct cmnd_info *info,
807771
const struct command_digest_list *digests)
808772
{
809-
const bool intercepted = info ? info->intercepted : false;
810773
struct sudoers_pivot pivot_state = SUDOERS_PIVOT_INITIALIZER;
811774
char *saved_user_cmnd = NULL;
812775
struct stat saved_user_stat;
@@ -859,14 +822,14 @@ command_matches(struct sudoers_context *ctx, const char *sudoers_cmnd,
859822

860823
if (sudoers_cmnd == NULL) {
861824
sudoers_cmnd = "ALL";
862-
ret = command_matches_all(ctx, real_root, intercepted, digests);
825+
ret = command_matches_all(ctx, real_root, digests);
863826
goto done;
864827
}
865828

866829
/* Check for regular expressions first. */
867830
if (sudoers_cmnd[0] == '^') {
868831
ret = command_matches_regex(ctx, sudoers_cmnd, sudoers_args, real_root,
869-
intercepted, digests);
832+
digests);
870833
goto done;
871834
}
872835

@@ -896,14 +859,14 @@ command_matches(struct sudoers_context *ctx, const char *sudoers_cmnd,
896859
*/
897860
if (def_fast_glob) {
898861
ret = command_matches_fnmatch(ctx, sudoers_cmnd, sudoers_args,
899-
real_root, intercepted, digests);
862+
real_root, digests);
900863
} else {
901864
ret = command_matches_glob(ctx, sudoers_cmnd, sudoers_args,
902-
real_root, intercepted, digests);
865+
real_root, digests);
903866
}
904867
} else {
905868
ret = command_matches_normal(ctx, sudoers_cmnd, sudoers_args,
906-
real_root, intercepted, digests);
869+
real_root, digests);
907870
}
908871
done:
909872
/* Restore root. */

plugins/sudoers/parse.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,6 @@ struct cmnd_info {
339339
struct stat cmnd_stat;
340340
char *cmnd_path;
341341
int status;
342-
bool intercepted;
343342
};
344343

345344
/*

plugins/sudoers/sudoers.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,23 @@ sudoers_check_common(struct sudoers_context *ctx, int pwflag)
533533
goto bad;
534534
}
535535

536+
/*
537+
* Check if the user is trying to run a setid binary in intercept mode.
538+
* For the DSO intercept_type, we reject attempts to run setid binaries
539+
* by default since the dynamic loader will clear LD_PRELOAD, defeating
540+
* intercept.
541+
*/
542+
if (def_intercept || ISSET(ctx->mode, MODE_POLICY_INTERCEPTED)) {
543+
if (!def_intercept_allow_setid && ctx->user.cmnd_stat != NULL) {
544+
if (ISSET(ctx->user.cmnd_stat->st_mode, S_ISUID|S_ISGID)) {
545+
CLR(validated, VALIDATE_SUCCESS);
546+
if (!log_denial(ctx, validated|FLAG_INTERCEPT_SETID, true))
547+
goto done;
548+
goto bad;
549+
}
550+
}
551+
}
552+
536553
/* Create Ubuntu-style dot file to indicate sudo was successful. */
537554
if (create_admin_success_flag(ctx) == -1)
538555
goto done;

plugins/sudoers/sudoers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ struct sudoers_context {
233233
#define FLAG_NO_CHECK 0x080U
234234
#define FLAG_NO_USER_INPUT 0x100U
235235
#define FLAG_BAD_PASSWORD 0x200U
236+
#define FLAG_INTERCEPT_SETID 0x400U
236237

237238
/*
238239
* Return values for check_user() (rowhammer resistant).

0 commit comments

Comments
 (0)