]> granicus.if.org Git - sudo/blob - src/net_ifs.c
82610d8edfabdb79421c8c7f7b874bc7a53b7fd6
[sudo] / src / net_ifs.c
1 /*
2  * Copyright (c) 1996, 1998-2005, 2007-2015, 2018
3  *      Todd C. Miller <Todd.Miller@sudo.ws>
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  *
17  * Sponsored in part by the Defense Advanced Research Projects
18  * Agency (DARPA) and Air Force Research Laboratory, Air Force
19  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
20  */
21
22 /*
23  * This is an open source non-commercial project. Dear PVS-Studio, please check it.
24  * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
25  */
26
27 /*
28  * Suppress a warning w/ gcc on Digital UN*X.
29  * The system headers should really do this....
30  */
31 #if defined(__osf__) && !defined(__cplusplus)
32 struct mbuf;
33 struct rtentry;
34 #endif
35
36 /* Avoid a compilation problem with gcc and machine/sys/getppdp.h */
37 #define _MACHINE_SYS_GETPPDP_INCLUDED
38
39 #include <config.h>
40
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <sys/ioctl.h>
44 #if defined(HAVE_SYS_SOCKIO_H) && !defined(SIOCGIFCONF)
45 # include <sys/sockio.h>
46 #endif
47 #include <stdio.h>
48 #include <stdlib.h>
49 #ifdef HAVE_STRING_H
50 # include <string.h>
51 #endif /* HAVE_STRING_H */
52 #ifdef HAVE_STRINGS_H
53 # include <strings.h>
54 #endif /* HAVE_STRINGS_H */
55 #ifdef HAVE_STDBOOL_H
56 # include <stdbool.h>
57 #else
58 # include "compat/stdbool.h"
59 #endif /* HAVE_STDBOOL_H */
60 #include <unistd.h>
61 #include <netdb.h>
62 #include <errno.h>
63 #ifdef _ISC
64 # include <sys/stream.h>
65 # include <sys/sioctl.h>
66 # include <sys/stropts.h>
67 # define STRSET(cmd, param, len) {strioctl.ic_cmd=(cmd);\
68                                  strioctl.ic_dp=(param);\
69                                  strioctl.ic_timout=0;\
70                                  strioctl.ic_len=(len);}
71 #endif /* _ISC */
72 #ifdef _MIPS
73 # include <net/soioctl.h>
74 #endif /* _MIPS */
75 #include <netinet/in.h>
76 #include <arpa/inet.h>
77 #ifdef NEED_RESOLV_H
78 # include <arpa/nameser.h>
79 # include <resolv.h>
80 #endif /* NEED_RESOLV_H */
81 #include <net/if.h>
82 #ifdef HAVE_GETIFADDRS
83 # include <ifaddrs.h>
84 #endif
85
86 #define SUDO_NET_IFS_C          /* to expose sudo_inet_ntop in sudo_compat.h */
87
88 #define DEFAULT_TEXT_DOMAIN     "sudo"
89 #include "sudo_gettext.h"       /* must be included before sudo_compat.h */
90
91 #include "sudo_compat.h"
92 #include "sudo_fatal.h"
93 #include "sudo_conf.h"
94 #include "sudo_debug.h"
95
96 /* Minix apparently lacks IFF_LOOPBACK */
97 #ifndef IFF_LOOPBACK
98 # define IFF_LOOPBACK   0
99 #endif
100
101 #ifndef INET_ADDRSTRLEN
102 # define INET_ADDRSTRLEN 16
103 #endif
104 #ifndef INET6_ADDRSTRLEN
105 # define INET6_ADDRSTRLEN 46
106 #endif
107
108 #ifdef HAVE_GETIFADDRS
109
110 /*
111  * Fill in the interfaces string with the machine's ip addresses and netmasks
112  * and return the number of interfaces found.  Returns -1 on error.
113  */
114 int
115 get_net_ifs(char **addrinfo)
116 {
117     struct ifaddrs *ifa, *ifaddrs;
118     struct sockaddr_in *sin;
119 #ifdef HAVE_STRUCT_IN6_ADDR
120     struct sockaddr_in6 *sin6;
121     char addrstr[INET6_ADDRSTRLEN], maskstr[INET6_ADDRSTRLEN];
122 #else
123     char addrstr[INET_ADDRSTRLEN], maskstr[INET_ADDRSTRLEN];
124 #endif
125     int ailen, len, num_interfaces = 0;
126     char *cp;
127     debug_decl(get_net_ifs, SUDO_DEBUG_NETIF)
128
129     if (!sudo_conf_probe_interfaces())
130         debug_return_int(0);
131
132     if (getifaddrs(&ifaddrs) == -1)
133         debug_return_int(-1);
134
135     /* Allocate space for the interfaces info string. */
136     for (ifa = ifaddrs; ifa != NULL; ifa = ifa -> ifa_next) {
137         /* Skip interfaces marked "down" and "loopback". */
138         if (ifa->ifa_addr == NULL || ifa->ifa_netmask == NULL ||
139             !ISSET(ifa->ifa_flags, IFF_UP) || ISSET(ifa->ifa_flags, IFF_LOOPBACK))
140             continue;
141
142         switch (ifa->ifa_addr->sa_family) {
143             case AF_INET:
144 #ifdef HAVE_STRUCT_IN6_ADDR
145             case AF_INET6:
146 #endif
147                 num_interfaces++;
148                 break;
149         }
150     }
151     if (num_interfaces == 0)
152         goto done;
153     ailen = num_interfaces * 2 * INET6_ADDRSTRLEN;
154     if ((cp = malloc(ailen)) == NULL) {
155         sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
156             "unable to allocate memory");
157         num_interfaces = -1;
158         goto done;
159     }
160     *addrinfo = cp;
161
162     /* Store the IP addr/netmask pairs. */
163     for (ifa = ifaddrs; ifa != NULL; ifa = ifa -> ifa_next) {
164         /* Skip interfaces marked "down" and "loopback". */
165         if (ifa->ifa_addr == NULL || ifa->ifa_netmask == NULL ||
166             !ISSET(ifa->ifa_flags, IFF_UP) || ISSET(ifa->ifa_flags, IFF_LOOPBACK))
167                 continue;
168
169         switch (ifa->ifa_addr->sa_family) {
170             case AF_INET:
171                 sin = (struct sockaddr_in *)ifa->ifa_addr;
172                 if (inet_ntop(AF_INET, &sin->sin_addr, addrstr, sizeof(addrstr)) == NULL)
173                     continue;
174                 sin = (struct sockaddr_in *)ifa->ifa_netmask;
175                 if (inet_ntop(AF_INET, &sin->sin_addr, maskstr, sizeof(maskstr)) == NULL)
176                     continue;
177
178                 len = snprintf(cp, ailen - (*addrinfo - cp),
179                     "%s%s/%s", cp == *addrinfo ? "" : " ", addrstr, maskstr);
180                 if (len <= 0 || len >= ailen - (*addrinfo - cp)) {
181                     sudo_warnx(U_("internal error, %s overflow"), __func__);
182                     goto done;
183                 }
184                 cp += len;
185                 break;
186 #ifdef HAVE_STRUCT_IN6_ADDR
187             case AF_INET6:
188                 sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
189                 if (inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr, sizeof(addrstr)) == NULL)
190                     continue;
191                 sin6 = (struct sockaddr_in6 *)ifa->ifa_netmask;
192                 if (inet_ntop(AF_INET6, &sin6->sin6_addr, maskstr, sizeof(maskstr)) == NULL)
193                     continue;
194
195                 len = snprintf(cp, ailen - (*addrinfo - cp),
196                     "%s%s/%s", cp == *addrinfo ? "" : " ", addrstr, maskstr);
197                 if (len <= 0 || len >= ailen - (*addrinfo - cp)) {
198                     sudo_warnx(U_("internal error, %s overflow"), __func__);
199                     goto done;
200                 }
201                 cp += len;
202                 break;
203 #endif /* HAVE_STRUCT_IN6_ADDR */
204         }
205     }
206
207 done:
208 #ifdef HAVE_FREEIFADDRS
209     freeifaddrs(ifaddrs);
210 #else
211     free(ifaddrs);
212 #endif
213     debug_return_int(num_interfaces);
214 }
215
216 #elif defined(SIOCGIFCONF) && !defined(STUB_LOAD_INTERFACES)
217
218 /*
219  * Fill in the interfaces string with the machine's ip addresses and netmasks
220  * and return the number of interfaces found.  Returns -1 on error.
221  */
222 int
223 get_net_ifs(char **addrinfo)
224 {
225     char ifr_tmpbuf[sizeof(struct ifreq)];
226     struct ifreq *ifr, *ifr_tmp = (struct ifreq *)ifr_tmpbuf;
227     struct ifconf *ifconf;
228     struct sockaddr_in *sin;
229     int ailen, i, len, n, sock, num_interfaces = 0;
230     size_t buflen = sizeof(struct ifconf) + BUFSIZ;
231     char *cp, *previfname = "", *ifconf_buf = NULL;
232     char addrstr[INET_ADDRSTRLEN], maskstr[INET_ADDRSTRLEN];
233 #ifdef _ISC
234     struct strioctl strioctl;
235 #endif /* _ISC */
236     debug_decl(get_net_ifs, SUDO_DEBUG_NETIF)
237
238     if (!sudo_conf_probe_interfaces())
239         debug_return_int(0);
240
241     sock = socket(AF_INET, SOCK_DGRAM, 0);
242     if (sock < 0)
243         debug_return_int(-1);
244
245     /*
246      * Get interface configuration or return.
247      */
248     for (;;) {
249         if ((ifconf_buf = malloc(buflen)) == NULL) {
250             sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
251                 "unable to allocate memory");
252             num_interfaces = -1;
253             goto done;
254         }
255         ifconf = (struct ifconf *) ifconf_buf;
256         ifconf->ifc_len = buflen - sizeof(struct ifconf);
257         ifconf->ifc_buf = (caddr_t) (ifconf_buf + sizeof(struct ifconf));
258
259 #ifdef _ISC
260         STRSET(SIOCGIFCONF, (caddr_t) ifconf, buflen);
261         if (ioctl(sock, I_STR, (caddr_t) &strioctl) < 0)
262 #else
263         /* Note that some kernels return EINVAL if the buffer is too small */
264         if (ioctl(sock, SIOCGIFCONF, (caddr_t) ifconf) < 0 && errno != EINVAL)
265 #endif /* _ISC */
266             goto done;
267
268         /* Break out of loop if we have a big enough buffer. */
269         if (ifconf->ifc_len + sizeof(struct ifreq) < buflen)
270             break;
271         buflen += BUFSIZ;
272         free(ifconf_buf);
273     }
274
275     /* Allocate space for the maximum number of interfaces that could exist. */
276     if ((n = ifconf->ifc_len / sizeof(struct ifreq)) == 0)
277         goto done;
278     ailen = n * 2 * INET6_ADDRSTRLEN;
279     if ((cp = malloc(ailen)) == NULL) {
280         sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
281             "unable to allocate memory");
282         num_interfaces = -1;
283         goto done;
284     }
285     *addrinfo = cp;
286
287     /* For each interface, store the ip address and netmask. */
288     for (i = 0; i < ifconf->ifc_len; ) {
289         /* Get a pointer to the current interface. */
290         ifr = (struct ifreq *) &ifconf->ifc_buf[i];
291
292         /* Set i to the subscript of the next interface. */
293         i += sizeof(struct ifreq);
294 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
295         if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_addr))
296             i += ifr->ifr_addr.sa_len - sizeof(struct sockaddr);
297 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
298
299         /* Skip duplicates and interfaces with NULL addresses. */
300         sin = (struct sockaddr_in *) &ifr->ifr_addr;
301         if (sin->sin_addr.s_addr == 0 ||
302             strncmp(previfname, ifr->ifr_name, sizeof(ifr->ifr_name) - 1) == 0)
303             continue;
304
305         if (ifr->ifr_addr.sa_family != AF_INET)
306                 continue;
307
308 #ifdef SIOCGIFFLAGS
309         memset(ifr_tmp, 0, sizeof(*ifr_tmp));
310         strncpy(ifr_tmp->ifr_name, ifr->ifr_name, sizeof(ifr_tmp->ifr_name) - 1);
311         if (ioctl(sock, SIOCGIFFLAGS, (caddr_t) ifr_tmp) < 0)
312 #endif
313             memcpy(ifr_tmp, ifr, sizeof(*ifr_tmp));
314         
315         /* Skip interfaces marked "down" and "loopback". */
316         if (!ISSET(ifr_tmp->ifr_flags, IFF_UP) ||
317             ISSET(ifr_tmp->ifr_flags, IFF_LOOPBACK))
318                 continue;
319
320         /* Get the netmask. */
321         memset(ifr_tmp, 0, sizeof(*ifr_tmp));
322         strncpy(ifr_tmp->ifr_name, ifr->ifr_name, sizeof(ifr_tmp->ifr_name) - 1);
323         sin = (struct sockaddr_in *) &ifr_tmp->ifr_addr;
324 #ifdef _ISC
325         STRSET(SIOCGIFNETMASK, (caddr_t) ifr_tmp, sizeof(*ifr_tmp));
326         if (ioctl(sock, I_STR, (caddr_t) &strioctl) < 0)
327 #else
328         if (ioctl(sock, SIOCGIFNETMASK, (caddr_t) ifr_tmp) < 0)
329 #endif /* _ISC */
330             sin->sin_addr.s_addr = htonl(IN_CLASSC_NET);
331
332         /* Convert the addr and mask to string form. */
333         sin = (struct sockaddr_in *) &ifr->ifr_addr;
334         if (inet_ntop(AF_INET, &sin->sin_addr, addrstr, sizeof(addrstr)) == NULL)
335             continue;
336         sin = (struct sockaddr_in *) &ifr_tmp->ifr_addr;
337         if (inet_ntop(AF_INET, &sin->sin_addr, maskstr, sizeof(maskstr)) == NULL)
338             continue;
339
340         len = snprintf(cp, ailen - (*addrinfo - cp),
341             "%s%s/%s", cp == *addrinfo ? "" : " ", addrstr, maskstr);
342         if (len <= 0 || len >= ailen - (*addrinfo - cp)) {
343             sudo_warnx(U_("internal error, %s overflow"), __func__);
344             goto done;
345         }
346         cp += len;
347
348         /* Stash the name of the interface we saved. */
349         previfname = ifr->ifr_name;
350         num_interfaces++;
351     }
352
353 done:
354     free(ifconf_buf);
355     (void) close(sock);
356
357     debug_return_int(num_interfaces);
358 }
359
360 #else /* !SIOCGIFCONF || STUB_LOAD_INTERFACES */
361
362 /*
363  * Stub function for those without SIOCGIFCONF or getifaddrs()
364  */
365 int
366 get_net_ifs(char **addrinfo)
367 {
368     debug_decl(get_net_ifs, SUDO_DEBUG_NETIF)
369     debug_return_int(0);
370 }
371
372 #endif /* SIOCGIFCONF && !STUB_LOAD_INTERFACES */