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