[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.10 and 1.11

version 1.10, 2001/11/02 17:59:06 version 1.11, 2002/01/03 03:49:16
Line 1 
Line 1 
 /*  /*
  * Copyright (c) 1996, 1998-2000 Todd C. Miller <Todd.Miller@courtesan.com>   * Copyright (c) 1996, 1998-2001 Todd C. Miller <Todd.Miller@courtesan.com>
  * All rights reserved.   * All rights reserved.
  *   *
  * Redistribution and use in source and binary forms, with or without   * Redistribution and use in source and binary forms, with or without
Line 38 
Line 38 
   
 #include "config.h"  #include "config.h"
   
   #include <sys/types.h>
   #include <sys/param.h>
   #include <sys/stat.h>
   #include <sys/file.h>
   #include <sys/wait.h>
 #include <stdio.h>  #include <stdio.h>
 #ifdef STDC_HEADERS  #ifdef STDC_HEADERS
 #include <stdlib.h>  # include <stdlib.h>
   # include <stddef.h>
   #else
   # ifdef HAVE_STDLIB_H
   #  include <stdlib.h>
   # endif
 #endif /* STDC_HEADERS */  #endif /* STDC_HEADERS */
   #ifdef HAVE_STRING_H
   # include <string.h>
   #else
   # ifdef HAVE_STRINGS_H
   #  include <strings.h>
   # endif
   #endif /* HAVE_STRING_H */
 #ifdef HAVE_UNISTD_H  #ifdef HAVE_UNISTD_H
 #include <unistd.h>  #include <unistd.h>
 #endif /* HAVE_UNISTD_H */  #endif /* HAVE_UNISTD_H */
 #ifdef HAVE_STRING_H  
 #include <string.h>  
 #endif /* HAVE_STRING_H */  
 #ifdef HAVE_STRINGS_H  
 #include <strings.h>  
 #endif /* HAVE_STRINGS_H */  
 #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)  
 #include <malloc.h>  
 #endif /* HAVE_MALLOC_H && !STDC_HEADERS */  
 #include <ctype.h>  #include <ctype.h>
 #include <pwd.h>  #include <pwd.h>
 #include <time.h>  #include <time.h>
 #include <signal.h>  #include <signal.h>
 #include <errno.h>  #include <errno.h>
 #include <fcntl.h>  #include <fcntl.h>
 #include <sys/types.h>  
 #include <sys/param.h>  
 #include <sys/stat.h>  
 #include <sys/file.h>  
   
 #include "sudo.h"  #include "sudo.h"
 #include "version.h"  #include "version.h"
   
 #ifndef STDC_HEADERS  
 #ifndef __GNUC__        /* gcc has its own malloc */  
 extern char *malloc     __P((size_t));  
 #endif /* __GNUC__ */  
 extern char *getenv     __P((const char *));  
 extern int stat         __P((const char *, struct stat *));  
 #endif /* !STDC_HEADERS */  
   
 #if defined(POSIX_SIGNALS) && !defined(SA_RESETHAND)  
 #define SA_RESETHAND    0  
 #endif /* POSIX_SIGNALS && !SA_RESETHAND */  
   
 #ifndef lint  #ifndef lint
 static const char rcsid[] = "$Sudo: visudo.c,v 1.126 2000/03/23 04:38:22 millert Exp $";  static const char rcsid[] = "$Sudo: visudo.c,v 1.145 2001/12/14 19:52:48 millert Exp $";
 #endif /* lint */  #endif /* lint */
   
 /*  /*
Line 91 
Line 83 
 static char whatnow             __P((void));  static char whatnow             __P((void));
 static RETSIGTYPE Exit          __P((int));  static RETSIGTYPE Exit          __P((int));
 static void setup_signals       __P((void));  static void setup_signals       __P((void));
   static int run_command          __P((char *, char **));
   static int check_syntax         __P((int));
 int command_matches             __P((char *, char *, char *, char *));  int command_matches             __P((char *, char *, char *, char *));
 int addr_matches                __P((char *));  int addr_matches                __P((char *));
 int hostname_matches            __P((char *, char *, char *));  int hostname_matches            __P((char *, char *, char *));
Line 105 
Line 99 
 extern FILE *yyin, *yyout;  extern FILE *yyin, *yyout;
 extern int errorlineno;  extern int errorlineno;
 extern int pedantic;  extern int pedantic;
   extern int quiet;
   
   /* For getopt(3) */
   extern char *optarg;
   extern int optind;
   
 /*  /*
  * Globals   * Globals
  */   */
Line 115 
Line 114 
 struct sudo_user sudo_user;  struct sudo_user sudo_user;
 int parse_error = FALSE;  int parse_error = FALSE;
   
   
 int  int
 main(argc, argv)  main(argc, argv)
     int argc;      int argc;
Line 123 
Line 121 
 {  {
     char buf[MAXPATHLEN*2];             /* buffer used for copying files */      char buf[MAXPATHLEN*2];             /* buffer used for copying files */
     char *Editor;                       /* editor to use */      char *Editor;                       /* editor to use */
       char *UserEditor;                   /* editor user wants to use */
       char *EditorPath;                   /* colon-separated list of editors */
       char *av[4];                        /* argument vector for run_command */
       int checkonly;                      /* only check existing file? */
     int sudoers_fd;                     /* sudoers file descriptor */      int sudoers_fd;                     /* sudoers file descriptor */
     int stmp_fd;                        /* stmp file descriptor */      int stmp_fd;                        /* stmp file descriptor */
     int n;                              /* length parameter */      int n;                              /* length parameter */
       int ch;                             /* getopt char */
     time_t now;                         /* time now */      time_t now;                         /* time now */
     struct stat stmp_sb, sudoers_sb;    /* to check for changes */      struct stat stmp_sb, sudoers_sb;    /* to check for changes */
   
Line 140 
Line 143 
     /*      /*
      * Arg handling.       * Arg handling.
      */       */
     while (--argc) {      checkonly = 0;
         if (!strcmp(argv[argc], "-V")) {      while ((ch = getopt(argc, argv, "Vcf:sq")) != -1) {
             (void) printf("visudo version %s\n", version);          switch (ch) {
             exit(0);              case 'V':
         } else if (!strcmp(argv[argc], "-s")) {                  (void) printf("visudo version %s\n", version);
             pedantic++;                 /* strict mode */                  exit(0);
         } else {              case 'c':
             usage();                  checkonly++;            /* check mode */
                   break;
               case 'f':
                   sudoers = optarg;       /* sudoers file path */
                   easprintf(&stmp, "%s.tmp", optarg);
                   break;
               case 's':
                   pedantic++;             /* strict mode */
                   break;
               case 'q':
                   quiet++;                /* quiet mode */
                   break;
               default:
                   usage();
         }          }
     }      }
       argc -= optind;
       argv += optind;
       if (argc)
           usage();
   
     /* 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 = "";
Line 162 
Line 182 
     /* Setup defaults data structures. */      /* Setup defaults data structures. */
     init_defaults();      init_defaults();
   
       if (checkonly)
           exit(check_syntax(quiet));
   
     /*      /*
      * Open sudoers, lock it and stat it.       * Open sudoers, lock it and stat it.
      * sudoers_fd must remain open throughout in order to hold the lock.       * sudoers_fd must remain open throughout in order to hold the lock.
Line 224 
Line 247 
         (void) close(stmp_fd);          (void) close(stmp_fd);
   
     /*      /*
      * If we are allowing EDITOR and VISUAL envariables set Editor       * Check EDITOR and VISUAL environment variables to see which editor
      * base on whichever exists...       * the user wants to use (we may not end up using it though).
        * If the path is not fully-qualified, make it so and check that
        * the specified executable actually exists.
      */       */
     if (!def_flag(I_ENV_EDITOR) ||      if ((UserEditor = getenv("EDITOR")) == NULL || *UserEditor == '\0')
         (!(Editor = getenv("EDITOR")) && !(Editor = getenv("VISUAL"))))          UserEditor = getenv("VISUAL");
         Editor = estrdup(def_str(I_EDITOR));      if (UserEditor && *UserEditor == '\0')
           UserEditor = NULL;
       else if (UserEditor) {
           if (find_path(UserEditor, &Editor, getenv("PATH")) == FOUND) {
               UserEditor = Editor;
           } else {
               if (def_flag(I_ENV_EDITOR)) {
                   /* If we are honoring $EDITOR this is a fatal error. */
                   (void) fprintf(stderr,
                       "%s: specified editor (%s) doesn't exist!\n",
                       Argv[0], UserEditor);
                   Exit(-1);
               } else {
                   /* Otherwise, just ignore $EDITOR. */
                   UserEditor = NULL;
               }
           }
       }
   
     /*      /*
      * Edit the temp file and parse it (for sanity checking)       * See if we can use the user's choice of editors either because
        * we allow any $EDITOR or because $EDITOR is in the allowable list.
      */       */
     do {      Editor = NULL;
         /*      if (def_flag(I_ENV_EDITOR) && UserEditor)
          * Build up a buffer to execute          Editor = UserEditor;
          */      else if (UserEditor) {
         if (strlen(Editor) + strlen(stmp) + 30 > sizeof(buf)) {          struct stat editor_sb;
             (void) fprintf(stderr, "%s: Buffer too short (line %d).\n",          struct stat user_editor_sb;
                            Argv[0], __LINE__);          char *base, *userbase;
   
           if (stat(UserEditor, &user_editor_sb) != 0) {
               /* Should never happen since we already checked above. */
               (void) fprintf(stderr, "%s: unable to stat editor (%s): %s\n",
                   Argv[0], UserEditor, strerror(errno));
             Exit(-1);              Exit(-1);
         }          }
         if (parse_error == TRUE)          EditorPath = estrdup(def_str(I_EDITOR));
             (void) sprintf(buf, "%s +%d %s", Editor, errorlineno, stmp);          Editor = strtok(EditorPath, ":");
           do {
               /*
                * Both Editor and UserEditor should be fully qualified but
                * check anyway...
                */
               if ((base = strrchr(Editor, '/')) == NULL)
                   continue;
               if ((userbase = strrchr(UserEditor, '/')) == NULL) {
                   Editor = NULL;
                   break;
               }
               base++, userbase++;
   
               /*
                * We compare the basenames first and then use stat to match
                * for sure.
                */
               if (strcmp(base, userbase) == 0) {
                   if (stat(Editor, &editor_sb) == 0 && S_ISREG(editor_sb.st_mode)
                       && (editor_sb.st_mode & 0000111) &&
                       editor_sb.st_dev == user_editor_sb.st_dev &&
                       editor_sb.st_ino == user_editor_sb.st_ino)
                       break;
               }
           } while ((Editor = strtok(NULL, ":")));
           free(EditorPath);
       }
   
       /*
        * Can't use $EDITOR, try each element of I_EDITOR until we
        * find one that exists, is regular, and is executable.
        */
       if (Editor == NULL || *Editor == '\0') {
           EditorPath = estrdup(def_str(I_EDITOR));
           Editor = strtok(EditorPath, ":");
           do {
               if (sudo_goodpath(Editor))
                   break;
           } while ((Editor = strtok(NULL, ":")));
   
           /* Bleah, none of the editors existed! */
           if (Editor == NULL || *Editor == '\0') {
               (void) fprintf(stderr, "%s: no editor found (editor path = %s)\n",
                   Argv[0], def_str(I_EDITOR));
               Exit(-1);
           }
       }
   
       /*
        * Edit the temp file and parse it (for sanity checking)
        */
       do {
           char linestr[64];
   
           /* Build up argument vector for the command */
           if ((av[0] = strrchr(Editor, '/')) != NULL)
               av[0]++;
         else          else
             (void) sprintf(buf, "%s %s", Editor, stmp);              av[0] = Editor;
           n = 1;
           if (parse_error == TRUE) {
               (void) snprintf(linestr, sizeof(linestr), "+%d", errorlineno);
               av[n++] = linestr;
           }
           av[n++] = stmp;
           av[n++] = NULL;
   
         /* Do the edit -- some SYSV editors exit with 1 instead of 0 */          /*
            * Do the edit:
            *  We cannot check the editor's exit value against 0 since
            *  XPG4 specifies that vi's exit value is a function of the
            *  number of errors during editing (?!?!).
            */
         now = time(NULL);          now = time(NULL);
         n = system(buf);          if (run_command(Editor, av) != -1) {
         if (n != -1 && ((n >> 8) == 0 || (n >> 8) == 1)) {  
             /*              /*
              * Sanity checks.               * Sanity checks.
              */               */
Line 298 
Line 414 
             }              }
         } else {          } else {
             (void) fprintf(stderr,              (void) fprintf(stderr,
                 "%s: Editor (%s) failed with exit status %d, %s unchanged.\n",                  "%s: Editor (%s) failed, %s unchanged.\n", Argv[0],
                 Argv[0], Editor, n, sudoers);                      Editor, sudoers);
             Exit(-1);              Exit(-1);
         }          }
   
Line 352 
Line 468 
      */       */
     if (rename(stmp, sudoers)) {      if (rename(stmp, sudoers)) {
         if (errno == EXDEV) {          if (errno == EXDEV) {
             char *tmpbuf;  
   
             (void) fprintf(stderr,              (void) fprintf(stderr,
               "%s: %s and %s not on the same filesystem, using mv to rename.\n",                "%s: %s and %s not on the same filesystem, using mv to rename.\n",
               Argv[0], stmp, sudoers);                Argv[0], stmp, sudoers);
   
             /* Allocate just enough space for tmpbuf */              /* Build up argument vector for the command */
             n = sizeof(char) * (strlen(_PATH_MV) + strlen(stmp) +              if ((av[0] = strrchr(_PATH_MV, '/')) != NULL)
                   strlen(sudoers) + 4);                  av[0]++;
             if ((tmpbuf = (char *) malloc(n)) == NULL) {              else
                 (void) fprintf(stderr,                  av[0] = _PATH_MV;
                               "%s: Cannot alocate memory, %s unchanged: %s\n",              av[1] = stmp;
                               Argv[0], sudoers, strerror(errno));              av[2] = sudoers;
                 Exit(-1);              av[3] = NULL;
             }  
   
             /* Build up command and execute it */              /* And run it... */
             (void) sprintf(tmpbuf, "%s %s %s", _PATH_MV, stmp, sudoers);              if (run_command(_PATH_MV, av)) {
             if (system(tmpbuf)) {  
                 (void) fprintf(stderr,                  (void) fprintf(stderr,
                                "%s: Command failed: '%s', %s unchanged.\n",                                 "%s: Command failed: '%s %s %s', %s unchanged.\n",
                                Argv[0], tmpbuf, sudoers);                                 Argv[0], _PATH_MV, stmp, sudoers, sudoers);
                 Exit(-1);                  Exit(-1);
             }              }
             free(tmpbuf);  
         } else {          } else {
             (void) fprintf(stderr, "%s: Error renaming %s, %s unchanged: %s\n",              (void) fprintf(stderr, "%s: Error renaming %s, %s unchanged: %s\n",
                                    Argv[0], stmp, sudoers, strerror(errno));                                     Argv[0], stmp, sudoers, strerror(errno));
Line 435 
Line 546 
     return;      return;
 }  }
   
   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 473 
Line 596 
 static void  static void
 setup_signals()  setup_signals()
 {  {
 #ifdef POSIX_SIGNALS          sigaction_t sa;
         struct sigaction action;                /* POSIX signal structure */  
 #endif /* POSIX_SIGNALS */  
   
         /*          /*
          * Setup signal handlers to cleanup nicely.           * Setup signal handlers to cleanup nicely.
          */           */
 #ifdef POSIX_SIGNALS          sigemptyset(&sa.sa_mask);
         (void) memset((VOID *)&action, 0, sizeof(action));          sa.sa_flags = SA_RESTART;
         action.sa_handler = Exit;          sa.sa_handler = Exit;
         action.sa_flags = SA_RESETHAND;          (void) sigaction(SIGTERM, &sa, NULL);
         (void) sigaction(SIGTERM, &action, NULL);          (void) sigaction(SIGHUP, &sa, NULL);
         (void) sigaction(SIGHUP, &action, NULL);          (void) sigaction(SIGINT, &sa, NULL);
         (void) sigaction(SIGINT, &action, NULL);          (void) sigaction(SIGQUIT, &sa, NULL);
         (void) sigaction(SIGQUIT, &action, NULL);  }
   
   static int
   run_command(path, argv)
       char *path;
       char **argv;
   {
       int status;
       pid_t pid;
       sigset_t set, oset;
   
       (void) sigemptyset(&set);
       (void) sigaddset(&set, SIGCHLD);
       (void) sigprocmask(SIG_BLOCK, &set, &oset);
   
       switch (pid = fork()) {
           case -1:
               (void) fprintf(stderr,
                   "%s: unable to run %s: %s\n", Argv[0], path, strerror(errno));
               Exit(-1);
               break;      /* NOTREACHED */
           case 0:
               (void) sigprocmask(SIG_SETMASK, &oset, NULL);
               execv(path, argv);
               (void) fprintf(stderr,
                   "%s: unable to run %s: %s\n", Argv[0], path, strerror(errno));
               _exit(127);
               break;      /* NOTREACHED */
       }
   
   #ifdef sudo_waitpid
       pid = sudo_waitpid(pid, &status, 0);
 #else  #else
         (void) signal(SIGTERM, Exit);      pid = wait(&status);
         (void) signal(SIGHUP, Exit);  #endif
         (void) signal(SIGINT, Exit);  
         (void) signal(SIGQUIT, Exit);      (void) sigprocmask(SIG_SETMASK, &oset, NULL);
 #endif /* POSIX_SIGNALS */  
       /* XXX - should use WEXITSTATUS() */
       return(pid == -1 ? -1 : (status >> 8));
 }  }
   
   static int
   check_syntax(quiet)
       int quiet;
   {
   
       if ((yyin = fopen(sudoers, "r")) == NULL) {
           if (!quiet)
               (void) fprintf(stderr, "%s: unable to open %s: %s\n", Argv[0],
                   sudoers, strerror(errno));
           exit(1);
       }
       yyout = stdout;
       init_parser();
       if (yyparse() && parse_error != TRUE) {
           if (!quiet)
               (void) fprintf(stderr,
                   "%s: failed to parse %s file, unknown error.\n",
                   Argv[0], sudoers);
           parse_error = TRUE;
       }
       if (!quiet){
           if (parse_error)
               (void) printf("parse error in %s near line %d\n", sudoers,
                   errorlineno);
           else
               (void) printf("%s file parsed OK\n", sudoers);
       }
   
       return(parse_error == TRUE);
   }
   
 /*  /*
  * Unlink the sudoers temp file (if it exists) and exit.   * Unlink the sudoers temp file (if it exists) and exit.
  * Used in place of a normal exit() and as a signal handler.   * Used in place of a normal exit() and as a signal handler.
Line 520 
Line 705 
 static void  static void
 usage()  usage()
 {  {
     (void) fprintf(stderr, "usage: %s [-s] [-V]\n", Argv[0]);      (void) fprintf(stderr, "usage: %s [-c] [-f sudoers] [-q] [-s] [-V]\n",
           Argv[0]);
     exit(1);      exit(1);
 }  }

Legend:
Removed from v.1.10  
changed lines
  Added in v.1.11