8ebf3fe2d29dcc0d899fabdece3b7d60c29e0bfe
[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) rbtree_create(client_num_cmp, NULL, 0);
207
208         client->number = clients->max;
209         clients->max++;
210         if (clients->num) rbtree_insert(clients->num, client);
211 #endif
212
213         if (client->prefix < clients->min_prefix) {
214                 clients->min_prefix = client->prefix;
215         }
216
217         return 1;
218 }
219
220
221 /*
222  *      Find a client in the RADCLIENTS list by number.
223  *      This is a support function for the SNMP code.
224  */
225 RADCLIENT *client_findbynumber(const RADCLIENT_LIST *clients,
226                                int number)
227 {
228 #ifdef WITH_SNMP
229         if (!clients) return NULL;
230
231         if (clients->num) {
232                 RADCLIENT myclient;
233                 
234                 myclient.number = number;
235                 
236                 return rbtree_finddata(clients->num, &myclient);
237         }
238 #endif
239         return NULL;
240 }
241
242
243 /*
244  *      Find a client in the RADCLIENTS list.
245  */
246 RADCLIENT *client_find(const RADCLIENT_LIST *clients,
247                        const lrad_ipaddr_t *ipaddr)
248 {
249         int i, max_prefix;
250         RADCLIENT myclient;
251
252         if (!clients || !ipaddr) return NULL;
253
254         switch (ipaddr->af) {
255         case AF_INET:
256                 max_prefix = 32;
257                 break;
258
259         case AF_INET6:
260                 max_prefix = 128;
261                 break;
262
263         default :
264                 return NULL;
265         }
266
267         for (i = max_prefix; i >= clients->min_prefix; i--) {
268                 void *data;
269
270                 myclient.prefix = i;
271                 myclient.ipaddr = *ipaddr;
272                 client_sane(&myclient); /* clean up the ipaddress */
273
274                 if (!clients->trees[i]) continue;
275                 
276                 data = rbtree_finddata(clients->trees[i], &myclient);
277                 if (data) {
278                         return data;
279                 }
280         }
281
282         return NULL;
283 }
284
285
286 /*
287  *      Old wrapper for client_find
288  */
289 RADCLIENT *client_find_old(const lrad_ipaddr_t *ipaddr)
290 {
291         return client_find(mainconfig.clients, ipaddr);
292 }
293
294
295 /*
296  *      Find the name of a client (prefer short name).
297  */
298 const char *client_name(const RADCLIENT_LIST *clients,
299                         const lrad_ipaddr_t *ipaddr)
300 {
301         /* We don't call this unless we should know about the client. */
302         RADCLIENT *cl;
303         char host_ipaddr[128];
304
305         if ((cl = client_find(clients, ipaddr)) != NULL) {
306                 if (cl->shortname && cl->shortname[0])
307                         return cl->shortname;
308                 else
309                         return cl->longname;
310         }
311
312         /*
313          * this isn't normally reachable, but if a loggable event happens just
314          * after a client list change and a HUP, then we may not know this
315          * information any more.
316          *
317          * If you see lots of these, then there's something wrong.
318          */
319         radlog(L_ERR, "Trying to look up name of unknown client %s.\n",
320                ip_ntoh(ipaddr, host_ipaddr, sizeof(host_ipaddr)));
321
322         return "UNKNOWN-CLIENT";
323 }
324
325 const char *client_name_old(const lrad_ipaddr_t *ipaddr)
326 {
327         return client_name(mainconfig.clients, ipaddr);
328 }
329
330 static const CONF_PARSER client_config[] = {
331         { "secret",  PW_TYPE_STRING_PTR, 
332           offsetof(RADCLIENT, secret), 0, NULL },
333         { "shortname",  PW_TYPE_STRING_PTR, 
334           offsetof(RADCLIENT, shortname), 0, NULL },
335         { "nastype",  PW_TYPE_STRING_PTR, 
336           offsetof(RADCLIENT, nastype), 0, NULL },
337         { "login",  PW_TYPE_STRING_PTR, 
338           offsetof(RADCLIENT, login), 0, NULL },
339         { "password",  PW_TYPE_STRING_PTR, 
340           offsetof(RADCLIENT, password), 0, NULL },
341
342         { NULL, -1, 0, NULL, NULL }
343 };
344
345
346 /*
347  *      Create the linked list of clients from the new configuration
348  *      type.  This way we don't have to change too much in the other
349  *      source-files.
350  */
351 RADCLIENT_LIST *clients_parse_section(const char *filename,
352                                       CONF_SECTION *section)
353 {
354         CONF_SECTION    *cs;
355         RADCLIENT       *c;
356         char            *hostnm, *prefix_ptr = NULL;
357         const char      *name2;
358         RADCLIENT_LIST  *clients;
359
360         /*
361          *      Be forgiving.  If there's already a clients, return
362          *      it.  Otherwise create a new one.
363          */
364         clients = cf_data_find(section, "clients");
365         if (clients) return clients;
366
367         clients = clients_init();
368         if (!clients) return NULL;
369
370         /*
371          *      Associate the clients structure with the section, where
372          *      it will be freed once the section is freed.
373          */
374         if (cf_data_add(section, "clients", clients, clients_free) < 0) {
375                 radlog(L_ERR, "%s[%d]: Failed to associate clients with section %s",
376                        filename, cf_section_lineno(section),
377                        cf_section_name1(section));
378                 clients_free(clients);
379                 return NULL;
380         }
381
382         for (cs = cf_subsection_find_next(section, NULL, "client");
383              cs != NULL;
384              cs = cf_subsection_find_next(section, cs, "client")) {
385                 name2 = cf_section_name2(cs);
386                 if (!name2) {
387                         radlog(L_CONS|L_ERR, "%s[%d]: Missing client name",
388                                filename, cf_section_lineno(cs));
389                         return NULL;
390                 }
391                 /*
392                  * Check the lengths, we don't want any core dumps
393                  */
394                 hostnm = name2;
395                 prefix_ptr = strchr(hostnm, '/');
396
397                 /*
398                  * The size is fine.. Let's create the buffer
399                  */
400                 c = rad_malloc(sizeof(*c));
401                 memset(c, 0, sizeof(*c));
402
403 #ifdef WITH_SNMP
404                 c->auth = rad_malloc(sizeof(*c->auth));
405                 memset(c->auth, 0, sizeof(*c->auth));
406
407                 c->acct = rad_malloc(sizeof(*c->acct));
408                 memset(c->acct, 0, sizeof(*c->acct));
409 #endif
410
411                 if (cf_section_parse(cs, c, client_config) < 0) {
412                         radlog(L_CONS|L_ERR, "%s[%d]: Error parsing client section.",
413                                filename, cf_section_lineno(cs));
414                         return NULL;
415                 }
416
417                 /*
418                  * Look for prefixes.
419                  */
420                 c->prefix = -1;
421                 if (prefix_ptr) {
422                         c->prefix = atoi(prefix_ptr + 1);
423                         if ((c->prefix < 0) || (c->prefix > 128)) {
424                                 radlog(L_ERR, "%s[%d]: Invalid Prefix value '%s' for IP.",
425                                                 filename, cf_section_lineno(cs), prefix_ptr + 1);
426                                 return NULL;
427                         }
428                         /* Replace '/' with '\0' */
429                         *prefix_ptr = '\0';
430                 }
431
432                 /*
433                  * Always get the numeric representation of IP
434                  */
435                 if (ip_hton(hostnm, AF_UNSPEC, &c->ipaddr) < 0) {
436                         radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s: %s",
437                                filename, cf_section_lineno(cs),
438                                hostnm, librad_errstr);
439                         return NULL;
440                 } else {
441                         char buffer[256];
442                         ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
443                         c->longname = strdup(buffer);
444                 }
445
446                 /*
447                  *      This makes later life easier.
448                  */
449                 if (!c->shortname) c->shortname = strdup(c->longname);
450
451                 if (c->prefix < 0) switch (c->ipaddr.af) {
452                 case AF_INET:
453                         c->prefix = 32;
454                         break;
455                 case AF_INET6:
456                         c->prefix = 128;
457                         break;
458                 default:
459                         break;
460                 }
461
462                 /*
463                  *      FIXME: Add the client as data via cf_data_add,
464                  *      for migration issues.
465                  */
466
467                 if (!client_add(clients, c)) {
468                         radlog(L_CONS|L_ERR, "%s[%d]: Failed to add client %s",
469                                filename, cf_section_lineno(cs), hostnm);
470                         client_free(c);
471                         return NULL;
472                 }
473         }
474
475         return clients;
476 }