./scripts/min-includes +n <files listed here>
[freeradius.git] / src / main / client.c
1 /*
2  * client.c     Read clients into memory.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  */
24
25 static const char rcsid[] = "$Id$";
26
27 #include <freeradius-devel/autoconf.h>
28
29 #include <sys/stat.h>
30
31 #ifdef HAVE_NETINET_IN_H
32 #       include <netinet/in.h>
33 #endif
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <netdb.h>
38 #include <ctype.h>
39 #include <fcntl.h>
40
41 #include <freeradius-devel/radiusd.h>
42 #include <freeradius-devel/rad_assert.h>
43
44 struct radclient_list {
45         /*
46          *      FIXME: One set of trees for IPv4, and another for IPv6?
47          */
48         rbtree_t        *trees[129]; /* for 0..128, inclusive. */
49         int             min_prefix;
50 #ifdef WITH_SNMP
51         rbtree_t        *num;   /* client numbers 0..N */
52         int             max;
53 #endif
54 };
55
56 /*
57  *      Callback for freeing a client.
58  */
59 void client_free(RADCLIENT *client)
60 {
61         free(client->longname);
62         free(client->secret);
63         free(client->shortname);
64         free(client->nastype);
65         free(client->login);
66         free(client->password);
67         
68         free(client);
69 }
70
71
72 /*
73  *      Callback for comparing two clients.
74  */
75 static int client_ipaddr_cmp(const void *one, const void *two)
76 {
77         const RADCLIENT *a = one;
78         const RADCLIENT *b = two;
79
80         if (a->ipaddr.af < b->ipaddr.af) return -1;
81         if (a->ipaddr.af > b->ipaddr.af) return +1;
82
83         rad_assert(a->prefix == b->prefix);
84
85         switch (a->ipaddr.af) {
86         case AF_INET:
87                 return memcmp(&a->ipaddr.ipaddr.ip4addr,
88                               &b->ipaddr.ipaddr.ip4addr,
89                               sizeof(a->ipaddr.ipaddr.ip4addr));
90                 break;
91
92         case AF_INET6:
93                 return memcmp(&a->ipaddr.ipaddr.ip6addr,
94                               &b->ipaddr.ipaddr.ip6addr,
95                               sizeof(a->ipaddr.ipaddr.ip6addr));
96                 break;
97
98         default:
99                 break;
100         }
101
102         /*
103          *      Something bad happened...
104          */
105         rad_assert("Internal sanity check failed");
106         return -1;
107 }
108
109 #ifdef WITH_SNMP
110 static int client_num_cmp(const void *one, const void *two)
111 {
112         const RADCLIENT *a = one;
113         const RADCLIENT *b = two;
114
115         return (a->number - b->number);
116 }
117 #endif
118
119 /*
120  *      Free a RADCLIENT list.
121  */
122 void clients_free(RADCLIENT_LIST *clients)
123 {
124         int i;
125
126         if (!clients) return;
127
128         for (i = 0; i <= 128; i++) {
129                 if (clients->trees[i]) rbtree_free(clients->trees[i]);
130                 clients->trees[i] = NULL;
131         }
132 #ifdef WITH_SNMP
133         if (clients->num) rbtree_free(clients->num);
134 #endif
135 }
136
137 /*
138  *      Return a new, initialized, set of clients.
139  */
140 RADCLIENT_LIST *clients_init(void)
141 {
142         RADCLIENT_LIST *clients = calloc(1, sizeof(RADCLIENT_LIST));
143
144         if (!clients) return NULL;
145
146         clients->min_prefix = 128;
147
148         return clients;
149 }
150
151
152 /*
153  *      Sanity check a client.
154  */
155 static int client_sane(RADCLIENT *client)
156 {
157         switch (client->ipaddr.af) {
158         case AF_INET:
159                 if (client->prefix > 32) {
160                         return 0;
161                 }
162
163                 /*
164                  *      Zero out the subnet bits.
165                  */
166                 if (client->prefix < 32) {
167                         uint32_t mask = ~0;
168
169                         mask <<= (32 - client->prefix);
170                         client->ipaddr.ipaddr.ip4addr.s_addr &= htonl(mask);
171                 }
172                 break;
173
174         case AF_INET6:
175                 if (client->prefix > 128) return 0;
176
177                 if (client->prefix < 128) {
178                         int i;
179                         uint32_t mask, *addr;
180
181                         addr = (uint32_t *) &client->ipaddr.ipaddr.ip6addr;
182
183                         for (i = client->prefix; i < 128; i += 32) {
184                                 mask = ~0;
185                                 mask <<= ((128 - i) & 0x1f);
186                                 addr[i / 32] &= mask;
187                         }
188                 }
189                 break;
190
191         default:
192                 return 0;
193         }
194
195         return 1;
196 }
197
198
199 /*
200  *      Add a client to the tree.
201  */
202 int client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
203 {
204         if (!clients || !client) {
205                 return 0;
206         }
207
208         if (client->prefix < 0) {
209                 return 0;
210         }
211
212         if (!client_sane(client)) return 0;
213
214         /*
215          *      Create a tree for it.
216          */
217         if (!clients->trees[client->prefix]) {
218                 clients->trees[client->prefix] = rbtree_create(client_ipaddr_cmp,
219                                                                client_free, 0);
220                 if (!clients->trees[client->prefix]) {
221                         return 0;
222                 }
223         }
224
225         /*
226          *      Duplicate?
227          */
228         if (!rbtree_insert(clients->trees[client->prefix], client)) {
229                 return 0;
230         }
231
232 #ifdef WITH_SNMP
233         if (!clients->num) rbtree_create(client_num_cmp, NULL, 0);
234
235         client->number = clients->max;
236         clients->max++;
237         if (clients->num) rbtree_insert(clients->num, client);
238 #endif
239
240         if (client->prefix < clients->min_prefix) {
241                 clients->min_prefix = client->prefix;
242         }
243
244         return 1;
245 }
246
247
248 /*
249  *      Find a client in the RADCLIENTS list by number.
250  *      This is a support function for the SNMP code.
251  */
252 RADCLIENT *client_findbynumber(const RADCLIENT_LIST *clients,
253                                int number)
254 {
255 #ifdef WITH_SNMP
256         if (!clients) return NULL;
257
258         if (clients->num) {
259                 RADCLIENT myclient;
260                 
261                 myclient.number = number;
262                 
263                 return rbtree_finddata(clients->num, &myclient);
264         }
265 #endif
266         return NULL;
267 }
268
269
270 /*
271  *      Find a client in the RADCLIENTS list.
272  */
273 RADCLIENT *client_find(const RADCLIENT_LIST *clients,
274                        const lrad_ipaddr_t *ipaddr)
275 {
276         int i, max_prefix;
277         RADCLIENT myclient;
278
279         if (!clients || !ipaddr) return NULL;
280
281         switch (ipaddr->af) {
282         case AF_INET:
283                 max_prefix = 32;
284                 break;
285
286         case AF_INET6:
287                 max_prefix = 128;
288                 break;
289
290         default :
291                 return NULL;
292         }
293
294         for (i = max_prefix; i >= clients->min_prefix; i--) {
295                 void *data;
296
297                 myclient.prefix = i;
298                 myclient.ipaddr = *ipaddr;
299                 client_sane(&myclient); /* clean up the ipaddress */
300
301                 if (!clients->trees[i]) continue;
302                 
303                 data = rbtree_finddata(clients->trees[i], &myclient);
304                 if (data) {
305                         return data;
306                 }
307         }
308
309         return NULL;
310 }
311
312
313 /*
314  *      Old wrapper for client_find
315  */
316 RADCLIENT *client_find_old(const lrad_ipaddr_t *ipaddr)
317 {
318         return client_find(mainconfig.clients, ipaddr);
319 }
320
321
322 /*
323  *      Find the name of a client (prefer short name).
324  */
325 const char *client_name(const RADCLIENT_LIST *clients,
326                         const lrad_ipaddr_t *ipaddr)
327 {
328         /* We don't call this unless we should know about the client. */
329         RADCLIENT *cl;
330         char host_ipaddr[128];
331
332         if ((cl = client_find(clients, ipaddr)) != NULL) {
333                 if (cl->shortname && cl->shortname[0])
334                         return cl->shortname;
335                 else
336                         return cl->longname;
337         }
338
339         /*
340          * this isn't normally reachable, but if a loggable event happens just
341          * after a client list change and a HUP, then we may not know this
342          * information any more.
343          *
344          * If you see lots of these, then there's something wrong.
345          */
346         radlog(L_ERR, "Trying to look up name of unknown client %s.\n",
347                ip_ntoh(ipaddr, host_ipaddr, sizeof(host_ipaddr)));
348
349         return "UNKNOWN-CLIENT";
350 }
351
352 const char *client_name_old(const lrad_ipaddr_t *ipaddr)
353 {
354         return client_name(mainconfig.clients, ipaddr);
355 }
356
357 static const CONF_PARSER client_config[] = {
358         { "secret",  PW_TYPE_STRING_PTR, 
359           offsetof(RADCLIENT, secret), 0, NULL },
360         { "shortname",  PW_TYPE_STRING_PTR, 
361           offsetof(RADCLIENT, shortname), 0, NULL },
362         { "nastype",  PW_TYPE_STRING_PTR, 
363           offsetof(RADCLIENT, nastype), 0, NULL },
364         { "login",  PW_TYPE_STRING_PTR, 
365           offsetof(RADCLIENT, login), 0, NULL },
366         { "password",  PW_TYPE_STRING_PTR, 
367           offsetof(RADCLIENT, password), 0, NULL },
368
369         { NULL, -1, 0, NULL, NULL }
370 };
371
372
373 /*
374  *      Create the linked list of clients from the new configuration
375  *      type.  This way we don't have to change too much in the other
376  *      source-files.
377  */
378 RADCLIENT_LIST *clients_parse_section(const char *filename,
379                                       CONF_SECTION *section)
380 {
381         CONF_SECTION    *cs;
382         RADCLIENT       *c;
383         char            *hostnm, *prefix_ptr = NULL;
384         const char      *name2;
385         RADCLIENT_LIST  *clients;
386
387         /*
388          *      Be forgiving.  If there's already a clients, return
389          *      it.  Otherwise create a new one.
390          */
391         clients = cf_data_find(section, "clients");
392         if (clients) return clients;
393
394         clients = clients_init();
395         if (!clients) return NULL;
396
397         /*
398          *      Associate the clients structure with the section, where
399          *      it will be freed once the section is freed.
400          */
401         if (cf_data_add(section, "clients", clients, clients_free) < 0) {
402                 radlog(L_ERR, "%s[%d]: Failed to associate clients with section %s",
403                        filename, cf_section_lineno(section),
404                        cf_section_name1(section));
405                 clients_free(clients);
406                 return NULL;
407         }
408
409         for (cs = cf_subsection_find_next(section, NULL, "client");
410              cs != NULL;
411              cs = cf_subsection_find_next(section, cs, "client")) {
412                 name2 = cf_section_name2(cs);
413                 if (!name2) {
414                         radlog(L_CONS|L_ERR, "%s[%d]: Missing client name",
415                                filename, cf_section_lineno(cs));
416                         return NULL;
417                 }
418                 /*
419                  * Check the lengths, we don't want any core dumps
420                  */
421                 hostnm = name2;
422                 prefix_ptr = strchr(hostnm, '/');
423
424                 /*
425                  * The size is fine.. Let's create the buffer
426                  */
427                 c = rad_malloc(sizeof(RADCLIENT));
428                 memset(c, 0, sizeof(RADCLIENT));
429
430                 if (cf_section_parse(cs, c, client_config) < 0) {
431                         radlog(L_CONS|L_ERR, "%s[%d]: Error parsing client section.",
432                                filename, cf_section_lineno(cs));
433                         return NULL;
434                 }
435
436                 /*
437                  * Look for prefixes.
438                  */
439                 c->prefix = -1;
440                 if (prefix_ptr) {
441                         c->prefix = atoi(prefix_ptr + 1);
442                         if ((c->prefix < 0) || (c->prefix > 128)) {
443                                 radlog(L_ERR, "%s[%d]: Invalid Prefix value '%s' for IP.",
444                                                 filename, cf_section_lineno(cs), prefix_ptr + 1);
445                                 return NULL;
446                         }
447                         /* Replace '/' with '\0' */
448                         *prefix_ptr = '\0';
449                 }
450
451                 /*
452                  * Always get the numeric representation of IP
453                  */
454                 if (ip_hton(hostnm, AF_UNSPEC, &c->ipaddr) < 0) {
455                         radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s: %s",
456                                filename, cf_section_lineno(cs),
457                                hostnm, librad_errstr);
458                         return NULL;
459                 } else {
460                         char buffer[256];
461                         ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
462                         c->longname = strdup(buffer);
463                 }
464
465                 /*
466                  *      This makes later life easier.
467                  */
468                 if (!c->shortname) c->shortname = strdup(c->longname);
469
470                 if (c->prefix < 0) switch (c->ipaddr.af) {
471                 case AF_INET:
472                         c->prefix = 32;
473                         break;
474                 case AF_INET6:
475                         c->prefix = 128;
476                         break;
477                 default:
478                         break;
479                 }
480
481                 /*
482                  *      FIXME: Add the client as data via cf_data_add,
483                  *      for migration issues.
484                  */
485
486                 if (!client_add(clients, c)) {
487                         radlog(L_CONS|L_ERR, "%s[%d]: Failed to add client %s",
488                                filename, cf_section_lineno(cs), hostnm);
489                         client_free(c);
490                         return NULL;
491                 }
492         }
493
494         return clients;
495 }