diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 25 | ||||
-rw-r--r-- | src/mandi_daemon.c | 269 | ||||
-rw-r--r-- | src/plugin.h | 21 | ||||
-rw-r--r-- | src/plugins/ifw/black_list.c | 76 | ||||
-rw-r--r-- | src/plugins/ifw/black_list.h | 20 | ||||
-rw-r--r-- | src/plugins/ifw/ifw.h | 48 | ||||
-rw-r--r-- | src/plugins/ifw/ifw_dbus.c | 456 | ||||
-rw-r--r-- | src/plugins/ifw/ifw_dbus.h | 43 | ||||
-rw-r--r-- | src/plugins/ifw/ipset.c | 89 | ||||
-rw-r--r-- | src/plugins/ifw/ipset.h | 22 | ||||
-rw-r--r-- | src/plugins/ifw/libnl_ifw.c | 62 | ||||
-rw-r--r-- | src/plugins/ifw/libnl_ifw.h | 35 | ||||
-rw-r--r-- | src/plugins/ifw/list.h | 155 | ||||
-rw-r--r-- | src/plugins/ifw/plugin.c | 224 | ||||
-rw-r--r-- | src/plugins/ifw/report_list.c | 81 | ||||
-rw-r--r-- | src/plugins/ifw/report_list.h | 24 | ||||
-rw-r--r-- | src/plugins/ifw/white_list.c | 119 | ||||
-rw-r--r-- | src/plugins/ifw/white_list.h | 23 | ||||
-rw-r--r-- | src/plugins/wireless/plugin.c | 148 | ||||
-rw-r--r-- | src/plugins/wireless/wpa_ctrl.c | 238 | ||||
-rw-r--r-- | src/plugins/wireless/wpa_ctrl.h | 179 |
21 files changed, 2357 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..ba8b1c8 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,25 @@ +CC = gcc +CFLAGS = -Wall -g +DEFS = -DDBUS_API_SUBJECT_TO_CHANGE=1 +INCLUDES = $(shell pkg-config dbus-1 --cflags) -I$(PWD) +LDFLAGS = $(shell pkg-config dbus-1 --libs) + +DAEMON_LDFLAGS = +DAEMON_OBJS = mandi_daemon.o +DAEMON_TARGET = mandi + +DAEMON_OBJS += $(addprefix plugins/wireless/,plugin.o wpa_ctrl.o) + +#CFLAGS += -DIFW_FAKE +DAEMON_OBJS += $(addprefix plugins/ifw/,plugin.o ifw_dbus.o ipset.o white_list.o black_list.o report_list.o libnl_ifw.o) + +all: $(DAEMON_TARGET) + +.c.o: + $(CC) $(CFLAGS) $(DEFS) $(INCLUDES) -c $< -o $@ + +$(DAEMON_TARGET): $(DAEMON_OBJS) + $(CC) $(CFLAGS) $(DAEMON_OBJS) $(LDFLAGS) $(DAEMON_LDFLAGS) -o $@ + +clean: + rm -f $(DAEMON_OBJS) $(DAEMON_TARGET) diff --git a/src/mandi_daemon.c b/src/mandi_daemon.c new file mode 100644 index 0000000..6c67d88 --- /dev/null +++ b/src/mandi_daemon.c @@ -0,0 +1,269 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> + +#include <time.h> +#include <unistd.h> + +#include <dbus/dbus.h> +#include <errno.h> + +#include "plugin.h" + +#define MANDI_DAEMON_SERVICE PLUGIN_ROOT_INTF + +typedef struct { + DBusConnection *bus; + DBusWatch *bus_read_watch; + int bus_read_fd; +} mandi_daemon_t; + +extern plugin_t wpa_supplicant_plugin; +extern plugin_t ifw_plugin; +plugin_t *plugins[] = { + &wpa_supplicant_plugin, + &ifw_plugin, + NULL, +}; + +static mandi_daemon_t *mandi_daemon_p; /* global variable used in signal handlers only */ +static void sigquit(int signum); +static int mandi_daemon_init(mandi_daemon_t *daemon); +static void mandi_daemon_exit(mandi_daemon_t *daemon, int exit_code); +static void mandi_daemon_handle_dbus(mandi_daemon_t *daemon); +static int mandi_daemon_acquire_service(mandi_daemon_t *daemon); +static int mandi_daemon_init_watch(mandi_daemon_t *daemon); +static dbus_bool_t mandi_daemon_add_watch(DBusWatch *watch, void *data); +static void mandi_daemon_toggle_watch(DBusWatch *watch, void *data); +static void mandi_daemon_remove_watch(DBusWatch *watch, void *data); +static DBusConnection *mandi_daemon_get_system_bus(); +static int mandi_daemon_init_path(mandi_daemon_t *daemon, plugin_t *plugin); +static void mandi_daemon_object_path_unregister(DBusConnection *connection, void *user_data); +static DBusHandlerResult mandi_daemon_object_path_handle_message(DBusConnection *connection, DBusMessage *message, void *user_data); + +int main(int argc, char **argv) +{ + mandi_daemon_t mandi_daemon; + + /* set up signal handlers to exit nicely when needed */ + mandi_daemon_p = &mandi_daemon; + signal(SIGINT, sigquit); + signal(SIGTERM, sigquit); + signal(SIGQUIT, sigquit); + + mandi_daemon_init(&mandi_daemon); + + if (getopt(argc, argv, "d") == 'd') { + daemon(0, 0); + } + + printf("Monitoring daemon waiting for events ...\n"); + + while (1) { + int num_fds; + fd_set read_fds; + plugin_t **ptr; + plugin_t *plugin; + + FD_ZERO(&read_fds); + FD_SET(mandi_daemon.bus_read_fd, &read_fds); + num_fds = mandi_daemon.bus_read_fd + 1; + for (ptr = plugins; *ptr; ptr++) { + plugin = *ptr; + if (plugin->fd >= 0) { + FD_SET(plugin->fd, &read_fds); + if (plugin->fd + 1 > num_fds) { + num_fds = plugin->fd + 1; + } + } + } + + if (select(num_fds, &read_fds, NULL, NULL, NULL) > 0) { + if (FD_ISSET(mandi_daemon.bus_read_fd, &read_fds)) { + mandi_daemon_handle_dbus(&mandi_daemon); + } + for (ptr = plugins; *ptr; ptr++) { + plugin = *ptr; + if (plugin->fd >= 0 && FD_ISSET(plugin->fd, &read_fds)) { + plugin->handle_incoming(plugin, mandi_daemon.bus); + } + } + } else { + fprintf(stderr, "unhandled error in select(): %s\n", strerror(errno)); + + } + } + + mandi_daemon_exit(&mandi_daemon, EXIT_SUCCESS); + return 0; +} + +static void sigquit(int signum) { + printf("SIGINT, SIGTERM or SIGQUIT catched, trying to exit nicely\n"); + mandi_daemon_exit(mandi_daemon_p, EXIT_SUCCESS); +} + +static int mandi_daemon_init(mandi_daemon_t *daemon) { + plugin_t **ptr; + plugin_t *plugin; + + daemon->bus = mandi_daemon_get_system_bus(); + if (!daemon->bus) { + mandi_daemon_exit(daemon, EXIT_FAILURE); + } + + if (mandi_daemon_init_watch(daemon) != 0) { + fprintf(stderr, "unable to init DBus watch\n"); + mandi_daemon_exit(daemon, EXIT_FAILURE); + } + + if (mandi_daemon_acquire_service(daemon) != 0) { + fprintf(stderr, "unable to init DBus service\n"); + mandi_daemon_exit(daemon, EXIT_FAILURE); + } + + for (ptr = plugins; *ptr; ptr++) { + plugin = *ptr; + if (plugin->init(plugin, daemon->bus) != 0) { + fprintf(stderr, "unable to init \"%s\" plugin\n", plugin->name); + mandi_daemon_exit(daemon, EXIT_FAILURE); + } + + mandi_daemon_init_path(daemon, plugin); + } + + return 0; +} + +static void mandi_daemon_exit(mandi_daemon_t *daemon, int exit_code) { + plugin_t **ptr; + plugin_t *plugin; + for (ptr = plugins; *ptr; ptr++) { + plugin = *ptr; + plugin->deinit(plugin, daemon->bus); + } + /* unregister dbus */ + exit(exit_code); +} + +static void mandi_daemon_handle_dbus(mandi_daemon_t *daemon) { + dbus_watch_handle(daemon->bus_read_watch, DBUS_WATCH_READABLE); + dbus_connection_dispatch(daemon->bus); +} + +int mandi_daemon_acquire_service(mandi_daemon_t *daemon) { + DBusError error; + dbus_error_init(&error); + + if (dbus_bus_acquire_service(daemon->bus, MANDI_DAEMON_SERVICE, 0, &error) == -1) { + fprintf(stderr, "dbus_bus_acquire_service(): %s\n", error.message); + fprintf(stderr, "Make sure a DBus policy allows you to acquire this service.\n"); + dbus_error_free(&error); + return -1; + } + dbus_connection_dispatch(daemon->bus); + + dbus_error_free(&error); + return 0; +} + +/* get fds to be watched */ +static int mandi_daemon_init_watch(mandi_daemon_t *daemon) { + DBusError error; + + dbus_error_init(&error); + + if (dbus_connection_set_watch_functions(daemon->bus, + mandi_daemon_add_watch, + mandi_daemon_toggle_watch, + mandi_daemon_remove_watch, + (void *) daemon, + NULL) == FALSE) { + fprintf(stderr, "dbus_connection_set_watch_functions(): %s\n", error.message); + dbus_error_free(&error); + return -1; + } + dbus_connection_dispatch(daemon->bus); + + dbus_error_free(&error); + return 0; +} + +static dbus_bool_t mandi_daemon_add_watch(DBusWatch *watch, void *data) { + mandi_daemon_t *daemon = (mandi_daemon_t *) data; + if (dbus_watch_get_flags(watch) & DBUS_WATCH_READABLE) { + fprintf(stderr, "mandi_daemon_add_watch(): READABLE\n"); + daemon->bus_read_watch = watch; + daemon->bus_read_fd = dbus_watch_get_fd(daemon->bus_read_watch); + } + /* do nothing for WRITABLE watch, we dispatch when needed */ + return TRUE; +} + +static void mandi_daemon_toggle_watch(DBusWatch *watch, void *data) { + fprintf(stderr, "mandi_daemon_toggle_watch()\n"); + /* FIXME : do we need to do something here ? */ +} + +static void mandi_daemon_remove_watch(DBusWatch *watch, void *data) { + if (dbus_watch_get_flags(watch) & DBUS_WATCH_READABLE) { + fprintf(stderr, "mandi_daemon_remove_watch(): READABLE\n"); + /* FIXME : do we need to do something here ? */ + } +} + +/* set a connection to the system bus */ +static DBusConnection *mandi_daemon_get_system_bus() { + DBusConnection *bus; + DBusError error; + dbus_error_init(&error); + + bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (!bus) { + fprintf(stderr, "Failed to connect to the D-BUS daemon: %s\n", error.message); + dbus_error_free(&error); + return NULL; + } + + dbus_error_free(&error); + return bus; +} + +static int mandi_daemon_init_path(mandi_daemon_t *daemon, plugin_t *plugin) { + struct DBusObjectPathVTable object_path_vtable = { + mandi_daemon_object_path_unregister, + mandi_daemon_object_path_handle_message, + NULL, NULL, NULL, NULL + }; + + DBusError error; + dbus_error_init(&error); + + if (dbus_connection_register_object_path(daemon->bus, plugin->path, &object_path_vtable, plugin) == FALSE) { + fprintf(stderr, "dbus_connection_register_object_path(): not enough memory\n"); + dbus_error_free(&error); + return -1; + } + dbus_connection_dispatch(daemon->bus); + + dbus_error_free(&error); + return 0; +} + +static void mandi_daemon_object_path_unregister(DBusConnection *connection, void *user_data) { +} + +static DBusHandlerResult mandi_daemon_object_path_handle_message(DBusConnection *connection, DBusMessage *message, void *user_data) { + plugin_t *plugin = (plugin_t *) user_data; + + printf("handling method call '%s' on interface '%s'\n", + dbus_message_get_member(message), + dbus_message_get_interface(message)); + + return plugin->handle_message(connection, message, plugin); +} diff --git a/src/plugin.h b/src/plugin.h new file mode 100644 index 0000000..ad3973a --- /dev/null +++ b/src/plugin.h @@ -0,0 +1,21 @@ +#ifndef PLUGIN_H +#define PLUGIN_H + +#include <dbus/dbus.h> + +#define PLUGIN_ROOT_INTF "com.mandriva.monitoring" +#define PLUGIN_ROOT_PATH "/com/mandriva/monitoring" + +typedef struct plugin_s plugin_t; +struct plugin_s { + const char *name; + const char *path; + int fd; + void *priv; + int (*init)(plugin_t *plugin, DBusConnection *connection); + void (*handle_incoming)(plugin_t *plugin, DBusConnection *connection); + DBusHandlerResult (*handle_message)(DBusConnection *connection, DBusMessage *message, plugin_t *plugin); + void (*deinit)(plugin_t *plugin, DBusConnection *connection); +}; + +#endif /* PLUGIN_H */ diff --git a/src/plugins/ifw/black_list.c b/src/plugins/ifw/black_list.c new file mode 100644 index 0000000..1e7dbbb --- /dev/null +++ b/src/plugins/ifw/black_list.c @@ -0,0 +1,76 @@ +#include "black_list.h" +#include "ipset.h" + +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/socket.h> + +void black_list_init(black_list_t *list) { + INIT_LIST_HEAD(list); +} + +void black_list_add(black_list_t *list, msg_usr_t *attack) { + black_list_cell_t *cell; + + cell = malloc(sizeof(black_list_cell_t)); + if (!cell) { + fprintf(stderr, "unable to alloc enough memory for black list cell, skipping\n"); + return; + } + cell->info = *attack; + INIT_LIST_HEAD(&cell->list); + list_add_tail(&cell->list, list); + + ipset_blacklist_add(cell->info.s_addr); +} + +black_list_cell_t *black_list_find(black_list_t *list, u_int32_t addr) { + struct list_head *entry; + + __list_for_each(entry, list) { + black_list_cell_t *cell; + cell = list_entry(entry, black_list_cell_t, list); + if (cell->info.s_addr == addr) { + return cell; + } + } + + return NULL; +} + +void black_list_remove(black_list_t *list, u_int32_t addr) { + black_list_cell_t *cell, *n, *prev; + + ipset_blacklist_remove(addr); + + prev = NULL; + list_for_each_entry_safe(cell, n, list, list) { + if (prev) + free(prev); + if (cell->info.s_addr == addr) { + list_del(&cell->list); + prev = cell; + } else { + prev = NULL; + } + } + if (prev) + free(prev); +} + + +void black_list_print(black_list_t *list) { + struct list_head *entry; + + printf("* black list {\n"); + __list_for_each(entry, list) { + black_list_cell_t *cell; + struct in_addr addr; + cell = list_entry(entry, black_list_cell_t, list); + addr.s_addr = cell->info.s_addr; + printf("%s,\n", inet_ntoa(addr)); + } + printf("} black list *\n"); +} diff --git a/src/plugins/ifw/black_list.h b/src/plugins/ifw/black_list.h new file mode 100644 index 0000000..4cfe74b --- /dev/null +++ b/src/plugins/ifw/black_list.h @@ -0,0 +1,20 @@ +#ifndef BLACK_LIST_H +#define BLACK_LIST_H + +#include "list.h" +typedef struct list_head black_list_t; + +#include "ifw.h" + +typedef struct { + struct list_head list; + msg_usr_t info; +} black_list_cell_t; + +void black_list_init(black_list_t *list); +void black_list_add(black_list_t *list, msg_usr_t *attack); +black_list_cell_t *black_list_find(black_list_t *list, u_int32_t addr); +void black_list_remove(black_list_t *list, u_int32_t addr); +void black_list_print(black_list_t *list); + +#endif /* BLACK_LIST_H */ diff --git a/src/plugins/ifw/ifw.h b/src/plugins/ifw/ifw.h new file mode 100644 index 0000000..f007b0f --- /dev/null +++ b/src/plugins/ifw/ifw.h @@ -0,0 +1,48 @@ +#ifndef IFW_H +#define IFW_H + +#include "plugin.h" + +#define IFW_DBUS_PATH PLUGIN_ROOT_PATH "/ifw" +#define IFW_DBUS_INTERFACE PLUGIN_ROOT_INTF ".ifw" + +#include <sys/types.h> +#include "libnl_ifw.h" + +#define IFW_SYSCONF_ROOT "/etc/ifw/" +#define IFW_BLACKLIST_FILENAME IFW_SYSCONF_ROOT "blacklist" +#define IFW_WHITELIST_FILENAME IFW_SYSCONF_ROOT "whitelist" + +typedef enum { + IFW_MODE_AUTO, + IFW_MODE_INTERACTIVE +} ifw_mode_t; + +typedef struct { + long timestamp_sec; /* date */ + char indev_name[IFNAMSIZ]; /* input interface */ + char prefix[PREFSIZ]; /* summary of attack */ + int sensor; /* sensor the alert come from */ + int protocol; /* Protocol */ + u_int32_t s_addr; /* source address */ + u_int16_t d_port; /* destination port UDP/TCP */ + u_int8_t icmp_type; /* icmp type */ +} msg_usr_t; + +typedef struct popup_verdict { + int seq; + int bl; +} popup_verdict_t; + +#include "black_list.h" +#include "white_list.h" +#include "report_list.h" + +typedef struct { + ifw_mode_t mode; + black_list_t blacklist; + report_list_t reports; + white_list_t whitelist; +} ifw_t; + +#endif /* IFW_H */ diff --git a/src/plugins/ifw/ifw_dbus.c b/src/plugins/ifw/ifw_dbus.c new file mode 100644 index 0000000..82b2675 --- /dev/null +++ b/src/plugins/ifw/ifw_dbus.c @@ -0,0 +1,456 @@ +#include <stdio.h> + +#include "ifw_dbus.h" + +static void ifw_dbus_notify_simple_signal(DBusConnection *bus, char *signal) { + DBusMessage *message; + + message = dbus_message_new_signal(IFW_DBUS_PATH, + IFW_DBUS_INTERFACE, + signal); + dbus_connection_send(bus, message, NULL); + dbus_connection_flush(bus); + dbus_message_unref(message); +} + +void ifw_dbus_apply_report_verdict(DBusConnection *connection, ifw_t *ifw, report_list_cell_t *report, int do_blacklist) { + if (do_blacklist) { + if (!black_list_find(&ifw->blacklist, report->info.s_addr)) { + printf("blacklisting seq %d\n", report->seq); + black_list_add(&ifw->blacklist, &report->info); + ifw_dbus_notify_blacklist(connection, &report->info); + } else { + printf("(seq %d) addr %u already in blacklist\n", report->seq, report->info.s_addr); + } + } else { + printf("ignoring seq %d\n", report->seq); + } + report->processed = 1; +} + +/* notify frontends of a new attack with a DBus signal */ +void ifw_dbus_notify_attack(DBusConnection *bus, report_list_cell_t *report) { + DBusMessage *message; + + message = dbus_message_new_signal(IFW_DBUS_PATH, + IFW_DBUS_INTERFACE, + "Attack"); + + dbus_message_append_args(message, + DBUS_TYPE_UINT32, + report->info.timestamp_sec, + DBUS_TYPE_STRING, + report->info.indev_name, + DBUS_TYPE_STRING, + report->info.prefix, + DBUS_TYPE_UINT32, + report->info.sensor, + DBUS_TYPE_UINT32, + report->info.protocol, + DBUS_TYPE_UINT32, + report->info.s_addr, + DBUS_TYPE_UINT32, + report->info.d_port, + DBUS_TYPE_UINT32, + report->info.icmp_type, + DBUS_TYPE_UINT32, + report->seq, + DBUS_TYPE_UINT32, + report->processed, + DBUS_TYPE_INVALID); + dbus_connection_send(bus, message, NULL); + dbus_connection_flush(bus); + dbus_message_unref(message); +} + +/* notify frontends of a new blacklist with a DBus signal */ +void ifw_dbus_notify_blacklist(DBusConnection *bus, msg_usr_t *attack) { + DBusMessage *message; + + message = dbus_message_new_signal(IFW_DBUS_PATH, + IFW_DBUS_INTERFACE, + "Blacklist"); + + dbus_message_append_args(message, + DBUS_TYPE_UINT32, + attack->timestamp_sec, + DBUS_TYPE_STRING, + attack->indev_name, + DBUS_TYPE_STRING, + attack->prefix, + DBUS_TYPE_UINT32, + attack->sensor, + DBUS_TYPE_UINT32, + attack->protocol, + DBUS_TYPE_UINT32, + attack->s_addr, + DBUS_TYPE_UINT32, + attack->d_port, + DBUS_TYPE_UINT32, + attack->icmp_type, + DBUS_TYPE_INVALID); + dbus_connection_send(bus, message, NULL); + dbus_connection_flush(bus); + dbus_message_unref(message); +} + +/* notify frontends of a new whitelist with a DBus signal */ +void ifw_dbus_notify_whitelist(DBusConnection *bus, u_int32_t addr) { + DBusMessage *message; + + message = dbus_message_new_signal(IFW_DBUS_PATH, + IFW_DBUS_INTERFACE, + "Whitelist"); + dbus_message_append_args(message, + DBUS_TYPE_UINT32, + addr, + DBUS_TYPE_INVALID); + dbus_connection_send(bus, message, NULL); + dbus_connection_flush(bus); + dbus_message_unref(message); +} + +/* notify frontends that ifw data isn't usable with a DBus signal */ +void ifw_dbus_notify_clear(DBusConnection *bus) { + ifw_dbus_notify_simple_signal(bus, "Clear"); +} + +/* notify frontends that ifw has just been started */ +void ifw_dbus_notify_init(DBusConnection *bus) { + ifw_dbus_notify_simple_signal(bus, "Init"); +} + +/* notify frontends that a user is aware of the attacks */ +void ifw_dbus_notify_alert_ack(DBusConnection *bus) { + ifw_dbus_notify_simple_signal(bus, "AlertAck"); +} + +/* notify frontends that a user is wants to review the attacks */ +void ifw_dbus_notify_manage_request(DBusConnection *bus) { + ifw_dbus_notify_simple_signal(bus, "ManageRequest"); +} + +DBusHandlerResult ifw_dbus_get_mode(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusMessage *reply; + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, + DBUS_TYPE_UINT32, + ifw->mode, + DBUS_TYPE_INVALID); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_set_mode(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusError error; + DBusMessage *reply; + ifw_mode_t mode; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &mode, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "ifw_dbus_set_mode(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + printf("setting new ifw mode : %s\n", mode == IFW_MODE_AUTO ? "auto" : mode == IFW_MODE_INTERACTIVE ? "interactive" : "unknown"); + ifw->mode = mode; + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, + DBUS_TYPE_UINT32, + ifw->mode, + DBUS_TYPE_INVALID); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_get_reports(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusError error; + struct list_head *entry; + DBusMessage *reply; + char include_processed; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &include_processed, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "ifw_dbus_get_reports(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + reply = dbus_message_new_method_return(message); + __list_for_each(entry, &ifw->reports) { + report_list_cell_t *cell; + cell = list_entry(entry, report_list_cell_t, list); + if (cell->processed && !include_processed) { + continue; + } + dbus_message_append_args(reply, + DBUS_TYPE_UINT32, + cell->info.timestamp_sec, + DBUS_TYPE_STRING, + cell->info.indev_name, + DBUS_TYPE_STRING, + cell->info.prefix, + DBUS_TYPE_UINT32, + cell->info.sensor, + DBUS_TYPE_UINT32, + cell->info.protocol, + DBUS_TYPE_UINT32, + cell->info.s_addr, + DBUS_TYPE_UINT32, + cell->info.d_port, + DBUS_TYPE_UINT32, + cell->info.icmp_type, + DBUS_TYPE_UINT32, + cell->seq, + DBUS_TYPE_UINT32, + cell->processed, + DBUS_TYPE_INVALID); + } + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_get_blacklist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + struct list_head *entry; + DBusMessage *reply; + + reply = dbus_message_new_method_return(message); + __list_for_each(entry, &ifw->blacklist) { + black_list_cell_t *cell; + cell = list_entry(entry, black_list_cell_t, list); + dbus_message_append_args(reply, + DBUS_TYPE_UINT32, + cell->info.timestamp_sec, + DBUS_TYPE_STRING, + cell->info.indev_name, + DBUS_TYPE_STRING, + cell->info.prefix, + DBUS_TYPE_UINT32, + cell->info.sensor, + DBUS_TYPE_UINT32, + cell->info.protocol, + DBUS_TYPE_UINT32, + cell->info.s_addr, + DBUS_TYPE_UINT32, + cell->info.d_port, + DBUS_TYPE_UINT32, + cell->info.icmp_type, + DBUS_TYPE_INVALID); + } + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_set_blacklist_verdict(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusError error; + DBusMessage *reply; + int seq, do_blacklist; + report_list_cell_t *report; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &seq, + DBUS_TYPE_UINT32, + &do_blacklist, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "ifw_dbus_blacklist(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + report = report_list_find_seq(&ifw->reports, seq); + if (report) { + ifw_dbus_apply_report_verdict(connection, ifw, report, do_blacklist); + } else { + fprintf(stderr, "unable find sequence number in report list, skipping\n"); + } + + black_list_print(&ifw->blacklist); + report_list_print(&ifw->reports); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_unblacklist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusError error; + DBusMessage *reply; + u_int32_t addr; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &addr, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "ifw_dbus_blacklist(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + black_list_remove(&ifw->blacklist, addr); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_get_whitelist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + struct list_head *entry; + DBusMessage *reply; + + reply = dbus_message_new_method_return(message); + __list_for_each(entry, &ifw->whitelist) { + white_list_cell_t *cell; + cell = list_entry(entry, white_list_cell_t, list); + dbus_message_append_args(reply, + DBUS_TYPE_UINT32, + cell->addr, + DBUS_TYPE_INVALID); + } + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_whitelist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusError error; + DBusMessage *reply; + u_int32_t addr; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &addr, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "ifw_dbus_whitelist(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + if (!white_list_find(&ifw->whitelist, addr)) { + printf("whitelisting addr %u\n", addr); + white_list_add(&ifw->whitelist, addr); + } else { + printf("addr %u already in whitelist\n", addr); + } + white_list_print(&ifw->whitelist); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + ifw_dbus_notify_whitelist(connection, addr); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_unwhitelist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusError error; + DBusMessage *reply; + u_int32_t addr; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &addr, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "ifw_dbus_whitelist(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + printf("remove addr from whitelist %u\n", addr); + white_list_remove(&ifw->whitelist, addr); + + white_list_print(&ifw->whitelist); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_clear_processed_reports(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusMessage *reply; + + report_list_clear_processed(&ifw->reports); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_send_alert_ack(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusMessage *reply; + + ifw_dbus_notify_alert_ack(connection); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_send_manage_request(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusMessage *reply; + + ifw_dbus_notify_manage_request(connection); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} diff --git a/src/plugins/ifw/ifw_dbus.h b/src/plugins/ifw/ifw_dbus.h new file mode 100644 index 0000000..a4385db --- /dev/null +++ b/src/plugins/ifw/ifw_dbus.h @@ -0,0 +1,43 @@ +#ifndef IFW_DBUS_H +#define IFW_DBUS_H + +#include <dbus/dbus.h> +#include "ifw.h" + +void ifw_dbus_apply_report_verdict(DBusConnection *connection, ifw_t *ifw, report_list_cell_t *report, int do_blacklist); + +/* notify frontends of a new attack with a DBus signal */ +void ifw_dbus_notify_attack(DBusConnection *bus, report_list_cell_t *report); + +/* notify frontends of a new whitelist with a DBus signal */ +void ifw_dbus_notify_whitelist(DBusConnection *bus, u_int32_t addr); + +/* notify frontends of a new blacklist with a DBus signal */ +void ifw_dbus_notify_blacklist(DBusConnection *bus, msg_usr_t *attack); + +/* notify frontends that ifw data isn't usable with a DBus signal */ +void ifw_dbus_notify_clear(DBusConnection *bus); + +/* notify frontends that ifw has just been started */ +void ifw_dbus_notify_init(DBusConnection *bus); + +/* notify frontends that a user is aware of the attacks */ +void ifw_dbus_notify_alert_ack(DBusConnection *bus); + +/* notify frontends that a user is wants to review the attacks */ +void ifw_dbus_notify_manage_request(DBusConnection *bus); + +DBusHandlerResult ifw_dbus_get_mode(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_set_mode(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_get_reports(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_get_blacklist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_set_blacklist_verdict(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_unblacklist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_get_whitelist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_whitelist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_unwhitelist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_clear_processed_reports(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_send_alert_ack(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_send_manage_request(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); + +#endif /* IFW_DBUS_H */ diff --git a/src/plugins/ifw/ipset.c b/src/plugins/ifw/ipset.c new file mode 100644 index 0000000..74ca06e --- /dev/null +++ b/src/plugins/ifw/ipset.c @@ -0,0 +1,89 @@ +#include "ipset.h" + +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> + +#define CMD_MAX_SIZE 1024 + +#ifdef IPSET_DEBUG +#define DPRINTF(s) printf("%s\n", s) +#else +#define DPRINTF(s) +#endif + +void ipset_init() { + char cmd[CMD_MAX_SIZE]; + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -N " IPSET_BLACKLIST_NAME " iptree --timeout " IPSET_BLACKLIST_TIMEOUT); + DPRINTF(cmd); + system(cmd); + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -N " IPSET_WHITELIST_NAME " iptree"); + DPRINTF(cmd); + system(cmd); +} + +void ipset_destroy() { + char cmd[CMD_MAX_SIZE]; + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -X " IPSET_BLACKLIST_NAME); + DPRINTF(cmd); + system(cmd); + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -X " IPSET_WHITELIST_NAME); + DPRINTF(cmd); + system(cmd); +} + +/* void ipset_blacklist_load(char *filename) { */ +/* char cmd[CMD_MAX_SIZE]; */ +/* snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -R < %s", filename); */ +/* DPRINTF(cmd); */ +/* system(cmd); */ +/* } */ + +/* void ipset_blacklist_save(char *filename) { */ +/* char cmd[CMD_MAX_SIZE]; */ +/* snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -S " IPSET_BLACKLIST_NAME " > %s", filename); */ +/* DPRINTF(cmd); */ +/* system(cmd); */ +/* } */ + +/* void ipset_whitelist_load(char *filename) { */ +/* char cmd[CMD_MAX_SIZE]; */ +/* snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -R < %s", filename); */ +/* DPRINTF(cmd); */ +/* system(cmd); */ +/* } */ + +/* void ipset_whitelist_save(char *filename) { */ +/* char cmd[CMD_MAX_SIZE]; */ +/* snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -S " IPSET_WHITELIST_NAME " > %s", filename); */ +/* DPRINTF(cmd); */ +/* system(cmd); */ +/* } */ + +void ipset_blacklist_add(u_int32_t addr) { + char cmd[CMD_MAX_SIZE]; + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -A " IPSET_BLACKLIST_NAME " %u", ntohl(addr)); + DPRINTF(cmd); + system(cmd); +} + +void ipset_blacklist_remove(u_int32_t addr) { + char cmd[CMD_MAX_SIZE]; + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -D " IPSET_BLACKLIST_NAME " %u", ntohl(addr)); + DPRINTF(cmd); + system(cmd); +} + +void ipset_whitelist_add(u_int32_t addr) { + char cmd[CMD_MAX_SIZE]; + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -A " IPSET_WHITELIST_NAME " %u", ntohl(addr)); + DPRINTF(cmd); + system(cmd); +} + +void ipset_whitelist_remove(u_int32_t addr) { + char cmd[CMD_MAX_SIZE]; + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -D " IPSET_WHITELIST_NAME " %u", ntohl(addr)); + DPRINTF(cmd); + system(cmd); +} diff --git a/src/plugins/ifw/ipset.h b/src/plugins/ifw/ipset.h new file mode 100644 index 0000000..a78395a --- /dev/null +++ b/src/plugins/ifw/ipset.h @@ -0,0 +1,22 @@ +#ifndef IPSET_H +#define IPSET_H + +#define IPSET_CMD "ipset" +#define IPSET_BLACKLIST_NAME "ifw_bl" +#define IPSET_WHITELIST_NAME "ifw_wl" +#define IPSET_BLACKLIST_TIMEOUT "3600" + +#include <sys/types.h> + +void ipset_init(); +void ipset_destroy(); +/* void ipset_blacklist_load(char *filename); */ +/* void ipset_blacklist_save(char *filename); */ +/* void ipset_whitelist_load(char *filename); */ +/* void ipset_whitelist_save(char *filename); */ +void ipset_blacklist_add(u_int32_t addr); +void ipset_blacklist_remove(u_int32_t addr); +void ipset_whitelist_add(u_int32_t addr); +void ipset_whitelist_remove(u_int32_t addr); + +#endif /* IPSET_H */ diff --git a/src/plugins/ifw/libnl_ifw.c b/src/plugins/ifw/libnl_ifw.c new file mode 100644 index 0000000..bb41363 --- /dev/null +++ b/src/plugins/ifw/libnl_ifw.c @@ -0,0 +1,62 @@ +/* nl_create_socket(), nl_bind_socket() and nl_read_msg() + * for Interactive Firewall + * sbellabes@mandriva.com + */ + + +#include <asm/types.h> +#include <sys/socket.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> + +#include "libnl_ifw.h" + +int nl_ifw_bind_socket(int s) { + struct sockaddr_nl saddr_nl; + int res; + + memset(&saddr_nl, 0, sizeof(struct sockaddr_nl)); + saddr_nl.nl_family = AF_NETLINK; + saddr_nl.nl_pid = getpid(); + saddr_nl.nl_groups = 10; + + res = bind(s, (struct sockaddr *)&saddr_nl, sizeof(saddr_nl)); + if (res == -1) { + perror("nl_bind_socket"); + return -1; + } + return 1; +} + +int nl_ifw_create_socket(void) { + int s; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_IFWLOG); + if (s < 0) { + perror("nl_create_socket"); + return -1; + } + + if (nl_ifw_bind_socket(s) < 0) { + close(s); + fprintf(stderr, "bind failed\n"); + return -1; + } + + return s; +} + +int nl_ifw_read_msg(int s, struct nlmsghdr *nlh, struct nl_msg *msg) { + char buf[sizeof(struct nlmsghdr) + sizeof(struct nl_msg)]; + int ret; + + ret = recv(s, &buf, sizeof(buf), 0); + if (ret > 0) { + if (nlh) memcpy(nlh, buf, sizeof(struct nlmsghdr)); + if (msg) memcpy(msg, NLMSG_DATA(buf), sizeof(struct nl_msg)); + } + + return ret; +} diff --git a/src/plugins/ifw/libnl_ifw.h b/src/plugins/ifw/libnl_ifw.h new file mode 100644 index 0000000..896d05c --- /dev/null +++ b/src/plugins/ifw/libnl_ifw.h @@ -0,0 +1,35 @@ +/* + * libnl_ifw.h + */ + +#ifndef _LIBNL_IFW_H +#define _LIBNL_IFW_H + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <linux/if.h> +#include <linux/netlink.h> + +#define NETLINK_IFWLOG 19 + +#define PREFSIZ 32 + +struct nl_msg { /* Netlink kernel to user message */ + long timestamp_sec; /* time packet */ + char indev_name[IFNAMSIZ]; /* name of the ingoing interface */ + char outdev_name[IFNAMSIZ]; /* name of the outgoing interface */ + unsigned char prefix[PREFSIZ]; /* logging informations */ + struct iphdr ip; + union { + struct tcphdr th; + struct udphdr uh; + } h; +}; + +int nl_ifw_bind_socket(int s); +int nl_ifw_create_socket(void); +int nl_ifw_read_msg(int s, struct nlmsghdr *nlh, struct nl_msg *msg); + +#endif /* !_LIBNL_IFW_H */ diff --git a/src/plugins/ifw/list.h b/src/plugins/ifw/list.h new file mode 100644 index 0000000..1b55f95 --- /dev/null +++ b/src/plugins/ifw/list.h @@ -0,0 +1,155 @@ +#ifndef LIST_H +#define LIST_H + +/* borrowed from kernel header linux/list.h */ + +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + + +/* borrowed from kernel header linux/kernel.h */ + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - linux_offsetof(type,member) );}) + + +/* borrowed from linux/stdddef.h */ + +#define linux_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#endif /* LIST_H */ diff --git a/src/plugins/ifw/plugin.c b/src/plugins/ifw/plugin.c new file mode 100644 index 0000000..0e6ac69 --- /dev/null +++ b/src/plugins/ifw/plugin.c @@ -0,0 +1,224 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/socket.h> + +#include "ifw.h" +#include "ifw_dbus.h" +#include "ipset.h" + +static int init(plugin_t *plugin, DBusConnection *connection); +static void deinit(plugin_t *plugin, DBusConnection *connection); +static void process_attack(plugin_t *plugin, DBusConnection *connection, int seq, msg_usr_t *attack); +static DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *message, plugin_t *plugin); +#ifdef IFW_FAKE +static int generate_fake_attack(msg_usr_t *attack); +static void handle_fake(plugin_t *plugin, DBusConnection *connection); +#else +static void handle_incoming(plugin_t *plugin, DBusConnection *connection); +#endif + +static int init(plugin_t *plugin, DBusConnection *connection) { + ifw_t *ifw; + + ifw = malloc(sizeof(ifw_t)); + if (!ifw) { + fprintf(stderr, "unable to malloc ifw\n"); + return -1; + } + + report_list_init(&ifw->reports); + black_list_init(&ifw->blacklist); + white_list_init(&ifw->whitelist); + white_list_load(&ifw->whitelist, IFW_WHITELIST_FILENAME); + + ifw->mode = IFW_MODE_INTERACTIVE; + +#ifdef IFW_FAKE + plugin->fd = 0; +#else + plugin->fd = nl_ifw_create_socket(); + if (plugin->fd < 0) { + fprintf(stderr, "unable to init netlink\n"); + return -1; + } +#endif + + ifw_dbus_notify_clear(connection); + ifw_dbus_notify_init(connection); + + plugin->priv = (void *) ifw; + + return 0; +} + +static void deinit(plugin_t *plugin, DBusConnection *connection) { + ifw_t *ifw = (ifw_t *) plugin->priv; + + ifw_dbus_notify_clear(connection); + close(plugin->fd); +} + +#ifdef IFW_FAKE +#include <time.h> + +static int generate_fake_attack(msg_usr_t *attack) { + static int seq = 0; + uint32_t addr = 0xC0A86401; + + time(&attack->timestamp_sec); + strcpy(attack->indev_name, "ppp0"); + attack->sensor = 0; + attack->protocol = 0; + attack->icmp_type = 0; + attack->d_port = 0; + attack->s_addr = htonl(addr); + + switch(seq%3) { + case 0: + strcpy(attack->prefix, "SCAN"); + break; + case 1: + strcpy(attack->prefix, "SERV"); + attack->d_port = 22; + break; + case 2: + strcpy(attack->prefix, "PASS"); + break; + } + + addr++; + seq++; + + return seq; +} + +static void handle_fake(plugin_t *plugin, DBusConnection *connection) { + msg_usr_t fake_attack; + int seq; + + read(0, NULL, 1); + + seq = generate_fake_attack(&fake_attack); + printf("seq : %d\n", seq); + + process_attack(plugin, connection, seq, &fake_attack); +} + +#else + +static void handle_incoming(plugin_t *plugin, DBusConnection *connection) { + struct nl_msg msg; + static int seq = 0; + msg_usr_t attack; + + if (nl_ifw_read_msg(plugin->fd, NULL, &msg) <= 0) { + fprintf(stderr, "unable to read packet from netlink\n"); + return; + } + + attack.timestamp_sec = msg.timestamp_sec; + strncpy(attack.indev_name, msg.indev_name, IFNAMSIZ); + strncpy(attack.prefix, (char *) msg.prefix, PREFSIZ); + attack.sensor = 0; + attack.protocol = msg.ip.protocol; + attack.s_addr = msg.ip.saddr; + switch (msg.ip.protocol) { + case IPPROTO_TCP: + attack.d_port = msg.h.th.dest; + break; + case IPPROTO_UDP: + attack.d_port = msg.h.uh.dest; + break; + default: + attack.d_port = 0; + break; + } + attack.icmp_type = 0; + + process_attack(plugin, connection, seq++, &attack); +} + +#endif + +static void process_attack(plugin_t *plugin, DBusConnection *connection, int seq, msg_usr_t *attack) { + ifw_t *ifw = (ifw_t *) plugin->priv; + report_list_cell_t *report; + + if (black_list_find(&ifw->blacklist, attack->s_addr) || + white_list_find(&ifw->whitelist, attack->s_addr) || + report_list_find(&ifw->reports, attack->s_addr, 0)) { + struct in_addr addr; + addr.s_addr = attack->s_addr; + fprintf(stderr, "skipping known address: %s\n", inet_ntoa(addr)); + return; + } + + if (!strcmp(attack->indev_name, "lo")) { + fprintf(stderr, "skipping loopback interface\n"); + return; + } + + report = report_list_add(&ifw->reports, seq, attack); + if (report) { + if (ifw->mode == IFW_MODE_AUTO) { + /* add ip address in ipset blacklist */ + ifw_dbus_apply_report_verdict(connection, ifw, report, 1); + } + + /* notify the attack to frontends */ + ifw_dbus_notify_attack(connection, report); + } + + black_list_print(&ifw->blacklist); + white_list_print(&ifw->whitelist); + report_list_print(&ifw->reports); +} + +static DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *message, plugin_t *plugin) { + ifw_t *ifw = (ifw_t *) plugin->priv; + if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "GetMode")) { + return ifw_dbus_get_mode(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "SetMode")) { + return ifw_dbus_set_mode(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "GetReports")) { + return ifw_dbus_get_reports(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "GetBlacklist")) { + return ifw_dbus_get_blacklist(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "SetBlacklistVerdict")) { + return ifw_dbus_set_blacklist_verdict(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "UnBlacklist")) { + return ifw_dbus_unblacklist(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "GetWhitelist")) { + return ifw_dbus_get_whitelist(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "Whitelist")) { + return ifw_dbus_whitelist(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "UnWhitelist")) { + return ifw_dbus_unwhitelist(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "ClearProcessedReports")) { + return ifw_dbus_clear_processed_reports(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "SendAlertAck")) { + return ifw_dbus_send_alert_ack(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "SendManageRequest")) { + return ifw_dbus_send_manage_request(connection, message, ifw); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +plugin_t ifw_plugin = { + .name = "Interactive Firewall", + .path = IFW_DBUS_PATH, + .init = init, +#ifdef IFW_FAKE + .handle_incoming = handle_fake, +#else + .handle_incoming = handle_incoming, +#endif + .handle_message = handle_message, + .deinit = deinit, +}; diff --git a/src/plugins/ifw/report_list.c b/src/plugins/ifw/report_list.c new file mode 100644 index 0000000..c21df7e --- /dev/null +++ b/src/plugins/ifw/report_list.c @@ -0,0 +1,81 @@ +#include "report_list.h" + +#include <stdio.h> +#include <stdlib.h> + +void report_list_init(report_list_t *list) { + INIT_LIST_HEAD(list); +} + +report_list_cell_t *report_list_add(report_list_t *list, int seq, msg_usr_t *attack) { + report_list_cell_t *cell; + + cell = malloc(sizeof(report_list_cell_t)); + if (!cell) { + fprintf(stderr, "unable to alloc enough memory for report list cell, skipping\n"); + return NULL; + } + cell->seq = seq; + cell->info = *attack; + cell->processed = 0; + INIT_LIST_HEAD(&cell->list); + list_add_tail(&cell->list, list); + + return cell; +} + +report_list_cell_t *report_list_find(report_list_t *list, u_int32_t addr, int include_processed) { + struct list_head *entry; + + __list_for_each(entry, list) { + report_list_cell_t *cell; + cell = list_entry(entry, report_list_cell_t, list); + if (cell->info.s_addr == addr && include_processed || !cell->processed) { + return cell; + } + } + + return NULL; +} + +report_list_cell_t *report_list_find_seq(report_list_t *list, int seq) { + struct list_head *entry; + + __list_for_each(entry, list) { + report_list_cell_t *cell; + cell = list_entry(entry, report_list_cell_t, list); + if (cell->seq == seq) { + return cell; + } + } + + return NULL; +} + +void report_list_remove(report_list_cell_t *cell) { + list_del(&cell->list); + free(cell); +} + +void report_list_clear_processed(report_list_t *list) { + report_list_cell_t *cell, *n; + + list_for_each_entry_safe(cell, n, list, list) { + if (cell->processed) { + report_list_remove(cell); + } + } +} + +void report_list_print(report_list_t *list) { + struct list_head *entry; + + printf("* report list {\n"); + __list_for_each(entry, list) { + report_list_cell_t *cell; + cell = list_entry(entry, report_list_cell_t, list); + printf("%d,\n", cell->seq); + } + printf("} report list *\n"); +} + diff --git a/src/plugins/ifw/report_list.h b/src/plugins/ifw/report_list.h new file mode 100644 index 0000000..626afed --- /dev/null +++ b/src/plugins/ifw/report_list.h @@ -0,0 +1,24 @@ +#ifndef REPORT_LIST_H +#define REPORT_LIST_H + +#include "list.h" +typedef struct list_head report_list_t; + +#include "ifw.h" + +typedef struct { + struct list_head list; + int seq; + msg_usr_t info; + char processed; +} report_list_cell_t; + +void report_list_init(report_list_t *list); +report_list_cell_t *report_list_add(report_list_t *list, int seq, msg_usr_t *attack); +report_list_cell_t *report_list_find(report_list_t *list, u_int32_t addr, int include_processed); +report_list_cell_t *report_list_find_seq(report_list_t *list, int seq); +void report_list_remove(report_list_cell_t *cell); +void report_list_clear_processed(report_list_t *list); +void report_list_print(report_list_t *list); + +#endif /* REPORT_LIST_H */ diff --git a/src/plugins/ifw/white_list.c b/src/plugins/ifw/white_list.c new file mode 100644 index 0000000..4318abc --- /dev/null +++ b/src/plugins/ifw/white_list.c @@ -0,0 +1,119 @@ +#include "white_list.h" +#include "ipset.h" +#include "ifw.h" + +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/socket.h> + +void white_list_init(white_list_t *list) { + INIT_LIST_HEAD(list); +} + +void white_list_add(white_list_t *list, u_int32_t addr) { + white_list_cell_t *cell; + + cell = malloc(sizeof(white_list_cell_t)); + if (!cell) { + fprintf(stderr, "unable to alloc enough memory for white list cell, skipping\n"); + return; + } + cell->addr = addr; + INIT_LIST_HEAD(&cell->list); + list_add_tail(&cell->list, list); + + ipset_whitelist_add(cell->addr); + white_list_save(list, IFW_WHITELIST_FILENAME); +} + +white_list_cell_t *white_list_find(white_list_t *list, u_int32_t addr) { + struct list_head *entry; + + __list_for_each(entry, list) { + white_list_cell_t *cell; + cell = list_entry(entry, white_list_cell_t, list); + if (cell->addr == addr) { + return cell; + } + } + + return NULL; +} + +void white_list_remove(white_list_t *list, u_int32_t addr) { + white_list_cell_t *cell, *n, *prev; + + ipset_whitelist_remove(addr); + + prev = NULL; + list_for_each_entry_safe(cell, n, list, list) { + if (prev) + free(prev); + if (cell->addr == addr) { + list_del(&cell->list); + prev = cell; + } else { + prev = NULL; + } + } + if (prev) + free(prev); + + white_list_save(list, IFW_WHITELIST_FILENAME); +} + + +void white_list_print(white_list_t *list) { + struct list_head *entry; + + printf("* white list {\n"); + __list_for_each(entry, list) { + white_list_cell_t *cell; + struct in_addr addr; + cell = list_entry(entry, white_list_cell_t, list); + addr.s_addr = cell->addr; + printf("%s,\n", inet_ntoa(addr)); + } + printf("} white list *\n"); +} + +void white_list_load(white_list_t *list, const char *filepath) { + FILE *fp; + + fp = fopen(filepath, "r"); + if (fp) { + char addr_str[16]; + struct in_addr addr; + while (fscanf(fp, "%15s\n", addr_str) > 0) { + if (inet_aton(addr_str, &addr)) { + white_list_add(list, addr.s_addr); + printf("adding IP address in white list: %s\n", addr_str); + } else { + fprintf(stderr, "unable to parse IP address in white list: %s\n", addr_str); + } + } + } else { + fprintf(stderr, "unable to open white list file\n"); + } +} + +void white_list_save(white_list_t *list, const char *filepath) { + FILE *fp; + struct list_head *entry; + + fp = fopen(filepath, "w+"); + if (fp) { + __list_for_each(entry, list) { + white_list_cell_t *cell; + struct in_addr addr; + cell = list_entry(entry, white_list_cell_t, list); + addr.s_addr = cell->addr; + fprintf(fp, "%15s\n", inet_ntoa(addr)); + printf("adding IP address in white list: %s\n", inet_ntoa(addr)); + } + } else { + fprintf(stderr, "unable to write white list file\n"); + } +} diff --git a/src/plugins/ifw/white_list.h b/src/plugins/ifw/white_list.h new file mode 100644 index 0000000..564f5ee --- /dev/null +++ b/src/plugins/ifw/white_list.h @@ -0,0 +1,23 @@ +#ifndef WHITE_LIST_H +#define WHITE_LIST_H + +#include "list.h" + +#include <sys/types.h> + +typedef struct list_head white_list_t; + +typedef struct { + struct list_head list; + u_int32_t addr; +} white_list_cell_t; + +void white_list_init(white_list_t *list); +void white_list_add(white_list_t *list, u_int32_t addr); +white_list_cell_t *white_list_find(white_list_t *list, u_int32_t addr); +void white_list_remove(white_list_t *list, u_int32_t addr); +void white_list_print(white_list_t *list); +void white_list_load(white_list_t *list, const char *filepath); +void white_list_save(white_list_t *list, const char *filepath); + +#endif /* WHITE_LIST_H */ diff --git a/src/plugins/wireless/plugin.c b/src/plugins/wireless/plugin.c new file mode 100644 index 0000000..1ddeb95 --- /dev/null +++ b/src/plugins/wireless/plugin.c @@ -0,0 +1,148 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <dirent.h> + +#include "wpa_ctrl.h" +#include "plugin.h" + +#define WIRELESS_PATH PLUGIN_ROOT_PATH "/wireless" +#define WIRELESS_INTERFACE PLUGIN_ROOT_INTF ".wireless" + +static int init(plugin_t *plugin, DBusConnection *connection); +static void deinit(plugin_t *plugin, DBusConnection *connection); +static void handle_incoming(plugin_t *plugin, DBusConnection *connection); +static DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *message, plugin_t *plugin); +static DBusHandlerResult select_network(DBusConnection *connection, DBusMessage *message, plugin_t *plugin); +static DBusHandlerResult wpa_supplicant_request(DBusConnection *connection, DBusMessage *message, plugin_t *plugin, char *cmd); + +static int init(plugin_t *plugin, DBusConnection *connection) { + struct wpa_ctrl *ctrl_conn = NULL; + const char *ctrl_iface_dir = "/var/run/wpa_supplicant"; + DIR *dir = opendir(ctrl_iface_dir); + + if (dir) { + struct dirent *dent; + while ((dent = readdir(dir))) { + char *cfile; + int flen; + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + printf("Selected interface '%s'\n", + dent->d_name); + flen = strlen(ctrl_iface_dir) + strlen(dent->d_name) + 2; + cfile = malloc(flen); + if (cfile == NULL) + return -1; + snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, dent->d_name); + ctrl_conn = wpa_ctrl_open(cfile); + free(cfile); + break; + } + closedir(dir); + } + + if (ctrl_conn != NULL) { + plugin->fd = wpa_ctrl_get_fd(ctrl_conn); + } else { + /* do not fail, the plugin will try to re-init when needed */ + plugin->fd = -1; + } + plugin->priv = (void *) ctrl_conn; + return 0; +} + +static void deinit(plugin_t *plugin, DBusConnection *connection) { + if (plugin->fd > 0) + close(plugin->fd); +} + +static void handle_incoming(plugin_t *plugin, DBusConnection *connection) { + struct wpa_ctrl *ctrl_conn = (struct wpa_ctrl *) plugin->priv; + char buf[2048]; + size_t len; + wpa_ctrl_recv(ctrl_conn, buf, &len); + buf[len] = '\0'; + printf("received event: %s\n", buf); +} + +static DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *message, plugin_t *plugin) { + if (dbus_message_is_method_call(message, WIRELESS_INTERFACE, "ScanResults")) { + return wpa_supplicant_request(connection, message, plugin, "SCAN_RESULTS"); + } else if (dbus_message_is_method_call(message, WIRELESS_INTERFACE, "ListNetworks")) { + return wpa_supplicant_request(connection, message, plugin, "LIST_NETWORKS"); + } else if (dbus_message_is_method_call(message, WIRELESS_INTERFACE, "SelectNetwork")) { + return select_network(connection, message, plugin); + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult select_network(DBusConnection *connection, DBusMessage *message, plugin_t *plugin) { + DBusError error; + u_int32_t net; + char cmd[32]; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &net, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "select_network(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %u", net); + return wpa_supplicant_request(connection, message, plugin, cmd); +} + +static DBusHandlerResult wpa_supplicant_request(DBusConnection *connection, DBusMessage *message, plugin_t *plugin, char *cmd) { + struct wpa_ctrl *ctrl_conn = (struct wpa_ctrl *) plugin->priv; + DBusMessage *reply; + char buf[2048]; + size_t len; + int ret = -1; + + len = sizeof(buf) - 1; + if (ctrl_conn) { + ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, &len, NULL); + } + if (ret == -1) { + fprintf(stderr, "connection to wpa_supplicant daemon lost, reconnecting\n"); + init(plugin, connection); + ctrl_conn = (struct wpa_ctrl *) plugin->priv; + if (ctrl_conn) { + ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, &len, NULL); + } + } + if (ret != 0) { + fprintf(stderr, "unable to request command\n"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + buf[len] = '\0'; + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, + DBUS_TYPE_STRING, + buf, + DBUS_TYPE_INVALID); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +plugin_t wpa_supplicant_plugin = { + .name = "Wireless", + .path = WIRELESS_PATH, + .init = init, + .handle_incoming = handle_incoming, + .handle_message = handle_message, + .deinit = deinit, +}; diff --git a/src/plugins/wireless/wpa_ctrl.c b/src/plugins/wireless/wpa_ctrl.c new file mode 100644 index 0000000..1c88e69 --- /dev/null +++ b/src/plugins/wireless/wpa_ctrl.c @@ -0,0 +1,238 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/time.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <sys/socket.h> +#include <sys/un.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "wpa_ctrl.h" +#ifdef CONFIG_NATIVE_WINDOWS +#include "common.h" +#endif /* CONFIG_NATIVE_WINDOWS */ + + +/** + * struct wpa_ctrl - Internal structure for control interface library + * + * This structure is used by the wpa_supplicant/hostapd control interface + * library to store internal data. Programs using the library should not touch + * this data directly. They can only use the pointer to the data structure as + * an identifier for the control interface connection and use this as one of + * the arguments for most of the control interface library functions. + */ +struct wpa_ctrl { + int s; +#ifdef CONFIG_CTRL_IFACE_UDP + struct sockaddr_in local; + struct sockaddr_in dest; +#else /* CONFIG_CTRL_IFACE_UDP */ + struct sockaddr_un local; + struct sockaddr_un dest; +#endif /* CONFIG_CTRL_IFACE_UDP */ +}; + + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + struct wpa_ctrl *ctrl; +#ifndef CONFIG_CTRL_IFACE_UDP + static int counter = 0; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + ctrl = malloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + memset(ctrl, 0, sizeof(*ctrl)); + +#ifdef CONFIG_CTRL_IFACE_UDP + ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + perror("socket"); + free(ctrl); + return NULL; + } + + ctrl->local.sin_family = AF_INET; + ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local)) < 0) { + close(ctrl->s); + free(ctrl); + return NULL; + } + + ctrl->dest.sin_family = AF_INET; + ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); + ctrl->dest.sin_port = htons(9877); + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, + sizeof(ctrl->dest)) < 0) { + perror("connect"); + close(ctrl->s); + free(ctrl); + return NULL; + } +#else /* CONFIG_CTRL_IFACE_UDP */ + ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + free(ctrl); + return NULL; + } + + ctrl->local.sun_family = AF_UNIX; + snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), + "/tmp/wpa_ctrl_%d-%d", getpid(), counter++); + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local)) < 0) { + close(ctrl->s); + free(ctrl); + return NULL; + } + + ctrl->dest.sun_family = AF_UNIX; + snprintf(ctrl->dest.sun_path, sizeof(ctrl->dest.sun_path), "%s", + ctrl_path); + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, + sizeof(ctrl->dest)) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + free(ctrl); + return NULL; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ + + return ctrl; +} + + +void wpa_ctrl_close(struct wpa_ctrl *ctrl) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + unlink(ctrl->local.sun_path); +#endif /* CONFIG_CTRL_IFACE_UDP */ + close(ctrl->s); + free(ctrl); +} + + +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)) +{ + struct timeval tv; + int res; + fd_set rfds; + + if (send(ctrl->s, cmd, cmd_len, 0) < 0) + return -1; + + for (;;) { + tv.tv_sec = 2; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + if (FD_ISSET(ctrl->s, &rfds)) { + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + if (res > 0 && reply[0] == '<') { + /* This is an unsolicited message from + * wpa_supplicant, not the reply to the + * request. Use msg_cb to report this to the + * caller. */ + if (msg_cb) { + /* Make sure the message is nul + * terminated. */ + if ((size_t) res == *reply_len) + res = (*reply_len) - 1; + reply[res] = '\0'; + msg_cb(reply, res); + } + continue; + } + *reply_len = res; + break; + } else { + return -2; + } + } + return 0; +} + + +static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach) +{ + char buf[10]; + int ret; + size_t len = 10; + + ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6, + buf, &len, NULL); + if (ret < 0) + return ret; + if (len == 3 && memcmp(buf, "OK\n", 3) == 0) + return 0; + return -1; +} + + +int wpa_ctrl_attach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 1); +} + + +int wpa_ctrl_detach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 0); +} + + +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) +{ + int res; + + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + *reply_len = res; + return 0; +} + + +int wpa_ctrl_pending(struct wpa_ctrl *ctrl) +{ + struct timeval tv; + int res; + fd_set rfds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + return FD_ISSET(ctrl->s, &rfds); +} + + +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) +{ + return ctrl->s; +} diff --git a/src/plugins/wireless/wpa_ctrl.h b/src/plugins/wireless/wpa_ctrl.h new file mode 100644 index 0000000..964f7ab --- /dev/null +++ b/src/plugins/wireless/wpa_ctrl.h @@ -0,0 +1,179 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_CTRL_H +#define WPA_CTRL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* wpa_supplicant control interface - fixed message prefixes */ + +/** Interactive request for identity/password/pin */ +#define WPA_CTRL_REQ "CTRL-REQ-" + +/** Response to identity/password/pin request */ +#define WPA_CTRL_RSP "CTRL-RSP-" + +/* Event messages with fixed prefix */ +/** Authentication completed successfully and data connection enabled */ +#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED " +/** Disconnected, data connection is not available */ +#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " +/** wpa_supplicant is exiting */ +#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " +/** Password change was completed successfully */ +#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED " +/** EAP-Request/Notification received */ +#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION " +/** EAP authentication started (EAP-Request/Identity received) */ +#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED " +/** EAP method selected */ +#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD " +/** EAP authentication completed successfully */ +#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " +/** EAP authentication failed (EAP-Failure received) */ +#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " + + +/* wpa_supplicant/hostapd control interface access */ + +/** + * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. + * Returns: Pointer to abstract control interface data or %NULL on failure + * + * This function is used to open a control interface to wpa_supplicant/hostapd. + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path + * is configured in wpa_supplicant/hostapd and other programs using the control + * interface need to use matching path configuration. + */ +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); + + +/** + * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * + * This function is used to close a control interface. + */ +void wpa_ctrl_close(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * @cmd: Command; usually, ASCII text, e.g., "PING" + * @cmd_len: Length of the cmd in bytes + * @reply: Buffer for the response + * @reply_len: Reply buffer length + * @msg_cb: Callback function for unsolicited messages or %NULL if not used + * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout + * + * This function is used to send commands to wpa_supplicant/hostapd. Received + * response will be written to reply and reply_len is set to the actual length + * of the reply. This function will block for up to two seconds while waiting + * for the reply. If unsolicited messages are received, the blocking time may + * be longer. + * + * msg_cb can be used to register a callback function that will be called for + * unsolicited messages received while waiting for the command response. These + * messages may be received if wpa_ctrl_request() is called at the same time as + * wpa_supplicant/hostapd is sending such a message. This can happen only if + * the program has used wpa_ctrl_attach() to register itself as a monitor for + * event messages. Alternatively to msg_cb, programs can register two control + * interface connections and use one of them for commands and the other one for + * receiving event messages, in other words, call wpa_ctrl_attach() only for + * the control interface connection that will be used for event messages. + */ +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)); + + +/** + * wpa_ctrl_attach - Register as an event monitor for the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function registers the control interface connection as a monitor for + * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the + * control interface connection starts receiving event messages that can be + * read with wpa_ctrl_recv(). + */ +int wpa_ctrl_attach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_detach - Unregister event monitor from the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function unregisters the control interface connection as a monitor for + * wpa_supplicant/hostapd events, i.e., cancels the registration done with + * wpa_ctrl_attach(). + */ +int wpa_ctrl_detach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_recv - Receive a pending control interface message + * @ctrl: Control interface data from wpa_ctrl_open() + * @reply: Buffer for the message data + * @reply_len: Length of the reply buffer + * Returns: 0 on success, -1 on failure + * + * This function will receive a pending control interface message. This + * function will block if no messages are available. The received response will + * be written to reply and reply_len is set to the actual length of the reply. + * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach() + * must have been used to register the control interface as an event monitor. + */ +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len); + + +/** + * wpa_ctrl_pending - Check whether there are pending event messages + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: Non-zero if there are pending messages + * + * This function will check whether there are any pending control interface + * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is + * only used for event messages, i.e., wpa_ctrl_attach() must have been used to + * register the control interface as an event monitor. + */ +int wpa_ctrl_pending(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_get_fd - Get file descriptor used by the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: File descriptor used for the connection + * + * This function can be used to get the file descriptor that is used for the + * control interface connection. The returned value can be used, e.g., with + * select() while waiting for multiple events. + * + * The returned file descriptor must not be used directly for sending or + * receiving packets; instead, the library functions wpa_ctrl_request() and + * wpa_ctrl_recv() must be used for this. + */ +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); + +#ifdef __cplusplus +} +#endif + +#endif /* WPA_CTRL_H */ |