]> git.ipfire.org Git - ipfire-2.x.git/blob - src/misc-progs/captivectrl.c
captivectrl: Allow empty IP addresses
[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 client_curr = (client_t*)malloc(sizeof(client_t));
75 memset(client_curr, 0, sizeof(client_t));
76
77 if (client_first == NULL)
78 client_first = client_curr;
79 else
80 client_last->next = client_curr;
81 client_last = client_curr;
82
83 unsigned int count = 0;
84 char* lineptr = line;
85 while (1) {
86 if (!*lineptr)
87 break;
88
89 char* word = lineptr;
90 while (*lineptr != '\0') {
91 if (*lineptr == ',') {
92 *lineptr = '\0';
93 lineptr++;
94 break;
95 }
96 lineptr++;
97 }
98
99 switch (count++) {
100 // Ethernet address
101 case 1:
102 strcpy(client_curr->etheraddr, word);
103 break;
104
105 // IP address
106 case 2:
107 strcpy(client_curr->ipaddr, word);
108 break;
109
110 // Start time
111 case 3:
112 client_curr->time_start = parse_time(word);
113 break;
114
115 // Expire duration
116 case 4:
117 client_curr->expires = atoi(word);
118 break;
119
120 default:
121 break;
122 }
123 }
124 }
125
126 if (f)
127 fclose(f);
128
129 return client_first;
130 }
131
132 static void flush_chains() {
133 // filter
134 safe_system(IPTABLES " -F CAPTIVE_PORTAL");
135 safe_system(IPTABLES " -F CAPTIVE_PORTAL_CLIENTS");
136
137 // nat
138 safe_system(IPTABLES " -t nat -F CAPTIVE_PORTAL");
139 }
140
141 static int add_client_rules(const client_t* clients) {
142 char command[STRING_SIZE];
143 char match[STRING_SIZE];
144
145 while (clients) {
146 time_t expires = clients->time_start + clients->expires;
147
148 char* time_start = format_time(&clients->time_start);
149 char* time_end = format_time(&expires);
150
151 size_t len = 0;
152
153 if (*clients->ipaddr) {
154 len += snprintf(match + len, sizeof(match) - len,
155 "-s %s", clients->ipaddr);
156 }
157
158 len += snprintf(match + len, sizeof(match) - len,
159 " -m mac --mac-source %s -m time --datestart %s --datestop %s",
160 clients->etheraddr, time_start, time_end);
161
162 free(time_start);
163 free(time_end);
164
165 // filter
166 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
167 " %s -j RETURN", match);
168 safe_system(command);
169
170 // nat
171 snprintf(command, sizeof(command), IPTABLES " -t nat -A CAPTIVE_PORTAL"
172 " %s -j RETURN", match);
173 safe_system(command);
174
175 // Move on to the next client
176 clients = clients->next;
177 }
178
179 return 0;
180 }
181
182 static char* get_key(struct keyvalue* settings, char* key) {
183 char value[STRING_SIZE];
184
185 if (!findkey(settings, key, value))
186 return NULL;
187
188 return strdup(value);
189 }
190
191 static int add_interface_rule(const char* intf, int allow_webif_access) {
192 int r;
193 char command[STRING_SIZE];
194
195 if ((intf == NULL) || (strlen(intf) == 0)) {
196 fprintf(stderr, "Empty interface given\n");
197 return -1;
198 }
199
200 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL -i %s"
201 " -j CAPTIVE_PORTAL_CLIENTS", intf);
202 r = safe_system(command);
203 if (r)
204 return r;
205
206 #if 0
207 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL -o %s"
208 " -j CAPTIVE_PORTAL_CLIENTS", intf);
209 r = safe_system(command);
210 if (r)
211 return r;
212 #endif
213
214 if (allow_webif_access) {
215 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
216 " -i %s -p tcp --dport 444 -j RETURN", intf);
217 r = safe_system(command);
218 if (r)
219 return r;
220 }
221
222 // Redirect all unauthenticated clients
223 snprintf(command, sizeof(command), IPTABLES " -t nat -A CAPTIVE_PORTAL -i %s"
224 " -p tcp --dport %d -j REDIRECT --to-ports %d", intf, HTTP_PORT, REDIRECT_PORT);
225 r = safe_system(command);
226 if (r)
227 return r;
228
229 return 0;
230 }
231
232 static int add_interface_rules(struct keyvalue* captive_portal_settings, struct keyvalue* ethernet_settings) {
233 const char* intf;
234 char* setting;
235 int r = 0;
236
237 setting = get_key(captive_portal_settings, "ENABLE_GREEN");
238 if (setting && (strcmp(setting, "on") == 0)) {
239 free(setting);
240
241 intf = get_key(ethernet_settings, "GREEN_DEV");
242 r = add_interface_rule(intf, /* allow webif access from green */ 1);
243 if (r)
244 return r;
245 }
246
247 setting = get_key(captive_portal_settings, "ENABLE_BLUE");
248 if (setting && (strcmp(setting, "on") == 0)) {
249 free(setting);
250
251 intf = get_key(ethernet_settings, "BLUE_DEV");
252 r = add_interface_rule(intf, /* do not allow webif access */ 0);
253 if (r)
254 return r;
255 }
256
257 // Always pass DNS packets through all firewall rules
258 r = safe_system(IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -p udp --dport 53 -j RETURN");
259 if (r)
260 return r;
261
262 r = safe_system(IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -p tcp --dport 53 -j RETURN");
263 if (r)
264 return r;
265
266 char command[STRING_SIZE];
267 snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
268 " -p tcp --dport %d -j RETURN", REDIRECT_PORT);
269 r = safe_system(command);
270 if (r)
271 return r;
272
273 // Add the last rule
274 r = safe_system(IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -j DROP");
275 if (r)
276 return r;
277
278 return r;
279 }
280
281 int main(int argc, char** argv) {
282 int r = 0;
283 char* intf = NULL;
284 client_t* clients = NULL;
285
286 if (!(initsetuid()))
287 exit(2);
288
289 struct keyvalue* ethernet_settings = initkeyvalues();
290 if (!readkeyvalues(ethernet_settings, ETHERNET_SETTINGS)) {
291 fprintf(stderr, "Could not read %s\n", ETHERNET_SETTINGS);
292 r = 1;
293 goto END;
294 }
295
296 struct keyvalue* captive_portal_settings = initkeyvalues();
297 if (!readkeyvalues(captive_portal_settings, CAPTIVE_PORTAL_SETTINGS)) {
298 fprintf(stderr, "Could not read %s\n", CAPTIVE_PORTAL_SETTINGS);
299 r = 1;
300 goto END;
301 }
302
303 clients = read_clients(CLIENTS);
304
305 // Clean up all old rules
306 flush_chains();
307
308 // Add all client rules
309 r = add_client_rules(clients);
310 if (r)
311 goto END;
312
313 // Add all interface rules
314 r = add_interface_rules(captive_portal_settings, ethernet_settings);
315 if (r)
316 goto END;
317
318 END:
319 while (clients) {
320 client_t* head = clients;
321 clients = clients->next;
322
323 free(head);
324 }
325
326 if (ethernet_settings)
327 freekeyvalues(ethernet_settings);
328
329 if (captive_portal_settings)
330 freekeyvalues(captive_portal_settings);
331
332 if (intf)
333 free(intf);
334
335 return r;
336 }