Diff for /embedaddon/sudo/src/sudo.c between versions 1.1.1.2 and 1.1.1.5

version 1.1.1.2, 2012/05/29 12:26:49 version 1.1.1.5, 2013/10/14 07:56:35
Line 1 Line 1
 /*  /*
 * Copyright (c) 2009-2012 Todd C. Miller <Todd.Miller@courtesan.com> * Copyright (c) 2009-2013 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 21 Line 21
 #include <config.h>  #include <config.h>
   
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/param.h>  
 #include <sys/stat.h>  #include <sys/stat.h>
 #include <sys/wait.h>  #include <sys/wait.h>
 #include <sys/socket.h>  #include <sys/socket.h>
Line 58 Line 57
 #if TIME_WITH_SYS_TIME  #if TIME_WITH_SYS_TIME
 # include <time.h>  # include <time.h>
 #endif  #endif
 #ifdef HAVE_SETLOCALE  
 # include <locale.h>  
 #endif  
 #ifdef HAVE_LOGIN_CAP_H  #ifdef HAVE_LOGIN_CAP_H
 # include <login_cap.h>  # include <login_cap.h>
 # ifndef LOGIN_SETENV  # ifndef LOGIN_SETENV
Line 86 Line 82
 # endif /* __hpux */  # endif /* __hpux */
 # include <prot.h>  # include <prot.h>
 #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */  #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
 #if defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) || defined (HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV)  
 # include <sys/sysctl.h>  
 #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)  
 # include <sys/sysctl.h>  
 # include <sys/user.h>  
 #endif  
   
   #include <sudo_usage.h>
 #include "sudo.h"  #include "sudo.h"
 #include "sudo_plugin.h"  #include "sudo_plugin.h"
 #include "sudo_plugin_int.h"  #include "sudo_plugin_int.h"
 #include <sudo_usage.h>  
   
 /*  /*
  * Local variables   * Local variables
Line 104 Line 94
 struct plugin_container policy_plugin;  struct plugin_container policy_plugin;
 struct plugin_container_list io_plugins;  struct plugin_container_list io_plugins;
 struct user_details user_details;  struct user_details user_details;
const char *list_user, *runas_user, *runas_group; /* extern for parse_args.c */const char *list_user; /* extern for parse_args.c */
 static struct command_details command_details;
 static int sudo_mode;  static int sudo_mode;
   
 /*  /*
Line 142  static void iolog_unlink(struct plugin_container *plug Line 133  static void iolog_unlink(struct plugin_container *plug
   
 #ifdef RLIMIT_CORE  #ifdef RLIMIT_CORE
 static struct rlimit corelimit;  static struct rlimit corelimit;
#endif /* RLIMIT_CORE */#endif
#if defined(__linux__)#ifdef __linux__
 static struct rlimit nproclimit;  static struct rlimit nproclimit;
 #endif  #endif
   
   __dso_public int main(int argc, char *argv[], char *envp[]);
   
 int  int
 main(int argc, char *argv[], char *envp[])  main(int argc, char *argv[], char *envp[])
 {  {
Line 154  main(int argc, char *argv[], char *envp[]) Line 147  main(int argc, char *argv[], char *envp[])
     char **nargv, **settings, **env_add;      char **nargv, **settings, **env_add;
     char **user_info, **command_info, **argv_out, **user_env_out;      char **user_info, **command_info, **argv_out, **user_env_out;
     struct plugin_container *plugin, *next;      struct plugin_container *plugin, *next;
     struct command_details command_details;  
     sigset_t mask;      sigset_t mask;
     debug_decl(main, SUDO_DEBUG_MAIN)      debug_decl(main, SUDO_DEBUG_MAIN)
   
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)    os_init(argc, argv, envp);
    { 
        extern char *malloc_options; 
        malloc_options = "AFGJPR"; 
    } 
#endif 
   
 #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)  
     if (argc > 0)  
         setprogname(argv[0]);  
 #endif  
   
 #ifdef HAVE_SETLOCALE  
     setlocale(LC_ALL, "");      setlocale(LC_ALL, "");
 #endif  
     bindtextdomain(PACKAGE_NAME, LOCALEDIR);      bindtextdomain(PACKAGE_NAME, LOCALEDIR);
     textdomain(PACKAGE_NAME);      textdomain(PACKAGE_NAME);
   
   #ifdef HAVE_TZSET
       (void) tzset();
   #endif /* HAVE_TZSET */
   
     /* Must be done before we do any password lookups */      /* Must be done before we do any password lookups */
 #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)  #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
     (void) set_auth_parameters(argc, argv);      (void) set_auth_parameters(argc, argv);
Line 187  main(int argc, char *argv[], char *envp[]) Line 171  main(int argc, char *argv[], char *envp[])
     /* Make sure we are setuid root. */      /* Make sure we are setuid root. */
     sudo_check_suid(argv[0]);      sudo_check_suid(argv[0]);
   
    /* Reset signal mask and make sure fds 0-2 are open. */    /* Reset signal mask, save signal state and make sure fds 0-2 are open. */
     (void) sigemptyset(&mask);      (void) sigemptyset(&mask);
     (void) sigprocmask(SIG_SETMASK, &mask, NULL);      (void) sigprocmask(SIG_SETMASK, &mask, NULL);
       save_signals();
     fix_fds();      fix_fds();
   
       /* Read sudo.conf. */
       sudo_conf_read(NULL);
   
     /* Fill in user_info with user name, uid, cwd, etc. */      /* Fill in user_info with user name, uid, cwd, etc. */
     memset(&user_details, 0, sizeof(user_details));      memset(&user_details, 0, sizeof(user_details));
     user_info = get_user_info(&user_details);      user_info = get_user_info(&user_details);
   
     /* Read sudo.conf. */  
     sudo_conf_read();  
   
     /* Disable core dumps if not enabled in sudo.conf. */      /* Disable core dumps if not enabled in sudo.conf. */
     disable_coredumps();      disable_coredumps();
   
Line 215  main(int argc, char *argv[], char *envp[]) Line 200  main(int argc, char *argv[], char *envp[])
   
     /* Load plugins. */      /* Load plugins. */
     if (!sudo_load_plugins(&policy_plugin, &io_plugins))      if (!sudo_load_plugins(&policy_plugin, &io_plugins))
        errorx(1, _("fatal error, unable to load plugins"));        fatalx(_("fatal error, unable to load plugins"));
   
     /* Open policy plugin. */      /* Open policy plugin. */
     ok = policy_open(&policy_plugin, settings, user_info, envp);      ok = policy_open(&policy_plugin, settings, user_info, envp);
Line 223  main(int argc, char *argv[], char *envp[]) Line 208  main(int argc, char *argv[], char *envp[])
         if (ok == -2)          if (ok == -2)
             usage(1);              usage(1);
         else          else
            errorx(1, _("unable to initialize policy plugin"));            fatalx(_("unable to initialize policy plugin"));
     }      }
   
       init_signals();
   
     switch (sudo_mode & MODE_MASK) {      switch (sudo_mode & MODE_MASK) {
         case MODE_VERSION:          case MODE_VERSION:
             policy_show_version(&policy_plugin, !user_details.uid);              policy_show_version(&policy_plugin, !user_details.uid);
             tq_foreach_fwd(&io_plugins, plugin) {              tq_foreach_fwd(&io_plugins, plugin) {
                 ok = iolog_open(plugin, settings, user_info, NULL,                  ok = iolog_open(plugin, settings, user_info, NULL,
                     nargc, nargv, envp);                      nargc, nargv, envp);
                if (ok == 1)                if (ok != -1)
                     iolog_show_version(plugin, !user_details.uid);                      iolog_show_version(plugin, !user_details.uid);
             }              }
             break;              break;
Line 278  main(int argc, char *argv[], char *envp[]) Line 265  main(int argc, char *argv[], char *envp[])
                     usage(1);                      usage(1);
                     break;                      break;
                 default:                  default:
                    errorx(1, _("error initializing I/O plugin %s"),                    fatalx(_("error initializing I/O plugin %s"),
                         plugin->name);                          plugin->name);
                 }                  }
             }              }
Line 289  main(int argc, char *argv[], char *envp[]) Line 276  main(int argc, char *argv[], char *envp[])
             if (ISSET(sudo_mode, MODE_BACKGROUND))              if (ISSET(sudo_mode, MODE_BACKGROUND))
                 SET(command_details.flags, CD_BACKGROUND);                  SET(command_details.flags, CD_BACKGROUND);
             /* Become full root (not just setuid) so user cannot kill us. */              /* Become full root (not just setuid) so user cannot kill us. */
            (void) setuid(ROOT_UID);            if (setuid(ROOT_UID) == -1)
                 warning("setuid(%d)", ROOT_UID);
             /* Restore coredumpsize resource limit before running. */              /* Restore coredumpsize resource limit before running. */
 #ifdef RLIMIT_CORE  #ifdef RLIMIT_CORE
             if (sudo_conf_disable_coredump())              if (sudo_conf_disable_coredump())
Line 303  main(int argc, char *argv[], char *envp[]) Line 291  main(int argc, char *argv[], char *envp[])
             /* The close method was called by sudo_edit/run_command. */              /* The close method was called by sudo_edit/run_command. */
             break;              break;
         default:          default:
            errorx(1, _("unexpected sudo mode 0x%x"), sudo_mode);            fatalx(_("unexpected sudo mode 0x%x"), sudo_mode);
     }      }
     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);
 }  }
   
   int
   os_init_common(int argc, char *argv[], char *envp[])
   {
   #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)
       if (argc > 0)
           setprogname(argv[0]);
   #endif
       return 0;
   }
   
 /*  /*
  * Ensure that stdin, stdout and stderr are open; set to /dev/null if not.   * Ensure that stdin, stdout and stderr are open; set to /dev/null if not.
  * Some operating systems do this automatically in the kernel or libc.   * Some operating systems do this automatically in the kernel or libc.
Line 328  fix_fds(void) Line 326  fix_fds(void)
     miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;      miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
     if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {      if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
         if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) == -1)          if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) == -1)
            error(1, _("unable to open %s"), _PATH_DEVNULL);            fatal(_("unable to open %s"), _PATH_DEVNULL);
         if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1)          if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1)
            error(1, "dup2");            fatal("dup2");
         if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1)          if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1)
            error(1, "dup2");            fatal("dup2");
         if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1)          if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1)
            error(1, "dup2");            fatal("dup2");
         if (devnull > STDERR_FILENO)          if (devnull > STDERR_FILENO)
             close(devnull);              close(devnull);
     }      }
Line 343  fix_fds(void) Line 341  fix_fds(void)
   
 /*  /*
  * Allocate space for groups and fill in using getgrouplist()   * Allocate space for groups and fill in using getgrouplist()
 * for when we cannot use getgroups(). * for when we cannot (or don't want to) use getgroups().
  */   */
 static int  static int
fill_group_list(struct user_details *ud)fill_group_list(struct user_details *ud, int system_maxgroups)
 {  {
    int maxgroups, tries, rval = -1;    int tries, rval = -1;
     debug_decl(fill_group_list, SUDO_DEBUG_UTIL)      debug_decl(fill_group_list, SUDO_DEBUG_UTIL)
   
 #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)  
     maxgroups = (int)sysconf(_SC_NGROUPS_MAX);  
     if (maxgroups < 0)  
 #endif  
         maxgroups = NGROUPS_MAX;  
   
     /*      /*
     * It is possible to belong to more groups in the group database     * If user specified a max number of groups, use it, otherwise keep
     * than NGROUPS_MAX.  We start off with NGROUPS_MAX * 2 entries     * trying getgrouplist() until we have enough room in the array.
     * and double this as needed. 
      */       */
    ud->groups = NULL;    ud->ngroups = sudo_conf_max_groups();
    ud->ngroups = maxgroups;    if (ud->ngroups > 0) {
    for (tries = 0; tries < 10 && rval == -1; tries++) { 
        ud->ngroups *= 2; 
        efree(ud->groups); 
         ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));          ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
        rval = getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);        /* No error on insufficient space if user specified max_groups. */
         (void)getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);
         rval = 0;
     } else {
         /*
          * It is possible to belong to more groups in the group database
          * than NGROUPS_MAX.  We start off with NGROUPS_MAX * 4 entries
          * and double this as needed.
          */
         ud->groups = NULL;
         ud->ngroups = system_maxgroups << 1;
         for (tries = 0; tries < 10 && rval == -1; tries++) {
             ud->ngroups <<= 1;
             efree(ud->groups);
             ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
             rval = getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);
         }
     }      }
     debug_return_int(rval);      debug_return_int(rval);
 }  }
Line 378  get_user_groups(struct user_details *ud) Line 382  get_user_groups(struct user_details *ud)
 {  {
     char *cp, *gid_list = NULL;      char *cp, *gid_list = NULL;
     size_t glsize;      size_t glsize;
    int i, len;    int i, len, maxgroups, group_source;
     debug_decl(get_user_groups, SUDO_DEBUG_UTIL)      debug_decl(get_user_groups, SUDO_DEBUG_UTIL)
   
    /*#if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
     * Systems with mbr_check_membership() support more than NGROUPS_MAX    maxgroups = (int)sysconf(_SC_NGROUPS_MAX);
     * groups so we cannot use getgroups().    if (maxgroups < 0)
     */#endif
         maxgroups = NGROUPS_MAX;
 
     ud->groups = NULL;      ud->groups = NULL;
#ifndef HAVE_MBR_CHECK_MEMBERSHIP    group_source = sudo_conf_group_source();
    if ((ud->ngroups = getgroups(0, NULL)) > 0) {    if (group_source != GROUP_SOURCE_DYNAMIC) {
        ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));        if ((ud->ngroups = getgroups(0, NULL)) > 0) {
        if (getgroups(ud->ngroups, ud->groups) < 0) {            /* Use groups from kernel if not too many or source is static. */
            efree(ud->groups);            if (ud->ngroups < maxgroups || group_source == GROUP_SOURCE_STATIC) {
            ud->groups = NULL;                ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
                 if (getgroups(ud->ngroups, ud->groups) < 0) {
                     efree(ud->groups);
                     ud->groups = NULL;
                 }
             }
         }          }
     }      }
 #endif /* HAVE_MBR_CHECK_MEMBERSHIP */  
     if (ud->groups == NULL) {      if (ud->groups == NULL) {
        if (fill_group_list(ud) == -1)        /*
            error(1, _("unable to get group vector"));         * Query group database if kernel list is too small or disabled.
          * Typically, this is because NFS can only support up to 16 groups.
          */
         if (fill_group_list(ud, maxgroups) == -1)
             fatal(_("unable to get group vector"));
     }      }
   
     /*      /*
Line 423  get_user_groups(struct user_details *ud) Line 437  get_user_groups(struct user_details *ud)
 static char **  static char **
 get_user_info(struct user_details *ud)  get_user_info(struct user_details *ud)
 {  {
    char *cp, **user_info, cwd[PATH_MAX], host[MAXHOSTNAMELEN];    char *cp, **user_info, cwd[PATH_MAX], host[HOST_NAME_MAX + 1];
     struct passwd *pw;      struct passwd *pw;
     int fd, i = 0;      int fd, i = 0;
     debug_decl(get_user_info, SUDO_DEBUG_UTIL)      debug_decl(get_user_info, SUDO_DEBUG_UTIL)
Line 449  get_user_info(struct user_details *ud) Line 463  get_user_info(struct user_details *ud)
   
     pw = getpwuid(ud->uid);      pw = getpwuid(ud->uid);
     if (pw == NULL)      if (pw == NULL)
        errorx(1, _("unknown uid %u: who are you?"), (unsigned int)ud->uid);        fatalx(_("unknown uid %u: who are you?"), (unsigned int)ud->uid);
   
     user_info[i] = fmt_string("user", pw->pw_name);      user_info[i] = fmt_string("user", pw->pw_name);
     if (user_info[i] == NULL)      if (user_info[i] == NULL)
        errorx(1, _("unable to allocate memory"));        fatal(NULL);
     ud->username = user_info[i] + sizeof("user=") - 1;      ud->username = user_info[i] + sizeof("user=") - 1;
   
     /* Stash user's shell for use with the -s flag; don't pass to plugin. */      /* Stash user's shell for use with the -s flag; don't pass to plugin. */
Line 479  get_user_info(struct user_details *ud) Line 493  get_user_info(struct user_details *ud)
     if (getcwd(cwd, sizeof(cwd)) != NULL) {      if (getcwd(cwd, sizeof(cwd)) != NULL) {
         user_info[++i] = fmt_string("cwd", cwd);          user_info[++i] = fmt_string("cwd", cwd);
         if (user_info[i] == NULL)          if (user_info[i] == NULL)
            errorx(1, _("unable to allocate memory"));            fatal(NULL);
         ud->cwd = user_info[i] + sizeof("cwd=") - 1;          ud->cwd = user_info[i] + sizeof("cwd=") - 1;
     }      }
   
     if ((cp = get_process_ttyname()) != NULL) {      if ((cp = get_process_ttyname()) != NULL) {
         user_info[++i] = fmt_string("tty", cp);          user_info[++i] = fmt_string("tty", cp);
         if (user_info[i] == NULL)          if (user_info[i] == NULL)
            errorx(1, _("unable to allocate memory"));            fatal(NULL);
         ud->tty = user_info[i] + sizeof("tty=") - 1;          ud->tty = user_info[i] + sizeof("tty=") - 1;
         efree(cp);          efree(cp);
     }      }
Line 497  get_user_info(struct user_details *ud) Line 511  get_user_info(struct user_details *ud)
         strlcpy(host, "localhost", sizeof(host));          strlcpy(host, "localhost", sizeof(host));
     user_info[++i] = fmt_string("host", host);      user_info[++i] = fmt_string("host", host);
     if (user_info[i] == NULL)      if (user_info[i] == NULL)
        errorx(1, _("unable to allocate memory"));        fatal(NULL);
     ud->host = user_info[i] + sizeof("host=") - 1;      ud->host = user_info[i] + sizeof("host=") - 1;
   
     get_ttysize(&ud->ts_lines, &ud->ts_cols);      get_ttysize(&ud->ts_lines, &ud->ts_cols);
Line 516  static void Line 530  static void
 command_info_to_details(char * const info[], struct command_details *details)  command_info_to_details(char * const info[], struct command_details *details)
 {  {
     int i;      int i;
       id_t id;
     long lval;      long lval;
     unsigned long ulval;  
     char *cp, *ep;      char *cp, *ep;
       const char *errstr;
     debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM)      debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM)
   
     memset(details, 0, sizeof(*details));      memset(details, 0, sizeof(*details));
Line 539  command_info_to_details(char * const info[], struct co Line 554  command_info_to_details(char * const info[], struct co
                 SET_STRING("command=", command)                  SET_STRING("command=", command)
                 SET_STRING("cwd=", cwd)                  SET_STRING("cwd=", cwd)
                 if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) {                  if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) {
                     cp = info[i] + sizeof("closefrom=") - 1;  
                     if (*cp == '\0')  
                         break;  
                     errno = 0;                      errno = 0;
                    lval = strtol(cp, &ep, 0);                    cp = info[i] + sizeof("closefrom=") - 1;
                    if (*cp != '\0' && *ep == '\0' &&                    lval = strtol(cp, &ep, 10);
                        !(errno == ERANGE &&                    if (*cp == '\0' || *ep != '\0')
                        (lval == LONG_MAX || lval == LONG_MIN)) &&                        fatalx(_("%s: %s"), info[i], _("invalid value"));
                        lval < INT_MAX && lval > INT_MIN) {                    if ((errno == ERANGE &&
                        details->closefrom = (int)lval;                        (lval == LONG_MAX || lval == LONG_MIN)) ||
                    }                        (lval > INT_MAX || lval < 0))
                         fatalx(_("%s: %s"), info[i], _("value out of range"));
                     details->closefrom = (int)lval;
                     break;                      break;
                 }                  }
                 break;                  break;
               case 'e':
                   if (strncmp("exec_background=", info[i], sizeof("exec_background=") - 1) == 0) {
                       if (atobool(info[i] + sizeof("exec_background=") - 1) == true)
                           SET(details->flags, CD_EXEC_BG);
                       break;
                   }
                   break;
             case 'l':              case 'l':
                 SET_STRING("login_class=", login_class)                  SET_STRING("login_class=", login_class)
                 break;                  break;
             case 'n':              case 'n':
                 /* XXX - bounds check  -NZERO to NZERO (inclusive). */  
                 if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {                  if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {
                     cp = info[i] + sizeof("nice=") - 1;  
                     if (*cp == '\0')  
                         break;  
                     errno = 0;                      errno = 0;
                    lval = strtol(cp, &ep, 0);                    cp = info[i] + sizeof("nice=") - 1;
                    if (*cp != '\0' && *ep == '\0' &&                    lval = strtol(cp, &ep, 10);
                        !(errno == ERANGE &&                    if (*cp == '\0' || *ep != '\0')
                        (lval == LONG_MAX || lval == LONG_MIN)) &&                        fatalx(_("%s: %s"), info[i], _("invalid value"));
                        lval < INT_MAX && lval > INT_MIN) {                    if ((errno == ERANGE &&
                        details->priority = (int)lval;                        (lval == LONG_MAX || lval == LONG_MIN)) ||
                        SET(details->flags, CD_SET_PRIORITY);                        (lval > INT_MAX || lval < INT_MIN))
                    }                        fatalx(_("%s: %s"), info[i], _("value out of range"));
                     details->priority = (int)lval;
                     SET(details->flags, CD_SET_PRIORITY);
                     break;                      break;
                 }                  }
                 if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) {                  if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) {
Line 589  command_info_to_details(char * const info[], struct co Line 608  command_info_to_details(char * const info[], struct co
             case 'r':              case 'r':
                 if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {                  if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {
                     cp = info[i] + sizeof("runas_egid=") - 1;                      cp = info[i] + sizeof("runas_egid=") - 1;
                    if (*cp == '\0')                    id = atoid(cp, NULL, NULL, &errstr);
                        break;                    if (errstr != NULL)
                    errno = 0;                        fatalx(_("%s: %s"), info[i], _(errstr));
                    ulval = strtoul(cp, &ep, 0);                    details->egid = (gid_t)id;
                    if (*cp != '\0' && *ep == '\0' &&                    SET(details->flags, CD_SET_EGID);
                        (errno != ERANGE || ulval != ULONG_MAX)) { 
                        details->egid = (gid_t)ulval; 
                        SET(details->flags, CD_SET_EGID); 
                    } 
                     break;                      break;
                 }                  }
                 if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {                  if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
                     cp = info[i] + sizeof("runas_euid=") - 1;                      cp = info[i] + sizeof("runas_euid=") - 1;
                    if (*cp == '\0')                    id = atoid(cp, NULL, NULL, &errstr);
                        break;                    if (errstr != NULL)
                    errno = 0;                        fatalx(_("%s: %s"), info[i], _(errstr));
                    ulval = strtoul(cp, &ep, 0);                    details->euid = (uid_t)id;
                    if (*cp != '\0' && *ep == '\0' &&                    SET(details->flags, CD_SET_EUID);
                        (errno != ERANGE || ulval != ULONG_MAX)) { 
                        details->euid = (uid_t)ulval; 
                        SET(details->flags, CD_SET_EUID); 
                    } 
                     break;                      break;
                 }                  }
                 if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {                  if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
                     cp = info[i] + sizeof("runas_gid=") - 1;                      cp = info[i] + sizeof("runas_gid=") - 1;
                    if (*cp == '\0')                    id = atoid(cp, NULL, NULL, &errstr);
                        break;                    if (errstr != NULL)
                    errno = 0;                        fatalx(_("%s: %s"), info[i], _(errstr));
                    ulval = strtoul(cp, &ep, 0);                    details->gid = (gid_t)id;
                    if (*cp != '\0' && *ep == '\0' &&                    SET(details->flags, CD_SET_GID);
                        (errno != ERANGE || ulval != ULONG_MAX)) { 
                        details->gid = (gid_t)ulval; 
                        SET(details->flags, CD_SET_GID); 
                    } 
                     break;                      break;
                 }                  }
                 if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {                  if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
                    int j;                    /* parse_gid_list() will call fatalx() on error. */
 
                    /* count groups, alloc and fill in */ 
                     cp = info[i] + sizeof("runas_groups=") - 1;                      cp = info[i] + sizeof("runas_groups=") - 1;
                    if (*cp == '\0')                    details->ngroups = parse_gid_list(cp, NULL, &details->groups);
                        break; 
                    for (;;) { 
                        details->ngroups++; 
                        if ((cp = strchr(cp, ',')) == NULL) 
                            break; 
                        cp++; 
                    } 
                    if (details->ngroups != 0) { 
                        details->groups = 
                            emalloc2(details->ngroups, sizeof(GETGROUPS_T)); 
                        cp = info[i] + sizeof("runas_groups=") - 1; 
                        for (j = 0; j < details->ngroups;) { 
                            errno = 0; 
                            ulval = strtoul(cp, &ep, 0); 
                            if (*cp == '\0' || (*ep != ',' && *ep != '\0') || 
                                (ulval == ULONG_MAX && errno == ERANGE)) { 
                                break; 
                            } 
                            details->groups[j++] = (gid_t)ulval; 
                            cp = ep + 1; 
                        } 
                        details->ngroups = j; 
                    } 
                     break;                      break;
                 }                  }
                 if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {                  if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
                     cp = info[i] + sizeof("runas_uid=") - 1;                      cp = info[i] + sizeof("runas_uid=") - 1;
                    if (*cp == '\0')                    id = atoid(cp, NULL, NULL, &errstr);
                        break;                    if (errstr != NULL)
                    errno = 0;                        fatalx(_("%s: %s"), info[i], _(errstr));
                    ulval = strtoul(cp, &ep, 0);                    details->uid = (uid_t)id;
                    if (*cp != '\0' && *ep == '\0' &&                    SET(details->flags, CD_SET_UID);
                        (errno != ERANGE || ulval != ULONG_MAX)) {                    break;
                        details->uid = (uid_t)ulval;                }
                        SET(details->flags, CD_SET_UID);#ifdef HAVE_PRIV_SET
                 if (strncmp("runas_privs=", info[i], sizeof("runas_privs=") - 1) == 0) {
                     const char *endp;
                     cp = info[i] + sizeof("runas_privs=") - 1;
                     if (*cp != '\0') {
                         details->privs = priv_str_to_set(cp, ",", &endp);
                         if (details->privs == NULL)
                             warning("invalid runas_privs %s", endp);
                     }                      }
                     break;                      break;
                 }                  }
                   if (strncmp("runas_limitprivs=", info[i], sizeof("runas_limitprivs=") - 1) == 0) {
                       const char *endp;
                       cp = info[i] + sizeof("runas_limitprivs=") - 1;
                       if (*cp != '\0') {
                           details->limitprivs = priv_str_to_set(cp, ",", &endp);
                           if (details->limitprivs == NULL)
                               warning("invalid runas_limitprivs %s", endp);
                       }
                       break;
                   }
   #endif /* HAVE_PRIV_SET */
                 break;                  break;
             case 's':              case 's':
                 SET_STRING("selinux_role=", selinux_role)                  SET_STRING("selinux_role=", selinux_role)
Line 687  command_info_to_details(char * const info[], struct co Line 687  command_info_to_details(char * const info[], struct co
                 break;                  break;
             case 't':              case 't':
                 if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {                  if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {
                     cp = info[i] + sizeof("timeout=") - 1;  
                     if (*cp == '\0')  
                         break;  
                     errno = 0;                      errno = 0;
                    lval = strtol(cp, &ep, 0);                    cp = info[i] + sizeof("timeout=") - 1;
                    if (*cp != '\0' && *ep == '\0' &&                    lval = strtol(cp, &ep, 10);
                        !(errno == ERANGE &&                    if (*cp == '\0' || *ep != '\0')
                        (lval == LONG_MAX || lval == LONG_MIN)) &&                        fatalx(_("%s: %s"), info[i], _("invalid value"));
                        lval <= INT_MAX && lval >= 0) {                    if ((errno == ERANGE &&
                        details->timeout = (int)lval;                        (lval == LONG_MAX || lval == LONG_MIN)) ||
                        SET(details->flags, CD_SET_TIMEOUT);                        (lval > INT_MAX || lval < 0))
                    }                        fatalx(_("%s: %s"), info[i], _("value out of range"));
                     details->timeout = (int)lval;
                     SET(details->flags, CD_SET_TIMEOUT);
                     break;                      break;
                 }                  }
                 break;                  break;
             case 'u':              case 'u':
                 if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {                  if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
                     cp = info[i] + sizeof("umask=") - 1;  
                     if (*cp == '\0')  
                         break;  
                     errno = 0;                      errno = 0;
                    ulval = strtoul(cp, &ep, 8);                    cp = info[i] + sizeof("umask=") - 1;
                    if (*cp != '\0' && *ep == '\0' &&                    lval = strtol(cp, &ep, 8);
                        (errno != ERANGE || ulval != ULONG_MAX)) {                    if (*cp == '\0' || *ep != '\0')
                        details->umask = (uid_t)ulval;                        fatalx(_("%s: %s"), info[i], _("invalid value"));
                        SET(details->flags, CD_SET_UMASK);                    if ((errno == ERANGE &&
                    }                        (lval == LONG_MAX || lval == LONG_MIN)) ||
                         (lval > 0777 || lval < 0))
                         fatalx(_("%s: %s"), info[i], _("value out of range"));
                     details->umask = (mode_t)lval;
                     SET(details->flags, CD_SET_UMASK);
                     break;                      break;
                 }                  }
                 if (strncmp("use_pty=", info[i], sizeof("use_pty=") - 1) == 0) {                  if (strncmp("use_pty=", info[i], sizeof("use_pty=") - 1) == 0) {
Line 734  command_info_to_details(char * const info[], struct co Line 734  command_info_to_details(char * const info[], struct co
 #endif  #endif
     details->pw = getpwuid(details->euid);      details->pw = getpwuid(details->euid);
     if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL)      if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL)
        errorx(1, _("unable to allocate memory"));        fatal(NULL);
 #ifdef HAVE_SETAUTHDB  #ifdef HAVE_SETAUTHDB
     aix_restoreauthdb();      aix_restoreauthdb();
 #endif  #endif
Line 747  command_info_to_details(char * const info[], struct co Line 747  command_info_to_details(char * const info[], struct co
 }  }
   
 static void  static void
sudo_check_suid(const char *path)sudo_check_suid(const char *sudo)
 {  {
       char pathbuf[PATH_MAX];
     struct stat sb;      struct stat sb;
       bool qualified;
     debug_decl(sudo_check_suid, SUDO_DEBUG_PCOMM)      debug_decl(sudo_check_suid, SUDO_DEBUG_PCOMM)
   
     if (geteuid() != 0) {      if (geteuid() != 0) {
        if (strchr(path, '/') != NULL && stat(path, &sb) == 0) {        /* Search for sudo binary in PATH if not fully qualified. */
         qualified = strchr(sudo, '/') != NULL;
         if (!qualified) {
             char *path = getenv_unhooked("PATH");
             if (path != NULL) {
                 int len;
                 char *cp, *colon;
 
                 cp = path = estrdup(path);
                 do {
                     if ((colon = strchr(cp, ':')))
                         *colon = '\0';
                     len = snprintf(pathbuf, sizeof(pathbuf), "%s/%s", cp, sudo);
                     if (len <= 0 || len >= sizeof(pathbuf))
                         continue;
                     if (access(pathbuf, X_OK) == 0) {
                         sudo = pathbuf;
                         qualified = true;
                         break;
                     }
                     cp = colon + 1;
                 } while (colon);
                 efree(path);
             }
         }
 
         if (qualified && stat(sudo, &sb) == 0) {
             /* Try to determine why sudo was not running as root. */              /* Try to determine why sudo was not running as root. */
             if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) {              if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) {
                errorx(1,                fatalx(
                     _("%s must be owned by uid %d and have the setuid bit set"),                      _("%s must be owned by uid %d and have the setuid bit set"),
                    path, ROOT_UID);                    sudo, ROOT_UID);
             } else {              } else {
                errorx(1, _("effective uid is not %d, is %s on a file system "                fatalx(_("effective uid is not %d, is %s on a file system "
                     "with the 'nosuid' option set or an NFS file system without"                      "with the 'nosuid' option set or an NFS file system without"
                    " root privileges?"), ROOT_UID, path);                    " root privileges?"), ROOT_UID, sudo);
             }              }
         } else {          } else {
            errorx(1,            fatalx(
                 _("effective uid is not %d, is sudo installed setuid root?"),                  _("effective uid is not %d, is sudo installed setuid root?"),
                 ROOT_UID);                  ROOT_UID);
         }          }
Line 781  sudo_check_suid(const char *path) Line 809  sudo_check_suid(const char *path)
 static void  static void
 disable_coredumps(void)  disable_coredumps(void)
 {  {
#if defined(__linux__) || defined(RLIMIT_CORE)#if defined(RLIMIT_CORE)
     struct rlimit rl;      struct rlimit rl;
 #endif  
     debug_decl(disable_coredumps, SUDO_DEBUG_UTIL)      debug_decl(disable_coredumps, SUDO_DEBUG_UTIL)
   
 #if defined(__linux__)  
     /*      /*
      * Unlimit the number of processes since Linux's setuid() will  
      * apply resource limits when changing uid and return EAGAIN if  
      * nproc would be violated by the uid switch.  
      */  
     (void) getrlimit(RLIMIT_NPROC, &nproclimit);  
     rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;  
     if (setrlimit(RLIMIT_NPROC, &rl)) {  
         memcpy(&rl, &nproclimit, sizeof(struct rlimit));  
         rl.rlim_cur = rl.rlim_max;  
         (void)setrlimit(RLIMIT_NPROC, &rl);  
     }  
 #endif /* __linux__ */  
 #ifdef RLIMIT_CORE  
     /*  
      * Turn off core dumps?       * Turn off core dumps?
      */       */
     if (sudo_conf_disable_coredump()) {      if (sudo_conf_disable_coredump()) {
Line 810  disable_coredumps(void) Line 822  disable_coredumps(void)
         rl.rlim_cur = 0;          rl.rlim_cur = 0;
         (void) setrlimit(RLIMIT_CORE, &rl);          (void) setrlimit(RLIMIT_CORE, &rl);
     }      }
 #endif /* RLIMIT_CORE */  
     debug_return;      debug_return;
   #endif /* RLIMIT_CORE */
 }  }
   
#ifdef HAVE_PROJECT_H/*
  * Unlimit the number of processes since Linux's setuid() will
  * apply resource limits when changing uid and return EAGAIN if
  * nproc would be exceeded by the uid switch.
  */
 static void  static void
set_project(struct passwd *pw)unlimit_nproc(void)
 {  {
    struct project proj;#ifdef __linux__
    char buf[PROJECT_BUFSZ];    struct rlimit rl;
    int errval;    debug_decl(unlimit_nproc, SUDO_DEBUG_UTIL)
    debug_decl(set_project, SUDO_DEBUG_UTIL) 
   
    /*    (void) getrlimit(RLIMIT_NPROC, &nproclimit);
     * Collect the default project for the user and settaskid    rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
     */    if (setrlimit(RLIMIT_NPROC, &rl) != 0) {
    setprojent();        memcpy(&rl, &nproclimit, sizeof(struct rlimit));
    if (getdefaultproj(pw->pw_name, &proj, buf, sizeof(buf)) != NULL) {        rl.rlim_cur = rl.rlim_max;
        errval = setproject(proj.pj_name, pw->pw_name, TASK_NORMAL);        (void)setrlimit(RLIMIT_NPROC, &rl);
        switch(errval) { 
        case 0: 
            break; 
        case SETPROJ_ERR_TASK: 
            switch (errno) { 
            case EAGAIN: 
                warningx(_("resource control limit has been reached")); 
                break; 
            case ESRCH: 
                warningx(_("user \"%s\" is not a member of project \"%s\""), 
                    pw->pw_name, proj.pj_name); 
                break; 
            case EACCES: 
                warningx(_("the invoking task is final")); 
                break; 
            default: 
                warningx(_("could not join project \"%s\""), proj.pj_name); 
            } 
        case SETPROJ_ERR_POOL: 
            switch (errno) { 
            case EACCES: 
                warningx(_("no resource pool accepting default bindings " 
                    "exists for project \"%s\""), proj.pj_name); 
                break; 
            case ESRCH: 
                warningx(_("specified resource pool does not exist for " 
                    "project \"%s\""), proj.pj_name); 
                break; 
            default: 
                warningx(_("could not bind to default resource pool for " 
                    "project \"%s\""), proj.pj_name); 
            } 
            break; 
        default: 
            if (errval <= 0) { 
                warningx(_("setproject failed for project \"%s\""), proj.pj_name); 
            } else { 
                warningx(_("warning, resource control assignment failed for " 
                    "project \"%s\""), proj.pj_name); 
            } 
        } 
    } else { 
        warning("getdefaultproj"); 
     }      }
     endprojent();  
     debug_return;      debug_return;
   #endif /* __linux__ */
 }  }
 #endif /* HAVE_PROJECT_H */  
   
 /*  /*
    * Restore saved value of RLIMIT_NPROC.
    */
   static void
   restore_nproc(void)
   {
   #ifdef __linux__
       debug_decl(restore_nproc, SUDO_DEBUG_UTIL)
   
       (void) setrlimit(RLIMIT_NPROC, &nproclimit);
   
       debug_return;
   #endif /* __linux__ */
   }
   
   /*
  * Setup the execution environment immediately prior to the call to execve()   * Setup the execution environment immediately prior to the call to execve()
  * Returns true on success and false on failure.   * Returns true on success and false on failure.
  */   */
Line 900  exec_setup(struct command_details *details, const char Line 886  exec_setup(struct command_details *details, const char
 #ifdef HAVE_PROJECT_H  #ifdef HAVE_PROJECT_H
         set_project(details->pw);          set_project(details->pw);
 #endif  #endif
   #ifdef HAVE_PRIV_SET
           if (details->privs != NULL) {
               if (setppriv(PRIV_SET, PRIV_INHERITABLE, details->privs) != 0) {
                   warning("unable to set privileges");
                   goto done;
               }
           }
           if (details->limitprivs != NULL) {
               if (setppriv(PRIV_SET, PRIV_LIMIT, details->limitprivs) != 0) {
                   warning("unable to set limit privileges");
                   goto done;
               }
           } else if (details->privs != NULL) {
               if (setppriv(PRIV_SET, PRIV_LIMIT, details->privs) != 0) {
                   warning("unable to set limit privileges");
                   goto done;
               }
           }
   #endif /* HAVE_PRIV_SET */
   
 #ifdef HAVE_GETUSERATTR  #ifdef HAVE_GETUSERATTR
         aix_prep_user(details->pw->pw_name, ptyname ? ptyname : user_details.tty);          aix_prep_user(details->pw->pw_name, ptyname ? ptyname : user_details.tty);
 #endif  #endif
Line 927  exec_setup(struct command_details *details, const char Line 933  exec_setup(struct command_details *details, const char
                 flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;                  flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
             }              }
             if (setusercontext(lc, details->pw, details->pw->pw_uid, flags)) {              if (setusercontext(lc, details->pw, details->pw->pw_uid, flags)) {
                if (details->pw->pw_uid != ROOT_UID) {                warning(_("unable to set user context"));
                    warning(_("unable to set user context"));                if (details->pw->pw_uid != ROOT_UID)
                     goto done;                      goto done;
                 } else  
                     warning(_("unable to set user context"));  
             }              }
         }          }
 #endif /* HAVE_LOGIN_CAP_H */  #endif /* HAVE_LOGIN_CAP_H */
Line 976  exec_setup(struct command_details *details, const char Line 980  exec_setup(struct command_details *details, const char
         }          }
     }      }
   
       /* 
        * Unlimit the number of processes since Linux's setuid() will
        * return EAGAIN if RLIMIT_NPROC would be exceeded by the uid switch.
        */
       unlimit_nproc();
   
 #ifdef HAVE_SETRESUID  #ifdef HAVE_SETRESUID
     if (setresuid(details->uid, details->euid, details->euid) != 0) {      if (setresuid(details->uid, details->euid, details->euid) != 0) {
         warning(_("unable to change to runas uid (%u, %u)"), details->uid,          warning(_("unable to change to runas uid (%u, %u)"), details->uid,
Line 996  exec_setup(struct command_details *details, const char Line 1006  exec_setup(struct command_details *details, const char
     }      }
 #endif /* !HAVE_SETRESUID && !HAVE_SETREUID */  #endif /* !HAVE_SETRESUID && !HAVE_SETREUID */
   
       /* Restore previous value of RLIMIT_NPROC. */
       restore_nproc();
   
     /*      /*
      * Only change cwd if we have chroot()ed or the policy modules       * Only change cwd if we have chroot()ed or the policy modules
      * specifies a different cwd.  Must be done after uid change.       * specifies a different cwd.  Must be done after uid change.
Line 1010  exec_setup(struct command_details *details, const char Line 1023  exec_setup(struct command_details *details, const char
         }          }
     }      }
   
     /*  
      * Restore nproc resource limit if pam_limits didn't do it for us.  
      * We must do this *after* the uid change to avoid potential EAGAIN  
      * from setuid().  
      */  
 #if defined(__linux__)  
     {  
         struct rlimit rl;  
         if (getrlimit(RLIMIT_NPROC, &rl) == 0) {  
             if (rl.rlim_cur == RLIM_INFINITY && rl.rlim_max == RLIM_INFINITY)  
                 (void) setrlimit(RLIMIT_NPROC, &nproclimit);  
         }  
     }  
 #endif  
   
     rval = true;      rval = true;
   
 done:  done:
Line 1110  static void Line 1108  static void
 policy_close(struct plugin_container *plugin, int exit_status, int error)  policy_close(struct plugin_container *plugin, int exit_status, int error)
 {  {
     debug_decl(policy_close, SUDO_DEBUG_PCOMM)      debug_decl(policy_close, SUDO_DEBUG_PCOMM)
    plugin->u.policy->close(exit_status, error);    if (plugin->u.policy->close != NULL)
         plugin->u.policy->close(exit_status, error);
     else
         warning(_("unable to execute %s"), command_details.command);
     debug_return;      debug_return;
 }  }
   
Line 1118  static int Line 1119  static int
 policy_show_version(struct plugin_container *plugin, int verbose)  policy_show_version(struct plugin_container *plugin, int verbose)
 {  {
     debug_decl(policy_show_version, SUDO_DEBUG_PCOMM)      debug_decl(policy_show_version, SUDO_DEBUG_PCOMM)
       if (plugin->u.policy->show_version == NULL)
           debug_return_bool(true);
     debug_return_bool(plugin->u.policy->show_version(verbose));      debug_return_bool(plugin->u.policy->show_version(verbose));
 }  }
   
Line 1127  policy_check(struct plugin_container *plugin, int argc Line 1130  policy_check(struct plugin_container *plugin, int argc
     char **user_env_out[])      char **user_env_out[])
 {  {
     debug_decl(policy_check, SUDO_DEBUG_PCOMM)      debug_decl(policy_check, SUDO_DEBUG_PCOMM)
       if (plugin->u.policy->check_policy == NULL) {
           fatalx(_("policy plugin %s is missing the `check_policy' method"),
               plugin->name);
       }
     debug_return_bool(plugin->u.policy->check_policy(argc, argv, env_add,      debug_return_bool(plugin->u.policy->check_policy(argc, argv, env_add,
         command_info, argv_out, user_env_out));          command_info, argv_out, user_env_out));
 }  }
Line 1161  policy_invalidate(struct plugin_container *plugin, int Line 1168  policy_invalidate(struct plugin_container *plugin, int
 {  {
     debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM)      debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM)
     if (plugin->u.policy->invalidate == NULL) {      if (plugin->u.policy->invalidate == NULL) {
        errorx(1, _("policy plugin %s does not support the -k/-K options"),        fatalx(_("policy plugin %s does not support the -k/-K options"),
             plugin->name);              plugin->name);
     }      }
     plugin->u.policy->invalidate(remove);      plugin->u.policy->invalidate(remove);
Line 1225  static void Line 1232  static void
 iolog_close(struct plugin_container *plugin, int exit_status, int error)  iolog_close(struct plugin_container *plugin, int exit_status, int error)
 {  {
     debug_decl(iolog_close, SUDO_DEBUG_PCOMM)      debug_decl(iolog_close, SUDO_DEBUG_PCOMM)
    plugin->u.io->close(exit_status, error);    if (plugin->u.io->close != NULL)
         plugin->u.io->close(exit_status, error);
     debug_return;      debug_return;
 }  }
   
Line 1233  static int Line 1241  static int
 iolog_show_version(struct plugin_container *plugin, int verbose)  iolog_show_version(struct plugin_container *plugin, int verbose)
 {  {
     debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM)      debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM)
       if (plugin->u.io->show_version == NULL)
           debug_return_bool(true);
     debug_return_bool(plugin->u.io->show_version(verbose));      debug_return_bool(plugin->u.io->show_version(verbose));
 }  }
   

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


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