root/cherokee/trunk/cherokee/access.c

Revision 1679, 12.6 kB (checked in by alo, 3 months ago)

--

Line 
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* Cherokee
4  *
5  * Authors:
6  *      Alvaro Lopez Ortega <alvaro@alobbs.com>
7  *
8  * Copyright (C) 2001-2008 Alvaro Lopez Ortega
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of version 2 of the GNU General Public
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22  * USA
23  */
24
25 #include "common-internal.h"
26 #include "access.h"
27
28 #ifdef HAVE_SYS_TYPES_H
29 # include <sys/types.h>
30 #endif
31
32 #include <limits.h>
33
34 #ifdef HAVE_SYS_SOCKET_H
35 # include <sys/socket.h>
36 #endif
37
38 #ifdef HAVE_NETINET_IN_H
39 # include <netinet/in.h>
40 #endif
41
42 #ifdef HAVE_ARPA_INET_H
43 # include <arpa/inet.h> 
44 #endif
45
46 #include "util.h"
47 #include "resolv_cache.h"
48
49 #define ENTRIES "access"
50
51
52 #ifndef AF_INET6
53 # define AF_INET6 10
54 #endif
55
56 typedef enum {
57         ipv4 = AF_INET,
58         ipv6 = AF_INET6
59 } ip_type_t;
60
61
62 typedef union {
63         struct in_addr  ip4;
64 #ifdef HAVE_IPV6
65         struct in6_addr ip6;
66 #endif
67 } ip_t;
68
69 typedef struct {
70         cherokee_list_t node;
71        
72         ip_type_t       type;
73         ip_t            ip;
74 } ip_item_t;
75
76 typedef struct {
77         ip_item_t base;
78         ip_t      mask;
79 } subnet_item_t;
80
81 #define IP_NODE(x)     ((ip_item_t *)(x))
82 #define SUBNET_NODE(x) ((subnet_item_t *)(x))
83
84
85
86
87 static ip_item_t *
88 new_ip (void)
89 {
90         ip_item_t *n = (ip_item_t *) malloc (sizeof(ip_item_t));
91         if (n == NULL) return NULL;
92
93         INIT_LIST_HEAD (LIST(n));
94         memset (&n->ip, 0, sizeof(ip_t));
95
96         return n;
97 }
98
99 static ret_t
100 free_ip (ip_item_t *ip)
101 {
102         free (ip);
103         return ret_ok;
104 }
105
106 static subnet_item_t *
107 new_subnet (void)
108 {
109         subnet_item_t *n = (subnet_item_t *) malloc (sizeof(subnet_item_t));
110         if (n == NULL) return NULL;
111
112         memset (&n->base.ip, 0, sizeof(ip_t));
113         memset (&n->mask, 0, sizeof(ip_t));
114
115         INIT_LIST_HEAD (LIST(n));
116         return n;
117 }
118
119
120 ret_t
121 cherokee_access_new (cherokee_access_t **entry)
122 {
123         CHEROKEE_NEW_STRUCT (n, access);
124
125         INIT_LIST_HEAD(&n->list_ips);
126         INIT_LIST_HEAD(&n->list_subnets);
127        
128         *entry = n;
129         return ret_ok;
130 }
131
132
133 static void
134 print_ip (ip_type_t type, ip_t *ip)
135 {
136         CHEROKEE_TEMP(dir,255);
137
138 #ifdef HAVE_INET_PTON
139 # ifdef HAVE_IPV6
140         if (type == ipv6) {
141                 printf ("%s", inet_ntop (AF_INET6, ip, dir, dir_size));
142                 return;
143         }
144 # endif
145         if (type == ipv4) {
146                 printf ("%s", inet_ntop (AF_INET, ip, dir, dir_size));
147                 return;
148         }
149 #else
150         printf ("%s", inet_ntoa (ip->ip4));
151 #endif
152 }
153
154
155 ret_t
156 cherokee_access_free (cherokee_access_t *entry)
157 {
158         cherokee_list_t *i, *tmp;
159        
160         /* Free the IP list items
161          */
162         list_for_each_safe (i, tmp, LIST(&entry->list_ips)) {
163                 cherokee_list_del (i);
164                 free (i);
165         }
166
167         /* Free the Subnet list items
168          */
169         list_for_each_safe (i, tmp, LIST(&entry->list_subnets)) {
170                 cherokee_list_del (i);
171                 free (i);
172         }
173
174         free (entry);
175         return ret_ok;
176 }
177
178
179 static ret_t
180 parse_ip (char *ip, ip_item_t *n)
181 {
182         int ok;
183
184         /* Test if it is a IPv4 or IPv6 connection
185          */
186         n->type = ((strchr (ip, ':') != NULL) ||
187                    (strlen(ip) > 15)) ? ipv6 : ipv4;
188        
189 #ifndef HAVE_IPV6
190         if (n->type == ipv6) {
191                 return ret_error;
192         }
193 #endif
194
195         /* Parse the IP string
196          */
197 #ifdef HAVE_INET_PTON
198         ok = (inet_pton (n->type, ip, &n->ip) > 0);
199
200 # ifdef HAVE_IPV6
201         if (n->type == ipv6) {
202                 if (IN6_IS_ADDR_V4MAPPED (&(n->ip).ip6)) {
203                         PRINT_ERROR ("ERROR: This IP '%s' is IPv6-mapped IPv6 address.  "
204                                      "Please, specify IPv4 in a.b.c.d style instead "
205                                      "of ::ffff:a.b.c.d style\n", ip);
206                         return ret_error;
207                 }
208         }
209 # endif /* HAVE_IPV6 */
210
211 #else
212         ok = (inet_aton (ip, &n->ip) != 0);
213 #endif
214
215         return (ok) ? ret_ok : ret_error;
216 }
217  
218
219 static ret_t
220 parse_netmask (char *netmask, subnet_item_t *n)
221 {
222         int num;
223
224         /* IPv6 or IPv4 Mask
225          * Eg: 255.255.0.0
226          */
227         if ((strchr (netmask, ':') != NULL) ||
228             (strchr (netmask, '.') != NULL))
229         {
230                 int ok;
231
232 #ifdef HAVE_INET_PTON
233                 ok = (inet_pton (IP_NODE(n)->type, netmask, &n->mask) > 0);
234 #else
235                 ok = (inet_aton (netmask, &n->mask) != 0);
236 #endif         
237                 return (ok) ? ret_ok : ret_error;
238         }
239
240
241         /* Length mask
242          * Eg: 16
243          */
244         if (strlen(netmask) > 3) {
245                 return ret_error;
246         }
247
248         num = strtol(netmask, NULL, 10);
249         if (num < 0)
250                 return ret_error;
251
252         /* Sanity checks
253          */
254         if ((IP_NODE(n)->type == ipv4) && (num >  32))
255                 return ret_error;
256
257         if ((IP_NODE(n)->type == ipv6) && (num > 128))
258                 return ret_error;
259
260 #ifdef HAVE_IPV6
261         if (num > 128) {
262                 return ret_error;
263         }
264
265         /* Special case
266          */
267         if (num == 128) {
268                 int i;
269
270                 for (i=0; i<16; i++) {
271                         n->mask.ip6.s6_addr[i] = 0;
272                 }
273
274                 return ret_ok;
275         }
276
277         if (IP_NODE(n)->type == ipv6) {
278                 int i, j, jj;
279                 unsigned char mask    = 0;
280                 unsigned char maskbit = 0x80L;
281
282                 j  = (int) num / 8;
283                 jj = num % 8;
284
285                 for (i=0; i<j; i++) {
286                         n->mask.ip6.s6_addr[i] = 0xFF;
287                 }
288
289                 while (jj--) {
290                         mask |= maskbit;
291                         maskbit >>= 1;
292                 }
293                 n->mask.ip6.s6_addr[j] = mask;
294
295                 return ret_ok;
296         } else
297 #endif
298                 if (num == 0)
299                         n->mask.ip4.s_addr = (in_addr_t) 0;
300                 else
301                         n->mask.ip4.s_addr = (in_addr_t) htonl(~0L << (32 - num));
302
303         return ret_ok;
304 }
305
306
307 static ret_t
308 cherokee_access_add_ip (cherokee_access_t *entry, char *ip)
309 {
310         ret_t      ret;
311         ip_item_t *n;
312
313         n = new_ip();
314         if (n == NULL) return ret_error;
315
316         ret = parse_ip (ip, n);
317         if (ret < ret_ok) {
318                 PRINT_ERROR ("IP address '%s' seems to be invalid\n", ip);
319
320                 free_ip(n);
321                 return ret;
322         }
323
324         cherokee_list_add (LIST(n), &entry->list_ips);
325         TRACE (ENTRIES, "Access: adding IP '%s'\n", ip);
326
327         return ret;
328 }
329
330
331 static ret_t
332 cherokee_access_add_domain (cherokee_access_t *entry, char *domain)
333 {
334         ret_t                    ret;
335         const char              *ip;
336         cherokee_resolv_cache_t *resolv;
337
338         ret = cherokee_resolv_cache_get_default (&resolv);
339         if (unlikely(ret!=ret_ok)) return ret;
340
341         ret = cherokee_resolv_cache_get_ipstr (resolv, domain, &ip);
342         if (unlikely(ret!=ret_ok)) return ret;
343
344         TRACE (ENTRIES, "Access: domain '%s'\n", domain);
345         return cherokee_access_add_ip (entry, (char *)ip);
346 }
347
348
349 static ret_t
350 cherokee_access_add_subnet (cherokee_access_t *entry, char *subnet)
351 {
352         ret_t              ret;
353         char              *slash;
354         char              *mask;
355         subnet_item_t     *n;
356         cherokee_buffer_t  ip = CHEROKEE_BUF_INIT;
357
358         /* Split the string
359          */
360         slash = strpbrk (subnet, "/\\");
361         if (slash == NULL) return ret_error;
362
363         mask = slash +1;
364         cherokee_buffer_add (&ip, subnet, mask-subnet-1);
365        
366         /* Create the new list object
367          */
368         n = new_subnet();
369         if (n == NULL) return ret_error;
370
371         cherokee_list_add (LIST(n), &entry->list_subnets);
372
373         /* Parse the IP
374          */
375         ret = parse_ip (ip.buf, IP_NODE(n));
376         if (ret < ret_ok) {
377                 PRINT_ERROR ("IP address '%s' seems to be invalid\n", ip.buf);
378                 goto error;
379         }
380
381         /* Parse the Netmask
382          */
383         ret = parse_netmask (mask, n);
384         if (ret < ret_ok) {
385                 PRINT_ERROR ("Netmask '%s' seems to be invalid\n", mask);
386                 goto error;     
387         }
388
389         TRACE (ENTRIES, "Access: subnet IP '%s', mask '%s'\n", ip.buf, mask);
390
391         cherokee_buffer_mrproper (&ip);
392         return ret_ok;
393
394 error:
395         cherokee_buffer_mrproper (&ip);
396         return ret_error;
397 }
398
399
400 ret_t
401 cherokee_access_add (cherokee_access_t *entry, char *ip_or_subnet)
402 {
403         ret_t  ret;
404         char  *slash;
405         int    mask;
406         char   sep;
407
408         slash = strpbrk(ip_or_subnet, "/\\");
409
410         /* Add a single IP address
411          */
412         if (slash == NULL) {
413                 char *i         = ip_or_subnet;
414                 int   is_domain = 0;
415
416                 while (*i && !is_domain) {
417                         if (((*i >= 'a') && (*i <= 'z')) ||
418                             ((*i >= 'A') && (*i <= 'Z')))
419                                 is_domain = 1;
420                         i++;
421                 }
422
423                 if (is_domain)
424                         return cherokee_access_add_domain (entry, ip_or_subnet);
425                 else
426                         return cherokee_access_add_ip (entry, ip_or_subnet);
427         }
428        
429         /* Special cases of subnets
430          */
431         mask = atoi(slash+1);
432
433         if ((strchr(ip_or_subnet, ':') != NULL) && (mask == 128)) {
434                 sep = *slash;
435                 *slash = '\0';
436                 ret = cherokee_access_add_ip (entry, ip_or_subnet);
437                 *slash = sep;
438                 return ret;
439         }
440
441         if ((strchr(ip_or_subnet, '.') != NULL) && (mask == 32)) {
442                 sep = *slash;
443                 *slash = '\0';
444                 ret = cherokee_access_add_ip (entry, ip_or_subnet);             
445                 *slash = sep;
446                 return ret;
447         }
448
449         /* Add a subnet
450          */
451         return cherokee_access_add_subnet (entry, ip_or_subnet);               
452 }
453
454
455 ret_t
456 cherokee_access_print_debug (cherokee_access_t *entry)
457 {
458         cherokee_list_t *i;
459
460         printf ("IPs: ");
461         list_for_each (i, LIST(&entry->list_ips)) {
462                 print_ip (IP_NODE(i)->type, &IP_NODE(i)->ip);
463                 printf(" ");
464         }
465         printf("\n");
466
467         printf ("Subnets: ");
468         list_for_each (i, LIST(&entry->list_subnets)) {
469                 print_ip (IP_NODE(i)->type, &IP_NODE(i)->ip);
470                 printf("/");
471                 print_ip (IP_NODE(i)->type, &SUBNET_NODE(i)->mask);
472                 printf(" ");
473         }
474         printf("\n");
475
476         return ret_ok;
477 }
478
479
480 ret_t
481 cherokee_access_ip_match (cherokee_access_t *entry, cherokee_socket_t *sock)
482 {
483         int              re;
484         cherokee_list_t *i;
485
486         TRACE (ENTRIES, "Matching ip(%x)\n", SOCKET_ADDR_IPv4(sock)->sin_addr);
487
488         /* Check in the IP list
489          */
490         list_for_each (i, LIST(&entry->list_ips)) {
491                
492 #ifdef HAVE_IPV6
493                 /* This is a special case:
494                  * The socket is IPv6 with a mapped IPv4 address
495                  */
496                 if ((SOCKET_AF(sock) == ipv6) &&
497                     (IP_NODE(i)->type == ipv4) &&
498                     (IN6_IS_ADDR_V4MAPPED (&SOCKET_ADDR_IPv6(sock)->sin6_addr)) &&
499                     (!memcmp (&SOCKET_ADDR_IPv6(sock)->sin6_addr.s6_addr[12], &IP_NODE(i)->ip, 4)))
500                 {
501                         TRACE (ENTRIES, "IPv4 mapped in IPv6 address: %s\n", "matched");
502                         return ret_ok;
503                 }
504 #endif
505
506                 if (SOCKET_AF(sock) == IP_NODE(i)->type) {
507                         switch (IP_NODE(i)->type) {
508                         case ipv4:
509                                 re = memcmp (&SOCKET_ADDR_IPv4(sock)->sin_addr, &IP_NODE(i)->ip, 4);
510                                 /*
511                                 printf ("4 remote "); print_ip(ipv4, &SOCKET_ADDRESS_IPv4(sock)); printf ("\n");
512                                 printf ("4 list   "); print_ip(ipv4, &IP_NODE(i)->ip); printf ("\n");
513                                 */
514                                 TRACE (ENTRIES, "IPv4 address (%x)%s matched (ip=%x)\n",
515                                        IP_NODE(i)->ip.ip4,
516                                        re ? " haven't" : "",
517                                        SOCKET_ADDR_IPv4(sock)->sin_addr);
518                                 break;
519 #ifdef HAVE_IPV6
520                         case ipv6:
521                                 re = (! IN6_ARE_ADDR_EQUAL (&SOCKET_ADDR_IPv6(sock)->sin6_addr, &IP_NODE(i)->ip.ip6));
522
523                                 /* re = memcmp (&SOCKET_ADDR_IPv6(sock)->sin6_addr, &IP_NODE(i)->ip, 16); */
524
525                                 /*
526                                 printf ("6 family=%d, ipv6=%d\n", SOCKET_ADDR_IPv6(sock)->sin6_family, ipv6);
527                                 printf ("6 port=%d\n",            SOCKET_ADDR_IPv6(sock)->sin6_port);
528                                 printf ("6 remote "); print_ip(ipv6, &SOCKET_ADDRESS_IPv6(sock)); printf ("\n");
529                                 printf ("6 list   "); print_ip(ipv6, &IP_NODE(i)->ip); printf ("\n");
530                                 printf ("6 re = %d\n", re);
531                                 */
532                                 break;
533 #endif
534                         default:
535                                 SHOULDNT_HAPPEN;
536                                 return ret_error;
537                         }
538
539                         if (re == 0) {
540                                 return ret_ok;
541                         }
542                 }
543         }
544
545
546         /* Check in the Subnets list
547          */
548         list_for_each (i, LIST(&entry->list_subnets)) {
549                 int j;
550                 ip_t masqued_remote, masqued_list;
551
552 #ifdef HAVE_IPV6
553                 if ((SOCKET_AF(sock) == ipv6) &&
554                     (IP_NODE(i)->type == ipv4) &&
555                     IN6_IS_ADDR_V4MAPPED (&SOCKET_ADDR_IPv6(sock)->sin6_addr))
556                 {
557                         cuint_t ip4_sock;
558
559                         memcpy (&ip4_sock, &SOCKET_ADDR_IPv6(sock)->sin6_addr.s6_addr[12], 4);
560
561                         masqued_list.ip4.s_addr   = (IP_NODE(i)->ip.ip4.s_addr &
562                                                      SUBNET_NODE(i)->mask.ip4.s_addr);
563                         masqued_remote.ip4.s_addr = (ip4_sock &
564                                                      SUBNET_NODE(i)->mask.ip4.s_addr);
565                        
566                         if (masqued_remote.ip4.s_addr == masqued_list.ip4.s_addr) {
567                                 return ret_ok;
568                         }
569                 }
570 #endif
571                
572                 if (SOCKET_AF(sock) == IP_NODE(i)->type) {
573                         switch (IP_NODE(i)->type) {
574                         case ipv4:
575                                 masqued_list.ip4.s_addr   = (IP_NODE(i)->ip.ip4.s_addr &
576                                                              SUBNET_NODE(i)->mask.ip4.s_addr);
577                                 masqued_remote.ip4.s_addr = (SOCKET_ADDR_IPv4(sock)->sin_addr.s_addr &
578                                                              SUBNET_NODE(i)->mask.ip4.s_addr);
579                                
580                                 TRACE (ENTRIES, "Checking IPv4 net: (mask=%x) %x == %x ?\n",
581                                        SUBNET_NODE(i)->mask.ip4.s_addr,
582                                        masqued_remote.ip4.s_addr,
583                                        masqued_list.ip4.s_addr);
584
585                                 if (masqued_remote.ip4.s_addr == masqued_list.ip4.s_addr) {
586                                         return ret_ok;
587                                 }
588
589                                 break;
590 #ifdef HAVE_IPV6
591                         case ipv6:
592                         {
593                                 cherokee_boolean_t equal = true;
594
595                                 for (j=0; j<16; j++) {
596                                         masqued_list.ip6.s6_addr[j] = (
597                                                 IP_NODE(i)->ip.ip6.s6_addr[j] &
598                                                 SUBNET_NODE(i)->mask.ip6.s6_addr[j]);
599                                         masqued_remote.ip6.s6_addr[j] = (
600                                                 SOCKET_ADDR_IPv6(sock)->sin6_addr.s6_addr[j] &
601                                                 SUBNET_NODE(i)->mask.ip6.s6_addr[j]);
602
603                                         if (masqued_list.ip6.s6_addr[j] !=
604                                             masqued_remote.ip6.s6_addr[j])
605                                         {
606                                                 equal = false;
607                                                 break;
608                                         }
609                                 }
610
611                                 TRACE (ENTRIES, "Checking IPv6 net: (mask=%x) %x == %x ?\n",
612                                        SUBNET_NODE(i)->mask.ip6.s6_addr,
613                                        masqued_remote.ip6.s6_addr,
614                                        masqued_list.ip6.s6_addr);
615
616
617                                 if (equal == true) {
618                                         return ret_ok;
619                                 }
620                                 break;
621                         }
622 #endif
623                         default:
624                                 SHOULDNT_HAPPEN;
625                                 return ret_error;
626                         }
627                 }
628         }
629
630         return ret_not_found;
631 }
Note: See TracBrowser for help on using the browser.