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