Diff for /embedaddon/sudo/plugins/sudoers/visudo.c between versions 1.1.1.5 and 1.1.1.6

version 1.1.1.5, 2013/10/14 07:56:35 version 1.1.1.6, 2014/06/15 16:12:54
Line 67 Line 67
 #include <fcntl.h>  #include <fcntl.h>
 #include <netinet/in.h>  #include <netinet/in.h>
 #include <arpa/inet.h>  #include <arpa/inet.h>
#if TIME_WITH_SYS_TIME#ifdef TIME_WITH_SYS_TIME
 # include <time.h>  # include <time.h>
 #endif  #endif
 #ifdef HAVE_GETOPT_LONG  #ifdef HAVE_GETOPT_LONG
Line 79 Line 79
 #include "sudoers.h"  #include "sudoers.h"
 #include "parse.h"  #include "parse.h"
 #include "redblack.h"  #include "redblack.h"
 #include "gettext.h"  
 #include "sudoers_version.h"  #include "sudoers_version.h"
 #include "sudo_conf.h"  #include "sudo_conf.h"
 #include <gram.h>  #include <gram.h>
   
 struct sudoersfile {  struct sudoersfile {
    struct sudoersfile *prev, *next;    TAILQ_ENTRY(sudoersfile) entries;
     char *path;      char *path;
     char *tpath;      char *tpath;
     int fd;      int fd;
     int modified;      int modified;
     int doedit;      int doedit;
 };  };
TQ_DECLARE(sudoersfile)TAILQ_HEAD(sudoersfile_list, sudoersfile);
   
 /*  /*
  * Function prototypes   * Function prototypes
Line 114  static void help(void) __attribute__((__noreturn__)); Line 113  static void help(void) __attribute__((__noreturn__));
 static void usage(int);  static void usage(int);
 static void visudo_cleanup(void);  static void visudo_cleanup(void);
   
   extern bool export_sudoers(const char *, const char *, bool, bool);
   
 extern void sudoerserror(const char *);  extern void sudoerserror(const char *);
 extern void sudoersrestart(FILE *);  extern void sudoersrestart(FILE *);
   
 /*  /*
  * External globals exported by the parser  
  */  
 extern struct rbtree *aliases;  
 extern FILE *sudoersin;  
 extern char *sudoers, *errorfile;  
 extern int errorlineno;  
 extern bool parse_error;  
   
 /*  
  * Globals   * Globals
  */   */
 struct sudo_user sudo_user;  struct sudo_user sudo_user;
 struct passwd *list_pw;  struct passwd *list_pw;
static struct sudoersfile_list sudoerslist;static struct sudoersfile_list sudoerslist = TAILQ_HEAD_INITIALIZER(sudoerslist);
 static struct rbtree *alias_freelist;  static struct rbtree *alias_freelist;
 static bool checkonly;  static bool checkonly;
static const char short_opts[] =  "cf:hqsV";static const char short_opts[] =  "cf:hqsVx:";
 static struct option long_opts[] = {  static struct option long_opts[] = {
     { "check",          no_argument,            NULL,   'c' },      { "check",          no_argument,            NULL,   'c' },
       { "export",         required_argument,      NULL,   'x' },
     { "file",           required_argument,      NULL,   'f' },      { "file",           required_argument,      NULL,   'f' },
     { "help",           no_argument,            NULL,   'h' },      { "help",           no_argument,            NULL,   'h' },
     { "quiet",          no_argument,            NULL,   'q' },      { "quiet",          no_argument,            NULL,   'q' },
Line 154  main(int argc, char *argv[]) Line 147  main(int argc, char *argv[])
     char *args, *editor, *sudoers_path;      char *args, *editor, *sudoers_path;
     int ch, exitcode = 0;      int ch, exitcode = 0;
     bool quiet, strict, oldperms;      bool quiet, strict, oldperms;
       const char *export_path;
     debug_decl(main, SUDO_DEBUG_MAIN)      debug_decl(main, SUDO_DEBUG_MAIN)
   
 #if defined(SUDO_DEVEL) && defined(__OpenBSD__)  #if defined(SUDO_DEVEL) && defined(__OpenBSD__)
Line 163  main(int argc, char *argv[]) Line 157  main(int argc, char *argv[])
     }      }
 #endif  #endif
   
#if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)    initprogname(argc > 0 ? argv[0] : "visudo");
    setprogname(argc > 0 ? argv[0] : "visudo");    sudoers_initlocale(setlocale(LC_ALL, ""), def_sudoers_locale);
#endif 
 
    sudoers_setlocale(SUDOERS_LOCALE_USER, NULL); 
     bindtextdomain("sudoers", LOCALEDIR); /* XXX - should have visudo domain */      bindtextdomain("sudoers", LOCALEDIR); /* XXX - should have visudo domain */
     textdomain("sudoers");      textdomain("sudoers");
   
Line 184  main(int argc, char *argv[]) Line 175  main(int argc, char *argv[])
      * Arg handling.       * Arg handling.
      */       */
     checkonly = oldperms = quiet = strict = false;      checkonly = oldperms = quiet = strict = false;
       export_path = NULL;
     sudoers_path = _PATH_SUDOERS;      sudoers_path = _PATH_SUDOERS;
     while ((ch = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {      while ((ch = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
         switch (ch) {          switch (ch) {
Line 209  main(int argc, char *argv[]) Line 201  main(int argc, char *argv[])
             case 'q':              case 'q':
                 quiet = true;           /* quiet mode */                  quiet = true;           /* quiet mode */
                 break;                  break;
               case 'x':
                   export_path = optarg;   /* export mode */
                   break;
             default:              default:
                 usage(1);                  usage(1);
         }          }
Line 221  main(int argc, char *argv[]) Line 216  main(int argc, char *argv[])
     sudo_setgrent();      sudo_setgrent();
   
     /* Mock up a fake sudo_user struct. */      /* Mock up a fake sudo_user struct. */
    user_cmnd = "";    user_cmnd = user_base = "";
     if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL)      if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL)
        fatalx(_("you do not exist in the %s database"), "passwd");        fatalx(U_("you do not exist in the %s database"), "passwd");
     get_hostname();      get_hostname();
   
     /* Setup defaults data structures. */      /* Setup defaults data structures. */
Line 233  main(int argc, char *argv[]) Line 228  main(int argc, char *argv[])
         exitcode = check_syntax(sudoers_path, quiet, strict, oldperms) ? 0 : 1;          exitcode = check_syntax(sudoers_path, quiet, strict, oldperms) ? 0 : 1;
         goto done;          goto done;
     }      }
       if (export_path != NULL) {
           exitcode = export_sudoers(sudoers_path, export_path, quiet, strict) ? 0 : 1;
           goto done;
       }
   
     /*      /*
      * Parse the existing sudoers file(s) to highlight any existing       * Parse the existing sudoers file(s) to highlight any existing
Line 250  main(int argc, char *argv[]) Line 249  main(int argc, char *argv[])
     setup_signals();      setup_signals();
   
     /* Edit the sudoers file(s) */      /* Edit the sudoers file(s) */
    tq_foreach_fwd(&sudoerslist, sp) {    TAILQ_FOREACH(sp, &sudoerslist, entries) {
         if (!sp->doedit)          if (!sp->doedit)
             continue;              continue;
        if (sp != tq_first(&sudoerslist)) {        if (sp != TAILQ_FIRST(&sudoerslist)) {
             printf(_("press return to edit %s: "), sp->path);              printf(_("press return to edit %s: "), sp->path);
             while ((ch = getchar()) != EOF && ch != '\n')              while ((ch = getchar()) != EOF && ch != '\n')
                     continue;                      continue;
Line 266  main(int argc, char *argv[]) Line 265  main(int argc, char *argv[])
      * and install the edited files as needed.       * and install the edited files as needed.
      */       */
     if (reparse_sudoers(editor, args, strict, quiet)) {      if (reparse_sudoers(editor, args, strict, quiet)) {
        tq_foreach_fwd(&sudoerslist, sp) {        TAILQ_FOREACH(sp, &sudoerslist, entries) {
             (void) install_sudoers(sp, oldperms);              (void) install_sudoers(sp, oldperms);
         }          }
     }      }
   
 done:  done:
    sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);                    sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);
     exit(exitcode);      exit(exitcode);
 }  }
   
Line 323  edit_sudoers(struct sudoersfile *sp, char *editor, cha Line 322  edit_sudoers(struct sudoersfile *sp, char *editor, cha
     debug_decl(edit_sudoers, SUDO_DEBUG_UTIL)      debug_decl(edit_sudoers, SUDO_DEBUG_UTIL)
   
     if (fstat(sp->fd, &sb) == -1)      if (fstat(sp->fd, &sb) == -1)
        fatal(_("unable to stat %s"), sp->path);        fatal(U_("unable to stat %s"), sp->path);
     orig_size = sb.st_size;      orig_size = sb.st_size;
     mtim_get(&sb, &orig_mtim);      mtim_get(&sb, &orig_mtim);
   
Line 339  edit_sudoers(struct sudoersfile *sp, char *editor, cha Line 338  edit_sudoers(struct sudoersfile *sp, char *editor, cha
             (void) lseek(sp->fd, (off_t)0, SEEK_SET);              (void) lseek(sp->fd, (off_t)0, SEEK_SET);
             while ((nread = read(sp->fd, buf, sizeof(buf))) > 0)              while ((nread = read(sp->fd, buf, sizeof(buf))) > 0)
                 if (write(tfd, buf, nread) != nread)                  if (write(tfd, buf, nread) != nread)
                    fatal(_("write error"));                    fatal(U_("write error"));
   
             /* Add missing newline at EOF if needed. */              /* Add missing newline at EOF if needed. */
             if (nread > 0 && buf[nread - 1] != '\n') {              if (nread > 0 && buf[nread - 1] != '\n') {
                 buf[0] = '\n';                  buf[0] = '\n';
                 if (write(tfd, buf, 1) != 1)                  if (write(tfd, buf, 1) != 1)
                    fatal(_("write error"));                    fatal(U_("write error"));
             }              }
         }          }
         (void) close(tfd);          (void) close(tfd);
Line 427  edit_sudoers(struct sudoersfile *sp, char *editor, cha Line 426  edit_sudoers(struct sudoersfile *sp, char *editor, cha
          * Sanity checks.           * Sanity checks.
          */           */
         if (stat(sp->tpath, &sb) < 0) {          if (stat(sp->tpath, &sb) < 0) {
            warningx(_("unable to stat temporary file (%s), %s unchanged"),            warningx(U_("unable to stat temporary file (%s), %s unchanged"),
                 sp->tpath, sp->path);                  sp->tpath, sp->path);
             goto done;              goto done;
         }          }
         if (sb.st_size == 0 && orig_size != 0) {          if (sb.st_size == 0 && orig_size != 0) {
            warningx(_("zero length temporary file (%s), %s unchanged"),            warningx(U_("zero length temporary file (%s), %s unchanged"),
                 sp->tpath, sp->path);                  sp->tpath, sp->path);
             sp->modified = true;              sp->modified = true;
             goto done;              goto done;
         }          }
     } else {      } else {
        warningx(_("editor (%s) failed, %s unchanged"), editor, sp->path);        warningx(U_("editor (%s) failed, %s unchanged"), editor, sp->path);
         goto done;          goto done;
     }      }
   
    /* Set modified bit if use changed the file. */    /* Set modified bit if the user changed the file. */
     modified = true;      modified = true;
     mtim_get(&sb, &tv);      mtim_get(&sb, &tv);
    if (orig_size == sb.st_size && timevalcmp(&orig_mtim, &tv, ==)) {    if (orig_size == sb.st_size && sudo_timevalcmp(&orig_mtim, &tv, ==)) {
         /*          /*
          * If mtime and size match but the user spent no measurable           * If mtime and size match but the user spent no measurable
          * time in the editor we can't tell if the file was changed.           * time in the editor we can't tell if the file was changed.
          */           */
        timevalsub(&tv1, &tv2);        if (sudo_timevalcmp(&tv1, &tv2, !=))
        if (timevalisset(&tv2)) 
             modified = false;              modified = false;
     }      }
   
Line 461  edit_sudoers(struct sudoersfile *sp, char *editor, cha Line 459  edit_sudoers(struct sudoersfile *sp, char *editor, cha
     if (modified)      if (modified)
         sp->modified = modified;          sp->modified = modified;
     else      else
        warningx(_("%s unchanged"), sp->tpath);        warningx(U_("%s unchanged"), sp->tpath);
   
     rval = true;      rval = true;
 done:  done:
Line 482  reparse_sudoers(char *editor, char *args, bool strict, Line 480  reparse_sudoers(char *editor, char *args, bool strict,
     /*      /*
      * Parse the edited sudoers files and do sanity checking       * Parse the edited sudoers files and do sanity checking
      */       */
    while ((sp = tq_first(&sudoerslist)) != NULL) {    while ((sp = TAILQ_FIRST(&sudoerslist)) != NULL) {
        last = tq_last(&sudoerslist);        last = TAILQ_LAST(&sudoerslist, sudoersfile_list);
         fp = fopen(sp->tpath, "r+");          fp = fopen(sp->tpath, "r+");
         if (fp == NULL)          if (fp == NULL)
            fatalx(_("unable to re-open temporary file (%s), %s unchanged."),            fatalx(U_("unable to re-open temporary file (%s), %s unchanged."),
                 sp->tpath, sp->path);                  sp->tpath, sp->path);
   
         /* Clean slate for each parse */          /* Clean slate for each parse */
Line 496  reparse_sudoers(char *editor, char *args, bool strict, Line 494  reparse_sudoers(char *editor, char *args, bool strict,
         /* Parse the sudoers temp file(s) */          /* Parse the sudoers temp file(s) */
         sudoersrestart(fp);          sudoersrestart(fp);
         if (sudoersparse() && !parse_error) {          if (sudoersparse() && !parse_error) {
            warningx(_("unabled to parse temporary file (%s), unknown error"),            warningx(U_("unabled to parse temporary file (%s), unknown error"),
                 sp->tpath);                  sp->tpath);
             parse_error = true;              parse_error = true;
             errorfile = sp->path;              errorfile = sp->path;
Line 524  reparse_sudoers(char *editor, char *args, bool strict, Line 522  reparse_sudoers(char *editor, char *args, bool strict,
             case 'e':              case 'e':
             default:              default:
                 /* Edit file with the parse error */                  /* Edit file with the parse error */
                tq_foreach_fwd(&sudoerslist, sp) {                TAILQ_FOREACH(sp, &sudoerslist, entries) {
                     if (errorfile == NULL || strcmp(sp->path, errorfile) == 0) {                      if (errorfile == NULL || strcmp(sp->path, errorfile) == 0) {
                         edit_sudoers(sp, editor, args, errorlineno);                          edit_sudoers(sp, editor, args, errorlineno);
                         if (errorfile != NULL)                          if (errorfile != NULL)
Line 532  reparse_sudoers(char *editor, char *args, bool strict, Line 530  reparse_sudoers(char *editor, char *args, bool strict,
                     }                      }
                 }                  }
                 if (errorfile != NULL && sp == NULL) {                  if (errorfile != NULL && sp == NULL) {
                    fatalx(_("internal error, unable to find %s in list!"),                    fatalx(U_("internal error, unable to find %s in list!"),
                         sudoers);                          sudoers);
                 }                  }
                 break;                  break;
Line 540  reparse_sudoers(char *editor, char *args, bool strict, Line 538  reparse_sudoers(char *editor, char *args, bool strict,
         }          }
   
         /* If any new #include directives were added, edit them too. */          /* If any new #include directives were added, edit them too. */
        for (sp = last->next; sp != NULL; sp = sp->next) {        for (sp = TAILQ_NEXT(last, entries); sp != NULL; sp = TAILQ_NEXT(sp, entries)) {
             printf(_("press return to edit %s: "), sp->path);              printf(_("press return to edit %s: "), sp->path);
             while ((ch = getchar()) != EOF && ch != '\n')              while ((ch = getchar()) != EOF && ch != '\n')
                     continue;                      continue;
Line 588  install_sudoers(struct sudoersfile *sp, bool oldperms) Line 586  install_sudoers(struct sudoersfile *sp, bool oldperms)
     if (oldperms) {      if (oldperms) {
         /* Use perms of the existing file.  */          /* Use perms of the existing file.  */
         if (fstat(sp->fd, &sb) == -1)          if (fstat(sp->fd, &sb) == -1)
            fatal(_("unable to stat %s"), sp->path);            fatal(U_("unable to stat %s"), sp->path);
         if (chown(sp->tpath, sb.st_uid, sb.st_gid) != 0) {          if (chown(sp->tpath, sb.st_uid, sb.st_gid) != 0) {
            warning(_("unable to set (uid, gid) of %s to (%u, %u)"),            warning(U_("unable to set (uid, gid) of %s to (%u, %u)"),
                 sp->tpath, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid);                  sp->tpath, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid);
         }          }
         if (chmod(sp->tpath, sb.st_mode & 0777) != 0) {          if (chmod(sp->tpath, sb.st_mode & 0777) != 0) {
            warning(_("unable to change mode of %s to 0%o"), sp->tpath,            warning(U_("unable to change mode of %s to 0%o"), sp->tpath,
                 (unsigned int)(sb.st_mode & 0777));                  (unsigned int)(sb.st_mode & 0777));
         }          }
     } else {      } else {
         if (chown(sp->tpath, SUDOERS_UID, SUDOERS_GID) != 0) {          if (chown(sp->tpath, SUDOERS_UID, SUDOERS_GID) != 0) {
            warning(_("unable to set (uid, gid) of %s to (%u, %u)"),            warning(U_("unable to set (uid, gid) of %s to (%u, %u)"),
                 sp->tpath, SUDOERS_UID, SUDOERS_GID);                  sp->tpath, SUDOERS_UID, SUDOERS_GID);
             goto done;              goto done;
         }          }
         if (chmod(sp->tpath, SUDOERS_MODE) != 0) {          if (chmod(sp->tpath, SUDOERS_MODE) != 0) {
            warning(_("unable to change mode of %s to 0%o"), sp->tpath,            warning(U_("unable to change mode of %s to 0%o"), sp->tpath,
                 SUDOERS_MODE);                  SUDOERS_MODE);
             goto done;              goto done;
         }          }
Line 621  install_sudoers(struct sudoersfile *sp, bool oldperms) Line 619  install_sudoers(struct sudoersfile *sp, bool oldperms)
     } else {      } else {
         if (errno == EXDEV) {          if (errno == EXDEV) {
             char *av[4];              char *av[4];
            warningx(_("%s and %s not on the same file system, using mv to rename"),            warningx(U_("%s and %s not on the same file system, using mv to rename"),
               sp->tpath, sp->path);                sp->tpath, sp->path);
   
             /* Build up argument vector for the command */              /* Build up argument vector for the command */
Line 635  install_sudoers(struct sudoersfile *sp, bool oldperms) Line 633  install_sudoers(struct sudoersfile *sp, bool oldperms)
   
             /* And run it... */              /* And run it... */
             if (run_command(_PATH_MV, av)) {              if (run_command(_PATH_MV, av)) {
                warningx(_("command failed: '%s %s %s', %s unchanged"),                warningx(U_("command failed: '%s %s %s', %s unchanged"),
                     _PATH_MV, sp->tpath, sp->path, sp->path);                      _PATH_MV, sp->tpath, sp->path, sp->path);
                 (void) unlink(sp->tpath);                  (void) unlink(sp->tpath);
                 efree(sp->tpath);                  efree(sp->tpath);
Line 645  install_sudoers(struct sudoersfile *sp, bool oldperms) Line 643  install_sudoers(struct sudoersfile *sp, bool oldperms)
             efree(sp->tpath);              efree(sp->tpath);
             sp->tpath = NULL;              sp->tpath = NULL;
         } else {          } else {
            warning(_("error renaming %s, %s unchanged"), sp->tpath, sp->path);            warning(U_("error renaming %s, %s unchanged"), sp->tpath, sp->path);
             (void) unlink(sp->tpath);              (void) unlink(sp->tpath);
             goto done;              goto done;
         }          }
Line 762  run_command(char *path, char **argv) Line 760  run_command(char *path, char **argv)
   
     switch (pid = sudo_debug_fork()) {      switch (pid = sudo_debug_fork()) {
         case -1:          case -1:
            fatal(_("unable to execute %s"), path);            fatal(U_("unable to execute %s"), path);
             break;      /* NOTREACHED */              break;      /* NOTREACHED */
         case 0:          case 0:
             sudo_endpwent();              sudo_endpwent();
             sudo_endgrent();              sudo_endgrent();
             closefrom(STDERR_FILENO + 1);              closefrom(STDERR_FILENO + 1);
             execv(path, argv);              execv(path, argv);
            warning(_("unable to run %s"), path);            warning(U_("unable to run %s"), path);
             _exit(127);              _exit(127);
             break;      /* NOTREACHED */              break;      /* NOTREACHED */
     }      }
Line 821  check_syntax(char *sudoers_path, bool quiet, bool stri Line 819  check_syntax(char *sudoers_path, bool quiet, bool stri
         sudoers_path = "stdin";          sudoers_path = "stdin";
     } else if ((sudoersin = fopen(sudoers_path, "r")) == NULL) {      } else if ((sudoersin = fopen(sudoers_path, "r")) == NULL) {
         if (!quiet)          if (!quiet)
            warning(_("unable to open %s"), sudoers_path);            warning(U_("unable to open %s"), sudoers_path);
         goto done;          goto done;
     }      }
     init_parser(sudoers_path, quiet);      init_parser(sudoers_path, quiet);
     if (sudoersparse() && !parse_error) {      if (sudoersparse() && !parse_error) {
         if (!quiet)          if (!quiet)
            warningx(_("failed to parse %s file, unknown error"), sudoers_path);            warningx(U_("failed to parse %s file, unknown error"), sudoers_path);
         parse_error = true;          parse_error = true;
         errorfile = sudoers_path;          errorfile = sudoers_path;
     }      }
Line 858  check_syntax(char *sudoers_path, bool quiet, bool stri Line 856  check_syntax(char *sudoers_path, bool quiet, bool stri
         } else {          } else {
             ok = false;              ok = false;
         }          }
        tq_foreach_fwd(&sudoerslist, sp) {        TAILQ_FOREACH(sp, &sudoerslist, entries) {
             if (oldperms || check_owner(sp->path, quiet)) {              if (oldperms || check_owner(sp->path, quiet)) {
                 if (!quiet)                  if (!quiet)
                     (void) printf(_("%s: parsed OK\n"), sp->path);                      (void) printf(_("%s: parsed OK\n"), sp->path);
Line 890  open_sudoers(const char *path, bool doedit, bool *keep Line 888  open_sudoers(const char *path, bool doedit, bool *keep
         open_flags = O_RDWR | O_CREAT;          open_flags = O_RDWR | O_CREAT;
   
     /* Check for existing entry */      /* Check for existing entry */
    tq_foreach_fwd(&sudoerslist, entry) {    TAILQ_FOREACH(entry, &sudoerslist, entries) {
         if (strcmp(path, entry->path) == 0)          if (strcmp(path, entry->path) == 0)
             break;              break;
     }      }
Line 898  open_sudoers(const char *path, bool doedit, bool *keep Line 896  open_sudoers(const char *path, bool doedit, bool *keep
         entry = ecalloc(1, sizeof(*entry));          entry = ecalloc(1, sizeof(*entry));
         entry->path = estrdup(path);          entry->path = estrdup(path);
         /* entry->modified = 0; */          /* entry->modified = 0; */
         entry->prev = entry;  
         /* entry->next = NULL; */  
         entry->fd = open(entry->path, open_flags, SUDOERS_MODE);          entry->fd = open(entry->path, open_flags, SUDOERS_MODE);
         /* entry->tpath = NULL; */          /* entry->tpath = NULL; */
         entry->doedit = doedit;          entry->doedit = doedit;
Line 909  open_sudoers(const char *path, bool doedit, bool *keep Line 905  open_sudoers(const char *path, bool doedit, bool *keep
             debug_return_ptr(NULL);              debug_return_ptr(NULL);
         }          }
         if (!checkonly && !lock_file(entry->fd, SUDO_TLOCK))          if (!checkonly && !lock_file(entry->fd, SUDO_TLOCK))
            fatalx(_("%s busy, try again later"), entry->path);            fatalx(U_("%s busy, try again later"), entry->path);
         if ((fp = fdopen(entry->fd, "r")) == NULL)          if ((fp = fdopen(entry->fd, "r")) == NULL)
             fatal("%s", entry->path);              fatal("%s", entry->path);
        tq_append(&sudoerslist, entry);        TAILQ_INSERT_TAIL(&sudoerslist, entry, entries);
     } else {      } else {
         /* Already exists, open .tmp version if there is one. */          /* Already exists, open .tmp version if there is one. */
         if (entry->tpath != NULL) {          if (entry->tpath != NULL) {
Line 953  get_editor(char **args) Line 949  get_editor(char **args)
         } else {          } else {
             if (def_env_editor) {              if (def_env_editor) {
                 /* If we are honoring $EDITOR this is a fatal error. */                  /* If we are honoring $EDITOR this is a fatal error. */
                fatalx(_("specified editor (%s) doesn't exist"), UserEditor);                fatalx(U_("specified editor (%s) doesn't exist"), UserEditor);
             } else {              } else {
                 /* Otherwise, just ignore $EDITOR. */                  /* Otherwise, just ignore $EDITOR. */
                 UserEditor = NULL;                  UserEditor = NULL;
Line 976  get_editor(char **args) Line 972  get_editor(char **args)
   
         if (stat(UserEditor, &user_editor_sb) != 0) {          if (stat(UserEditor, &user_editor_sb) != 0) {
             /* Should never happen since we already checked above. */              /* Should never happen since we already checked above. */
            fatal(_("unable to stat editor (%s)"), UserEditor);            fatal(U_("unable to stat editor (%s)"), UserEditor);
         }          }
         EditorPath = estrdup(def_editor);          EditorPath = estrdup(def_editor);
         Editor = strtok(EditorPath, ":");          Editor = strtok(EditorPath, ":");
Line 1024  get_editor(char **args) Line 1020  get_editor(char **args)
   
         /* Bleah, none of the editors existed! */          /* Bleah, none of the editors existed! */
         if (Editor == NULL || *Editor == '\0')          if (Editor == NULL || *Editor == '\0')
            fatalx(_("no editor found (editor path = %s)"), def_editor);            fatalx(U_("no editor found (editor path = %s)"), def_editor);
     }      }
     *args = EditorArgs;      *args = EditorArgs;
     debug_return_str(Editor);      debug_return_str(Editor);
Line 1087  alias_remove_recursive(char *name, int type) Line 1083  alias_remove_recursive(char *name, int type)
     debug_decl(alias_remove_recursive, SUDO_DEBUG_ALIAS)      debug_decl(alias_remove_recursive, SUDO_DEBUG_ALIAS)
   
     if ((a = alias_remove(name, type)) != NULL) {      if ((a = alias_remove(name, type)) != NULL) {
        tq_foreach_fwd(&a->members, m) {        TAILQ_FOREACH(m, &a->members, entries) {
             if (m->type == ALIAS) {              if (m->type == ALIAS) {
                 if (!alias_remove_recursive(m->name, type))                  if (!alias_remove_recursive(m->name, type))
                     rval = false;                      rval = false;
Line 1108  check_alias(char *name, int type, int strict, int quie Line 1104  check_alias(char *name, int type, int strict, int quie
   
     if ((a = alias_get(name, type)) != NULL) {      if ((a = alias_get(name, type)) != NULL) {
         /* check alias contents */          /* check alias contents */
        tq_foreach_fwd(&a->members, m) {        TAILQ_FOREACH(m, &a->members, entries) {
             if (m->type == ALIAS)              if (m->type == ALIAS)
                 errors += check_alias(m->name, type, strict, quiet);                  errors += check_alias(m->name, type, strict, quiet);
         }          }
Line 1117  check_alias(char *name, int type, int strict, int quie Line 1113  check_alias(char *name, int type, int strict, int quie
         if (!quiet) {          if (!quiet) {
             if (errno == ELOOP) {              if (errno == ELOOP) {
                 warningx(strict ?                  warningx(strict ?
                    _("Error: cycle in %s_Alias `%s'") :                    U_("Error: cycle in %s_Alias `%s'") :
                    _("Warning: cycle in %s_Alias `%s'"),                    U_("Warning: cycle in %s_Alias `%s'"),
                     type == HOSTALIAS ? "Host" : type == CMNDALIAS ? "Cmnd" :                      type == HOSTALIAS ? "Host" : type == CMNDALIAS ? "Cmnd" :
                     type == USERALIAS ? "User" : type == RUNASALIAS ? "Runas" :                      type == USERALIAS ? "User" : type == RUNASALIAS ? "Runas" :
                     "Unknown", name);                      "Unknown", name);
             } else {              } else {
                 warningx(strict ?                  warningx(strict ?
                    _("Error: %s_Alias `%s' referenced but not defined") :                    U_("Error: %s_Alias `%s' referenced but not defined") :
                    _("Warning: %s_Alias `%s' referenced but not defined"),                    U_("Warning: %s_Alias `%s' referenced but not defined"),
                     type == HOSTALIAS ? "Host" : type == CMNDALIAS ? "Cmnd" :                      type == HOSTALIAS ? "Host" : type == CMNDALIAS ? "Cmnd" :
                     type == USERALIAS ? "User" : type == RUNASALIAS ? "Runas" :                      type == USERALIAS ? "User" : type == RUNASALIAS ? "Runas" :
                     "Unknown", name);                      "Unknown", name);
Line 1145  static int Line 1141  static int
 check_aliases(bool strict, bool quiet)  check_aliases(bool strict, bool quiet)
 {  {
     struct cmndspec *cs;      struct cmndspec *cs;
    struct member *m, *binding;    struct member *m;
     struct privilege *priv;      struct privilege *priv;
     struct userspec *us;      struct userspec *us;
     struct defaults *d;      struct defaults *d;
Line 1155  check_aliases(bool strict, bool quiet) Line 1151  check_aliases(bool strict, bool quiet)
     alias_freelist = rbcreate(alias_compare);      alias_freelist = rbcreate(alias_compare);
   
     /* Forward check. */      /* Forward check. */
    tq_foreach_fwd(&userspecs, us) {    TAILQ_FOREACH(us, &userspecs, entries) {
        tq_foreach_fwd(&us->users, m) {        TAILQ_FOREACH(m, &us->users, entries) {
             if (m->type == ALIAS) {              if (m->type == ALIAS) {
                 errors += check_alias(m->name, USERALIAS, strict, quiet);                  errors += check_alias(m->name, USERALIAS, strict, quiet);
             }              }
         }          }
        tq_foreach_fwd(&us->privileges, priv) {        TAILQ_FOREACH(priv, &us->privileges, entries) {
            tq_foreach_fwd(&priv->hostlist, m) {            TAILQ_FOREACH(m, &priv->hostlist, entries) {
                 if (m->type == ALIAS) {                  if (m->type == ALIAS) {
                     errors += check_alias(m->name, HOSTALIAS, strict, quiet);                      errors += check_alias(m->name, HOSTALIAS, strict, quiet);
                 }                  }
             }              }
            tq_foreach_fwd(&priv->cmndlist, cs) {            TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
                tq_foreach_fwd(&cs->runasuserlist, m) {                if (cs->runasuserlist != NULL) {
                    if (m->type == ALIAS) {                    TAILQ_FOREACH(m, cs->runasuserlist, entries) {
                        errors += check_alias(m->name, RUNASALIAS, strict, quiet);                        if (m->type == ALIAS) {
                             errors += check_alias(m->name, RUNASALIAS, strict, quiet);
                         }
                     }                      }
                 }                  }
                   if (cs->runasgrouplist != NULL) {
                       TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
                           if (m->type == ALIAS) {
                               errors += check_alias(m->name, RUNASALIAS, strict, quiet);
                           }
                       }
                   }
                 if ((m = cs->cmnd)->type == ALIAS) {                  if ((m = cs->cmnd)->type == ALIAS) {
                     errors += check_alias(m->name, CMNDALIAS, strict, quiet);                      errors += check_alias(m->name, CMNDALIAS, strict, quiet);
                 }                  }
Line 1181  check_aliases(bool strict, bool quiet) Line 1186  check_aliases(bool strict, bool quiet)
     }      }
   
     /* Reverse check (destructive) */      /* Reverse check (destructive) */
    tq_foreach_fwd(&userspecs, us) {    TAILQ_FOREACH(us, &userspecs, entries) {
        tq_foreach_fwd(&us->users, m) {        TAILQ_FOREACH(m, &us->users, entries) {
             if (m->type == ALIAS) {              if (m->type == ALIAS) {
                 if (!alias_remove_recursive(m->name, USERALIAS))                  if (!alias_remove_recursive(m->name, USERALIAS))
                     errors++;                      errors++;
             }              }
         }          }
        tq_foreach_fwd(&us->privileges, priv) {        TAILQ_FOREACH(priv, &us->privileges, entries) {
            tq_foreach_fwd(&priv->hostlist, m) {            TAILQ_FOREACH(m, &priv->hostlist, entries) {
                 if (m->type == ALIAS) {                  if (m->type == ALIAS) {
                     if (!alias_remove_recursive(m->name, HOSTALIAS))                      if (!alias_remove_recursive(m->name, HOSTALIAS))
                         errors++;                          errors++;
                 }                  }
             }              }
            tq_foreach_fwd(&priv->cmndlist, cs) {            TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
                tq_foreach_fwd(&cs->runasuserlist, m) {                if (cs->runasuserlist != NULL) {
                    if (m->type == ALIAS) {                    TAILQ_FOREACH(m, cs->runasuserlist, entries) {
                        if (!alias_remove_recursive(m->name, RUNASALIAS))                        if (m->type == ALIAS) {
                            errors++;                            if (!alias_remove_recursive(m->name, RUNASALIAS))
                                 errors++;
                         }
                     }                      }
                 }                  }
                   if (cs->runasgrouplist != NULL) {
                       TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
                           if (m->type == ALIAS) {
                               if (!alias_remove_recursive(m->name, RUNASALIAS))
                                   errors++;
                           }
                       }
                   }
                 if ((m = cs->cmnd)->type == ALIAS) {                  if ((m = cs->cmnd)->type == ALIAS) {
                     if (!alias_remove_recursive(m->name, CMNDALIAS))                      if (!alias_remove_recursive(m->name, CMNDALIAS))
                         errors++;                          errors++;
Line 1209  check_aliases(bool strict, bool quiet) Line 1224  check_aliases(bool strict, bool quiet)
             }              }
         }          }
     }      }
    tq_foreach_fwd(&defaults, d) {    TAILQ_FOREACH(d, &defaults, entries) {
         switch (d->type) {          switch (d->type) {
             case DEFAULTS_HOST:              case DEFAULTS_HOST:
                 atype = HOSTALIAS;                  atype = HOSTALIAS;
Line 1226  check_aliases(bool strict, bool quiet) Line 1241  check_aliases(bool strict, bool quiet)
             default:              default:
                 continue; /* not an alias */                  continue; /* not an alias */
         }          }
        tq_foreach_fwd(&d->binding, binding) {        TAILQ_FOREACH(m, d->binding, entries) {
            for (m = binding; m != NULL; m = m->next) {            if (m->type == ALIAS) {
                if (m->type == ALIAS) {                if (!alias_remove_recursive(m->name, atype))
                    if (!alias_remove_recursive(m->name, atype))                    errors++;
                        errors++; 
                } 
             }              }
         }          }
     }      }
Line 1250  print_unused(void *v1, void *v2) Line 1263  print_unused(void *v1, void *v2)
     struct alias *a = (struct alias *)v1;      struct alias *a = (struct alias *)v1;
     char *prefix = (char *)v2;      char *prefix = (char *)v2;
   
    warningx_nodebug(_("%s: unused %s_Alias %s"), prefix,    warningx_nodebug(U_("%s: unused %s_Alias %s"), prefix,
         a->type == HOSTALIAS ? "Host" : a->type == CMNDALIAS ? "Cmnd" :          a->type == HOSTALIAS ? "Host" : a->type == CMNDALIAS ? "Cmnd" :
         a->type == USERALIAS ? "User" : a->type == RUNASALIAS ? "Runas" :          a->type == USERALIAS ? "User" : a->type == RUNASALIAS ? "Runas" :
         "Unknown", a->name);          "Unknown", a->name);
Line 1265  visudo_cleanup(void) Line 1278  visudo_cleanup(void)
 {  {
     struct sudoersfile *sp;      struct sudoersfile *sp;
   
    tq_foreach_fwd(&sudoerslist, sp) {    TAILQ_FOREACH(sp, &sudoerslist, entries) {
         if (sp->tpath != NULL)          if (sp->tpath != NULL)
             (void) unlink(sp->tpath);              (void) unlink(sp->tpath);
     }      }
Line 1282  quit(int signo) Line 1295  quit(int signo)
     struct sudoersfile *sp;      struct sudoersfile *sp;
     struct iovec iov[4];      struct iovec iov[4];
   
    tq_foreach_fwd(&sudoerslist, sp) {    TAILQ_FOREACH(sp, &sudoerslist, entries) {
         if (sp->tpath != NULL)          if (sp->tpath != NULL)
             (void) unlink(sp->tpath);              (void) unlink(sp->tpath);
     }      }
Line 1304  static void Line 1317  static void
 usage(int fatal)  usage(int fatal)
 {  {
     (void) fprintf(fatal ? stderr : stdout,      (void) fprintf(fatal ? stderr : stdout,
        "usage: %s [-chqsV] [-f sudoers]\n", getprogname());        "usage: %s [-chqsV] [-f sudoers] [-x file]\n", getprogname());
     if (fatal)      if (fatal)
         exit(1);          exit(1);
 }  }
Line 1315  help(void) Line 1328  help(void)
     (void) printf(_("%s - safely edit the sudoers file\n\n"), getprogname());      (void) printf(_("%s - safely edit the sudoers file\n\n"), getprogname());
     usage(0);      usage(0);
     (void) puts(_("\nOptions:\n"      (void) puts(_("\nOptions:\n"
        "  -c, --check      check-only mode\n"        "  -c, --check       check-only mode\n"
        "  -f, --file=file  specify sudoers file location\n"        "  -f, --file=file   specify sudoers file location\n"
        "  -h, --help       display help message and exit\n"        "  -h, --help        display help message and exit\n"
        "  -q, --quiet      less verbose (quiet) syntax error messages\n"        "  -q, --quiet       less verbose (quiet) syntax error messages\n"
        "  -s, --strict     strict syntax checking\n"        "  -s, --strict      strict syntax checking\n"
        "  -V, --version    display version information and exit"));        "  -V, --version     display version information and exit\n"
         "  -x, --export=file export sudoers in JSON format"));
     exit(0);      exit(0);
 }  }

Removed from v.1.1.1.5  
changed lines
  Added in v.1.1.1.6


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>