Support "*" for CWD/CHROOT to allow user to specify cwd or chroot.

Adds two new command line options, -D (--chdir) and -R (--chroot)
that can only be used when sudoers sets runcwd or runchroot to "*".
This commit is contained in:
Todd C. Miller
2020-09-01 14:10:02 -06:00
parent bd254e1042
commit 1676f0ceeb
17 changed files with 397 additions and 128 deletions

View File

@@ -25,7 +25,7 @@
.nr BA @BAMAN@
.nr LC @LCMAN@
.nr PS @PSMAN@
.TH "SUDO" "@mansectsu@" "August 11, 2020" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
.TH "SUDO" "@mansectsu@" "September 1, 2020" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
.nh
.if n .ad l
.SH "NAME"
@@ -66,9 +66,11 @@
.if \n(BA [\fB\-a\fR\ \fItype\fR]
[\fB\-C\fR\ \fInum\fR]
.if \n(LC [\fB\-c\fR\ \fIclass\fR]
[\fB\-D\fR\ \fIdirectory\fR]
[\fB\-g\fR\ \fIgroup\fR]
[\fB\-h\fR\ \fIhost\fR]
[\fB\-p\fR\ \fIprompt\fR]
[\fB\-R\fR\ \fIdirectory\fR]
.if \n(SL [\fB\-r\fR\ \fIrole\fR]
.if \n(SL [\fB\-t\fR\ \fItype\fR]
[\fB\-T\fR\ \fItimeout\fR]
@@ -83,9 +85,13 @@
.if \n(BA [\fB\-a\fR\ \fItype\fR]
[\fB\-C\fR\ \fInum\fR]
.if \n(LC [\fB\-c\fR\ \fIclass\fR]
[\fB\-D\fR\ \fIdirectory\fR]
[\fB\-g\fR\ \fIgroup\fR]
[\fB\-h\fR\ \fIhost\fR]
[\fB\-p\fR\ \fIprompt\fR]
[\fB\-R\fR\ \fIdirectory\fR]
.if \n(SL [\fB\-r\fR\ \fIrole\fR]
.if \n(SL [\fB\-t\fR\ \fItype\fR]
[\fB\-T\fR\ \fItimeout\fR]
[\fB\-u\fR\ \fIuser\fR]
\fIfile\ ...\fR
@@ -288,6 +294,13 @@ BSD
login classes.
.\}
.TP 12n
\fB\-D\fR \fIdirectory\fR, \fB\--chdir\fR=\fIdirectory\fR
Run the command in the specified
\fIdirectory\fR
instead of the current working directory.
The security policy may return an error if the user does not have
permission to specify the working directory.
.TP 12n
\fB\-E\fR, \fB\--preserve-env\fR
Indicates to the security policy that the user wishes to
preserve their existing environment variables.
@@ -584,6 +597,15 @@ specified by a PAM module unless the
flag is disabled in
\fIsudoers\fR.
.RE
.TP 12n
\fB\-R\fR \fIdirectory\fR, \fB\--chroot\fR=\fIdirectory\fR
Change to the specified root
\fIdirectory\fR
(see
chroot(@mansectsu@))
before running the command.
The security policy may return an error if the user does not have
permission to specify the root directory.
.if \n(SL \{\
.TP 12n
\fB\-r\fR \fIrole\fR, \fB\--role\fR=\fIrole\fR

View File

@@ -24,7 +24,7 @@
.nr BA @BAMAN@
.nr LC @LCMAN@
.nr PS @PSMAN@
.Dd August 11, 2020
.Dd September 1, 2020
.Dt SUDO @mansectsu@
.Os Sudo @PACKAGE_VERSION@
.Sh NAME
@@ -65,9 +65,11 @@
.if \n(LC \{\
.Op Fl c Ar class
.\}
.Op Fl D Ar directory
.Op Fl g Ar group
.Op Fl h Ar host
.Op Fl p Ar prompt
.Op Fl R Ar directory
.if \n(SL \{\
.Op Fl r Ar role
.Op Fl t Ar type
@@ -86,9 +88,15 @@
.if \n(LC \{\
.Op Fl c Ar class
.\}
.Op Fl D Ar directory
.Op Fl g Ar group
.Op Fl h Ar host
.Op Fl p Ar prompt
.Op Fl R Ar directory
.if \n(SL \{\
.Op Fl r Ar role
.Op Fl t Ar type
.\}
.Op Fl T Ar timeout
.Op Fl u Ar user
.Ar
@@ -279,6 +287,12 @@ This option is only available on systems with
.Bx
login classes.
.\}
.It Fl D Ar directory , Fl -chdir Ns = Ns Ar directory
Run the command in the specified
.Ar directory
instead of the current working directory.
The security policy may return an error if the user does not have
permission to specify the working directory.
.It Fl E , -preserve-env
Indicates to the security policy that the user wishes to
preserve their existing environment variables.
@@ -545,6 +559,14 @@ specified by a PAM module unless the
.Em passprompt_override
flag is disabled in
.Em sudoers .
.It Fl R Ar directory , Fl -chroot Ns = Ns Ar directory
Change to the specified root
.Ar directory
(see
.Xr chroot @mansectsu@ )
before running the command.
The security policy may return an error if the user does not have
permission to specify the root directory.
.if \n(SL \{\
.It Fl r Ar role , Fl -role Ns = Ns Ar role
Run the command with an SELinux security context that includes

View File

@@ -215,6 +215,24 @@ The plugin may optionally pass this, or another value, back in the
\fIcommand_info\fR
list.
.TP 6n
cmnd_chroot=string
The root directory (see
chroot(2))
to run the command in, as specified by the user via the
\fB\-R\fR
option.
The plugin may ignore or restrict the user's ability to specify a new
root directory.
Only available starting with API version 1.16.
.TP 6n
cmnd_cwd=string
The working directory to run the command in, as specified by the user via the
\fB\-D\fR
option.
The plugin may ignore or restrict the user's ability to specify a new
working directory.
Only available starting with API version 1.16.
.TP 6n
debug_flags=string
A debug file path name followed by a space and a comma-separated
list of debug flags that correspond to the plugin's
@@ -250,10 +268,6 @@ contains a plugin-specific
\fRDebug\fR
entry.
.TP 6n
debug_level=number
This setting has been deprecated in favor of
\fIdebug_flags\fR.
.TP 6n
ignore_ticket=bool
Set to true if the user specified the
\fB\-k\fR
@@ -425,8 +439,10 @@ For more information, see the
section.
.TP 6n
timeout=string
User-specified command timeout.
Not all plugins support command timeouts and the ability for the
Command timeout specified by the user via the
\fB\-T\fR
option.
Not all plugins support command timeouts and the ability of the
user to set a timeout may be restricted by policy.
The format of the timeout string is plugin-specific.
.PP
@@ -5038,6 +5054,14 @@ Version 1.16 (sudo 1.9.3)
Initial resource limit values were added to the
\fRuser_info\fR
list.
.sp
The
\fIcmnd_chroot\fR
and
\fIcmnd_cwd\fR
enties were added to the
\fRsettings\fR
list.
.SH "SEE ALSO"
sudo.conf(@mansectform@),
sudoers(@mansectform@),

View File

@@ -197,6 +197,22 @@ or higher.
The plugin may optionally pass this, or another value, back in the
.Em command_info
list.
.It cmnd_chroot=string
The root directory (see
.Xr chroot 2 )
to run the command in, as specified by the user via the
.Fl R
option.
The plugin may ignore or restrict the user's ability to specify a new
root directory.
Only available starting with API version 1.16.
.It cmnd_cwd=string
The working directory to run the command in, as specified by the user via the
.Fl D
option.
The plugin may ignore or restrict the user's ability to specify a new
working directory.
Only available starting with API version 1.16.
.It debug_flags=string
A debug file path name followed by a space and a comma-separated
list of debug flags that correspond to the plugin's
@@ -231,9 +247,6 @@ if
contains a plugin-specific
.Li Debug
entry.
.It debug_level=number
This setting has been deprecated in favor of
.Em debug_flags .
.It ignore_ticket=bool
Set to true if the user specified the
.Fl k
@@ -384,8 +397,10 @@ For more information, see the
.Em check_policy
section.
.It timeout=string
User-specified command timeout.
Not all plugins support command timeouts and the ability for the
Command timeout specified by the user via the
.Fl T
option.
Not all plugins support command timeouts and the ability of the
user to set a timeout may be restricted by policy.
The format of the timeout string is plugin-specific.
.El
@@ -4451,6 +4466,14 @@ Support for audit and approval plugins was added.
Initial resource limit values were added to the
.Li user_info
list.
.Pp
The
.Em cmnd_chroot
and
.Em cmnd_cwd
enties were added to the
.Li settings
list.
.El
.Sh SEE ALSO
.Xr sudo.conf @mansectform@ ,

View File

@@ -25,7 +25,7 @@
.nr BA @BAMAN@
.nr LC @LCMAN@
.nr PS @PSMAN@
.TH "SUDOERS" "@mansectform@" "August 28, 2020" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
.TH "SUDOERS" "@mansectform@" "September 1, 2020" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
.nh
.if n .ad l
.SH "NAME"
@@ -1517,7 +1517,15 @@ must be a fully-qualified path name beginning with a
\(oq/\(cq
or
\(oq~\(cq
character.
character, or the special value
\(lq*\(rq.
A value of
\(lq*\(rq
indicates that the user may specify the working directory by running
\fBsudo\fR
with the
\fB\-D\fR
option.
By default, commands are run from the invoking user's current working
directory, unless the
\fB\-i\fR
@@ -1540,7 +1548,15 @@ must be a fully-qualified path name beginning with a
\(oq/\(cq
or
\(oq~\(cq
character.
character, or the special value
\(lq*\(rq.
A value of
\(lq*\(rq
indicates that the user may specify the root directory by running
\fBsudo\fR
with the
\fB\-R\fR
option .
This setting can be used to run the command in a
chroot(2)
\(lqsandbox\(rq
@@ -4455,6 +4471,12 @@ runchroot
If set,
\fBsudo\fR
will use this value for the root directory when running a command.
The special value
\(lq*\(rq
will allow the user to specify the root directory via
\fBsudo\fR's
\fB\-R\fR
option.
See the
\fIChroot_Spec\fR
section for more details.
@@ -4465,6 +4487,12 @@ runcwd
If set,
\fBsudo\fR
will use this value for the working directory when running a command.
The special value
\(lq*\(rq
will allow the user to specify the working directory via
\fBsudo\fR's
\fB\-D\fR
option.
See the
\fIChdir_Spec\fR
section for more details.

View File

@@ -24,7 +24,7 @@
.nr BA @BAMAN@
.nr LC @LCMAN@
.nr PS @PSMAN@
.Dd August 28, 2020
.Dd September 1, 2020
.Dt SUDOERS @mansectform@
.Os Sudo @PACKAGE_VERSION@
.Sh NAME
@@ -1437,7 +1437,15 @@ must be a fully-qualified path name beginning with a
.Sq /
or
.Sq ~
character.
character, or the special value
.Dq * .
A value of
.Dq *
indicates that the user may specify the working directory by running
.Nm sudo
with the
.Fl D
option.
By default, commands are run from the invoking user's current working
directory, unless the
.Fl i
@@ -1460,7 +1468,15 @@ must be a fully-qualified path name beginning with a
.Sq /
or
.Sq ~
character.
character, or the special value
.Dq * .
A value of
.Dq *
indicates that the user may specify the root directory by running
.Nm sudo
with the
.Fl R
option .
This setting can be used to run the command in a
.Xr chroot 2
.Dq sandbox
@@ -4166,6 +4182,12 @@ are processed before the contents of
If set,
.Nm sudo
will use this value for the root directory when running a command.
The special value
.Dq *
will allow the user to specify the root directory via
.Nm sudo Ns 's
.Fl R
option.
See the
.Sx Chroot_Spec
section for more details.
@@ -4175,6 +4197,12 @@ This setting is only supported by version 1.9.3 or higher.
If set,
.Nm sudo
will use this value for the working directory when running a command.
The special value
.Dq *
will allow the user to specify the working directory via
.Nm sudo Ns 's
.Fl D
option.
See the
.Sx Chdir_Spec
section for more details.

View File

@@ -552,11 +552,11 @@ struct sudo_defs_types sudo_defs_table[] = {
N_("Set the pam remote host to the local host name"),
NULL,
}, {
"runcwd", T_STR|T_BOOL|T_PATH|T_TILDE,
"runcwd", T_STR|T_BOOL|T_CHPATH,
N_("Working directory to change to before executing the command: %s"),
NULL,
}, {
"runchroot", T_STR|T_BOOL|T_PATH|T_TILDE,
"runchroot", T_STR|T_BOOL|T_CHPATH,
N_("Root directory to change to before executing the command: %s"),
NULL,
}, {

View File

@@ -400,8 +400,8 @@ pam_rhost
T_FLAG
"Set the pam remote host to the local host name"
runcwd
T_STR|T_BOOL|T_PATH|T_TILDE
T_STR|T_BOOL|T_CHPATH
"Working directory to change to before executing the command: %s"
runchroot
T_STR|T_BOOL|T_PATH|T_TILDE
T_STR|T_BOOL|T_CHPATH
"Root directory to change to before executing the command: %s"

View File

@@ -67,6 +67,7 @@ static bool store_tuple(const char *str, union sudo_defs_val *sd_un, struct def_
static bool store_uint(const char *str, union sudo_defs_val *sd_un);
static bool store_timespec(const char *str, union sudo_defs_val *sd_un);
static bool list_op(const char *str, size_t, union sudo_defs_val *sd_un, enum list_ops op);
static bool valid_path(struct sudo_defs_types *def, const char *val, const char *file, int lineno, bool quiet);
/*
* Table describing compile-time and run-time options.
@@ -249,21 +250,13 @@ parse_default_entry(struct sudo_defs_types *def, const char *val, int op,
rc = store_syslogpri(val, &def->sd_un);
break;
case T_STR:
if (ISSET(def->type, T_PATH) && val != NULL && *val != '/' &&
(!ISSET(def->type, T_TILDE) || *val != '~')) {
if (!quiet) {
if (lineno > 0) {
sudo_warnx(U_("%s:%d: values for \"%s\" must start with a '/'"),
file, lineno, def->name);
} else {
sudo_warnx(U_("%s: values for \"%s\" must start with a '/'"),
file, def->name);
}
if (val != NULL && ISSET(def->type, T_PATH|T_CHPATH)) {
if (!valid_path(def, val, file, lineno, quiet)) {
rc = -1;
break;
}
rc = -1;
break;
}
rc = store_str(val, &def->sd_un);
rc = store_str(val, &def->sd_un);
break;
case T_INT:
rc = store_int(val, &def->sd_un);
@@ -1017,6 +1010,48 @@ store_timeout(const char *str, union sudo_defs_val *sd_un)
debug_return_bool(true);
}
static bool
valid_path(struct sudo_defs_types *def, const char *val,
const char *file, int lineno, bool quiet)
{
bool ret = true;
debug_decl(valid_path, SUDOERS_DEBUG_DEFAULTS);
if (ISSET(def->type, T_CHPATH)) {
if (val[0] != '/' && val[0] != '~' && (val[0] != '*' || val[1] != '\0')) {
if (!quiet) {
if (lineno > 0) {
sudo_warnx(
U_("%s:%d: values for \"%s\" must start with a '/', '*', or '*'"),
file, lineno, def->name);
} else {
sudo_warnx(
U_("%s: values for \"%s\" must start with a '/', '*', or '*'"),
file, def->name);
}
}
ret = false;
}
} else {
if (val[0] != '/') {
if (!quiet) {
if (lineno > 0) {
sudo_warnx(
U_("%s:%d: values for \"%s\" must start with a '/'"),
file, lineno, def->name);
} else {
sudo_warnx(
U_("%s: values for \"%s\" must start with a '/'"),
file, def->name);
}
}
ret = false;
}
}
debug_return_bool(ret);
}
static bool
list_op(const char *str, size_t len, union sudo_defs_val *sd_un,
enum list_ops op)

View File

@@ -111,8 +111,8 @@ struct early_default {
#define T_BOOL 0x100
#undef T_PATH
#define T_PATH 0x200
#undef T_TILDE
#define T_TILDE 0x400
#undef T_CHPATH
#define T_CHPATH 0x400
/*
* Argument to update_defaults()

View File

@@ -583,7 +583,7 @@ short *yysslim;
YYSTYPE *yyvs;
unsigned int yystacksize;
int yyparse(void);
#line 1004 "gram.y"
#line 1018 "gram.y"
void
sudoerserror(const char *s)
{
@@ -1746,71 +1746,85 @@ break;
case 54:
#line 576 "gram.y"
{
if (yyvsp[0].string[0] != '/' && yyvsp[0].string[0] != '~') {
if (strcmp(yyvsp[0].string, "*") != 0) {
sudoerserror(N_("values for \"CWD\" must"
" start with a '/', '~', or '*'"));
YYERROR;
}
}
yyval.string = yyvsp[0].string;
}
break;
case 55:
#line 581 "gram.y"
#line 588 "gram.y"
{
if (yyvsp[0].string[0] != '/' && yyvsp[0].string[0] != '~') {
if (strcmp(yyvsp[0].string, "*") != 0) {
sudoerserror(N_("values for \"CHROOT\" must"
" start with a '/', '~', or '*'"));
YYERROR;
}
}
yyval.string = yyvsp[0].string;
}
break;
case 56:
#line 586 "gram.y"
{
yyval.string = yyvsp[0].string;
}
break;
case 57:
#line 591 "gram.y"
{
yyval.string = yyvsp[0].string;
}
break;
case 58:
#line 595 "gram.y"
{
yyval.string = yyvsp[0].string;
}
break;
case 59:
#line 600 "gram.y"
{
yyval.string = yyvsp[0].string;
}
break;
case 60:
case 57:
#line 605 "gram.y"
{
yyval.string = yyvsp[0].string;
}
break;
case 61:
#line 610 "gram.y"
case 58:
#line 609 "gram.y"
{
yyval.string = yyvsp[0].string;
}
break;
case 62:
case 59:
#line 614 "gram.y"
{
yyval.string = yyvsp[0].string;
}
break;
case 63:
case 60:
#line 619 "gram.y"
{
yyval.string = yyvsp[0].string;
}
break;
case 61:
#line 624 "gram.y"
{
yyval.string = yyvsp[0].string;
}
break;
case 62:
#line 628 "gram.y"
{
yyval.string = yyvsp[0].string;
}
break;
case 63:
#line 633 "gram.y"
{
yyval.runas = NULL;
}
break;
case 64:
#line 622 "gram.y"
#line 636 "gram.y"
{
yyval.runas = yyvsp[-1].runas;
}
break;
case 65:
#line 627 "gram.y"
#line 641 "gram.y"
{
yyval.runas = calloc(1, sizeof(struct runascontainer));
if (yyval.runas != NULL) {
@@ -1828,7 +1842,7 @@ case 65:
}
break;
case 66:
#line 642 "gram.y"
#line 656 "gram.y"
{
yyval.runas = calloc(1, sizeof(struct runascontainer));
if (yyval.runas == NULL) {
@@ -1840,7 +1854,7 @@ case 66:
}
break;
case 67:
#line 651 "gram.y"
#line 665 "gram.y"
{
yyval.runas = calloc(1, sizeof(struct runascontainer));
if (yyval.runas == NULL) {
@@ -1852,7 +1866,7 @@ case 67:
}
break;
case 68:
#line 660 "gram.y"
#line 674 "gram.y"
{
yyval.runas = calloc(1, sizeof(struct runascontainer));
if (yyval.runas == NULL) {
@@ -1864,7 +1878,7 @@ case 68:
}
break;
case 69:
#line 669 "gram.y"
#line 683 "gram.y"
{
yyval.runas = calloc(1, sizeof(struct runascontainer));
if (yyval.runas != NULL) {
@@ -1882,27 +1896,27 @@ case 69:
}
break;
case 70:
#line 686 "gram.y"
#line 700 "gram.y"
{
init_options(&yyval.options);
}
break;
case 71:
#line 689 "gram.y"
#line 703 "gram.y"
{
free(yyval.options.runcwd);
yyval.options.runcwd = yyvsp[0].string;
}
break;
case 72:
#line 693 "gram.y"
#line 707 "gram.y"
{
free(yyval.options.runchroot);
yyval.options.runchroot = yyvsp[0].string;
}
break;
case 73:
#line 697 "gram.y"
#line 711 "gram.y"
{
yyval.options.notbefore = parse_gentime(yyvsp[0].string);
free(yyvsp[0].string);
@@ -1913,7 +1927,7 @@ case 73:
}
break;
case 74:
#line 705 "gram.y"
#line 719 "gram.y"
{
yyval.options.notafter = parse_gentime(yyvsp[0].string);
free(yyvsp[0].string);
@@ -1924,7 +1938,7 @@ case 74:
}
break;
case 75:
#line 713 "gram.y"
#line 727 "gram.y"
{
yyval.options.timeout = parse_timeout(yyvsp[0].string);
free(yyvsp[0].string);
@@ -1938,7 +1952,7 @@ case 75:
}
break;
case 76:
#line 724 "gram.y"
#line 738 "gram.y"
{
#ifdef HAVE_SELINUX
free(yyval.options.role);
@@ -1947,7 +1961,7 @@ case 76:
}
break;
case 77:
#line 730 "gram.y"
#line 744 "gram.y"
{
#ifdef HAVE_SELINUX
free(yyval.options.type);
@@ -1956,7 +1970,7 @@ case 77:
}
break;
case 78:
#line 736 "gram.y"
#line 750 "gram.y"
{
#ifdef HAVE_PRIV_SET
free(yyval.options.privs);
@@ -1965,7 +1979,7 @@ case 78:
}
break;
case 79:
#line 742 "gram.y"
#line 756 "gram.y"
{
#ifdef HAVE_PRIV_SET
free(yyval.options.limitprivs);
@@ -1974,97 +1988,97 @@ case 79:
}
break;
case 80:
#line 750 "gram.y"
#line 764 "gram.y"
{
TAGS_INIT(yyval.tag);
}
break;
case 81:
#line 753 "gram.y"
#line 767 "gram.y"
{
yyval.tag.nopasswd = true;
}
break;
case 82:
#line 756 "gram.y"
#line 770 "gram.y"
{
yyval.tag.nopasswd = false;
}
break;
case 83:
#line 759 "gram.y"
#line 773 "gram.y"
{
yyval.tag.noexec = true;
}
break;
case 84:
#line 762 "gram.y"
#line 776 "gram.y"
{
yyval.tag.noexec = false;
}
break;
case 85:
#line 765 "gram.y"
#line 779 "gram.y"
{
yyval.tag.setenv = true;
}
break;
case 86:
#line 768 "gram.y"
#line 782 "gram.y"
{
yyval.tag.setenv = false;
}
break;
case 87:
#line 771 "gram.y"
#line 785 "gram.y"
{
yyval.tag.log_input = true;
}
break;
case 88:
#line 774 "gram.y"
#line 788 "gram.y"
{
yyval.tag.log_input = false;
}
break;
case 89:
#line 777 "gram.y"
#line 791 "gram.y"
{
yyval.tag.log_output = true;
}
break;
case 90:
#line 780 "gram.y"
#line 794 "gram.y"
{
yyval.tag.log_output = false;
}
break;
case 91:
#line 783 "gram.y"
#line 797 "gram.y"
{
yyval.tag.follow = true;
}
break;
case 92:
#line 786 "gram.y"
#line 800 "gram.y"
{
yyval.tag.follow = false;
}
break;
case 93:
#line 789 "gram.y"
#line 803 "gram.y"
{
yyval.tag.send_mail = true;
}
break;
case 94:
#line 792 "gram.y"
#line 806 "gram.y"
{
yyval.tag.send_mail = false;
}
break;
case 95:
#line 797 "gram.y"
#line 811 "gram.y"
{
yyval.member = new_member(NULL, ALL);
if (yyval.member == NULL) {
@@ -2074,7 +2088,7 @@ case 95:
}
break;
case 96:
#line 804 "gram.y"
#line 818 "gram.y"
{
yyval.member = new_member(yyvsp[0].string, ALIAS);
if (yyval.member == NULL) {
@@ -2084,7 +2098,7 @@ case 96:
}
break;
case 97:
#line 811 "gram.y"
#line 825 "gram.y"
{
struct sudo_command *c;
@@ -2101,7 +2115,7 @@ case 97:
}
break;
case 100:
#line 831 "gram.y"
#line 845 "gram.y"
{
const char *s;
s = alias_add(&parsed_policy, yyvsp[-2].string, HOSTALIAS,
@@ -2113,14 +2127,14 @@ case 100:
}
break;
case 102:
#line 843 "gram.y"
#line 857 "gram.y"
{
HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries);
yyval.member = yyvsp[-2].member;
}
break;
case 105:
#line 853 "gram.y"
#line 867 "gram.y"
{
const char *s;
s = alias_add(&parsed_policy, yyvsp[-2].string, CMNDALIAS,
@@ -2132,14 +2146,14 @@ case 105:
}
break;
case 107:
#line 865 "gram.y"
#line 879 "gram.y"
{
HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries);
yyval.member = yyvsp[-2].member;
}
break;
case 110:
#line 875 "gram.y"
#line 889 "gram.y"
{
const char *s;
s = alias_add(&parsed_policy, yyvsp[-2].string, RUNASALIAS,
@@ -2151,7 +2165,7 @@ case 110:
}
break;
case 113:
#line 890 "gram.y"
#line 904 "gram.y"
{
const char *s;
s = alias_add(&parsed_policy, yyvsp[-2].string, USERALIAS,
@@ -2163,28 +2177,28 @@ case 113:
}
break;
case 115:
#line 902 "gram.y"
#line 916 "gram.y"
{
HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries);
yyval.member = yyvsp[-2].member;
}
break;
case 116:
#line 908 "gram.y"
#line 922 "gram.y"
{
yyval.member = yyvsp[0].member;
yyval.member->negated = false;
}
break;
case 117:
#line 912 "gram.y"
#line 926 "gram.y"
{
yyval.member = yyvsp[0].member;
yyval.member->negated = true;
}
break;
case 118:
#line 918 "gram.y"
#line 932 "gram.y"
{
yyval.member = new_member(yyvsp[0].string, ALIAS);
if (yyval.member == NULL) {
@@ -2194,7 +2208,7 @@ case 118:
}
break;
case 119:
#line 925 "gram.y"
#line 939 "gram.y"
{
yyval.member = new_member(NULL, ALL);
if (yyval.member == NULL) {
@@ -2204,7 +2218,7 @@ case 119:
}
break;
case 120:
#line 932 "gram.y"
#line 946 "gram.y"
{
yyval.member = new_member(yyvsp[0].string, NETGROUP);
if (yyval.member == NULL) {
@@ -2214,7 +2228,7 @@ case 120:
}
break;
case 121:
#line 939 "gram.y"
#line 953 "gram.y"
{
yyval.member = new_member(yyvsp[0].string, USERGROUP);
if (yyval.member == NULL) {
@@ -2224,7 +2238,7 @@ case 121:
}
break;
case 122:
#line 946 "gram.y"
#line 960 "gram.y"
{
yyval.member = new_member(yyvsp[0].string, WORD);
if (yyval.member == NULL) {
@@ -2234,28 +2248,28 @@ case 122:
}
break;
case 124:
#line 956 "gram.y"
#line 970 "gram.y"
{
HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries);
yyval.member = yyvsp[-2].member;
}
break;
case 125:
#line 962 "gram.y"
#line 976 "gram.y"
{
yyval.member = yyvsp[0].member;
yyval.member->negated = false;
}
break;
case 126:
#line 966 "gram.y"
#line 980 "gram.y"
{
yyval.member = yyvsp[0].member;
yyval.member->negated = true;
}
break;
case 127:
#line 972 "gram.y"
#line 986 "gram.y"
{
yyval.member = new_member(yyvsp[0].string, ALIAS);
if (yyval.member == NULL) {
@@ -2265,7 +2279,7 @@ case 127:
}
break;
case 128:
#line 979 "gram.y"
#line 993 "gram.y"
{
yyval.member = new_member(NULL, ALL);
if (yyval.member == NULL) {
@@ -2275,7 +2289,7 @@ case 128:
}
break;
case 129:
#line 986 "gram.y"
#line 1000 "gram.y"
{
yyval.member = new_member(yyvsp[0].string, WORD);
if (yyval.member == NULL) {
@@ -2285,18 +2299,18 @@ case 129:
}
break;
case 130:
#line 995 "gram.y"
#line 1009 "gram.y"
{
;
}
break;
case 131:
#line 998 "gram.y"
#line 1012 "gram.y"
{
; /* EOF */
}
break;
#line 2286 "gram.c"
#line 2300 "gram.c"
}
yyssp -= yym;
yystate = *yyssp;

View File

@@ -574,11 +574,25 @@ opcmnd : cmnd {
;
chdirspec : CWD '=' WORD {
if ($3[0] != '/' && $3[0] != '~') {
if (strcmp($3, "*") != 0) {
sudoerserror(N_("values for \"CWD\" must"
" start with a '/', '~', or '*'"));
YYERROR;
}
}
$$ = $3;
}
;
chrootspec : CHROOT '=' WORD {
if ($3[0] != '/' && $3[0] != '~') {
if (strcmp($3, "*") != 0) {
sudoerserror(N_("values for \"CHROOT\" must"
" start with a '/', '~', or '*'"));
YYERROR;
}
}
$$ = $3;
}
;

View File

@@ -180,6 +180,16 @@ sudoers_policy_deserialize_info(void *v, char **runas_user, char **runas_group)
}
continue;
}
if (MATCHES(*cur, "cmnd_chroot=")) {
CHECK(*cur, "cmnd_chroot=");
user_runchroot = *cur + sizeof("cmnd_chroot=") - 1;
continue;
}
if (MATCHES(*cur, "cmnd_cwd=")) {
CHECK(*cur, "cmnd_cwd=");
user_runcwd = *cur + sizeof("cmnd_cwd=") - 1;
continue;
}
if (MATCHES(*cur, "runas_user=")) {
CHECK(*cur, "runas_user=");
*runas_user = *cur + sizeof("runas_user=") - 1;
@@ -618,7 +628,7 @@ sudoers_policy_exec_setup(char *argv[], char *envp[], mode_t cmnd_umask,
goto oom;
}
}
if (def_runcwd) {
if (def_runcwd && strcmp(def_runcwd, "*") != 0) {
/* Set cwd to explicit value in sudoers. */
if (!expand_tilde(&def_runcwd, runas_pw->pw_name)) {
sudo_warnx(U_("invalid working directory: %s"), def_runcwd);
@@ -787,7 +797,7 @@ sudoers_policy_exec_setup(char *argv[], char *envp[], mode_t cmnd_umask,
if (asprintf(&command_info[info_len++], "timeout=%u", timeout) == -1)
goto oom;
}
if (def_runchroot != NULL) {
if (def_runchroot != NULL && strcmp(def_runchroot, "*") != 0) {
if (!expand_tilde(&def_runchroot, runas_pw->pw_name)) {
sudo_warnx(U_("invalid chroot directory: %s"), def_runchroot);
goto bad;

View File

@@ -396,6 +396,28 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
}
}
if (user_runchroot != NULL) {
if (def_runchroot == NULL || strcmp(def_runchroot, "*") != 0) {
audit_failure(NewArgv,
N_("user not allowed to change root directory to %s"),
user_runchroot);
sudo_warnx("%s", U_("you are not permitted to use the -R option"));
goto bad;
}
free(def_runchroot);
def_runchroot = user_runchroot;
}
if (user_runcwd != NULL) {
if (def_runcwd == NULL || strcmp(def_runcwd, "*") != 0) {
audit_failure(NewArgv,
N_("user not allowed to change directory to %s"), user_runcwd);
sudo_warnx("%s", U_("you are not permitted to use the -D option"));
goto bad;
}
free(def_runcwd);
def_runcwd = user_runcwd;
}
/*
* Look up the timestamp dir owner if one is specified.
*/

View File

@@ -90,6 +90,8 @@ struct sudo_user {
char *shost;
char *runhost;
char *srunhost;
char *runchroot;
char *runcwd;
char *prompt;
char *cmnd;
char *cmnd_args;
@@ -236,6 +238,8 @@ struct sudo_user {
#define runas_privs (sudo_user.privs)
#define runas_limitprivs (sudo_user.limitprivs)
#define user_timeout (sudo_user.timeout)
#define user_runchroot (sudo_user.runchroot)
#define user_runcwd (sudo_user.runcwd)
/* Default sudoers uid/gid/mode if not set by the Makefile. */
#ifndef SUDOERS_UID

View File

@@ -100,7 +100,11 @@ static struct sudo_settings sudo_settings[] = {
{ "remote_host" },
#define ARG_TIMEOUT 22
{ "timeout" },
#define NUM_SETTINGS 23
#define ARG_CHROOT 23
{ "cmnd_chroot" },
#define ARG_CWD 24
{ "cmnd_cwd" },
#define NUM_SETTINGS 25
{ NULL }
};
@@ -123,7 +127,7 @@ struct environment {
* Note that we must disable arg permutation to support setting environment
* variables and to better support the optional arg of the -h flag.
*/
static const char short_opts[] = "+Aa:BbC:c:D:Eeg:Hh::iKklnPp:r:SsT:t:U:u:Vv";
static const char short_opts[] = "+Aa:BbC:c:D:Eeg:Hh::iKklnPp:R:r:SsT:t:U:u:Vv";
static struct option long_opts[] = {
{ "askpass", no_argument, NULL, 'A' },
{ "auth-type", required_argument, NULL, 'a' },
@@ -131,6 +135,7 @@ static struct option long_opts[] = {
{ "bell", no_argument, NULL, 'B' },
{ "close-from", required_argument, NULL, 'C' },
{ "login-class", required_argument, NULL, 'c' },
{ "chdir", required_argument, NULL, 'D' },
{ "preserve-env", optional_argument, NULL, 'E' },
{ "edit", no_argument, NULL, 'e' },
{ "group", required_argument, NULL, 'g' },
@@ -144,6 +149,7 @@ static struct option long_opts[] = {
{ "non-interactive", no_argument, NULL, 'n' },
{ "preserve-groups", no_argument, NULL, 'P' },
{ "prompt", required_argument, NULL, 'p' },
{ "chroot", required_argument, NULL, 'R' },
{ "role", required_argument, NULL, 'r' },
{ "stdin", no_argument, NULL, 'S' },
{ "shell", no_argument, NULL, 's' },
@@ -334,7 +340,12 @@ parse_args(int argc, char **argv, int *old_optind, int *nargc, char ***nargv,
break;
#endif
case 'D':
/* Ignored for backwards compatibility. */
assert(optarg != NULL);
if (*optarg == '\0')
usage();
if (sudo_settings[ARG_CWD].value != NULL)
usage();
sudo_settings[ARG_CWD].value = optarg;
break;
case 'E':
/*
@@ -436,6 +447,14 @@ parse_args(int argc, char **argv, int *old_optind, int *nargc, char ***nargv,
usage();
sudo_settings[ARG_PROMPT].value = optarg;
break;
case 'R':
assert(optarg != NULL);
if (*optarg == '\0')
usage();
if (sudo_settings[ARG_CHROOT].value != NULL)
usage();
sudo_settings[ARG_CHROOT].value = optarg;
break;
#ifdef HAVE_SELINUX
case 'r':
assert(optarg != NULL);
@@ -775,6 +794,8 @@ help(void)
sudo_lbuf_append(&lbuf, " -c, --login-class=class %s\n",
_("run command with the specified BSD login class"));
#endif
sudo_lbuf_append(&lbuf, " -D, --chdir=directory %s\n",
_("change the working directory before running command"));
sudo_lbuf_append(&lbuf, " -E, --preserve-env %s\n",
_("preserve user environment when running command"));
sudo_lbuf_append(&lbuf, " --preserve-env=list %s\n",
@@ -803,6 +824,8 @@ help(void)
_("preserve group vector instead of setting to target's"));
sudo_lbuf_append(&lbuf, " -p, --prompt=prompt %s\n",
_("use the specified password prompt"));
sudo_lbuf_append(&lbuf, " -R, --chroot=directory %s\n",
_("change the root directory before running command"));
#ifdef HAVE_SELINUX
sudo_lbuf_append(&lbuf, " -r, --role=role %s\n",
_("create SELinux security context with specified role"));

View File

@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2007-2010, 2013, 2015, 2017
* Copyright (c) 2007-2010, 2013, 2015, 2017, 2020
* Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -27,8 +27,8 @@
#define SUDO_USAGE1 " -h | -K | -k | -V"
#define SUDO_USAGE2 " -v [-AknS] @BSDAUTH_USAGE@[-g group] [-h host] [-p prompt] [-u user]"
#define SUDO_USAGE3 " -l [-AknS] @BSDAUTH_USAGE@[-g group] [-h host] [-p prompt] [-U user] [-u user] [command]"
#define SUDO_USAGE4 " [-AbEHknPS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C num] @LOGINCAP_USAGE@[-g group] [-h host] [-p prompt] [-T timeout] [-u user] [VAR=value] [-i|-s] [<command>]"
#define SUDO_USAGE5 " -e [-AknS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C num] @LOGINCAP_USAGE@[-g group] [-h host] [-p prompt] [-T timeout] [-u user] file ..."
#define SUDO_USAGE4 " [-AbEHknPS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C num] [-D directory] @LOGINCAP_USAGE@[-g group] [-h host] [-p prompt] [-R directory] [-T timeout] [-u user] [VAR=value] [-i|-s] [<command>]"
#define SUDO_USAGE5 " -e [-AknS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C num] @LOGINCAP_USAGE@[-D directory] [-g group] [-h host] [-p prompt] [-R directory] [-T timeout] [-u user] file ..."
/*
* Configure script arguments used to build sudo.