5b35576aaf900c35dba78e9d1fba2707d4105cd8
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "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 "radiusd.h"
42 #include "conffile.h"
43 #include "rad_assert.h"
44
45 struct radclient_list {
46         /*
47          *      FIXME: One set of trees for IPv4, and another for IPv6?
48          */
49         rbtree_t        *trees[129]; /* for 0..128, inclusive. */
50         int             min_prefix;
51 #ifdef WITH_SNMP
52         rbtree_t        *num;   /* client numbers 0..N */
53         int             max;
54 #endif
55 };
56
57 /*
58  *      Callback for freeing a client.
59  */
60 void client_free(RADCLIENT *client)
61 {
62         free(client->longname);
63         free(client->secret);
64         free(client->shortname);
65         free(client->nastype);
66         free(client->login);
67         free(client->password);
68         
69         free(client);
70 }
71
72
73 /*
74  *      Callback for comparing two clients.
75  */
76 static int client_ipaddr_cmp(const void *one, const void *two)
77 {
78         const RADCLIENT *a = one;
79         const RADCLIENT *b = two;
80
81         if (a->ipaddr.af < b->ipaddr.af) return -1;
82         if (a->ipaddr.af > b->ipaddr.af) return +1;
83
84         rad_assert(a->prefix == b->prefix);
85
86         switch (a->ipaddr.af) {
87         case AF_INET:
88                 return memcmp(&a->ipaddr.ipaddr.ip4addr,
89                               &b->ipaddr.ipaddr.ip4addr,
90                               sizeof(a->ipaddr.ipaddr.ip4addr));
91                 break;
92
93         case AF_INET6:
94                 return memcmp(&a->ipaddr.ipaddr.ip6addr,
95                               &b->ipaddr.ipaddr.ip6addr,
96                               sizeof(a->ipaddr.ipaddr.ip6addr));
97                 break;
98
99         default:
100                 break;
101         }
102
103         /*
104          *      Something bad happened...
105          */
106         rad_assert("Internal sanity check failed");
107         return -1;
108 }
109
110 #ifdef WITH_SNMP
111 static int client_num_cmp(const void *one, const void *two)
112 {
113         const RADCLIENT *a = one;
114         const RADCLIENT *b = two;
115
116         return (a->number - b->number);
117 }
118 #endif
119
120 /*
121  *      Free a RADCLIENT list.
122  */
123 void clients_free(RADCLIENT_LIST *clients)
124 {
125         int i;
126
127         if (!clients) return;
128
129         for (i = 0; i <= 128; i++) {
130                 if (clients->trees[i]) rbtree_free(clients->trees[i]);
131                 clients->trees[i] = NULL;
132         }
133 #ifdef WITH_SNMP
134         if (clients->num) rbtree_free(clients->num);
135 #endif
136 }
137
138 /*
139  *      Return a new, initialized, set of clients.
140  */
141 RADCLIENT_LIST *clients_init(void)
142 {
143         RADCLIENT_LIST *clients = calloc(1, sizeof(RADCLIENT_LIST));
144
145         if (!clients) return NULL;
146
147         clients->min_prefix = 128;
148
149         return clients;
150 }
151
152
153 /*
154  *      Sanity check a client.
155  */
156 static int client_sane(RADCLIENT *client)
157 {
158         switch (client->ipaddr.af) {
159         case AF_INET:
160                 if (client->prefix > 32) {
161                         return 0;
162                 }
163
164                 /*
165                  *      Zero out the subnet bits.
166                  */
167                 if (client->prefix < 32) {
168                         uint32_t mask = ~0;
169
170                         mask <<= (32 - client->prefix);
171                         client->ipaddr.ipaddr.ip4addr.s_addr &= mask;
172                 }
173                 break;
174
175         case AF_INET6:
176                 if (client->prefix > 128) return 0;
177
178                 if (client->prefix < 128) {
179                         int i;
180                         uint32_t mask, *addr;
181
182                         addr = (uint32_t *) &client->ipaddr.ipaddr.ip6addr;
183
184                         for (i = client->prefix; i < 128; i += 32) {
185                                 mask = ~0;
186                                 mask <<= ((128 - i) & 0x1f);
187                                 addr[i / 32] &= mask;
188                         }
189                 }
190                 break;
191
192         default:
193                 return 0;
194         }
195
196         return 1;
197 }
198
199
200 /*
201  *      Add a client to the tree.
202  */
203 int client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
204 {
205         if (!clients || !client) {
206                 return 0;
207         }
208
209         if (client->prefix < 0) {
210                 return 0;
211         }
212
213         if (!client_sane(client)) return 0;
214
215         /*
216          *      Create a tree for it.
217          */
218         if (!clients->trees[client->prefix]) {
219                 clients->trees[client->prefix] = rbtree_create(client_ipaddr_cmp,
220                                                                client_free, 0);
221                 if (!clients->trees[client->prefix]) {
222                         return 0;
223                 }
224         }
225
226         /*
227          *      Duplicate?
228          */
229         if (!rbtree_insert(clients->trees[client->prefix], client)) {
230                 return 0;
231         }
232
233 #ifdef WITH_SNMP
234         if (!clients->num) rbtree_create(client_num_cmp, NULL, 0);
235
236         client->number = clients->max;
237         clients->max++;
238         if (clients->num) rbtree_insert(clients->num, client);
239 #endif
240
241         if (client->prefix < clients->min_prefix) {
242                 clients->min_prefix = client->prefix;
243         }
244
245         return 1;
246 }
247
248
249 /*
250  *      Find a client in the RADCLIENTS list by number.
251  *      This is a support function for the SNMP code.
252  */
253 RADCLIENT *client_findbynumber(const RADCLIENT_LIST *clients,
254                                int number)
255 {
256 #ifdef WITH_SNMP
257         if (!clients) return NULL;
258
259         if (clients->num) {
260                 RADCLIENT myclient;
261                 
262                 myclient.number = number;
263                 
264                 return rbtree_finddata(clients->num, &myclient);
265         }
266 #endif
267         return NULL;
268 }
269
270
271 /*
272  *      Find a client in the RADCLIENTS list.
273  */
274 RADCLIENT *client_find(const RADCLIENT_LIST *clients,
275                        const lrad_ipaddr_t *ipaddr)
276 {
277         int i, max_prefix;
278         RADCLIENT myclient;
279
280         if (!clients || !ipaddr) return NULL;
281
282         switch (ipaddr->af) {
283         case AF_INET:
284                 max_prefix = 32;
285                 break;
286
287         case AF_INET6:
288                 max_prefix = 128;
289                 break;
290
291         default :
292                 return NULL;
293         }
294
295         for (i = max_prefix; i >= clients->min_prefix; i--) {
296                 void *data;
297
298                 myclient.prefix = i;
299                 myclient.ipaddr = *ipaddr;
300                 client_sane(&myclient); /* clean up the ipaddress */
301
302                 if (!clients->trees[i]) continue;
303                 
304                 data = rbtree_finddata(clients->trees[i], &myclient);
305                 if (data) {
306                         return data;
307                 }
308         }
309
310         return NULL;
311 }
312
313
314 /*
315  *      Old wrapper for client_find
316  */
317 RADCLIENT *client_find_old(const lrad_ipaddr_t *ipaddr)
318 {
319         return client_find(mainconfig.clients, ipaddr);
320 }
321
322
323 /*
324  *      Read the clients file.
325  */
326 int read_clients_file(RADCLIENT_LIST *clients, const char *file)
327 {
328         FILE *fp;
329         RADCLIENT *c;
330         char buffer[256];
331         char hostnm[256];
332         char secret[256];
333         char shortnm[256];
334         int prefix = 0;
335         int lineno = 0;
336         char *p;
337         int got_clients = FALSE;
338
339         if ((fp = fopen(file, "r")) == NULL) {
340                 /* The clients file is no longer required.  All configuration
341                    information is read from radiusd.conf and friends.  If
342                    clients exists it will be used, but if it doesn't no harm
343                    done. */
344                 return 0;
345         }
346
347         while(fgets(buffer, 256, fp) != NULL) {
348                 lineno++;
349                 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
350                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
351                         return -1;
352                 }
353
354                 /*
355                  *      Skip whitespace.
356                  */
357                 p = buffer;
358                 while (*p &&
359                                 ((*p == ' ') || (*p == '\t')))
360                         p++;
361
362                 /*
363                  *      Skip comments and blank lines.
364                  */
365                 if ((*p == '#') || (*p == '\n') || (*p == '\r'))
366                         continue;
367
368                 if (!getword(&p, hostnm, sizeof(hostnm)) ||
369                                 !getword(&p, secret, sizeof(secret))) {
370                         radlog(L_ERR, "%s[%d]: unexpected end of line",
371                                         file, lineno);
372                         return -1;
373                 }
374
375                 (void)getword(&p, shortnm, sizeof(shortnm));
376
377                 /*
378                  *      Look for a mask in the hostname
379                  */
380                 p = strchr(hostnm, '/');
381
382                 if (p) {
383                         *p = '\0';
384                         p++;
385
386                         prefix = atoi(p);
387                         if ((prefix < 0) || (prefix > 128)) {
388                                 radlog(L_ERR, "%s[%d]: Invalid value '%s' for IP network mask.",
389                                        file, lineno, p);
390                                 return -1;
391                         }
392                 }
393
394                 /*
395                  *      It should be OK now, let's create the buffer.
396                  */
397                 got_clients = TRUE;
398                 c = rad_malloc(sizeof(RADCLIENT));
399                 memset(c, 0, sizeof(*c));
400
401                 if (ip_hton(hostnm, AF_UNSPEC, &c->ipaddr) < 0) {
402                         radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
403                                         file, lineno, hostnm);
404                         return -1;
405                 }
406                 c->prefix = prefix;
407                 c->secret = strdup(secret);
408                 c->shortname = strdup(shortnm);
409
410                 switch (c->ipaddr.af) {
411                         NAS *nas;
412                 case AF_INET :
413                         if ((prefix < 0) || (prefix > 32)) {
414                                 radlog(L_ERR, "%s[%d]: Invalid value '%s' for IP network mask.",
415                                        file, lineno, p);
416                                 return -1;
417                         }
418
419                         if (prefix) {
420                                 hostnm[strlen(hostnm)] = '/';
421                                 /* Long Name includes prefix too */
422                                 c->longname = strdup(hostnm);
423                         } else {
424
425                                 /*
426                                  * Only do DNS lookups for machines.  Just print
427                                  * the network as the long name.
428                                  */
429                                 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
430                                 c->longname = strdup(buffer);
431
432                         }
433                         /*
434                          *      Pull information over from the NAS.
435                          */
436                         nas = nas_find(c->ipaddr.ipaddr.ip4addr.s_addr);
437                         if (nas) {
438                                 /*
439                                  *      No short name in the
440                                  *      'clients' file, try
441                                  *      copying one over from
442                                  *      the 'naslist' file.
443                                  */
444                                 if (!c->shortname) {
445                                         c->shortname = strdup(nas->shortname);
446                                 }
447                                 
448                                 /*
449                                  *  Copy the nastype over, too.
450                                  */
451                                 c->nastype = strdup(nas->nastype);
452                         }
453                         break;
454
455                 case AF_INET6 :
456                         if (prefix) {
457                                 hostnm[strlen(hostnm)] = '/';
458                                 c->longname = strdup(hostnm);
459                         } else {
460
461                                 /*
462                                  * Only do DNS lookups for machines.  Just print
463                                  * the network as the long name.
464                                  */
465                                 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
466                                 c->longname = strdup(buffer);
467                         }
468                         /* TODO: NAS info as in IPv4 above */
469                         break;
470                 default :
471                         break;
472                 }
473
474                 /*
475                  *      Failed to add the client: ignore the error
476                  *      and continue.
477                  */
478                 if (!client_add(clients, c)) {
479                         client_free(c);
480                 }
481         }
482         fclose(fp);
483
484         if (got_clients) {
485                 radlog(L_INFO, "Using deprecated clients file.  Support for this will go away soon.");
486         }
487
488         return 0;
489 }
490
491
492 /*
493  *      Find the name of a client (prefer short name).
494  */
495 const char *client_name(const RADCLIENT_LIST *clients,
496                         const lrad_ipaddr_t *ipaddr)
497 {
498         /* We don't call this unless we should know about the client. */
499         RADCLIENT *cl;
500         char host_ipaddr[128];
501
502         if ((cl = client_find(clients, ipaddr)) != NULL) {
503                 if (cl->shortname && cl->shortname[0])
504                         return cl->shortname;
505                 else
506                         return cl->longname;
507         }
508
509         /*
510          * this isn't normally reachable, but if a loggable event happens just
511          * after a client list change and a HUP, then we may not know this
512          * information any more.
513          *
514          * If you see lots of these, then there's something wrong.
515          */
516         radlog(L_ERR, "Trying to look up name of unknown client %s.\n",
517                ip_ntoh(ipaddr, host_ipaddr, sizeof(host_ipaddr)));
518
519         return "UNKNOWN-CLIENT";
520 }
521
522 const char *client_name_old(const lrad_ipaddr_t *ipaddr)
523 {
524         return client_name(mainconfig.clients, ipaddr);
525 }
526
527 static const CONF_PARSER client_config[] = {
528         { "secret",  PW_TYPE_STRING_PTR, 
529           offsetof(RADCLIENT, secret), 0, NULL },
530         { "shortname",  PW_TYPE_STRING_PTR, 
531           offsetof(RADCLIENT, shortname), 0, NULL },
532         { "nastype",  PW_TYPE_STRING_PTR, 
533           offsetof(RADCLIENT, nastype), 0, NULL },
534         { "login",  PW_TYPE_STRING_PTR, 
535           offsetof(RADCLIENT, login), 0, NULL },
536         { "password",  PW_TYPE_STRING_PTR, 
537           offsetof(RADCLIENT, password), 0, NULL },
538
539         { NULL, -1, 0, NULL, NULL }
540 };
541
542
543 /*
544  *      Create the linked list of clients from the new configuration
545  *      type.  This way we don't have to change too much in the other
546  *      source-files.
547  */
548 RADCLIENT_LIST *clients_parse_section(const char *filename,
549                                       CONF_SECTION *section)
550 {
551         CONF_SECTION    *cs;
552         RADCLIENT       *c;
553         char            *hostnm, *prefix_ptr = NULL;
554         const char      *name2;
555         RADCLIENT_LIST  *clients;
556
557         /*
558          *      Be forgiving.  If there's already a clients, return
559          *      it.  Otherwise create a new one.
560          */
561         clients = cf_data_find(section, "clients");
562         if (clients) return clients;
563
564         clients = clients_init();
565         if (!clients) return NULL;
566
567         /*
568          *      Associate the clients structure with the section, where
569          *      it will be freed once the section is freed.
570          */
571         if (cf_data_add(section, "clients", clients, clients_free) < 0) {
572                 radlog(L_ERR, "%s[%d]: Failed to associate clients with section %s",
573                        filename, cf_section_lineno(section),
574                        cf_section_name1(section));
575                 clients_free(clients);
576                 return NULL;
577         }
578
579         for (cs = cf_subsection_find_next(section, NULL, "client");
580              cs != NULL;
581              cs = cf_subsection_find_next(section, cs, "client")) {
582                 name2 = cf_section_name2(cs);
583                 if (!name2) {
584                         radlog(L_CONS|L_ERR, "%s[%d]: Missing client name",
585                                filename, cf_section_lineno(cs));
586                         return NULL;
587                 }
588                 /*
589                  * Check the lengths, we don't want any core dumps
590                  */
591                 hostnm = name2;
592                 prefix_ptr = strchr(hostnm, '/');
593
594                 /*
595                  * The size is fine.. Let's create the buffer
596                  */
597                 c = rad_malloc(sizeof(RADCLIENT));
598                 memset(c, 0, sizeof(RADCLIENT));
599
600                 if (cf_section_parse(cs, c, client_config) < 0) {
601                         radlog(L_CONS|L_ERR, "%s[%d]: Error parsing client section.",
602                                filename, cf_section_lineno(cs));
603                         return NULL;
604                 }
605
606                 /*
607                  * Look for prefixes.
608                  */
609                 c->prefix = -1;
610                 if (prefix_ptr) {
611                         c->prefix = atoi(prefix_ptr + 1);
612                         if ((c->prefix < 0) || (c->prefix > 128)) {
613                                 radlog(L_ERR, "%s[%d]: Invalid Prefix value '%s' for IP.",
614                                                 filename, cf_section_lineno(cs), prefix_ptr + 1);
615                                 return NULL;
616                         }
617                         /* Replace '/' with '\0' */
618                         *prefix_ptr = '\0';
619                 }
620
621                 /*
622                  * Always get the numeric representation of IP
623                  */
624                 if (ip_hton(hostnm, AF_UNSPEC, &c->ipaddr) < 0) {
625                         radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s: %s",
626                                filename, cf_section_lineno(cs),
627                                hostnm, librad_errstr);
628                         return NULL;
629                 } else {
630                         char buffer[256];
631                         ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
632                         c->longname = strdup(buffer);
633                 }
634
635                 /*
636                  *      This makes later life easier.
637                  */
638                 if (!c->shortname) c->shortname = strdup(c->longname);
639
640                 if (c->prefix < 0) switch (c->ipaddr.af) {
641                 case AF_INET:
642                         c->prefix = 32;
643                         break;
644                 case AF_INET6:
645                         c->prefix = 128;
646                         break;
647                 default:
648                         break;
649                 }
650
651                 /*
652                  *      FIXME: Add the client as data via cf_data_add,
653                  *      for migration issues.
654                  */
655
656                 if (!client_add(clients, c)) {
657                         radlog(L_CONS|L_ERR, "%s[%d]: Failed to add client %s",
658                                filename, cf_section_lineno(cs), hostnm);
659                         client_free(c);
660                         return NULL;
661                 }
662         }
663
664         return clients;
665 }