/*
- * Copyright (c) 1996, 1998-2005, 2007-2008
+ * Copyright (c) 1996, 1998-2005, 2007-2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
#include "sudo.h"
#include "interfaces.h"
#include "parse.h"
+#include "redblack.h"
#include <gram.h>
-#include "version.h"
-
-#ifndef lint
-__unused static const char rcsid[] = "$Sudo: visudo.c,v 1.223 2008/11/22 15:12:26 millert Exp $";
-#endif /* lint */
struct sudoersfile {
char *path;
- int fd;
char *tpath;
+ int fd;
int tfd;
int modified;
+ int doedit;
struct sudoersfile *next;
};
static RETSIGTYPE quit __P((int));
static char *get_args __P((char *));
static char *get_editor __P((char **));
+static void get_hostname __P((void));
static char whatnow __P((void));
-static int check_aliases __P((int));
+static int check_aliases __P((int, int));
static int check_syntax __P((char *, int, int));
static int edit_sudoers __P((struct sudoersfile *, char *, char *, int));
static int install_sudoers __P((struct sudoersfile *, int));
static int print_unused __P((void *, void *));
static int reparse_sudoers __P((char *, char *, int, int));
static int run_command __P((char *, char **));
+static void print_undefined __P((char *name, int, int, int));
static void setup_signals __P((void));
static void usage __P((void)) __attribute__((__noreturn__));
/*
* External globals exported by the parser
*/
+extern struct rbtree *aliases;
extern FILE *yyin;
extern char *sudoers, *errorfile;
extern int errorlineno, parse_error;
static struct sudoerslist {
struct sudoersfile *first, *last;
} sudoerslist;
+static struct rbtree *alias_freelist;
int
main(argc, argv)
while ((ch = getopt(argc, argv, "Vcf:sq")) != -1) {
switch (ch) {
case 'V':
- (void) printf("%s version %s\n", getprogname(), version);
+ (void) printf("%s version %s\n", getprogname(), PACKAGE_VERSION);
exit(0);
case 'c':
checkonly++; /* check mode */
sudo_setgrent();
/* Mock up a fake sudo_user struct. */
- user_host = user_shost = user_cmnd = "";
+ user_cmnd = "";
if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL)
errorx(1, "you don't exist in the passwd database");
+ get_hostname();
/* Setup defaults data structures. */
init_defaults();
* Parse the existing sudoers file(s) in quiet mode to highlight any
* existing errors and to pull in editor and env_editor conf values.
*/
- if ((yyin = open_sudoers(sudoers_path, NULL)) == NULL)
+ if ((yyin = open_sudoers(sudoers_path, TRUE, NULL)) == NULL) {
error(1, "%s", sudoers_path);
+ }
init_parser(sudoers_path, 0);
yyparse();
(void) update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER);
/* Edit the sudoers file(s) */
tq_foreach_fwd(&sudoerslist, sp) {
+ if (!sp->doedit)
+ continue;
if (sp != tq_first(&sudoerslist)) {
printf("press return to edit %s: ", sp->path);
while ((ch = getchar()) != EOF && ch != '\n')
parse_error = TRUE;
}
fclose(yyin);
- if (check_aliases(strict) != 0)
+ if (check_aliases(strict, quiet) != 0)
parse_error = TRUE;
/*
warningx("failed to parse %s file, unknown error", sudoers_path);
parse_error = TRUE;
}
+ if (!parse_error) {
+ if (check_aliases(strict, quiet) != 0)
+ parse_error = TRUE;
+ }
error = parse_error;
if (!quiet) {
if (parse_error)
- (void) printf("parse error in %s near line %d\n", sudoers_path,
+ (void) printf("parse error in %s near line %d\n", errorfile,
errorlineno);
else
(void) printf("%s: parsed OK\n", sudoers_path);
* any subsequent files #included via a callback from the parser.
*/
FILE *
-open_sudoers(path, keepopen)
+open_sudoers(path, doedit, keepopen)
const char *path;
+ int doedit;
int *keepopen;
{
struct sudoersfile *entry;
entry->next = NULL;
entry->fd = open(entry->path, O_RDWR | O_CREAT, SUDOERS_MODE);
entry->tpath = NULL;
+ entry->tfd = -1;
+ entry->doedit = doedit;
if (entry->fd == -1) {
warning("%s", entry->path);
efree(entry);
sudoerslist.last->next = entry;
sudoerslist.last = entry;
}
- if (keepopen != NULL)
- *keepopen = TRUE;
} else {
/* Already exists, open .tmp version if there is one. */
if (entry->tpath != NULL) {
} else {
if ((fp = fdopen(entry->fd, "r")) == NULL)
error(1, "%s", entry->path);
+ rewind(fp);
}
}
+ if (keepopen != NULL)
+ *keepopen = TRUE;
return(fp);
}
return(*args ? args : NULL);
}
+/*
+ * Look up the hostname and set user_host and user_shost.
+ */
+static void
+get_hostname()
+{
+ char *p, thost[MAXHOSTNAMELEN + 1];
+
+ if (gethostname(thost, sizeof(thost)) != 0) {
+ user_host = user_shost = "localhost";
+ return;
+ }
+ thost[sizeof(thost) - 1] = '\0';
+ user_host = estrdup(thost);
+
+ if ((p = strchr(user_host, '.'))) {
+ *p = '\0';
+ user_shost = estrdup(user_host);
+ *p = '.';
+ } else {
+ user_shost = user_host;
+ }
+}
+
+static void
+alias_remove_recursive(name, type)
+ char *name;
+ int type;
+{
+ struct member *m;
+ struct alias *a;
+
+ if ((a = alias_find(name, type)) != NULL) {
+ tq_foreach_fwd(&a->members, m) {
+ if (m->type == ALIAS) {
+ alias_remove_recursive(m->name, type);
+ }
+ }
+ }
+ alias_seqno++;
+ a = alias_remove(name, type);
+ if (a)
+ rbinsert(alias_freelist, a);
+}
+
/*
* Iterate through the sudoers datastructures looking for undefined
* aliases or unused aliases.
*/
static int
-check_aliases(strict)
+check_aliases(strict, quiet)
int strict;
+ int quiet;
{
struct cmndspec *cs;
struct member *m, *binding;
struct defaults *d;
int atype, error = 0;
+ alias_freelist = rbcreate(alias_compare);
+
/* Forward check. */
tq_foreach_fwd(&userspecs, us) {
tq_foreach_fwd(&us->users, m) {
if (m->type == ALIAS) {
alias_seqno++;
- if (find_alias(m->name, USERALIAS) == NULL) {
- warningx("%s: User_Alias `%s' referenced but not defined",
- strict ? "Error" : "Warning", m->name);
+ if (alias_find(m->name, USERALIAS) == NULL) {
+ print_undefined(m->name, USERALIAS, strict, quiet);
error++;
}
}
tq_foreach_fwd(&priv->hostlist, m) {
if (m->type == ALIAS) {
alias_seqno++;
- if (find_alias(m->name, HOSTALIAS) == NULL) {
- warningx("%s: Host_Alias `%s' referenced but not defined",
- strict ? "Error" : "Warning", m->name);
+ if (alias_find(m->name, HOSTALIAS) == NULL) {
+ print_undefined(m->name, HOSTALIAS, strict, quiet);
error++;
}
}
tq_foreach_fwd(&cs->runasuserlist, m) {
if (m->type == ALIAS) {
alias_seqno++;
- if (find_alias(m->name, RUNASALIAS) == NULL) {
- warningx("%s: Runas_Alias `%s' referenced but not defined",
- strict ? "Error" : "Warning", m->name);
+ if (alias_find(m->name, RUNASALIAS) == NULL) {
+ print_undefined(m->name, RUNASALIAS, strict, quiet);
error++;
}
}
}
if ((m = cs->cmnd)->type == ALIAS) {
alias_seqno++;
- if (find_alias(m->name, CMNDALIAS) == NULL) {
- warningx("%s: Cmnd_Alias `%s' referenced but not defined",
- strict ? "Error" : "Warning", m->name);
+ if (alias_find(m->name, CMNDALIAS) == NULL) {
+ print_undefined(m->name, CMNDALIAS, strict, quiet);
error++;
}
}
/* Reverse check (destructive) */
tq_foreach_fwd(&userspecs, us) {
tq_foreach_fwd(&us->users, m) {
- if (m->type == ALIAS)
- (void) alias_remove(m->name, USERALIAS);
+ if (m->type == ALIAS) {
+ (void) alias_remove_recursive(m->name, USERALIAS);
+ }
}
tq_foreach_fwd(&us->privileges, priv) {
tq_foreach_fwd(&priv->hostlist, m) {
if (m->type == ALIAS)
- (void) alias_remove(m->name, HOSTALIAS);
+ (void) alias_remove_recursive(m->name, HOSTALIAS);
}
tq_foreach_fwd(&priv->cmndlist, cs) {
tq_foreach_fwd(&cs->runasuserlist, m) {
if (m->type == ALIAS)
- (void) alias_remove(m->name, RUNASALIAS);
+ (void) alias_remove_recursive(m->name, RUNASALIAS);
}
if ((m = cs->cmnd)->type == ALIAS)
- (void) alias_remove(m->name, CMNDALIAS);
+ (void) alias_remove_recursive(m->name, CMNDALIAS);
}
}
}
tq_foreach_fwd(&d->binding, binding) {
for (m = binding; m != NULL; m = m->next) {
if (m->type == ALIAS)
- (void) alias_remove(m->name, atype);
+ (void) alias_remove_recursive(m->name, atype);
}
}
}
+ rbdestroy(alias_freelist, alias_free);
/* If all aliases were referenced we will have an empty tree. */
if (no_aliases())
return(0);
- alias_apply(print_unused, strict ? "Error" : "Warning");
+ if (!quiet) {
+ alias_apply(print_unused, strict ? "Error" : "Warning");
+ }
return (strict ? 1 : 0);
}
+static void
+print_undefined(name, type, strict, quiet)
+ char *name;
+ int type;
+ int strict;
+ int quiet;
+{
+ if (!quiet) {
+ warningx("%s: %s_Alias `%s' referenced but not defined",
+ strict ? "Error" : "Warning",
+ type == HOSTALIAS ? "Host" : type == CMNDALIAS ? "Cmnd" :
+ type == USERALIAS ? "User" : type == RUNASALIAS ? "Runas" :
+ "Unknown", name);
+ }
+}
+
static int
print_unused(v1, v2)
void *v1;