1 /* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2    It is considered beerware. Prost. Skol. Cheers or whatever.
3 
4    $id$ */
5 
6 /* System */
7 #include <pthread.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <signal.h>
12 #include <unistd.h>
13 
14 /* Libowfat */
15 #include "byte.h"
16 #include "scan.h"
17 #include "ip6.h"
18 #include "mmap.h"
19 
20 /* Opentracker */
21 #include "trackerlogic.h"
22 #include "ot_accesslist.h"
23 #include "ot_vector.h"
24 
25 /* GLOBAL VARIABLES */
26 #ifdef WANT_ACCESSLIST
27        char    *g_accesslist_filename;
28 static ot_hash *g_accesslist;
29 static size_t   g_accesslist_size;
30 static pthread_mutex_t g_accesslist_mutex;
31 
vector_compare_hash(const void * hash1,const void * hash2)32 static int vector_compare_hash(const void *hash1, const void *hash2 ) {
33   return memcmp( hash1, hash2, OT_HASH_COMPARE_SIZE );
34 }
35 
36 /* Read initial access list */
accesslist_readfile(void)37 static void accesslist_readfile( void ) {
38   ot_hash *info_hash, *accesslist_new = NULL;
39   char    *map, *map_end, *read_offs;
40   size_t   maplen;
41 
42   if( ( map = mmap_read( g_accesslist_filename, &maplen ) ) == NULL ) {
43     char *wd = getcwd( NULL, 0 );
44     fprintf( stderr, "Warning: Can't open accesslist file: %s (but will try to create it later, if necessary and possible).\nPWD: %s\n", g_accesslist_filename, wd );
45     free( wd );
46     return;
47   }
48 
49   /* You need at least 41 bytes to pass an info_hash, make enough room
50      for the maximum amount of them */
51   info_hash = accesslist_new = malloc( ( maplen / 41 ) * 20 );
52   if( !accesslist_new ) {
53     fprintf( stderr, "Warning: Not enough memory to allocate %zd bytes for accesslist buffer. May succeed later.\n", ( maplen / 41 ) * 20 );
54     return;
55   }
56 
57   /* No use to scan if there's not enough room for another full info_hash */
58   map_end = map + maplen - 40;
59   read_offs = map;
60 
61   /* We do ignore anything that is not of the form "^[:xdigit:]{40}[^:xdigit:].*" */
62   while( read_offs <= map_end ) {
63     int i;
64     for( i=0; i<(int)sizeof(ot_hash); ++i ) {
65       int eger1 = scan_fromhex( read_offs[ 2*i ] );
66       int eger2 = scan_fromhex( read_offs[ 1 + 2*i ] );
67       if( eger1 < 0 || eger2 < 0 )
68         break;
69       (*info_hash)[i] = eger1 * 16 + eger2;
70     }
71 
72     if( i == sizeof(ot_hash) ) {
73       read_offs += 40;
74 
75       /* Append accesslist to accesslist vector */
76       if( read_offs == map_end || scan_fromhex( *read_offs ) < 0 )
77         ++info_hash;
78     }
79 
80     /* Find start of next line */
81     while( read_offs <= map_end && *(read_offs++) != '\n' );
82   }
83 #ifdef _DEBUG
84   fprintf( stderr, "Added %zd info_hashes to accesslist\n", (size_t)(info_hash - accesslist_new) );
85 #endif
86 
87   mmap_unmap( map, maplen);
88 
89   qsort( accesslist_new, info_hash - accesslist_new, sizeof( *info_hash ), vector_compare_hash );
90 
91   /* Now exchange the accesslist vector in the least race condition prone way */
92   pthread_mutex_lock(&g_accesslist_mutex);
93   free( g_accesslist );
94   g_accesslist      = accesslist_new;
95   g_accesslist_size = info_hash - accesslist_new;
96   pthread_mutex_unlock(&g_accesslist_mutex);
97 }
98 
accesslist_hashisvalid(ot_hash hash)99 int accesslist_hashisvalid( ot_hash hash ) {
100   void *exactmatch;
101 
102   /* Lock should hardly ever be contended */
103   pthread_mutex_lock(&g_accesslist_mutex);
104   exactmatch = bsearch( hash, g_accesslist, g_accesslist_size, OT_HASH_COMPARE_SIZE, vector_compare_hash );
105   pthread_mutex_unlock(&g_accesslist_mutex);
106 
107 #ifdef WANT_ACCESSLIST_BLACK
108   return exactmatch == NULL;
109 #else
110   return exactmatch != NULL;
111 #endif
112 }
113 
accesslist_worker(void * args)114 static void * accesslist_worker( void * args ) {
115   int sig;
116   sigset_t   signal_mask;
117 
118   sigemptyset(&signal_mask);
119   sigaddset(&signal_mask, SIGHUP);
120 
121   (void)args;
122 
123   while( 1 ) {
124 
125     /* Initial attempt to read accesslist */
126     accesslist_readfile( );
127 
128     /* Wait for signals */
129     while( sigwait (&signal_mask, &sig) != 0 && sig != SIGHUP );
130   }
131   return NULL;
132 }
133 
134 static pthread_t thread_id;
accesslist_init()135 void accesslist_init( ) {
136   pthread_mutex_init(&g_accesslist_mutex, NULL);
137   pthread_create( &thread_id, NULL, accesslist_worker, NULL );
138 }
139 
accesslist_deinit(void)140 void accesslist_deinit( void ) {
141   pthread_cancel( thread_id );
142   pthread_mutex_destroy(&g_accesslist_mutex);
143   free( g_accesslist );
144   g_accesslist = 0;
145   g_accesslist_size = 0;
146 }
147 #endif
148 
address_in_net(const ot_ip6 address,const ot_net * net)149 int address_in_net( const ot_ip6 address, const ot_net *net ) {
150   int bits = net->bits;
151   int result = memcmp( address, &net->address, bits >> 3 );
152   if( !result && ( bits & 7 ) )
153     result = ( ( 0x7f00 >> ( bits & 7 ) ) & address[bits>>3] ) - net->address[bits>>3];
154   return result == 0;
155 }
156 
set_value_for_net(const ot_net * net,ot_vector * vector,const void * value,const size_t member_size)157 void *set_value_for_net( const ot_net *net, ot_vector *vector, const void *value, const size_t member_size ) {
158   size_t i;
159   int exactmatch;
160 
161   /* Caller must have a concept of ot_net in it's member */
162   if( member_size < sizeof(ot_net) )
163     return 0;
164 
165   /* Check each net in vector for overlap */
166   uint8_t *member = ((uint8_t*)vector->data);
167   for( i=0; i<vector->size; ++i ) {
168     if( address_in_net( *(ot_ip6*)member, net ) ||
169         address_in_net( net->address, (ot_net*)member ) )
170       return 0;
171     member += member_size;
172   }
173 
174   member = vector_find_or_insert( vector, (void*)net, member_size, sizeof(ot_net), &exactmatch );
175   if( member ) {
176     memcpy( member, net, sizeof(ot_net));
177     memcpy( member + sizeof(ot_net), value, member_size - sizeof(ot_net));
178   }
179 
180   return member;
181 }
182 
183 /* Takes a vector filled with { ot_net net, uint8_t[x] value };
184    Returns value associated with the net, or NULL if not found */
get_value_for_net(const ot_ip6 address,const ot_vector * vector,const size_t member_size)185 void *get_value_for_net( const ot_ip6 address, const ot_vector *vector, const size_t member_size ) {
186   int exactmatch;
187   /* This binary search will return a pointer to the first non-containing network... */
188   ot_net *net = binary_search( address, vector->data, vector->size, member_size, sizeof(ot_ip6), &exactmatch );
189   if( !net )
190     return NULL;
191   /* ... so we'll need to move back one step unless we've exactly hit the first address in network */
192   if( !exactmatch && ( (void*)net > vector->data ) )
193     --net;
194   if( !address_in_net( address, net ) )
195     return NULL;
196   return (void*)net;
197 }
198 
199 #ifdef WANT_FULLLOG_NETWORKS
200 static ot_vector g_lognets_list;
201 ot_log *g_logchain_first, *g_logchain_last;
202 
203 static pthread_mutex_t g_lognets_list_mutex = PTHREAD_MUTEX_INITIALIZER;
loglist_add_network(const ot_net * net)204 void loglist_add_network( const ot_net *net ) {
205   pthread_mutex_lock(&g_lognets_list_mutex);
206   set_value_for_net( net, &g_lognets_list, NULL, sizeof(ot_net));
207   pthread_mutex_unlock(&g_lognets_list_mutex);
208 }
209 
loglist_reset()210 void loglist_reset( ) {
211   pthread_mutex_lock(&g_lognets_list_mutex);
212   free( g_lognets_list.data );
213   g_lognets_list.data = 0;
214   g_lognets_list.size = g_lognets_list.space = 0;
215   pthread_mutex_unlock(&g_lognets_list_mutex);
216 }
217 
loglist_check_address(const ot_ip6 address)218 int loglist_check_address( const ot_ip6 address ) {
219   int result;
220   pthread_mutex_lock(&g_lognets_list_mutex);
221   result = ( NULL != get_value_for_net( address, &g_lognets_list, sizeof(ot_net)) );
222   pthread_mutex_unlock(&g_lognets_list_mutex);
223   return result;
224 }
225 #endif
226 
227 #ifdef WANT_IP_FROM_PROXY
228 typedef struct {
229   ot_net    *proxy;
230   ot_vector  networks;
231 } ot_proxymap;
232 
233 static ot_vector g_proxies_list;
234 static pthread_mutex_t g_proxies_list_mutex = PTHREAD_MUTEX_INITIALIZER;
235 
proxylist_add_network(const ot_net * proxy,const ot_net * net)236 int proxylist_add_network( const ot_net *proxy, const ot_net *net ) {
237   ot_proxymap *map;
238   int exactmatch, result = 1;
239   pthread_mutex_lock(&g_proxies_list_mutex);
240 
241   /* If we have a direct hit, use and extend the vector there */
242   map = binary_search( proxy, g_proxies_list.data, g_proxies_list.size, sizeof(ot_proxymap), sizeof(ot_net), &exactmatch );
243 
244   if( !map || !exactmatch ) {
245     /* else see, if we've got overlapping networks
246        and get a new empty vector if not */
247     ot_vector empty;
248     memset( &empty, 0, sizeof( ot_vector ) );
249     map = set_value_for_net( proxy, &g_proxies_list, &empty, sizeof(ot_proxymap));
250   }
251 
252   if( map && set_value_for_net( net, &map->networks, NULL, sizeof(ot_net) ) )
253        result = 1;
254 
255   pthread_mutex_unlock(&g_proxies_list_mutex);
256   return result;
257 }
258 
proxylist_check_proxy(const ot_ip6 proxy,const ot_ip6 address)259 int proxylist_check_proxy( const ot_ip6 proxy, const ot_ip6 address ) {
260   int result = 0;
261   ot_proxymap *map;
262 
263   pthread_mutex_lock(&g_proxies_list_mutex);
264 
265   if( ( map = get_value_for_net( proxy, &g_proxies_list, sizeof(ot_proxymap) ) ) )
266     if( !address || get_value_for_net( address, &map->networks, sizeof(ot_net) ) )
267       result = 1;
268 
269   pthread_mutex_unlock(&g_proxies_list_mutex);
270   return result;
271 }
272 
273 #endif
274 
275 static ot_ip6         g_adminip_addresses[OT_ADMINIP_MAX];
276 static ot_permissions g_adminip_permissions[OT_ADMINIP_MAX];
277 static unsigned int   g_adminip_count = 0;
278 
accesslist_blessip(ot_ip6 ip,ot_permissions permissions)279 int accesslist_blessip( ot_ip6 ip, ot_permissions permissions ) {
280   if( g_adminip_count >= OT_ADMINIP_MAX )
281     return -1;
282 
283   memcpy(g_adminip_addresses + g_adminip_count,ip,sizeof(ot_ip6));
284   g_adminip_permissions[ g_adminip_count++ ] = permissions;
285 
286 #ifdef _DEBUG
287   {
288     char _debug[512];
289     int off = snprintf( _debug, sizeof(_debug), "Blessing ip address " );
290     off += fmt_ip6c(_debug+off, ip );
291 
292     if( permissions & OT_PERMISSION_MAY_STAT       ) off += snprintf( _debug+off, 512-off, " may_fetch_stats" );
293     if( permissions & OT_PERMISSION_MAY_LIVESYNC   ) off += snprintf( _debug+off, 512-off, " may_sync_live" );
294     if( permissions & OT_PERMISSION_MAY_FULLSCRAPE ) off += snprintf( _debug+off, 512-off, " may_fetch_fullscrapes" );
295     if( permissions & OT_PERMISSION_MAY_PROXY      ) off += snprintf( _debug+off, 512-off, " may_proxy" );
296     if( !permissions ) off += snprintf( _debug+off, sizeof(_debug)-off, " nothing\n" );
297     _debug[off++] = '.';
298     write( 2, _debug, off );
299   }
300 #endif
301 
302   return 0;
303 }
304 
accesslist_isblessed(ot_ip6 ip,ot_permissions permissions)305 int accesslist_isblessed( ot_ip6 ip, ot_permissions permissions ) {
306   unsigned int i;
307   for( i=0; i<g_adminip_count; ++i )
308     if( !memcmp( g_adminip_addresses + i, ip, sizeof(ot_ip6)) && ( g_adminip_permissions[ i ] & permissions ) )
309       return 1;
310   return 0;
311 }
312 
313 const char *g_version_accesslist_c = "$Source$: $Revision$\n";
314