]> granicus.if.org Git - sudo/commitdiff
Initial support filtering by user, group and host in cvtsudoers.
authorTodd C. Miller <Todd.Miller@sudo.ws>
Wed, 21 Mar 2018 18:24:11 +0000 (12:24 -0600)
committerTodd C. Miller <Todd.Miller@sudo.ws>
Wed, 21 Mar 2018 18:24:11 +0000 (12:24 -0600)
Currently forces alias expansion when a filter is applied and the
entire matching user or host list is printed, even the non-matching
entries.  This effectively allows you to grep sudoers by user, group
and host.

doc/cvtsudoers.cat
doc/cvtsudoers.man.in
doc/cvtsudoers.mdoc.in
plugins/sudoers/Makefile.in
plugins/sudoers/alias.c
plugins/sudoers/cvtsudoers.c
plugins/sudoers/cvtsudoers.h
plugins/sudoers/cvtsudoers_ldif.c
plugins/sudoers/cvtsudoers_pwutil.c [new file with mode: 0644]
plugins/sudoers/parse.h

index 006581a4869e8d793ae6b5fd4d7afac5b9c06b19..e6361f58c22080c6465fe08cdcfe5ef095e15f0d 100644 (file)
@@ -4,9 +4,9 @@ N\bNA\bAM\bME\bE
      c\bcv\bvt\bts\bsu\bud\bdo\boe\ber\brs\bs - convert between sudoers file formats
 
 S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS
-     c\bcv\bvt\bts\bsu\bud\bdo\boe\ber\brs\bs [-\b-e\beh\bhV\bV] [-\b-b\bb _\bd_\bn] [-\b-c\bc _\bc_\bo_\bn_\bf_\b__\bf_\bi_\bl_\be] [-\b-I\bI _\bi_\bn_\bc_\br_\be_\bm_\be_\bn_\bt] [-\b-i\bi _\bi_\bn_\bp_\bu_\bt_\b__\bf_\bo_\br_\bm_\ba_\bt]
-                [-\b-f\bf _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bo_\br_\bm_\ba_\bt] [-\b-O\bO _\bs_\bt_\ba_\br_\bt_\b__\bp_\bo_\bi_\bn_\bt] [-\b-o\bo _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be]
-                [_\bi_\bn_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be]
+     c\bcv\bvt\bts\bsu\bud\bdo\boe\ber\brs\bs [-\b-e\beh\bhV\bV] [-\b-b\bb _\bd_\bn] [-\b-c\bc _\bc_\bo_\bn_\bf_\b__\bf_\bi_\bl_\be] [-\b-f\bf _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bo_\br_\bm_\ba_\bt]
+                [-\b-i\bi _\bi_\bn_\bp_\bu_\bt_\b__\bf_\bo_\br_\bm_\ba_\bt] [-\b-I\bI _\bi_\bn_\bc_\br_\be_\bm_\be_\bn_\bt] [-\b-m\bm _\bf_\bi_\bl_\bt_\be_\br] [-\b-o\bo _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be]
+                [-\b-O\bO _\bs_\bt_\ba_\br_\bt_\b__\bp_\bo_\bi_\bn_\bt] [_\bi_\bn_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be]
 
 D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
      c\bcv\bvt\bts\bsu\bud\bdo\boe\ber\brs\bs can be used to convert between _\bs_\bu_\bd_\bo_\be_\br_\bs security policy file
@@ -52,14 +52,13 @@ D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
 
                            Conversion to LDIF has the following limitations:
 
-                           +\b+\bo\bo     Command, host, runas and user-specific
-                                 Defaults lines cannot be translated as they
-                                 don't have an equivalent in the sudoers LDAP
-                                 schema.
+                           +\b+\bo\bo  Command, host, runas and user-specific Defaults
+                              lines cannot be translated as they don't have an
+                              equivalent in the sudoers LDAP schema.
 
-                           +\b+\bo\bo     Command, host, runas and user aliases are not
-                                 supported by the sudoers LDAP schema so they
-                                 are expanded during the conversion.
+                           +\b+\bo\bo  Command, host, runas and user aliases are not
+                              supported by the sudoers LDAP schema so they are
+                              expanded during the conversion.
 
                  sudoers   Traditional sudoers format.  A new sudoers file
                            will be reconstructed from the parsed input file.
@@ -68,11 +67,6 @@ D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
 
      -\b-h\bh, -\b--\b-h\bhe\bel\blp\bp  Display a short help message to the standard output and exit.
 
-     -\b-I\bI _\bi_\bn_\bc_\br_\be_\bm_\be_\bn_\bt, -\b--\b-i\bin\bnc\bcr\bre\bem\bme\ben\bnt\bt=_\bi_\bn_\bc_\br_\be_\bm_\be_\bn_\bt
-                 When generating LDIF output, increment each sudoOrder
-                 attribute by the specified number.  Defaults to an increment
-                 of 1.
-
      -\b-i\bi _\bi_\bn_\bp_\bu_\bt_\b__\bf_\bo_\br_\bm_\ba_\bt, -\b--\b-i\bin\bnp\bpu\but\bt-\b-f\bfo\bor\brm\bma\bat\bt=_\bi_\bn_\bp_\bu_\bt_\b__\bf_\bo_\br_\bm_\ba_\bt
                  Specify the input format.  The following formats are
                  supported:
@@ -88,6 +82,28 @@ D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
                  sudoers   Traditional sudoers format.  This is the default
                            input format.
 
+     -\b-I\bI _\bi_\bn_\bc_\br_\be_\bm_\be_\bn_\bt, -\b--\b-i\bin\bnc\bcr\bre\bem\bme\ben\bnt\bt=_\bi_\bn_\bc_\br_\be_\bm_\be_\bn_\bt
+                 When generating LDIF output, increment each sudoOrder
+                 attribute by the specified number.  Defaults to an increment
+                 of 1.
+
+     -\b-m\bm _\bf_\bi_\bl_\bt_\be_\br, -\b--\b-m\bma\bat\btc\bch\bh=_\bf_\bi_\bl_\bt_\be_\br
+                 Only output rules that match the specified _\bf_\bi_\bl_\bt_\be_\br.  A _\bf_\bi_\bl_\bt_\be_\br
+                 expression is made up of one or more k\bke\bey\by =\b= _\bv_\ba_\bl_\bu_\be pairs,
+                 separated by a comma (`,').  The k\bke\bey\by may be "user", "group"
+                 or "host".  For example, u\bus\bse\ber\br = _\bo_\bp_\be_\br_\ba_\bt_\bo_\br or h\bho\bos\bst\bt = _\bw_\bw_\bw.
+
+                 The password and group databases are not consulted when
+                 matching against the filter so the users and groups do not
+                 need to be present on the local system.  Only aliases that
+                 are referenced by the filtered policy rules will be
+                 displayed.
+
+     -\b-o\bo _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be, -\b--\b-o\bou\but\btp\bpu\but\bt=_\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be
+                 Write the converted output to _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be.  If no _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be
+                 is specified, or if it is `-', the converted _\bs_\bu_\bd_\bo_\be_\br_\bs policy
+                 will be written to the standard output.
+
      -\b-O\bO _\bs_\bt_\ba_\br_\bt_\b__\bp_\bo_\bi_\bn_\bt, -\b--\b-o\bor\brd\bde\ber\br-\b-s\bst\bta\bar\brt\bt=_\bs_\bt_\ba_\br_\bt_\b__\bp_\bo_\bi_\bn_\bt
                  When generating LDIF output, use the number specified by
                  _\bs_\bt_\ba_\br_\bt_\b__\bp_\bo_\bi_\bn_\bt in the sudoOrder attribute of the first sudoRole
@@ -97,11 +113,6 @@ D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
                  point of 0 will disable the generation of sudoOrder
                  attributes in the resulting LDIF file.
 
-     -\b-o\bo _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be, -\b--\b-o\bou\but\btp\bpu\but\bt=_\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be
-                 Write the converted output to _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be.  If no _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be
-                 is specified, or if it is `-', the converted _\bs_\bu_\bd_\bo_\be_\br_\bs policy
-                 will be written to the standard output.
-
      -\b-V\bV, -\b--\b-v\bve\ber\brs\bsi\bio\bon\bn
                  Print the c\bcv\bvt\bts\bsu\bud\bdo\boe\ber\brs\bs and _\bs_\bu_\bd_\bo_\be_\br_\bs grammar versions and exit.
 
@@ -115,6 +126,9 @@ D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
      i\bin\bnp\bpu\but\bt_\b_f\bfo\bor\brm\bma\bat\bt =\b= _\bl_\bd_\bi_\bf | _\bs_\bu_\bd_\bo_\be_\br_\bs
            See the description of the -\b-i\bi command line option.
 
+     m\bma\bat\btc\bch\bh =\b= _\bf_\bi_\bl_\bt_\be_\br
+           See the description of the -\b-m\bm command line option.
+
      o\bor\brd\bde\ber\br_\b_i\bin\bnc\bcr\bre\bem\bme\ben\bnt\bt =\b= _\bi_\bn_\bc_\br_\be_\bm_\be_\bn_\bt
            See the description of the -\b-I\bI command line option.
 
@@ -162,4 +176,4 @@ D\bDI\bIS\bSC\bCL\bLA\bAI\bIM\bME\bER\bR
      file distributed with s\bsu\bud\bdo\bo or https://www.sudo.ws/license.html for
      complete details.
 
-Sudo 1.8.23                    February 23, 2018                   Sudo 1.8.23
+Sudo 1.8.23                     March 21, 2018                     Sudo 1.8.23
index 28c23bcd65fe6b2bd01a0fa5698c00ecbd8b05de..599f64e9dc9ca331a2ce30cbf02ec85b0be24678 100644 (file)
@@ -16,7 +16,7 @@
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.TH "CVTSUDOERS" "8" "February 23, 2018" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
+.TH "CVTSUDOERS" "8" "March 21, 2018" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
 .nh
 .if n .ad l
 .SH "NAME"
 [\fB\-ehV\fR]
 [\fB\-b\fR\ \fIdn\fR]
 [\fB\-c\fR\ \fIconf_file\fR]
-[\fB\-I\fR\ \fIincrement\fR]
-[\fB\-i\fR\ \fIinput_format\fR]
 [\fB\-f\fR\ \fIoutput_format\fR]
-[\fB\-O\fR\ \fIstart_point\fR]
+[\fB\-i\fR\ \fIinput_format\fR]
+[\fB\-I\fR\ \fIincrement\fR]
+[\fB\-m\fR\ \fIfilter\fR]
 [\fB\-o\fR\ \fIoutput_file\fR]
+[\fB\-O\fR\ \fIstart_point\fR]
 [\fIinput_file\fR]
 .SH "DESCRIPTION"
 \fBcvtsudoers\fR
@@ -105,12 +106,12 @@ Conversion to LDIF has the following limitations:
 .PP
 .RS 10n
 .PD 0
-.TP 6n
+.TP 3n
 \fB\(bu\fR
 Command, host, runas and user-specific Defaults lines cannot be
 translated as they don't have an equivalent in the sudoers LDAP schema.
 .PD
-.TP 6n
+.TP 3n
 \fB\(bu\fR
 Command, host, runas and user aliases are not supported by the
 sudoers LDAP schema so they are expanded during the conversion.
@@ -132,11 +133,6 @@ output inline.
 \fB\-h\fR, \fB\--help\fR
 Display a short help message to the standard output and exit.
 .TP 12n
-\fB\-I\fR \fIincrement\fR, \fB\--increment\fR=\fIincrement\fR
-When generating LDIF output, increment each sudoOrder attribute by
-the specified number.
-Defaults to an increment of 1.
-.TP 12n
 \fB\-i\fR \fIinput_format\fR, \fB\--input-format\fR=\fIinput_format\fR
 Specify the input format.
 The following formats are supported:
@@ -162,6 +158,49 @@ This is the default input format.
 .RE
 .PD
 .TP 12n
+\fB\-I\fR \fIincrement\fR, \fB\--increment\fR=\fIincrement\fR
+When generating LDIF output, increment each sudoOrder attribute by
+the specified number.
+Defaults to an increment of 1.
+.TP 12n
+\fB\-m\fR \fIfilter\fR, \fB\--match\fR=\fIfilter\fR
+Only output rules that match the specified
+\fIfilter\fR.
+A
+\fIfilter\fR
+expression is made up of one or more
+\fBkey =\fR \fIvalue\fR
+pairs, separated by a comma
+(\(oq\&,\(cq).
+The
+\fBkey\fR
+may be
+\(Lquser\(Rq,
+\(Lqgroup\(Rq
+or
+\(Lqhost\(Rq.
+For example,
+\fBuser\fR = \fIoperator\fR
+or
+\fBhost\fR = \fIwww\fR.
+.sp
+The password and group databases are not consulted when matching
+against the filter so the users and groups do not need to be present
+on the local system.
+Only aliases that are referenced by the filtered policy rules will
+be displayed.
+.TP 12n
+\fB\-o\fR \fIoutput_file\fR, \fB\--output\fR=\fIoutput_file\fR
+Write the converted output to
+\fIoutput_file\fR.
+If no
+\fIoutput_file\fR
+is specified, or if it is
+\(oq-\(cq,
+the converted
+\fIsudoers\fR
+policy will be written to the standard output.
+.TP 12n
 \fB\-O\fR \fIstart_point\fR, \fB\--order-start\fR=\fIstart_point\fR
 When generating LDIF output, use the number specified by
 \fIstart_point\fR
@@ -175,17 +214,6 @@ Defaults to a starting point of 1.
 A starting point of 0 will disable the generation of sudoOrder
 attributes in the resulting LDIF file.
 .TP 12n
-\fB\-o\fR \fIoutput_file\fR, \fB\--output\fR=\fIoutput_file\fR
-Write the converted output to
-\fIoutput_file\fR.
-If no
-\fIoutput_file\fR
-is specified, or if it is
-\(oq-\(cq,
-the converted
-\fIsudoers\fR
-policy will be written to the standard output.
-.TP 12n
 \fB\-V\fR, \fB\--version\fR
 Print the
 \fBcvtsudoers\fR
@@ -210,6 +238,11 @@ See the description of the
 \fB\-i\fR
 command line option.
 .TP 6n
+\fBmatch =\fR \fIfilter\fR
+See the description of the
+\fB\-m\fR
+command line option.
+.TP 6n
 \fBorder_increment =\fR \fIincrement\fR
 See the description of the
 \fB\-I\fR
index 6a1df133d60a85b1ad9467fdae97339304b33b04..81e492c1d709096d2a49efd2d1457af38b2933be 100644 (file)
@@ -14,7 +14,7 @@
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd February 23, 2018
+.Dd March 21, 2018
 .Dt CVTSUDOERS @mansectsu@
 .Os Sudo @PACKAGE_VERSION@
 .Sh NAME
 .Op Fl ehV
 .Op Fl b Ar dn
 .Op Fl c Ar conf_file
-.Op Fl I Ar increment
-.Op Fl i Ar input_format
 .Op Fl f Ar output_format
-.Op Fl O Ar start_point
+.Op Fl i Ar input_format
+.Op Fl I Ar increment
+.Op Fl m Ar filter
 .Op Fl o Ar output_file
+.Op Fl O Ar start_point
 .Op Ar input_file
 .Sh DESCRIPTION
 .Nm
@@ -91,7 +92,7 @@ server for use with
 .Xr sudoers.ldap @mansectform@ .
 .Pp
 Conversion to LDIF has the following limitations:
-.Bl -bullet -width 4n
+.Bl -bullet -width 1n
 .It
 Command, host, runas and user-specific Defaults lines cannot be
 translated as they don't have an equivalent in the sudoers LDAP schema.
@@ -107,10 +108,6 @@ output inline.
 .El
 .It Fl h , Fl -help
 Display a short help message to the standard output and exit.
-.It Fl I Ar increment , Fl -increment Ns = Ns Ar increment
-When generating LDIF output, increment each sudoOrder attribute by
-the specified number.
-Defaults to an increment of 1.
 .It Fl i Ar input_format , Fl -input-format Ns = Ns Ar input_format
 Specify the input format.
 The following formats are supported:
@@ -127,6 +124,46 @@ LDIF to sudoers format.
 Traditional sudoers format.
 This is the default input format.
 .El
+.It Fl I Ar increment , Fl -increment Ns = Ns Ar increment
+When generating LDIF output, increment each sudoOrder attribute by
+the specified number.
+Defaults to an increment of 1.
+.It Fl m Ar filter , Fl -match Ns = Ns Ar filter
+Only output rules that match the specified
+.Ar filter .
+A
+.Ar filter
+expression is made up of one or more
+.Sy key = Ar value
+pairs, separated by a comma
+.Pq Ql \&, .
+The
+.Sy key
+may be
+.Dq user ,
+.Dq group
+or
+.Dq host .
+For example,
+.Sy user No = Ar operator
+or
+.Sy host No = Ar www .
+.Pp
+The password and group databases are not consulted when matching
+against the filter so the users and groups do not need to be present
+on the local system.
+Only aliases that are referenced by the filtered policy rules will
+be displayed.
+.It Fl o Ar output_file , Fl -output Ns = Ns Ar output_file
+Write the converted output to
+.Ar output_file .
+If no
+.Ar output_file
+is specified, or if it is
+.Ql - ,
+the converted
+.Em sudoers
+policy will be written to the standard output.
 .It Fl O Ar start_point , Fl -order-start Ns = Ns Ar start_point
 When generating LDIF output, use the number specified by
 .Ar start_point
@@ -139,16 +176,6 @@ option for details.
 Defaults to a starting point of 1.
 A starting point of 0 will disable the generation of sudoOrder
 attributes in the resulting LDIF file.
-.It Fl o Ar output_file , Fl -output Ns = Ns Ar output_file
-Write the converted output to
-.Ar output_file .
-If no
-.Ar output_file
-is specified, or if it is
-.Ql - ,
-the converted
-.Em sudoers
-policy will be written to the standard output.
 .It Fl V , -version
 Print the
 .Nm
@@ -172,6 +199,10 @@ command line option.
 See the description of the
 .Fl i
 command line option.
+.It Sy match = Ar filter
+See the description of the
+.Fl m
+command line option.
 .It Sy order_increment = Ar increment
 See the description of the
 .Fl I
index 9bc6fd046640101754058310637377f657adf9d4..c540f1645f7d893223a572ad37963fe2a9b8ce1f 100644 (file)
@@ -163,7 +163,8 @@ SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo editor.lo env.lo \
 VISUDO_OBJS = editor.o find_path.o goodpath.o locale.o stubs.o sudo_printf.o visudo.o
 
 CVTSUDOERS_OBJS = cvtsudoers.o cvtsudoers_json.o cvtsudoers_ldif.o \
-                 fmtsudoers.o locale.o stubs.o sudo_printf.o ldap_util.o
+                 cvtsudoers_pwutil.o fmtsudoers.o locale.o stubs.o \
+                 sudo_printf.o ldap_util.o
 
 REPLAY_OBJS = getdate.o sudoreplay.o
 
@@ -747,14 +748,26 @@ cvtsudoers_ldif.o: $(srcdir)/cvtsudoers_ldif.c $(devdir)/def_data.h \
                    $(devdir)/gram.h $(incdir)/compat/stdbool.h \
                    $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
                    $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \
-                   $(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \
-                   $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
-                   $(srcdir)/cvtsudoers.h $(srcdir)/defaults.h \
-                   $(srcdir)/logging.h $(srcdir)/parse.h $(srcdir)/redblack.h \
-                   $(srcdir)/sudo_ldap.h $(srcdir)/sudo_nss.h \
-                   $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \
-                   $(top_builddir)/config.h $(top_builddir)/pathnames.h
+                   $(incdir)/sudo_gettext.h $(incdir)/sudo_lbuf.h \
+                   $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
+                   $(incdir)/sudo_util.h $(srcdir)/cvtsudoers.h \
+                   $(srcdir)/defaults.h $(srcdir)/logging.h $(srcdir)/parse.h \
+                   $(srcdir)/redblack.h $(srcdir)/sudo_ldap.h \
+                   $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
+                   $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
+                   $(top_builddir)/pathnames.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/cvtsudoers_ldif.c
+cvtsudoers_pwutil.o: $(srcdir)/cvtsudoers_pwutil.c $(devdir)/def_data.h \
+                     $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
+                     $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
+                     $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
+                     $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
+                     $(incdir)/sudo_util.h $(srcdir)/cvtsudoers.h \
+                     $(srcdir)/defaults.h $(srcdir)/logging.h \
+                     $(srcdir)/pwutil.h $(srcdir)/sudo_nss.h \
+                     $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \
+                     $(top_builddir)/config.h $(top_builddir)/pathnames.h
+       $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/cvtsudoers_pwutil.c
 dce.lo: $(authdir)/dce.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \
         $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
         $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
@@ -1321,10 +1334,10 @@ sudoreplay.o: $(srcdir)/sudoreplay.c $(incdir)/compat/getopt.h \
               $(top_builddir)/pathnames.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/sudoreplay.c
 testsudoers.o: $(srcdir)/testsudoers.c $(devdir)/def_data.h $(devdir)/gram.h \
-               $(incdir)/compat/fnmatch.h $(incdir)/compat/stdbool.h \
-               $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
-               $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \
-               $(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \
+               $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
+               $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
+               $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
+               $(incdir)/sudo_lbuf.h $(incdir)/sudo_plugin.h \
                $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
                $(srcdir)/defaults.h $(srcdir)/interfaces.h $(srcdir)/logging.h \
                $(srcdir)/parse.h $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
index b8e70fc7aa98d1c8ffe5dc6aa32edd59a95eae18..efd380f7e3060a0e02eebd0606bffa75c7b983cb 100644 (file)
@@ -39,7 +39,7 @@
 /*
  * Globals
  */
-struct rbtree *aliases;
+static struct rbtree *aliases;
 
 /*
  * Comparison function for the red-black tree.
@@ -166,6 +166,20 @@ no_aliases(void)
     debug_return_bool(rbisempty(aliases));
 }
 
+/*
+ * Replace the aliases tree with a new one, returns the old.
+ */
+struct rbtree *
+replace_aliases(struct rbtree *new_aliases)
+{
+    struct rbtree *old_aliases = aliases;
+    debug_decl(replace_aliases, SUDOERS_DEBUG_ALIAS)
+
+    aliases = new_aliases;
+
+    debug_return_ptr(old_aliases);
+}
+
 /*
  * Free memory used by an alias struct and its members.
  */
index e3fd021539009e4b570193565d463c4902bda265..cbbc0803ecf0a4dcfe7f84742d988dd59399f91f 100644 (file)
@@ -32,6 +32,7 @@
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <pwd.h>
 #include <unistd.h>
 
 #include "sudoers.h"
@@ -39,6 +40,7 @@
 #include "sudoers_version.h"
 #include "sudo_conf.h"
 #include "sudo_lbuf.h"
+#include "redblack.h"
 #include "cvtsudoers.h"
 #include <gram.h>
 
 /*
  * Globals
  */
+struct cvtsudoers_filter *filters;
 struct sudo_user sudo_user;
 struct passwd *list_pw;
-static const char short_opts[] =  "b:c:ef:hi:I:o:O:V";
+static const char short_opts[] =  "b:c:ef:hi:I:m:o:O:V";
 static struct option long_opts[] = {
     { "base",          required_argument,      NULL,   'b' },
     { "config",                required_argument,      NULL,   'c' },
@@ -62,6 +65,7 @@ static struct option long_opts[] = {
     { "help",          no_argument,            NULL,   'h' },
     { "input-format",  required_argument,      NULL,   'i' },
     { "increment",     required_argument,      NULL,   'I' },
+    { "match",         required_argument,      NULL,   'm' },
     { "order-start",   required_argument,      NULL,   'O' },
     { "output",                required_argument,      NULL,   'o' },
     { "version",       no_argument,            NULL,   'V' },
@@ -73,8 +77,12 @@ static void help(void) __attribute__((__noreturn__));
 static void usage(int);
 static bool convert_sudoers_sudoers(const char *output_file, struct cvtsudoers_config *conf);
 static bool parse_sudoers(const char *input_file, struct cvtsudoers_config *conf);
+static bool parse_filter(char *expression);
+static bool alias_remove_unused(void);
 static struct cvtsudoers_config *cvtsudoers_conf_read(const char *conf_file);
 static void cvtsudoers_conf_free(struct cvtsudoers_config *conf);
+static void filter_userspecs(void);
+static void filter_defaults(void);
 
 int
 main(int argc, char *argv[])
@@ -174,6 +182,9 @@ main(int argc, char *argv[])
                usage(1);
            }
            break;
+       case 'm':
+           conf->filter = optarg;
+           break;
        case 'o':
            output_file = optarg;
            break;
@@ -223,6 +234,11 @@ main(int argc, char *argv[])
            usage(1);
        }
     }
+    if (conf->filter != NULL) {
+       /* We always expand aliases when filtering (may change in future). */
+       if (!parse_filter(conf->filter))
+           usage(1);
+    }
 
     /* If no base DN specified, check SUDOERS_BASE. */
     if (conf->sudoers_base == NULL) {
@@ -269,6 +285,15 @@ main(int argc, char *argv[])
        sudo_fatalx("error: unhandled input %d", input_format);
     }
 
+    /* Apply filters. */
+    if (conf->filter != NULL) {
+           filter_userspecs();
+
+           filter_defaults();
+
+           alias_remove_unused();
+    }
+
     switch (output_format) {
     case format_json:
        exitcode = !convert_sudoers_json(output_file, conf);
@@ -299,6 +324,7 @@ static struct cvtsudoers_conf_table cvtsudoers_conf_vars[] = {
     { "sudoers_base", CONF_STR, &cvtsudoers_config.sudoers_base },
     { "input_format", CONF_STR, &cvtsudoers_config.input_format },
     { "output_format", CONF_STR, &cvtsudoers_config.output_format },
+    { "match", CONF_STR, &cvtsudoers_config.filter },
     { "expand_aliases", CONF_BOOL, &cvtsudoers_config.expand_aliases }
 };
 
@@ -410,6 +436,60 @@ cvtsudoers_conf_free(struct cvtsudoers_config *conf)
     debug_return;
 }
 
+static bool
+parse_filter(char *expression)
+{
+    char *last = NULL, *cp = expression;
+    debug_decl(parse_filter, SUDOERS_DEBUG_UTIL)
+
+    if (filters == NULL) {
+       if ((filters = malloc(sizeof(*filters))) == NULL) {
+           sudo_fatalx(U_("%s: %s"), __func__,
+               U_("unable to allocate memory"));
+       }
+       STAILQ_INIT(&filters->users);
+       STAILQ_INIT(&filters->groups);
+       STAILQ_INIT(&filters->hosts);
+    }
+
+    for ((cp = strtok_r(cp, ",", &last)); cp != NULL; (cp = strtok_r(NULL, ",", &last))) {
+       /*
+        * Filter expression:
+        *      user=foo,group=bar,host=baz
+        */
+       char *keyword;
+       struct cvtsudoers_string *s;
+
+       if ((s = malloc(sizeof(*s))) == NULL) {
+           sudo_fatalx(U_("%s: %s"), __func__,
+               U_("unable to allocate memory"));
+       }
+
+       /* Parse keyword = value */
+       keyword = cp;
+       if ((cp = strchr(cp, '=')) == NULL) {
+           sudo_warnx(U_("invalid filter: %s"), keyword);;
+           debug_return_bool(false);
+       }
+       *cp++ = '\0';
+       s->str = cp;
+
+       if (strcmp(keyword, "user") == 0 ){
+           STAILQ_INSERT_TAIL(&filters->users, s, entries);
+       } else if (strcmp(keyword, "group") == 0 ){
+           STAILQ_INSERT_TAIL(&filters->groups, s, entries);
+       } else if (strcmp(keyword, "host") == 0 ){
+           STAILQ_INSERT_TAIL(&filters->hosts, s, entries);
+       } else {
+           sudo_warnx(U_("invalid filter: %s"), keyword);;
+           free(s);
+           debug_return_bool(false);
+       }
+    }
+
+    debug_return_bool(true);
+}
+
 static bool
 parse_sudoers(const char *input_file, struct cvtsudoers_config *conf)
 {
@@ -446,6 +526,89 @@ open_sudoers(const char *sudoers, bool doedit, bool *keepopen)
     return fopen(sudoers, "r");
 }
 
+bool
+userlist_matches_filter(struct member_list *userlist)
+{
+    struct cvtsudoers_string *s;
+    bool matches = false;
+    debug_decl(userlist_matches_filter, SUDOERS_DEBUG_UTIL)
+
+    if (filters == NULL ||
+       (STAILQ_EMPTY(&filters->users) && STAILQ_EMPTY(&filters->groups)))
+       debug_return_bool(true);
+
+    if (STAILQ_EMPTY(&filters->users)) {
+       struct passwd pw;
+
+       /*
+        * Only groups in filter, make a dummy user so userlist_matches()
+        * can do its thing.
+        */
+       memset(&pw, 0, sizeof(pw));
+       pw.pw_name = "_nobody";
+       pw.pw_uid = (uid_t)-1;
+       pw.pw_gid = (gid_t)-1;
+       if (userlist_matches(&pw, userlist) == true)
+           matches = true;
+    }
+
+    STAILQ_FOREACH(s, &filters->users, entries) {
+       struct passwd *pw = NULL;
+        if (s->str[0] == '#') {
+           const char *errstr;
+            uid_t uid = sudo_strtoid(s->str + 1, NULL, NULL, &errstr);
+            if (errstr == NULL)
+                pw = sudo_getpwuid(uid);
+       }
+       if (pw == NULL)
+           pw = sudo_getpwnam(s->str);
+       if (pw == NULL)
+           continue;
+       if (userlist_matches(pw, userlist) == true)
+           matches = true;
+       sudo_pw_delref(pw);
+       if (matches)
+           break;
+    }
+
+    debug_return_bool(matches);
+}
+
+bool
+hostlist_matches_filter(struct member_list *hostlist)
+{
+    struct cvtsudoers_string *s;
+    bool matches = false;
+    debug_decl(hostlist_matches_filter, SUDOERS_DEBUG_UTIL)
+
+    if (filters == NULL || STAILQ_EMPTY(&filters->hosts))
+       debug_return_bool(true);
+
+    STAILQ_FOREACH(s, &filters->hosts, entries) {
+       user_runhost = s->str;
+       if ((user_srunhost = strchr(user_runhost, '.')) != NULL) {
+           user_srunhost = strndup(user_runhost,
+               (size_t)(user_srunhost - user_runhost));
+           if (user_srunhost == NULL) {
+               sudo_fatalx(U_("%s: %s"), __func__,
+                   U_("unable to allocate memory"));
+           }
+       } else {
+           user_srunhost = user_runhost;
+       }
+       /* XXX - can't use netgroup_tuple with NULL pw */
+       if (hostlist_matches(NULL, hostlist) == true)
+           matches = true;
+       if (user_srunhost != user_runhost)
+           free(user_srunhost);
+       user_runhost = user_host;
+       user_srunhost = user_shost;
+       if (matches)
+           break;
+    }
+    debug_return_bool(matches);
+}
+
 /*
  * Display Defaults entries
  */
@@ -502,6 +665,195 @@ convert_sudoers_output(const char *buf)
     return fputs(buf, output_fp);
 }
 
+/*
+ * Apply filters to userspecs, removing non-matching entries.
+ */
+static void
+filter_userspecs(void)
+{
+    struct userspec *us, *next_us;
+    struct privilege *priv, *next_priv;
+    debug_decl(filter_userspecs, SUDOERS_DEBUG_UTIL)
+
+    /*
+     * Does not currently prune out non-matching entries in the user or
+     * host lists.  It acts more like a grep than a true filter.
+     * In the future, we may want to add a prune option.
+     */
+    TAILQ_FOREACH_SAFE(us, &userspecs, entries, next_us) {
+       if (!userlist_matches_filter(&us->users)) {
+           TAILQ_REMOVE(&userspecs, us, entries);
+           free_userspec(us);
+           continue;
+       }
+       TAILQ_FOREACH_SAFE(priv, &us->privileges, entries, next_priv) {
+           if (!hostlist_matches_filter(&priv->hostlist)) {
+               TAILQ_REMOVE(&us->privileges, priv, entries);
+               free_privilege(priv);
+           }
+       }
+       if (TAILQ_EMPTY(&us->privileges)) {
+           TAILQ_REMOVE(&userspecs, us, entries);
+           free_userspec(us);
+           continue;
+       }
+    }
+    debug_return;
+}
+
+/*
+ * Apply filters to host/user-based Defaults, removing non-matching entries.
+ */
+static void
+filter_defaults(void)
+{
+    struct defaults *def, *next;
+    struct member_list *binding = NULL;
+    debug_decl(filter_defaults, SUDOERS_DEBUG_DEFAULTS)
+
+    TAILQ_FOREACH_SAFE(def, &defaults, entries, next) {
+       switch (def->type) {
+       case DEFAULTS_USER:
+           if (!userlist_matches_filter(def->binding)) {
+               TAILQ_REMOVE(&defaults, def, entries);
+               binding = free_default(def, binding);
+           } else {
+               binding = def->binding;
+           }
+           break;
+       case DEFAULTS_HOST:
+           if (!hostlist_matches_filter(def->binding)) {
+               TAILQ_REMOVE(&defaults, def, entries);
+               binding = free_default(def, binding);
+           } else {
+               binding = def->binding;
+           }
+           break;
+       default:
+           break;
+       }
+    }
+    debug_return;
+}
+
+/*
+ * Remove the alias of the specified type as well as any other aliases
+ * referenced by that alias.
+ * XXX - share with visudo
+ */
+static bool
+alias_remove_recursive(char *name, int type, struct rbtree *freelist)
+{
+    struct member *m;
+    struct alias *a;
+    bool ret = true;
+    debug_decl(alias_remove_recursive, SUDOERS_DEBUG_ALIAS)
+
+    if ((a = alias_remove(name, type)) != NULL) {
+       TAILQ_FOREACH(m, &a->members, entries) {
+           if (m->type == ALIAS) {
+               if (!alias_remove_recursive(m->name, type, freelist))
+                   ret = false;
+           }
+       }
+       if (rbinsert(freelist, a, NULL) != 0)
+           ret = false;
+    }
+    debug_return_bool(ret);
+}
+
+/*
+ * Remove unreferenced aliases.
+ * XXX - share with visudo
+ */
+static bool
+alias_remove_unused(void)
+{
+    struct cmndspec *cs;
+    struct member *m;
+    struct privilege *priv;
+    struct userspec *us;
+    struct defaults *d;
+    int atype, errors = 0;
+    struct rbtree *used_aliases;
+    struct rbtree *unused_aliases;
+    debug_decl(alias_remove_unused, SUDOERS_DEBUG_ALIAS)
+
+    used_aliases = rbcreate(alias_compare);
+    if (used_aliases == NULL) {
+       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+       debug_return_int(-1);
+    }
+
+    /* Move referenced aliases to used_aliases. */
+    TAILQ_FOREACH(us, &userspecs, entries) {
+       TAILQ_FOREACH(m, &us->users, entries) {
+           if (m->type == ALIAS) {
+               if (!alias_remove_recursive(m->name, USERALIAS, used_aliases))
+                   errors++;
+           }
+       }
+       TAILQ_FOREACH(priv, &us->privileges, entries) {
+           TAILQ_FOREACH(m, &priv->hostlist, entries) {
+               if (m->type == ALIAS) {
+                   if (!alias_remove_recursive(m->name, HOSTALIAS, used_aliases))
+                       errors++;
+               }
+           }
+           TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
+               if (cs->runasuserlist != NULL) {
+                   TAILQ_FOREACH(m, cs->runasuserlist, entries) {
+                       if (m->type == ALIAS) {
+                           if (!alias_remove_recursive(m->name, RUNASALIAS, used_aliases))
+                               errors++;
+                       }
+                   }
+               }
+               if (cs->runasgrouplist != NULL) {
+                   TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
+                       if (m->type == ALIAS) {
+                           if (!alias_remove_recursive(m->name, RUNASALIAS, used_aliases))
+                               errors++;
+                       }
+                   }
+               }
+               if ((m = cs->cmnd)->type == ALIAS) {
+                   if (!alias_remove_recursive(m->name, CMNDALIAS, used_aliases))
+                       errors++;
+               }
+           }
+       }
+    }
+    TAILQ_FOREACH(d, &defaults, entries) {
+       switch (d->type) {
+           case DEFAULTS_HOST:
+               atype = HOSTALIAS;
+               break;
+           case DEFAULTS_USER:
+               atype = USERALIAS;
+               break;
+           case DEFAULTS_RUNAS:
+               atype = RUNASALIAS;
+               break;
+           case DEFAULTS_CMND:
+               atype = CMNDALIAS;
+               break;
+           default:
+               continue; /* not an alias */
+       }
+       TAILQ_FOREACH(m, d->binding, entries) {
+           if (m->type == ALIAS) {
+               if (!alias_remove_recursive(m->name, atype, used_aliases))
+                   errors++;
+           }
+       }
+    }
+    unused_aliases = replace_aliases(used_aliases);
+    rbdestroy(unused_aliases, alias_free);
+
+    debug_return_int(errors ? false : true);
+}
+
 /*
  * Convert back to sudoers.
  */
@@ -571,7 +923,7 @@ usage(int fatal)
 {
     (void) fprintf(fatal ? stderr : stdout, "usage: %s [-ehV] [-b dn] "
        "[-c conf_file ] [-f output_format] [-i input_format] [-I increment] "
-       "[-o output_file] [-O start_point] [input_file]\n",
+       "[-m filter] [-o output_file] [-O start_point] [input_file]\n",
        getprogname());
     if (fatal)
        exit(1);
@@ -586,11 +938,12 @@ help(void)
        "  -b, --base=dn              the base DN for sudo LDAP queries\n"
        "  -e, --expand-aliases       expand aliases when converting\n"
        "  -f, --output-format=format set output format: JSON, LDIF or sudoers\n"
-       "  -I, --increment=num        amount to increase each sudoOrder by\n"
        "  -i, --input-format=format  set input format: LDIF or sudoers\n"
+       "  -I, --increment=num        amount to increase each sudoOrder by\n"
        "  -h, --help                 display help message and exit\n"
-       "  -O, --order-start=num      starting point for first sudoOrder\n"
+       "  -m, --match=filter         only convert entries that match the filter expression\n"
        "  -o, --output=output_file   write converted sudoers to output_file\n"
+       "  -O, --order-start=num      starting point for first sudoOrder\n"
        "  -V, --version              display version information and exit"));
     exit(0);
 }
index f24f1547242aa956ce9e3e690c3294c70416d432..efce454fdba699ec81b5078881bbebe756c6cff9 100644 (file)
@@ -24,11 +24,26 @@ enum sudoers_formats {
     format_sudoers
 };
 
+/*
+ * Simple string list with optional reference count.
+ * XXX - move this so fmtsudoers can use it
+ */
+struct cvtsudoers_string {
+    STAILQ_ENTRY(cvtsudoers_string) entries;
+    char *str;
+};
+struct cvtsudoers_str_list {
+    struct cvtsudoers_string *stqh_first;
+    struct cvtsudoers_string **stqh_last;
+    unsigned int refcnt;
+};
+
 /* cvtsudoers.conf settings */
 struct cvtsudoers_config {
     char *sudoers_base;
     char *input_format;
     char *output_format;
+    char *filter;
     unsigned int sudo_order;
     unsigned int order_increment;
     bool expand_aliases;
@@ -36,7 +51,7 @@ struct cvtsudoers_config {
 };
 
 /* Initial config settings for above. */
-#define INITIAL_CONFIG { NULL, NULL, NULL, 1, 1, false, true }
+#define INITIAL_CONFIG { NULL, NULL, NULL, NULL, 1, 1, false, true }
 
 #define CONF_BOOL      0
 #define CONF_UINT      1
@@ -48,9 +63,27 @@ struct cvtsudoers_conf_table {
     void *valp;                        /* pointer into cvtsudoers_config */
 };
 
+struct cvtsudoers_filter {
+    struct cvtsudoers_str_list users;
+    struct cvtsudoers_str_list groups;
+    struct cvtsudoers_str_list hosts;
+};
+
 bool convert_sudoers_json(const char *output_file, struct cvtsudoers_config *conf);
 bool convert_sudoers_ldif(const char *output_file, struct cvtsudoers_config *conf);
 bool parse_ldif(const char *input_file, struct cvtsudoers_config *conf);
 void get_hostname(void);
 
+struct member_list;
+struct userspec_list;
+bool userlist_matches_filter(struct member_list *userlist);
+bool hostlist_matches_filter(struct member_list *hostlist);
+
+struct cvtsudoers_str_list *str_list_alloc(void);
+void str_list_free(void *v);
+struct cvtsudoers_string *cvtsudoers_string_alloc(const char *s);
+void cvtsudoers_string_free(struct cvtsudoers_string *ls);
+
+extern struct cvtsudoers_filter *filters;
+
 #endif /* SUDOERS_CVTSUDOERS_H */
index 3209a03e7e2ac2308b3e0825951e693ec5438f52..c9ef0731fc7c6767d8f9cdf7da5485e579c7b577 100644 (file)
@@ -465,7 +465,7 @@ print_userspecs_ldif(FILE *fp, struct cvtsudoers_config *conf)
 {
     struct userspec *us;
     debug_decl(print_userspecs_ldif, SUDOERS_DEBUG_UTIL)
-
     TAILQ_FOREACH(us, &userspecs, entries) {
        if (!print_userspec_ldif(fp, us, conf))
            debug_return_bool(false);
@@ -513,36 +513,27 @@ convert_sudoers_ldif(const char *output_file, struct cvtsudoers_config *conf)
     debug_return_bool(ret);
 }
 
-struct ldif_string {
-    STAILQ_ENTRY(ldif_string) entries;
-    char *str;
-};
-struct ldif_str_list {
-    struct ldif_string *stqh_first;
-    struct ldif_string **stqh_last;
-    unsigned int refcnt;
-};
-
 struct sudo_role {
     STAILQ_ENTRY(sudo_role) entries;
     char *cn;
     char *notbefore;
     char *notafter;
     double order;
-    struct ldif_str_list *cmnds;
-    struct ldif_str_list *hosts;
-    struct ldif_str_list *users;
-    struct ldif_str_list *runasusers;
-    struct ldif_str_list *runasgroups;
-    struct ldif_str_list *options;
+    struct cvtsudoers_str_list *cmnds;
+    struct cvtsudoers_str_list *hosts;
+    struct cvtsudoers_str_list *users;
+    struct cvtsudoers_str_list *runasusers;
+    struct cvtsudoers_str_list *runasgroups;
+    struct cvtsudoers_str_list *options;
 };
 STAILQ_HEAD(sudo_role_list, sudo_role);
 
-static struct ldif_string *
-ldif_string_alloc(const char *s)
+/* XXX - move to cvtsudoers.c */
+struct cvtsudoers_string *
+cvtsudoers_string_alloc(const char *s)
 {
-    struct ldif_string *ls;
-    debug_decl(ldif_string_alloc, SUDOERS_DEBUG_UTIL)
+    struct cvtsudoers_string *ls;
+    debug_decl(cvtsudoers_string_alloc, SUDOERS_DEBUG_UTIL)
 
     if ((ls = malloc(sizeof(*ls))) != NULL) {
        if ((ls->str = strdup(s)) == NULL) {
@@ -554,17 +545,17 @@ ldif_string_alloc(const char *s)
     debug_return_ptr(ls);
 }
 
-static void
-ldif_string_free(struct ldif_string *ls)
+void
+cvtsudoers_string_free(struct cvtsudoers_string *ls)
 {
     free(ls->str);
     free(ls);
 }
 
-static struct ldif_str_list *
+struct cvtsudoers_str_list *
 str_list_alloc(void)
 {
-    struct ldif_str_list *strlist;
+    struct cvtsudoers_str_list *strlist;
     debug_decl(str_list_alloc, SUDOERS_DEBUG_UTIL)
 
     strlist = malloc(sizeof(*strlist));
@@ -574,17 +565,17 @@ str_list_alloc(void)
     debug_return_ptr(strlist);
 }
 
-static void
+void
 str_list_free(void *v)
 {
-    struct ldif_str_list *strlist = v;
-    struct ldif_string *first;
+    struct cvtsudoers_str_list *strlist = v;
+    struct cvtsudoers_string *first;
     debug_decl(str_list_free, SUDOERS_DEBUG_UTIL)
 
     if (--strlist->refcnt == 0) {
        while ((first = STAILQ_FIRST(strlist)) != NULL) {
            STAILQ_REMOVE_HEAD(strlist, entries);
-           ldif_string_free(first);
+           cvtsudoers_string_free(first);
        }
        free(strlist);
     }
@@ -644,25 +635,25 @@ sudo_role_free(struct sudo_role *role)
 }
 
 /*
- * Allocate a struct ldif_string, store str in it and
+ * Allocate a struct cvtsudoers_string, store str in it and
  * insert into the specified strlist.
  */
 static void
-ldif_store_string(const char *str, struct ldif_str_list *strlist, bool sorted)
+ldif_store_string(const char *str, struct cvtsudoers_str_list *strlist, bool sorted)
 {
-    struct ldif_string *ls;
+    struct cvtsudoers_string *ls;
     debug_decl(ldif_store_string, SUDOERS_DEBUG_UTIL)
 
     while (isblank((unsigned char)*str))
        str++;
-    if ((ls = ldif_string_alloc(str)) == NULL) {
+    if ((ls = cvtsudoers_string_alloc(str)) == NULL) {
        sudo_fatalx(U_("%s: %s"), __func__,
            U_("unable to allocate memory"));
     }
     if (!sorted) {
        STAILQ_INSERT_TAIL(strlist, ls, entries);
     } else {
-       struct ldif_string *prev, *next;
+       struct cvtsudoers_string *prev, *next;
 
        /* Insertion sort, list is small. */
        prev = STAILQ_FIRST(strlist);
@@ -683,13 +674,13 @@ ldif_store_string(const char *str, struct ldif_str_list *strlist, bool sorted)
 
 /*
  * Iterator for sudo_ldap_role_to_priv().
- * Takes a pointer to a struct ldif_string *.
+ * Takes a pointer to a struct cvtsudoers_string *.
  * Returns the string or NULL if we've reached the end.
  */
-static char *
-ldif_string_iter(void **vp)
+char *
+cvtsudoers_string_iter(void **vp)
 {
-    struct ldif_string *ls = *vp;
+    struct cvtsudoers_string *ls = *vp;
 
     if (ls == NULL)
        return NULL;
@@ -714,10 +705,10 @@ role_order_cmp(const void *va, const void *vb)
  * Parse list of sudoOption and store in global defaults list.
  */
 static void
-ldif_store_options(struct ldif_str_list *options)
+ldif_store_options(struct cvtsudoers_str_list *options)
 {
     struct defaults *d;
-    struct ldif_string *ls;
+    struct cvtsudoers_string *ls;
     char *var, *val;
     debug_decl(ldif_store_options, SUDOERS_DEBUG_UTIL)
 
@@ -748,10 +739,10 @@ ldif_store_options(struct ldif_str_list *options)
 static int
 str_list_cmp(const void *aa, const void *bb)
 {
-    const struct ldif_str_list *a = aa;
-    const struct ldif_str_list *b = bb;
-    const struct ldif_string *lsa = STAILQ_FIRST(a);
-    const struct ldif_string *lsb = STAILQ_FIRST(b);
+    const struct cvtsudoers_str_list *a = aa;
+    const struct cvtsudoers_str_list *b = bb;
+    const struct cvtsudoers_string *lsa = STAILQ_FIRST(a);
+    const struct cvtsudoers_string *lsb = STAILQ_FIRST(b);
     int ret;
 
     while (lsa != NULL && lsb != NULL) {
@@ -764,9 +755,9 @@ str_list_cmp(const void *aa, const void *bb)
 }
 
 static int
-str_list_cache(struct rbtree *cache, struct ldif_str_list **strlistp)
+str_list_cache(struct rbtree *cache, struct cvtsudoers_str_list **strlistp)
 {
-    struct ldif_str_list *strlist = *strlistp;
+    struct cvtsudoers_str_list *strlist = *strlistp;
     struct rbnode *node;
     int ret;
     debug_decl(str_list_cache, SUDOERS_DEBUG_UTIL)
@@ -797,7 +788,7 @@ role_to_sudoers(struct sudo_role *role, bool store_options,
     bool reuse_userspec, bool reuse_privilege, bool reuse_runas)
 {
     struct privilege *priv;
-    struct ldif_string *ls;
+    struct cvtsudoers_string *ls;
     struct userspec *us;
     struct member *m;
     debug_decl(role_to_sudoers, SUDOERS_DEBUG_UTIL)
@@ -882,7 +873,7 @@ role_to_sudoers(struct sudo_role *role, bool store_options,
        STAILQ_FIRST(role->runasusers), STAILQ_FIRST(role->runasgroups),
        STAILQ_FIRST(role->cmnds), STAILQ_FIRST(role->options),
        role->notbefore, role->notafter, true, store_options,
-       ldif_string_iter);
+       cvtsudoers_string_iter);
     if (priv == NULL) {
        sudo_fatalx(U_("%s: %s"), __func__,
            U_("unable to allocate memory"));
diff --git a/plugins/sudoers/cvtsudoers_pwutil.c b/plugins/sudoers/cvtsudoers_pwutil.c
new file mode 100644 (file)
index 0000000..4f8de07
--- /dev/null
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 1996, 1998-2005, 2007-2017
+ *     Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "sudoers.h"
+#include "cvtsudoers.h"
+#include "pwutil.h"
+
+#ifndef LOGIN_NAME_MAX
+# ifdef _POSIX_LOGIN_NAME_MAX
+#  define LOGIN_NAME_MAX _POSIX_LOGIN_NAME_MAX
+# else
+#  define LOGIN_NAME_MAX 9
+# endif
+#endif /* LOGIN_NAME_MAX */
+
+#define FIELD_SIZE(src, name, size)                    \
+do {                                                   \
+       if ((src)->name) {                              \
+               size = strlen((src)->name) + 1;         \
+               total += size;                          \
+       }                                               \
+} while (0)
+
+#define FIELD_COPY(src, dst, name, size)               \
+do {                                                   \
+       if ((src)->name) {                              \
+               memcpy(cp, (src)->name, size);          \
+               (dst)->name = cp;                               \
+               cp += size;                             \
+       }                                               \
+} while (0)
+
+/*
+ * Dynamically allocate space for a struct item plus the key and data
+ * elements.  If name is non-NULL it is used as the key, else the
+ * uid is the key.  Fills in datum from struct password.
+ * Returns NULL on calloc error or unknown name/id, setting errno
+ * to ENOMEM or ENOENT respectively.
+ */
+struct cache_item *
+sudo_make_pwitem(uid_t uid, const char *name)
+{
+    char *cp, uidstr[MAX_UID_T_LEN + 2];
+    size_t nsize, psize, csize, gsize, dsize, ssize, total;
+    struct cache_item_pw *pwitem;
+    struct passwd pw, *newpw;
+    struct cvtsudoers_string *s = NULL;
+    debug_decl(sudo_make_pwitem, SUDOERS_DEBUG_NSS)
+
+    /* Look up name or uid in filter list. */
+    if (name != NULL) {
+       STAILQ_FOREACH(s, &filters->users, entries) {
+           if (strcasecmp(name, s->str) == 0) {
+               uid = (uid_t)-1;
+               break;
+           }
+       }
+    } else {
+       STAILQ_FOREACH(s, &filters->users, entries) {
+           const char *errstr;
+           uid_t filter_uid;
+
+           if (s->str[0] != '#')
+               continue;
+
+           filter_uid = sudo_strtoid(s->str + 1, NULL, NULL, &errstr);
+           if (errstr == NULL) {
+               if (uid != filter_uid)
+                   continue;
+               snprintf(uidstr, sizeof(uidstr), "#%u", (unsigned int)uid);
+               break;
+           }
+       }
+    }
+    if (s == NULL) {
+       errno = ENOENT;
+       debug_return_ptr(NULL);
+    }
+
+    /* Fake up a passwd struct. */
+    memset(&pw, 0, sizeof(pw));
+    pw.pw_name = name ? s->str : uidstr;
+    pw.pw_passwd = "*";
+    pw.pw_uid = uid;
+    pw.pw_gid = (gid_t)-1;
+    pw.pw_shell = _PATH_BSHELL;
+    pw.pw_dir = "/";
+
+    /* Allocate in one big chunk for easy freeing. */
+    nsize = psize = csize = gsize = dsize = ssize = 0;
+    total = sizeof(*pwitem);
+    FIELD_SIZE(&pw, pw_name, nsize);
+    FIELD_SIZE(&pw, pw_passwd, psize);
+#ifdef HAVE_LOGIN_CAP_H
+    FIELD_SIZE(&pw, pw_class, csize);
+#endif
+    FIELD_SIZE(&pw, pw_gecos, gsize);
+    FIELD_SIZE(&pw, pw_dir, dsize);
+    FIELD_SIZE(&pw, pw_shell, ssize);
+    if (name != NULL)
+       total += strlen(name) + 1;
+
+    /* Allocate space for struct item, struct passwd and the strings. */
+    if ((pwitem = calloc(1, total)) == NULL) {
+       sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+           "unable to allocate memory");
+       debug_return_ptr(NULL);
+    }
+    newpw = &pwitem->pw;
+
+    /*
+     * Copy in passwd contents and make strings relative to space
+     * at the end of the struct.
+     */
+    memcpy(newpw, &pw, sizeof(pw));
+    cp = (char *)(pwitem + 1);
+    FIELD_COPY(&pw, newpw, pw_name, nsize);
+    FIELD_COPY(&pw, newpw, pw_passwd, psize);
+#ifdef HAVE_LOGIN_CAP_H
+    FIELD_COPY(&pw, newpw, pw_class, csize);
+#endif
+    FIELD_COPY(&pw, newpw, pw_gecos, gsize);
+    FIELD_COPY(&pw, newpw, pw_dir, dsize);
+    FIELD_COPY(&pw, newpw, pw_shell, ssize);
+
+    /* Set key and datum. */
+    if (name != NULL) {
+       memcpy(cp, name, strlen(name) + 1);
+       pwitem->cache.k.name = cp;
+    } else {
+       pwitem->cache.k.uid = pw.pw_uid;
+    }
+    pwitem->cache.d.pw = newpw;
+    pwitem->cache.refcnt = 1;
+
+    debug_return_ptr(&pwitem->cache);
+}
+
+/*
+ * Dynamically allocate space for a struct item plus the key and data
+ * elements.  If name is non-NULL it is used as the key, else the
+ * gid is the key.  Fills in datum from struct group.
+ * Returns NULL on calloc error or unknown name/id, setting errno
+ * to ENOMEM or ENOENT respectively.
+ */
+struct cache_item *
+sudo_make_gritem(gid_t gid, const char *name)
+{
+    char *cp, gidstr[MAX_UID_T_LEN + 2];
+    size_t nsize, psize, nmem, total, len;
+    struct cache_item_gr *gritem;
+    struct group gr, *newgr;
+    struct cvtsudoers_string *s = NULL;
+    debug_decl(sudo_make_gritem, SUDOERS_DEBUG_NSS)
+
+    /* Look up name or gid in filter list. */
+    if (name != NULL) {
+       STAILQ_FOREACH(s, &filters->users, entries) {
+           if (strcasecmp(name, s->str) == 0) {
+               gid = (gid_t)-1;
+               break;
+           }
+       }
+    } else {
+       STAILQ_FOREACH(s, &filters->users, entries) {
+           const char *errstr;
+           gid_t filter_gid;
+
+           if (s->str[0] != '#')
+               continue;
+
+           filter_gid = sudo_strtoid(s->str + 1, NULL, NULL, &errstr);
+           if (errstr == NULL) {
+               if (gid != filter_gid)
+                   continue;
+               snprintf(gidstr, sizeof(gidstr), "#%u", (unsigned int)gid);
+               break;
+           }
+       }
+    }
+    if (s == NULL) {
+       errno = ENOENT;
+       debug_return_ptr(NULL);
+    }
+
+    /* Fake up a group struct with all filter users as members. */
+    memset(&gr, 0, sizeof(gr));
+    gr.gr_name = name ? s->str : gidstr;
+    gr.gr_gid = gid;
+
+    /* Allocate in one big chunk for easy freeing. */
+    nsize = psize = nmem = 0;
+    total = sizeof(*gritem);
+    FIELD_SIZE(&gr, gr_name, nsize);
+    FIELD_SIZE(&gr, gr_passwd, psize);
+    if (!STAILQ_EMPTY(&filters->users)) {
+       STAILQ_FOREACH(s, &filters->users, entries) {
+           total += strlen(s->str) + 1;
+           nmem++;
+       }
+       total += sizeof(char *) * nmem;
+    }
+    if (name != NULL)
+       total += strlen(name) + 1;
+
+    if ((gritem = calloc(1, total)) == NULL) {
+       sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+           "unable to allocate memory");
+       debug_return_ptr(NULL);
+    }
+
+    /*
+     * Copy in group contents and make strings relative to space
+     * at the end of the buffer.  Note that gr_mem must come
+     * immediately after struct group to guarantee proper alignment.
+     */
+    newgr = &gritem->gr;
+    memcpy(newgr, &gr, sizeof(gr));
+    cp = (char *)(gritem + 1);
+    if (nmem != 0) {
+       STAILQ_FOREACH(s, &filters->groups, entries) {
+           total += strlen(s->str) + 1;
+       }
+    }
+    if (nmem != 0) {
+       newgr->gr_mem = (char **)cp;
+       cp += sizeof(char *) * nmem;
+       nmem = 0;
+       STAILQ_FOREACH(s, &filters->groups, entries) {
+           len = strlen(s->str) + 1;
+           memcpy(cp, s->str, len);
+           newgr->gr_mem[nmem++] = cp;
+           cp += len;
+       }
+       newgr->gr_mem[nmem] = NULL;
+    }
+    FIELD_COPY(&gr, newgr, gr_passwd, psize);
+    FIELD_COPY(&gr, newgr, gr_name, nsize);
+
+    /* Set key and datum. */
+    if (name != NULL) {
+       memcpy(cp, name, strlen(name) + 1);
+       gritem->cache.k.name = cp;
+    } else {
+       gritem->cache.k.gid = gr.gr_gid;
+    }
+    gritem->cache.d.gr = newgr;
+    gritem->cache.refcnt = 1;
+
+    debug_return_ptr(&gritem->cache);
+}
+
+static struct cache_item_gidlist *gidlist_item;
+
+/*
+ * Dynamically allocate space for a struct item plus the key and data
+ * elements.  Fills in datum from user_gids or from getgrouplist(3).
+ */
+struct cache_item *
+sudo_make_gidlist_item(const struct passwd *pw, char * const *unused1,
+    unsigned int type)
+{
+    char *cp;
+    size_t nsize, total;
+    struct cache_item_gidlist *glitem;
+    struct cvtsudoers_string *s;
+    struct gid_list *gidlist;
+    GETGROUPS_T *gids = NULL;
+    int i, ngids = 0;
+    debug_decl(sudo_make_gidlist_item, SUDOERS_DEBUG_NSS)
+
+    /*
+     * There's only a single gid list.
+     */
+    if (gidlist_item != NULL) {
+       gidlist_item->cache.refcnt++;
+       debug_return_ptr(&gidlist_item->cache);
+    }
+
+    /* Count number of possible gids in the filter. */
+    STAILQ_FOREACH(s, &filters->groups, entries) {
+       if (s->str[0] == '#')
+               ngids++;
+    }
+
+    /* Allocate gids[] array and fill it with parsed gids. */
+    if (ngids != 0) {
+       gids = reallocarray(NULL, ngids, sizeof(GETGROUPS_T));
+       if (gids == NULL) {
+           sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+               "unable to allocate memory");
+           debug_return_ptr(NULL);
+       }
+       ngids = 0;
+       STAILQ_FOREACH(s, &filters->groups, entries) {
+           if (s->str[0] == '#') {
+               const char *errstr;
+               gid_t gid = sudo_strtoid(s->str + 1, NULL, NULL, &errstr);
+               if (errstr == NULL) {
+                   /* Valid gid. */
+                   gids[ngids++] = gid;
+               }
+           }
+       }
+    }
+    if (ngids == 0) {
+       free(gids);
+       errno = ENOENT;
+       debug_return_ptr(NULL);
+    }
+
+    /* Allocate in one big chunk for easy freeing. */
+    nsize = strlen(pw->pw_name) + 1;
+    total = sizeof(*glitem) + nsize;
+    total += sizeof(gid_t *) * ngids;
+
+    if ((glitem = calloc(1, total)) == NULL) {
+       sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+           "unable to allocate memory");
+       free(gids);
+       debug_return_ptr(NULL);
+    }
+
+    /*
+     * Copy in group list and make pointers relative to space
+     * at the end of the buffer.  Note that the groups array must come
+     * immediately after struct group to guarantee proper alignment.
+     */
+    gidlist = &glitem->gidlist;
+    cp = (char *)(glitem + 1);
+    gidlist->gids = (gid_t *)cp;
+    cp += sizeof(gid_t) * ngids;
+
+    /* Set key and datum. */
+    memcpy(cp, pw->pw_name, nsize);
+    glitem->cache.k.name = cp;
+    glitem->cache.d.gidlist = gidlist;
+    glitem->cache.refcnt = 1;
+    glitem->cache.type = type;
+
+    /*
+     * Store group IDs.
+     */
+    for (i = 0; i < ngids; i++)
+       gidlist->gids[i] = gids[i];
+    gidlist->ngids = ngids;
+    free(gids);
+
+    debug_return_ptr(&glitem->cache);
+}
+
+static struct cache_item_gidlist *grlist_item;
+
+/*
+ * Dynamically allocate space for a struct item plus the key and data
+ * elements.  Fills in group names from a call to sudo_get_gidlist().
+ */
+struct cache_item *
+sudo_make_grlist_item(const struct passwd *pw, char * const *unused1)
+{
+    char *cp;
+    size_t nsize, ngroups, total, len;
+    struct cache_item_grlist *grlitem;
+    struct cvtsudoers_string *s;
+    struct group_list *grlist;
+    int groupname_len;
+    debug_decl(sudo_make_grlist_item, SUDOERS_DEBUG_NSS)
+
+    /*
+     * There's only a single group list.
+     */
+    if (grlist_item != NULL) {
+       grlist_item->cache.refcnt++;
+       debug_return_ptr(&grlist_item->cache);
+    }
+
+    /* Count number of groups in the filter. */
+    ngroups = 0;
+    STAILQ_FOREACH(s, &filters->groups, entries) {
+       ngroups++;
+    }
+
+#ifdef _SC_LOGIN_NAME_MAX
+    groupname_len = MAX((int)sysconf(_SC_LOGIN_NAME_MAX), 32);
+#else
+    groupname_len = MAX(LOGIN_NAME_MAX, 32);
+#endif
+
+    /* Allocate in one big chunk for easy freeing. */
+    nsize = strlen(pw->pw_name) + 1;
+    total = sizeof(*grlitem) + nsize;
+    total += groupname_len * ngroups;
+
+again:
+    if ((grlitem = calloc(1, total)) == NULL) {
+       sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+           "unable to allocate memory");
+       debug_return_ptr(NULL);
+    }
+
+    /*
+     * Copy in group list and make pointers relative to space
+     * at the end of the buffer.  Note that the groups array must come
+     * immediately after struct group to guarantee proper alignment.
+     */
+    grlist = &grlitem->grlist;
+    cp = (char *)(grlitem + 1);
+    grlist->groups = (char **)cp;
+    cp += sizeof(char *) * ngroups;
+
+    /* Set key and datum. */
+    memcpy(cp, pw->pw_name, nsize);
+    grlitem->cache.k.name = cp;
+    grlitem->cache.d.grlist = grlist;
+    grlitem->cache.refcnt = 1;
+    cp += nsize;
+
+    /*
+     * Copy groups from filter.
+     */
+    ngroups = 0;
+    STAILQ_FOREACH(s, &filters->groups, entries) {
+       if (s->str[0] == '#') {
+           const char *errstr;
+           sudo_strtoid(s->str + 1, NULL, NULL, &errstr);
+           if (errstr == NULL) {
+               /* Group ID not name, ignore it. */
+               continue;
+           }
+       }
+       len = strlen(s->str) + 1;
+       if (cp - (char *)grlitem + len > total) {
+           total += len + groupname_len;
+           free(grlitem);
+           goto again;
+       }
+       memcpy(cp, s->str, len);
+       grlist->groups[ngroups++] = cp;
+       cp += len;
+    }
+    grlist->ngroups = ngroups;
+
+    debug_return_ptr(&grlitem->cache);
+}
index 347baa77cdd208d24cf6fc91e18f94f8ccc32c05..1d7770ff0982c16e58522491fce3af0653203411 100644 (file)
@@ -252,6 +252,7 @@ extern struct defaults_list defaults;
 
 /* alias.c */
 bool no_aliases(void);
+struct rbtree *replace_aliases(struct rbtree *new_aliases);
 const char *alias_add(char *name, int type, char *file, int lineno, struct member *members);
 const char *alias_type_to_string(int alias_type);
 int alias_compare(const void *a1, const void *a2);