Compile-time option WITH_ACCOUNTING
[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 };
44
45
46 #ifdef WITH_SNMP
47 static rbtree_t         *tree_num;      /* client numbers 0..N */
48 static int              tree_num_max;
49 #endif
50 static RADCLIENT_LIST   *root_clients;
51
52 /*
53  *      Callback for freeing a client.
54  */
55 void client_free(RADCLIENT *client)
56 {
57         free(client->longname);
58         free(client->secret);
59         free(client->shortname);
60         free(client->nastype);
61         free(client->login);
62         free(client->password);
63         free(client->server);
64
65 #ifdef WITH_SNMP
66         free(client->auth);
67 #ifdef WITH_ACCOUNTING
68         free(client->acct);
69 #endif
70 #endif
71
72         free(client);
73 }
74
75
76 /*
77  *      Callback for comparing two clients.
78  */
79 static int client_ipaddr_cmp(const void *one, const void *two)
80 {
81         const RADCLIENT *a = one;
82         const RADCLIENT *b = two;
83
84         return fr_ipaddr_cmp(&a->ipaddr, &b->ipaddr);
85 }
86
87 #ifdef WITH_SNMP
88 static int client_num_cmp(const void *one, const void *two)
89 {
90         const RADCLIENT *a = one;
91         const RADCLIENT *b = two;
92
93         return (a->number - b->number);
94 }
95 #endif
96
97 /*
98  *      Free a RADCLIENT list.
99  */
100 void clients_free(RADCLIENT_LIST *clients)
101 {
102         int i;
103
104         if (!clients) return;
105
106         for (i = 0; i <= 128; i++) {
107                 if (clients->trees[i]) rbtree_free(clients->trees[i]);
108                 clients->trees[i] = NULL;
109         }
110
111         if (clients == root_clients) {
112 #ifdef WITH_SNMP
113                 if (tree_num) rbtree_free(tree_num);
114                 tree_num = NULL;
115                 tree_num_max = 0;
116 #endif
117                 root_clients = NULL;
118         }
119
120         free(clients);
121 }
122
123 /*
124  *      Return a new, initialized, set of clients.
125  */
126 RADCLIENT_LIST *clients_init(void)
127 {
128         RADCLIENT_LIST *clients = calloc(1, sizeof(RADCLIENT_LIST));
129
130         if (!clients) return NULL;
131
132         clients->min_prefix = 128;
133
134         return clients;
135 }
136
137
138 /*
139  *      Sanity check a client.
140  */
141 static int client_sane(RADCLIENT *client)
142 {
143         switch (client->ipaddr.af) {
144         case AF_INET:
145                 if (client->prefix > 32) {
146                         return 0;
147                 }
148
149                 /*
150                  *      Zero out the subnet bits.
151                  */
152                 if (client->prefix == 0) {
153                         memset(&client->ipaddr.ipaddr.ip4addr, 0,
154                                sizeof(client->ipaddr.ipaddr.ip4addr));
155
156                 } else if (client->prefix < 32) {
157                         uint32_t mask = ~0;
158
159                         mask <<= (32 - client->prefix);
160                         client->ipaddr.ipaddr.ip4addr.s_addr &= htonl(mask);
161                 }
162                 break;
163
164         case AF_INET6:
165                 if (client->prefix > 128) return 0;
166
167                 if (client->prefix == 0) {
168                         memset(&client->ipaddr.ipaddr.ip6addr, 0,
169                                sizeof(client->ipaddr.ipaddr.ip6addr));
170
171                 } else if (client->prefix < 128) {
172                         int i;
173                         uint32_t mask, *addr;
174
175                         addr = (uint32_t *) &client->ipaddr.ipaddr.ip6addr;
176
177                         for (i = client->prefix; i < 128; i += 32) {
178                                 mask = ~0;
179                                 mask <<= ((128 - i) & 0x1f);
180                                 addr[i / 32] &= mask;
181                         }
182                 }
183                 break;
184
185         default:
186                 return 0;
187         }
188
189         return 1;
190 }
191
192
193 /*
194  *      Add a client to the tree.
195  */
196 int client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
197 {
198         if (!client) {
199                 return 0;
200         }
201
202         /*
203          *      If "clients" is NULL, it means add to the global list.
204          */
205         if (!clients) {
206                 /*
207                  *      Initialize it, if not done already.
208                  */
209                 if (!root_clients) {
210                         root_clients = clients_init();
211                         if (!root_clients) return 0;
212                 }
213                 clients = root_clients;
214         }
215
216         if ((client->prefix < 0) || (client->prefix > 128)) {
217                 return 0;
218         }
219
220         if (!client_sane(client)) return 0;
221
222         /*
223          *      Create a tree for it.
224          */
225         if (!clients->trees[client->prefix]) {
226                 clients->trees[client->prefix] = rbtree_create(client_ipaddr_cmp,
227                                                                (void *) client_free, 0);
228                 if (!clients->trees[client->prefix]) {
229                         return 0;
230                 }
231         }
232
233         /*
234          *      Cannot insert the same client twice.
235          */
236         if (rbtree_find(clients->trees[client->prefix], client)) {
237                 radlog(L_ERR, "Failed to add duplicate client %s",
238                        client->shortname);
239                 return 0;
240         }
241
242         /*
243          *      Other error adding client: likely is fatal.
244          */
245         if (!rbtree_insert(clients->trees[client->prefix], client)) {
246                 return 0;
247         }
248
249 #ifdef WITH_SNMP
250         if (!tree_num) {
251                 tree_num = rbtree_create(client_num_cmp, NULL, 0);
252         }
253
254
255         /*
256          *      Catch clients added by rlm_sql.
257          */
258         if (!client->auth) {
259                 client->auth = rad_malloc(sizeof(*client->auth));
260                 memset(client->auth, 0, sizeof(*client->auth));
261         }
262
263 #ifdef WITH_ACCOUNTING
264         if (!client->acct) {
265                 client->acct = rad_malloc(sizeof(*client->acct));
266                 memset(client->acct, 0, sizeof(*client->acct));
267         }
268 #endif
269
270         client->number = tree_num_max;
271         tree_num_max++;
272         if (tree_num) rbtree_insert(tree_num, client);
273 #endif
274
275         if (client->prefix < clients->min_prefix) {
276                 clients->min_prefix = client->prefix;
277         }
278
279         return 1;
280 }
281
282
283 /*
284  *      Find a client in the RADCLIENTS list by number.
285  *      This is a support function for the SNMP code.
286  */
287 RADCLIENT *client_findbynumber(const RADCLIENT_LIST *clients,
288                                int number)
289 {
290 #ifdef WITH_SNMP
291         if (!clients) clients = root_clients;
292
293         if (!clients) return NULL;
294
295         if (number >= tree_num_max) return NULL;
296
297         if (tree_num) {
298                 RADCLIENT myclient;
299
300                 myclient.number = number;
301
302                 return rbtree_finddata(tree_num, &myclient);
303         }
304 #else
305         clients = clients;      /* -Wunused */
306         number = number;        /* -Wunused */
307 #endif
308         return NULL;
309 }
310
311
312 /*
313  *      Find a client in the RADCLIENTS list.
314  */
315 RADCLIENT *client_find(const RADCLIENT_LIST *clients,
316                        const fr_ipaddr_t *ipaddr)
317 {
318         int i, max_prefix;
319         RADCLIENT myclient;
320
321         if (!clients) clients = root_clients;
322
323         if (!clients || !ipaddr) return NULL;
324
325         switch (ipaddr->af) {
326         case AF_INET:
327                 max_prefix = 32;
328                 break;
329
330         case AF_INET6:
331                 max_prefix = 128;
332                 break;
333
334         default :
335                 return NULL;
336         }
337
338         for (i = max_prefix; i >= clients->min_prefix; i--) {
339                 void *data;
340
341                 myclient.prefix = i;
342                 myclient.ipaddr = *ipaddr;
343                 client_sane(&myclient); /* clean up the ipaddress */
344
345                 if (!clients->trees[i]) continue;
346
347                 data = rbtree_finddata(clients->trees[i], &myclient);
348                 if (data) {
349                         return data;
350                 }
351         }
352
353         return NULL;
354 }
355
356
357 /*
358  *      Old wrapper for client_find
359  */
360 RADCLIENT *client_find_old(const fr_ipaddr_t *ipaddr)
361 {
362         return client_find(root_clients, ipaddr);
363 }
364
365 static struct in_addr cl_ip4addr;
366 static struct in6_addr cl_ip6addr;
367
368 static const CONF_PARSER client_config[] = {
369         { "ipaddr",  PW_TYPE_IPADDR,
370           0, &cl_ip4addr,  NULL },
371         { "ipv6addr",  PW_TYPE_IPV6ADDR,
372           0, &cl_ip6addr, NULL },
373         { "netmask",  PW_TYPE_INTEGER,
374           offsetof(RADCLIENT, prefix), 0, NULL },
375
376         { "require_message_authenticator",  PW_TYPE_BOOLEAN,
377           offsetof(RADCLIENT, message_authenticator), 0, "no" },
378
379         { "secret",  PW_TYPE_STRING_PTR,
380           offsetof(RADCLIENT, secret), 0, NULL },
381         { "shortname",  PW_TYPE_STRING_PTR,
382           offsetof(RADCLIENT, shortname), 0, NULL },
383         { "nastype",  PW_TYPE_STRING_PTR,
384           offsetof(RADCLIENT, nastype), 0, NULL },
385         { "login",  PW_TYPE_STRING_PTR,
386           offsetof(RADCLIENT, login), 0, NULL },
387         { "password",  PW_TYPE_STRING_PTR,
388           offsetof(RADCLIENT, password), 0, NULL },
389         { "virtual_server",  PW_TYPE_STRING_PTR,
390           offsetof(RADCLIENT, server), 0, NULL },
391         { "server",  PW_TYPE_STRING_PTR, /* compatability with 2.0-pre */
392           offsetof(RADCLIENT, server), 0, NULL },
393
394         { NULL, -1, 0, NULL, NULL }
395 };
396
397
398 static RADCLIENT *client_parse(CONF_SECTION *cs, int in_server)
399 {
400         RADCLIENT       *c;
401         const char      *name2;
402
403         name2 = cf_section_name2(cs);
404         if (!name2) {
405                 cf_log_err(cf_sectiontoitem(cs),
406                            "Missing client name");
407                 return NULL;
408         }
409
410         /*
411          * The size is fine.. Let's create the buffer
412          */
413         c = rad_malloc(sizeof(*c));
414         memset(c, 0, sizeof(*c));
415         c->cs = cs;
416
417 #ifdef WITH_SNMP
418         c->auth = rad_malloc(sizeof(*c->auth));
419         memset(c->auth, 0, sizeof(*c->auth));
420
421 #ifdef WITH_ACCOUNTING
422         c->acct = rad_malloc(sizeof(*c->acct));
423         memset(c->acct, 0, sizeof(*c->acct));
424 #endif
425 #endif
426
427         memset(&cl_ip4addr, 0, sizeof(cl_ip4addr));
428         memset(&cl_ip6addr, 0, sizeof(cl_ip6addr));
429         c->prefix = -1;
430
431         if (cf_section_parse(cs, c, client_config) < 0) {
432                 client_free(c);
433                 cf_log_err(cf_sectiontoitem(cs),
434                            "Error parsing client section.");
435                 return NULL;
436         }
437
438         /*
439          *      Global clients can set servers to use,
440          *      per-server clients cannot.
441          */
442         if (in_server && c->server) {
443                 client_free(c);
444                 cf_log_err(cf_sectiontoitem(cs),
445                            "Clients inside of an server section cannot point to a server.");
446                 return NULL;
447         }
448                 
449         /*
450          *      No "ipaddr" or "ipv6addr", use old-style
451          *      "client <ipaddr> {" syntax.
452          */
453         if (!cf_pair_find(cs, "ipaddr") &&
454             !cf_pair_find(cs, "ipv6addr")) {
455                 char *prefix_ptr;
456
457                 prefix_ptr = strchr(name2, '/');
458
459                 /*
460                  *      Look for prefixes.
461                  */
462                 if (prefix_ptr) {
463                         c->prefix = atoi(prefix_ptr + 1);
464                         if ((c->prefix < 0) || (c->prefix > 128)) {
465                                 client_free(c);
466                                 cf_log_err(cf_sectiontoitem(cs),
467                                            "Invalid Prefix value '%s' for IP.",
468                                            prefix_ptr + 1);
469                                 return NULL;
470                         }
471                         /* Replace '/' with '\0' */
472                         *prefix_ptr = '\0';
473                 }
474                         
475                 /*
476                  *      Always get the numeric representation of IP
477                  */
478                 if (ip_hton(name2, AF_UNSPEC, &c->ipaddr) < 0) {
479                         client_free(c);
480                         cf_log_err(cf_sectiontoitem(cs),
481                                    "Failed to look up hostname %s: %s",
482                                    name2, librad_errstr);
483                         return NULL;
484                 }
485
486                 if (prefix_ptr) *prefix_ptr = '/';
487                 c->longname = strdup(name2);
488
489                 if (!c->shortname) c->shortname = strdup(c->longname);
490
491         } else {
492                 char buffer[1024];
493
494                 /*
495                  *      Figure out which one to use.
496                  */
497                 if (cf_pair_find(cs, "ipaddr")) {
498                         c->ipaddr.af = AF_INET;
499                         c->ipaddr.ipaddr.ip4addr = cl_ip4addr;
500
501                         if ((c->prefix < -1) || (c->prefix > 32)) {
502                                 client_free(c);
503                                 cf_log_err(cf_sectiontoitem(cs),
504                                            "Netmask must be between 0 and 32");
505                                 return NULL;
506                         }
507                                 
508                 } else if (cf_pair_find(cs, "ipv6addr")) {
509                         c->ipaddr.af = AF_INET6;
510                         c->ipaddr.ipaddr.ip6addr = cl_ip6addr;
511                                 
512                         if ((c->prefix < -1) || (c->prefix > 128)) {
513                                 client_free(c);
514                                 cf_log_err(cf_sectiontoitem(cs),
515                                            "Netmask must be between 0 and 128");
516                                 return NULL;
517                         }
518                 } else {
519                         cf_log_err(cf_sectiontoitem(cs),
520                                    "No IP address defined for the client");
521                         client_free(c);
522                         return NULL;
523                 }
524
525                 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
526                 c->longname = strdup(buffer);
527
528                 /*
529                  *      Set the short name to the name2
530                  */
531                 if (!c->shortname) c->shortname = strdup(name2);
532         }
533
534         if (c->prefix < 0) switch (c->ipaddr.af) {
535         case AF_INET:
536                 c->prefix = 32;
537                 break;
538         case AF_INET6:
539                 c->prefix = 128;
540                 break;
541         default:
542                 break;
543         }
544
545         if (!c->secret || !*c->secret) {
546 #ifdef WITH_DHCP
547                 const char *value = NULL;
548                 CONF_PAIR *cp = cf_pair_find(cs, "dhcp");
549
550                 if (cp) value = cf_pair_value(cp);
551
552                 /*
553                  *      Secrets aren't needed for DHCP.
554                  */
555                 if (value && (strcmp(value, "yes") == 0)) return c;
556
557 #endif
558                 client_free(c);
559                 cf_log_err(cf_sectiontoitem(cs),
560                            "secret must be at least 1 character long");
561                 return NULL;
562         }
563
564
565         return c;
566 }
567
568 /*
569  *      Create the linked list of clients from the new configuration
570  *      type.  This way we don't have to change too much in the other
571  *      source-files.
572  */
573 RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section)
574 {
575         int             global = FALSE, in_server = FALSE;
576         CONF_SECTION    *cs;
577         RADCLIENT       *c;
578         RADCLIENT_LIST  *clients;
579
580         /*
581          *      Be forgiving.  If there's already a clients, return
582          *      it.  Otherwise create a new one.
583          */
584         clients = cf_data_find(section, "clients");
585         if (clients) return clients;
586
587         clients = clients_init();
588         if (!clients) return NULL;
589
590         if (cf_top_section(section) == section) global = TRUE;
591
592         if (strcmp("server", cf_section_name1(section)) == 0) in_server = TRUE;
593
594         /*
595          *      Associate the clients structure with the section, where
596          *      it will be freed once the section is freed.
597          */
598         if (cf_data_add(section, "clients", clients, (void *) clients_free) < 0) {
599                 cf_log_err(cf_sectiontoitem(section),
600                            "Failed to associate clients with section %s",
601                        cf_section_name1(section));
602                 clients_free(clients);
603                 return NULL;
604         }
605
606         for (cs = cf_subsection_find_next(section, NULL, "client");
607              cs != NULL;
608              cs = cf_subsection_find_next(section, cs, "client")) {
609                 c = client_parse(cs, in_server);
610                 if (!c) {
611                         return NULL;
612                 }
613
614                 /*
615                  *      FIXME: Add the client as data via cf_data_add,
616                  *      for migration issues.
617                  */
618
619                 if (!client_add(clients, c)) {
620                         cf_log_err(cf_sectiontoitem(cs),
621                                    "Failed to add client %s",
622                                    cf_section_name2(cs));
623                         client_free(c);
624                         return NULL;
625                 }
626         }
627
628         /*
629          *      Replace the global list of clients with the new one.
630          *      The old one is still referenced from the original
631          *      configuration, and will be freed when that is freed.
632          */
633         if (global) {
634                 root_clients = clients;
635         }
636
637         return clients;
638 }