public inbox for development@lists.ipfire.org
 help / color / mirror / Atom feed
From: Alexander Marx <alexander.marx@ipfire.org>
To: development@lists.ipfire.org
Subject: [PATCH] Captive-Portal: add c-wrapper captivectrl
Date: Thu, 28 Jan 2016 14:24:53 +0100	[thread overview]
Message-ID: <1453987493-12458-1-git-send-email-alexander.marx@ipfire.org> (raw)

[-- Attachment #1: Type: text/plain, Size: 8264 bytes --]

This wrapper reads the captive settings and clients and sets the
firewall access rules. It is called every time the config changed or
everytime that a client changes. Also this wrapper is later called once
hourly to flush the chains and rebuild rules for actual clients.
Developed by Michael Tremer.

Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>
---
 src/misc-progs/captivectrl.c | 317 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 317 insertions(+)
 create mode 100644 src/misc-progs/captivectrl.c

diff --git a/src/misc-progs/captivectrl.c b/src/misc-progs/captivectrl.c
new file mode 100644
index 0000000..ed6668c
--- /dev/null
+++ b/src/misc-progs/captivectrl.c
@@ -0,0 +1,317 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libsmooth.h"
+#include "setuid.h"
+
+#define CAPTIVE_PORTAL_SETTINGS		CONFIG_ROOT "/captive/settings"
+#define ETHERNET_SETTINGS			CONFIG_ROOT "/ethernet/settings"
+
+#define CLIENTS						CONFIG_ROOT "/captive/clients"
+#define IPTABLES					"/sbin/iptables --wait"
+#define HTTP_PORT					80
+#define REDIRECT_PORT				1013
+
+typedef struct client {
+	char etheraddr[STRING_SIZE];
+	char ipaddr[STRING_SIZE];
+	int time_start;
+	int time_end;
+
+	struct client* next;
+} client_t;
+
+static int parse_time(const char* s) {
+	int hrs = 0;
+	int min = 0;
+
+	if (sscanf(s, "%d:%d", &hrs, &min) == 2) {
+		return (hrs * 60) + min;
+	}
+
+	return -1;
+}
+
+static char* format_time(int t) {
+	char buffer[STRING_SIZE];
+	snprintf(buffer, sizeof(buffer), "%02d:%02d", (t / 60), (t % 60));
+
+	return strdup(buffer);
+}
+
+static client_t* read_clients(char* filename) {
+	FILE* f = NULL;
+
+	if (!(f = fopen(filename, "r"))) {
+		fprintf(stderr, "Could not open configuration file: %s\n", filename);
+		return NULL;;
+	}
+
+	char line[STRING_SIZE];
+
+	client_t* client_first = NULL;
+	client_t* client_last = NULL;
+	client_t* client_curr;
+
+	while ((fgets(line, STRING_SIZE, f) != NULL)) {
+		if (line[strlen(line) - 1] == '\n')
+			line[strlen(line) - 1] = '\0';
+
+		client_curr = (client_t*)malloc(sizeof(client_t));
+		memset(client_curr, 0, sizeof(client_t));
+
+		if (client_first == NULL)
+			client_first = client_curr;
+		else
+			client_last->next = client_curr;
+		client_last = client_curr;
+
+		unsigned int count = 0;
+		char* lineptr = line;
+		while (1) {
+			if (!*lineptr)
+				break;
+
+			char* word = lineptr;
+			while (*lineptr != '\0') {
+				if (*lineptr == ',') {
+					*lineptr = '\0';
+					lineptr++;
+					break;
+				}
+				lineptr++;
+			}
+
+			switch (count++) {
+				// Ethernet address
+				case 1:
+					strcpy(client_curr->etheraddr, word);
+					break;
+
+				// IP address
+				case 2:
+					strcpy(client_curr->ipaddr, word);
+					break;
+
+				// Start time
+				case 3:
+					client_curr->time_start = parse_time(word);
+					break;
+
+				// End time
+				case 4:
+					client_curr->time_end = parse_time(word);
+					break;
+
+				default:
+					break;
+			}
+		}
+	}
+
+	if (f)
+		fclose(f);
+
+	return client_first;
+}
+
+static void flush_chains() {
+	// filter
+	safe_system(IPTABLES " -F CAPTIVE_PORTAL");
+	safe_system(IPTABLES " -F CAPTIVE_PORTAL_CLIENTS");
+
+	// nat
+	safe_system(IPTABLES " -t nat -F CAPTIVE_PORTAL");
+}
+
+static int add_client_rules(const client_t* clients) {
+	char command[STRING_SIZE];
+	char match[STRING_SIZE];
+
+	while (clients) {
+		char* time_start = format_time(clients->time_start);
+		char* time_end   = format_time(clients->time_end);
+
+		snprintf(match, sizeof(match), "-s %s -m mac --mac-source %s"
+			" -m time %s --timestart %s --timestop %s",
+			clients->ipaddr, clients->etheraddr,
+			(clients->time_start > clients->time_end) ? "--contiguous" : "",
+			time_start, time_end);
+
+		free(time_start);
+		free(time_end);
+
+		// filter
+		snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
+			" %s -j RETURN", match);
+		safe_system(command);
+
+		// nat
+		snprintf(command, sizeof(command), IPTABLES " -t nat -A CAPTIVE_PORTAL"
+			" %s -j RETURN", match);
+		safe_system(command);
+
+		// Move on to the next client
+		clients = clients->next;
+	}
+
+	return 0;
+}
+
+static char* get_key(struct keyvalue* settings, char* key) {
+	char value[STRING_SIZE];
+
+	if (!findkey(settings, key, value))
+		return NULL;
+
+	return strdup(value);
+}
+
+static int add_interface_rule(const char* intf, int allow_webif_access) {
+	int r;
+	char command[STRING_SIZE];
+
+	if ((intf == NULL) || (strlen(intf) == 0)) {
+		fprintf(stderr, "Empty interface given\n");
+		return -1;
+	}
+
+	snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL -i %s"
+		" -j CAPTIVE_PORTAL_CLIENTS", intf);
+	r = safe_system(command);
+	if (r)
+		return r;
+
+#if 0
+	snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL -o %s"
+		" -j CAPTIVE_PORTAL_CLIENTS", intf);
+	r = safe_system(command);
+	if (r)
+		return r;
+#endif
+
+	if (allow_webif_access) {
+		snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
+			" -i %s -p tcp --dport 444 -j RETURN", intf);
+		r = safe_system(command);
+		if (r)
+			return r;
+	}
+
+	// Redirect all unauthenticated clients
+	snprintf(command, sizeof(command), IPTABLES " -t nat -A CAPTIVE_PORTAL -i %s"
+		" -p tcp --dport %d -j REDIRECT --to-ports %d", intf, HTTP_PORT, REDIRECT_PORT);
+	r = safe_system(command);
+	if (r)
+		return r;
+
+	return 0;
+}
+
+static int add_interface_rules(struct keyvalue* captive_portal_settings, struct keyvalue* ethernet_settings) {
+	const char* intf;
+	char* setting;
+	int r = 0;
+
+	setting = get_key(captive_portal_settings, "ENABLE_GREEN");
+	if (setting && (strcmp(setting, "on") == 0)) {
+		free(setting);
+
+		intf = get_key(ethernet_settings, "GREEN_DEV");
+		r = add_interface_rule(intf, /* allow webif access from green */ 1);
+		if (r)
+			return r;
+	}
+
+	setting = get_key(captive_portal_settings, "ENABLE_BLUE");
+	if (setting && (strcmp(setting, "on") == 0)) {
+		free(setting);
+
+		intf = get_key(ethernet_settings, "BLUE_DEV");
+		r = add_interface_rule(intf, /* do not allow webif access */ 0);
+		if (r)
+			return r;
+	}
+
+	// Always pass DNS packets through all firewall rules
+	r = safe_system(IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -p udp --dport 53 -j RETURN");
+	if (r)
+		return r;
+
+	r = safe_system(IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -p tcp --dport 53 -j RETURN");
+	if (r)
+		return r;
+
+	char command[STRING_SIZE];
+	snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
+		" -p tcp --dport %d -j RETURN", REDIRECT_PORT);
+	r = safe_system(command);
+	if (r)
+		return r;
+
+	// Add the last rule
+	r = safe_system(IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -j DROP");
+	if (r)
+		return r;
+
+	return r;
+}
+
+int main(int argc, char** argv) {
+	int r = 0;
+	char* intf = NULL;
+	client_t* clients = NULL;
+
+	if (!(initsetuid()))
+		exit(2);
+
+	struct keyvalue* ethernet_settings = initkeyvalues();
+	if (!readkeyvalues(ethernet_settings, ETHERNET_SETTINGS)) {
+		fprintf(stderr, "Could not read %s\n", ETHERNET_SETTINGS);
+		r = 1;
+		goto END;
+	}
+
+	struct keyvalue* captive_portal_settings = initkeyvalues();
+	if (!readkeyvalues(captive_portal_settings, CAPTIVE_PORTAL_SETTINGS)) {
+		fprintf(stderr, "Could not read %s\n", CAPTIVE_PORTAL_SETTINGS);
+		r = 1;
+		goto END;
+	}
+
+	clients = read_clients(CLIENTS);
+
+	// Clean up all old rules
+	flush_chains();
+
+	// Add all client rules
+	r = add_client_rules(clients);
+	if (r)
+		goto END;
+
+	// Add all interface rules
+	r = add_interface_rules(captive_portal_settings, ethernet_settings);
+	if (r)
+		goto END;
+
+END:
+	while (clients) {
+		client_t* head = clients;
+		clients = clients->next;
+
+		free(head);
+	}
+
+	if (ethernet_settings)
+		freekeyvalues(ethernet_settings);
+
+	if (captive_portal_settings)
+		freekeyvalues(captive_portal_settings);
+
+	if (intf)
+		free(intf);
+
+	return r;
+}
-- 
1.9.1


                 reply	other threads:[~2016-01-28 13:24 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1453987493-12458-1-git-send-email-alexander.marx@ipfire.org \
    --to=alexander.marx@ipfire.org \
    --cc=development@lists.ipfire.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox