2
0
mirror of https://github.com/sudo-project/sudo.git synced 2025-08-22 01:49:11 +00:00

Initial support filtering by user, group and host in cvtsudoers.

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.
This commit is contained in:
Todd C. Miller 2018-03-21 12:24:11 -06:00
parent bc5e2d06a7
commit ff79de8592
10 changed files with 1090 additions and 128 deletions

View File

@ -4,9 +4,9 @@ NNAAMMEE
ccvvttssuuddooeerrss - convert between sudoers file formats
SSYYNNOOPPSSIISS
ccvvttssuuddooeerrss [--eehhVV] [--bb _d_n] [--cc _c_o_n_f___f_i_l_e] [--II _i_n_c_r_e_m_e_n_t] [--ii _i_n_p_u_t___f_o_r_m_a_t]
[--ff _o_u_t_p_u_t___f_o_r_m_a_t] [--OO _s_t_a_r_t___p_o_i_n_t] [--oo _o_u_t_p_u_t___f_i_l_e]
[_i_n_p_u_t___f_i_l_e]
ccvvttssuuddooeerrss [--eehhVV] [--bb _d_n] [--cc _c_o_n_f___f_i_l_e] [--ff _o_u_t_p_u_t___f_o_r_m_a_t]
[--ii _i_n_p_u_t___f_o_r_m_a_t] [--II _i_n_c_r_e_m_e_n_t] [--mm _f_i_l_t_e_r] [--oo _o_u_t_p_u_t___f_i_l_e]
[--OO _s_t_a_r_t___p_o_i_n_t] [_i_n_p_u_t___f_i_l_e]
DDEESSCCRRIIPPTTIIOONN
ccvvttssuuddooeerrss can be used to convert between _s_u_d_o_e_r_s security policy file
@ -52,14 +52,13 @@ DDEESSCCRRIIPPTTIIOONN
Conversion to LDIF has the following limitations:
++oo Command, host, runas and user-specific
Defaults lines cannot be translated as they
don't have an equivalent in the sudoers LDAP
schema.
++oo Command, host, runas and user-specific Defaults
lines cannot be translated as they don't have an
equivalent in the sudoers LDAP schema.
++oo Command, host, runas and user aliases are not
supported by the sudoers LDAP schema so they
are expanded during the conversion.
++oo 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 @@ DDEESSCCRRIIPPTTIIOONN
--hh, ----hheellpp Display a short help message to the standard output and exit.
--II _i_n_c_r_e_m_e_n_t, ----iinnccrreemmeenntt=_i_n_c_r_e_m_e_n_t
When generating LDIF output, increment each sudoOrder
attribute by the specified number. Defaults to an increment
of 1.
--ii _i_n_p_u_t___f_o_r_m_a_t, ----iinnppuutt--ffoorrmmaatt=_i_n_p_u_t___f_o_r_m_a_t
Specify the input format. The following formats are
supported:
@ -88,6 +82,28 @@ DDEESSCCRRIIPPTTIIOONN
sudoers Traditional sudoers format. This is the default
input format.
--II _i_n_c_r_e_m_e_n_t, ----iinnccrreemmeenntt=_i_n_c_r_e_m_e_n_t
When generating LDIF output, increment each sudoOrder
attribute by the specified number. Defaults to an increment
of 1.
--mm _f_i_l_t_e_r, ----mmaattcchh=_f_i_l_t_e_r
Only output rules that match the specified _f_i_l_t_e_r. A _f_i_l_t_e_r
expression is made up of one or more kkeeyy == _v_a_l_u_e pairs,
separated by a comma (`,'). The kkeeyy may be "user", "group"
or "host". For example, uusseerr = _o_p_e_r_a_t_o_r or hhoosstt = _w_w_w.
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.
--oo _o_u_t_p_u_t___f_i_l_e, ----oouuttppuutt=_o_u_t_p_u_t___f_i_l_e
Write the converted output to _o_u_t_p_u_t___f_i_l_e. If no _o_u_t_p_u_t___f_i_l_e
is specified, or if it is `-', the converted _s_u_d_o_e_r_s policy
will be written to the standard output.
--OO _s_t_a_r_t___p_o_i_n_t, ----oorrddeerr--ssttaarrtt=_s_t_a_r_t___p_o_i_n_t
When generating LDIF output, use the number specified by
_s_t_a_r_t___p_o_i_n_t in the sudoOrder attribute of the first sudoRole
@ -97,11 +113,6 @@ DDEESSCCRRIIPPTTIIOONN
point of 0 will disable the generation of sudoOrder
attributes in the resulting LDIF file.
--oo _o_u_t_p_u_t___f_i_l_e, ----oouuttppuutt=_o_u_t_p_u_t___f_i_l_e
Write the converted output to _o_u_t_p_u_t___f_i_l_e. If no _o_u_t_p_u_t___f_i_l_e
is specified, or if it is `-', the converted _s_u_d_o_e_r_s policy
will be written to the standard output.
--VV, ----vveerrssiioonn
Print the ccvvttssuuddooeerrss and _s_u_d_o_e_r_s grammar versions and exit.
@ -115,6 +126,9 @@ DDEESSCCRRIIPPTTIIOONN
iinnppuutt__ffoorrmmaatt == _l_d_i_f | _s_u_d_o_e_r_s
See the description of the --ii command line option.
mmaattcchh == _f_i_l_t_e_r
See the description of the --mm command line option.
oorrddeerr__iinnccrreemmeenntt == _i_n_c_r_e_m_e_n_t
See the description of the --II command line option.
@ -162,4 +176,4 @@ DDIISSCCLLAAIIMMEERR
file distributed with ssuuddoo 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

View 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"
@ -28,11 +28,12 @@
[\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

View 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
@ -25,11 +25,12 @@
.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

View 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 \

View 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.
*/

View 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>
@ -51,9 +53,10 @@
/*
* 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);
}

View 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 */

View 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"));

View File

@ -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);
}

View 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);