Imported Upstream version 1.8.7
[debian/sudo] / plugins / sudoers / match_addr.c
1 /*
2  * Copyright (c) 1996, 1998-2005, 2007-2013
3  *      Todd C. Miller <Todd.Miller@courtesan.com>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
17  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18  *
19  * Sponsored in part by the Defense Advanced Research Projects
20  * Agency (DARPA) and Air Force Research Laboratory, Air Force
21  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22  */
23
24 #include <config.h>
25
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <stdio.h>
29 #ifdef STDC_HEADERS
30 # include <stdlib.h>
31 # include <stddef.h>
32 #else
33 # ifdef HAVE_STDLIB_H
34 #  include <stdlib.h>
35 # endif
36 #endif /* STDC_HEADERS */
37 #ifdef HAVE_STRING_H
38 # include <string.h>
39 #endif /* HAVE_STRING_H */
40 #ifdef HAVE_STRINGS_H
41 # include <strings.h>
42 #endif /* HAVE_STRINGS_H */
43 #ifdef HAVE_UNISTD_H
44 # include <unistd.h>
45 #endif /* HAVE_UNISTD_H */
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48
49 #include "sudoers.h"
50 #include "interfaces.h"
51
52 static bool
53 addr_matches_if(char *n)
54 {
55     union sudo_in_addr_un addr;
56     struct interface *ifp;
57 #ifdef HAVE_STRUCT_IN6_ADDR
58     int j;
59 #endif
60     int family;
61     debug_decl(addr_matches_if, SUDO_DEBUG_MATCH)
62
63 #ifdef HAVE_STRUCT_IN6_ADDR
64     if (inet_pton(AF_INET6, n, &addr.ip6) > 0) {
65         family = AF_INET6;
66     } else
67 #endif /* HAVE_STRUCT_IN6_ADDR */
68     {
69         family = AF_INET;
70         addr.ip4.s_addr = inet_addr(n);
71     }
72
73     for (ifp = get_interfaces(); ifp != NULL; ifp = ifp->next) {
74         if (ifp->family != family)
75             continue;
76         switch (family) {
77             case AF_INET:
78                 if (ifp->addr.ip4.s_addr == addr.ip4.s_addr ||
79                     (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr)
80                     == addr.ip4.s_addr)
81                     debug_return_bool(true);
82                 break;
83 #ifdef HAVE_STRUCT_IN6_ADDR
84             case AF_INET6:
85                 if (memcmp(ifp->addr.ip6.s6_addr, addr.ip6.s6_addr,
86                     sizeof(addr.ip6.s6_addr)) == 0)
87                     debug_return_bool(true);
88                 for (j = 0; j < sizeof(addr.ip6.s6_addr); j++) {
89                     if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr.ip6.s6_addr[j])
90                         break;
91                 }
92                 if (j == sizeof(addr.ip6.s6_addr))
93                     debug_return_bool(true);
94                 break;
95 #endif /* HAVE_STRUCT_IN6_ADDR */
96         }
97     }
98
99     debug_return_bool(false);
100 }
101
102 static bool
103 addr_matches_if_netmask(char *n, char *m)
104 {
105     int i;
106     union sudo_in_addr_un addr, mask;
107     struct interface *ifp;
108 #ifdef HAVE_STRUCT_IN6_ADDR
109     int j;
110 #endif
111     int family;
112     debug_decl(addr_matches_if, SUDO_DEBUG_MATCH)
113
114 #ifdef HAVE_STRUCT_IN6_ADDR
115     if (inet_pton(AF_INET6, n, &addr.ip6) > 0)
116         family = AF_INET6;
117     else
118 #endif /* HAVE_STRUCT_IN6_ADDR */
119     {
120         family = AF_INET;
121         addr.ip4.s_addr = inet_addr(n);
122     }
123
124     if (family == AF_INET) {
125         if (strchr(m, '.')) {
126             mask.ip4.s_addr = inet_addr(m);
127         } else {
128             i = atoi(m);
129             if (i == 0)
130                 mask.ip4.s_addr = 0;
131             else if (i == 32)
132                 mask.ip4.s_addr = 0xffffffff;
133             else
134                 mask.ip4.s_addr = 0xffffffff - (1 << (32 - i)) + 1;
135             mask.ip4.s_addr = htonl(mask.ip4.s_addr);
136         }
137         addr.ip4.s_addr &= mask.ip4.s_addr;
138     }
139 #ifdef HAVE_STRUCT_IN6_ADDR
140     else {
141         if (inet_pton(AF_INET6, m, &mask.ip6) <= 0) {
142             j = atoi(m);
143             for (i = 0; i < sizeof(addr.ip6.s6_addr); i++) {
144                 if (j < i * 8)
145                     mask.ip6.s6_addr[i] = 0;
146                 else if (i * 8 + 8 <= j)
147                     mask.ip6.s6_addr[i] = 0xff;
148                 else
149                     mask.ip6.s6_addr[i] = 0xff00 >> (j - i * 8);
150                 addr.ip6.s6_addr[i] &= mask.ip6.s6_addr[i];
151             }
152         }
153     }
154 #endif /* HAVE_STRUCT_IN6_ADDR */
155
156     for (ifp = get_interfaces(); ifp != NULL; ifp = ifp->next) {
157         if (ifp->family != family)
158             continue;
159         switch (family) {
160             case AF_INET:
161                 if ((ifp->addr.ip4.s_addr & mask.ip4.s_addr) == addr.ip4.s_addr)
162                     debug_return_bool(true);
163                 break;
164 #ifdef HAVE_STRUCT_IN6_ADDR
165             case AF_INET6:
166                 for (j = 0; j < sizeof(addr.ip6.s6_addr); j++) {
167                     if ((ifp->addr.ip6.s6_addr[j] & mask.ip6.s6_addr[j]) != addr.ip6.s6_addr[j])
168                         break;
169                 }
170                 if (j == sizeof(addr.ip6.s6_addr))
171                     debug_return_bool(true);
172                 break;
173 #endif /* HAVE_STRUCT_IN6_ADDR */
174         }
175     }
176
177     debug_return_bool(false);
178 }
179
180 /*
181  * Returns true if "n" is one of our ip addresses or if
182  * "n" is a network that we are on, else returns false.
183  */
184 bool
185 addr_matches(char *n)
186 {
187     char *m;
188     bool retval;
189     debug_decl(addr_matches, SUDO_DEBUG_MATCH)
190
191     /* If there's an explicit netmask, use it. */
192     if ((m = strchr(n, '/'))) {
193         *m++ = '\0';
194         retval = addr_matches_if_netmask(n, m);
195         *(m - 1) = '/';
196     } else
197         retval = addr_matches_if(n);
198
199     debug_return_bool(retval);
200 }