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