]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/commitdiff
Captive Portal: add c-wrapper captivectrl
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 28 Jan 2016 13:24:07 +0000 (14:24 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 22 Sep 2017 17:54:03 +0000 (18:54 +0100)
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.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Signed-off-by: Alexander Marx <alexander.marx@ipfire.org>
config/rootfiles/common/misc-progs
src/misc-progs/Makefile
src/misc-progs/captivectrl.c [new file with mode: 0644]

index f94f3195dfc1436a89b51e9f6fa25976254af9b1..789179513b1053547288759bf6e7889cbd133a77 100644 (file)
@@ -1,6 +1,7 @@
 usr/local/bin/addonctrl
 #usr/local/bin/applejuicectrl
 usr/local/bin/backupctrl
+usr/local/bin/captivectrl
 #usr/local/bin/clamavctrl
 usr/local/bin/collectdctrl
 usr/local/bin/ddnsctrl
index 7cc70b48211c66002d06934daf11a4f09218074f..c3329b13047e8f3e300399f5067caa23dc9e0e5f 100644 (file)
@@ -31,7 +31,8 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
        redctrl syslogdctrl extrahdctrl sambactrl upnpctrl \
        smartctrl clamavctrl addonctrl pakfire mpfirectrl wlanapctrl \
        setaliases urlfilterctrl updxlratorctrl fireinfoctrl rebuildroutes \
-       getconntracktable wirelessclient torctrl ddnsctrl unboundctrl
+       getconntracktable wirelessclient torctrl ddnsctrl unboundctrl \
+       captivectrl
 SUID_UPDX = updxsetperms
 
 OBJS = $(patsubst %,%.o,$(PROGS) $(SUID_PROGS))
diff --git a/src/misc-progs/captivectrl.c b/src/misc-progs/captivectrl.c
new file mode 100644 (file)
index 0000000..32a689a
--- /dev/null
@@ -0,0 +1,321 @@
+/* This file is part of the IPFire Firewall.
+*
+* This program is distributed under the terms of the GNU General Public
+* Licence.  See the file COPYING for details. */
+
+#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;
+}