]> git.ipfire.org Git - ipfire-2.x.git/blame - src/misc-progs/captivectrl.c
captivectrl: Skip all lines that start with #
[ipfire-2.x.git] / src / misc-progs / captivectrl.c
CommitLineData
ee40139d
MT
1/* This file is part of the IPFire Firewall.
2*
3* This program is distributed under the terms of the GNU General Public
4* Licence. See the file COPYING for details. */
5
7ef66b61
MT
6#define _BSD_SOURCE
7#define _XOPEN_SOURCE
8
ee40139d
MT
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
7ef66b61 12#include <time.h>
ee40139d
MT
13
14#include "libsmooth.h"
15#include "setuid.h"
16
17#define CAPTIVE_PORTAL_SETTINGS CONFIG_ROOT "/captive/settings"
18#define ETHERNET_SETTINGS CONFIG_ROOT "/ethernet/settings"
19
20#define CLIENTS CONFIG_ROOT "/captive/clients"
21#define IPTABLES "/sbin/iptables --wait"
22#define HTTP_PORT 80
23#define REDIRECT_PORT 1013
24
25typedef struct client {
26 char etheraddr[STRING_SIZE];
27 char ipaddr[STRING_SIZE];
7ef66b61
MT
28 time_t time_start;
29 int expires;
ee40139d
MT
30
31 struct client* next;
32} client_t;
33
7ef66b61
MT
34static time_t parse_time(const char* s) {
35 int t = 0;
ee40139d 36
7ef66b61
MT
37 if (sscanf(s, "%d", &t) == 1) {
38 return (time_t)t;
ee40139d
MT
39 }
40
41 return -1;
42}
43
7ef66b61 44static char* format_time(const time_t* t) {
ee40139d 45 char buffer[STRING_SIZE];
7ef66b61
MT
46
47 struct tm* tm = gmtime(t);
48 if (tm == NULL)
49 return NULL;
50
51 strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M", tm);
ee40139d
MT
52
53 return strdup(buffer);
54}
55
56static client_t* read_clients(char* filename) {
57 FILE* f = NULL;
58
59 if (!(f = fopen(filename, "r"))) {
60 fprintf(stderr, "Could not open configuration file: %s\n", filename);
61 return NULL;;
62 }
63
64 char line[STRING_SIZE];
65
66 client_t* client_first = NULL;
67 client_t* client_last = NULL;
68 client_t* client_curr;
69
70 while ((fgets(line, STRING_SIZE, f) != NULL)) {
71 if (line[strlen(line) - 1] == '\n')
72 line[strlen(line) - 1] = '\0';
73
76ece323
MT
74 // Skip all commented lines
75 if (*line == '#')
76 continue;
77
ee40139d
MT
78 client_curr = (client_t*)malloc(sizeof(client_t));
79 memset(client_curr, 0, sizeof(client_t));
80
81 if (client_first == NULL)
82 client_first = client_curr;
83 else
84 client_last->next = client_curr;
85 client_last = client_curr;
86
87 unsigned int count = 0;
88 char* lineptr = line;
89 while (1) {
90 if (!*lineptr)
91 break;
92
93 char* word = lineptr;
94 while (*lineptr != '\0') {
95 if (*lineptr == ',') {
96 *lineptr = '\0';
97 lineptr++;
98 break;
99 }
100 lineptr++;
101 }
102
103 switch (count++) {
104 // Ethernet address
105 case 1:
106 strcpy(client_curr->etheraddr, word);
107 break;
108
109 // IP address
110 case 2:
111 strcpy(client_curr->ipaddr, word);
112 break;
113
114 // Start time
115 case 3:
116 client_curr->time_start = parse_time(word);
117 break;
118
7ef66b61 119 // Expire duration
ee40139d 120 case 4:
7ef66b61 121 client_curr->expires = atoi(word);
ee40139d
MT
122 break;
123
124 default:
125 break;
126 }
127 }
128 }
129
130 if (f)
131 fclose(f);
132
133 return client_first;
134}
135
136static void flush_chains() {
137 // filter
138 safe_system(IPTABLES " -F CAPTIVE_PORTAL");
139 safe_system(IPTABLES " -F CAPTIVE_PORTAL_CLIENTS");
140
141 // nat
142 safe_system(IPTABLES " -t nat -F CAPTIVE_PORTAL");
143}
144
145static int add_client_rules(const client_t* clients) {
146 char command[STRING_SIZE];
147 char match[STRING_SIZE];
148
149 while (clients) {
5fbeaf13
MT
150 size_t len = 0;
151
0c24f0a9 152 if (*clients->ipaddr && clients->expires > 0) {
5fbeaf13
MT
153 len += snprintf(match + len, sizeof(match) - len,
154 "-s %s", clients->ipaddr);
155 }
156
157 len += snprintf(match + len, sizeof(match) - len,
0c24f0a9
MT
158 " -m mac --mac-source %s", clients->etheraddr);
159
160 if (clients->expires > 0) {
161 time_t expires = clients->time_start + clients->expires;
162
163 char* time_start = format_time(&clients->time_start);
164 char* time_end = format_time(&expires);
ee40139d 165
0c24f0a9 166 len += snprintf(match + len, sizeof(match) - len,
0d6a599a 167 " -m time --datestart %s --datestop %s",
0c24f0a9
MT
168 time_start, time_end);
169
170 free(time_start);
171 free(time_end);
172 }
ee40139d
MT
173
174 // filter
175 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
176 " %s -j RETURN", match);
177 safe_system(command);
178
179 // nat
180 snprintf(command, sizeof(command), IPTABLES " -t nat -A CAPTIVE_PORTAL"
181 " %s -j RETURN", match);
182 safe_system(command);
183
184 // Move on to the next client
185 clients = clients->next;
186 }
187
188 return 0;
189}
190
191static char* get_key(struct keyvalue* settings, char* key) {
192 char value[STRING_SIZE];
193
194 if (!findkey(settings, key, value))
195 return NULL;
196
197 return strdup(value);
198}
199
200static int add_interface_rule(const char* intf, int allow_webif_access) {
201 int r;
202 char command[STRING_SIZE];
203
204 if ((intf == NULL) || (strlen(intf) == 0)) {
205 fprintf(stderr, "Empty interface given\n");
206 return -1;
207 }
208
209 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL -i %s"
210 " -j CAPTIVE_PORTAL_CLIENTS", intf);
211 r = safe_system(command);
212 if (r)
213 return r;
214
215#if 0
216 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL -o %s"
217 " -j CAPTIVE_PORTAL_CLIENTS", intf);
218 r = safe_system(command);
219 if (r)
220 return r;
221#endif
222
223 if (allow_webif_access) {
224 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
225 " -i %s -p tcp --dport 444 -j RETURN", intf);
226 r = safe_system(command);
227 if (r)
228 return r;
229 }
230
231 // Redirect all unauthenticated clients
232 snprintf(command, sizeof(command), IPTABLES " -t nat -A CAPTIVE_PORTAL -i %s"
233 " -p tcp --dport %d -j REDIRECT --to-ports %d", intf, HTTP_PORT, REDIRECT_PORT);
234 r = safe_system(command);
235 if (r)
236 return r;
237
238 return 0;
239}
240
241static int add_interface_rules(struct keyvalue* captive_portal_settings, struct keyvalue* ethernet_settings) {
242 const char* intf;
243 char* setting;
244 int r = 0;
245
246 setting = get_key(captive_portal_settings, "ENABLE_GREEN");
247 if (setting && (strcmp(setting, "on") == 0)) {
248 free(setting);
249
250 intf = get_key(ethernet_settings, "GREEN_DEV");
251 r = add_interface_rule(intf, /* allow webif access from green */ 1);
252 if (r)
253 return r;
254 }
255
256 setting = get_key(captive_portal_settings, "ENABLE_BLUE");
257 if (setting && (strcmp(setting, "on") == 0)) {
258 free(setting);
259
260 intf = get_key(ethernet_settings, "BLUE_DEV");
261 r = add_interface_rule(intf, /* do not allow webif access */ 0);
262 if (r)
263 return r;
264 }
265
266 // Always pass DNS packets through all firewall rules
267 r = safe_system(IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -p udp --dport 53 -j RETURN");
268 if (r)
269 return r;
270
271 r = safe_system(IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -p tcp --dport 53 -j RETURN");
272 if (r)
273 return r;
274
275 char command[STRING_SIZE];
276 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
277 " -p tcp --dport %d -j RETURN", REDIRECT_PORT);
278 r = safe_system(command);
279 if (r)
280 return r;
281
282 // Add the last rule
283 r = safe_system(IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -j DROP");
284 if (r)
285 return r;
286
287 return r;
288}
289
290int main(int argc, char** argv) {
291 int r = 0;
292 char* intf = NULL;
293 client_t* clients = NULL;
294
cec16b82
MT
295 struct keyvalue* captive_portal_settings = NULL;
296 struct keyvalue* ethernet_settings = NULL;
297
ee40139d
MT
298 if (!(initsetuid()))
299 exit(2);
300
cec16b82 301 ethernet_settings = initkeyvalues();
ee40139d
MT
302 if (!readkeyvalues(ethernet_settings, ETHERNET_SETTINGS)) {
303 fprintf(stderr, "Could not read %s\n", ETHERNET_SETTINGS);
304 r = 1;
305 goto END;
306 }
307
cec16b82 308 captive_portal_settings = initkeyvalues();
ee40139d
MT
309 if (!readkeyvalues(captive_portal_settings, CAPTIVE_PORTAL_SETTINGS)) {
310 fprintf(stderr, "Could not read %s\n", CAPTIVE_PORTAL_SETTINGS);
311 r = 1;
312 goto END;
313 }
314
315 clients = read_clients(CLIENTS);
316
317 // Clean up all old rules
318 flush_chains();
319
320 // Add all client rules
321 r = add_client_rules(clients);
322 if (r)
323 goto END;
324
325 // Add all interface rules
326 r = add_interface_rules(captive_portal_settings, ethernet_settings);
327 if (r)
328 goto END;
329
330END:
331 while (clients) {
332 client_t* head = clients;
333 clients = clients->next;
334
335 free(head);
336 }
337
338 if (ethernet_settings)
339 freekeyvalues(ethernet_settings);
340
341 if (captive_portal_settings)
342 freekeyvalues(captive_portal_settings);
343
344 if (intf)
345 free(intf);
346
347 return r;
348}