Imported Upstream version 1.6.9p6
[debian/sudo] / testsudoers.c
index 88fc2afc1bf48d363bb4ba10a8ed243759bb3f59..dd9953fe10c0e1b48ae129ed44113e0d6c46c868 100644 (file)
@@ -1,40 +1,28 @@
 /*
- * Copyright (c) 1996, 1998-2001 Todd C. Miller <Todd.Miller@courtesan.com>
- * All rights reserved.
+ * Copyright (c) 1996, 1998-2005 Todd C. Miller <Todd.Miller@courtesan.com>
  *
- * This code is derived from software contributed by Chris Jepeway.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * 4. Products derived from this software may not be called "Sudo" nor
- *    may "Sudo" appear in their names without specific prior written
- *    permission from the author.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
- * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * 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
  * 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"
+#define _SUDO_MAIN
+
+#include <config.h>
 
 #include <sys/param.h>
 #include <sys/types.h>
 #endif /* HAVE_UNISTD_H */
 #ifdef HAVE_FNMATCH
 # include <fnmatch.h>
-#endif /* HAVE_FNMATCH_H */
+#endif /* HAVE_FNMATCH */
 #ifdef HAVE_NETGROUP_H
 # include <netgroup.h>
 #endif /* HAVE_NETGROUP_H */
+#ifdef HAVE_ERR_H
+# include <err.h>
+#else
+# include "emul/err.h"
+#endif /* HAVE_ERR_H */
 #include <ctype.h>
 #include <pwd.h>
 #include <grp.h>
@@ -82,7 +75,7 @@
 #endif /* HAVE_FNMATCH */
 
 #ifndef lint
-static const char rcsid[] = "$Sudo: testsudoers.c,v 1.76 2002/03/16 00:44:48 millert Exp $";
+__unused static const char rcsid[] = "$Sudo: testsudoers.c,v 1.88.2.5 2007/08/25 02:45:09 millert Exp $";
 #endif /* lint */
 
 
@@ -91,18 +84,16 @@ static const char rcsid[] = "$Sudo: testsudoers.c,v 1.76 2002/03/16 00:44:48 mil
  */
 void init_parser       __P((void));
 void dumpaliases       __P((void));
-void set_perms_dummy   __P((int, int));
 
 /*
  * Globals
  */
-char **Argv, **NewArgv;
 int  Argc, NewArgc;
+char **Argv, **NewArgv;
 int parse_error = FALSE;
 int num_interfaces;
 struct interface *interfaces;
 struct sudo_user sudo_user;
-void (*set_perms) __P((int, int)) = set_perms_dummy;
 extern int clearaliases;
 extern int pedantic;
 
@@ -115,7 +106,7 @@ has_meta(s)
     char *s;
 {
     char *t;
-    
+
     for (t = s; *t; t++) {
        if (*t == '\\' || *t == '?' || *t == '*' || *t == '[' || *t == ']')
            return(TRUE);
@@ -124,78 +115,141 @@ has_meta(s)
 }
 
 /*
- * Returns TRUE if cmnd matches, in the sudo sense,
+ * Returns TRUE if user_cmnd matches, in the sudo sense,
  * the pathname in path; otherwise, return FALSE
  */
 int
-command_matches(cmnd, cmnd_args, path, sudoers_args)
-    char *cmnd;
-    char *cmnd_args;
+command_matches(path, sudoers_args)
     char *path;
     char *sudoers_args;
 {
     int clen, plen;
     char *args;
 
-    if (cmnd == NULL)
+    if (user_cmnd == NULL)
        return(FALSE);
 
-    if ((args = strchr(path, ' ')))  
+    if ((args = strchr(path, ' ')))
        *args++ = '\0';
 
     if (has_meta(path)) {
-       if (fnmatch(path, cmnd, FNM_PATHNAME))
+       if (fnmatch(path, user_cmnd, FNM_PATHNAME))
            return(FALSE);
        if (!sudoers_args)
            return(TRUE);
-       else if (!cmnd_args && sudoers_args && !strcmp("\"\"", sudoers_args))
+       else if (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args))
            return(TRUE);
        else if (sudoers_args)
-           return((fnmatch(sudoers_args, cmnd_args ? cmnd_args : "", 0) == 0));
+           return((fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0));
        else
            return(FALSE);
     } else {
        plen = strlen(path);
        if (path[plen - 1] != '/') {
-           if (strcmp(cmnd, path))
+           if (strcmp(user_cmnd, path))
                return(FALSE);
            if (!sudoers_args)
                return(TRUE);
-           else if (!cmnd_args && sudoers_args && !strcmp("\"\"", sudoers_args))
+           else if (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args))
                return(TRUE);
            else if (sudoers_args)
-               return((fnmatch(sudoers_args, cmnd_args ? cmnd_args : "", 0) == 0));
+               return((fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0));
            else
                return(FALSE);
        }
 
-       clen = strlen(cmnd);
+       clen = strlen(user_cmnd);
        if (clen < plen + 1)
-           /* path cannot be the parent dir of cmnd */
+           /* path cannot be the parent dir of user_cmnd */
            return(FALSE);
 
-       if (strchr(cmnd + plen + 1, '/') != NULL)
-           /* path could only be an anscestor of cmnd -- */
+       if (strchr(user_cmnd + plen + 1, '/') != NULL)
+           /* path could only be an anscestor of user_cmnd -- */
            /* ignoring, of course, things like // & /./  */
            return(FALSE);
 
-       /* see whether path is the prefix of cmnd */
-       return((strncmp(cmnd, path, plen) == 0));
+       /* see whether path is the prefix of user_cmnd */
+       return((strncmp(user_cmnd, path, plen) == 0));
     }
 }
 
-int
-addr_matches(n)
+static int
+addr_matches_if(n)
     char *n;
 {
     int i;
+    struct in_addr addr;
+    struct interface *ifp;
+#ifdef AF_INET6
+    struct in6_addr addr6;
+    int j;
+#endif
+    int family;
+
+#ifdef AF_INET6
+    if (inet_pton(AF_INET6, n, &addr6) > 0) {
+       family = AF_INET6;
+    } else
+#endif
+    {
+       family = AF_INET;
+       addr.s_addr = inet_addr(n);
+    }
+
+    for (i = 0; i < num_interfaces; i++) {
+       ifp = &interfaces[i];
+       if (ifp->family != family)
+           continue;
+       switch(family) {
+           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 AF_INET6
+           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))
+                   return(TRUE);
+#endif /* AF_INET6 */
+       }
+    }
+
+    return(FALSE);
+}
+
+static int
+addr_matches_if_netmask(n, m)
+    char *n;
     char *m;
+{
+    int i;
     struct in_addr addr, mask;
+    struct interface *ifp;
+#ifdef AF_INET6
+    struct in6_addr addr6, mask6;
+    int j;
+#endif
+    int family;
 
-    /* If there's an explicit netmask, use it. */
-    if ((m = strchr(n, '/'))) {
-       *m++ = '\0';
+#ifdef AF_INET6
+    if (inet_pton(AF_INET6, n, &addr6) > 0)
+       family = AF_INET6;
+    else
+#endif
+    {
+       family = AF_INET;
        addr.s_addr = inet_addr(n);
+    }
+
+    if (family == AF_INET) {
        if (strchr(m, '.'))
            mask.s_addr = inet_addr(m);
        else {
@@ -205,32 +259,76 @@ addr_matches(n)
            mask.s_addr <<= i;
            mask.s_addr = htonl(mask.s_addr);
        }
-       *(m - 1) = '/';               
-
-       for (i = 0; i < num_interfaces; i++)
-           if ((interfaces[i].addr.s_addr & mask.s_addr) == addr.s_addr)
-               return(TRUE);
-    } else {
-       addr.s_addr = inet_addr(n);
-
-       for (i = 0; i < num_interfaces; i++)
-           if (interfaces[i].addr.s_addr == addr.s_addr ||
-               (interfaces[i].addr.s_addr & interfaces[i].netmask.s_addr)
-               == addr.s_addr)
-               return(TRUE);
+    }
+#ifdef AF_INET6
+    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);
+           }
+       }
+    }
+#endif /* AF_INET6 */
+
+    for (i = 0; i < num_interfaces; i++) {
+       ifp = &interfaces[i];
+       if (ifp->family != family)
+           continue;
+       switch(family) {
+           case AF_INET:
+               if ((ifp->addr.ip4.s_addr & mask.s_addr) == addr.s_addr)
+                   return(TRUE);
+#ifdef AF_INET6
+           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))
+                   return(TRUE);
+#endif /* AF_INET6 */
+       }
     }
 
     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
+addr_matches(n)
+    char *n;
+{
+    char *m;
+    int retval;
+
+    /* If there's an explicit netmask, use it. */
+    if ((m = strchr(n, '/'))) {
+       *m++ = '\0';
+       retval = addr_matches_if_netmask(n, m);
+       *(m - 1) = '/';
+    } else
+       retval = addr_matches_if(n);
+
+    return(retval);
+}
+
 int
 hostname_matches(shost, lhost, pattern)
     char *shost;
     char *lhost;
     char *pattern;
 {
-    if (has_meta(pattern)) {  
-        if (strchr(pattern, '.'))   
+    if (has_meta(pattern)) {
+        if (strchr(pattern, '.'))
             return(fnmatch(pattern, lhost, FNM_CASEFOLD));
         else
             return(fnmatch(pattern, shost, FNM_CASEFOLD));
@@ -243,9 +341,24 @@ hostname_matches(shost, lhost, pattern)
 }
 
 int
-usergr_matches(group, user)
+userpw_matches(sudoers_user, user, pw)
+    char *sudoers_user;
+    char *user;
+    struct passwd *pw;
+{
+    if (pw != NULL && *sudoers_user == '#') {
+       uid_t uid = atoi(sudoers_user + 1);
+       if (uid == pw->pw_uid)
+           return(1);
+    }
+    return(strcmp(sudoers_user, user) == 0);
+}
+
+int
+usergr_matches(group, user, pw)
     char *group;
     char *user;
+    struct passwd *pw;
 {
     struct group *grp;
     char **cur;
@@ -254,7 +367,7 @@ usergr_matches(group, user)
     if (*group++ != '%')
        return(FALSE);
 
-    if ((grp = getgrnam(group)) == NULL) 
+    if ((grp = getgrnam(group)) == NULL)
        return(FALSE);
 
     /*
@@ -294,7 +407,7 @@ netgr_matches(netgr, host, shost, user)
        domain = (char *) emalloc(MAXHOSTNAMELEN);
 
        if (getdomainname(domain, MAXHOSTNAMELEN) != 0 || *domain == '\0') {
-           free(domain);
+           efree(domain);
            domain = NULL;
        }
     }
@@ -311,8 +424,8 @@ netgr_matches(netgr, host, shost, user)
 }
 
 void
-set_perms_dummy(i, j)
-    int i, j;
+set_perms(i)
+    int i;
 {
     return;
 }
@@ -323,6 +436,13 @@ set_fqdn()
     return;
 }
 
+int
+set_runaspw(user)
+    char *user;
+{
+    return(TRUE);
+}
+
 void
 init_envtables()
 {
@@ -361,7 +481,7 @@ main(argc, argv)
        NewArgc = Argc - 3;
     } else {
        (void) fprintf(stderr,
-           "usage: %s [-u user] <user> <host> <command> [args]\n", Argv[0]);
+           "usage: sudo [-u user] <user> <host> <command> [args]\n");
        exit(1);
     }
 
@@ -375,19 +495,22 @@ main(argc, argv)
        user_shost = user_host;
     }
 
-    /* Fill in cmnd_args from NewArgv. */
+    /* Fill in user_args from NewArgv. */
     if (NewArgc > 1) {
-       size_t size;
        char *to, **from;
+       size_t size, n;
 
-       size = (size_t) NewArgv[NewArgc-1] + strlen(NewArgv[NewArgc-1]) -
-              (size_t) NewArgv[1] + 1;
+       size = (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
+               strlen(NewArgv[NewArgc-1]) + 1;
        user_args = (char *) emalloc(size);
-       for (to = user_args, from = &NewArgv[1]; *from; from++) {
+       for (to = user_args, from = NewArgv + 1; *from; from++) {
+           n = strlcpy(to, *from, size - (to - user_args));
+           if (n >= size - (to - user_args))
+                   errx(1, "internal error, init_vars() overflow");
+           to += n;
            *to++ = ' ';
-           (void) strcpy(to, *from);
-           to += strlen(*from);
        }
+       *--to = '\0';
     }
 
     /* Initialize default values. */