Pass resource limits values to the plugin in user_info[]

Sudo resets the resource limits early in its execution so
the plugin cannot tell what the original limits were itself.
This commit is contained in:
Todd C. Miller
2020-08-31 16:37:01 -06:00
parent 84e6e6ccf9
commit c4a579cf8a
9 changed files with 275 additions and 23 deletions

5
NEWS
View File

@@ -44,6 +44,11 @@ What's new in Sudo 1.9.3
for "sudoers_policy" but "sudoers_audit" is not listed, those
arguments will be applied to "sudoers_audit" instead.
* The user's resource limits are now passed to sudo plugins in
the user_info[] list. A plugin cannot determine the limits
itself because sudo changes the limits while it runs to prevent
resource starvation.
What's new in Sudo 1.9.2
* Fixed package builds on RedHat Enterprise Linux 8.

View File

@@ -16,7 +16,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.TH "SUDO_PLUGIN" "5" "June 16, 2020" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
.TH "SUDO_PLUGIN" "5" "August 31, 2020" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
.nh
.if n .ad l
.SH "NAME"
@@ -513,6 +513,97 @@ The parent process ID of the running
process.
Only available starting with API version 1.2.
.TP 6n
rlimit_as=soft,hard
The maximum size to which the process's address space may grow (in bytes),
if supported by the operating system.
The soft and hard limits are separated by a comma.
A value of
\(lqinfinity\(rq
indicates that there is no limit.
Only available starting with API version 1.16.
.TP 6n
rlimit_core=soft,hard
The largest size core dump file that may be created (in bytes).
The soft and hard limits are separated by a comma.
A value of
\(lqinfinity\(rq
indicates that there is no limit.
Only available starting with API version 1.16.
.TP 6n
rlimit_cpu=soft,hard
The maximum amount of CPU time that the process may use (in seconds).
The soft and hard limits are separated by a comma.
A value of
\(lqinfinity\(rq
indicates that there is no limit.
Only available starting with API version 1.16.
.TP 6n
rlimit_data=soft,hard
The maximum size of the data segment for the process (in bytes).
The soft and hard limits are separated by a comma.
A value of
\(lqinfinity\(rq
indicates that there is no limit.
Only available starting with API version 1.16.
.TP 6n
rlimit_fsize=soft,hard
The largest size file that the process may create (in bytes).
The soft and hard limits are separated by a comma.
A value of
\(lqinfinity\(rq
indicates that there is no limit.
Only available starting with API version 1.16.
.TP 6n
rlimit_locks=soft,hard
The maximum number of locks that the process may establish,
if supported by the operating system.
The soft and hard limits are separated by a comma.
A value of
\(lqinfinity\(rq
indicates that there is no limit.
Only available starting with API version 1.16.
.TP 6n
rlimit_memlock=soft,hard
The maximum size that the process may lock in memory (in bytes),
if supported by the operating system.
The soft and hard limits are separated by a comma.
A value of
\(lqinfinity\(rq
indicates that there is no limit.
Only available starting with API version 1.16.
.TP 6n
rlimit_nofile=soft,hard
The maximum number of files that the process may have open.
The soft and hard limits are separated by a comma.
A value of
\(lqinfinity\(rq
indicates that there is no limit.
Only available starting with API version 1.16.
.TP 6n
rlimit_nproc=soft,hard
The maximum number of processes that the user may run simultaneously.
The soft and hard limits are separated by a comma.
A value of
\(lqinfinity\(rq
indicates that there is no limit.
Only available starting with API version 1.16.
.TP 6n
rlimit_rss=soft,hard
The maximum size to which the process's resident set size may grow (in bytes).
The soft and hard limits are separated by a comma.
A value of
\(lqinfinity\(rq
indicates that there is no limit.
Only available starting with API version 1.16.
.TP 6n
rlimit_stack=soft,hard
The maximum size to which the process's stack may grow (in bytes).
The soft and hard limits are separated by a comma.
A value of
\(lqinfinity\(rq
indicates that there is no limit.
Only available starting with API version 1.16.
.TP 6n
sid=int
The session ID of the running
\fBsudo\fR
@@ -4942,6 +5033,11 @@ command was not run.
has increased from 255 to 1023 bytes.
.sp
Support for audit and approval plugins was added.
.TP 6n
Version 1.16 (sudo 1.9.3)
Initial resource limit values were added to the
\fRuser_info\fR
list.
.SH "SEE ALSO"
sudo.conf(@mansectform@),
sudoers(@mansectform@),

View File

@@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd June 16, 2020
.Dd August 31, 2020
.Dt SUDO_PLUGIN @mansectform@
.Os Sudo @PACKAGE_VERSION@
.Sh NAME
@@ -456,6 +456,86 @@ The parent process ID of the running
.Nm sudo
process.
Only available starting with API version 1.2.
.It rlimit_as=soft,hard
The maximum size to which the process's address space may grow (in bytes),
if supported by the operating system.
The soft and hard limits are separated by a comma.
A value of
.Dq infinity
indicates that there is no limit.
Only available starting with API version 1.16.
.It rlimit_core=soft,hard
The largest size core dump file that may be created (in bytes).
The soft and hard limits are separated by a comma.
A value of
.Dq infinity
indicates that there is no limit.
Only available starting with API version 1.16.
.It rlimit_cpu=soft,hard
The maximum amount of CPU time that the process may use (in seconds).
The soft and hard limits are separated by a comma.
A value of
.Dq infinity
indicates that there is no limit.
Only available starting with API version 1.16.
.It rlimit_data=soft,hard
The maximum size of the data segment for the process (in bytes).
The soft and hard limits are separated by a comma.
A value of
.Dq infinity
indicates that there is no limit.
Only available starting with API version 1.16.
.It rlimit_fsize=soft,hard
The largest size file that the process may create (in bytes).
The soft and hard limits are separated by a comma.
A value of
.Dq infinity
indicates that there is no limit.
Only available starting with API version 1.16.
.It rlimit_locks=soft,hard
The maximum number of locks that the process may establish,
if supported by the operating system.
The soft and hard limits are separated by a comma.
A value of
.Dq infinity
indicates that there is no limit.
Only available starting with API version 1.16.
.It rlimit_memlock=soft,hard
The maximum size that the process may lock in memory (in bytes),
if supported by the operating system.
The soft and hard limits are separated by a comma.
A value of
.Dq infinity
indicates that there is no limit.
Only available starting with API version 1.16.
.It rlimit_nofile=soft,hard
The maximum number of files that the process may have open.
The soft and hard limits are separated by a comma.
A value of
.Dq infinity
indicates that there is no limit.
Only available starting with API version 1.16.
.It rlimit_nproc=soft,hard
The maximum number of processes that the user may run simultaneously.
The soft and hard limits are separated by a comma.
A value of
.Dq infinity
indicates that there is no limit.
Only available starting with API version 1.16.
.It rlimit_rss=soft,hard
The maximum size to which the process's resident set size may grow (in bytes).
The soft and hard limits are separated by a comma.
A value of
.Dq infinity
indicates that there is no limit.
Only available starting with API version 1.16.
.It rlimit_stack=soft,hard
The maximum size to which the process's stack may grow (in bytes).
The soft and hard limits are separated by a comma.
A value of
.Dq infinity
indicates that there is no limit.
Only available starting with API version 1.16.
.It sid=int
The session ID of the running
.Nm sudo
@@ -4367,6 +4447,10 @@ command was not run.
has increased from 255 to 1023 bytes.
.Pp
Support for audit and approval plugins was added.
.It Version 1.16 (sudo 1.9.3)
Initial resource limit values were added to the
.Li user_info
list.
.El
.Sh SEE ALSO
.Xr sudo.conf @mansectform@ ,

View File

@@ -21,7 +21,7 @@
/* API version major/minor */
#define SUDO_API_VERSION_MAJOR 1
#define SUDO_API_VERSION_MINOR 15
#define SUDO_API_VERSION_MINOR 16
#define SUDO_API_MKVERSION(x, y) (((x) << 16) | (y))
#define SUDO_API_VERSION SUDO_API_MKVERSION(SUDO_API_VERSION_MAJOR, SUDO_API_VERSION_MINOR)

View File

@@ -26,7 +26,7 @@
"INFO1=VALUE1",
"info2=value2"
\],
"version": "1.15"
"version": "1.16"
}
(APPROVAL 2) Constructed:
{
@@ -56,7 +56,7 @@
"INFO1=VALUE1",
"info2=value2"
\],
"version": "1.15"
"version": "1.16"
}
(APPROVAL 1) Show version was called with arguments: (0,)
Python approval plugin (API 1.0): ApprovalTestPlugin (loaded from 'SRC_DIR/regress/plugin_approval_test.py')

View File

@@ -309,7 +309,7 @@ sudo_terminated(struct command_status *cstat)
debug_return_bool(false);
}
#if SUDO_API_VERSION != SUDO_API_MKVERSION(1, 15)
#if SUDO_API_VERSION != SUDO_API_MKVERSION(1, 16)
# error "Update sudo_needs_pty() after changing the plugin API"
#endif
static bool

View File

@@ -24,6 +24,7 @@
#include <config.h>
#include <sys/resource.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef __linux__
@@ -55,6 +56,11 @@
# define RLIM_INFINITY RLIM64_INFINITY
#endif /* HAVE_SETRLIMIT64 */
/* Older BSD systems have RLIMIT_VMEM, not RLIMIT_AS. */
#if !defined(RLIMIT_AS) && defined(RLIMIT_VMEM)
# define RLIMIT_AS RLIMIT_VMEM
#endif
/*
* macOS doesn't allow nofile soft limit to be infinite or
* the stack hard limit to be infinite.
@@ -64,27 +70,35 @@ static struct rlimit nofile_fallback = { SUDO_OPEN_MAX, RLIM_INFINITY };
static struct rlimit stack_fallback = { SUDO_STACK_MIN, 65532 * 1024 };
static struct saved_limit {
const char *name;
int resource;
bool saved;
struct rlimit *fallback;
struct rlimit newlimit;
struct rlimit oldlimit;
const char *name; /* rlimit_foo in lower case */
int resource; /* RLIMIT_FOO definition */
bool override; /* override limit while sudo executes? */
bool saved; /* true if we were able to get the value */
struct rlimit *fallback; /* fallback if we fail to set to newlimit */
struct rlimit newlimit; /* new limit to use if override is true */
struct rlimit oldlimit; /* original limit, valid if saved is true */
} saved_limits[] = {
#ifdef RLIMIT_AS
{ "RLIMIT_AS", RLIMIT_AS, false, NULL, { RLIM_INFINITY, RLIM_INFINITY } },
{ "rlimit_as", RLIMIT_AS, true, false, NULL, { RLIM_INFINITY, RLIM_INFINITY } },
#endif
{ "RLIMIT_CPU", RLIMIT_CPU, false, NULL, { RLIM_INFINITY, RLIM_INFINITY } },
{ "RLIMIT_DATA", RLIMIT_DATA, false, NULL, { RLIM_INFINITY, RLIM_INFINITY } },
{ "RLIMIT_FSIZE", RLIMIT_FSIZE, false, NULL, { RLIM_INFINITY, RLIM_INFINITY } },
{ "RLIMIT_NOFILE", RLIMIT_NOFILE, false, &nofile_fallback, { RLIM_INFINITY, RLIM_INFINITY } },
{ "rlimit_core", RLIMIT_CORE, false },
{ "rlimit_cpu", RLIMIT_CPU, true, false, NULL, { RLIM_INFINITY, RLIM_INFINITY } },
{ "rlimit_data", RLIMIT_DATA, true, false, NULL, { RLIM_INFINITY, RLIM_INFINITY } },
{ "rlimit_fsize", RLIMIT_FSIZE, true, false, NULL, { RLIM_INFINITY, RLIM_INFINITY } },
#ifdef RLIMIT_LOCKS
{ "rlimit_locks", RLIMIT_LOCKS, false },
#endif
#ifdef RLIMIT_MEMLOCK
{ "rlimit_memlock", RLIMIT_MEMLOCK, false },
#endif
{ "rlimit_nofile", RLIMIT_NOFILE, true, false, &nofile_fallback, { RLIM_INFINITY, RLIM_INFINITY } },
#ifdef RLIMIT_NPROC
{ "RLIMIT_NPROC", RLIMIT_NPROC, false, NULL, { RLIM_INFINITY, RLIM_INFINITY } },
{ "rlimit_nproc", RLIMIT_NPROC, true, false, NULL, { RLIM_INFINITY, RLIM_INFINITY } },
#endif
#ifdef RLIMIT_RSS
{ "RLIMIT_RSS", RLIMIT_RSS, false, NULL, { RLIM_INFINITY, RLIM_INFINITY } },
{ "rlimit_rss", RLIMIT_RSS, true, false, NULL, { RLIM_INFINITY, RLIM_INFINITY } },
#endif
{ "RLIMIT_STACK", RLIMIT_STACK, false, &stack_fallback, { SUDO_STACK_MIN, RLIM_INFINITY } }
{ "rlimit_stack", RLIMIT_STACK, true, false, &stack_fallback, { SUDO_STACK_MIN, RLIM_INFINITY } }
};
static struct rlimit corelimit;
@@ -228,6 +242,9 @@ unlimit_sudo(void)
(long long)lim->oldlimit.rlim_max);
lim->saved = true;
if (!lim->override)
continue;
if (lim->newlimit.rlim_cur != RLIM_INFINITY) {
/* Don't reduce the soft resource limit. */
if (lim->oldlimit.rlim_cur == RLIM_INFINITY ||
@@ -284,7 +301,7 @@ restore_limits(void)
/* Restore resource limits to saved values. */
for (idx = 0; idx < nitems(saved_limits); idx++) {
struct saved_limit *lim = &saved_limits[idx];
if (lim->saved) {
if (lim->override && lim->saved) {
struct rlimit rl = lim->oldlimit;
int i, rc;
@@ -324,3 +341,45 @@ restore_limits(void)
debug_return;
}
int
serialize_limits(char **info, size_t info_max)
{
char *str;
unsigned int idx, nstored = 0;
debug_decl(serialize_limits, SUDO_DEBUG_UTIL);
for (idx = 0; idx < nitems(saved_limits); idx++) {
const struct saved_limit *lim = &saved_limits[idx];
const struct rlimit *rl = &lim->oldlimit;
char curlim[(((sizeof(int) * 8) + 2) / 3) + 2];
char maxlim[(((sizeof(int) * 8) + 2) / 3) + 2];
if (!lim->saved)
continue;
if (nstored == info_max)
goto oom;
if (rl->rlim_cur == RLIM_INFINITY) {
strlcpy(curlim, "infinity", sizeof(curlim));
} else {
snprintf(curlim, sizeof(curlim), "%llu",
(unsigned long long)rl->rlim_cur);
}
if (rl->rlim_max == RLIM_INFINITY) {
strlcpy(maxlim, "infinity", sizeof(maxlim));
} else {
snprintf(maxlim, sizeof(maxlim), "%llu",
(unsigned long long)rl->rlim_max);
}
if (asprintf(&str, "%s=%s,%s", lim->name, curlim, maxlim) == -1)
goto oom;
info[nstored++] = str;
}
debug_return_int(nstored);
oom:
while (nstored--)
free(info[nstored]);
debug_return_int(-1);
}

View File

@@ -30,6 +30,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <stddef.h>
#include <stdio.h>
@@ -490,10 +491,11 @@ static char **
get_user_info(struct user_details *ud)
{
char *cp, **user_info, path[PATH_MAX];
size_t user_info_max = 32 + RLIM_NLIMITS;
unsigned int i = 0;
mode_t mask;
struct passwd *pw;
int fd;
int fd, n;
debug_decl(get_user_info, SUDO_DEBUG_UTIL);
/*
@@ -512,7 +514,7 @@ get_user_info(struct user_details *ud)
memset(ud, 0, sizeof(*ud));
/* XXX - bound check number of entries */
user_info = reallocarray(NULL, 32, sizeof(char *));
user_info = reallocarray(NULL, user_info_max, sizeof(char *));
if (user_info == NULL)
goto oom;
@@ -614,6 +616,11 @@ get_user_info(struct user_details *ud)
if (asprintf(&user_info[++i], "cols=%d", ud->ts_cols) == -1)
goto oom;
n = serialize_limits(&user_info[i + 1], user_info_max - (i + 1));
if (n == -1)
goto oom;
i += n;
user_info[++i] = NULL;
/* Add to list of vectors to be garbage collected at exit. */

View File

@@ -293,5 +293,6 @@ void restore_limits(void);
void restore_nproc(void);
void unlimit_nproc(void);
void unlimit_sudo(void);
int serialize_limits(char **info, size_t info_max);
#endif /* SUDO_SUDO_H */