|
|
@@ -204,7 +204,7 @@ open_cmnd(const char *path, const struct command_digest_list *digests, int *fdp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
static void
|
|
|
|
set_cmnd_fd(struct sudoers_context *ctx, int fd, int rootfd)
|
|
|
|
set_cmnd_fd(struct sudoers_context *ctx, int fd, int real_root)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
debug_decl(set_cmnd_fd, SUDOERS_DEBUG_MATCH);
|
|
|
|
debug_decl(set_cmnd_fd, SUDOERS_DEBUG_MATCH);
|
|
|
|
|
|
|
|
|
|
|
@@ -222,10 +222,10 @@ set_cmnd_fd(struct sudoers_context *ctx, int fd, int rootfd)
|
|
|
|
int error, flags;
|
|
|
|
int error, flags;
|
|
|
|
|
|
|
|
|
|
|
|
/* We can only use fexecve() on a script if /dev/fd/N exists. */
|
|
|
|
/* We can only use fexecve() on a script if /dev/fd/N exists. */
|
|
|
|
if (rootfd != -1) {
|
|
|
|
if (real_root != -1) {
|
|
|
|
/* Path relative to old root directory. */
|
|
|
|
/* Path relative to old root directory. */
|
|
|
|
(void)snprintf(fdpath, sizeof(fdpath), "dev/fd/%d", fd);
|
|
|
|
(void)snprintf(fdpath, sizeof(fdpath), "dev/fd/%d", fd);
|
|
|
|
error = fstatat(rootfd, fdpath, &sb, 0);
|
|
|
|
error = fstatat(real_root, fdpath, &sb, 0);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
/* Absolute path. */
|
|
|
|
/* Absolute path. */
|
|
|
|
(void)snprintf(fdpath, sizeof(fdpath), "/dev/fd/%d", fd);
|
|
|
|
(void)snprintf(fdpath, sizeof(fdpath), "/dev/fd/%d", fd);
|
|
|
@@ -257,7 +257,8 @@ set_cmnd_fd(struct sudoers_context *ctx, int fd, int rootfd)
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
command_matches_dir(struct sudoers_context *ctx, const char *sudoers_dir,
|
|
|
|
command_matches_dir(struct sudoers_context *ctx, const char *sudoers_dir,
|
|
|
|
size_t dlen, int rootfd, bool intercepted, const struct command_digest_list *digests)
|
|
|
|
size_t dlen, int real_root, bool intercepted,
|
|
|
|
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct stat sudoers_stat;
|
|
|
|
struct stat sudoers_stat;
|
|
|
|
char path[PATH_MAX];
|
|
|
|
char path[PATH_MAX];
|
|
|
@@ -316,7 +317,7 @@ done:
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
command_matches_dir(struct sudoers_context *ctx, const char *sudoers_dir,
|
|
|
|
command_matches_dir(struct sudoers_context *ctx, const char *sudoers_dir,
|
|
|
|
size_t dlen, int rootfd, bool intercepted,
|
|
|
|
size_t dlen, int real_root, bool intercepted,
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
int fd = -1;
|
|
|
|
int fd = -1;
|
|
|
@@ -335,7 +336,7 @@ command_matches_dir(struct sudoers_context *ctx, const char *sudoers_dir,
|
|
|
|
goto bad;
|
|
|
|
goto bad;
|
|
|
|
if (digest_matches(fd, ctx->user.cmnd, digests) != ALLOW)
|
|
|
|
if (digest_matches(fd, ctx->user.cmnd, digests) != ALLOW)
|
|
|
|
goto bad;
|
|
|
|
goto bad;
|
|
|
|
set_cmnd_fd(ctx, fd, rootfd);
|
|
|
|
set_cmnd_fd(ctx, fd, real_root);
|
|
|
|
|
|
|
|
|
|
|
|
debug_return_int(ALLOW);
|
|
|
|
debug_return_int(ALLOW);
|
|
|
|
bad:
|
|
|
|
bad:
|
|
|
@@ -346,8 +347,8 @@ bad:
|
|
|
|
#endif /* SUDOERS_NAME_MATCH */
|
|
|
|
#endif /* SUDOERS_NAME_MATCH */
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
command_matches_all(struct sudoers_context *ctx, int rootfd, bool intercepted,
|
|
|
|
command_matches_all(struct sudoers_context *ctx, int real_root,
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
bool intercepted, const struct command_digest_list *digests)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
#ifndef SUDOERS_NAME_MATCH
|
|
|
|
#ifndef SUDOERS_NAME_MATCH
|
|
|
|
struct stat sb;
|
|
|
|
struct stat sb;
|
|
|
@@ -378,7 +379,7 @@ command_matches_all(struct sudoers_context *ctx, int rootfd, bool intercepted,
|
|
|
|
/* Check digest of ctx->user.cmnd since we have no sudoers_cmnd for ALL. */
|
|
|
|
/* Check digest of ctx->user.cmnd since we have no sudoers_cmnd for ALL. */
|
|
|
|
if (digest_matches(fd, ctx->user.cmnd, digests) != ALLOW)
|
|
|
|
if (digest_matches(fd, ctx->user.cmnd, digests) != ALLOW)
|
|
|
|
goto bad;
|
|
|
|
goto bad;
|
|
|
|
set_cmnd_fd(ctx, fd, rootfd);
|
|
|
|
set_cmnd_fd(ctx, fd, real_root);
|
|
|
|
|
|
|
|
|
|
|
|
/* No need to set ctx->runas.cmnd for ALL. */
|
|
|
|
/* No need to set ctx->runas.cmnd for ALL. */
|
|
|
|
debug_return_int(ALLOW);
|
|
|
|
debug_return_int(ALLOW);
|
|
|
@@ -390,7 +391,7 @@ bad:
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
command_matches_fnmatch(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
command_matches_fnmatch(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
const char *sudoers_args, int rootfd, bool intercepted,
|
|
|
|
const char *sudoers_args, int real_root, bool intercepted,
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const char *cmnd = ctx->user.cmnd;
|
|
|
|
const char *cmnd = ctx->user.cmnd;
|
|
|
@@ -435,7 +436,7 @@ command_matches_fnmatch(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
/* Check digest of cmnd since sudoers_cmnd is a pattern. */
|
|
|
|
/* Check digest of cmnd since sudoers_cmnd is a pattern. */
|
|
|
|
if (digest_matches(fd, cmnd, digests) != ALLOW)
|
|
|
|
if (digest_matches(fd, cmnd, digests) != ALLOW)
|
|
|
|
goto bad;
|
|
|
|
goto bad;
|
|
|
|
set_cmnd_fd(ctx, fd, rootfd);
|
|
|
|
set_cmnd_fd(ctx, fd, real_root);
|
|
|
|
|
|
|
|
|
|
|
|
/* No need to set ctx->runas.cmnd since cmnd matches sudoers_cmnd */
|
|
|
|
/* No need to set ctx->runas.cmnd since cmnd matches sudoers_cmnd */
|
|
|
|
debug_return_int(ALLOW);
|
|
|
|
debug_return_int(ALLOW);
|
|
|
@@ -448,7 +449,7 @@ bad:
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
command_matches_regex(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
command_matches_regex(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
const char *sudoers_args, int rootfd, bool intercepted,
|
|
|
|
const char *sudoers_args, int real_root, bool intercepted,
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const char *cmnd = ctx->user.cmnd;
|
|
|
|
const char *cmnd = ctx->user.cmnd;
|
|
|
@@ -493,7 +494,7 @@ command_matches_regex(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
/* Check digest of cmnd since sudoers_cmnd is a pattern. */
|
|
|
|
/* Check digest of cmnd since sudoers_cmnd is a pattern. */
|
|
|
|
if (digest_matches(fd, cmnd, digests) != ALLOW)
|
|
|
|
if (digest_matches(fd, cmnd, digests) != ALLOW)
|
|
|
|
goto bad;
|
|
|
|
goto bad;
|
|
|
|
set_cmnd_fd(ctx, fd, rootfd);
|
|
|
|
set_cmnd_fd(ctx, fd, real_root);
|
|
|
|
|
|
|
|
|
|
|
|
/* No need to set ctx->runas.cmnd since cmnd matches sudoers_cmnd */
|
|
|
|
/* No need to set ctx->runas.cmnd since cmnd matches sudoers_cmnd */
|
|
|
|
debug_return_int(ALLOW);
|
|
|
|
debug_return_int(ALLOW);
|
|
|
@@ -507,7 +508,7 @@ bad:
|
|
|
|
#ifndef SUDOERS_NAME_MATCH
|
|
|
|
#ifndef SUDOERS_NAME_MATCH
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
command_matches_glob(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
command_matches_glob(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
const char *sudoers_args, int rootfd, bool intercepted,
|
|
|
|
const char *sudoers_args, int real_root, bool intercepted,
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct stat sudoers_stat;
|
|
|
|
struct stat sudoers_stat;
|
|
|
@@ -591,7 +592,7 @@ command_matches_glob(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
/* If it ends in '/' it is a directory spec. */
|
|
|
|
/* If it ends in '/' it is a directory spec. */
|
|
|
|
dlen = strlen(cp);
|
|
|
|
dlen = strlen(cp);
|
|
|
|
if (cp[dlen - 1] == '/') {
|
|
|
|
if (cp[dlen - 1] == '/') {
|
|
|
|
if (command_matches_dir(ctx, cp, dlen, rootfd, intercepted,
|
|
|
|
if (command_matches_dir(ctx, cp, dlen, real_root, intercepted,
|
|
|
|
digests) == ALLOW) {
|
|
|
|
digests) == ALLOW) {
|
|
|
|
globfree(&gl);
|
|
|
|
globfree(&gl);
|
|
|
|
debug_return_int(ALLOW);
|
|
|
|
debug_return_int(ALLOW);
|
|
|
@@ -649,7 +650,7 @@ done:
|
|
|
|
if (cp != NULL) {
|
|
|
|
if (cp != NULL) {
|
|
|
|
if (command_args_match(ctx, sudoers_cmnd, sudoers_args) == ALLOW) {
|
|
|
|
if (command_args_match(ctx, sudoers_cmnd, sudoers_args) == ALLOW) {
|
|
|
|
/* ctx->runas.cmnd was set above. */
|
|
|
|
/* ctx->runas.cmnd was set above. */
|
|
|
|
set_cmnd_fd(ctx, fd, rootfd);
|
|
|
|
set_cmnd_fd(ctx, fd, real_root);
|
|
|
|
debug_return_int(ALLOW);
|
|
|
|
debug_return_int(ALLOW);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -660,7 +661,7 @@ done:
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
const char *sudoers_args, int rootfd, bool intercepted,
|
|
|
|
const char *sudoers_args, int real_root, bool intercepted,
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct stat sudoers_stat;
|
|
|
|
struct stat sudoers_stat;
|
|
|
@@ -672,8 +673,8 @@ command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
/* If it ends in '/' it is a directory spec. */
|
|
|
|
/* If it ends in '/' it is a directory spec. */
|
|
|
|
dlen = strlen(sudoers_cmnd);
|
|
|
|
dlen = strlen(sudoers_cmnd);
|
|
|
|
if (sudoers_cmnd[dlen - 1] == '/') {
|
|
|
|
if (sudoers_cmnd[dlen - 1] == '/') {
|
|
|
|
debug_return_int(command_matches_dir(ctx, sudoers_cmnd, dlen, rootfd,
|
|
|
|
debug_return_int(command_matches_dir(ctx, sudoers_cmnd, dlen,
|
|
|
|
intercepted, digests));
|
|
|
|
real_root, intercepted, digests));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Only proceed if ctx->user.cmnd_base and basename(sudoers_cmnd) match */
|
|
|
|
/* Only proceed if ctx->user.cmnd_base and basename(sudoers_cmnd) match */
|
|
|
@@ -736,7 +737,7 @@ command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
|
|
|
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
|
|
|
goto bad;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
set_cmnd_fd(ctx, fd, rootfd);
|
|
|
|
set_cmnd_fd(ctx, fd, real_root);
|
|
|
|
debug_return_int(ALLOW);
|
|
|
|
debug_return_int(ALLOW);
|
|
|
|
bad:
|
|
|
|
bad:
|
|
|
|
if (fd != -1)
|
|
|
|
if (fd != -1)
|
|
|
@@ -746,16 +747,16 @@ bad:
|
|
|
|
#else /* SUDOERS_NAME_MATCH */
|
|
|
|
#else /* SUDOERS_NAME_MATCH */
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
command_matches_glob(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
command_matches_glob(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
const char *sudoers_args, int rootfd, bool intercepted,
|
|
|
|
const char *sudoers_args, int real_root, bool intercepted,
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return command_matches_fnmatch(ctx, sudoers_cmnd, sudoers_args, rootfd,
|
|
|
|
return command_matches_fnmatch(ctx, sudoers_cmnd, sudoers_args, real_root,
|
|
|
|
intercepted, digests);
|
|
|
|
intercepted, digests);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
const char *sudoers_args, int rootfd, bool intercepted,
|
|
|
|
const char *sudoers_args, int real_root, bool intercepted,
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
const struct command_digest_list *digests)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
size_t dlen;
|
|
|
|
size_t dlen;
|
|
|
@@ -765,7 +766,7 @@ command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
/* If it ends in '/' it is a directory spec. */
|
|
|
|
/* If it ends in '/' it is a directory spec. */
|
|
|
|
dlen = strlen(sudoers_cmnd);
|
|
|
|
dlen = strlen(sudoers_cmnd);
|
|
|
|
if (sudoers_cmnd[dlen - 1] == '/') {
|
|
|
|
if (sudoers_cmnd[dlen - 1] == '/') {
|
|
|
|
debug_return_int(command_matches_dir(ctx, sudoers_cmnd, dlen, rootfd,
|
|
|
|
debug_return_int(command_matches_dir(ctx, sudoers_cmnd, dlen, real_root,
|
|
|
|
intercepted, digests));
|
|
|
|
intercepted, digests));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -784,7 +785,7 @@ command_matches_normal(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
U_("unable to allocate memory"));
|
|
|
|
U_("unable to allocate memory"));
|
|
|
|
goto bad;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
set_cmnd_fd(ctx, fd, rootfd);
|
|
|
|
set_cmnd_fd(ctx, fd, real_root);
|
|
|
|
debug_return_int(ALLOW);
|
|
|
|
debug_return_int(ALLOW);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -810,6 +811,7 @@ command_matches(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
char *saved_user_cmnd = NULL;
|
|
|
|
char *saved_user_cmnd = NULL;
|
|
|
|
struct stat saved_user_stat;
|
|
|
|
struct stat saved_user_stat;
|
|
|
|
bool reset_cmnd = false;
|
|
|
|
bool reset_cmnd = false;
|
|
|
|
|
|
|
|
int real_root = -1;
|
|
|
|
int ret = DENY;
|
|
|
|
int ret = DENY;
|
|
|
|
debug_decl(command_matches, SUDOERS_DEBUG_MATCH);
|
|
|
|
debug_decl(command_matches, SUDOERS_DEBUG_MATCH);
|
|
|
|
|
|
|
|
|
|
|
@@ -834,6 +836,7 @@ command_matches(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
if (runchroot != NULL) {
|
|
|
|
if (runchroot != NULL) {
|
|
|
|
if (!pivot_root(runchroot, &pivot_state))
|
|
|
|
if (!pivot_root(runchroot, &pivot_state))
|
|
|
|
goto done;
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
real_root = pivot_state.saved_root;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (reset_cmnd) {
|
|
|
|
if (reset_cmnd) {
|
|
|
@@ -856,15 +859,14 @@ command_matches(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
|
|
|
|
|
|
|
|
if (sudoers_cmnd == NULL) {
|
|
|
|
if (sudoers_cmnd == NULL) {
|
|
|
|
sudoers_cmnd = "ALL";
|
|
|
|
sudoers_cmnd = "ALL";
|
|
|
|
ret = command_matches_all(ctx, pivot_get_root(&pivot_state),
|
|
|
|
ret = command_matches_all(ctx, real_root, intercepted, digests);
|
|
|
|
intercepted, digests);
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Check for regular expressions first. */
|
|
|
|
/* Check for regular expressions first. */
|
|
|
|
if (sudoers_cmnd[0] == '^') {
|
|
|
|
if (sudoers_cmnd[0] == '^') {
|
|
|
|
ret = command_matches_regex(ctx, sudoers_cmnd, sudoers_args,
|
|
|
|
ret = command_matches_regex(ctx, sudoers_cmnd, sudoers_args, real_root,
|
|
|
|
pivot_get_root(&pivot_state), intercepted, digests);
|
|
|
|
intercepted, digests);
|
|
|
|
goto done;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -894,14 +896,14 @@ command_matches(struct sudoers_context *ctx, const char *sudoers_cmnd,
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
if (def_fast_glob) {
|
|
|
|
if (def_fast_glob) {
|
|
|
|
ret = command_matches_fnmatch(ctx, sudoers_cmnd, sudoers_args,
|
|
|
|
ret = command_matches_fnmatch(ctx, sudoers_cmnd, sudoers_args,
|
|
|
|
pivot_get_root(&pivot_state), intercepted, digests);
|
|
|
|
real_root, intercepted, digests);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
ret = command_matches_glob(ctx, sudoers_cmnd, sudoers_args,
|
|
|
|
ret = command_matches_glob(ctx, sudoers_cmnd, sudoers_args,
|
|
|
|
pivot_get_root(&pivot_state), intercepted, digests);
|
|
|
|
real_root, intercepted, digests);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
ret = command_matches_normal(ctx, sudoers_cmnd, sudoers_args,
|
|
|
|
ret = command_matches_normal(ctx, sudoers_cmnd, sudoers_args,
|
|
|
|
pivot_get_root(&pivot_state), intercepted, digests);
|
|
|
|
real_root, intercepted, digests);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
done:
|
|
|
|
/* Restore root. */
|
|
|
|
/* Restore root. */
|
|
|
|