/* linux.c - ifchd Linux-specific functions * Time-stamp: <2010-11-12 05:14:52 njk> * * (C) 2004 Nicholas J. Kain * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #define __USE_GNU 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "defines.h" #include "log.h" #include "strlist.h" #include "ifproto.h" #include "strl.h" /* Symbolic name of the interface associated with a connection. */ static char ifnam[SOCK_QUEUE][IFNAMSIZ]; static strlist_t *okif; /* Clear a specified ifnam structure. */ void clear_if_data(int idx) { memset(ifnam[idx], '\0', IFNAMSIZ); } /* Clear all ifnam structures. */ void initialize_if_data(void) { int i; for (i = 0; i < SOCK_QUEUE; i++) { clear_if_data(i); } } /* Adds to the list of interface names ifchd clients are allowed to change. */ void add_permitted_if(char *s) { if (!s) return; add_to_strlist(&okif, s); } /* Checks if changes are permitted to a given interface. 1 == allowed */ static int is_permitted(char *name) { strlist_t *p; /* If empty, permit all. */ if (!okif) return 1; if (!name || strlen(name) == 0) return 0; p = okif; while (p) { if (strcmp(name, p->str) == 0) return 1; p = p->next; } log_line("attempt to modify interface %s denied\n", name); return 0; } /* Verify that peer is authorized to connect (return 1 on success). */ int authorized_peer(int sk, pid_t pid, uid_t uid, gid_t gid) { int ret = 0; unsigned int cl; struct ucred cr; /* No credentials to verify. */ if ( !(pid || uid || gid) ) return 1; /* Verify that peer has authorized uid/gid/pid. */ cl = sizeof(struct ucred); if (getsockopt(sk, SOL_SOCKET, SO_PEERCRED, &cr, &cl) != -1) { if ((pid == 0 || cr.pid == pid) || (uid == 0 || cr.uid == uid) || (gid == 0 || cr.gid == gid)) ret = 1; } else log_line("getsockopt returned an error: %s\n", strerror(errno)); return ret; } void perform_interface(int idx, char *str) { if (!str) return; /* Update interface name. */ memset(ifnam[idx], '\0', IFNAMSIZ); strlcpy(ifnam[idx], str, IFNAMSIZ); } static int set_if_flag(int idx, short flag) { int fd, ret = -1; struct ifreq ifrt; if (!is_permitted(ifnam[idx])) goto out0; fd = socket(PF_INET, SOCK_DGRAM, 0); if (fd == -1) { log_line("%s: (set_if_flag) failed to open interface socket: %s\n", ifnam[idx], strerror(errno)); goto out0; } strlcpy(ifrt.ifr_name, ifnam[idx], IFNAMSIZ); if (ioctl(fd, SIOCGIFFLAGS, &ifrt) < 0) { log_line("%s: unknown interface: %s\n", ifnam[idx], strerror(errno)); goto out1; } strlcpy(ifrt.ifr_name, ifnam[idx], IFNAMSIZ); ifrt.ifr_flags |= flag; if (ioctl(fd, SIOCSIFFLAGS, &ifrt) < 0) { log_line("%s: failed to set interface flags: %s\n", ifnam[idx], strerror(errno)); goto out1; } ret = 0; out1: close(fd); out0: return ret; } /* Sets IP address on an interface and brings it up. */ void perform_ip(int idx, char *str) { int fd; struct in_addr ipaddr; struct ifreq ifrt; struct sockaddr_in sin; if (!str) return; if (!is_permitted(ifnam[idx])) return; if (!inet_aton(str, &ipaddr)) return; if (set_if_flag(idx, (IFF_UP | IFF_RUNNING))) return; strlcpy(ifrt.ifr_name, ifnam[idx], IFNAMSIZ); memset(&sin, 0, sizeof(struct sockaddr)); sin.sin_family = AF_INET; sin.sin_addr = ipaddr; memcpy(&ifrt.ifr_addr, &sin, sizeof(struct sockaddr)); fd = socket(PF_INET, SOCK_DGRAM, 0); if (fd == -1) { log_line("%s: (perform_ip) failed to open interface socket: %s\n", ifnam[idx], strerror(errno)); return; } if (ioctl(fd, SIOCSIFADDR, &ifrt) < 0) log_line("%s: failed to configure IP: %s\n", ifnam[idx], strerror(errno)); close(fd); } /* Sets the subnet mask on an interface. */ void perform_subnet(int idx, char *str) { int fd; struct in_addr subnet; struct ifreq ifrt; struct sockaddr_in sin; if (!str) return; if (!is_permitted(ifnam[idx])) return; if (!inet_aton(str, &subnet)) return; strlcpy(ifrt.ifr_name, ifnam[idx], IFNAMSIZ); memset(&sin, 0, sizeof(struct sockaddr)); sin.sin_family = AF_INET; sin.sin_addr = subnet; memcpy(&ifrt.ifr_addr, &sin, sizeof(struct sockaddr)); fd = socket(PF_INET, SOCK_DGRAM, 0); if (fd == -1) { log_line("%s: (perform_ip) failed to open interface socket: %s\n", ifnam[idx], strerror(errno)); return; } if (ioctl(fd, SIOCSIFNETMASK, &ifrt) < 0) { sin.sin_addr.s_addr = 0xffffffff; if (ioctl(fd, SIOCSIFNETMASK, &ifrt) < 0) log_line("%s: failed to configure subnet: %s\n", ifnam[idx], strerror(errno)); } close(fd); } void perform_router(int idx, char *str) { struct rtentry rt; struct sockaddr_in *dest; struct sockaddr_in *gateway; struct sockaddr_in *mask; struct in_addr router; int fd; if (!str) return; if (!is_permitted(ifnam[idx])) return; if (!inet_aton(str, &router)) return; memset(&rt, 0, sizeof(struct rtentry)); dest = (struct sockaddr_in *) &rt.rt_dst; dest->sin_family = AF_INET; dest->sin_addr.s_addr = 0x00000000; gateway = (struct sockaddr_in *) &rt.rt_gateway; gateway->sin_family = AF_INET; gateway->sin_addr = router; mask = (struct sockaddr_in *) &rt.rt_genmask; mask->sin_family = AF_INET; mask->sin_addr.s_addr = 0x00000000; rt.rt_flags = RTF_UP | RTF_GATEWAY; if (mask->sin_addr.s_addr == 0xffffffff) rt.rt_flags |= RTF_HOST; rt.rt_dev = ifnam[idx]; rt.rt_metric = 1; fd = socket(PF_INET, SOCK_DGRAM, 0); if (fd == -1) { log_line("%s: (perform_router) failed to open interface socket: %s\n", ifnam[idx], strerror(errno)); return; } if (ioctl(fd, SIOCADDRT, &rt)) log_line("%s: failed to set route: %s\n", ifnam[idx], strerror(errno)); close(fd); } void perform_mtu(int idx, char *str) { int fd; unsigned int mtu; struct ifreq ifrt; if (!str) return; if (!is_permitted(ifnam[idx])) return; mtu = strtol(str, NULL, 10); ifrt.ifr_mtu = mtu; strlcpy(ifrt.ifr_name, ifnam[idx], IFNAMSIZ); fd = socket(PF_INET, SOCK_DGRAM, 0); if (fd == -1) { log_line("%s: (perform_mtu) failed to open interface socket: %s\n", ifnam[idx], strerror(errno)); return; } if (ioctl(fd, SIOCSIFMTU, &ifrt) < 0) log_line("%s: failed to set MTU (%d): %s\n", ifnam[idx], mtu, strerror(errno)); close(fd); } void perform_broadcast(int idx, char *str) { int fd; struct in_addr broadcast; struct ifreq ifrt; struct sockaddr_in sin; if (!str) return; if (!is_permitted(ifnam[idx])) return; if (!inet_aton(str, &broadcast)) return; strlcpy(ifrt.ifr_name, ifnam[idx], IFNAMSIZ); memset(&sin, 0, sizeof(struct sockaddr)); sin.sin_family = AF_INET; sin.sin_addr = broadcast; memcpy(&ifrt.ifr_addr, &sin, sizeof(struct sockaddr)); fd = socket(PF_INET, SOCK_DGRAM, 0); if (fd == -1) { log_line("%s: (perform_broadcast) failed to open interface socket: %s\n", ifnam[idx], strerror(errno)); return; } if (ioctl(fd, SIOCSIFBRDADDR, &ifrt) < 0) log_line("%s: failed to set broadcast: %s\n", ifnam[idx], strerror(errno)); close(fd); } void set_cap(uid_t uid, gid_t gid, char *captxt) { cap_t caps; if (!captxt) { log_line("FATAL - set_cap: captxt == NULL\n"); exit(EXIT_FAILURE); } if (prctl(PR_SET_KEEPCAPS, 1)) { log_line("FATAL - set_cap: prctl() failed\n"); exit(EXIT_FAILURE); } if (setgroups(0, NULL) == -1) { log_line("FATAL - set_cap: setgroups() failed\n"); exit(EXIT_FAILURE); } if (setegid(gid) == -1 || seteuid(uid) == -1) { log_line("FATAL - set_cap: seteuid() failed\n"); exit(EXIT_FAILURE); } caps = cap_from_text(captxt); if (!caps) { log_line("FATAL - set_cap: cap_from_text() failed\n"); exit(EXIT_FAILURE); } if (cap_set_proc(caps) == -1) { log_line("FATAL - set_cap: cap_set_proc() failed\n"); exit(EXIT_FAILURE); } cap_free(caps); }