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

Diff for /src/usr.bin/sudo/Attic/parse.c between version 1.19.2.1 and 1.20

version 1.19.2.1, 2009/02/22 21:56:32 version 1.20, 2008/11/14 11:58:08
Line 1 
Line 1 
 /*  /*
  * Copyright (c) 1996, 1998-2005, 2007   * Copyright (c) 2004-2005, 2007-2008 Todd C. Miller <Todd.Miller@courtesan.com>
  *      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 15 
Line 14 
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  
  * Sponsored in part by the Defense Advanced Research Projects  
  * Agency (DARPA) and Air Force Research Laboratory, Air Force  
  * Materiel Command, USAF, under agreement number F39502-99-1-0512.  
  */   */
   
 #include <config.h>  #include <config.h>
   
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/param.h>  #include <sys/param.h>
 #include <sys/socket.h>  
 #include <sys/stat.h>  
 #include <stdio.h>  #include <stdio.h>
 #ifdef STDC_HEADERS  #ifdef STDC_HEADERS
 # include <stdlib.h>  # include <stdlib.h>
Line 46 
Line 39 
 #ifdef HAVE_UNISTD_H  #ifdef HAVE_UNISTD_H
 # include <unistd.h>  # include <unistd.h>
 #endif /* HAVE_UNISTD_H */  #endif /* HAVE_UNISTD_H */
 #ifdef HAVE_FNMATCH  
 # include <fnmatch.h>  
 #endif /* HAVE_FNMATCH */  
 #ifdef HAVE_EXTENDED_GLOB  
 # include <glob.h>  
 #endif /* HAVE_EXTENDED_GLOB */  
 #ifdef HAVE_NETGROUP_H  
 # include <netgroup.h>  
 #endif /* HAVE_NETGROUP_H */  
 #include <ctype.h>  #include <ctype.h>
 #include <pwd.h>  #include <pwd.h>
 #include <grp.h>  #include <grp.h>
 #include <netinet/in.h>  
 #include <arpa/inet.h>  
 #include <netdb.h>  
 #ifdef HAVE_DIRENT_H  
 # include <dirent.h>  
 # define NAMLEN(dirent) strlen((dirent)->d_name)  
 #else  
 # define dirent direct  
 # define NAMLEN(dirent) (dirent)->d_namlen  
 # ifdef HAVE_SYS_NDIR_H  
 #  include <sys/ndir.h>  
 # endif  
 # ifdef HAVE_SYS_DIR_H  
 #  include <sys/dir.h>  
 # endif  
 # ifdef HAVE_NDIR_H  
 #  include <ndir.h>  
 # endif  
 #endif  
   
 #include "sudo.h"  #include "sudo.h"
 #include "parse.h"  #include "parse.h"
 #include "interfaces.h"  #include "lbuf.h"
   #include <gram.h>
   
 #ifndef HAVE_FNMATCH  
 # include "emul/fnmatch.h"  
 #endif /* HAVE_FNMATCH */  
 #ifndef HAVE_EXTENDED_GLOB  
 # include "emul/glob.h"  
 #endif /* HAVE_EXTENDED_GLOB */  
   
 #ifndef lint  #ifndef lint
 __unused static const char rcsid[] = "$Sudo: parse.c,v 1.160.2.16 2008/02/09 14:44:48 millert Exp $";  __unused static const char rcsid[] = "$Sudo: parse.c,v 1.236 2008/11/09 14:13:12 millert Exp $";
 #endif /* lint */  #endif /* lint */
   
   /* Characters that must be quoted in sudoers */
   #define SUDOERS_QUOTED  ":\\,=#\""
   
   /* sudoers nsswitch routines */
   struct sudo_nss sudo_nss_file = {
       &sudo_nss_file,
       NULL,
       sudo_file_open,
       sudo_file_close,
       sudo_file_parse,
       sudo_file_setdefs,
       sudo_file_lookup,
       sudo_file_display_cmnd,
       sudo_file_display_defaults,
       sudo_file_display_bound_defaults,
       sudo_file_display_privs
   };
   
 /*  /*
  * Globals   * Parser externs.
  */   */
 int parse_error = FALSE;  extern FILE *yyin;
 extern int keepall;  extern char *errorfile;
 extern FILE *yyin, *yyout;  extern int errorlineno, parse_error;
   
 /*  /*
  * Prototypes   * Local prototypes.
  */   */
 static int has_meta     __P((char *));  static void print_member        __P((struct lbuf *, char *, int, int, int));
        void init_parser __P((void));  static int display_bound_defaults __P((int, struct lbuf *));
   
   int
   sudo_file_open(nss)
       struct sudo_nss *nss;
   {
       if (def_ignore_local_sudoers)
           return(-1);
       nss->handle = open_sudoers(_PATH_SUDOERS, NULL);
       return(nss->handle ? 0 : -1);
   }
   
   int
   sudo_file_close(nss)
       struct sudo_nss *nss;
   {
       /* Free parser data structures and close sudoers file. */
       init_parser(NULL, 0);
       if (nss->handle != NULL) {
           fclose(nss->handle);
           nss->handle = NULL;
           yyin = NULL;
       }
       return(0);
   }
   
 /*  /*
  * Look up the user in the sudoers file and check to see if they are   * Parse the specified sudoers file.
  * allowed to run the specified command on this host as the target user.  
  */   */
 int  int
 sudoers_lookup(pwflag)  sudo_file_parse(nss)
     int pwflag;      struct sudo_nss *nss;
 {  {
     int error, nopass;      if (nss->handle == NULL)
           return(-1);
   
     /* We opened _PATH_SUDOERS in check_sudoers() so just rewind it. */      init_parser(_PATH_SUDOERS, 0);
     rewind(sudoers_fp);      yyin = nss->handle;
     yyin = sudoers_fp;      if (yyparse() != 0 || parse_error) {
     yyout = stdout;          log_error(NO_EXIT, "parse error in %s near line %d",
               errorfile, errorlineno);
           return(-1);
       }
       return(0);
   }
   
     /* Allocate space for data structures in the parser. */  /*
     init_parser();   * Wrapper around update_defaults() for nsswitch code.
    */
   int
   sudo_file_setdefs(nss)
       struct sudo_nss *nss;
   {
       if (nss->handle == NULL)
           return(-1);
   
     /* Keep more state for pseudo-commands so that listpw and verifypw work */      if (!update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER))
     if (pwflag > 0)          return(-1);
         keepall = TRUE;      return(0);
   }
   
     /* Need to be runas user while stat'ing things in the parser. */  /*
     set_perms(PERM_RUNAS);   * Look up the user in the parsed sudoers file and check to see if they are
     error = yyparse();   * allowed to run the specified command on this host as the target user.
    */
   int
   sudo_file_lookup(nss, validated, pwflag)
       struct sudo_nss *nss;
       int validated;
       int pwflag;
   {
       int match, host_match, runas_match, cmnd_match;
       struct cmndspec *cs;
       struct cmndtag *tags = NULL;
       struct privilege *priv;
       struct userspec *us;
   
     /* Close the sudoers file now that we are done with it. */      if (nss->handle == NULL)
     (void) fclose(sudoers_fp);          return(validated);
     sudoers_fp = NULL;  
   
     if (error || parse_error) {  
         set_perms(PERM_ROOT);  
         return(VALIDATE_ERROR);  
     }  
   
     /*      /*
      * Assume the worst.  If the stack is empty the user was  
      * not mentioned at all.  
      */  
     if (def_authenticate)  
         error = VALIDATE_NOT_OK;  
     else  
         error = VALIDATE_NOT_OK | FLAG_NOPASS;  
     if (pwflag) {  
         SET(error, FLAG_NO_CHECK);  
     } else {  
         SET(error, FLAG_NO_HOST);  
         if (!top)  
             SET(error, FLAG_NO_USER);  
     }  
   
     /*  
      * Only check the actual command if pwflag is not set.       * Only check the actual command if pwflag is not set.
      * It is set for the "validate", "list" and "kill" pseudo-commands.       * It is set for the "validate", "list" and "kill" pseudo-commands.
      * Always check the host and user.       * Always check the host and user.
      */       */
     nopass = -1;  
     if (pwflag) {      if (pwflag) {
         int found;          int nopass = UNSPEC;
         enum def_tupple pwcheck;          enum def_tupple pwcheck;
   
         pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;          pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
   
         if (pwcheck == always && def_authenticate)          if (list_pw == NULL)
             nopass = FLAG_CHECK_USER;              SET(validated, FLAG_NO_CHECK);
         else if (pwcheck == never || !def_authenticate)          CLR(validated, FLAG_NO_USER);
             nopass = FLAG_NOPASS;          CLR(validated, FLAG_NO_HOST);
         found = 0;          match = DENY;
         while (top) {          tq_foreach_rev(&userspecs, us) {
             if (host_matches == TRUE) {              if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
                 found = 1;                  continue;
                 if (pwcheck == any && no_passwd == TRUE)              tq_foreach_rev(&us->privileges, priv) {
                     nopass = FLAG_NOPASS;                  if (hostlist_matches(&priv->hostlist) != ALLOW)
                 else if (pwcheck == all && nopass != 0)                      continue;
                     nopass = (no_passwd == TRUE) ? FLAG_NOPASS : 0;                  tq_foreach_rev(&priv->cmndlist, cs) {
             }                      /* Only check the command when listing another user. */
             top--;                      if (user_uid == 0 || list_pw == NULL ||
         }                          user_uid == list_pw->pw_uid ||
         if (found) {                          cmnd_matches(cs->cmnd) == ALLOW)
             set_perms(PERM_ROOT);                              match = ALLOW;
             if (nopass == -1)                      if ((pwcheck == any && nopass != TRUE) ||
                 nopass = 0;                          (pwcheck == all && nopass != FALSE))
             return(VALIDATE_OK | nopass);                          nopass = cs->tags.nopasswd;
         }                      if (match == ALLOW)
     } else {                          goto matched_pseudo;
         while (top) {  
             if (host_matches == TRUE) {  
                 CLR(error, FLAG_NO_HOST);  
                 if (runas_matches == TRUE && cmnd_matches == TRUE) {  
                     /*  
                      * User was granted access to cmnd on host as user.  
                      */  
 #ifdef HAVE_SELINUX  
                     /* Set role and type if not specified on command line. */  
                     if (user_role == NULL) {  
                         if (match[top-1].role != NULL)  
                             user_role = match[top-1].role;  
                         else  
                             user_role = def_role;  
                     }  
                     if (user_type == NULL) {  
                         if (match[top-1].type != NULL)  
                             user_type = match[top-1].type;  
                         else  
                             user_type = def_type;  
                     }  
 #endif  
                     set_perms(PERM_ROOT);  
                     return(VALIDATE_OK |  
                         (no_passwd == TRUE ? FLAG_NOPASS : 0) |  
                         (no_execve == TRUE ? FLAG_NOEXEC : 0) |  
                         (setenv_ok >= TRUE ? FLAG_SETENV : 0));  
                 } else if ((runas_matches == TRUE && cmnd_matches == FALSE) ||  
                     (runas_matches == FALSE && cmnd_matches == TRUE)) {  
                     /*  
                      * User was explicitly denied access to cmnd on host.  
                      */  
                     set_perms(PERM_ROOT);  
                     return(VALIDATE_NOT_OK |  
                         (no_passwd == TRUE ? FLAG_NOPASS : 0) |  
                         (no_execve == TRUE ? FLAG_NOEXEC : 0) |  
                         (setenv_ok >= TRUE ? FLAG_SETENV : 0));  
                 }                  }
             }              }
             top--;  
         }          }
           matched_pseudo:
           if (match == ALLOW || user_uid == 0) {
               /* User has an entry for this host. */
               SET(validated, VALIDATE_OK);
           } else if (match == DENY)
               SET(validated, VALIDATE_NOT_OK);
           if (pwcheck == always && def_authenticate)
               SET(validated, FLAG_CHECK_USER);
           else if (pwcheck == never || nopass == TRUE)
               def_authenticate = FALSE;
           return(validated);
     }      }
     set_perms(PERM_ROOT);  
   
     /*      /* Need to be runas user while stat'ing things. */
      * The user was neither explicitly granted nor denied access.      set_perms(PERM_RUNAS);
      */  
     if (nopass == -1)  
         nopass = 0;  
     return(error | nopass);  
 }  
   
 /*      match = UNSPEC;
  * If path doesn't end in /, return TRUE iff cmnd & path name the same inode;      tq_foreach_rev(&userspecs, us) {
  * otherwise, return TRUE if user_cmnd names one of the inodes in path.          if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
  */              continue;
 int          CLR(validated, FLAG_NO_USER);
 command_matches(sudoers_cmnd, sudoers_args)          tq_foreach_rev(&us->privileges, priv) {
     char *sudoers_cmnd;              host_match = hostlist_matches(&priv->hostlist);
     char *sudoers_args;              if (host_match == ALLOW)
 {                  CLR(validated, FLAG_NO_HOST);
     struct stat sudoers_stat;  
     struct dirent *dent;  
     char **ap, *base, buf[PATH_MAX];  
     glob_t gl;  
     DIR *dirp;  
   
     /* Check for pseudo-commands */  
     if (strchr(user_cmnd, '/') == NULL) {  
         /*  
          * Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND  
          *  a) there are no args in sudoers OR  
          *  b) there are no args on command line and none req by sudoers OR  
          *  c) there are args in sudoers and on command line and they match  
          */  
         if (strcmp(sudoers_cmnd, "sudoedit") != 0 ||  
             strcmp(user_cmnd, "sudoedit") != 0)  
             return(FALSE);  
         if (!sudoers_args ||  
             (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||  
             (sudoers_args &&  
              fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {  
             efree(safe_cmnd);  
             safe_cmnd = estrdup(sudoers_cmnd);  
             return(TRUE);  
         } else  
             return(FALSE);  
     }  
   
     /*  
      * If sudoers_cmnd has meta characters in it, use fnmatch(3)  
      * to do the matching.  
      */  
     if (has_meta(sudoers_cmnd)) {  
         /*  
          * Return true if we find a match in the glob(3) results AND  
          *  a) there are no args in sudoers OR  
          *  b) there are no args on command line and none required by sudoers OR  
          *  c) there are args in sudoers and on command line and they match  
          * else return false.  
          *  
          * Could optimize patterns ending in "/*" to "/user_base"  
          */  
 #define GLOB_FLAGS      (GLOB_NOSORT | GLOB_MARK | GLOB_BRACE | GLOB_TILDE)  
         if (glob(sudoers_cmnd, GLOB_FLAGS, NULL, &gl) != 0) {  
             globfree(&gl);  
             return(FALSE);  
         }  
         /* For each glob match, compare basename, st_dev and st_ino. */  
         for (ap = gl.gl_pathv; *ap != NULL; ap++) {  
             /* only stat if basenames are the same */  
             if ((base = strrchr(*ap, '/')) != NULL)  
                 base++;  
             else              else
                 base = *ap;  
             if (strcmp(user_base, base) != 0 ||  
                 stat(*ap, &sudoers_stat) == -1)  
                 continue;                  continue;
             if (user_stat->st_dev == sudoers_stat.st_dev &&              tq_foreach_rev(&priv->cmndlist, cs) {
                 user_stat->st_ino == sudoers_stat.st_ino) {                  runas_match = runaslist_matches(&cs->runasuserlist,
                 efree(safe_cmnd);                      &cs->runasgrouplist);
                 safe_cmnd = estrdup(*ap);                  if (runas_match == ALLOW) {
                 break;                      cmnd_match = cmnd_matches(cs->cmnd);
                       if (cmnd_match != UNSPEC) {
                           match = cmnd_match;
                           tags = &cs->tags;
   #ifdef HAVE_SELINUX
                           /* Set role and type if not specified on command line. */
                           if (user_role == NULL)
                               user_role = cs->role ? estrdup(cs->role) : def_role;
                           if (user_type == NULL)
                               user_type = cs->type ? estrdup(cs->type) : def_type;
   #endif /* HAVE_SELINUX */
                           goto matched2;
                       }
                   }
             }              }
         }          }
         globfree(&gl);      }
         if (*ap == NULL)      matched2:
             return(FALSE);      if (match == ALLOW) {
           SET(validated, VALIDATE_OK);
         if (!sudoers_args ||          CLR(validated, VALIDATE_NOT_OK);
             (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||          if (tags != NULL) {
             (sudoers_args &&              if (tags->nopasswd != UNSPEC)
              fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {                  def_authenticate = !tags->nopasswd;
             efree(safe_cmnd);              if (tags->noexec != UNSPEC)
             safe_cmnd = estrdup(user_cmnd);                  def_noexec = tags->noexec;
             return(TRUE);              if (tags->setenv != UNSPEC)
         } else                  def_setenv = tags->setenv;
             return(FALSE);  
     } else {  
         size_t dlen = strlen(sudoers_cmnd);  
   
         /*  
          * No meta characters  
          * Check to make sure this is not a directory spec (doesn't end in '/')  
          */  
         if (sudoers_cmnd[dlen - 1] != '/') {  
             /* Only proceed if user_base and basename(sudoers_cmnd) match */  
             if ((base = strrchr(sudoers_cmnd, '/')) == NULL)  
                 base = sudoers_cmnd;  
             else  
                 base++;  
             if (strcmp(user_base, base) != 0 ||  
                 stat(sudoers_cmnd, &sudoers_stat) == -1)  
                 return(FALSE);  
   
             /*  
              * Return true if inode/device matches AND  
              *  a) there are no args in sudoers OR  
              *  b) there are no args on command line and none req by sudoers OR  
              *  c) there are args in sudoers and on command line and they match  
              */  
             if (user_stat->st_dev != sudoers_stat.st_dev ||  
                 user_stat->st_ino != sudoers_stat.st_ino)  
                 return(FALSE);  
             if (!sudoers_args ||  
                 (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||  
                 (sudoers_args &&  
                  fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {  
                 efree(safe_cmnd);  
                 safe_cmnd = estrdup(sudoers_cmnd);  
                 return(TRUE);  
             } else  
                 return(FALSE);  
         }          }
       } else if (match == DENY) {
           SET(validated, VALIDATE_NOT_OK);
           CLR(validated, VALIDATE_OK);
       }
       set_perms(PERM_ROOT);
       return(validated);
   }
   
         /*  #define TAG_CHANGED(t) \
          * Grot through sudoers_cmnd's directory entries, looking for user_base.          (cs->tags.t != UNSPEC && cs->tags.t != IMPLIED && cs->tags.t != tags->t)
          */  
         dirp = opendir(sudoers_cmnd);  
         if (dirp == NULL)  
             return(FALSE);  
   
         if (strlcpy(buf, sudoers_cmnd, sizeof(buf)) >= sizeof(buf))  static void
             return(FALSE);  sudo_file_append_cmnd(cs, tags, lbuf)
         while ((dent = readdir(dirp)) != NULL) {      struct cmndspec *cs;
             /* ignore paths > PATH_MAX (XXX - log) */      struct cmndtag *tags;
             buf[dlen] = '\0';      struct lbuf *lbuf;
             if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))  {
                 continue;      struct member *m;
   
             /* only stat if basenames are the same */  #ifdef HAVE_SELINUX
             if (strcmp(user_base, dent->d_name) != 0 ||      if (cs->role)
                 stat(buf, &sudoers_stat) == -1)          lbuf_append(lbuf, "ROLE=", cs->role, " ", NULL);
                 continue;      if (cs->type)
             if (user_stat->st_dev == sudoers_stat.st_dev &&          lbuf_append(lbuf, "TYPE=", cs->type, " ", NULL);
                 user_stat->st_ino == sudoers_stat.st_ino) {  #endif /* HAVE_SELINUX */
                 efree(safe_cmnd);      if (TAG_CHANGED(setenv)) {
                 safe_cmnd = estrdup(buf);          lbuf_append(lbuf, cs->tags.setenv ? "SETENV: " :
                 break;              "NOSETENV: ", NULL);
             }          tags->setenv = cs->tags.setenv;
         }  
   
         closedir(dirp);  
         return(dent != NULL);  
     }      }
       if (TAG_CHANGED(noexec)) {
           lbuf_append(lbuf, cs->tags.noexec ? "NOEXEC: " :
               "EXEC: ", NULL);
           tags->noexec = cs->tags.noexec;
       }
       if (TAG_CHANGED(nopasswd)) {
           lbuf_append(lbuf, cs->tags.nopasswd ? "NOPASSWD: " :
               "PASSWD: ", NULL);
           tags->nopasswd = cs->tags.nopasswd;
       }
       m = cs->cmnd;
       print_member(lbuf, m->name, m->type, m->negated,
           CMNDALIAS);
 }  }
   
 static int  static int
 addr_matches_if(n)  sudo_file_display_priv_short(pw, us, lbuf)
     char *n;      struct passwd *pw;
       struct userspec *us;
       struct lbuf *lbuf;
 {  {
     int i;      struct cmndspec *cs;
     struct in_addr addr;      struct member *m;
     struct interface *ifp;      struct privilege *priv;
 #ifdef HAVE_IN6_ADDR      struct cmndtag tags;
     struct in6_addr addr6;      int nfound = 0;
     int j;  
 #endif  
     int family;  
   
 #ifdef HAVE_IN6_ADDR      tq_foreach_fwd(&us->privileges, priv) {
     if (inet_pton(AF_INET6, n, &addr6) > 0) {          tags.noexec = UNSPEC;
         family = AF_INET6;          tags.setenv = UNSPEC;
     } else          tags.nopasswd = UNSPEC;
 #endif          lbuf_append(lbuf, "    ", NULL);
     {          tq_foreach_fwd(&priv->cmndlist, cs) {
         family = AF_INET;              if (cs != tq_first(&priv->cmndlist))
         addr.s_addr = inet_addr(n);                  lbuf_append(lbuf, ", ", NULL);
     }              lbuf_append(lbuf, "(", NULL);
               if (!tq_empty(&cs->runasuserlist)) {
     for (i = 0; i < num_interfaces; i++) {                  tq_foreach_fwd(&cs->runasuserlist, m) {
         ifp = &interfaces[i];                      if (m != tq_first(&cs->runasuserlist))
         if (ifp->family != family)                          lbuf_append(lbuf, ", ", NULL);
             continue;                      print_member(lbuf, m->name, m->type, m->negated,
         switch(family) {                          RUNASALIAS);
             case AF_INET:  
                 if (ifp->addr.ip4.s_addr == addr.s_addr ||  
                     (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr)  
                     == addr.s_addr)  
                     return(TRUE);  
                 break;  
 #ifdef HAVE_IN6_ADDR  
             case AF_INET6:  
                 if (memcmp(ifp->addr.ip6.s6_addr, addr6.s6_addr,  
                     sizeof(addr6.s6_addr)) == 0)  
                     return(TRUE);  
                 for (j = 0; j < sizeof(addr6.s6_addr); j++) {  
                     if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr6.s6_addr[j])  
                         break;  
                 }                  }
                 if (j == sizeof(addr6.s6_addr))              } else {
                     return(TRUE);                  lbuf_append(lbuf, def_runas_default, NULL);
 #endif /* HAVE_IN6_ADDR */              }
               if (!tq_empty(&cs->runasgrouplist)) {
                   lbuf_append(lbuf, " : ", NULL);
                   tq_foreach_fwd(&cs->runasgrouplist, m) {
                       if (m != tq_first(&cs->runasgrouplist))
                           lbuf_append(lbuf, ", ", NULL);
                       print_member(lbuf, m->name, m->type, m->negated,
                           RUNASALIAS);
                   }
               }
               lbuf_append(lbuf, ") ", NULL);
               sudo_file_append_cmnd(cs, &tags, lbuf);
               nfound++;
         }          }
           lbuf_print(lbuf);               /* forces a newline */
     }      }
       return(nfound);
     return(FALSE);  
 }  }
   
 static int  static int
 addr_matches_if_netmask(n, m)  sudo_file_display_priv_long(pw, us, lbuf)
     char *n;      struct passwd *pw;
     char *m;      struct userspec *us;
       struct lbuf *lbuf;
 {  {
     int i;      struct cmndspec *cs;
     struct in_addr addr, mask;      struct member *m;
     struct interface *ifp;      struct privilege *priv;
 #ifdef HAVE_IN6_ADDR      struct cmndtag tags;
     struct in6_addr addr6, mask6;      int nfound = 0;
     int j;  
 #endif  
     int family;  
   
 #ifdef HAVE_IN6_ADDR      tq_foreach_fwd(&us->privileges, priv) {
     if (inet_pton(AF_INET6, n, &addr6) > 0)          tags.noexec = UNSPEC;
         family = AF_INET6;          tags.setenv = UNSPEC;
     else          tags.nopasswd = UNSPEC;
 #endif          lbuf_print(lbuf);       /* force a newline */
     {          lbuf_append(lbuf, "Sudoers entry:", NULL);
         family = AF_INET;          lbuf_print(lbuf);
         addr.s_addr = inet_addr(n);          tq_foreach_fwd(&priv->cmndlist, cs) {
     }              lbuf_append(lbuf, "    RunAsUsers: ", NULL);
               if (!tq_empty(&cs->runasuserlist)) {
     if (family == AF_INET) {                  tq_foreach_fwd(&cs->runasuserlist, m) {
         if (strchr(m, '.'))                      if (m != tq_first(&cs->runasuserlist))
             mask.s_addr = inet_addr(m);                          lbuf_append(lbuf, ", ", NULL);
         else {                      print_member(lbuf, m->name, m->type, m->negated,
             i = 32 - atoi(m);                          RUNASALIAS);
             mask.s_addr = 0xffffffff;                  }
             mask.s_addr >>= i;              } else {
             mask.s_addr <<= i;                  lbuf_append(lbuf, def_runas_default, NULL);
             mask.s_addr = htonl(mask.s_addr);  
         }  
     }  
 #ifdef HAVE_IN6_ADDR  
     else {  
         if (inet_pton(AF_INET6, m, &mask6) <= 0) {  
             j = atoi(m);  
             for (i = 0; i < 16; i++) {  
                 if (j < i * 8)  
                     mask6.s6_addr[i] = 0;  
                 else if (i * 8 + 8 <= j)  
                     mask6.s6_addr[i] = 0xff;  
                 else  
                     mask6.s6_addr[i] = 0xff00 >> (j - i * 8);  
             }              }
         }              lbuf_print(lbuf);
     }              if (!tq_empty(&cs->runasgrouplist)) {
 #endif /* HAVE_IN6_ADDR */                  lbuf_append(lbuf, "    RunAsGroups: ", NULL);
                   tq_foreach_fwd(&cs->runasgrouplist, m) {
     for (i = 0; i < num_interfaces; i++) {                      if (m != tq_first(&cs->runasgrouplist))
         ifp = &interfaces[i];                          lbuf_append(lbuf, ", ", NULL);
         if (ifp->family != family)                      print_member(lbuf, m->name, m->type, m->negated,
             continue;                          RUNASALIAS);
         switch(family) {  
             case AF_INET:  
                 if ((ifp->addr.ip4.s_addr & mask.s_addr) == addr.s_addr)  
                     return(TRUE);  
 #ifdef HAVE_IN6_ADDR  
             case AF_INET6:  
                 for (j = 0; j < sizeof(addr6.s6_addr); j++) {  
                     if ((ifp->addr.ip6.s6_addr[j] & mask6.s6_addr[j]) != addr6.s6_addr[j])  
                         break;  
                 }                  }
                 if (j == sizeof(addr6.s6_addr))                  lbuf_print(lbuf);
                     return(TRUE);              }
 #endif /* HAVE_IN6_ADDR */              lbuf_append(lbuf, "    Commands: ", NULL);
               lbuf_print(lbuf);
               lbuf_append(lbuf, "\t", NULL);
               sudo_file_append_cmnd(cs, &tags, lbuf);
               lbuf_print(lbuf);
               nfound++;
         }          }
     }      }
       return(nfound);
     return(FALSE);  
 }  }
   
 /*  
  * Returns TRUE if "n" is one of our ip addresses or if  
  * "n" is a network that we are on, else returns FALSE.  
  */  
 int  int
 addr_matches(n)  sudo_file_display_privs(nss, pw, lbuf)
     char *n;      struct sudo_nss *nss;
       struct passwd *pw;
       struct lbuf *lbuf;
 {  {
     char *m;      struct userspec *us;
     int retval;      int nfound = 0;
   
     /* If there's an explicit netmask, use it. */      if (nss->handle == NULL)
     if ((m = strchr(n, '/'))) {          return(-1);
         *m++ = '\0';  
         retval = addr_matches_if_netmask(n, m);  
         *(m - 1) = '/';  
     } else  
         retval = addr_matches_if(n);  
   
     return(retval);      tq_foreach_fwd(&userspecs, us) {
 }          /* XXX - why only check the first privilege here? */
           if (userlist_matches(pw, &us->users) != ALLOW ||
               hostlist_matches(&us->privileges.first->hostlist) != ALLOW)
               continue;
   
 /*          if (long_list)
  * Returns 0 if the hostname matches the pattern and non-zero otherwise.              nfound += sudo_file_display_priv_long(pw, us, lbuf);
  */  
 int  
 hostname_matches(shost, lhost, pattern)  
     char *shost;  
     char *lhost;  
     char *pattern;  
 {  
     if (has_meta(pattern)) {  
         if (strchr(pattern, '.'))  
             return(fnmatch(pattern, lhost, FNM_CASEFOLD));  
         else          else
             return(fnmatch(pattern, shost, FNM_CASEFOLD));              nfound += sudo_file_display_priv_short(pw, us, lbuf);
     } else {  
         if (strchr(pattern, '.'))  
             return(strcasecmp(lhost, pattern));  
         else  
             return(strcasecmp(shost, pattern));  
     }      }
       return(nfound);
 }  }
   
 /*  /*
  *  Returns TRUE if the user/uid from sudoers matches the specified user/uid,   * Display matching Defaults entries for the given user on this host.
  *  else returns FALSE.  
  */   */
 int  int
 userpw_matches(sudoers_user, user, pw)  sudo_file_display_defaults(nss, pw, lbuf)
     char *sudoers_user;      struct sudo_nss *nss;
     char *user;  
     struct passwd *pw;      struct passwd *pw;
       struct lbuf *lbuf;
 {  {
     if (pw != NULL && *sudoers_user == '#') {      struct defaults *d;
         uid_t uid = atoi(sudoers_user + 1);      char *prefix = NULL;
         if (uid == pw->pw_uid)      int nfound = 0;
             return(1);  
       if (nss->handle == NULL)
           return(-1);
   
       if (lbuf->len == 0)
           prefix = "    ";
       else
           prefix = ", ";
   
       tq_foreach_fwd(&defaults, d) {
           switch (d->type) {
               case DEFAULTS_HOST:
                   if (hostlist_matches(&d->binding) != ALLOW)
                       continue;
                   break;
               case DEFAULTS_USER:
                   if (userlist_matches(pw, &d->binding) != ALLOW)
                       continue;
                   break;
               case DEFAULTS_RUNAS:
               case DEFAULTS_CMND:
                   continue;
           }
           lbuf_append(lbuf, prefix, NULL);
           if (d->val != NULL) {
               lbuf_append(lbuf, d->var, d->op == '+' ? "+=" :
                   d->op == '-' ? "-=" : "=", NULL);
               if (strpbrk(d->val, " \t") != NULL) {
                   lbuf_append(lbuf, "\"", NULL);
                   lbuf_append_quoted(lbuf, "\"", d->val, NULL);
                   lbuf_append(lbuf, "\"", NULL);
               } else
                   lbuf_append_quoted(lbuf, SUDOERS_QUOTED, d->val, NULL);
           } else
               lbuf_append(lbuf, d->op == FALSE ? "!" : "", d->var, NULL);
           prefix = ", ";
           nfound++;
     }      }
     return(strcmp(sudoers_user, user) == 0);  
       return(nfound);
 }  }
   
 /*  /*
  *  Returns TRUE if the given user belongs to the named group,   * Display Defaults entries that are per-runas or per-command
  *  else returns FALSE.  
  *  XXX - reduce the number of passwd/group lookups  
  */   */
 int  int
 usergr_matches(group, user, pw)  sudo_file_display_bound_defaults(nss, pw, lbuf)
     char *group;      struct sudo_nss *nss;
     char *user;  
     struct passwd *pw;      struct passwd *pw;
       struct lbuf *lbuf;
 {  {
     struct group *grp;      int nfound = 0;
     gid_t pw_gid;  
     char **cur;  
     int i;  
   
     /* make sure we have a valid usergroup, sudo style */      /* XXX - should only print ones that match what the user can do. */
     if (*group++ != '%')      nfound += display_bound_defaults(DEFAULTS_RUNAS, lbuf);
         return(FALSE);      nfound += display_bound_defaults(DEFAULTS_CMND, lbuf);
   
     /* look up user's primary gid in the passwd file */      return(nfound);
     if (pw == NULL && (pw = getpwnam(user)) == NULL)  }
         return(FALSE);  
     pw_gid = pw->pw_gid;  
   
     if ((grp = getgrnam(group)) == NULL)  /*
         return(FALSE);   * Display Defaults entries of the given type.
    */
   static int
   display_bound_defaults(dtype, lbuf)
       int dtype;
       struct lbuf *lbuf;
   {
       struct defaults *d;
       struct member *m, *binding = NULL;
       char *dname, *dsep;
       int atype, nfound = 0;
   
     /* check against user's primary (passwd file) gid */      switch (dtype) {
     if (grp->gr_gid == pw_gid)          case DEFAULTS_HOST:
         return(TRUE);              atype = HOSTALIAS;
               dname = "host";
               dsep = "@";
               break;
           case DEFAULTS_USER:
               atype = USERALIAS;
               dname = "user";
               dsep = ":";
               break;
           case DEFAULTS_RUNAS:
               atype = RUNASALIAS;
               dname = "runas";
               dsep = ">";
               break;
           case DEFAULTS_CMND:
               atype = CMNDALIAS;
               dname = "cmnd";
               dsep = "!";
               break;
           default:
               return(-1);
       }
       /* printf("Per-%s Defaults entries:\n", dname); */
       tq_foreach_fwd(&defaults, d) {
           if (d->type != dtype)
               continue;
   
     /*          nfound++;
      * If the user has a supplementary group vector, check it first.          if (binding != tq_first(&d->binding)) {
      */              binding = tq_first(&d->binding);
     if (strcmp(user, user_name) == 0) {              lbuf_append(lbuf, "    Defaults", dsep, NULL);
         for (i = 0; i < user_ngroups; i++) {              for (m = binding; m != NULL; m = m->next) {
             if (grp->gr_gid == user_groups[i])                  if (m != binding)
                 return(TRUE);                      lbuf_append(lbuf, ",", NULL);
         }                  print_member(lbuf, m->name, m->type, m->negated, atype);
                   lbuf_append(lbuf, " ", NULL);
               }
           } else
               lbuf_append(lbuf, ", ", NULL);
           if (d->val != NULL) {
               lbuf_append(lbuf, d->var, d->op == '+' ? "+=" :
                   d->op == '-' ? "-=" : "=", d->val, NULL);
           } else
               lbuf_append(lbuf, d->op == FALSE ? "!" : "", d->var, NULL);
     }      }
     if (grp->gr_mem != NULL) {  
         for (cur = grp->gr_mem; *cur; cur++) {  
             if (strcmp(*cur, user) == 0)  
                 return(TRUE);  
         }  
     }  
   
     return(FALSE);      return(nfound);
 }  }
   
 /*  
  * Returns TRUE if "host" and "user" belong to the netgroup "netgr",  
  * else return FALSE.  Either of "host", "shost" or "user" may be NULL  
  * in which case that argument is not checked...  
  */  
 int  int
 netgr_matches(netgr, host, shost, user)  sudo_file_display_cmnd(nss, pw)
     char *netgr;      struct sudo_nss *nss;
     char *host;      struct passwd *pw;
     char *shost;  
     char *user;  
 {  {
     static char *domain;      struct cmndspec *cs;
 #ifdef HAVE_GETDOMAINNAME      struct member *match;
     static int initialized;      struct privilege *priv;
 #endif      struct userspec *us;
       int rval = 1;
       int host_match, runas_match, cmnd_match;
   
     /* make sure we have a valid netgroup, sudo style */      if (nss->handle == NULL)
     if (*netgr++ != '+')          return(rval);
         return(FALSE);  
   
 #ifdef HAVE_GETDOMAINNAME      match = NULL;
     /* get the domain name (if any) */      tq_foreach_rev(&userspecs, us) {
     if (!initialized) {          if (userlist_matches(pw, &us->users) != ALLOW)
         domain = (char *) emalloc(MAXHOSTNAMELEN);              continue;
         if (getdomainname(domain, MAXHOSTNAMELEN) == -1 || *domain == '\0') {  
             efree(domain);          tq_foreach_rev(&us->privileges, priv) {
             domain = NULL;              host_match = hostlist_matches(&priv->hostlist);
               if (host_match != ALLOW)
                   continue;
               tq_foreach_rev(&priv->cmndlist, cs) {
                   runas_match = runaslist_matches(&cs->runasuserlist,
                       &cs->runasgrouplist);
                   if (runas_match == ALLOW) {
                       cmnd_match = cmnd_matches(cs->cmnd);
                       if (cmnd_match != UNSPEC) {
                           match = host_match && runas_match ?
                               cs->cmnd : NULL;
                           goto matched;
                       }
                   }
               }
         }          }
         initialized = 1;  
     }      }
 #endif /* HAVE_GETDOMAINNAME */      matched:
       if (match != NULL && !match->negated) {
 #ifdef HAVE_INNETGR          printf("%s%s%s\n", safe_cmnd, user_args ? " " : "",
     if (innetgr(netgr, host, user, domain))              user_args ? user_args : "");
         return(TRUE);          rval = 0;
     else if (host != shost && innetgr(netgr, shost, user, domain))      }
         return(TRUE);      return(rval);
 #endif /* HAVE_INNETGR */  
   
     return(FALSE);  
 }  }
   
 /*  /*
  * Returns TRUE if "s" has shell meta characters in it,   * Print the contents of a struct member to stdout
  * else returns FALSE.  
  */   */
 static int  static void
 has_meta(s)  _print_member(lbuf, name, type, negated, alias_type)
     char *s;      struct lbuf *lbuf;
       char *name;
       int type, negated, alias_type;
 {  {
     char *t;      struct alias *a;
       struct member *m;
     for (t = s; *t; t++) {      struct sudo_command *c;
         if (*t == '\\' || *t == '?' || *t == '*' || *t == '[' || *t == ']')  
             return(TRUE);      switch (type) {
           case ALL:
               lbuf_append(lbuf, negated ? "!ALL" : "ALL", NULL);
               break;
           case COMMAND:
               c = (struct sudo_command *) name;
               if (negated)
                   lbuf_append(lbuf, "!", NULL);
               lbuf_append_quoted(lbuf, SUDOERS_QUOTED, c->cmnd, NULL);
               if (c->args) {
                   lbuf_append(lbuf, " ", NULL);
                   lbuf_append_quoted(lbuf, SUDOERS_QUOTED, c->args, NULL);
               }
               break;
           case ALIAS:
               if ((a = find_alias(name, alias_type)) != NULL) {
                   tq_foreach_fwd(&a->members, m) {
                       if (m != tq_first(&a->members))
                           lbuf_append(lbuf, ", ", NULL);
                       _print_member(lbuf, m->name, m->type,
                           negated ? !m->negated : m->negated, alias_type);
                   }
                   break;
               }
               /* FALLTHROUGH */
           default:
               lbuf_append(lbuf, negated ? "!" : "", name, NULL);
               break;
     }      }
     return(FALSE);  }
   
   static void
   print_member(lbuf, name, type, negated, alias_type)
       struct lbuf *lbuf;
       char *name;
       int type, negated, alias_type;
   {
       alias_seqno++;
       _print_member(lbuf, name, type, negated, alias_type);
 }  }

Legend:
Removed from v.1.19.2.1  
changed lines
  Added in v.1.20