Add sudoers output format to cvtsudoers. In the future this may

be used with filters to emit a partial sudoers file instead of a
full one.
This commit is contained in:
Todd C. Miller
2018-02-02 14:29:19 -07:00
parent df08d0d8f4
commit 3354cbd021
7 changed files with 410 additions and 24 deletions

View File

@@ -50,6 +50,11 @@ DDEESSCCRRIIPPTTIIOONN
supported by the sudoers LDAP schema so they supported by the sudoers LDAP schema so they
are expanded during the conversion. are expanded during the conversion.
sudoers Traditional sudoers format. A new sudoers file
will be reconstructed from the parsed input file.
Comments are not preserved and data from any
include files will be output inline.
--hh, ----hheellpp Display a short help message to the standard output and exit. --hh, ----hheellpp Display a short help message to the standard output and exit.
--oo _o_u_t_p_u_t___f_i_l_e, ----oouuttppuutt=_o_u_t_p_u_t___f_i_l_e --oo _o_u_t_p_u_t___f_i_l_e, ----oouuttppuutt=_o_u_t_p_u_t___f_i_l_e
@@ -89,4 +94,4 @@ DDIISSCCLLAAIIMMEERR
file distributed with ssuuddoo or https://www.sudo.ws/license.html for file distributed with ssuuddoo or https://www.sudo.ws/license.html for
complete details. complete details.
Sudo 1.8.22 January 28, 2018 Sudo 1.8.22 Sudo 1.8.23 February 2, 2018 Sudo 1.8.23

View File

@@ -16,7 +16,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.TH "CVTSUDOERS" "8" "January 28, 2018" "Sudo @PACKAGE_VERSION@" "System Manager's Manual" .TH "CVTSUDOERS" "8" "February 2, 2018" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
.nh .nh
.if n .ad l .if n .ad l
.SH "NAME" .SH "NAME"
@@ -100,6 +100,14 @@ sudoers LDAP schema so they are expanded during the conversion.
.PD 0 .PD 0
.PP .PP
.RE .RE
.PD
.TP 10n
sudoers
Traditional sudoers format.
A new sudoers file will be reconstructed from the parsed input file.
Comments are not preserved and data from any include files will be
output inline.
.PD 0
.PP .PP
.RE .RE
.PD .PD

View File

@@ -14,7 +14,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.Dd January 28, 2018 .Dd February 2, 2018
.Dt CVTSUDOERS @mansectsu@ .Dt CVTSUDOERS @mansectsu@
.Os Sudo @PACKAGE_VERSION@ .Os Sudo @PACKAGE_VERSION@
.Sh NAME .Sh NAME
@@ -84,6 +84,11 @@ translated as they don't have an equivalent in the sudoers LDAP schema.
Command, host, runas and user aliases are not supported by the Command, host, runas and user aliases are not supported by the
sudoers LDAP schema so they are expanded during the conversion. sudoers LDAP schema so they are expanded during the conversion.
.El .El
.It sudoers
Traditional sudoers format.
A new sudoers file will be reconstructed from the parsed input file.
Comments are not preserved and data from any include files will be
output inline.
.El .El
.It Fl h , Fl -help .It Fl h , Fl -help
Display a short help message to the standard output and exit. Display a short help message to the standard output and exit.

View File

@@ -213,3 +213,13 @@ init_aliases(void)
debug_return_bool(aliases != NULL); debug_return_bool(aliases != NULL);
} }
const char *
alias_type_to_string(int alias_type)
{
return alias_type == HOSTALIAS ? "Host_Alias" :
alias_type == CMNDALIAS ? "Cmnd_Alias" :
alias_type == USERALIAS ? "User_Alias" :
alias_type == RUNASALIAS ? "Runas_Alias" :
"Invalid_Alias";
}

View File

@@ -30,16 +30,14 @@
# include <strings.h> # include <strings.h>
#endif /* HAVE_STRINGS_H */ #endif /* HAVE_STRINGS_H */
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "sudoers.h" #include "sudoers.h"
#include "interfaces.h"
#include "parse.h" #include "parse.h"
#include "redblack.h"
#include "sudoers_version.h" #include "sudoers_version.h"
#include "sudo_conf.h" #include "sudo_conf.h"
#include "sudo_lbuf.h"
#include <gram.h> #include <gram.h>
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
@@ -48,6 +46,7 @@
# include "compat/getopt.h" # include "compat/getopt.h"
#endif /* HAVE_GETOPT_LONG */ #endif /* HAVE_GETOPT_LONG */
static bool convert_sudoers_sudoers(const char *output_file);
extern bool convert_sudoers_json(const char *output_file); extern bool convert_sudoers_json(const char *output_file);
extern bool convert_sudoers_ldif(const char *output_file, const char *base); extern bool convert_sudoers_ldif(const char *output_file, const char *base);
extern void get_hostname(void); extern void get_hostname(void);
@@ -75,9 +74,9 @@ static void help(void) __attribute__((__noreturn__));
static void usage(int); static void usage(int);
enum output_formats { enum output_formats {
output_invalid,
output_json, output_json,
output_ldif output_ldif,
output_sudoers
}; };
int int
@@ -104,11 +103,6 @@ main(int argc, char *argv[])
bindtextdomain("sudoers", LOCALEDIR); /* XXX - should have visudo domain */ bindtextdomain("sudoers", LOCALEDIR); /* XXX - should have visudo domain */
textdomain("sudoers"); textdomain("sudoers");
#if 0
/* Register fatal/fatalx callback. */
sudo_fatal_callback_register(cvtsudoers_cleanup);
#endif
/* Read debug and plugin sections of sudo.conf. */ /* Read debug and plugin sections of sudo.conf. */
if (sudo_conf_read(NULL, SUDO_CONF_DEBUG|SUDO_CONF_PLUGINS) == -1) if (sudo_conf_read(NULL, SUDO_CONF_DEBUG|SUDO_CONF_PLUGINS) == -1)
goto done; goto done;
@@ -130,6 +124,8 @@ main(int argc, char *argv[])
output_format = output_json; output_format = output_json;
} else if (strcasecmp(optarg, "ldif") == 0) { } else if (strcasecmp(optarg, "ldif") == 0) {
output_format = output_ldif; output_format = output_ldif;
} else if (strcasecmp(optarg, "sudoers") == 0) {
output_format = output_sudoers;
} else { } else {
sudo_warnx("unsupported output format %s", optarg); sudo_warnx("unsupported output format %s", optarg);
usage(1); usage(1);
@@ -218,6 +214,9 @@ main(int argc, char *argv[])
case output_ldif: case output_ldif:
exitcode = !convert_sudoers_ldif(output_file, sudoers_base); exitcode = !convert_sudoers_ldif(output_file, sudoers_base);
break; break;
case output_sudoers:
exitcode = !convert_sudoers_sudoers(output_file);
break;
default: default:
sudo_fatalx("error: unhandled output format %d", output_format); sudo_fatalx("error: unhandled output format %d", output_format);
} }
@@ -233,6 +232,374 @@ open_sudoers(const char *sudoers, bool doedit, bool *keepopen)
return fopen(sudoers, "r"); return fopen(sudoers, "r");
} }
/*
* Write the contents of a struct member to lbuf
*/
static bool
print_member_sudoers(struct sudo_lbuf *lbuf, struct member *m)
{
struct sudo_command *c;
debug_decl(print_member_sudoers, SUDOERS_DEBUG_UTIL)
switch (m->type) {
case ALL:
sudo_lbuf_append(lbuf, "%sALL", m->negated ? "!" : "");
break;
case MYSELF:
/* nothing to print */
break;
case COMMAND:
c = (struct sudo_command *)m->name;
if (m->negated)
sudo_lbuf_append(lbuf, "!");
sudo_lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->cmnd);
if (c->args) {
sudo_lbuf_append(lbuf, " ");
sudo_lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->args);
}
break;
default:
/* Do not quote UID/GID, all others get quoted. */
if (m->name[0] == '#' &&
m->name[strspn(m->name + 1, "0123456789") + 1] == '\0') {
sudo_lbuf_append(lbuf, "%s%s", m->negated ? "!" : "", m->name);
} else {
sudo_lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s%s",
m->negated ? "!" : "", m->name);
}
break;
}
debug_return_bool(!sudo_lbuf_error(lbuf));
}
/*
* Display Defaults entries
*/
static bool
print_defaults_sudoers(struct sudo_lbuf *lbuf)
{
struct defaults *def, *next;
struct member *m;
debug_decl(print_defaults_sudoers, SUDOERS_DEBUG_UTIL)
TAILQ_FOREACH_SAFE(def, &defaults, entries, next) {
/* Print Defaults type and binding (if present) */
switch (def->type) {
case DEFAULTS:
sudo_lbuf_append(lbuf, "Defaults");
break;
case DEFAULTS_HOST:
sudo_lbuf_append(lbuf, "Defaults@");
break;
case DEFAULTS_USER:
sudo_lbuf_append(lbuf, "Defaults:");
break;
case DEFAULTS_RUNAS:
sudo_lbuf_append(lbuf, "Defaults>");
break;
case DEFAULTS_CMND:
sudo_lbuf_append(lbuf, "Defaults!");
break;
}
TAILQ_FOREACH(m, def->binding, entries) {
if (m != TAILQ_FIRST(def->binding))
sudo_lbuf_append(lbuf, ", ");
print_member_sudoers(lbuf, m);
}
/* Print Defaults with the same binding, there may be multiple. */
for (;;) {
next = TAILQ_NEXT(def, entries);
if (def->val != NULL) {
sudo_lbuf_append(lbuf, " %s%s", def->var,
def->op == '+' ? "+=" : def->op == '-' ? "-=" : "=");
if (strpbrk(def->val, " \t") != NULL) {
sudo_lbuf_append(lbuf, "\"");
sudo_lbuf_append_quoted(lbuf, "\"", "%s", def->val);
sudo_lbuf_append(lbuf, "\"");
} else
sudo_lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", def->val);
} else {
sudo_lbuf_append(lbuf, " %s%s",
def->op == false ? "!" : "", def->var);
}
if (next == NULL || def->binding != next->binding)
break;
def = next;
sudo_lbuf_append(lbuf, ",");
}
sudo_lbuf_append(lbuf, "\n");
}
debug_return_bool(!sudo_lbuf_error(lbuf));
}
static int
print_alias_sudoers(void *v1, void *v2)
{
struct alias *a = v1;
struct sudo_lbuf *lbuf = v2;
struct member *m;
debug_decl(print_alias_sudoers, SUDOERS_DEBUG_UTIL)
sudo_lbuf_append(lbuf, "%s %s = ", alias_type_to_string(a->type),
a->name);
TAILQ_FOREACH(m, &a->members, entries) {
if (m != TAILQ_FIRST(&a->members))
sudo_lbuf_append(lbuf, ", ");
print_member_sudoers(lbuf, m);
}
sudo_lbuf_append(lbuf, "\n");
debug_return_int(sudo_lbuf_error(lbuf) ? -1 : 0);
}
/*
* Display aliases
*/
static bool
print_aliases_sudoers(struct sudo_lbuf *lbuf)
{
debug_decl(print_aliases_sudoers, SUDOERS_DEBUG_UTIL)
alias_apply(print_alias_sudoers, lbuf);
debug_return_bool(!sudo_lbuf_error(lbuf));
}
#define TAG_CHANGED(t) \
(TAG_SET(cs->tags.t) && cs->tags.t != tags->t)
/*
* XXX - same as parse.c:sudo_file_append_cmnd()
*/
static bool
print_cmndspec_sudoers(struct cmndspec *cs, struct cmndtag *tags,
struct sudo_lbuf *lbuf)
{
debug_decl(print_cmndspec_sudoers, SUDOERS_DEBUG_UTIL)
#ifdef HAVE_PRIV_SET
if (cs->privs)
sudo_lbuf_append(lbuf, "PRIVS=\"%s\" ", cs->privs);
if (cs->limitprivs)
sudo_lbuf_append(lbuf, "LIMITPRIVS=\"%s\" ", cs->limitprivs);
#endif /* HAVE_PRIV_SET */
#ifdef HAVE_SELINUX
if (cs->role)
sudo_lbuf_append(lbuf, "ROLE=%s ", cs->role);
if (cs->type)
sudo_lbuf_append(lbuf, "TYPE=%s ", cs->type);
#endif /* HAVE_SELINUX */
if (cs->timeout > 0) {
char numbuf[(((sizeof(int) * 8) + 2) / 3) + 2];
snprintf(numbuf, sizeof(numbuf), "%d", cs->timeout);
sudo_lbuf_append(lbuf, "TIMEOUT=%s ", numbuf);
}
if (cs->notbefore != UNSPEC) {
char buf[sizeof("CCYYMMDDHHMMSSZ")];
struct tm *tm = gmtime(&cs->notbefore);
snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02dZ",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
sudo_lbuf_append(lbuf, "NOTBEFORE=%s ", buf);
}
if (cs->notafter != UNSPEC) {
char buf[sizeof("CCYYMMDDHHMMSSZ")];
struct tm *tm = gmtime(&cs->notafter);
snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02dZ",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
sudo_lbuf_append(lbuf, "NOTAFTER=%s ", buf);
}
if (TAG_CHANGED(setenv)) {
tags->setenv = cs->tags.setenv;
sudo_lbuf_append(lbuf, tags->setenv ? "SETENV: " : "NOSETENV: ");
}
if (TAG_CHANGED(noexec)) {
tags->noexec = cs->tags.noexec;
sudo_lbuf_append(lbuf, tags->noexec ? "NOEXEC: " : "EXEC: ");
}
if (TAG_CHANGED(nopasswd)) {
tags->nopasswd = cs->tags.nopasswd;
sudo_lbuf_append(lbuf, tags->nopasswd ? "NOPASSWD: " : "PASSWD: ");
}
if (TAG_CHANGED(log_input)) {
tags->log_input = cs->tags.log_input;
sudo_lbuf_append(lbuf, tags->log_input ? "LOG_INPUT: " : "NOLOG_INPUT: ");
}
if (TAG_CHANGED(log_output)) {
tags->log_output = cs->tags.log_output;
sudo_lbuf_append(lbuf, tags->log_output ? "LOG_OUTPUT: " : "NOLOG_OUTPUT: ");
}
if (TAG_CHANGED(send_mail)) {
tags->send_mail = cs->tags.send_mail;
sudo_lbuf_append(lbuf, tags->send_mail ? "MAIL: " : "NOMAIL: ");
}
if (TAG_CHANGED(follow)) {
tags->follow = cs->tags.follow;
sudo_lbuf_append(lbuf, tags->follow ? "FOLLOW: " : "NOFOLLOW: ");
}
print_member_sudoers(lbuf, cs->cmnd);
debug_return_bool(!sudo_lbuf_error(lbuf));
}
/*
* Derived from parse.c:sudo_file_display_priv_short()
*/
static bool
print_userspec_sudoers(struct sudo_lbuf *lbuf, struct userspec *us)
{
struct cmndspec *cs, *prev_cs;
struct privilege *priv;
struct member *m;
struct cmndtag tags;
debug_decl(print_userspec_sudoers, SUDOERS_DEBUG_UTIL)
/* Print users list. */
TAILQ_FOREACH(m, &us->users, entries) {
if (m != TAILQ_FIRST(&us->users))
sudo_lbuf_append(lbuf, ", ");
print_member_sudoers(lbuf, m);
}
/* gcc -Wuninitialized false positive */
TAGS_INIT(tags);
TAILQ_FOREACH(priv, &us->privileges, entries) {
/* Print hosts list. */
if (priv != TAILQ_FIRST(&us->privileges))
sudo_lbuf_append(lbuf, " : ");
else
sudo_lbuf_append(lbuf, " ");
TAILQ_FOREACH(m, &priv->hostlist, entries) {
if (m != TAILQ_FIRST(&priv->hostlist))
sudo_lbuf_append(lbuf, ", ");
print_member_sudoers(lbuf, m);
}
/* Print commands. */
sudo_lbuf_append(lbuf, " = ");
prev_cs = NULL;
TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
if (prev_cs == NULL || RUNAS_CHANGED(cs, prev_cs)) {
if (cs != TAILQ_FIRST(&priv->cmndlist))
sudo_lbuf_append(lbuf, ", ");
if (cs->runasuserlist != NULL || cs->runasgrouplist != NULL)
sudo_lbuf_append(lbuf, "(");
if (cs->runasuserlist != NULL) {
TAILQ_FOREACH(m, cs->runasuserlist, entries) {
if (m != TAILQ_FIRST(cs->runasuserlist))
sudo_lbuf_append(lbuf, ", ");
print_member_sudoers(lbuf, m);
}
}
if (cs->runasgrouplist != NULL) {
sudo_lbuf_append(lbuf, " : ");
TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
if (m != TAILQ_FIRST(cs->runasgrouplist))
sudo_lbuf_append(lbuf, ", ");
print_member_sudoers(lbuf, m);
}
}
if (cs->runasuserlist != NULL || cs->runasgrouplist != NULL)
sudo_lbuf_append(lbuf, ") ");
TAGS_INIT(tags);
} else if (cs != TAILQ_FIRST(&priv->cmndlist)) {
sudo_lbuf_append(lbuf, ", ");
}
print_cmndspec_sudoers(cs, &tags, lbuf);
prev_cs = cs;
}
}
sudo_lbuf_append(lbuf, "\n");
debug_return_bool(!sudo_lbuf_error(lbuf));
}
/*
* Display User_Specs
*/
static bool
print_userspecs_sudoers(struct sudo_lbuf *lbuf)
{
struct userspec *us;
debug_decl(print_userspecs_sudoers, SUDOERS_DEBUG_UTIL)
TAILQ_FOREACH(us, &userspecs, entries) {
if (!print_userspec_sudoers(lbuf, us))
break;
}
debug_return_bool(!sudo_lbuf_error(lbuf));
}
static FILE *output_fp = stdout; /* global for convert_sudoers_output */
static int
convert_sudoers_output(const char *buf)
{
return fputs(buf, output_fp);
}
/*
* Convert back to sudoers.
*/
static bool
convert_sudoers_sudoers(const char *output_file)
{
bool ret = true;
struct sudo_lbuf lbuf;
debug_decl(convert_sudoers_sudoers, SUDOERS_DEBUG_UTIL)
if (strcmp(output_file, "-") != 0) {
if ((output_fp = fopen(output_file, "w")) == NULL)
sudo_fatal(U_("unable to open %s"), output_file);
}
/* Wrap lines at 80 columns with a 4 character indent. */
sudo_lbuf_init(&lbuf, convert_sudoers_output, 4, "\\", 80);
/* Print Defaults */
if (!print_defaults_sudoers(&lbuf))
goto done;
if (lbuf.len > 0) {
sudo_lbuf_print(&lbuf);
sudo_lbuf_append(&lbuf, "\n");
}
/* Print Aliases */
if (!print_aliases_sudoers(&lbuf))
goto done;
if (lbuf.len > 1) {
sudo_lbuf_print(&lbuf);
sudo_lbuf_append(&lbuf, "\n");
}
/* Print User_Specs */
if (!print_userspecs_sudoers(&lbuf))
goto done;
if (lbuf.len > 1) {
sudo_lbuf_print(&lbuf);
}
done:
if (sudo_lbuf_error(&lbuf)) {
if (errno == ENOMEM)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
ret = false;
}
sudo_lbuf_destroy(&lbuf);
(void)fflush(output_fp);
if (ferror(output_fp)) {
sudo_warn(U_("unable to write to %s"), output_file);
ret = false;
}
if (output_fp != stdout)
fclose(output_fp);
debug_return_bool(ret);
}
static void static void
usage(int fatal) usage(int fatal)
{ {

View File

@@ -244,6 +244,7 @@ extern struct defaults_list defaults;
/* alias.c */ /* alias.c */
bool no_aliases(void); bool no_aliases(void);
const char *alias_add(char *name, int type, char *file, int lineno, struct member *members); 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); int alias_compare(const void *a1, const void *a2);
struct alias *alias_get(char *name, int type); struct alias *alias_get(char *name, int type);
struct alias *alias_remove(char *name, int type); struct alias *alias_remove(char *name, int type);

View File

@@ -1035,16 +1035,6 @@ alias_remove_recursive(char *name, int type)
debug_return_bool(ret); debug_return_bool(ret);
} }
static const char *
alias_type_to_string(int alias_type)
{
return alias_type == HOSTALIAS ? "Host_Alias" :
alias_type == CMNDALIAS ? "Cmnd_Alias" :
alias_type == USERALIAS ? "User_Alias" :
alias_type == RUNASALIAS ? "Runas_Alias" :
"Invalid_Alias";
}
static int static int
check_alias(char *name, int type, char *file, int lineno, bool strict, bool quiet) check_alias(char *name, int type, char *file, int lineno, bool strict, bool quiet)
{ {