Moved generic RADIUS protocol support to a dedicated libradius
[strongswan.git] / src / pluto / foodgroups.c
1 /* Implement policy groups-style control files (aka "foodgroups")
2 * Copyright (C) 2002 D. Hugh Redelmeier.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 */
14
15 #include <string.h>
16 #include <stdio.h>
17 #include <stddef.h>
18 #include <stdlib.h>
19 #include <sys/queue.h>
20
21 #include <freeswan.h>
22
23 #include "constants.h"
24 #include "defs.h"
25 #include "connections.h"
26 #include "foodgroups.h"
27 #include "kernel.h"
28 #include "lex.h"
29 #include "log.h"
30 #include "whack.h"
31
32
33 /* Food group config files are found in directory fg_path */
34
35 #ifndef POLICYGROUPSDIR
36 #define POLICYGROUPSDIR IPSEC_CONFDIR "/ipsec.d/policies"
37 #endif
38
39 const char *policygroups_dir = POLICYGROUPSDIR;
40
41 static char *fg_path = NULL;
42 static size_t fg_path_space = 0;
43
44
45 /* Groups is a list of connections that are policy groups.
46 * The list is updated as group connections are added and deleted.
47 */
48
49 struct fg_groups {
50 struct fg_groups *next;
51 connection_t *connection;
52 };
53
54 static struct fg_groups *groups = NULL;
55
56
57 /* Targets is a list of pairs: subnet and its policy group.
58 * This list is bulk-updated on whack --listen and
59 * incrementally updated when group connections are deleted.
60 *
61 * It is ordered by source subnet, and if those are equal, then target subnet.
62 * A subnet is compared by comparing the network, and if those are equal,
63 * comparing the mask.
64 */
65
66 struct fg_targets {
67 struct fg_targets *next;
68 struct fg_groups *group;
69 ip_subnet subnet;
70 char *name; /* name of instance of group conn */
71 };
72
73 static struct fg_targets *targets = NULL;
74
75 struct fg_targets *new_targets;
76
77 /* ipcmp compares the two ip_address values a and b.
78 * It returns -1, 0, or +1 if a is, respectively,
79 * less than, equal to, or greater than b.
80 */
81 static int ipcmp(ip_address *a, ip_address *b)
82 {
83 if (addrtypeof(a) != addrtypeof(b))
84 {
85 return addrtypeof(a) < addrtypeof(b)? -1 : 1;
86 }
87 else if (sameaddr(a, b))
88 {
89 return 0;
90 }
91 else
92 {
93 const struct sockaddr *sa = sockaddrof(a)
94 , *sb = sockaddrof(b);
95
96 passert(addrtypeof(a) == AF_INET); /* not yet implemented IPv6 version :-( */
97 return (ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr)
98 < ntohl(((const struct sockaddr_in *)sb)->sin_addr.s_addr))
99 ? -1 : 1;
100 }
101 }
102
103 /* subnetcmp compares the two ip_subnet values a and b.
104 * It returns -1, 0, or +1 if a is, respectively,
105 * less than, equal to, or greater than b.
106 */
107 static int subnetcmp(const ip_subnet *a, const ip_subnet *b)
108 {
109 ip_address neta, maska, netb, maskb;
110 int r;
111
112 networkof(a, &neta);
113 maskof(a, &maska);
114 networkof(b, &netb);
115 maskof(b, &maskb);
116 r = ipcmp(&neta, &netb);
117 if (r == 0)
118 r = ipcmp(&maska, &maskb);
119 return r;
120 }
121
122 static void read_foodgroup(struct fg_groups *g)
123 {
124 const char *fgn = g->connection->name;
125 const ip_subnet *lsn = &g->connection->spd.this.client;
126 size_t plen = strlen(policygroups_dir) + 1 + strlen(fgn) + 1;
127 struct file_lex_position flp_space;
128
129 if (plen > fg_path_space)
130 {
131 free(fg_path);
132 fg_path_space = plen + 10;
133 fg_path = malloc(fg_path_space);
134 }
135 snprintf(fg_path, fg_path_space, "%s/%s", policygroups_dir, fgn);
136 if (!lexopen(&flp_space, fg_path, TRUE))
137 {
138 DBG(DBG_CONTROL, DBG_log("no group file \"%s\"", fg_path));
139 }
140 else
141 {
142 plog("loading group \"%s\"", fg_path);
143 for (;;)
144 {
145 switch (flp->bdry)
146 {
147 case B_none:
148 {
149 /* !!! this test is not sufficient for distinguishing address families.
150 * We need a notation to specify that a FQDN is to be resolved to IPv6.
151 */
152 const struct af_info *afi = strchr(tok, ':') == NULL
153 ? &af_inet4_info: &af_inet6_info;
154 ip_subnet sn;
155 err_t ugh;
156
157 if (strchr(tok, '/') == NULL)
158 {
159 /* no /, so treat as /32 or V6 equivalent */
160 ip_address t;
161
162 ugh = ttoaddr(tok, 0, afi->af, &t);
163 if (ugh == NULL)
164 ugh = addrtosubnet(&t, &sn);
165 }
166 else
167 {
168 ugh = ttosubnet(tok, 0, afi->af, &sn);
169 }
170
171 if (ugh != NULL)
172 {
173 loglog(RC_LOG_SERIOUS, "\"%s\" line %d: %s \"%s\""
174 , flp->filename, flp->lino, ugh, tok);
175 }
176 else if (afi->af != AF_INET)
177 {
178 loglog(RC_LOG_SERIOUS
179 , "\"%s\" line %d: unsupported Address Family \"%s\""
180 , flp->filename, flp->lino, tok);
181 }
182 else
183 {
184 /* Find where new entry ought to go in new_targets. */
185 struct fg_targets **pp;
186 int r;
187
188 for (pp = &new_targets; ; pp = &(*pp)->next)
189 {
190 if (*pp == NULL)
191 {
192 r = -1; /* end of list is infinite */
193 break;
194 }
195 r = subnetcmp(lsn, &(*pp)->group->connection->spd.this.client);
196 if (r == 0)
197 r = subnetcmp(&sn, &(*pp)->subnet);
198 if (r <= 0)
199 break;
200 }
201
202 if (r == 0)
203 {
204 char source[SUBNETTOT_BUF];
205
206 subnettot(lsn, 0, source, sizeof(source));
207 loglog(RC_LOG_SERIOUS
208 , "\"%s\" line %d: subnet \"%s\", source %s, already \"%s\""
209 , flp->filename
210 , flp->lino
211 , tok
212 , source
213 , (*pp)->group->connection->name);
214 }
215 else
216 {
217 struct fg_targets *f = malloc_thing(struct fg_targets);
218
219 f->next = *pp;
220 f->group = g;
221 f->subnet = sn;
222 f->name = NULL;
223 *pp = f;
224 }
225 }
226 }
227 (void)shift(); /* next */
228 continue;
229
230 case B_record:
231 flp->bdry = B_none; /* eat the Record Boundary */
232 (void)shift(); /* get real first token */
233 continue;
234
235 case B_file:
236 break; /* done */
237 }
238 break; /* if we reach here, out of loop */
239 }
240 lexclose();
241 }
242 }
243
244 static void free_targets(void)
245 {
246 while (targets != NULL)
247 {
248 struct fg_targets *t = targets;
249
250 targets = t->next;
251 free(t->name);
252 free(t);
253 }
254 }
255
256 void load_groups(void)
257 {
258 passert(new_targets == NULL);
259
260 /* for each group, add config file targets into new_targets */
261 {
262 struct fg_groups *g;
263
264 for (g = groups; g != NULL; g = g->next)
265 if (oriented(*g->connection))
266 read_foodgroup(g);
267 }
268
269 /* dump new_targets */
270 DBG(DBG_CONTROL,
271 {
272 struct fg_targets *t;
273
274 for (t = new_targets; t != NULL; t = t->next)
275 {
276 char asource[SUBNETTOT_BUF];
277 char atarget[SUBNETTOT_BUF];
278
279 subnettot(&t->group->connection->spd.this.client
280 , 0, asource, sizeof(asource));
281 subnettot(&t->subnet, 0, atarget, sizeof(atarget));
282 DBG_log("%s->%s %s"
283 , asource, atarget
284 , t->group->connection->name);
285 }
286 });
287
288 /* determine and deal with differences between targets and new_targets.
289 * structured like a merge.
290 */
291 {
292 struct fg_targets *op = targets
293 , *np = new_targets;
294
295 while (op != NULL && np != NULL)
296 {
297 int r = subnetcmp(&op->group->connection->spd.this.client
298 , &np->group->connection->spd.this.client);
299
300 if (r == 0)
301 r = subnetcmp(&op->subnet, &np->subnet);
302
303 if (r == 0 && op->group == np->group)
304 {
305 /* unchanged -- steal name & skip over */
306 np->name = op->name;
307 op->name = NULL;
308 op = op->next;
309 np = np->next;
310 }
311 else
312 {
313 /* note: following cases overlap! */
314 if (r <= 0)
315 {
316 remove_group_instance(op->group->connection, op->name);
317 op = op->next;
318 }
319 if (r >= 0)
320 {
321 np->name = add_group_instance(np->group->connection, &np->subnet);
322 np = np->next;
323 }
324 }
325 }
326 for (; op != NULL; op = op->next)
327 remove_group_instance(op->group->connection, op->name);
328 for (; np != NULL; np = np->next)
329 np->name = add_group_instance(np->group->connection, &np->subnet);
330
331 /* update: new_targets replaces targets */
332 free_targets();
333 targets = new_targets;
334 new_targets = NULL;
335 }
336 }
337
338
339 void add_group(connection_t *c)
340 {
341 struct fg_groups *g = malloc_thing(struct fg_groups);
342
343 g->next = groups;
344 groups = g;
345
346 g->connection = c;
347 }
348
349 static struct fg_groups *find_group(const connection_t *c)
350 {
351 struct fg_groups *g;
352
353 for (g = groups; g != NULL && g->connection != c; g = g->next)
354 ;
355 return g;
356 }
357
358 void route_group(connection_t *c)
359 {
360 /* it makes no sense to route a connection that is ISAKMP-only */
361 if (!NEVER_NEGOTIATE(c->policy) && !HAS_IPSEC_POLICY(c->policy))
362 {
363 loglog(RC_ROUTE, "cannot route an ISAKMP-only group connection");
364 }
365 else
366 {
367 struct fg_groups *g = find_group(c);
368 struct fg_targets *t;
369
370 passert(g != NULL);
371 g->connection->policy |= POLICY_GROUTED;
372 for (t = targets; t != NULL; t = t->next)
373 {
374 if (t->group == g)
375 {
376 connection_t *ci = con_by_name(t->name, FALSE);
377
378 if (ci != NULL)
379 {
380 set_cur_connection(ci);
381 if (!trap_connection(ci))
382 whack_log(RC_ROUTE, "could not route");
383 set_cur_connection(c);
384 }
385 }
386 }
387 }
388 }
389
390 void unroute_group(connection_t *c)
391 {
392 struct fg_groups *g = find_group(c);
393 struct fg_targets *t;
394
395 passert(g != NULL);
396 g->connection->policy &= ~POLICY_GROUTED;
397 for (t = targets; t != NULL; t = t->next)
398 {
399 if (t->group == g)
400 {
401 connection_t *ci = con_by_name(t->name, FALSE);
402
403 if (ci != NULL)
404 {
405 set_cur_connection(ci);
406 unroute_connection(ci);
407 set_cur_connection(c);
408 }
409 }
410 }
411 }
412
413 void delete_group(const connection_t *c)
414 {
415 struct fg_groups *g;
416
417 /* find and remove from groups */
418 {
419 struct fg_groups **pp;
420
421 for (pp = &groups; (g = *pp)->connection != c; pp = &(*pp)->next)
422 ;
423
424 *pp = g->next;
425 }
426
427 /* find and remove from targets */
428 {
429 struct fg_targets **pp;
430
431 for (pp = &targets; *pp != NULL; )
432 {
433 struct fg_targets *t = *pp;
434
435 if (t->group == g)
436 {
437 *pp = t->next;
438 remove_group_instance(t->group->connection, t->name);
439 free(t);
440 /* pp is ready for next iteration */
441 }
442 else
443 {
444 pp = &t->next;
445 }
446 }
447 }
448
449 free(g);
450 }