]> git.ipfire.org Git - ipfire-2.x.git/blob - src/misc-progs/captivectrl.c
captive: Fix another typo in captivectrl
[ipfire-2.x.git] / src / misc-progs / captivectrl.c
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
6 #define _BSD_SOURCE
7 #define _XOPEN_SOURCE
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <time.h>
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
25 typedef struct client {
26 char etheraddr[STRING_SIZE];
27 char ipaddr[STRING_SIZE];
28 time_t time_start;
29 int expires;
30
31 struct client* next;
32 } client_t;
33
34 static time_t parse_time(const char* s) {
35 int t = 0;
36
37 if (sscanf(s, "%d", &t) == 1) {
38 return (time_t)t;
39 }
40
41 return -1;
42 }
43
44 static char* format_time(const time_t* t) {
45 char buffer[STRING_SIZE];
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);
52
53 return strdup(buffer);
54 }
55
56 static 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
74 // Skip all commented lines
75 if (*line == '#')
76 continue;
77
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
119 // Expire duration
120 case 4:
121 client_curr->expires = atoi(word);
122 break;
123
124 default:
125 break;
126 }
127 }
128 }
129
130 if (f)
131 fclose(f);
132
133 return client_first;
134 }
135
136 static 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
145 static int setup_dns_filters() {
146 const char* protos[] = { "udp", "tcp", NULL };
147
148 // Limits the number of DNS requests to 3 kByte/s
149 // A burst of 1MB is permitted at the start
150 const char* limiter = "-m hashlimit --hashlimit-name dns-filter"
151 " --hashlimit-mode srcip --hashlimit-upto 3kb/sec --hashlimit-burst 1024kb";
152
153 char command[STRING_SIZE];
154
155 const char** proto = protos;
156 while (*proto) {
157 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -p %s"
158 " --dport 53 %s -j RETURN", *proto, limiter);
159
160 int r = safe_system(command);
161 if (r)
162 return r;
163
164 proto++;
165 }
166
167 return 0;
168 }
169
170 static int add_client_rules(const client_t* clients) {
171 char command[STRING_SIZE];
172 char match[STRING_SIZE];
173
174 while (clients) {
175 size_t len = 0;
176
177 if (*clients->ipaddr && clients->expires > 0) {
178 len += snprintf(match + len, sizeof(match) - len,
179 "-s %s", clients->ipaddr);
180 }
181
182 len += snprintf(match + len, sizeof(match) - len,
183 " -m mac --mac-source %s", clients->etheraddr);
184
185 if (clients->expires > 0) {
186 time_t expires = clients->time_start + clients->expires;
187
188 char* time_start = format_time(&clients->time_start);
189 char* time_end = format_time(&expires);
190
191 len += snprintf(match + len, sizeof(match) - len,
192 " -m time --datestart %s --datestop %s",
193 time_start, time_end);
194
195 free(time_start);
196 free(time_end);
197 }
198
199 // filter
200 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
201 " %s -j RETURN", match);
202 safe_system(command);
203
204 // nat
205 snprintf(command, sizeof(command), IPTABLES " -t nat -A CAPTIVE_PORTAL"
206 " %s -j RETURN", match);
207 safe_system(command);
208
209 // Move on to the next client
210 clients = clients->next;
211 }
212
213 return 0;
214 }
215
216 static char* get_key(struct keyvalue* settings, char* key) {
217 char value[STRING_SIZE];
218
219 if (!findkey(settings, key, value))
220 return NULL;
221
222 return strdup(value);
223 }
224
225 static int add_interface_rule(const char* intf, int allow_webif_access) {
226 int r;
227 char command[STRING_SIZE];
228
229 if ((intf == NULL) || (strlen(intf) == 0)) {
230 fprintf(stderr, "Empty interface given\n");
231 return -1;
232 }
233
234 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL -i %s"
235 " -j CAPTIVE_PORTAL_CLIENTS", intf);
236 r = safe_system(command);
237 if (r)
238 return r;
239
240 #if 0
241 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL -o %s"
242 " -j CAPTIVE_PORTAL_CLIENTS", intf);
243 r = safe_system(command);
244 if (r)
245 return r;
246 #endif
247
248 if (allow_webif_access) {
249 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
250 " -i %s -p tcp --dport 444 -j RETURN", intf);
251 r = safe_system(command);
252 if (r)
253 return r;
254 }
255
256 // Redirect all unauthenticated clients
257 snprintf(command, sizeof(command), IPTABLES " -t nat -A CAPTIVE_PORTAL -i %s"
258 " -p tcp --dport %d -j REDIRECT --to-ports %d", intf, HTTP_PORT, REDIRECT_PORT);
259 r = safe_system(command);
260 if (r)
261 return r;
262
263 // Allow access to captive portal site
264 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
265 " -i %s -p tcp --dport %d -j RETURN", intf, REDIRECT_PORT);
266 r = safe_system(command);
267 if (r)
268 return r;
269
270 return 0;
271 }
272
273 static int add_interface_rules(struct keyvalue* captive_portal_settings, struct keyvalue* ethernet_settings) {
274 const char* intf;
275 char* setting;
276 int r = 0;
277
278 setting = get_key(captive_portal_settings, "ENABLE_GREEN");
279 if (setting && (strcmp(setting, "on") == 0)) {
280 free(setting);
281
282 intf = get_key(ethernet_settings, "GREEN_DEV");
283 r = add_interface_rule(intf, /* allow webif access from green */ 1);
284 if (r)
285 return r;
286 }
287
288 setting = get_key(captive_portal_settings, "ENABLE_BLUE");
289 if (setting && (strcmp(setting, "on") == 0)) {
290 free(setting);
291
292 intf = get_key(ethernet_settings, "BLUE_DEV");
293 r = add_interface_rule(intf, /* do not allow webif access */ 0);
294 if (r)
295 return r;
296 }
297
298 // Always pass DNS packets through all firewall rules
299 r = setup_dns_filters();
300 if (r)
301 return r;
302
303 // Add the last rule
304 r = safe_system(IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -j DROP");
305 if (r)
306 return r;
307
308 return r;
309 }
310
311 int main(int argc, char** argv) {
312 int r = 0;
313 char* intf = NULL;
314 client_t* clients = NULL;
315
316 struct keyvalue* captive_portal_settings = NULL;
317 struct keyvalue* ethernet_settings = NULL;
318
319 if (!(initsetuid()))
320 exit(2);
321
322 ethernet_settings = initkeyvalues();
323 if (!readkeyvalues(ethernet_settings, ETHERNET_SETTINGS)) {
324 fprintf(stderr, "Could not read %s\n", ETHERNET_SETTINGS);
325 r = 1;
326 goto END;
327 }
328
329 captive_portal_settings = initkeyvalues();
330 if (!readkeyvalues(captive_portal_settings, CAPTIVE_PORTAL_SETTINGS)) {
331 fprintf(stderr, "Could not read %s\n", CAPTIVE_PORTAL_SETTINGS);
332 r = 1;
333 goto END;
334 }
335
336 clients = read_clients(CLIENTS);
337
338 // Clean up all old rules
339 flush_chains();
340
341 // Add all client rules
342 r = add_client_rules(clients);
343 if (r)
344 goto END;
345
346 // Add all interface rules
347 r = add_interface_rules(captive_portal_settings, ethernet_settings);
348 if (r)
349 goto END;
350
351 END:
352 while (clients) {
353 client_t* head = clients;
354 clients = clients->next;
355
356 free(head);
357 }
358
359 if (ethernet_settings)
360 freekeyvalues(ethernet_settings);
361
362 if (captive_portal_settings)
363 freekeyvalues(captive_portal_settings);
364
365 if (intf)
366 free(intf);
367
368 return r;
369 }