[BACK]Return to visudo.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sudo

Diff for /src/usr.bin/sudo/Attic/visudo.c between version 1.21 and 1.22

version 1.21, 2008/07/31 16:44:04 version 1.22, 2008/11/14 11:58:08
Line 1 
Line 1 
 /*  /*
  * Copyright (c) 1996, 1998-2005 Todd C. Miller <Todd.Miller@courtesan.com>   * Copyright (c) 1996, 1998-2005, 2007-2008
    *      Todd C. Miller <Todd.Miller@courtesan.com>
  *   *
  * Permission to use, copy, modify, and distribute this software for any   * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above   * purpose with or without fee is hereby granted, provided that the above
Line 33 
Line 34 
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/param.h>  #include <sys/param.h>
 #include <sys/stat.h>  #include <sys/stat.h>
   #include <sys/socket.h>
 #include <sys/time.h>  #include <sys/time.h>
 #ifndef __TANDEM  #ifndef __TANDEM
 # include <sys/file.h>  # include <sys/file.h>
Line 57 
Line 59 
 #ifdef HAVE_UNISTD_H  #ifdef HAVE_UNISTD_H
 #include <unistd.h>  #include <unistd.h>
 #endif /* HAVE_UNISTD_H */  #endif /* HAVE_UNISTD_H */
 #ifdef HAVE_ERR_H  
 # include <err.h>  
 #else  
 # include "emul/err.h"  
 #endif /* HAVE_ERR_H */  
 #include <ctype.h>  #include <ctype.h>
 #include <pwd.h>  #include <pwd.h>
 #if TIME_WITH_SYS_TIME  #include <grp.h>
 # include <time.h>  
 #endif  
 #include <signal.h>  #include <signal.h>
 #include <errno.h>  #include <errno.h>
 #include <fcntl.h>  #include <fcntl.h>
   #include <netinet/in.h>
   #include <arpa/inet.h>
   #include <netdb.h>
   #if TIME_WITH_SYS_TIME
   # include <time.h>
   #endif
   #ifdef __STDC__
   # include <stdarg.h>
   #else
   # include <varargs.h>
   #endif
 #ifndef HAVE_TIMESPEC  #ifndef HAVE_TIMESPEC
 # include <emul/timespec.h>  # include <emul/timespec.h>
 #endif  #endif
   
 #include "sudo.h"  #include "sudo.h"
   #include "interfaces.h"
   #include "parse.h"
   #include <gram.h>
 #include "version.h"  #include "version.h"
   
 #ifndef lint  #ifndef lint
 __unused static const char rcsid[] = "$Sudo: visudo.c,v 1.166.2.11 2008/06/21 00:47:52 millert Exp $";  __unused static const char rcsid[] = "$Sudo: visudo.c,v 1.220 2008/11/09 20:18:22 millert Exp $";
 #endif /* lint */  #endif /* lint */
   
 struct sudoersfile {  struct sudoersfile {
     char *path;      char *path;
     char *tpath;  
     int fd;      int fd;
     off_t orig_size;      char *tpath;
     struct timespec orig_mtim;      int tfd;
       int modified;
       struct sudoersfile *next;
 };  };
   
 /*  /*
  * Function prototypes   * Function prototypes
  */   */
 static void usage               __P((void)) __attribute__((__noreturn__));  static RETSIGTYPE quit          __P((int));
 static char whatnow             __P((void));  
 static RETSIGTYPE Exit          __P((int));  
 static void edit_sudoers        __P((struct sudoersfile *, char *, char *, int));  
 static void visudo              __P((struct sudoersfile *, char *, char *));  
 static void setup_signals       __P((void));  
 static void install_sudoers     __P((struct sudoersfile *, int));  
 static int check_syntax         __P(());  
 static int run_command          __P((char *, char **));  
 static char *get_args           __P((char *));  static char *get_args           __P((char *));
 static char *get_editor         __P((char **));  static char *get_editor         __P((char **));
 static FILE *open_sudoers       __P((struct sudoersfile *));  static char whatnow             __P((void));
   static int check_aliases        __P((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 setup_signals       __P((void));
   static void usage               __P((void)) __attribute__((__noreturn__));
   
 int command_matches             __P((char *, char *));  extern void yyerror             __P((const char *));
 int addr_matches                __P((char *));  extern void yyrestart           __P((FILE *));
 int hostname_matches            __P((char *, char *, char *));  
 int netgr_matches               __P((char *, char *, char *, char *));  
 int usergr_matches              __P((char *, char *, struct passwd *));  
 int userpw_matches              __P((char *, char *, struct passwd *));  
 void init_parser                __P((void));  
 void yyerror                    __P((char *));  
 void yyrestart                  __P((FILE *));  
   
 /*  /*
  * External globals exported by the parser   * External globals exported by the parser
  */   */
 extern FILE *yyin;  extern FILE *yyin;
 extern int errorlineno;  extern char *sudoers, *errorfile;
 extern int pedantic;  extern int errorlineno, parse_error;
 extern int quiet;  
   
 /* For getopt(3) */  /* For getopt(3) */
 extern char *optarg;  extern char *optarg;
 extern int optind;  extern int optind;
Line 130 
Line 132 
 /*  /*
  * Globals   * Globals
  */   */
   int Argc;
 char **Argv;  char **Argv;
   int num_interfaces;
   struct interface *interfaces;
 struct sudo_user sudo_user;  struct sudo_user sudo_user;
 int Argc, parse_error = FALSE;  struct passwd *list_pw;
 static struct sudoersfile sudoers;  static struct sudoerslist {
       struct sudoersfile *first, *last;
   } sudoerslist;
   
 int  int
 main(argc, argv)  main(argc, argv)
     int argc;      int argc;
     char **argv;      char **argv;
 {  {
     char *args, *editor;      struct sudoersfile *sp;
     int ch, checkonly, n, oldperms;      char *args, *editor, *sudoers_path;
       int ch, checkonly, quiet, strict, oldperms;
   
     /* Initialize sudoers struct. */  
     sudoers.path = _PATH_SUDOERS;  
     sudoers.tpath = _PATH_SUDOERS_TMP;  
     sudoers.fd = -1;  
   
     /* Warn about aliases that are used before being defined. */  
     pedantic = 1;  
   
     Argv = argv;      Argv = argv;
     if ((Argc = argc) < 1)      if ((Argc = argc) < 1)
         usage();          usage();
Line 158 
Line 158 
     /*      /*
      * Arg handling.       * Arg handling.
      */       */
     checkonly = oldperms = FALSE;      checkonly = oldperms = quiet = strict = FALSE;
       sudoers_path = _PATH_SUDOERS;
     while ((ch = getopt(argc, argv, "Vcf:sq")) != -1) {      while ((ch = getopt(argc, argv, "Vcf:sq")) != -1) {
         switch (ch) {          switch (ch) {
             case 'V':              case 'V':
Line 167 
Line 168 
             case 'c':              case 'c':
                 checkonly++;            /* check mode */                  checkonly++;            /* check mode */
                 break;                  break;
             case 'f':                   /* sudoers file path */              case 'f':
                 sudoers.path = optarg;                  sudoers_path = optarg;  /* sudoers file path */
                 easprintf(&sudoers.tpath, "%s.tmp", optarg);  
                 oldperms = TRUE;                  oldperms = TRUE;
                 break;                  break;
             case 's':              case 's':
                 pedantic++;             /* strict mode */                  strict++;               /* strict mode */
                 break;                  break;
             case 'q':              case 'q':
                 quiet++;                /* quiet mode */                  quiet++;                /* quiet mode */
Line 187 
Line 187 
     if (argc)      if (argc)
         usage();          usage();
   
       sudo_setpwent();
       sudo_setgrent();
   
     /* Mock up a fake sudo_user struct. */      /* Mock up a fake sudo_user struct. */
     user_host = user_shost = user_cmnd = "";      user_host = user_shost = user_cmnd = "";
     if ((sudo_user.pw = getpwuid(getuid())) == NULL)      if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL)
         errx(1, "you don't exist in the passwd database");          errorx(1, "you don't exist in the passwd database");
   
     /* Setup defaults data structures. */      /* Setup defaults data structures. */
     init_defaults();      init_defaults();
   
     if (checkonly)      if (checkonly)
         exit(check_syntax());          exit(check_syntax(sudoers_path, quiet, strict));
   
     /*      /*
      * Open and parse the existing sudoers file(s) in quiet mode to highlight       * Parse the existing sudoers file(s) in quiet mode to highlight any
      * any existing errors and to pull in editor and env_editor conf values.       * existing errors and to pull in editor and env_editor conf values.
      */       */
     if ((yyin = open_sudoers(&sudoers)) == NULL)      if ((yyin = open_sudoers(sudoers_path, NULL)) == NULL)
         err(1, "%s", sudoers.path);          error(1, "%s", sudoers_path);
     n = quiet;      init_parser(sudoers_path, 0);
     quiet = 1;  
     init_parser();  
     yyparse();      yyparse();
     parse_error = FALSE;      (void) update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER);
     quiet = n;  
   
     /* Edit sudoers, check for parse errors and re-edit on failure. */  
     editor = get_editor(&args);      editor = get_editor(&args);
     visudo(&sudoers, editor, args);  
   
     /* Install the new sudoers file. */      /* Install signal handlers to clean up temp files if we are killed. */
     install_sudoers(&sudoers, oldperms);      setup_signals();
   
       /* Edit the sudoers file(s) */
       tq_foreach_fwd(&sudoerslist, sp) {
           if (sp != tq_first(&sudoerslist)) {
               printf("press return to edit %s: ", sp->path);
               while ((ch = getchar()) != EOF && ch != '\n')
                       continue;
           }
           edit_sudoers(sp, editor, args, -1);
       }
   
       /* Check edited files for a parse error and re-edit any that fail. */
       reparse_sudoers(editor, args, strict, quiet);
   
       /* Install the sudoers temp files. */
       tq_foreach_fwd(&sudoerslist, sp) {
           if (!sp->modified)
               (void) unlink(sp->tpath);
           else
               (void) install_sudoers(sp, oldperms);
       }
   
     exit(0);      exit(0);
 }  }
   
 /*  /*
  * Edit the sudoers file.   * Edit each sudoers file.
  * Returns TRUE on success, else FALSE.   * Returns TRUE on success, else FALSE.
  */   */
 static void  static int
 edit_sudoers(sp, editor, args, lineno)  edit_sudoers(sp, editor, args, lineno)
     struct sudoersfile *sp;      struct sudoersfile *sp;
     char *editor, *args;      char *editor, *args;
     int lineno;      int lineno;
 {  {
       int tfd;                            /* sudoers temp file descriptor */
       int modified;                       /* was the file modified? */
     int ac;                             /* argument count */      int ac;                             /* argument count */
     char **av;                          /* argument vector for run_command */      char **av;                          /* argument vector for run_command */
     char *cp;                           /* scratch char pointer */      char *cp;                           /* scratch char pointer */
       char buf[PATH_MAX*2];               /* buffer used for copying files */
     char linestr[64];                   /* string version of lineno */      char linestr[64];                   /* string version of lineno */
     struct timespec ts1, ts2;           /* time before and after edit */      struct timespec ts1, ts2;           /* time before and after edit */
       struct timespec orig_mtim;          /* starting mtime of sudoers file */
       off_t orig_size;                    /* starting size of sudoers file */
       ssize_t nread;                      /* number of bytes read */
     struct stat sb;                     /* stat buffer */      struct stat sb;                     /* stat buffer */
   
     /* Make timestamp on temp file match original. */  #ifdef HAVE_FSTAT
     (void) touch(-1, sp->tpath, &sp->orig_mtim);      if (fstat(sp->fd, &sb) == -1)
   #else
       if (stat(sp->path, &sb) == -1)
   #endif
           error(1, "can't stat %s", sp->path);
       orig_size = sb.st_size;
       orig_mtim.tv_sec = mtim_getsec(sb);
       orig_mtim.tv_nsec = mtim_getnsec(sb);
   
       /* Create the temp file if needed and set timestamp. */
       if (sp->tpath == NULL) {
           easprintf(&sp->tpath, "%s.tmp", sp->path);
           tfd = open(sp->tpath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
           if (tfd < 0)
               error(1, "%s", sp->tpath);
   
           /* Copy sp->path -> sp->tpath and reset the mtime. */
           if (orig_size != 0) {
               (void) lseek(sp->fd, (off_t)0, SEEK_SET);
               while ((nread = read(sp->fd, buf, sizeof(buf))) > 0)
                   if (write(tfd, buf, nread) != nread)
                       error(1, "write error");
   
               /* Add missing newline at EOF if needed. */
               if (nread > 0 && buf[nread - 1] != '\n') {
                   buf[0] = '\n';
                   write(tfd, buf, 1);
               }
           }
           (void) close(tfd);
       }
       (void) touch(-1, sp->tpath, &orig_mtim);
   
     /* Find the length of the argument vector */      /* Find the length of the argument vector */
     ac = 3 + (lineno > 0);      ac = 3 + (lineno > 0);
     if (args) {      if (args) {
Line 288 
Line 344 
          * Sanity checks.           * Sanity checks.
          */           */
         if (stat(sp->tpath, &sb) < 0) {          if (stat(sp->tpath, &sb) < 0) {
             warnx("cannot stat temporary file (%s), %s unchanged",              warningx("cannot stat temporary file (%s), %s unchanged",
                 sp->tpath, sp->path);                  sp->tpath, sp->path);
             Exit(-1);              return(FALSE);
         }          }
         if (sb.st_size == 0) {          if (sb.st_size == 0 && orig_size != 0) {
             warnx("zero length temporary file (%s), %s unchanged",              warningx("zero length temporary file (%s), %s unchanged",
                 sp->tpath, sp->path);                  sp->tpath, sp->path);
             Exit(-1);              sp->modified = TRUE;
               return(FALSE);
         }          }
     } else {      } else {
         warnx("editor (%s) failed, %s unchanged", editor, sp->path);          warningx("editor (%s) failed, %s unchanged", editor, sp->path);
         Exit(-1);          return(FALSE);
     }      }
   
     /* Check to see if the user changed the file. */      /* Set modified bit if use changed the file. */
     if (sp->orig_size == sb.st_size &&      modified = TRUE;
         sp->orig_mtim.tv_sec == mtim_getsec(sb) &&      if (orig_size == sb.st_size &&
         sp->orig_mtim.tv_nsec == mtim_getnsec(sb)) {          orig_mtim.tv_sec == mtim_getsec(sb) &&
           orig_mtim.tv_nsec == mtim_getnsec(sb)) {
         /*          /*
          * 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.
Line 315 
Line 373 
 #else  #else
         timespecsub(&ts1, &ts2, &ts2);          timespecsub(&ts1, &ts2, &ts2);
 #endif  #endif
         if (timespecisset(&ts2)) {          if (timespecisset(&ts2))
             warnx("%s unchanged", sp->tpath);              modified = FALSE;
             Exit(0);  
         }  
     }      }
   
       /*
        * If modified in this edit session, mark as modified.
        */
       if (modified)
           sp->modified = modified;
       else
           warningx("%s unchanged", sp->tpath);
   
       return(TRUE);
 }  }
   
 /*  /*
  * Parse sudoers after editing and re-edit any ones that caused a parse error.   * Parse sudoers after editing and re-edit any ones that caused a parse error.
  * Returns TRUE on success, else FALSE.   * Returns TRUE on success, else FALSE.
  */   */
 static void  static int
 visudo(sp, editor, args)  reparse_sudoers(editor, args, strict, quiet)
     struct sudoersfile *sp;  
     char *editor, *args;      char *editor, *args;
       int strict, quiet;
 {  {
       struct sudoersfile *sp, *last;
       FILE *fp;
     int ch;      int ch;
   
     /*      /*
      * Parse the edited sudoers file and do sanity checking       * Parse the edited sudoers files and do sanity checking
      */       */
     do {      do {
         edit_sudoers(sp, editor, args, errorlineno);          sp = tq_first(&sudoerslist);
           last = tq_last(&sudoerslist);
         yyin = fopen(sp->tpath, "r+");          fp = fopen(sp->tpath, "r+");
         if (yyin == NULL) {          if (fp == NULL)
             warnx("can't re-open temporary file (%s), %s unchanged.",              errorx(1, "can't re-open temporary file (%s), %s unchanged.",
                 sp->tpath, sp->path);                  sp->tpath, sp->path);
             Exit(-1);  
         }  
   
         /* Add missing newline at EOF if needed. */  
         if (fseek(yyin, -1, SEEK_END) == 0 && (ch = fgetc(yyin)) != '\n')  
             fputc('\n', yyin);  
         rewind(yyin);  
   
         /* Clean slate for each parse */          /* Clean slate for each parse */
         user_runas = NULL;  
         init_defaults();          init_defaults();
         init_parser();          init_parser(sp->path, quiet);
   
         /* Parse the sudoers temp file */          /* Parse the sudoers temp file */
         yyrestart(yyin);          yyrestart(fp);
         if (yyparse() && parse_error != TRUE) {          if (yyparse() && parse_error != TRUE) {
             warnx("unabled to parse temporary file (%s), unknown error",              warningx("unabled to parse temporary file (%s), unknown error",
                 sp->tpath);                  sp->tpath);
             parse_error = TRUE;              parse_error = TRUE;
         }          }
         fclose(yyin);          fclose(yyin);
           if (check_aliases(strict) != 0)
               parse_error = TRUE;
   
         /*          /*
          * Got an error, prompt the user for what to do now           * Got an error, prompt the user for what to do now
Line 372 
Line 434 
             switch (whatnow()) {              switch (whatnow()) {
                 case 'Q' :      parse_error = FALSE;    /* ignore parse error */                  case 'Q' :      parse_error = FALSE;    /* ignore parse error */
                                 break;                                  break;
                 case 'x' :      Exit(0);                  case 'x' :      cleanup(0);
                                   exit(0);
                                 break;                                  break;
             }              }
         }          }
           if (parse_error) {
               /* Edit file with the parse error */
               tq_foreach_fwd(&sudoerslist, sp) {
                   if (errorfile == NULL || strcmp(sp->path, errorfile) == 0) {
                       edit_sudoers(sp, editor, args, errorlineno);
                       break;
                   }
               }
               if (sp == NULL)
                   errorx(1, "internal error, can't find %s in list!", sudoers);
           }
   
           /* If any new #include directives were added, edit them too. */
           for (sp = last->next; sp != NULL; sp = sp->next) {
               printf("press return to edit %s: ", sp->path);
               while ((ch = getchar()) != EOF && ch != '\n')
                       continue;
               edit_sudoers(sp, editor, args, errorlineno);
           }
     } while (parse_error);      } while (parse_error);
   
       return(TRUE);
 }  }
   
 /*  /*
  * Set the owner and mode on a sudoers temp file and   * Set the owner and mode on a sudoers temp file and
  * move it into place.  Returns TRUE on success, else FALSE.   * move it into place.  Returns TRUE on success, else FALSE.
  */   */
 static void  static int
 install_sudoers(sp, oldperms)  install_sudoers(sp, oldperms)
     struct sudoersfile *sp;      struct sudoersfile *sp;
     int oldperms;      int oldperms;
Line 401 
Line 485 
 #else  #else
         if (stat(sp->path, &sb) == -1)          if (stat(sp->path, &sb) == -1)
 #endif  #endif
             err(1, "can't stat %s", sp->path);              error(1, "can't stat %s", sp->path);
         (void) chown(sp->tpath, sb.st_uid, sb.st_gid);          (void) chown(sp->tpath, sb.st_uid, sb.st_gid);
         (void) chmod(sp->tpath, sb.st_mode & 0777);          (void) chmod(sp->tpath, sb.st_mode & 0777);
     } else {      } else {
         if (chown(sp->tpath, SUDOERS_UID, SUDOERS_GID) != 0) {          if (chown(sp->tpath, SUDOERS_UID, SUDOERS_GID) != 0) {
             warn("unable to set (uid, gid) of %s to (%d, %d)",              warning("unable to set (uid, gid) of %s to (%d, %d)",
                 sp->tpath, SUDOERS_UID, SUDOERS_GID);                  sp->tpath, SUDOERS_UID, SUDOERS_GID);
             Exit(-1);              return(FALSE);
         }          }
         if (chmod(sp->tpath, SUDOERS_MODE) != 0) {          if (chmod(sp->tpath, SUDOERS_MODE) != 0) {
             warn("unable to change mode of %s to 0%o", sp->tpath, SUDOERS_MODE);              warning("unable to change mode of %s to 0%o", sp->tpath,
             Exit(-1);                  SUDOERS_MODE);
               return(FALSE);
         }          }
     }      }
   
Line 421 
Line 506 
      * rename(2)'d to sp->path.  If the rename(2) fails we try using       * rename(2)'d to sp->path.  If the rename(2) fails we try using
      * mv(1) in case sp->tpath and sp->path are on different file systems.       * mv(1) in case sp->tpath and sp->path are on different file systems.
      */       */
     if (rename(sp->tpath, sp->path) != 0) {      if (rename(sp->tpath, sp->path) == 0) {
           efree(sp->tpath);
           sp->tpath = NULL;
       } else {
         if (errno == EXDEV) {          if (errno == EXDEV) {
             char *av[4];              char *av[4];
             warnx("%s and %s not on the same file system, using mv to rename",              warningx("%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 438 
Line 526 
   
             /* And run it... */              /* And run it... */
             if (run_command(_PATH_MV, av)) {              if (run_command(_PATH_MV, av)) {
                 warnx("command failed: '%s %s %s', %s unchanged",                  warningx("command failed: '%s %s %s', %s unchanged",
                     _PATH_MV, sp->tpath, sp->path, sp->path);                      _PATH_MV, sp->tpath, sp->path, sp->path);
                 Exit(-1);                  (void) unlink(sp->tpath);
                   efree(sp->tpath);
                   sp->tpath = NULL;
                   return(FALSE);
             }              }
               efree(sp->tpath);
               sp->tpath = NULL;
         } else {          } else {
             warn("error renaming %s, %s unchanged", sp->tpath, sp->path);              warning("error renaming %s, %s unchanged", sp->tpath, sp->path);
             Exit(-1);              (void) unlink(sp->tpath);
               return(FALSE);
         }          }
     }      }
 }  
   
 /*  
  * Dummy *_matches routines.  
  * These exist to allow us to use the same parser as sudo(8).  
  */  
 int  
 command_matches(path, sudoers_args)  
     char *path;  
     char *sudoers_args;  
 {  
     return(TRUE);      return(TRUE);
 }  }
   
 int  /* STUB */
 addr_matches(n)  void
     char *n;  set_fqdn()
 {  {
     return(TRUE);      return;
 }  }
   
 int  /* STUB */
 hostname_matches(s, l, p)  void
     char *s, *l, *p;  init_envtables()
 {  {
     return(TRUE);      return;
 }  }
   
   /* STUB */
 int  int
 usergr_matches(g, u, pw)  user_is_exempt()
     char *g, *u;  
     struct passwd *pw;  
 {  {
     return(TRUE);      return(FALSE);
 }  }
   
 int  /* STUB */
 userpw_matches(s, u, pw)  void
     char *s, *u;  sudo_setspent()
     struct passwd *pw;  
 {  {
     return(TRUE);      return;
 }  }
   
 int  /* STUB */
 netgr_matches(n, h, sh, u)  
     char *n, *h, *sh, *u;  
 {  
     return(TRUE);  
 }  
   
 void  void
 set_fqdn()  sudo_endspent()
 {  {
     return;      return;
 }  }
   
 int  char *
 set_runaspw(user)  sudo_getepw(pw)
     char *user;      const struct passwd *pw;
 {  {
     extern int sudolineno, used_runas;      return (pw->pw_passwd);
   
     if (used_runas) {  
         (void) fprintf(stderr,  
             "%s: runas_default set after old value is in use near line %d\n",  
             pedantic > 1 ? "Error" : "Warning", sudolineno);  
         if (pedantic > 1)  
             yyerror(NULL);  
     }  
     return(TRUE);  
 }  }
   
 int  
 user_is_exempt()  
 {  
     return(TRUE);  
 }  
   
 void  
 init_envtables()  
 {  
     return;  
 }  
   
 /*  /*
  * Assuming a parse error occurred, prompt the user for what they want   * Assuming a parse error occurred, prompt the user for what they want
  * to do now.  Returns the first letter of their choice.   * to do now.  Returns the first letter of their choice.
Line 575 
Line 629 
         /*          /*
          * Setup signal handlers to cleanup nicely.           * Setup signal handlers to cleanup nicely.
          */           */
           zero_bytes(&sa, sizeof(sa));
         sigemptyset(&sa.sa_mask);          sigemptyset(&sa.sa_mask);
         sa.sa_flags = SA_RESTART;          sa.sa_flags = SA_RESTART;
         sa.sa_handler = Exit;          sa.sa_handler = quit;
         (void) sigaction(SIGTERM, &sa, NULL);          (void) sigaction(SIGTERM, &sa, NULL);
         (void) sigaction(SIGHUP, &sa, NULL);          (void) sigaction(SIGHUP, &sa, NULL);
         (void) sigaction(SIGINT, &sa, NULL);          (void) sigaction(SIGINT, &sa, NULL);
Line 594 
Line 649 
   
     switch (pid = fork()) {      switch (pid = fork()) {
         case -1:          case -1:
             warn("unable to run %s", path);              error(1, "unable to run %s", path);
             Exit(-1);  
             break;      /* NOTREACHED */              break;      /* NOTREACHED */
         case 0:          case 0:
             endpwent();              sudo_endpwent();
               sudo_endgrent();
             closefrom(STDERR_FILENO + 1);              closefrom(STDERR_FILENO + 1);
             execv(path, argv);              execv(path, argv);
             warn("unable to run %s", path);              warning("unable to run %s", path);
             _exit(127);              _exit(127);
             break;      /* NOTREACHED */              break;      /* NOTREACHED */
     }      }
Line 620 
Line 675 
 }  }
   
 static int  static int
 check_syntax()  check_syntax(sudoers_path, quiet, strict)
       char *sudoers_path;
       int quiet;
       int strict;
 {  {
       struct stat sb;
       int error;
   
     if ((yyin = fopen(sudoers.path, "r")) == NULL) {      if ((yyin = fopen(sudoers_path, "r")) == NULL) {
         if (!quiet)          if (!quiet)
             warn("unable to open %s", sudoers.path);              warning("unable to open %s", sudoers_path);
         exit(1);          exit(1);
     }      }
     init_parser();      init_parser(sudoers_path, quiet);
     if (yyparse() && parse_error != TRUE) {      if (yyparse() && parse_error != TRUE) {
         if (!quiet)          if (!quiet)
             warnx("failed to parse %s file, unknown error", sudoers.path);              warningx("failed to parse %s file, unknown error", sudoers_path);
         parse_error = TRUE;          parse_error = TRUE;
     }      }
     if (!quiet){      error = parse_error;
       if (!quiet) {
         if (parse_error)          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", sudoers_path,
                 errorlineno);                  errorlineno);
         else          else
             (void) printf("%s file parsed OK\n", sudoers.path);              (void) printf("%s: parsed OK\n", sudoers_path);
     }      }
       /* Check mode and owner in strict mode. */
   #ifdef HAVE_FSTAT
       if (strict && fstat(fileno(yyin), &sb) == 0)
   #else
       if (strict && stat(sudoers_path, &sb) == 0)
   #endif
       {
           if (sb.st_uid != SUDOERS_UID || sb.st_gid != SUDOERS_GID) {
               error = TRUE;
               if (!quiet) {
                   fprintf(stderr, "%s: wrong owner (uid, gid) should be (%d, %d)\n",
                       sudoers_path, SUDOERS_UID, SUDOERS_GID);
                   }
           }
           if ((sb.st_mode & 07777) != SUDOERS_MODE) {
               error = TRUE;
               if (!quiet) {
                   fprintf(stderr, "%s: bad permissions, should be mode 0%o\n",
                       sudoers_path, SUDOERS_MODE);
               }
           }
       }
   
     return(parse_error == TRUE);      return(error);
 }  }
   
 static FILE *  /*
 open_sudoers(sp)   * Used to open (and lock) the initial sudoers file and to also open
     struct sudoersfile *sp;   * any subsequent files #included via a callback from the parser.
    */
   FILE *
   open_sudoers(path, keepopen)
       const char *path;
       int *keepopen;
 {  {
     struct stat sb;      struct sudoersfile *entry;
     ssize_t nread;  
     FILE *fp;      FILE *fp;
     char buf[PATH_MAX*2];  
     int tfd;  
   
     /* Open and lock sudoers. */      /* Check for existing entry */
     sp->fd = open(sp->path, O_RDWR | O_CREAT, SUDOERS_MODE);      tq_foreach_fwd(&sudoerslist, entry) {
     if (sp->fd == -1)          if (strcmp(path, entry->path) == 0)
         err(1, "%s", sp->path);              break;
     if (!lock_file(sp->fd, SUDO_TLOCK))      }
         errx(1, "%s busy, try again later", sp->path);      if (entry == NULL) {
     if ((fp = fdopen(sp->fd, "r")) == NULL)          entry = emalloc(sizeof(*entry));
         err(1, "%s", sp->path);          entry->path = estrdup(path);
           entry->modified = 0;
     /* Stash sudoers size and mtime. */          entry->next = NULL;
 #ifdef HAVE_FSTAT          entry->fd = open(entry->path, O_RDWR | O_CREAT, SUDOERS_MODE);
     if (fstat(sp->fd, &sb) == -1)          entry->tpath = NULL;
 #else          if (entry->fd == -1) {
     if (stat(sp->path, &sb) == -1)              warning("%s", entry->path);
 #endif              efree(entry);
         err(1, "can't stat %s", sp->path);              return(NULL);
     sp->orig_size = sb.st_size;  
     sp->orig_mtim.tv_sec = mtim_getsec(sb);  
     sp->orig_mtim.tv_nsec = mtim_getnsec(sb);  
   
     /* Create the temp file. */  
     tfd = open(sp->tpath, O_WRONLY | O_CREAT | O_TRUNC, 0600);  
     if (tfd < 0)  
         err(1, "%s", sp->tpath);  
   
     /* Install signal handlers to clean up temp file if we are killed. */  
     setup_signals();  
   
     /* Copy sp->path -> sp->tpath. */  
     if (sp->orig_size != 0) {  
         while ((nread = read(sp->fd, buf, sizeof(buf))) > 0)  
             if (write(tfd, buf, nread) != nread) {  
                 warn("write error");  
                 Exit(-1);  
             }  
   
         /* Add missing newline at EOF if needed. */  
         if (nread > 0 && buf[nread - 1] != '\n') {  
             buf[0] = '\n';  
             write(tfd, buf, 1);  
         }          }
           if (!lock_file(entry->fd, SUDO_TLOCK))
               errorx(1, "%s busy, try again later", entry->path);
           if ((fp = fdopen(entry->fd, "r")) == NULL)
               error(1, "%s", entry->path);
           /* XXX - macro here? */
           if (sudoerslist.last == NULL)
               sudoerslist.first = sudoerslist.last = entry;
           else {
               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) {
               if ((fp = fopen(entry->tpath, "r")) == NULL)
                   error(1, "%s", entry->tpath);
           } else {
               if ((fp = fdopen(entry->fd, "r")) == NULL)
                   error(1, "%s", entry->path);
           }
     }      }
     (void) close(tfd);  
     rewind(fp);  
   
     return(fp);      return(fp);
 }  }
   
Line 727 
Line 807 
         } 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. */
                 warnx("specified editor (%s) doesn't exist!", UserEditor);                  errorx(1, "specified editor (%s) doesn't exist!", UserEditor);
                 Exit(-1);  
             } else {              } else {
                 /* Otherwise, just ignore $EDITOR. */                  /* Otherwise, just ignore $EDITOR. */
                 UserEditor = NULL;                  UserEditor = NULL;
Line 751 
Line 830 
   
         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. */
             warn("unable to stat editor (%s)", UserEditor);              error(1, "unable to stat editor (%s)", UserEditor);
             Exit(-1);  
         }          }
         EditorPath = estrdup(def_editor);          EditorPath = estrdup(def_editor);
         Editor = strtok(EditorPath, ":");          Editor = strtok(EditorPath, ":");
Line 799 
Line 877 
         } while ((Editor = strtok(NULL, ":")));          } while ((Editor = strtok(NULL, ":")));
   
         /* Bleah, none of the editors existed! */          /* Bleah, none of the editors existed! */
         if (Editor == NULL || *Editor == '\0') {          if (Editor == NULL || *Editor == '\0')
             warnx("no editor found (editor path = %s)", def_editor);              errorx(1, "no editor found (editor path = %s)", def_editor);
             Exit(-1);  
         }  
     }      }
     *args = EditorArgs;      *args = EditorArgs;
     return(Editor);      return(Editor);
Line 829 
Line 905 
 }  }
   
 /*  /*
  * Unlink the sudoers temp file (if it exists) and exit.   * Iterate through the sudoers datastructures looking for undefined
  * Used in place of a normal exit() and as a signal handler.   * aliases or unused aliases.
  * A positive parameter indicates we were called as a signal handler.  
  */   */
 static RETSIGTYPE  static int
 Exit(sig)  check_aliases(strict)
     int sig;      int strict;
 {  {
 #define emsg     " exiting due to signal.\n"      struct cmndspec *cs;
       struct member *m;
       struct privilege *priv;
       struct userspec *us;
       int error = 0;
   
     (void) unlink(sudoers.tpath);      /* Forward check. */
       tq_foreach_fwd(&userspecs, us) {
           tq_foreach_fwd(&us->users, m) {
               if (m->type == USERALIAS) {
                   if (find_alias(m->name, m->type) == NULL) {
                       warningx("%s: User_Alias `%s' referenced but not defined",
                           strict ? "Error" : "Warning", m->name);
                       error++;
                   }
               }
           }
           tq_foreach_fwd(&us->privileges, priv) {
               tq_foreach_fwd(&priv->hostlist, m) {
                   if (m->type == HOSTALIAS) {
                       if (find_alias(m->name, m->type) == NULL) {
                           warningx("%s: Host_Alias `%s' referenced but not defined",
                               strict ? "Error" : "Warning", m->name);
                           error++;
                       }
                   }
               }
               tq_foreach_fwd(&priv->cmndlist, cs) {
                   tq_foreach_fwd(&cs->runasuserlist, m) {
                       if (m->type == RUNASALIAS) {
                           if (find_alias(m->name, m->type) == NULL) {
                               warningx("%s: Runas_Alias `%s' referenced but not defined",
                                   strict ? "Error" : "Warning", m->name);
                               error++;
                           }
                       }
                   }
                   if ((m = cs->cmnd)->type == CMNDALIAS) {
                       if (find_alias(m->name, m->type) == NULL) {
                           warningx("%s: Cmnd_Alias `%s' referenced but not defined",
                               strict ? "Error" : "Warning", m->name);
                           error++;
                       }
                   }
               }
           }
       }
   
     if (sig > 0) {      /* Reverse check (destructive) */
         write(STDERR_FILENO, getprogname(), strlen(getprogname()));      tq_foreach_fwd(&userspecs, us) {
         write(STDERR_FILENO, emsg, sizeof(emsg) - 1);          tq_foreach_fwd(&us->users, m) {
         _exit(sig);              if (m->type == USERALIAS)
                   (void) alias_remove(m->name, m->type);
           }
           tq_foreach_fwd(&us->privileges, priv) {
               tq_foreach_fwd(&priv->hostlist, m) {
                   if (m->type == HOSTALIAS)
                       (void) alias_remove(m->name, m->type);
               }
               tq_foreach_fwd(&priv->cmndlist, cs) {
                   tq_foreach_fwd(&cs->runasuserlist, m) {
                       if (m->type == RUNASALIAS)
                           (void) alias_remove(m->name, m->type);
                   }
                   if ((m = cs->cmnd)->type == CMNDALIAS)
                       (void) alias_remove(m->name, m->type);
               }
           }
     }      }
     exit(-sig);      /* If all aliases were referenced we will have an empty tree. */
       if (no_aliases())
           return(0);
       alias_apply(print_unused, strict ? "Error" : "Warning");
       return (strict ? 1 : 0);
   }
   
   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);
 }  }
   
 static void  static void

Legend:
Removed from v.1.21  
changed lines
  Added in v.1.22