+
+ /*
+ * Can't use $EDITOR, try each element of def_editor until we
+ * find one that exists, is regular, and is executable.
+ */
+ if (Editor == NULL || *Editor == '\0') {
+ efree(EditorPath);
+ EditorPath = estrdup(def_editor);
+ Editor = strtok(EditorPath, ":");
+ do {
+ EditorArgs = get_args(Editor);
+ if (sudo_goodpath(Editor, NULL))
+ break;
+ } while ((Editor = strtok(NULL, ":")));
+
+ /* Bleah, none of the editors existed! */
+ if (Editor == NULL || *Editor == '\0')
+ errorx(1, "no editor found (editor path = %s)", def_editor);
+ }
+ *args = EditorArgs;
+ return(Editor);
+}
+
+/*
+ * Split out any command line arguments and return them.
+ */
+static char *
+get_args(cmnd)
+ char *cmnd;
+{
+ char *args;
+
+ args = cmnd;
+ while (*args && !isblank((unsigned char) *args))
+ args++;
+ if (*args) {
+ *args++ = '\0';
+ while (*args && isblank((unsigned char) *args))
+ args++;
+ }
+ 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 int
+alias_remove_recursive(name, type, strict, quiet)
+ char *name;
+ int type;
+ int strict;
+ int quiet;
+{
+ struct member *m;
+ struct alias *a;
+ int error = 0;
+
+ if ((a = alias_find(name, type)) != NULL) {
+ tq_foreach_fwd(&a->members, m) {
+ if (m->type == ALIAS) {
+ if (strcmp(name, m->name) == 0) {
+ print_selfref(m->name, type, strict, quiet);
+ error = 1;
+ } else {
+ if (!alias_remove_recursive(m->name, type, strict, quiet))
+ error = 1;
+ }
+ }
+ }
+ }
+ alias_seqno++;
+ a = alias_remove(name, type);
+ if (a)
+ rbinsert(alias_freelist, a);
+ return(error);
+}
+
+/*
+ * Iterate through the sudoers datastructures looking for undefined
+ * aliases or unused aliases.
+ */
+static int
+check_aliases(strict, quiet)
+ int strict;
+ int quiet;
+{
+ struct cmndspec *cs;
+ struct member *m, *binding;
+ struct privilege *priv;
+ struct userspec *us;
+ 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 (alias_find(m->name, USERALIAS) == NULL) {
+ print_undefined(m->name, USERALIAS, strict, quiet);
+ error++;
+ }
+ }
+ }
+ tq_foreach_fwd(&us->privileges, priv) {
+ tq_foreach_fwd(&priv->hostlist, m) {
+ if (m->type == ALIAS) {
+ alias_seqno++;
+ if (alias_find(m->name, HOSTALIAS) == NULL) {
+ print_undefined(m->name, HOSTALIAS, strict, quiet);
+ error++;
+ }
+ }
+ }
+ tq_foreach_fwd(&priv->cmndlist, cs) {
+ tq_foreach_fwd(&cs->runasuserlist, m) {
+ if (m->type == ALIAS) {
+ alias_seqno++;
+ if (alias_find(m->name, RUNASALIAS) == NULL) {
+ print_undefined(m->name, RUNASALIAS, strict, quiet);
+ error++;
+ }
+ }
+ }
+ if ((m = cs->cmnd)->type == ALIAS) {
+ alias_seqno++;
+ 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) {
+ if (!alias_remove_recursive(m->name, USERALIAS, strict, quiet))
+ error++;
+ }
+ }
+ tq_foreach_fwd(&us->privileges, priv) {
+ tq_foreach_fwd(&priv->hostlist, m) {
+ if (m->type == ALIAS)
+ if (!alias_remove_recursive(m->name, HOSTALIAS, strict,
+ quiet))
+ error++;
+ }
+ tq_foreach_fwd(&priv->cmndlist, cs) {
+ tq_foreach_fwd(&cs->runasuserlist, m) {
+ if (m->type == ALIAS)
+ if (!alias_remove_recursive(m->name, RUNASALIAS,
+ strict, quiet))
+ error++;
+ }
+ if ((m = cs->cmnd)->type == ALIAS)
+ if (!alias_remove_recursive(m->name, CMNDALIAS, strict,
+ quiet))
+ error++;
+ }
+ }
+ }
+ tq_foreach_fwd(&defaults, d) {
+ 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 */
+ }
+ tq_foreach_fwd(&d->binding, binding) {
+ for (m = binding; m != NULL; m = m->next) {
+ if (m->type == ALIAS)
+ if (!alias_remove_recursive(m->name, atype, strict, quiet))
+ error++;
+ }
+ }
+ }
+ rbdestroy(alias_freelist, alias_free);
+
+ /* If all aliases were referenced we will have an empty tree. */
+ if (!no_aliases() && !quiet)
+ alias_apply(print_unused, strict ? "Error" : "Warning");
+
+ return (strict ? error : 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 void
+print_selfref(name, type, strict, quiet)
+ char *name;
+ int type;
+ int strict;
+ int quiet;
+{
+ if (!quiet) {
+ warningx("%s: %s_Alias `%s' references self",
+ 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;
+ void *v2;
+{
+ struct alias *a = (struct alias *)v1;
+ char *prefix = (char *)v2;
+
+ warningx("%s: unused %s_Alias %s", prefix,
+ a->type == HOSTALIAS ? "Host" : a->type == CMNDALIAS ? "Cmnd" :
+ a->type == USERALIAS ? "User" : a->type == RUNASALIAS ? "Runas" :
+ "Unknown", a->name);
+ return(0);
+}
+
+/*
+ * Unlink any sudoers temp files that remain.
+ */
+void
+cleanup(gotsignal)
+ int gotsignal;
+{
+ struct sudoersfile *sp;
+
+ tq_foreach_fwd(&sudoerslist, sp) {
+ if (sp->tpath != NULL)
+ (void) unlink(sp->tpath);
+ }
+ if (!gotsignal) {
+ sudo_endpwent();
+ sudo_endgrent();
+ }
+}
+
+/*
+ * Unlink sudoers temp files (if any) and exit.
+ */
+static RETSIGTYPE
+quit(signo)
+ int signo;
+{
+ cleanup(signo);
+#define emsg " exiting due to signal.\n"
+ write(STDERR_FILENO, getprogname(), strlen(getprogname()));
+ write(STDERR_FILENO, emsg, sizeof(emsg) - 1);
+ _exit(signo);