Skip to content

Commit fffcc07

Browse files
committed
Revert pivot_root and go back to prepending the new root directory.
We cannot perform passwd/group lookups _after_ changing the root directory. This does mean that symbolic links in a path are not currently handled properly when matching chroot()ed commands. Fixes a local privilege escalation vulnerability where a user could craft their own nsswitch.conf file to load a shared library of their choosing and run arbitrary code. CVE-2025-32463 Reported by Rich Mirch @ Stratascale Cyber Research Unit (CRU).
1 parent f8ff956 commit fffcc07

File tree

18 files changed

+641
-822
lines changed

18 files changed

+641
-822
lines changed

MANIFEST

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -687,8 +687,6 @@ plugins/sudoers/mkdefaults
687687
plugins/sudoers/parse.h
688688
plugins/sudoers/parse_ldif.c
689689
plugins/sudoers/parser_warnx.c
690-
plugins/sudoers/pivot.c
691-
plugins/sudoers/pivot.h
692690
plugins/sudoers/po/README
693691
plugins/sudoers/po/ast.mo
694692
plugins/sudoers/po/ast.po

plugins/sudoers/Makefile.in

Lines changed: 461 additions & 537 deletions
Large diffs are not rendered by default.

plugins/sudoers/editor.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ resolve_editor(const char *ed, size_t edlen, int nfiles, char * const *files,
147147
goto oom;
148148

149149
/* If we can't find the editor in the user's PATH, give up. */
150-
if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"),
150+
if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"), NULL,
151151
false, allowlist) != FOUND) {
152152
errno = ENOENT;
153153
goto bad;

plugins/sudoers/find_path.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@
4343
* On failure, returns false.
4444
*/
4545
static bool
46-
cmnd_allowed(char *cmnd, size_t cmnd_size, struct stat *cmnd_sbp,
47-
char * const *allowlist)
46+
cmnd_allowed(char *cmnd, size_t cmnd_size, const char *runchroot,
47+
struct stat *cmnd_sbp, char * const *allowlist)
4848
{
4949
const char *cmnd_base;
5050
char * const *al;
5151
debug_decl(cmnd_allowed, SUDOERS_DEBUG_UTIL);
5252

53-
if (!sudo_goodpath(cmnd, cmnd_sbp))
53+
if (!sudo_goodpath(cmnd, runchroot, cmnd_sbp))
5454
debug_return_bool(false);
5555

5656
if (allowlist == NULL)
@@ -67,7 +67,7 @@ cmnd_allowed(char *cmnd, size_t cmnd_size, struct stat *cmnd_sbp,
6767
if (strcmp(cmnd_base, base) != 0)
6868
continue;
6969

70-
if (sudo_goodpath(path, &sb) &&
70+
if (sudo_goodpath(path, runchroot, &sb) &&
7171
sb.st_dev == cmnd_sbp->st_dev && sb.st_ino == cmnd_sbp->st_ino) {
7272
/* Overwrite cmnd with safe version from allowlist. */
7373
if (strlcpy(cmnd, path, cmnd_size) < cmnd_size)
@@ -87,7 +87,8 @@ cmnd_allowed(char *cmnd, size_t cmnd_size, struct stat *cmnd_sbp,
8787
*/
8888
int
8989
find_path(const char *infile, char **outfile, struct stat *sbp,
90-
const char *path, bool ignore_dot, char * const *allowlist)
90+
const char *path, const char *runchroot, bool ignore_dot,
91+
char * const *allowlist)
9192
{
9293
char command[PATH_MAX];
9394
const char *cp, *ep, *pathend;
@@ -108,7 +109,8 @@ find_path(const char *infile, char **outfile, struct stat *sbp,
108109
errno = ENAMETOOLONG;
109110
debug_return_int(NOT_FOUND_ERROR);
110111
}
111-
found = cmnd_allowed(command, sizeof(command), sbp, allowlist);
112+
found = cmnd_allowed(command, sizeof(command), runchroot, sbp,
113+
allowlist);
112114
goto done;
113115
}
114116

@@ -137,7 +139,8 @@ find_path(const char *infile, char **outfile, struct stat *sbp,
137139
errno = ENAMETOOLONG;
138140
debug_return_int(NOT_FOUND_ERROR);
139141
}
140-
found = cmnd_allowed(command, sizeof(command), sbp, allowlist);
142+
found = cmnd_allowed(command, sizeof(command), runchroot,
143+
sbp, allowlist);
141144
if (found)
142145
break;
143146
}
@@ -151,7 +154,8 @@ find_path(const char *infile, char **outfile, struct stat *sbp,
151154
errno = ENAMETOOLONG;
152155
debug_return_int(NOT_FOUND_ERROR);
153156
}
154-
found = cmnd_allowed(command, sizeof(command), sbp, allowlist);
157+
found = cmnd_allowed(command, sizeof(command), runchroot,
158+
sbp, allowlist);
155159
if (found && ignore_dot)
156160
debug_return_int(NOT_FOUND_DOT);
157161
}

plugins/sudoers/goodpath.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,25 @@
3939
* Verify that path is a normal file and executable by root.
4040
*/
4141
bool
42-
sudo_goodpath(const char *path, struct stat *sbp)
42+
sudo_goodpath(const char *path, const char *runchroot, struct stat *sbp)
4343
{
4444
bool ret = false;
45-
struct stat sb;
4645
debug_decl(sudo_goodpath, SUDOERS_DEBUG_UTIL);
4746

4847
if (path != NULL) {
48+
char pathbuf[PATH_MAX];
49+
struct stat sb;
50+
51+
if (runchroot != NULL) {
52+
/* XXX - handle symlinks and '..' in path outside chroot */
53+
const int len =
54+
snprintf(pathbuf, sizeof(pathbuf), "%s%s", runchroot, path);
55+
if (len >= ssizeof(pathbuf)) {
56+
errno = ENAMETOOLONG;
57+
goto done;
58+
}
59+
path = pathbuf; // -V507
60+
}
4961
if (sbp == NULL)
5062
sbp = &sb;
5163

@@ -57,5 +69,6 @@ sudo_goodpath(const char *path, struct stat *sbp)
5769
errno = EACCES;
5870
}
5971
}
72+
done:
6073
debug_return_bool(ret);
6174
}

0 commit comments

Comments
 (0)