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
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.
-\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:
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
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.
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.
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
.\" 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
.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.
\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:
.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
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
\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
.\" 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
.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.
.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:
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
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
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
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
$(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 \
$(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 \
/*
* Globals
*/
-struct rbtree *aliases;
+static struct rbtree *aliases;
/*
* Comparison function for the red-black tree.
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.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+#include <pwd.h>
#include <unistd.h>
#include "sudoers.h"
#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' },
{ "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' },
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[])
usage(1);
}
break;
+ case 'm':
+ conf->filter = optarg;
+ break;
case 'o':
output_file = optarg;
break;
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) {
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);
{ "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 }
};
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)
{
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
*/
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.
*/
{
(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);
" -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);
}
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;
};
/* 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
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 */
{
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);
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) {
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));
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);
}
}
/*
- * 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);
/*
* 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;
* 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)
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) {
}
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)
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)
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"));
--- /dev/null
+/*
+ * 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);
+}
/* 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);