Configuration daemon for simple-netaid.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1248 lines
34 KiB

/*
* main.c - Monitor-netlink
*
* This file makes use of the following monitor-netlink example:
*
* https://github.com/lihongguang/monitor-netlink
*
* All modifications to the original source file are:
* Copyright (C) 2021 Aitor Cuadrado Zubizarreta <aitor_czr@gnuinos.org>
*
* simple-netaid 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 3 of the License, or
* (at your option) any later version.
*
* simple-netaid 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, see <http://www.gnu.org/licenses/>.
*
* See the COPYING file.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h> // dirname()
#include <time.h>
#include <sys/types.h>
#include <signal.h>
#include <memory.h>
#include <net/if.h>
#include <asm/types.h>
#include <arpa/inet.h>
#include <sys/un.h> /* UNIX socket */
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <netinet/in.h>
#include <assert.h>
#include <inttypes.h>
#include <simple-netaid/sbuf.h>
#include <simple-netaid/iproute.h>
#include <simple-netaid/interfaces.h>
#include <simple-netaid/helpers.h>
#include <simple-netaid/ipaddr.h>
#include <simple-netaid/wireless.h>
#include <simple-netaid/entry.h>
#include "ini.h"
#include "def.h"
#include "config.h"
#include "util.h"
#include "sigact.h"
#include "start_periodic.h"
#define MAX_SIZE 1024
//#define BUFFER_SIZE 128 /* already defined in simple-netaid/entry.h */
//#define PROTOCOL 0 /* already defined in simple-netaid/entry.h */
#define ACTIVE_WIFIS_SOCKET "/tmp/snetaid_active_wifis.socket"
#define WHITELIST "/etc/simple-netaid/whitelist"
int DEBUG = 1;
int _DEBUG_MESSAGES = 0;
int _INFO_MESSAGES = 0;
int _WARN_MESSAGES = 1;
int _ERROR_MESSAGES = 1;
config_t *m_config = NULL;
const char *progname, *libpath;
char arg0[512] = {0};
int pid_fd = -1; // lock file
pid_t m_pid = 0;
void get_fullpath(struct sbuf*, const char*);
void read_arguments();
bool request_whitelist(const char*);
/* Message structure. */
struct message
{
int key;
const char *str;
};
/* Socket interface to kernel */
struct nlsock
{
int sock;
int seq;
struct sockaddr_nl snl;
const char *name;
} netlink = { -1, 0, {0}, "netlink-listen"}; /* kernel messages */
#include "prefix.h"
// set debug level, by setting for info and debug messages
static void set_debug_level(int d)
{
if(d >= 1) {
_INFO_MESSAGES = 1;
}
if(d >= 2) {
_DEBUG_MESSAGES = 1;
}
}
// set error level, by setting for warning and error messages
static void set_error_level(int e)
{
if(e >= 1) {
_ERROR_MESSAGES = 1;
}
if(e >= 2) {
_WARN_MESSAGES = 1;
}
}
static const char* read_buffer(int df, char *buf)
{
int rc = read(df, buf, BUFFER_SIZE);
if (rc == -1) {
perror("read");
}
return strdup(buf);
}
static inline char *if_index2name(int index, char *name)
{
char *if_name = if_indextoname(index, name);
if (name == NULL && errno == ENXIO)
return "";
else
return if_name;
}
static inline unsigned int if_name2index(char *name)
{
return if_nametoindex(name);
}
/* Message lookup function. */
static const char *lookup(const struct message *mes, int key)
{
const struct message *pnt;
for (pnt = mes; pnt->str; pnt++)
if (pnt->key == key)
return pnt->str;
return "unknown";
}
/* Wrapper around strerror to handle case where it returns NULL. */
const char *safe_strerror(int errnum)
{
const char *s = strerror(errnum);
return (s != NULL) ? s : "Unknown error";
}
/* Make socket for Linux netlink interface. */
static int netlink_socket (struct nlsock *nl, unsigned long groups)
{
int ret;
struct sockaddr_nl snl;
int sock;
int namelen;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock < 0) {
printf("Can't open %s socket: %s\n", nl->name, safe_strerror(errno));
return -1;
}
memset (&snl, 0, sizeof(snl));
snl.nl_family = AF_NETLINK;
snl.nl_groups = groups;
/* Bind the socket to the netlink structure for anything. */
ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl));
if (ret < 0) {
printf("Can't bind %s socket to group 0x%x: %s\n",
nl->name, snl.nl_groups, safe_strerror(errno));
close (sock);
return -1;
}
/* multiple netlink sockets will have different nl_pid */
namelen = sizeof(snl);
ret = getsockname(sock, (struct sockaddr *)&snl, (socklen_t *)&namelen);
if (ret < 0 || namelen != sizeof(snl)) {
printf("Can't get %s socket name: %s\n", nl->name, safe_strerror(errno));
close (sock);
return -1;
}
nl->snl = snl;
nl->sock = sock;
return ret;
}
#ifndef HAVE_NETLINK
#define HAVE_NETLINK
/* Receive buffer size for netlink socket */
u_int32_t nl_rcvbufsize = 4194304;
#endif /* HAVE_NETLINK */
#ifndef SO_RCVBUFFORCE
#define SO_RCVBUFFORCE (33)
#endif
static int netlink_recvbuf(struct nlsock *nl, uint32_t newsize)
{
u_int32_t oldsize;
socklen_t newlen = sizeof(newsize);
socklen_t oldlen = sizeof(oldsize);
int ret;
ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldlen);
if (ret < 0) {
printf("Can't get %s receive buffer size: %s\n", nl->name,
safe_strerror (errno));
return -1;
}
/* Try force option (linux >= 2.6.14) and fall back to normal set */
ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUFFORCE, &nl_rcvbufsize,
sizeof(nl_rcvbufsize));
if (ret < 0) {
ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &nl_rcvbufsize,
sizeof(nl_rcvbufsize));
if (ret < 0) {
printf("Can't set %s receive buffer size: %s\n", nl->name,
safe_strerror (errno));
return -1;
}
}
ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &newsize, &newlen);
if (ret < 0) {
printf("Can't get %s receive buffer size: %s\n", nl->name,
safe_strerror (errno));
return -1;
}
printf("Setting netlink socket receive buffer size: %u -> %u\n",
oldsize, newsize);
return 0;
}
void kernel_init (void)
{
unsigned long groups;
groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR |
RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR;
netlink_socket (&netlink, groups);
/* Register kernel socket. */
if (netlink.sock > 0) {
#if 0
/* Only want non-blocking on the netlink event socket */
if (fcntl (netlink.sock, F_SETFL, O_NONBLOCK) < 0)
printf("Can't set %s socket flags: %s\n",
netlink.name, safe_strerror(errno));
#endif
/* Set receive buffer size if it's set from command line */
if (nl_rcvbufsize)
netlink_recvbuf (&netlink, nl_rcvbufsize);
}
}
static int is_valid_kernel_table(u_int32_t table_id)
{
if ((table_id == RT_TABLE_MAIN) ||
(table_id == RT_TABLE_LOCAL) ||
(table_id == RT_TABLE_COMPAT) ||
(table_id > RT_TABLE_UNSPEC))
return 1;
else
return 0;
}
/* Utility function for parse rtattr. */
static void netlink_parse_rtattr (struct rtattr **tb, int max,
struct rtattr *rta, int len)
{
while (RTA_OK (rta, len)) {
if (rta->rta_type <= max)
tb[rta->rta_type] = rta;
rta = RTA_NEXT (rta, len);
}
}
/* Routing information change from the kernel. */
static int netlink_route_change(struct sockaddr_nl *snl, struct nlmsghdr *h)
{
int len;
struct rtmsg *rtm;
struct rtattr *tb[RTA_MAX + 1];
char anyaddr[16] = {0};
//char straddr[INET6_ADDRSTRLEN];
char if_name[IFNAMSIZ];
int index;
int table;
int metric __attribute((unused));
void *dest;
void *gate;
void *src __attribute((unused));
rtm = NLMSG_DATA(h);
if (h->nlmsg_type != RTM_NEWROUTE && h->nlmsg_type != RTM_DELROUTE) {
/* If this is not route add/delete message print warning. */
printf("Kernel message: %d\n", h->nlmsg_type);
return 0;
}
if (rtm->rtm_flags & RTM_F_CLONED) {
//printf("This route is cloned from another route!\n");
return 0;
}
printf("nlmsg: %s, family: %s, rtable: %d-%s, rtype: %d-%s, rtproto: %d-%s\n",
lookup(nlmsg_str, h->nlmsg_type),
rtm->rtm_family == AF_INET ? "ipv4" : "ipv6",
rtm->rtm_table, lookup(rtable_str, rtm->rtm_table),
rtm->rtm_type, lookup(rtype_str, rtm->rtm_type),
rtm->rtm_protocol, lookup(rtproto_str, rtm->rtm_protocol));
table = rtm->rtm_table;
if (!is_valid_kernel_table(table)) {
printf("invalid kernel table: %d\n", table);
return 0;
}
len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg));
if (len < 0) {
printf("netlink msg length error!\n");
return -1;
}
memset(tb, 0, sizeof tb);
netlink_parse_rtattr(tb, RTA_MAX, RTM_RTA(rtm), len);
if (rtm->rtm_src_len != 0) {
printf("netlink_route_change(): no src len\n");
return 0;
}
index = 0;
metric = 0;
dest = NULL;
gate = NULL;
src = NULL;
if (tb[RTA_OIF])
index = *(int *)RTA_DATA(tb[RTA_OIF]);
if (tb[RTA_DST])
dest = RTA_DATA(tb[RTA_DST]);
else
dest = anyaddr;
if (tb[RTA_GATEWAY])
gate = RTA_DATA(tb[RTA_GATEWAY]);
if (tb[RTA_PREFSRC])
src = RTA_DATA(tb[RTA_PREFSRC]);
if (h->nlmsg_type == RTM_NEWROUTE && tb[RTA_PRIORITY])
metric = *(int *)RTA_DATA(tb[RTA_PRIORITY]);
if (rtm->rtm_family == AF_INET) {
struct prefix_ipv4 p;
p.family = AF_INET;
memcpy (&p.prefix, dest, 4);
p.prefixlen = rtm->rtm_dst_len;
//inet_ntop(p->family, &p->u.prefix, straddr, INET6_ADDRSTRLEN);
if (h->nlmsg_type == RTM_NEWROUTE)
printf("\tadd route %s/%d\n",
inet_ntoa(p.prefix), p.prefixlen);
else
printf("\tdel route %s/%d\n",
inet_ntoa(p.prefix), p.prefixlen);
if (!tb[RTA_MULTIPATH]) {
if (gate) {
if (index)
printf("\t\tnexthop via %s dev %s\n",
inet_ntoa(*(struct in_addr *)gate),
if_index2name(index, if_name));
else
printf("\t\tnexthop via %s\n",
inet_ntoa(*(struct in_addr *)gate));
} else {
if (index)
printf("\t\tdev %s\n",
if_index2name(index, if_name));
}
} else {
/* This is a multipath route */
struct rtnexthop *rtnh =
(struct rtnexthop *)RTA_DATA(tb[RTA_MULTIPATH]);
len = RTA_PAYLOAD (tb[RTA_MULTIPATH]);
for (;;) {
if (len < (int)sizeof(*rtnh) || rtnh->rtnh_len > len)
break;
index = rtnh->rtnh_ifindex;
gate = 0;
if (rtnh->rtnh_len > sizeof (*rtnh)) {
memset (tb, 0, sizeof(tb));
netlink_parse_rtattr (tb, RTA_MAX, RTNH_DATA(rtnh),
rtnh->rtnh_len - sizeof(*rtnh));
if (tb[RTA_GATEWAY])
gate = RTA_DATA(tb[RTA_GATEWAY]);
}
if (gate) {
if (index)
printf("\t\tnexthop via %s dev %s\n",
inet_ntoa(*(struct in_addr *)gate),
if_index2name(index, if_name));
else
printf("\t\tnexthop via %s\n",
inet_ntoa(*(struct in_addr *)gate));
} else {
if (index)
printf("\t\tdev %s\n",
if_index2name(index, if_name));
}
len -= NLMSG_ALIGN(rtnh->rtnh_len);
rtnh = RTNH_NEXT(rtnh);
}
}
}
#ifdef HAVE_IPV6
if (rtm->rtm_family == AF_INET6) {
struct prefix_ipv6 p;
char buf[BUFSIZ];
p.family = AF_INET6;
memcpy (&p.prefix, dest, 16);
p.prefixlen = rtm->rtm_dst_len;
if (h->nlmsg_type == RTM_NEWROUTE)
printf("\tadd route %s/%d\n",
inet_ntop(AF_INET6, &p.prefix, buf, BUFSIZ),
p.prefixlen);
else
printf("\tdel route %s/%d\n",
inet_ntop(AF_INET6, &p.prefix, buf, BUFSIZ),
p.prefixlen);
/* FIXME: add multipath process. */
if (h->nlmsg_type == RTM_NEWROUTE) {
if (gate) {
if (index)
printf("\t\tnexthop via %s dev %s\n",
inet_ntop(AF_INET6, gate, buf, INET6_ADDRSTRLEN),
if_index2name(index, if_name));
else
printf("\t\tnexthop via %s\n",
inet_ntop(AF_INET6, gate, buf, INET6_ADDRSTRLEN));
} else {
printf("\t\tdev %s\n", if_index2name(index, if_name));
}
} else {
if (gate)
printf("\t\tnexthop via %s",
inet_ntop(AF_INET6, gate, buf, INET6_ADDRSTRLEN));
if (index)
printf(" dev %s", if_index2name(index, if_name));
if (gate || index)
printf("\n");
}
}
#endif /* HAVE_IPV6 */
return 0;
}
/* Utility function to parse hardware link-layer address */
static void netlink_interface_get_hw_addr(struct rtattr **tb,
u_char hw_addr[], int *hw_addr_len)
{
int i;
if (tb[IFLA_ADDRESS]) {
int __hw_addr_len;
__hw_addr_len = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
if (__hw_addr_len > IF_HWADDR_MAX) {
printf("Hardware address is too large: %d\n", __hw_addr_len);
} else {
*hw_addr_len = __hw_addr_len;
memcpy(hw_addr, RTA_DATA(tb[IFLA_ADDRESS]), __hw_addr_len);
for (i = 0; i < __hw_addr_len; i++)
if (hw_addr[i] != 0)
break;
if (i == __hw_addr_len)
*hw_addr_len = 0;
else
*hw_addr_len = __hw_addr_len;
}
}
}
static int netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
{
int len;
unsigned int index;
struct ifinfomsg *ifi;
struct rtattr *tb[IFLA_MAX + 1];
char *name;
ifi = NLMSG_DATA (h);
if (!(h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)) {
/* If this is not link add/delete message so print warning. */
printf("netlink_link_change: wrong kernel message %d\n",
h->nlmsg_type);
return 0;
}
len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));
if (len < 0)
return -1;
/* Looking up interface name. */
memset (tb, 0, sizeof tb);
netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA(ifi), len);
#ifdef IFLA_WIRELESS
/* check for wireless messages to ignore */
if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) {
printf("%s: ignoring IFLA_WIRELESS message\n", __func__);
return 0;
}
#endif /* IFLA_WIRELESS */
if (tb[IFLA_IFNAME] == NULL)
return -1;
name = (char *)RTA_DATA(tb[IFLA_IFNAME]);
/* Add interface. */
if (h->nlmsg_type == RTM_NEWLINK) {
u_char hw_addr[IF_HWADDR_MAX];
int hw_addr_len = 0;
index = if_name2index(name);
if (0 == index) {
printf("add link dev %s index %d", name, ifi->ifi_index);
} else {
/* Interface status change. */
printf("update link dev %s index %d", name, ifi->ifi_index);
}
netlink_interface_get_hw_addr(tb, hw_addr, &hw_addr_len);
int i;
for (i = 0; i < hw_addr_len; i++)
printf("%s%02x", i == 0 ? "" : ":", hw_addr[i]);
printf(" mtu %d flags %d\n",
*(int *)RTA_DATA(tb[IFLA_MTU]), ifi->ifi_flags & 0x0000fffff);
}else {
/* RTM_DELLINK. */
index = if_name2index(name);
if (0 == index)
printf("interface %s is deleted but can't find\n", name);
else
printf("delete link dev %s index %d\n",
name, ifi->ifi_index);
}
return 0;
}
/* Lookup interface IPv4/IPv6 address. */
static int netlink_interface_addr(struct sockaddr_nl *snl, struct nlmsghdr *h)
{
int len;
struct ifaddrmsg *ifa;
struct rtattr *tb[IFA_MAX + 1];
char name[IFNAMSIZ];
char *if_name;
char buf[BUFSIZ];
ifa = NLMSG_DATA (h);
if (ifa->ifa_family != AF_INET
#ifdef HAVE_IPV6
&& ifa->ifa_family != AF_INET6
#endif /* HAVE_IPV6 */
)
return 0;
if (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR)
return 0;
len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));
if (len < 0)
return -1;
memset (tb, 0, sizeof tb);
netlink_parse_rtattr (tb, IFA_MAX, IFA_RTA(ifa), len);
if_name = if_index2name(ifa->ifa_index, name);
if (!if_name || if_name[0] == '\0') {
printf("netlink_interface_addr can't find interface by index %d\n",
ifa->ifa_index);
return -1;
}
printf("nlmsg: %s, family: %s, %s on dev %s\n",
lookup(nlmsg_str, h->nlmsg_type), ifa->ifa_family == AF_INET ? "ipv4" : "ipv6",
(h->nlmsg_type == RTM_NEWADDR) ? "add addr" : "del addr", if_name);
if (tb[IFA_LOCAL])
printf("\t IFA_LOCAL %s/%d\n",
inet_ntop (ifa->ifa_family, RTA_DATA(tb[IFA_LOCAL]),
buf, BUFSIZ), ifa->ifa_prefixlen);
if (tb[IFA_ADDRESS])
printf("\t IFA_ADDRESS %s/%d\n",
inet_ntop(ifa->ifa_family, RTA_DATA (tb[IFA_ADDRESS]),
buf, BUFSIZ), ifa->ifa_prefixlen);
if (tb[IFA_BROADCAST])
printf("\t IFA_BROADCAST %s/%d\n",
inet_ntop(ifa->ifa_family, RTA_DATA(tb[IFA_BROADCAST]),
buf, BUFSIZ), ifa->ifa_prefixlen);
if (tb[IFA_LABEL] && strcmp (if_name, RTA_DATA(tb[IFA_LABEL])))
printf("\t IFA_LABEL %s\n", (char *)RTA_DATA(tb[IFA_LABEL]));
if (tb[IFA_CACHEINFO]) {
struct ifa_cacheinfo *ci = RTA_DATA(tb[IFA_CACHEINFO]);
printf("\t IFA_CACHEINFO pref %d, valid %d\n",
ci->ifa_prefered, ci->ifa_valid);
}
return 0;
}
static int netlink_information_fetch(struct sockaddr_nl *snl, struct nlmsghdr *h)
{
/* Ignore messages that aren't from the kernel */
if (snl->nl_pid != 0) {
printf("Ignoring message from pid %u that isn't from the kernel!\n",
snl->nl_pid);
return 0;
}
// Emit sigusr2:
if(m_pid) {
union sigval sv;
sv.sival_int = 0;
if(sigqueue(m_pid, SIGUSR2, sv) != 0) {
log_error("%d: SIGSENT-ERROR:", m_pid);
}
}
switch (h->nlmsg_type) {
case RTM_NEWROUTE:
return netlink_route_change(snl, h);
break;
case RTM_DELROUTE:
return netlink_route_change(snl, h);
break;
case RTM_NEWLINK:
return netlink_link_change(snl, h);
break;
case RTM_DELLINK:
return netlink_link_change(snl, h);
break;
case RTM_NEWADDR:
return netlink_interface_addr(snl, h);
break;
case RTM_DELADDR:
return netlink_interface_addr(snl, h);
break;
default:
printf("Unknown netlink nlmsg_type %d\n", h->nlmsg_type);
break;
}
return 0;
}
#define NL_PKT_BUF_SIZE 8192
/* Hack for GNU libc version 2. */
#ifndef MSG_TRUNC
#define MSG_TRUNC 0x20
#endif /* MSG_TRUNC */
/* Receive message from netlink interface and pass those information
* to the given function.
*/
static int netlink_parse_info (int (*filter)(struct sockaddr_nl *, struct nlmsghdr *),
struct nlsock *nl)
{
int status;
int ret = 0;
int error;
while (1) {
char buf[NL_PKT_BUF_SIZE];
struct iovec iov = {
.iov_base = buf,
.iov_len = sizeof(buf)
};
struct sockaddr_nl snl;
struct msghdr msg = {
.msg_name = (void *)&snl,
.msg_namelen = sizeof(snl),
.msg_iov = &iov,
.msg_iovlen = 1
};
struct nlmsghdr *h;
status = recvmsg(nl->sock, &msg, 0);
if (status < 0) {
if (errno == EINTR)
continue;
if (errno == EWOULDBLOCK || errno == EAGAIN)
break;
printf("%s recvmsg overrun: %s\n",
nl->name, safe_strerror(errno));
continue;
}
else if (status == 0) {
printf("%s EOF\n", nl->name);
return -1;
}
if (msg.msg_namelen != sizeof(snl)){
printf("%s sender address length error: length %d\n",
nl->name, msg.msg_namelen);
return -1;
}
for (h = (struct nlmsghdr *)buf; NLMSG_OK(h, (unsigned int)status);
h = NLMSG_NEXT(h, status)) {
/* Finish of reading. */
if (h->nlmsg_type == NLMSG_DONE)
return ret;
/* Error handling. */
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
int errnum = err->error;
int msg_type = err->msg.nlmsg_type;
/* If the error field is zero, then this is an ACK */
if (err->error == 0) {
#if 0
printf("%s: %s ACK: type=%s(%u), seq=%u, pid=%u\n",
__FUNCTION__, nl->name,
lookup (nlmsg_str, err->msg.nlmsg_type),
err->msg.nlmsg_type, err->msg.nlmsg_seq,
err->msg.nlmsg_pid);
#endif
/* return if not a multipart message, otherwise continue */
if (!(h->nlmsg_flags & NLM_F_MULTI)) {
return 0;
}
continue;
}
if (h->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr))) {
printf("%s error: message truncated\n", nl->name);
return -1;
}
printf("%s error: %s, type=%s(%u), seq=%u, pid=%u\n",
nl->name, safe_strerror(-errnum),
lookup (nlmsg_str, msg_type),
msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid);
return -1;
}
#if 0
/* OK we got netlink message. */
printf("netlink_parse_info: %s type %s(%u), seq=%u, pid=%u\n",
nl->name,
lookup (nlmsg_str, h->nlmsg_type), h->nlmsg_type,
h->nlmsg_seq, h->nlmsg_pid);
#endif
error = (*filter)(&snl, h);
if (error < 0) {
printf("%s filter function error\n", nl->name);
ret = error;
}
}
/* After error care. */
if (msg.msg_flags & MSG_TRUNC) {
printf("%s error: message truncated\n", nl->name);
continue;
}
if (status) {
printf("%s error: data remnant size %d\n", nl->name, status);
return -1;
}
}
return ret;
}
int main (int argc, char **argv)
{
int rc = 0, temp __attribute__((unused));
struct stat st;
char *cad = NULL;
bool restarting = false;
sigaction_init();
lstat(SOCKET_NAME, &st);
if(st.st_nlink > 0 && atoi(argv[1]) == getpid()) {
restarting = true;
m_pid = (pid_t) atoi(argv[2]);
read_arguments();
sigaction_init();
}
// Get the fullpath to libnetaid
struct sbuf s;
sbuf_init(&s);
get_fullpath(&s, argv[0]);
libpath = strdup_or_null(s.buf);
free(s.buf);
strncpy_t(arg0, 512, argv[0], strlen(argv[0]) + 1);
if ((cad = strrchr(argv[0], '/')))
cad++;
else
cad = argv[0];
progname = strdup_or_null(cad);
m_config = (config_t*)calloc(sizeof(config_t), 1);
if(m_config == NULL) {
return -ENOMEM;
}
rc = config_init(m_config);
if(rc != 0) {
log_error("config_init rc = %d\n", rc);
return rc;
}
// parse config options from command-line
rc = config_load_from_args(m_config, argc, argv);
if(restarting) {
temp = m_config->automatically_connect;
}
if(rc != 0) {
log_error("snetaid_config_load_from_argv rc = %d\n", rc);
config_usage(argv[0]);
return rc;
}
// if we didn't get a config file, use the default one
if(m_config->config_path == NULL) {
m_config->config_path = strdup_or_null(DEFAULT_CONFIG_FILE);
if(m_config->config_path == NULL) {
// OOM
return -ENOMEM;
}
}
set_debug_level(m_config->debug_level);
set_error_level(m_config->error_level);
// load from file
rc = config_load(m_config->config_path, m_config);
if(rc != 0) {
log_error("snetaid_config_load('%s') rc = %d\n", m_config->config_path, rc);
return rc;
}
if(restarting) {
m_config->automatically_connect = temp;
}
if (!m_config->foreground) {
struct stat st = {0};
char *duplicate = strdup_or_null(m_config->pidfile_path);
const char* dir = dirname(duplicate);
if(stat(dir, &st) == -1) {
if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) == -1) {
log_error("Cannot create %s\n", dir);
exit(EXIT_FAILURE);
}
}
/* It is also possible to use glibc function deamon()
* at this point, but it is useful to customize your daemon. */
daemonize();
}
/* Open system log and write message to it */
openlog(progname, LOG_PID|LOG_CONS, LOG_DAEMON);
log_info("Started %s", progname);
sigaction_init();
if(m_pid) kill(m_pid, SIGUSR1);
if(!restarting && m_config->automatically_connect) {
// periodic_interval == value of automatically_connect (integer) in seconds
if(*iproute() == 0) start_periodic(m_config->automatically_connect);
}
kernel_init();
netlink_parse_info(netlink_information_fetch, &netlink);
return 0;
}
void get_fullpath(struct sbuf *s, const char *name)
{
char *token;
char delim[4] = "=> ";
char output[1024] = {0};
char cmd[64] = {0};
sprintf(cmd, "ldd ");
strcat(cmd, name);
strcat(cmd, " | grep libnetaid.so");
FILE *pf = popen(cmd, "r");
if(!pf) {
printf("Sorry, there was an error opening the pipe\n");
exit(EXIT_FAILURE);
}
fgets(output, 1024, pf);
output[strcspn(output, "\n")] = 0;
token = strtok(output, delim);
token = strtok(NULL, delim);
sbuf_addstr(s, token);
pclose(pf);
}
void read_arguments()
{
int count = 0;
int dfData = -1;
int num_messages __attribute__((unused));
struct sockaddr_un dirUNIXServer;
int sz, rc = -1;
char *arr_pid = NULL;
char buffer[BUFFER_SIZE] = {0};
int option;
const char *device, *essid, *passwd, *filename = NULL, *dname;
// Reference: https://man7.org/tlpi/code/online/dist/sockets/scm_cred_recv.c.html
/* Allocate a char array of suitable size to hold the ancillary data.
However, since this buffer is in reality a 'struct cmsghdr', use a
union to ensure that it is aligned as required for that structure.
Alternatively, we could allocate the buffer using malloc(), which
returns a buffer that satisfies the strictest alignment
requirements of any type */
union {
char buf[CMSG_SPACE(sizeof(struct ucred))];
/* Space large enough to hold a 'ucred' structure */
struct cmsghdr align;
} controlMsg;
dfData = socket(AF_UNIX, SOCK_STREAM, PROTOCOL);
if(dfData == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
/*
* For portability clear the whole structure, since some
* implementations have additional (nonstandard) fields in
* the structure.
*/
memset(&dirUNIXServer, 0, sizeof(struct sockaddr_un));
dirUNIXServer.sun_family = AF_UNIX;
sz = sizeof(dirUNIXServer.sun_path) - 1;
strncpy_t(dirUNIXServer.sun_path, sz, SOCKET_NAME, sz);
rc = connect(dfData, (const struct sockaddr*) &dirUNIXServer, sizeof(struct sockaddr_un));
while(rc == -1 && count < 10) {
sleep(1);
rc = connect(dfData, (const struct sockaddr*) &dirUNIXServer, sizeof(struct sockaddr_un));
count++;
}
option = atoi(read_buffer(dfData, buffer));
num_messages = atoi(read_buffer(dfData, buffer));
device = read_buffer(dfData, buffer);
switch(option) {
case 4:
essid = read_buffer(dfData, buffer);
passwd = read_buffer(dfData, buffer);
break;
case 5:
essid = read_buffer(dfData, buffer);
passwd = read_buffer(dfData, buffer);
filename = read_buffer(dfData, buffer);
break;
case 6:
filename = read_buffer(dfData, buffer);
break;
case 7:
filename = read_buffer(dfData, buffer);
dname = dirname(buffer);
break;
default:
break;
}
/* We must set the SO_PASSCRED socket option in order to receive
credentials */
int optval = 1;
if (setsockopt(dfData, SOCK_STREAM, SO_PASSCRED, &optval, sizeof(optval)) == -1)
perror("setsockopt");
/* The 'msg_name' field can be set to point to a buffer where the
kernel will place the address of the peer socket. However, we don't
need the address of the peer, so we set this field to NULL. */
struct msghdr msgh;
msgh.msg_name = NULL;
msgh.msg_namelen = 0;
/* Set fields of 'msgh' to point to buffer used to receive (real)
data read by recvmsg() */
struct iovec iov;
int data;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
iov.iov_base = &data;
iov.iov_len = sizeof(data);
/* Set 'msgh' fields to describe the ancillary data buffer */
msgh.msg_control = controlMsg.buf;
msgh.msg_controllen = sizeof(controlMsg.buf);
/* Receive real plus ancillary data */
ssize_t nr = recvmsg(dfData, &msgh, 0);
if (nr == -1)
log_error("%s: recvmsg() returned %zd\n", progname, nr);
if (nr > 0)
log_info("%s: Received data = %d\n", progname, data);
/* Get the address of the first 'cmsghdr' in the received
ancillary data */
struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh);
/* Check the validity of the 'cmsghdr' */
if(cmsgp == NULL || cmsgp->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
log_error("%s: bad cmsg header / message length", progname);
if(cmsgp->cmsg_level != SOL_SOCKET)
log_error("%s: cmsg_level != SOL_SOCKET", progname);
if(cmsgp->cmsg_type != SCM_CREDENTIALS)
log_error("%s: cmsg_type != SCM_CREDENTIALS", progname);
/* Copy the contents of the data field of the 'cmsghdr' to a
'struct ucred'. */
struct ucred rcred, scred;
memcpy(&rcred, CMSG_DATA(cmsgp), sizeof(struct ucred));
/* Display the credentials from the received data area */
log_info("%s: Received credentials pid=%ld, uid=%ld, gid=%ld\n",
progname, (long) rcred.pid, (long) rcred.uid, (long) rcred.gid);
/* The Linux-specific, read-only SO_PEERCRED socket option returns
credential information about the peer, as described in socket(7).
This operation can be performed on UNIX domain stream sockets and on
UNIX domain sockets (stream or datagram) created with socketpair(). */
socklen_t len = sizeof(struct ucred);
if (getsockopt(dfData, SOL_SOCKET, SO_PEERCRED, &scred, &len) == -1)
perror("getsockopt");
log_info("%s: Credentials from SO_PEERCRED: pid=%ld, euid=%ld, egid=%ld\n",
progname, (long) scred.pid, (long) scred.uid, (long) scred.gid);
if(scred.pid != m_pid) {
log_warn("%s: argv[1] and scred.pid don't match!\n", progname);
exit(EXIT_FAILURE);
}
if (asprintf(&arr_pid, "%jd", (intmax_t) scred.pid) != -1) {
struct sbuf pstat_binary;
sbuf_init(&pstat_binary);
get_pstat_binary(&pstat_binary, arr_pid);
free(arr_pid);
bool whitelisted = request_whitelist(pstat_binary.buf);
if(!whitelisted) {
union sigval sv;
log_error("'%s' is not included in the whitelist %s\n", pstat_binary.buf, m_config->whitelist);
sbuf_free(&pstat_binary);
sv.sival_int = 0;
if(sigqueue(m_pid, SIGUSR1, sv) != 0) {
log_error("%s: SIGSENT-ERROR:", progname);
}
return;
}
sbuf_free(&pstat_binary);
}
else {
log_error("%s: Unable to get the pid\n", progname);
return;
}
switch(option) {
case 0:
stop_periodic();
disconnect(device);
break;
case 1:
wired_connection(device);
break;
case 2:
interface_up(device);
break;
case 3:
stop_periodic();
interface_down(device);
break;
case 4:
wireless_connection(device, essid, passwd, NULL);
break;
case 5:
wireless_connection(device, essid, passwd, filename);
break;
case 6:
{
struct sbuf cmd;
kill_all_processes();
ipaddr_flush(device);
interface_down(device);
ifdown(device);
interface_up(device);
sbuf_init(&cmd);
sbuf_concat(&cmd, 5, "/sbin/wpa_supplicant -B -i", device, " -c \"", filename, "\"");
system(cmd.buf);
free(cmd.buf);
sleep(1);
ifup(device);
}
break;
case 7:
if(!strcmp(dname, WIFI_DIR)) {
remove(filename);
if(!file_exists(filename)) {
log_error("Error deleting %s file.\n", filename);
}
}
break;
default:
break;
}
close(dfData);
unlink(SOCKET_NAME);
}
bool request_whitelist(const char *pstat_binary)
{
FILE *wl;
char line[MAX_SIZE] = {0};
bool res = false;
wl = fopen(WHITELIST, "r");
if(!wl) {
log_error("Unable to open file: %s\n", WHITELIST);
exit(EXIT_FAILURE);
}
while(fgets(line, MAX_SIZE, wl)) {
line[strcspn(line, "\n")] = 0;
char str[MAX_SIZE] = {0};
sprintf(str, "\"");
strcat(str, line);
strcat(str, "\"");
// Do they match?
if(!strcmp(line, pstat_binary)) {
res = true;
break;
}
}
fclose(wl);
return res;
}