mirror of
https://github.com/sudo-project/sudo.git
synced 2025-08-22 01:49:11 +00:00
Add support for @include and @includedir
These are less confusing than #include and #includedir when the hash character is also the comment character. This commit also adds real parsing of include directives as opposed to the pure lexer approach used previously. As a result, it is now possible to include files with spaces by either using a double-quoted string or escaping the space characters with a backslash.
This commit is contained in:
parent
c63ba01e0e
commit
741c6f274e
6
MANIFEST
6
MANIFEST
@ -809,6 +809,10 @@ plugins/sudoers/regress/sudoers/test9.toke.ok
|
||||
plugins/sudoers/regress/testsudoers/group
|
||||
plugins/sudoers/regress/testsudoers/test1.out.ok
|
||||
plugins/sudoers/regress/testsudoers/test1.sh
|
||||
plugins/sudoers/regress/testsudoers/test10.out.ok
|
||||
plugins/sudoers/regress/testsudoers/test10.sh
|
||||
plugins/sudoers/regress/testsudoers/test11.out.ok
|
||||
plugins/sudoers/regress/testsudoers/test11.sh
|
||||
plugins/sudoers/regress/testsudoers/test2.inc
|
||||
plugins/sudoers/regress/testsudoers/test2.out.ok
|
||||
plugins/sudoers/regress/testsudoers/test2.sh
|
||||
@ -824,6 +828,8 @@ plugins/sudoers/regress/testsudoers/test7.out.ok
|
||||
plugins/sudoers/regress/testsudoers/test7.sh
|
||||
plugins/sudoers/regress/testsudoers/test8.out.ok
|
||||
plugins/sudoers/regress/testsudoers/test8.sh
|
||||
plugins/sudoers/regress/testsudoers/test9.out.ok
|
||||
plugins/sudoers/regress/testsudoers/test9.sh
|
||||
plugins/sudoers/regress/visudo/test1.out.ok
|
||||
plugins/sudoers/regress/visudo/test1.sh
|
||||
plugins/sudoers/regress/visudo/test10.out.ok
|
||||
|
5
NEWS
5
NEWS
@ -19,6 +19,11 @@ What's new in Sudo 1.9.1
|
||||
option to allow replaying a session that is still in progress,
|
||||
similar to "tail -f".
|
||||
|
||||
* The @include and @includedir directives can be used in sudoers
|
||||
instead of #include and #includedir. In addition, include paths
|
||||
may now have embedded white space by either using a double-quoted
|
||||
string or escaping the space characters with a backslash.
|
||||
|
||||
What's new in Sudo 1.9.0
|
||||
|
||||
* Fixed a test failure in the strsig_test regress test on FreeBSD.
|
||||
|
@ -25,7 +25,7 @@
|
||||
.nr BA @BAMAN@
|
||||
.nr LC @LCMAN@
|
||||
.nr PS @PSMAN@
|
||||
.TH "SUDOERS" "@mansectform@" "April 30, 2020" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
|
||||
.TH "SUDOERS" "@mansectform@" "May 19, 2020" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
|
||||
.nh
|
||||
.if n .ad l
|
||||
.SH "NAME"
|
||||
@ -1864,12 +1864,17 @@ It is possible to include other
|
||||
files from within the
|
||||
\fIsudoers\fR
|
||||
file currently being parsed using the
|
||||
\fR@include\fR
|
||||
and
|
||||
\fR@includedir\fR
|
||||
directives.
|
||||
For compatibility with sudo versions prior to 1.9.1,
|
||||
\fR#include\fR
|
||||
and
|
||||
\fR#includedir\fR
|
||||
directives.
|
||||
are also accepted.
|
||||
.PP
|
||||
This can be used, for example, to keep a site-wide
|
||||
An include file can be used, for example, to keep a site-wide
|
||||
\fIsudoers\fR
|
||||
file in addition to a local, per-machine file.
|
||||
For the sake of this example the site-wide
|
||||
@ -1882,13 +1887,12 @@ To include
|
||||
\fI/etc/sudoers.local\fR
|
||||
from within
|
||||
\fI/etc/sudoers\fR
|
||||
we would use the
|
||||
following line in
|
||||
one would use the following line in
|
||||
\fI/etc/sudoers\fR:
|
||||
.nf
|
||||
.sp
|
||||
.RS 4n
|
||||
#include /etc/sudoers.local
|
||||
@include /etc/sudoers.local
|
||||
.RE
|
||||
.fi
|
||||
.PP
|
||||
@ -1907,6 +1911,16 @@ Files that are included may themselves include other files.
|
||||
A hard limit of 128 nested include files is enforced to prevent include
|
||||
file loops.
|
||||
.PP
|
||||
The path to the include file may contain white space if it is
|
||||
escaped with a backslash
|
||||
(\(oq\e\(cq).
|
||||
Alternately, the entire path may be enclosed in double quotes
|
||||
(\&""),
|
||||
in which case no escaping is necessary.
|
||||
To include a literal backslash in the path,
|
||||
\(oq\e\e\(cq
|
||||
should be used.
|
||||
.PP
|
||||
If the path to the include file is not fully-qualified (does not
|
||||
begin with a
|
||||
\(oq/\(cq),
|
||||
@ -1918,7 +1932,7 @@ contains the line:
|
||||
.nf
|
||||
.sp
|
||||
.RS 4n
|
||||
\fR#include sudoers.local\fR
|
||||
\fR@include sudoers.local\fR
|
||||
.RE
|
||||
.fi
|
||||
.PP
|
||||
@ -1934,7 +1948,7 @@ then
|
||||
.nf
|
||||
.sp
|
||||
.RS 4n
|
||||
#include /etc/sudoers.%h
|
||||
@include /etc/sudoers.%h
|
||||
.RE
|
||||
.fi
|
||||
.PP
|
||||
@ -1944,7 +1958,7 @@ to include the file
|
||||
\fI/etc/sudoers.xerxes\fR.
|
||||
.PP
|
||||
The
|
||||
\fR#includedir\fR
|
||||
\fR@includedir\fR
|
||||
directive can be used to create a
|
||||
\fIsudoers.d\fR
|
||||
directory that the system package manager can drop
|
||||
@ -1954,7 +1968,7 @@ For example, given:
|
||||
.nf
|
||||
.sp
|
||||
.RS 4n
|
||||
#includedir /etc/sudoers.d
|
||||
@includedir /etc/sudoers.d
|
||||
.RE
|
||||
.fi
|
||||
.PP
|
||||
@ -1981,14 +1995,14 @@ Using a consistent number of leading zeroes in the file names can be used
|
||||
to avoid such problems.
|
||||
After parsing the files in the directory, control returns to the
|
||||
file that contained the
|
||||
\fR#includedir\fR
|
||||
\fR@includedir\fR
|
||||
directive.
|
||||
.PP
|
||||
Note that unlike files included via
|
||||
\fR#include\fR,
|
||||
\fR@include\fR,
|
||||
\fBvisudo\fR
|
||||
will not edit the files in a
|
||||
\fR#includedir\fR
|
||||
\fR@includedir\fR
|
||||
directory unless one of them contains a syntax error.
|
||||
It is still possible to run
|
||||
\fBvisudo\fR
|
||||
@ -4105,7 +4119,7 @@ Entries in this file should either be of the form
|
||||
\(lq\fRVARIABLE=value\fR\(rq
|
||||
or
|
||||
\(lq\fRexport VARIABLE=value\fR\(rq.
|
||||
The value may optionally be surrounded by single or double quotes.
|
||||
The value may optionally be enclosed in single or double quotes.
|
||||
Variables in this file are only added if the variable does not already
|
||||
exist in the environment.
|
||||
This file is considered to be part of the security policy,
|
||||
@ -4332,7 +4346,7 @@ Entries in this file should either be of the form
|
||||
\(lq\fRVARIABLE=value\fR\(rq
|
||||
or
|
||||
\(lq\fRexport VARIABLE=value\fR\(rq.
|
||||
The value may optionally be surrounded by single or double quotes.
|
||||
The value may optionally be enclosed in single or double quotes.
|
||||
Variables in this file are only added if the variable does not already
|
||||
exist in the environment.
|
||||
Unlike
|
||||
|
@ -24,7 +24,7 @@
|
||||
.nr BA @BAMAN@
|
||||
.nr LC @LCMAN@
|
||||
.nr PS @PSMAN@
|
||||
.Dd April 30, 2020
|
||||
.Dd May 19, 2020
|
||||
.Dt SUDOERS @mansectform@
|
||||
.Os Sudo @PACKAGE_VERSION@
|
||||
.Sh NAME
|
||||
@ -1753,12 +1753,17 @@ It is possible to include other
|
||||
files from within the
|
||||
.Em sudoers
|
||||
file currently being parsed using the
|
||||
.Li @include
|
||||
and
|
||||
.Li @includedir
|
||||
directives.
|
||||
For compatibility with sudo versions prior to 1.9.1,
|
||||
.Li #include
|
||||
and
|
||||
.Li #includedir
|
||||
directives.
|
||||
are also accepted.
|
||||
.Pp
|
||||
This can be used, for example, to keep a site-wide
|
||||
An include file can be used, for example, to keep a site-wide
|
||||
.Em sudoers
|
||||
file in addition to a local, per-machine file.
|
||||
For the sake of this example the site-wide
|
||||
@ -1771,11 +1776,10 @@ To include
|
||||
.Pa /etc/sudoers.local
|
||||
from within
|
||||
.Pa /etc/sudoers
|
||||
we would use the
|
||||
following line in
|
||||
one would use the following line in
|
||||
.Pa /etc/sudoers :
|
||||
.Bd -literal -offset 4n
|
||||
#include /etc/sudoers.local
|
||||
@include /etc/sudoers.local
|
||||
.Ed
|
||||
.Pp
|
||||
When
|
||||
@ -1793,6 +1797,16 @@ Files that are included may themselves include other files.
|
||||
A hard limit of 128 nested include files is enforced to prevent include
|
||||
file loops.
|
||||
.Pp
|
||||
The path to the include file may contain white space if it is
|
||||
escaped with a backslash
|
||||
.Pq Ql \e .
|
||||
Alternately, the entire path may be enclosed in double quotes
|
||||
.Pq \&"" ,
|
||||
in which case no escaping is necessary.
|
||||
To include a literal backslash in the path,
|
||||
.Ql \e\e
|
||||
should be used.
|
||||
.Pp
|
||||
If the path to the include file is not fully-qualified (does not
|
||||
begin with a
|
||||
.Ql / ) ,
|
||||
@ -1802,7 +1816,7 @@ For example, if
|
||||
.Pa /etc/sudoers
|
||||
contains the line:
|
||||
.Bd -literal -offset 4n
|
||||
.Li #include sudoers.local
|
||||
.Li @include sudoers.local
|
||||
.Ed
|
||||
.Pp
|
||||
the file that will be included is
|
||||
@ -1815,7 +1829,7 @@ In other words, if the machine's host name is
|
||||
.Dq xerxes ,
|
||||
then
|
||||
.Bd -literal -offset 4n
|
||||
#include /etc/sudoers.%h
|
||||
@include /etc/sudoers.%h
|
||||
.Ed
|
||||
.Pp
|
||||
will cause
|
||||
@ -1824,7 +1838,7 @@ to include the file
|
||||
.Pa /etc/sudoers.xerxes .
|
||||
.Pp
|
||||
The
|
||||
.Li #includedir
|
||||
.Li @includedir
|
||||
directive can be used to create a
|
||||
.Pa sudoers.d
|
||||
directory that the system package manager can drop
|
||||
@ -1832,7 +1846,7 @@ directory that the system package manager can drop
|
||||
file rules into as part of package installation.
|
||||
For example, given:
|
||||
.Bd -literal -offset 4n
|
||||
#includedir /etc/sudoers.d
|
||||
@includedir /etc/sudoers.d
|
||||
.Ed
|
||||
.Pp
|
||||
.Nm sudo
|
||||
@ -1858,14 +1872,14 @@ Using a consistent number of leading zeroes in the file names can be used
|
||||
to avoid such problems.
|
||||
After parsing the files in the directory, control returns to the
|
||||
file that contained the
|
||||
.Li #includedir
|
||||
.Li @includedir
|
||||
directive.
|
||||
.Pp
|
||||
Note that unlike files included via
|
||||
.Li #include ,
|
||||
.Li @include ,
|
||||
.Nm visudo
|
||||
will not edit the files in a
|
||||
.Li #includedir
|
||||
.Li @includedir
|
||||
directory unless one of them contains a syntax error.
|
||||
It is still possible to run
|
||||
.Nm visudo
|
||||
@ -3849,7 +3863,7 @@ Entries in this file should either be of the form
|
||||
.Dq Li VARIABLE=value
|
||||
or
|
||||
.Dq Li export VARIABLE=value .
|
||||
The value may optionally be surrounded by single or double quotes.
|
||||
The value may optionally be enclosed in single or double quotes.
|
||||
Variables in this file are only added if the variable does not already
|
||||
exist in the environment.
|
||||
This file is considered to be part of the security policy,
|
||||
@ -4045,7 +4059,7 @@ Entries in this file should either be of the form
|
||||
.Dq Li VARIABLE=value
|
||||
or
|
||||
.Dq Li export VARIABLE=value .
|
||||
The value may optionally be surrounded by single or double quotes.
|
||||
The value may optionally be enclosed in single or double quotes.
|
||||
Variables in this file are only added if the variable does not already
|
||||
exist in the environment.
|
||||
Unlike
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,44 +6,46 @@
|
||||
#define USERGROUP 262
|
||||
#define WORD 263
|
||||
#define DIGEST 264
|
||||
#define DEFAULTS 265
|
||||
#define DEFAULTS_HOST 266
|
||||
#define DEFAULTS_USER 267
|
||||
#define DEFAULTS_RUNAS 268
|
||||
#define DEFAULTS_CMND 269
|
||||
#define NOPASSWD 270
|
||||
#define PASSWD 271
|
||||
#define NOEXEC 272
|
||||
#define EXEC 273
|
||||
#define SETENV 274
|
||||
#define NOSETENV 275
|
||||
#define LOG_INPUT 276
|
||||
#define NOLOG_INPUT 277
|
||||
#define LOG_OUTPUT 278
|
||||
#define NOLOG_OUTPUT 279
|
||||
#define MAIL 280
|
||||
#define NOMAIL 281
|
||||
#define FOLLOWLNK 282
|
||||
#define NOFOLLOWLNK 283
|
||||
#define ALL 284
|
||||
#define COMMENT 285
|
||||
#define HOSTALIAS 286
|
||||
#define CMNDALIAS 287
|
||||
#define USERALIAS 288
|
||||
#define RUNASALIAS 289
|
||||
#define ERROR 290
|
||||
#define TYPE 291
|
||||
#define ROLE 292
|
||||
#define PRIVS 293
|
||||
#define LIMITPRIVS 294
|
||||
#define CMND_TIMEOUT 295
|
||||
#define NOTBEFORE 296
|
||||
#define NOTAFTER 297
|
||||
#define MYSELF 298
|
||||
#define SHA224_TOK 299
|
||||
#define SHA256_TOK 300
|
||||
#define SHA384_TOK 301
|
||||
#define SHA512_TOK 302
|
||||
#define INCLUDE 265
|
||||
#define INCLUDEDIR 266
|
||||
#define DEFAULTS 267
|
||||
#define DEFAULTS_HOST 268
|
||||
#define DEFAULTS_USER 269
|
||||
#define DEFAULTS_RUNAS 270
|
||||
#define DEFAULTS_CMND 271
|
||||
#define NOPASSWD 272
|
||||
#define PASSWD 273
|
||||
#define NOEXEC 274
|
||||
#define EXEC 275
|
||||
#define SETENV 276
|
||||
#define NOSETENV 277
|
||||
#define LOG_INPUT 278
|
||||
#define NOLOG_INPUT 279
|
||||
#define LOG_OUTPUT 280
|
||||
#define NOLOG_OUTPUT 281
|
||||
#define MAIL 282
|
||||
#define NOMAIL 283
|
||||
#define FOLLOWLNK 284
|
||||
#define NOFOLLOWLNK 285
|
||||
#define ALL 286
|
||||
#define COMMENT 287
|
||||
#define HOSTALIAS 288
|
||||
#define CMNDALIAS 289
|
||||
#define USERALIAS 290
|
||||
#define RUNASALIAS 291
|
||||
#define ERROR 292
|
||||
#define TYPE 293
|
||||
#define ROLE 294
|
||||
#define PRIVS 295
|
||||
#define LIMITPRIVS 296
|
||||
#define CMND_TIMEOUT 297
|
||||
#define NOTBEFORE 298
|
||||
#define NOTAFTER 299
|
||||
#define MYSELF 300
|
||||
#define SHA224_TOK 301
|
||||
#define SHA256_TOK 302
|
||||
#define SHA384_TOK 303
|
||||
#define SHA512_TOK 304
|
||||
#ifndef YYSTYPE_DEFINED
|
||||
#define YYSTYPE_DEFINED
|
||||
typedef union {
|
||||
|
@ -92,6 +92,8 @@ static struct command_digest *new_digest(int, char *);
|
||||
%token <string> USERGROUP /* a usergroup (%NAME) */
|
||||
%token <string> WORD /* a word */
|
||||
%token <string> DIGEST /* a SHA-2 digest */
|
||||
%token <tok> INCLUDE /* @include */
|
||||
%token <tok> INCLUDEDIR /* @includedir */
|
||||
%token <tok> DEFAULTS /* Defaults entry */
|
||||
%token <tok> DEFAULTS_HOST /* Host-specific defaults entry */
|
||||
%token <tok> DEFAULTS_USER /* User-specific defaults entry */
|
||||
@ -182,6 +184,20 @@ entry : COMMENT {
|
||||
| error COMMENT {
|
||||
yyerrok;
|
||||
}
|
||||
| INCLUDE WORD {
|
||||
if (!push_include($2, false)) {
|
||||
free($2);
|
||||
YYERROR;
|
||||
}
|
||||
free($2);
|
||||
}
|
||||
| INCLUDEDIR WORD {
|
||||
if (!push_include($2, true)) {
|
||||
free($2);
|
||||
YYERROR;
|
||||
}
|
||||
free($2);
|
||||
}
|
||||
| userlist privileges {
|
||||
if (!add_userspec($1, $2)) {
|
||||
sudoerserror(N_("unable to allocate memory"));
|
||||
|
51
plugins/sudoers/regress/testsudoers/test10.out.ok
Normal file
51
plugins/sudoers/regress/testsudoers/test10.out.ok
Normal file
@ -0,0 +1,51 @@
|
||||
Testing @include of a path with escaped white space
|
||||
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
||||
ALL = ALL
|
||||
host matched
|
||||
runas matched
|
||||
cmnd allowed
|
||||
|
||||
Command allowed
|
||||
|
||||
Testing @include of a double-quoted path with white space
|
||||
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
||||
ALL = ALL
|
||||
host matched
|
||||
runas matched
|
||||
cmnd allowed
|
||||
|
||||
Command allowed
|
||||
|
||||
Testing #include of a path with escaped white space
|
||||
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
||||
ALL = ALL
|
||||
host matched
|
||||
runas matched
|
||||
cmnd allowed
|
||||
|
||||
Command allowed
|
||||
|
||||
Testing #include of a double-quoted path with white space
|
||||
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
||||
ALL = ALL
|
||||
host matched
|
||||
runas matched
|
||||
cmnd allowed
|
||||
|
||||
Command allowed
|
44
plugins/sudoers/regress/testsudoers/test10.sh
Executable file
44
plugins/sudoers/regress/testsudoers/test10.sh
Executable file
@ -0,0 +1,44 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Test @include of a file with embedded white space
|
||||
#
|
||||
|
||||
# Create test file
|
||||
TESTDIR="`pwd`/regress/testsudoers"
|
||||
cat >"$TESTDIR/test 10.inc" <<EOF
|
||||
root ALL = ALL
|
||||
EOF
|
||||
|
||||
MYUID=`\ls -lnd "$TESTDIR/test 10.inc" | awk '{print $3}'`
|
||||
MYGID=`\ls -lnd "$TESTDIR/test 10.inc" | awk '{print $4}'`
|
||||
exec 2>&1
|
||||
|
||||
echo "Testing @include of a path with escaped white space"
|
||||
echo ""
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<-EOF
|
||||
@include $TESTDIR/test\ 10.inc
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "Testing @include of a double-quoted path with white space"
|
||||
echo ""
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<-EOF
|
||||
@include "$TESTDIR/test 10.inc"
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "Testing #include of a path with escaped white space"
|
||||
echo ""
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<-EOF
|
||||
#include $TESTDIR/test\ 10.inc
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "Testing #include of a double-quoted path with white space"
|
||||
echo ""
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<-EOF
|
||||
#include "$TESTDIR/test 10.inc"
|
||||
EOF
|
||||
|
||||
rm -f "$TESTDIR/test 10.inc"
|
||||
exit 0
|
27
plugins/sudoers/regress/testsudoers/test11.out.ok
Normal file
27
plugins/sudoers/regress/testsudoers/test11.out.ok
Normal file
@ -0,0 +1,27 @@
|
||||
Testing @include with garbage after the path name
|
||||
|
||||
>>> sudoers: syntax error near line 1 <<<
|
||||
Parse error in sudoers near line 1.
|
||||
|
||||
Entries for user root:
|
||||
|
||||
ALL = ALL
|
||||
host matched
|
||||
runas matched
|
||||
cmnd allowed
|
||||
|
||||
Command allowed
|
||||
|
||||
Testing #include with garbage after the path name
|
||||
|
||||
>>> sudoers: syntax error near line 1 <<<
|
||||
Parse error in sudoers near line 1.
|
||||
|
||||
Entries for user root:
|
||||
|
||||
ALL = ALL
|
||||
host matched
|
||||
runas matched
|
||||
cmnd allowed
|
||||
|
||||
Command allowed
|
26
plugins/sudoers/regress/testsudoers/test11.sh
Executable file
26
plugins/sudoers/regress/testsudoers/test11.sh
Executable file
@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Test @include with garbage after the path name
|
||||
#
|
||||
|
||||
# Avoid warnings about memory leaks when there is a syntax error
|
||||
ASAN_OPTIONS=detect_leaks=0; export ASAN_OPTIONS
|
||||
|
||||
MYUID=`\ls -ln $TESTDIR/test2.inc | awk '{print $3}'`
|
||||
MYGID=`\ls -ln $TESTDIR/test2.inc | awk '{print $4}'`
|
||||
exec 2>&1
|
||||
|
||||
echo "Testing @include with garbage after the path name"
|
||||
echo ""
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<EOF
|
||||
@include $TESTDIR/test2.inc womp womp
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "Testing #include with garbage after the path name"
|
||||
echo ""
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<EOF
|
||||
#include $TESTDIR/test2.inc womp womp
|
||||
EOF
|
||||
|
||||
exit 0
|
@ -1,3 +1,18 @@
|
||||
Testing @include
|
||||
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
||||
ALL = ALL
|
||||
host matched
|
||||
runas matched
|
||||
cmnd allowed
|
||||
|
||||
Command allowed
|
||||
|
||||
Testing #include
|
||||
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
@ -1,11 +1,21 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Test #include facility
|
||||
# Test @include facility
|
||||
#
|
||||
|
||||
MYUID=`\ls -ln $TESTDIR/test2.inc | awk '{print $3}'`
|
||||
MYGID=`\ls -ln $TESTDIR/test2.inc | awk '{print $4}'`
|
||||
exec 2>&1
|
||||
|
||||
echo "Testing @include"
|
||||
echo ""
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<EOF
|
||||
@include $TESTDIR/test2.inc
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "Testing #include"
|
||||
echo ""
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<EOF
|
||||
#include $TESTDIR/test2.inc
|
||||
EOF
|
||||
|
@ -1,3 +1,44 @@
|
||||
Testing @includedir of an unquoted path
|
||||
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
||||
ALL = ALL
|
||||
host matched
|
||||
runas matched
|
||||
cmnd allowed
|
||||
|
||||
Command allowed
|
||||
|
||||
Testing @includedir of a double-quoted path
|
||||
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
||||
ALL = ALL
|
||||
host matched
|
||||
runas matched
|
||||
cmnd allowed
|
||||
|
||||
Command allowed
|
||||
|
||||
Testing #includedir of an unquoted path
|
||||
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
||||
ALL = ALL
|
||||
host matched
|
||||
runas matched
|
||||
cmnd allowed
|
||||
|
||||
Command allowed
|
||||
|
||||
Testing #includedir of a double-quoted path
|
||||
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Test #include facility
|
||||
# Test @includedir facility
|
||||
#
|
||||
|
||||
parentdir="`echo $0 | sed 's:/[^/]*$::'`"
|
||||
@ -15,9 +15,35 @@ if [ -d "$parentdir" ]; then
|
||||
MYUID=`\ls -lnd $TESTDIR/test3.d | awk '{print $3}'`
|
||||
MYGID=`\ls -lnd $TESTDIR/test3.d | awk '{print $4}'`
|
||||
exec 2>&1
|
||||
|
||||
echo "Testing @includedir of an unquoted path"
|
||||
echo ""
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<-EOF
|
||||
@includedir $TESTDIR/test3.d
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "Testing @includedir of a double-quoted path"
|
||||
echo ""
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<-EOF
|
||||
@includedir "$TESTDIR/test3.d"
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "Testing #includedir of an unquoted path"
|
||||
echo ""
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<-EOF
|
||||
#includedir $TESTDIR/test3.d
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "Testing #includedir of a double-quoted path"
|
||||
echo ""
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<-EOF
|
||||
#includedir "$TESTDIR/test3.d"
|
||||
EOF
|
||||
|
||||
rm -rf "${parentdir}/test3.d"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
@ -8,7 +8,7 @@ ASAN_OPTIONS=detect_leaks=0; export ASAN_OPTIONS
|
||||
|
||||
exec 2>&1
|
||||
./testsudoers -U 1 root id <<EOF
|
||||
#include $TESTDIR/test2.inc
|
||||
@include $TESTDIR/test2.inc
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
|
@ -19,13 +19,13 @@ exec 2>&1
|
||||
# Test world writable
|
||||
chmod 666 $TESTFILE
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<EOF
|
||||
#include $TESTFILE
|
||||
@include $TESTFILE
|
||||
EOF
|
||||
|
||||
# Test group writable
|
||||
chmod 664 $TESTFILE
|
||||
./testsudoers -U $MYUID -G -2 root id <<EOF
|
||||
#include $TESTFILE
|
||||
@include $TESTFILE
|
||||
EOF
|
||||
|
||||
rm -f $TESTFILE
|
||||
|
@ -1,3 +1,18 @@
|
||||
Testing @include without a newline
|
||||
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
||||
ALL = ALL
|
||||
host matched
|
||||
runas matched
|
||||
cmnd allowed
|
||||
|
||||
Command allowed
|
||||
|
||||
Testing #include without a newline
|
||||
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
@ -1,12 +1,21 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Test #include facility w/o a final newline.
|
||||
# Test @include facility w/o a final newline.
|
||||
# Same as test2.sh but missing the final newline.
|
||||
#
|
||||
|
||||
MYUID=`\ls -ln $TESTDIR/test2.inc | awk '{print $3}'`
|
||||
MYGID=`\ls -ln $TESTDIR/test2.inc | awk '{print $4}'`
|
||||
exec 2>&1
|
||||
|
||||
echo "Testing @include without a newline"
|
||||
echo ""
|
||||
printf "@include $TESTDIR/test2.inc" | \
|
||||
./testsudoers -U $MYUID -G $MYGID root id
|
||||
|
||||
echo ""
|
||||
echo "Testing #include without a newline"
|
||||
echo ""
|
||||
printf "#include $TESTDIR/test2.inc" | \
|
||||
./testsudoers -U $MYUID -G $MYGID root id
|
||||
|
||||
|
10
plugins/sudoers/regress/testsudoers/test9.out.ok
Normal file
10
plugins/sudoers/regress/testsudoers/test9.out.ok
Normal file
@ -0,0 +1,10 @@
|
||||
Parses OK.
|
||||
|
||||
Entries for user root:
|
||||
|
||||
ALL = ALL
|
||||
host matched
|
||||
runas matched
|
||||
cmnd allowed
|
||||
|
||||
Command allowed
|
13
plugins/sudoers/regress/testsudoers/test9.sh
Executable file
13
plugins/sudoers/regress/testsudoers/test9.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Test #include facility
|
||||
#
|
||||
|
||||
MYUID=`\ls -ln $TESTDIR/test2.inc | awk '{print $3}'`
|
||||
MYGID=`\ls -ln $TESTDIR/test2.inc | awk '{print $4}'`
|
||||
exec 2>&1
|
||||
./testsudoers -U $MYUID -G $MYGID root id <<EOF
|
||||
#include $TESTDIR/test2.inc
|
||||
EOF
|
||||
|
||||
exit 0
|
@ -68,11 +68,12 @@
|
||||
* 45 sudo 1.8.15, added FOLLOW/NOFOLLOW tags as well as sudoedit_follow and sudoedit_checkdir Defaults.
|
||||
* 46 sudo 1.8.20, added TIMEOUT, NOTBEFORE and NOTAFTER options.
|
||||
* 47 sudo 1.9.0, Cmd_Alias treated as Cmnd_Alias, support for multiple digests per command and for ALL.
|
||||
* 48 sudo 1.9.1, @include and @includedir, include path escaping/quoting.
|
||||
*/
|
||||
|
||||
#ifndef SUDOERS_VERSION_H
|
||||
#define SUDOERS_VERSION_H
|
||||
|
||||
#define SUDOERS_GRAMMAR_VERSION 47
|
||||
#define SUDOERS_GRAMMAR_VERSION 48
|
||||
|
||||
#endif /* SUDOERS_VERSION_H */
|
||||
|
@ -450,12 +450,14 @@ open_sudoers(const char *sudoers, bool doedit, bool *keepopen)
|
||||
{
|
||||
struct stat sb;
|
||||
FILE *fp = NULL;
|
||||
char *sudoers_base;
|
||||
const char *sudoers_base;
|
||||
debug_decl(open_sudoers, SUDOERS_DEBUG_UTIL);
|
||||
|
||||
sudoers_base = strrchr(sudoers, '/');
|
||||
if (sudoers_base != NULL)
|
||||
sudoers_base++;
|
||||
else
|
||||
sudoers_base = sudoers;
|
||||
|
||||
switch (sudo_secure_file(sudoers, sudoers_uid, sudoers_gid, &sb)) {
|
||||
case SUDO_PATH_SECURE:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -24,8 +24,9 @@ bool fill_args(const char *, size_t, int);
|
||||
bool fill_cmnd(const char *, size_t);
|
||||
bool fill_txt(const char *, size_t, size_t);
|
||||
bool ipv6_valid(const char *s);
|
||||
int sudoers_trace_print(const char *msg);
|
||||
int sudoers_trace_print(const char *);
|
||||
void sudoerserror(const char *);
|
||||
bool push_include(const char *, bool);
|
||||
|
||||
#ifndef FLEX_SCANNER
|
||||
extern int (*trace_print)(const char *msg);
|
||||
|
@ -64,9 +64,7 @@ static bool continued, sawspace;
|
||||
static int prev_state;
|
||||
static int digest_type = -1;
|
||||
|
||||
static bool push_include_int(char *, bool);
|
||||
static bool pop_include(void);
|
||||
static char *parse_include_int(const char *, bool);
|
||||
|
||||
int (*trace_print)(const char *msg) = sudoers_trace_print;
|
||||
|
||||
@ -76,11 +74,6 @@ int (*trace_print)(const char *msg) = sudoers_trace_print;
|
||||
} while (0)
|
||||
|
||||
#define ECHO ignore_result(fwrite(sudoerstext, sudoersleng, 1, sudoersout))
|
||||
|
||||
#define parse_include(_p) (parse_include_int((_p), false))
|
||||
#define parse_includedir(_p) (parse_include_int((_p), true))
|
||||
#define push_include(_p) (push_include_int((_p), false))
|
||||
#define push_includedir(_p) (push_include_int((_p), true))
|
||||
%}
|
||||
|
||||
HEX16 [0-9A-Fa-f]{1,4}
|
||||
@ -106,6 +99,7 @@ DEFVAR [a-z_]+
|
||||
%x INDEFS
|
||||
%x INSTR
|
||||
%s WANTDIGEST
|
||||
%x GOTINC
|
||||
|
||||
%%
|
||||
<GOTDEFS>[[:blank:]]*,[[:blank:]]* {
|
||||
@ -280,43 +274,54 @@ DEFVAR [a-z_]+
|
||||
yyless(sudoersleng);
|
||||
} /* base64 digest */
|
||||
|
||||
<INITIAL>^#include[[:blank:]]+.*(\r\n|\n)? {
|
||||
char *path;
|
||||
|
||||
<INITIAL>@include {
|
||||
if (continued) {
|
||||
LEXTRACE("ERROR ");
|
||||
LEXRETURN(ERROR);
|
||||
}
|
||||
|
||||
if ((path = parse_include(sudoerstext)) == NULL)
|
||||
yyterminate();
|
||||
BEGIN GOTINC;
|
||||
LEXTRACE("INCLUDE ");
|
||||
LEXRETURN(INCLUDE);
|
||||
}
|
||||
|
||||
LEXTRACE("INCLUDE\n");
|
||||
<INITIAL>@includedir {
|
||||
if (continued) {
|
||||
LEXTRACE("ERROR ");
|
||||
LEXRETURN(ERROR);
|
||||
}
|
||||
|
||||
/* Push current buffer and switch to include file */
|
||||
if (!push_include(path))
|
||||
yyterminate();
|
||||
BEGIN GOTINC;
|
||||
LEXTRACE("INCLUDEDIR ");
|
||||
LEXRETURN(INCLUDEDIR);
|
||||
}
|
||||
|
||||
<INITIAL>^#include[[:blank:]]+.*(\r\n|\n)? {
|
||||
if (continued) {
|
||||
LEXTRACE("ERROR ");
|
||||
LEXRETURN(ERROR);
|
||||
}
|
||||
|
||||
/* only consume #include */
|
||||
yyless(sizeof("#include") - 1);
|
||||
|
||||
BEGIN GOTINC;
|
||||
LEXTRACE("INCLUDE ");
|
||||
LEXRETURN(INCLUDE);
|
||||
}
|
||||
|
||||
<INITIAL>^#includedir[[:blank:]]+.*(\r\n|\n)? {
|
||||
char *path;
|
||||
|
||||
if (continued) {
|
||||
LEXTRACE("ERROR ");
|
||||
LEXRETURN(ERROR);
|
||||
}
|
||||
|
||||
if ((path = parse_includedir(sudoerstext)) == NULL)
|
||||
yyterminate();
|
||||
/* only consume #includedir */
|
||||
yyless(sizeof("#includedir") - 1);
|
||||
|
||||
LEXTRACE("INCLUDEDIR\n");
|
||||
|
||||
/*
|
||||
* Push current buffer and switch to include file,
|
||||
* ignoring missing or empty directories.
|
||||
*/
|
||||
if (!push_includedir(path))
|
||||
yyterminate();
|
||||
BEGIN GOTINC;
|
||||
LEXTRACE("INCLUDEDIR ");
|
||||
LEXRETURN(INCLUDEDIR);
|
||||
}
|
||||
|
||||
<INITIAL>^[[:blank:]]*Defaults([:@>\!][[:blank:]]*\!*\"?({ID}|{WORD}))? {
|
||||
@ -638,7 +643,7 @@ sudoedit {
|
||||
}
|
||||
} /* a pathname */
|
||||
|
||||
<INITIAL,GOTDEFS>\" {
|
||||
<INITIAL,GOTDEFS>\" {
|
||||
LEXTRACE("BEGINSTR ");
|
||||
sudoerslval.string = NULL;
|
||||
prev_state = YY_START;
|
||||
@ -653,6 +658,24 @@ sudoedit {
|
||||
LEXRETURN(WORD);
|
||||
}
|
||||
|
||||
<GOTINC>{
|
||||
[^\"[:space:]]([^[:space:]]|\\[[:blank:]])* {
|
||||
/* include file/directory */
|
||||
if (!fill(sudoerstext, sudoersleng))
|
||||
yyterminate();
|
||||
BEGIN INITIAL;
|
||||
LEXTRACE("WORD(6) ");
|
||||
LEXRETURN(WORD);
|
||||
}
|
||||
|
||||
\" {
|
||||
LEXTRACE("BEGINSTR ");
|
||||
sudoerslval.string = NULL;
|
||||
prev_state = INITIAL;
|
||||
BEGIN INSTR;
|
||||
}
|
||||
}
|
||||
|
||||
\( {
|
||||
LEXTRACE("( ");
|
||||
LEXRETURN('(');
|
||||
@ -914,18 +937,91 @@ init_lexer(void)
|
||||
debug_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand any embedded %h (host) escapes in the given path and makes
|
||||
* a relative path fully-qualified based on the current sudoers file.
|
||||
* Returns a reference-counted string.
|
||||
*/
|
||||
static char *
|
||||
expand_include(const char *opath, size_t olen)
|
||||
{
|
||||
const char *cp, *ep;
|
||||
char *path, *pp;
|
||||
int dirlen = 0, len;
|
||||
size_t shost_len = 0;
|
||||
bool subst = false;
|
||||
debug_decl(expand_include, SUDOERS_DEBUG_PARSER);
|
||||
|
||||
/* Strip double quotes if present. */
|
||||
if (*opath == '"') {
|
||||
opath++;
|
||||
olen -= 2;
|
||||
}
|
||||
|
||||
/* Relative paths are located in the same dir as the sudoers file. */
|
||||
if (*opath != '/') {
|
||||
char *dirend = strrchr(sudoers, '/');
|
||||
if (dirend != NULL)
|
||||
dirlen = (int)(dirend - sudoers) + 1;
|
||||
}
|
||||
|
||||
len = olen;
|
||||
for (cp = opath, ep = opath + olen; cp < ep; cp++) {
|
||||
if (cp[0] == '%' && cp[1] == 'h') {
|
||||
shost_len = strlen(user_shost);
|
||||
len += shost_len - 2;
|
||||
subst = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make a copy of the fully-qualified path and return it. */
|
||||
path = pp = rcstr_alloc(len + dirlen);
|
||||
if (path == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
sudoerserror(NULL);
|
||||
debug_return_str(NULL);
|
||||
}
|
||||
if (dirlen) {
|
||||
memcpy(path, sudoers, dirlen);
|
||||
pp += dirlen;
|
||||
}
|
||||
if (subst) {
|
||||
/* substitute for %h */
|
||||
cp = opath;
|
||||
while (cp < ep) {
|
||||
if (cp[0] == '%' && cp[1] == 'h') {
|
||||
memcpy(pp, user_shost, shost_len);
|
||||
pp += shost_len;
|
||||
cp += 2;
|
||||
continue;
|
||||
}
|
||||
*pp++ = *cp++;
|
||||
}
|
||||
*pp = '\0';
|
||||
} else {
|
||||
memcpy(pp, opath, len);
|
||||
pp[len] = '\0';
|
||||
}
|
||||
|
||||
debug_return_str(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Open an include file (or file from a directory), push the old
|
||||
* sudoers file buffer and switch to the new one.
|
||||
* A missing or insecure include dir is simply ignored.
|
||||
* Returns false on error, else true.
|
||||
*/
|
||||
static bool
|
||||
push_include_int(char *path, bool isdir)
|
||||
bool
|
||||
push_include(const char *opath, bool isdir)
|
||||
{
|
||||
struct path_list *pl;
|
||||
char *path;
|
||||
FILE *fp;
|
||||
debug_decl(push_include_int, SUDOERS_DEBUG_PARSER);
|
||||
debug_decl(push_include, SUDOERS_DEBUG_PARSER);
|
||||
|
||||
if ((path = expand_include(opath, strlen(opath))) == NULL)
|
||||
debug_return_bool(false);
|
||||
|
||||
/* push current state onto stack */
|
||||
if (idepth >= istacksize) {
|
||||
@ -1065,72 +1161,6 @@ pop_include(void)
|
||||
debug_return_bool(true);
|
||||
}
|
||||
|
||||
static char *
|
||||
parse_include_int(const char *base, bool isdir)
|
||||
{
|
||||
const char *cp, *ep;
|
||||
char *path, *pp;
|
||||
int dirlen = 0, len = 0, subst = 0;
|
||||
size_t shost_len = 0;
|
||||
debug_decl(parse_include, SUDOERS_DEBUG_PARSER);
|
||||
|
||||
/* Pull out path from #include line. */
|
||||
cp = base + (isdir ? sizeof("#includedir") : sizeof("#include"));
|
||||
while (isblank((unsigned char) *cp))
|
||||
cp++;
|
||||
ep = cp;
|
||||
while (*ep != '\0' && !isspace((unsigned char) *ep)) {
|
||||
if (ep[0] == '%' && ep[1] == 'h') {
|
||||
shost_len = strlen(user_shost);
|
||||
len += shost_len - 2;
|
||||
subst = 1;
|
||||
}
|
||||
ep++;
|
||||
}
|
||||
|
||||
/* Relative paths are located in the same dir as the sudoers file. */
|
||||
if (*cp != '/') {
|
||||
char *dirend = strrchr(sudoers, '/');
|
||||
if (dirend != NULL)
|
||||
dirlen = (int)(dirend - sudoers) + 1;
|
||||
}
|
||||
|
||||
/* Make a copy of the fully-qualified path and return it. */
|
||||
len += (int)(ep - cp);
|
||||
path = pp = rcstr_alloc(len + dirlen);
|
||||
if (path == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
sudoerserror(NULL);
|
||||
debug_return_str(NULL);
|
||||
}
|
||||
if (dirlen) {
|
||||
memcpy(path, sudoers, dirlen);
|
||||
pp += dirlen;
|
||||
}
|
||||
if (subst) {
|
||||
/* substitute for %h */
|
||||
while (cp < ep) {
|
||||
if (cp[0] == '%' && cp[1] == 'h') {
|
||||
memcpy(pp, user_shost, shost_len);
|
||||
pp += shost_len;
|
||||
cp += 2;
|
||||
continue;
|
||||
}
|
||||
*pp++ = *cp++;
|
||||
}
|
||||
*pp = '\0';
|
||||
} else {
|
||||
memcpy(pp, cp, len);
|
||||
pp[len] = '\0';
|
||||
}
|
||||
|
||||
/* Push any excess characters (e.g. comment, newline) back to the lexer */
|
||||
if (*ep != '\0')
|
||||
yyless((int)(ep - base));
|
||||
|
||||
debug_return_str(path);
|
||||
}
|
||||
|
||||
#ifdef TRACELEXER
|
||||
int
|
||||
sudoers_trace_print(const char *msg)
|
||||
|
Loading…
x
Reference in New Issue
Block a user