1 %{
2 
3 /*
4  * ufdbGuard is copyrighted (C) 2005-2020 by URLfilterDB B.V. with all rights reserved.
5  *
6  * Parts of the ufdbGuard daemon are based on squidGuard.
7  * squidGuard is copyrighted (C) 1998 by
8  * ElTele �st AS, Oslo, Norway, with all rights reserved.
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License (version 2) as
12  * published by the Free Software Foundation.  It is distributed in the
13  * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE.  See the GNU General Public License (GPL) for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * (GPL2) along with this program.
19  */
20 
21 #define YYMALLOC  ufdbMalloc
22 #define YYFREE    ufdbFree
23 
24 #define UFDB_LOG_USER_QUOTA   0   /* TODO remove */
25 
26 #undef UFDB_DEBUG_ACL_ACCESS
27 #define UFDB_DEBUG_ACL_ACCESS 1
28 
29 #include "ufdb.h"
30 #include "sg.h"
31 #include "ufdb_globals.h"
32 #include "ufdblib.h"
33 #include "ufdbdb.h"
34 #include "ufdbchkport.h"
35 
36 #define UFDB_DEBUG 1
37 
38 #if UFDB_DEBUG
39 #undef YYDEBUG
40 #define YYDEBUG 1
41 #endif
42 
43 #define UFDB_TIME_DEBUG 0
44 
45 #if __linux__
46 #include <sys/mman.h>
47 #endif
48 #include <pthread.h>
49 #include <sys/types.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <errno.h>
53 #include <netdb.h>
54 #include <grp.h>
55 #include <syslog.h>
56 #include <signal.h>
57 #include <sched.h>
58 #include <fcntl.h>
59 #include <sys/stat.h>
60 #include <unistd.h>
61 #include <sys/socket.h>
62 
63 
64 extern FILE * yyin;
65 extern FILE * yyout;
66 
67 int    lineno;
68 
69 static int   numTimeElements = 0;
70 static int * TimeElementsEvents = NULL;
71 
72 static int   time_switch = 0;
73 static int   date_switch = 0;
74 
75 static void setDBhome( char * dbhome );
76 static void setAdministrator( char * value );
77 static void setLogdir( char * value );
78 static void setPidfile( char * value );
79 static void setRefreshUserlist( int nmin );
80 static void setRefreshDomainlist( int nmin );
81 static void setRefreshIPlist( int nmin );
82 static void ufdbSourceExecIPList( char * command );
83 
84 static void ufdbSourceGroup( int groupType, char * groupName );
85 static void ufdbSourceUserQuota( const char * seconds, const char * sporadic, const char * renew );
86 static void ufdbSourceUser( char * user );
87 static void ufdbSourceUserList( char * file );
88 static void ufdbSourceExecUserList( char * command );
89 
90 static void defSource( char * source );
91 static void defSourceEnd( void );
92 static struct Source * defSourceFindName( struct Source * slist, const char * name );
93 static void defSourceDomain( char * );
94 static void ufdbSourceEval( int method  );
95 static void defSourceIPV4List( char * file );
96 static void defSourceIPV6List( char * file );
97 
98 static void ufdbCategoryCACertsFile( char * cacertsFile );
99 static void ufdbCategoryCACertsDir( char * cacertsDir );
100 
101 static void sgIpv4( const char * name, int type, const char * file, int lineno );
102 static void sgIpv6( const char * addr, int type, const char * file, int line );
103 
104 void ufdbFreeDomainDb( struct sgDb * dbp );
105 
106 static void   ufdbAcl( const char * name, const char * value, int within );
107 static void   ufdbAclSetValue( const char * what, const char * value, int allowed );
108 
109 static void   sgTime( char * );
110 static struct ufdbTime * sgTimeFindName( const char * name );
111 static void   sgTimeElementInit( void );
112 static void   sgTimeElementAdd( char *, char );
113 static void   sgTimeElementEnd( void );
114 static void   sgTimeElementClone( void );
115 static void   defSourceTime( char * name, int within );
116 
117 static void   ufdbCategoryTime( char * name, int within );
118 static void   ufdbCategoryActiveBumping( int flag );
119 static void   ufdbCategoryBlockConnect( int flag );
120 static void   ufdbCategoryRewrite( char * value );
121 static void   ufdbCategoryRedirect( char * value );
122 static void   ufdbCategoryExecDomainList( char * command );
123 
124 static void   ufdbCategoryUrlList( char * urllist );
125 static void   ufdbCategoryOption( int value, int option );
126 
127 static void   sgRewrite( char * rewrite );
128 static void   sgRewriteTime( char * name, int within );
129 static void   sgRewriteSubstitute( char * string );
130 static struct sgRewrite * sgRewriteFindName( const char * name );
131 
132 static void   logConfig( void );
133 
134 %}
135 
136 %union {
137   char * string;
138   char * tval;
139   char * dval;
140   char * dvalcron;
141   int    integer;
142 }
143 
144 %token ADMINISTRATOR QSTRING
145 %token CHECK_PROXY_TUNNELS QUEUE_CHECKS AGGRESSIVE LOG_ONLY
146 %token ON OFF
147 %token CHAR_MINUS CHAR_I CHAR_EXCLAMATION
148 %token IGNORECASE
149 %token COMMA EQUAL PORT
150 %token HTTP_SERVER INTERFACE IMAGES
151 %token HTTPS_PROHIBIT_INSECURE_SSLV2 HTTPS_PROHIBIT_INSECURE_SSLV3
152 %token HTTPS_CONNECTION_CACHE_SIZE
153 %token IDENTIFIER WORD END START_BRACKET STOP_BRACKET WEEKDAY
154 %token CATEGORY REWRITE ACL CPUS TIME TVAL DVAL DVALCRON
155 %token BLOCK_BUMPED_CONNECT EVALUATE_AND EVALUATE_OR
156 %token SOURCE CONTINUE
157 %token IPV4ADDR IPV4NET IPV4CLASS IPV4RANGE
158 %token IPV6ADDR IPV6NET
159 %token DBHOME DOMAINLIST EXECDOMAINLIST REFRESHDOMAINLIST
160 %token URLLIST EXPRESSIONLIST CACERTS CACERTSDIR
161 %token IPV4 IPV4LIST IPV6 IPV6LIST
162 %token DOMAIN UNIX LDAP USER USERLIST EXECUSERLIST REFRESHUSERLIST
163 %token EXECIPLIST REFRESHIPLIST
164 %token USERQUOTA GROUP
165 %token NL NUMBER NUMBERS
166 %token PASS REDIRECT SUBST CHAR MINUTELY HOURLY DAILY WEEKLY DATE
167 %token REDIRECT_FATAL_ERROR REDIRECT_LOADING_DATABASE REDIRECT_HTTPS REDIRECT_BUMPED_HTTPS
168 %token WITHIN OUTSIDE ELSE ANONYMOUS SPORADIC
169 %token PIDFILE LOGFILE LOGDIR LOGPASS LOGBLOCK LOGALL LOGALL_HTTPD
170 %token MAIL_SERVER MY_HOSTNAME ADMIN_EMAIL SENDER_EMAIL EXTERNAL_STATUS_COMMAND
171 %token TOKEN_ALLOW TOKEN_DENY
172 %token YOUTUBE_EDUFILTER YOUTUBE_EDUFILTER_ID ALLOW_GOOGLE_HTTPS_USING_IP
173 %token URL_LOOKUP_RESULT_DB_RELOAD URL_LOOKUP_RESULT_FATAL_ERROR
174 %token URL_LOOKUP_DELAY_DB_RELOAD
175 %token OPTION UFDB_SHOW_URL_DETAILS UFDB_LOG_URL_DETAILS
176 %token SQUID_VERSION SQUID_USES_ACTIVE_BUMPING
177 %token UPLOAD_CRASH_REPORTS LOOKUP_REVERSE_IP USE_IPV6_ON_WAN PARSE_URL_PARAMETERS
178 %token STRIP_DOMAIN_FROM_USERNAME
179 %token UFDB_DEBUG_FILTER UFDB_DEBUG_COREDUMP
180 %token MADVISE_HUGEPAGES FAST_REFRESH
181 %token UFDB_DEBUG_SKYPE_PROBES UFDB_DEBUG_GTALK_PROBES
182 %token UFDB_DEBUG_YAHOOMSG_PROBES UFDB_DEBUG_AIM_PROBES UFDB_DEBUG_FBCHAT_PROBES
183 %token UFDB_DEBUG_CITRIXONLINE_PROBES
184 %token UFDB_EXPRESSION_OPTIMISATION UFDB_EXPRESSION_DEBUG UFDB_DEBUG_EXTERNAL_SCRIPTS
185 %token UFDB_NUM_WORKER_THREADS
186 %token ENFORCE_HTTPS_WITH_HOSTNAME ENFORCE_HTTPS_OFFICAL_CERTIFICATE
187 %token ALLOW_SKYPE_OVER_HTTPS ALLOW_UNKNOWN_PROTOCOL_OVER_HTTPS
188 %token ALLOW_GTALK_OVER_HTTPS ALLOW_YAHOOMSG_OVER_HTTPS
189 %token ALLOW_AIM_OVER_HTTPS ALLOW_FBCHAT_OVER_HTTPS ALLOW_CITRIXONLINE_OVER_HTTPS
190 %token ALLOW_ANYDESK_OVER_HTTPS ALLOW_TEAMVIEWER_OVER_HTTPS
191 %token ANALYSE_UNCATEGORISED LOG_UNCATEGORISED_URLS UPLOAD_STATS
192 %token SAFE_SEARCH MAX_LOGFILE_SIZE
193 
194 
195 %type <string> IDENTIFIER
196 %type <string> WORD
197 %type <string> QSTRING
198 %type <string> WEEKDAY
199 %type <string> NUMBER
200 %type <string> NUMBERS
201 %type <tval>   TVAL
202 %type <string> DVAL
203 %type <string> DVALCRON
204 %type <string> CHAR
205 %type <string> SUBST
206 %type <string> IPV4ADDR
207 %type <string> IPV4NET
208 %type <string> IPV4RANGE
209 %type <string> IPV4CLASS
210 %type <string> IPV6ADDR
211 %type <string> IPV6NET
212 %type <string> DBHOME LOGDIR
213 %type <string> dval
214 %type <string> dvalcron
215 %type <string> tval
216 %type <string> date
217 %type <string> ttime
218 %type <string> qidentifier
219 
220 %type <integer> check_proxy_tunnel_option
221 %type <integer> allow_or_deny
222 %type <integer> on_or_off
223 
224 %%
225 
226 start: statements
227        ;
228 
229 qidentifier:
230 	   	  IDENTIFIER    { $$ = $1; }
231 	 	| QSTRING       { $$ = $1; }
232 	 	;
233 
234 https_cache_size:
235            	  HTTPS_CONNECTION_CACHE_SIZE NUMBER   { ufdbNewGV.httpsConnectionCacheSize = atoi( $2 );
236 		                                         ufdbFree( $2 ); }
237 	 	;
238 
239 allow_or_deny:
240 		  TOKEN_ALLOW   { $$ = UFDB_ALLOW; }
241 	        | TOKEN_DENY    { $$ = UFDB_DENY; }
242 		;
243 
244 on_or_off:
245 	   	  ON    { $$ = 1; }
246 	 	| OFF   { $$ = 0; }
247 		| error { ufdbLogFatalError( "syntax error at line %d: expected 'on' or 'off'", lineno );   }
248 	 	;
249 
250 log_pass:
251 	   	  LOGPASS on_or_off   	{ ufdbNewGV.logPass = $2; }
252 	 	;
253 
254 log_block:
255 	   	  LOGBLOCK on_or_off   	{ ufdbNewGV.logBlock = $2; }
256 	 	;
257 
258 log_all:
259 	   	  LOGALL on_or_off     	{ ufdbNewGV.logAllRequests = $2; }
260 	 	;
261 
262 logall_httpd:
263 	   	  LOGALL_HTTPD on_or_off     	 { ufdbNewGV.debugHttpd = $2; }
264 	 	;
265 
266 youtube_edufilter:
267 		  YOUTUBE_EDUFILTER on_or_off    { ufdbNewGV.YoutubeEdufilter = $2; }
268 	        ;
269 
270 youtube_edufilter_id:
271 		  YOUTUBE_EDUFILTER_ID QSTRING   { ufdbNewGV.YoutubeEdufilterID = $2; }
272 	        ;
273 
274 allow_google_https_using_ip:
275 		  ALLOW_GOOGLE_HTTPS_USING_IP on_or_off   { ufdbNewGV.allowGoogleHTTPSusingIP = $2; }
276 	        ;
277 
278 madvise_hugepages:
279                   MADVISE_HUGEPAGES on_or_off   { ufdbNewGV.madviseHugePages = $2; }
280                 ;
281 
282 fast_refresh:
283                   FAST_REFRESH on_or_off        { ufdbNewGV.fastRefresh = $2; }
284                 ;
285 
286 debug_filter:
287 		  UFDB_DEBUG_FILTER on_or_off   { ufdbNewGV.debug = ufdbGV.debug = $2; }
288 	        | UFDB_DEBUG_FILTER NUMBER      { ufdbNewGV.debug = ufdbGV.debug = atoi( $2 );
289 		                                  ufdbFree( $2 ); }
290 	        ;
291 
292 debug_coredump:
293 		  UFDB_DEBUG_COREDUMP on_or_off { ufdbNewGV.debugCoreDump = $2; }
294 	        ;
295 
296 enforce_https_with_hostname:
297 	   	  ENFORCE_HTTPS_WITH_HOSTNAME on_or_off
298 	   	    { ufdbLogError( "line %d: option enforce-https-with-hostname must be part of "
299                                     "the security category", lineno );  }
300 	 	;
301 
302 enforce_https_offical_certificate:
303            	ENFORCE_HTTPS_OFFICAL_CERTIFICATE on_or_off
304 	   	  { ufdbLogError( "line %d: option enforce-https-official-certificate must be part of "
305                                   "the security category", lineno );  }
306 	 	;
307 
308 https_prohibit_insecure_sslv2:
309 	   	  HTTPS_PROHIBIT_INSECURE_SSLV2 on_or_off
310 	   	    { ufdbLogError( "line %d: option https-prohibit-insecure-sslv2 must be part of "
311                                     "the security category", lineno );  }
312 	 	;
313 
314 https_prohibit_insecure_sslv3:
315 	   	  HTTPS_PROHIBIT_INSECURE_SSLV3 on_or_off
316 	   	    { ufdbLogError( "line %d: option https-prohibit-insecure-sslv3 must be part of "
317                                     "the security category", lineno );  }
318 	 	;
319 
320 
321 check_proxy_tunnel_option:
322 	   	  OFF           { $$ = UFDB_API_HTTPS_CHECK_OFF; }
323 	 	| QUEUE_CHECKS  { $$ = UFDB_API_HTTPS_CHECK_QUEUE_CHECKS; }
324 	 	| AGGRESSIVE    { $$ = UFDB_API_HTTPS_CHECK_AGGRESSIVE; }
325 	 	| LOG_ONLY      { $$ = UFDB_API_HTTPS_LOG_ONLY; }
326 	 	;
327 
328 check_proxy_tunnels:
329            	  CHECK_PROXY_TUNNELS check_proxy_tunnel_option { ufdbNewGV.tunnelCheckMethod = $2; }
330 	 	;
331 
332 admin_spec:
333 		  ADMINISTRATOR QSTRING  { setAdministrator( $2 ); }
334 	   	;
335 
336 dbhome:
337 		  DBHOME qidentifier { setDBhome( $2 ); }
338 		| DBHOME WORD        { setDBhome( $2 ); }
339            	;
340 
341 refreshuserlist:
342                   REFRESHUSERLIST  NUMBER     { setRefreshUserlist( atoi($2) );
343 		                                ufdbFree( $2 ); }
344 	        ;
345 
346 refreshiplist:
347                   REFRESHIPLIST  NUMBER       { setRefreshIPlist( atoi($2) );
348 		                                ufdbFree( $2 ); }
349 	        ;
350 
351 refreshdomainlist:
352                   REFRESHDOMAINLIST  NUMBER   { setRefreshDomainlist( atoi($2) );
353 		                                ufdbFree( $2 ); }
354 	        ;
355 
356 url_lookup_result_db_reload:
357 		  URL_LOOKUP_RESULT_DB_RELOAD allow_or_deny
358 		  			{ ufdbNewGV.URLlookupResultDBreload = $2; }
359 
360 url_lookup_result_fatal_error:
361 		  URL_LOOKUP_RESULT_FATAL_ERROR allow_or_deny
362 		  			{ ufdbNewGV.URLlookupResultFatalError = $2; }
363 
364 url_lookup_delay_db_reload:
365 		  URL_LOOKUP_DELAY_DB_RELOAD on_or_off
366 		  			{ ufdbNewGV.URLlookupDelayDBreload = $2; }
367 
368 squid_version:
369 		  SQUID_VERSION QSTRING
370 		  			{ ufdbFree( ufdbNewGV.SquidVersion );  ufdbNewGV.SquidVersion = $2;  }
371 	        ;
372 
373 num_worker_threads:
374 		  UFDB_NUM_WORKER_THREADS NUMBER
375 		  			{ int new_n_workers;
376 					  /* ONLY increase #workers */
377 					  new_n_workers = atoi( $2 );
378 		                          ufdbFree( $2 );
379 					  if (new_n_workers < UFDB_MIN_THREADS)
380 					  {
381 					     ufdbLogError( "num-worker-threads must be at least %d",
382                                                            UFDB_MIN_THREADS );
383 					     new_n_workers = UFDB_MIN_THREADS;
384 					  }
385 					  else if (new_n_workers > UFDB_MAX_THREADS)
386 					  {
387 					     ufdbLogError( "num-worker-threads can be at most %d",
388                                                            UFDB_MAX_THREADS );
389 					     new_n_workers = UFDB_MAX_THREADS;
390 					  }
391 					  if (new_n_workers > ufdbNewGV.nWorkers)
392 					     ufdbNewGV.nWorkers = new_n_workers;
393 					}
394 	        ;
395 
396 upload_crash_reports:
397 		  UPLOAD_CRASH_REPORTS on_or_off
398 		  			{ ufdbNewGV.uploadCrashReports = $2;  }
399 	        ;
400 
401 lookup_reverse_ip:
402 		  LOOKUP_REVERSE_IP on_or_off
403 		  			{ ufdbNewGV.lookupReverseIP = $2;  }
404 	        ;
405 
406 use_ipv6_on_wan:
407 		  USE_IPV6_ON_WAN on_or_off
408 		  			{ ufdbNewGV.useAlsoIPv6onWan = $2;  }
409 	        ;
410 
411 parse_url_parameters:
412 		  PARSE_URL_PARAMETERS on_or_off
413 		  			{ ufdbNewGV.parseURLparameters = $2;  }
414 	        ;
415 
416 squid_uses_active_bumping:
417 		  SQUID_USES_ACTIVE_BUMPING on_or_off
418 		  			{ ufdbNewGV.SquidUsesActiveBumping = $2;  }
419 	        ;
420 
421 ufdb_log_url_details:
422 		  UFDB_LOG_URL_DETAILS on_or_off  { ufdbNewGV.logURLdetails = $2; }
423 	        ;
424 
425 ufdb_show_url_details:
426 		  UFDB_SHOW_URL_DETAILS on_or_off  { ufdbNewGV.showURLdetails = $2; }
427 	        ;
428 
429 ufdb_debug_skype_probes:
430 		  UFDB_DEBUG_SKYPE_PROBES on_or_off  { ufdbNewGV.debugSkype = $2; }
431 	        ;
432 
433 ufdb_debug_gtalk_probes:
434 		  UFDB_DEBUG_GTALK_PROBES on_or_off  { ufdbNewGV.debugGtalk = $2; }
435 	        ;
436 
437 ufdb_debug_yahoomsg_probes:
438 		  UFDB_DEBUG_YAHOOMSG_PROBES on_or_off  { ufdbNewGV.debugYahooMsg = $2; }
439 	        ;
440 
441 ufdb_debug_aim_probes:
442 		  UFDB_DEBUG_AIM_PROBES on_or_off  { ufdbNewGV.debugAim = $2; }
443 	        ;
444 
445 ufdb_debug_fbchat_probes:
446 		  UFDB_DEBUG_FBCHAT_PROBES on_or_off  { ufdbNewGV.debugFBchat = $2; }
447 	        ;
448 
449 ufdb_debug_citrixonline_probes:
450 		  UFDB_DEBUG_CITRIXONLINE_PROBES on_or_off  { ufdbNewGV.debugCitrixOnline = $2; }
451 	        ;
452 
453 ufdb_expression_optimisation:
454 		  UFDB_EXPRESSION_OPTIMISATION on_or_off  { ufdbNewGV.expressionOptimisation = $2; }
455 	        ;
456 
457 ufdb_expression_debug:
458 		  UFDB_EXPRESSION_DEBUG on_or_off  { ufdbNewGV.debugRegexp = $2; }
459 	        ;
460 
461 ufdb_debug_external_scripts:
462 		  UFDB_DEBUG_EXTERNAL_SCRIPTS on_or_off  { ufdbNewGV.debugExternalScripts = $2; }
463 	        ;
464 
465 mail_server:
466 		  MAIL_SERVER QSTRING  { ufdbFree( ufdbNewGV.emailServer ); ufdbNewGV.emailServer = $2; }
467 	        ;
468 
469 my_hostname:
470 		  MY_HOSTNAME QSTRING  { ufdbFree( ufdbNewGV.myHostname ); ufdbNewGV.myHostname = $2; }
471 	        ;
472 
473 admin_email:
474 		  ADMIN_EMAIL QSTRING  { ufdbFree( ufdbNewGV.adminEmail ); ufdbNewGV.adminEmail = $2; }
475 	        ;
476 
477 sender_email:
478 		  SENDER_EMAIL QSTRING { ufdbFree( ufdbNewGV.senderEmail ); ufdbNewGV.senderEmail = $2; }
479 	        ;
480 
481 external_status_command:
482 		  EXTERNAL_STATUS_COMMAND  QSTRING  {  ufdbFree( ufdbNewGV.externalStatusCommand );
483 		                                       ufdbNewGV.externalStatusCommand = $2; }
484 	        ;
485 
486 logdir:
487 		  LOGDIR qidentifier   { setLogdir( $2 ); }
488 		| LOGDIR WORD          { setLogdir( $2 ); }
489             	;
490 
491 pidfile:
492 		  PIDFILE qidentifier  { setPidfile( $2 ); }
493 		| PIDFILE WORD         { setPidfile( $2 ); }
494             	;
495 
496 port:
497 		  PORT NUMBER          { ufdbNewGV.portNum = atoi( $2 );  ufdbFree( $2 );
498                                          if (ufdbNewGV.portNum <= 0)
499                                          {
500                                             ufdbLogError( "port number must be > 0, using default port %d",
501                                                           UFDB_DAEMON_PORT );
502                                             ufdbNewGV.portNum = UFDB_DAEMON_PORT;
503                                          }
504                                        }
505                 ;
506 
507 interface:
508 	          INTERFACE qidentifier    {
509 #if HAVE_UNIX_SOCKETS
510                                              ufdbLogError( "ufdbguardd is configured to use UNIX sockets.  "
511 					                   "\"interface\" is ignored." );
512 #else
513 		                             if (strcmp( $2, "all" ) == 0)
514 						strcpy( ufdbNewGV.interface, "all" );
515 					     else
516 					        ufdbLogFatalError( "interface must be \"all\" or IP address" );
517 #endif
518 					     ufdbFree( $2 );
519 					   }
520 	        | INTERFACE IPV4ADDR { strcpy( ufdbNewGV.interface, $2 );  ufdbFree( $2 ); }
521 		;
522 
523 cpus:
524 		  CPUS NUMBER        { ufdbSetCPU( $2 ); ufdbFree( $2 ); }
525          	| CPUS NUMBERS       { ufdbSetCPU( $2 ); ufdbFree( $2 ); }
526            	;
527 
528 upload_stats:
529                	  UPLOAD_STATS on_or_off
530 		     { ufdbNewGV.uploadStats = $2;   }
531 	        ;
532 
533 redirect_https:
534 		  REDIRECT_HTTPS  QSTRING
535 		     { strcpy( ufdbNewGV.redirectHttps, $2 );  ufdbFree( $2 );  }
536 	        ;
537 
538 redirect_bumped_https:
539 		  REDIRECT_BUMPED_HTTPS  QSTRING
540 		     { strcpy( ufdbNewGV.redirectBumpedHttps, $2 );  ufdbFree( $2 );  }
541 	        ;
542 
543 redirect_loading_database:
544 		  REDIRECT_LOADING_DATABASE  QSTRING
545 		     { strcpy( ufdbNewGV.loadingDatabaseRedirect, $2 );  ufdbFree( $2 );  }
546 	        ;
547 
548 redirect_fatal_error:
549 		  REDIRECT_FATAL_ERROR  QSTRING
550 		     { strcpy( ufdbNewGV.fatalErrorRedirect, $2 );  ufdbFree( $2 );  }
551 	        ;
552 
553 log_uncategorised_urls:
554                   LOG_UNCATEGORISED_URLS on_or_off
555                      {
556 			ufdbNewGV.logUncategorisedURLs = $2;
557 		     }
558 	        ;
559 
560 analyse_uncategorised:
561                	  ANALYSE_UNCATEGORISED on_or_off
562 		     {
563 			ufdbNewGV.analyseUncategorisedURLs = $2;
564 		     }
565 	        |
566                	  ANALYSE_UNCATEGORISED NUMBER
567 		     {
568 			ufdbNewGV.analyseUncategorisedURLs = atoi( $2 );
569 			ufdbFree( $2 );
570 		     }
571 	       	;
572 
573 strip_domain_from_username:
574 		  STRIP_DOMAIN_FROM_USERNAME on_or_off
575 		     {
576 		        ufdbNewGV.stripDomainFromUsername = $2;
577 		     }
578 	       ;
579 
580 safe_search:
581 		  SAFE_SEARCH on_or_off
582 		     { ufdbNewGV.safeSearch = $2; }
583 	        ;
584 
585 max_logfile_size:
586 		  MAX_LOGFILE_SIZE NUMBER
587 		     {
588 		       ufdbNewGV.maxLogfileSize = strtoul( $2, NULL, 10 );
589 		       ufdbFree( $2 );
590 		       if (ufdbNewGV.maxLogfileSize < 2 * 1024 * 1024)		// minimum is 2 MB
591 		          ufdbNewGV.maxLogfileSize = 2 * 1024 * 1024;
592 		       if (ufdbNewGV.maxLogfileSize > 2000000000)		        // maximum is 2 GB
593 		          ufdbNewGV.maxLogfileSize = 2000000000;
594 		     }
595 	        ;
596 
597 httpd_option:
598 		  PORT EQUAL NUMBER        { ufdbNewGV.httpdPort = atoi( $3 ); ufdbFree( $3 ); }
599 	        | INTERFACE EQUAL qidentifier { if (strcmp($3,"all")== 0)
600 						strcpy( ufdbNewGV.httpdInterface, "all" );
601 					     else
602 					        ufdbLogFatalError( "http-server interface must be \"all\" or IP address" );
603 					     ufdbFree( $3 );
604 					   }
605 	        | INTERFACE EQUAL WORD     { if (strcmp($3,"all")== 0)
606 						strcpy( ufdbNewGV.httpdInterface, "all" );
607 					     else
608 					        ufdbLogFatalError( "http-server interface must be \"all\" or IP address" );
609 					     ufdbFree( $3 );
610 					   }
611 	        | INTERFACE EQUAL IPV4ADDR { strcpy( ufdbNewGV.httpdInterface, $3 );       ufdbFree( $3 ); }
612 		| IMAGES EQUAL qidentifier { strcpy( ufdbNewGV.httpdImagesDirectory, $3 ); ufdbFree( $3 ); }
613 		| IMAGES EQUAL WORD        { strcpy( ufdbNewGV.httpdImagesDirectory, $3 ); ufdbFree( $3 ); }
614 		;
615 
616 httpd_options:
617 		  httpd_option COMMA httpd_options
618 		| httpd_option
619 	        ;
620 
621 http_server_def:
622 		  HTTP_SERVER START_BRACKET httpd_options STOP_BRACKET
623 	        ;
624 
625 category:
626 		  CATEGORY qidentifier { ufdbCategory( $2 ); }
627 		| CATEGORY AGGRESSIVE  { ufdbCategory( ufdbStrdup("aggressive") );
628 		                         yyerror( (char *) "\"aggressive\" is a keyword and must be surrounded by quotes" ); }
629 		| CATEGORY TOKEN_ALLOW { ufdbCategory( ufdbStrdup("allow") );
630 		                         yyerror( (char *) "\"allow\" is a keyword and must be surrounded by quotes" ); }
631 		| CATEGORY TOKEN_DENY  { ufdbCategory( ufdbStrdup("deny") );
632 		                         yyerror( (char *) "\"deny\" is a keyword and must be surrounded by quotes" ); }
633 		| CATEGORY error       { ufdbCategory( ufdbStrdup("syntax-error") );
634 		                         yyerror( (char *) "erroneous category definition.  Perhaps the category ID is a reserved word?" ); }
635              	;
636 
637 category_block:
638 		  category START_BRACKET category_contents STOP_BRACKET
639                        { ufdbCategoryEnd(); }
640                 ;
641 
642 category_contents: %empty
643                 | category_contents category_content
644 		;
645 
646 category_content:
647  	      	  DOMAINLIST qidentifier                { ufdbCategoryDomainList( $2 ); }
648  	      	| DOMAINLIST WORD                       { ufdbCategoryDomainList( $2 ); }
649             	| DOMAINLIST CHAR_MINUS                 { ufdbCategoryDomainList( NULL ); }
650 		| EXECDOMAINLIST qidentifier            { ufdbCategoryExecDomainList( $2 ); }
651             	| URLLIST qidentifier                   { ufdbCategoryUrlList( $2 ); }
652             	| URLLIST WORD                          { ufdbCategoryUrlList( $2 ); }
653             	| URLLIST CHAR_MINUS                    { ufdbCategoryUrlList( NULL ); }
654             	| EXPRESSIONLIST qidentifier            { ufdbCategoryExpressionList( $2, "n" ); }
655             	| EXPRESSIONLIST WORD                   { ufdbCategoryExpressionList( $2, "n" ); }
656             	| EXPRESSIONLIST CHAR_MINUS             { ufdbCategoryExpressionList( NULL, NULL ); }
657             	| EXPRESSIONLIST CHAR_I qidentifier     { ufdbCategoryExpressionList( $3, "i" ); }
658             	| EXPRESSIONLIST CHAR_I WORD            { ufdbCategoryExpressionList( $3, "i" ); }
659             	| EXPRESSIONLIST IGNORECASE qidentifier { ufdbCategoryExpressionList( $3, "i" ); }
660             	| EXPRESSIONLIST IGNORECASE WORD        { ufdbCategoryExpressionList( $3, "i" ); }
661 		| CACERTS qidentifier                   { ufdbCategoryCACertsFile( $2 );  ufdbFree( $2 ); }
662 		| CACERTS WORD                          { ufdbCategoryCACertsFile( $2 );  ufdbFree( $2 ); }
663 		| CACERTSDIR qidentifier                { ufdbCategoryCACertsDir( $2 );  ufdbFree( $2 ); }
664 		| CACERTSDIR WORD                       { ufdbCategoryCACertsDir( $2 );  ufdbFree( $2 ); }
665             	| REDIRECT qidentifier                  { ufdbCategoryRedirect( $2 ); }
666             	| REDIRECT WORD                         { ufdbCategoryRedirect( $2 ); }
667             	| REWRITE qidentifier                   { ufdbCategoryRewrite( $2 ); }
668             	| REWRITE WORD                          { ufdbCategoryRewrite( $2 ); }
669             	| WITHIN qidentifier                    { ufdbCategoryTime( $2, UFDB_ACL_WITHIN ); }
670             	| OUTSIDE qidentifier                   { ufdbCategoryTime( $2, UFDB_ACL_OUTSIDE ); }
671 		| OPTION BLOCK_BUMPED_CONNECT on_or_off              { ufdbCategoryBlockConnect( $3 ); }
672                 | OPTION SQUID_USES_ACTIVE_BUMPING on_or_off         { ufdbCategoryActiveBumping( $3 ); }
673 		| OPTION SAFE_SEARCH on_or_off                       { ufdbCategoryOption( $3, UFDB_OPT_SAFE_SEARCH );  }
674 		| OPTION YOUTUBE_EDUFILTER on_or_off                 { ufdbCategoryOption( $3, UFDB_OPT_YOUTUBE_EDUFILTER );  }
675 		| OPTION ENFORCE_HTTPS_WITH_HOSTNAME                 { ufdbCategoryOption(  1, UFDB_OPT_HTTPS_WITH_HOSTNAME );  }
676 		| OPTION ENFORCE_HTTPS_WITH_HOSTNAME on_or_off       { ufdbCategoryOption( $3, UFDB_OPT_HTTPS_WITH_HOSTNAME );  }
677 		| OPTION ENFORCE_HTTPS_OFFICAL_CERTIFICATE           { ufdbCategoryOption(  1, UFDB_OPT_HTTPS_OFFICAL_CERTIFICATE );  }
678 		| OPTION ENFORCE_HTTPS_OFFICAL_CERTIFICATE on_or_off { ufdbCategoryOption( $3, UFDB_OPT_HTTPS_OFFICAL_CERTIFICATE );  }
679 		| OPTION HTTPS_PROHIBIT_INSECURE_SSLV2 on_or_off     { ufdbCategoryOption( $3, UFDB_OPT_PROHIBIT_INSECURE_SSLV2 );  }
680 		| OPTION HTTPS_PROHIBIT_INSECURE_SSLV3 on_or_off     { ufdbCategoryOption( $3, UFDB_OPT_PROHIBIT_INSECURE_SSLV3 );  }
681 		| OPTION ALLOW_SKYPE_OVER_HTTPS on_or_off	     { ufdbCategoryOption( $3, UFDB_OPT_SKYPE_OVER_HTTPS );  }
682 		| OPTION ALLOW_GTALK_OVER_HTTPS on_or_off	     { ufdbCategoryOption( $3, UFDB_OPT_GTALK_OVER_HTTPS );  }
683 		| OPTION ALLOW_YAHOOMSG_OVER_HTTPS on_or_off	     { ufdbCategoryOption( $3, UFDB_OPT_YAHOOMSG_OVER_HTTPS );  }
684 		| OPTION ALLOW_AIM_OVER_HTTPS on_or_off	             { ufdbCategoryOption( $3, UFDB_OPT_AIM_OVER_HTTPS );  }
685 		| OPTION ALLOW_FBCHAT_OVER_HTTPS on_or_off	     { ufdbCategoryOption( $3, UFDB_OPT_FBCHAT_OVER_HTTPS );  }
686 		| OPTION ALLOW_CITRIXONLINE_OVER_HTTPS on_or_off     { ufdbCategoryOption( $3, UFDB_OPT_CITRIXONLINE_OVER_HTTPS );  }
687 		| OPTION ALLOW_ANYDESK_OVER_HTTPS on_or_off          { ufdbCategoryOption( $3, UFDB_OPT_ANYDESK_OVER_HTTPS );  }
688 		| OPTION ALLOW_TEAMVIEWER_OVER_HTTPS on_or_off       { ufdbCategoryOption( $3, UFDB_OPT_TEAMVIEWER_OVER_HTTPS );  }
689 		| OPTION ALLOW_UNKNOWN_PROTOCOL_OVER_HTTPS on_or_off { ufdbCategoryOption( $3, UFDB_OPT_UNKNOWN_PROTOCOL_OVER_HTTPS );  }
690             	| LOGFILE ANONYMOUS qidentifier { ufdbLogError( "line %d: unsupported logfile context for %s", lineno, $3 );
691 		                                  ufdbFree( $3 ); }
692             	| LOGFILE ANONYMOUS WORD        { ufdbLogError( "line %d: unsupported logfile context for %s", lineno, $3 );
693 		                                  ufdbFree( $3 ); }
694             	| LOGFILE qidentifier           { ufdbLogError( "line %d: unsupported logfile context for %s", lineno, $2 );
695 		                                  ufdbFree( $2 ); }
696             	| LOGFILE WORD                  { ufdbLogError( "line %d: unsupported logfile context for %s", lineno, $2 );
697 		                                  ufdbFree( $2 ); }
698             	;
699 
700 source:
701 		  SOURCE qidentifier { defSource( $2 ); }
702              	;
703 
704 source_block:
705 		  source START_BRACKET source_contents STOP_BRACKET { defSourceEnd(); }
706              	;
707 
708 source_contents:
709 		  %empty
710 		| source_contents source_content
711 		;
712 
713 source_content:
714 		  DOMAIN domain
715                 | USER user
716                 | UNIX USER user
717                 | USERLIST qidentifier 		 { ufdbSourceUserList( $2 ); }
718                 | USERLIST WORD 		 { ufdbSourceUserList( $2 ); }
719                 | UNIX USERLIST qidentifier      { ufdbSourceUserList( $3 ); }
720                 | UNIX USERLIST WORD 	         { ufdbSourceUserList( $3 ); }
721                 | EXECUSERLIST qidentifier 	 { ufdbSourceExecUserList( $2 ); }
722                 | EXECIPLIST qidentifier 	 { ufdbSourceExecIPList( $2 ); }
723 		| GROUP qidentifier              { ufdbSourceGroup( UFDB_GROUPTYPE_UNIX, $2 ); }
724 		| GROUP WORD                     { ufdbSourceGroup( UFDB_GROUPTYPE_UNIX, $2 ); }
725 		| UNIX GROUP qidentifier         { ufdbSourceGroup( UFDB_GROUPTYPE_UNIX, $3 ); }
726 		| UNIX GROUP WORD                { ufdbSourceGroup( UFDB_GROUPTYPE_UNIX, $3 ); }
727                 | USERQUOTA NUMBER NUMBER HOURLY { ufdbSourceUserQuota( $2, $3, "3600" );
728 		                                   ufdbFree( $2 ); ufdbFree( $3 ); }
729                 | USERQUOTA NUMBER NUMBER DAILY  { ufdbSourceUserQuota( $2, $3, "86400" );
730 		                                   ufdbFree( $2 ); ufdbFree( $3 ); }
731                 | USERQUOTA NUMBER NUMBER WEEKLY { ufdbSourceUserQuota( $2, $3, "604800" );
732 		                                   ufdbFree( $2 ); ufdbFree( $3 ); }
733                 | USERQUOTA NUMBER NUMBER NUMBER { ufdbSourceUserQuota( $2, $3, $4 );
734 		                                   ufdbFree( $2 ); ufdbFree( $3 ); ufdbFree( $4 ); }
735                 | IPV4 ipv4s
736                 | IPV4LIST qidentifier         	 { defSourceIPV4List( $2 ); }
737                 | IPV4LIST WORD             	 { defSourceIPV4List( $2 ); }
738                 | IPV6 ipv6s
739                 | IPV6LIST qidentifier         	 { defSourceIPV6List( $2 ); }
740                 | IPV6LIST WORD         	 { defSourceIPV6List( $2 ); }
741 		| EVALUATE_AND                   { ufdbSourceEval( UFDB_EVAL_AND ); }
742 		| EVALUATE_OR                    { ufdbSourceEval( UFDB_EVAL_OR ); }
743                 | WITHIN qidentifier           	 { defSourceTime( $2, UFDB_ACL_WITHIN ); }
744                 | OUTSIDE qidentifier          	 { defSourceTime( $2, UFDB_ACL_OUTSIDE ); }
745                 | LOGFILE ANONYMOUS qidentifier	 { ufdbLogError( "line %d: unsupported logfile context for %s",
746                                                                  lineno, $3 );
747 						   ufdbFree( $3 ); }
748                 | LOGFILE ANONYMOUS WORD  	 { ufdbLogError( "line %d: unsupported logfile context for %s",
749                                                                  lineno, $3 );
750 						   ufdbFree( $3 ); }
751                 | LOGFILE qidentifier          	 { ufdbLogError( "line %d: unsupported logfile context for %s",
752                                                                  lineno, $2 );
753 						   ufdbFree( $2 ); }
754                 | LOGFILE WORD            	 { ufdbLogError( "line %d: unsupported logfile context for %s",
755                                                                  lineno, $2 );
756 						   ufdbFree( $2 ); }
757                 | CONTINUE                	 { ufdbNewGV.lastSource->cont_search = 1; }
758                 ;
759 
760 
761 domain:
762 		  %empty
763 		| domain qidentifier  	{ defSourceDomain( $2 ); }
764 		| domain WORD  		{ defSourceDomain( $2 ); }
765                 | domain COMMA
766 		;
767 
768 user:
769 		  %empty
770 		| user qidentifier	{ ufdbSourceUser( $2 ); }
771 		| user WORD    		{ ufdbSourceUser( $2 ); }
772                 | user COMMA
773 		;
774 
775 acl_block:
776 		  ACL START_BRACKET acl_contents STOP_BRACKET
777                 ;
778 
779 acl_contents:     %empty
780                 | acl_contents acl_content
781 	        ;
782 
783 acl_header:
784 		  qidentifier                     START_BRACKET  { ufdbAcl( $1, NULL, UFDB_ACL_NONE );  }
785 		| qidentifier WITHIN qidentifier  START_BRACKET  { ufdbAcl( $1, $3, UFDB_ACL_WITHIN );  }
786                 | qidentifier OUTSIDE qidentifier START_BRACKET  { ufdbAcl( $1, $3, UFDB_ACL_OUTSIDE ); }
787                 ;
788 
789 acl_content:
790 		  acl_header access_contents STOP_BRACKET
791                 | acl_header access_contents STOP_BRACKET ELSE
792                      { ufdbAcl( NULL, NULL, UFDB_ACL_ELSE );    }
793                      START_BRACKET access_contents STOP_BRACKET
794                 ;
795 
796 access_contents:
797 		  %empty
798                 | access_contents access_content
799                 ;
800 
801 access_content:
802 		  PASS access_pass              { if (ufdbNewGV.lastAcl != NULL  &&
803                                                       ufdbNewGV.lastAcl->pass == NULL)
804                                                   {
805                                                      ufdbLogMessage( "line %d: acl has an empty pass statement."
806                                                                      "  Adding 'any'.", lineno );
807                                                      ufdbAclSetValue( "pass", ufdbStrdup("any"), 1 );
808                                                   }
809                                                 }
810                 | REWRITE qidentifier           { ufdbAclSetValue( "rewrite", $2, 0 ); }
811                 | REWRITE WORD                  { ufdbAclSetValue( "rewrite", $2, 0 ); }
812                 | REDIRECT qidentifier          { ufdbAclSetValue( "redirect", $2, 0 ); }
813                 | REDIRECT WORD                 { ufdbAclSetValue( "redirect", $2, 0 ); }
814                 | LOGFILE ANONYMOUS qidentifier { ufdbLogError( "line %d: unsupported logfile context for %s",
815                                                                 lineno, $3 );
816 						  ufdbFree( $3 ); }
817                 | LOGFILE ANONYMOUS WORD        { ufdbLogError( "line %d: unsupported logfile context for %s",
818                                                                 lineno, $3 );
819 						  ufdbFree( $3 ); }
820                 | LOGFILE qidentifier           { ufdbLogError( "line %d: unsupported logfile context for %s",
821                                                                 lineno, $2 );
822 						  ufdbFree( $2 ); }
823                 | LOGFILE WORD                  { ufdbLogError( "line %d: unsupported logfile context for %s",
824                                                                 lineno, $2 );
825 						  ufdbFree( $2 ); }
826                 ;
827 
828 access_pass:
829 		  %empty
830 		| access_pass AGGRESSIVE		    { yyerror( (char *) "\"aggressive\" is a keyword and must be surrounded by quotes" ); }
831                 | access_pass qidentifier     		    { ufdbAclSetValue( "pass", $2, 1 ); }
832                 | access_pass CHAR_EXCLAMATION AGGRESSIVE   { yyerror( (char *) "\"aggressive\" is a keyword and must be surrounded by quotes" ); }
833                 | access_pass CHAR_EXCLAMATION qidentifier  { ufdbAclSetValue( "pass", $3, 0 ); }
834 		| access_pass COMMA
835                 ;
836 
837 ipv4s:
838 		  %empty
839                 | ipv4s ipv4
840                 | ipv4s COMMA ipv4
841 		;
842 
843 ipv4:
844 		  IPV4ADDR 		        { sgIpv4( $1, SG_IPTYPE_HOST, ufdbNewGV.configFile, lineno );
845 		                                  ufdbFree( $1 );  }
846 		| IPV4RANGE			{ sgIpv4( $1, SG_IPTYPE_RANGE, ufdbNewGV.configFile, lineno );
847 		                                  ufdbFree( $1 );  }
848 		| IPV4CLASS			{ sgIpv4( $1, SG_IPTYPE_CLASS, ufdbNewGV.configFile, lineno );
849 		                                  ufdbFree( $1 ); }
850 		| IPV4NET			{ sgIpv4( $1, SG_IPTYPE_CIDR, ufdbNewGV.configFile, lineno );
851 		                                  ufdbFree( $1 ); }
852      		;
853 
854 ipv6s:
855 		  %empty
856                 | ipv6s ipv6
857                 | ipv6s COMMA ipv6
858 		;
859 
860 ipv6:
861 		  IPV6ADDR 		        { sgIpv6( $1, SG_IPV6TYPE_HOST, ufdbNewGV.configFile, lineno );
862                                                   ufdbFree( $1 );  }
863                 | IPV6NET                       { sgIpv6( $1, SG_IPV6TYPE_CIDR, ufdbNewGV.configFile, lineno );
864                                                   ufdbFree( $1 );  }
865      		;
866 
867 rew:
868 		  REWRITE qidentifier  	{ sgRewrite( $2 ); }
869 		| REWRITE WORD 		{ sgRewrite( $2 ); }
870 		;
871 
872 rew_block:
873 		  rew START_BRACKET rew_contents STOP_BRACKET
874              	;
875 
876 rew_contents:
877 		  %empty
878 		| rew_contents rew_content
879 		;
880 
881 
882 rew_content:
883 		  SUBST                         { sgRewriteSubstitute( $1 ); ufdbFree( $1 ); }
884                 | WITHIN qidentifier            { sgRewriteTime( $2, UFDB_ACL_WITHIN ); }
885                 | OUTSIDE qidentifier           { sgRewriteTime( $2, UFDB_ACL_OUTSIDE ); }
886                 | LOGFILE ANONYMOUS qidentifier { ufdbLogError( "line %d: unsupported logfile context for %s",
887 		                                                lineno, $3 );
888 								ufdbFree( $3 ); }
889                 | LOGFILE ANONYMOUS WORD        { ufdbLogError( "line %d: unsupported logfile context for %s",
890 		                                                lineno, $3 );
891 								ufdbFree( $3 ); }
892                 | LOGFILE qidentifier           { ufdbLogError( "line %d: unsupported logfile context for %s",
893 		                                                lineno, $2 );
894 								ufdbFree( $2 ); }
895                 | LOGFILE WORD                  { ufdbLogError( "line %d: unsupported logfile context for %s",
896 		                                                lineno, $2 );
897 								ufdbFree( $2 ); }
898                 ;
899 
900 
901 time:
902 		  TIME qidentifier 		{ sgTime( $2 ); }
903 		;
904 
905 time_block:
906 		  time START_BRACKET time_contents STOP_BRACKET
907                 ;
908 
909 time_contents:
910 		  %empty
911 		| time_contents time_content
912 		;
913 
914 
915 time_content:
916                   WEEKLY { sgTimeElementInit(); } WEEKDAY     { sgTimeElementAdd($3,T_WEEKDAY); } ttime
917 		| WEEKLY { sgTimeElementInit(); } qidentifier { sgTimeElementAdd($3,T_WEEKLY); }  ttime
918 		| WEEKLY { sgTimeElementInit(); } WORD        { sgTimeElementAdd($3,T_WEEKLY); }  ttime
919                 | DATE   { sgTimeElementInit(); } date        { sgTimeElementEnd(); }
920 		| error  { ufdbLogFatalError( "invalid time specification at line %d", lineno );   }
921                 ;
922 
923 ttime:
924 		  ttime  { sgTimeElementClone(); }  tval CHAR_MINUS tval
925 		| tval CHAR_MINUS tval
926                 ;
927 
928 date:
929 		  dval ttime
930                 | dval
931                 | dval  CHAR_MINUS dval ttime
932                 | dval  CHAR_MINUS dval
933                 | dvalcron  ttime
934                 | dvalcron
935                 ;
936 
937 dval:		  DVAL 		{ sgTimeElementAdd( $1, T_DVAL ); }
938                 ;
939 
940 tval:		  TVAL 		{ sgTimeElementAdd( $1, T_TVAL ); }
941                 ;
942 
943 dvalcron:	  DVALCRON 	{ sgTimeElementAdd( $1, T_DVALCRON ); }
944                 ;
945 
946 an_error:
947 		  error  	{  yyerror( (char *) "syntax error" );
948                                    if (ufdbNewGV.serialno > 1  &&  ufdbNewGV.debug)
949                                       logConfig();
950                                 }
951 	        ;
952 
953 statements:       %empty
954        		| statements statement
955        		;
956 
957 statement:
958                   category
959 	     	| source_block
960 	     	| category_block
961 		| http_server_def
962 	     	| admin_spec
963 	     	| https_cache_size
964 	     	| check_proxy_tunnels
965 	     	| log_pass
966 	     	| log_block
967 	     	| log_all
968 		| logall_httpd
969                 | madvise_hugepages
970                 | fast_refresh
971 		| debug_filter
972 		| debug_coredump
973 		| enforce_https_with_hostname
974 		| enforce_https_offical_certificate
975 		| https_prohibit_insecure_sslv2
976 		| https_prohibit_insecure_sslv3
977              	| dbhome
978 	     	| logdir
979 		| pidfile
980 		| mail_server
981 		| my_hostname
982 		| admin_email
983 		| sender_email
984 		| external_status_command
985 		| url_lookup_delay_db_reload
986 		| url_lookup_result_db_reload
987 		| url_lookup_result_fatal_error
988 		| squid_version
989 		| num_worker_threads
990 		| upload_crash_reports
991 		| lookup_reverse_ip
992 		| use_ipv6_on_wan
993                 | parse_url_parameters
994 		| squid_uses_active_bumping
995 		| ufdb_log_url_details
996 		| ufdb_show_url_details
997 		| ufdb_debug_skype_probes
998 		| ufdb_debug_gtalk_probes
999 		| ufdb_debug_yahoomsg_probes
1000 		| ufdb_debug_aim_probes
1001 		| ufdb_debug_fbchat_probes
1002 		| ufdb_debug_citrixonline_probes
1003 		| ufdb_expression_optimisation
1004 		| ufdb_expression_debug
1005 		| refreshuserlist
1006 		| refreshiplist
1007 		| refreshdomainlist
1008 		| ufdb_debug_external_scripts
1009 		| interface
1010 	     	| port
1011 	     	| cpus
1012 	     	| upload_stats
1013 		| redirect_https
1014 		| redirect_bumped_https
1015 		| redirect_loading_database
1016 		| redirect_fatal_error
1017 	     	| analyse_uncategorised
1018 		| log_uncategorised_urls
1019 		| safe_search
1020 		| strip_domain_from_username
1021 		| youtube_edufilter
1022 		| allow_google_https_using_ip
1023 		| youtube_edufilter_id
1024 		| max_logfile_size
1025 	     	| acl_block
1026 	     	| rew_block
1027 	     	| time_block
1028 	     	| NL
1029 	     	| an_error
1030              	;
1031 
1032 %%
1033 
1034 int ufdbReadConfig(
1035    const char *      file )
1036 {
1037    struct Source *   source;
1038    struct Category * cat;
1039    struct Acl *      acl;
1040 
1041    lineno = 1;
1042    ufdbResetCPUs();
1043 
1044    ufdbNewGV.configFile = file;
1045 
1046    yyin = fopen( ufdbNewGV.configFile, "r" );
1047    if (yyin == NULL)
1048    {
1049       syslog( LOG_ALERT, "%s: cannot open configuration file %s", ufdbNewGV.progname, ufdbNewGV.configFile );
1050       ufdbLogFatalError( "cannot open configuration file %s", ufdbNewGV.configFile );
1051       return 0;
1052    }
1053 
1054    ufdbLogMessage( "configuration file: %s", ufdbNewGV.configFile );
1055 
1056    /* keep the old HTTPS redirection URL.  It is most likely that we need it during the database reload */
1057    /* dot not reset ufdbNewGV.RedirectHttps and ufdbNewGV.RedirectBumpedHttps. */
1058 
1059    ufdbNewGV.debug = 0;
1060    ufdbNewGV.debugCoreDump = 0;
1061    strcpy( ufdbNewGV.interface, "all" );
1062    ufdbNewGV.portNum = UFDB_DAEMON_PORT;
1063    ufdbNewGV.nWorkers = UFDB_MIN_THREADS;
1064    ufdbNewGV.debugHttpd = 0;
1065    ufdbNewGV.logPass = 0;
1066    ufdbNewGV.logBlock = 0;
1067    ufdbNewGV.logAllRequests = 0;
1068    ufdbNewGV.YoutubeEdufilter = 0;
1069    ufdbFree( ufdbNewGV.YoutubeEdufilterID );
1070    ufdbNewGV.YoutubeEdufilterID = NULL;
1071    ufdbNewGV.allowGoogleHTTPSusingIP = 0;
1072 
1073    /* When a database is reloaded, these variables may NOT be reset since they are used during the reload !! */
1074    ufdbNewGV.URLlookupDelayDBreload = 0;
1075    ufdbNewGV.URLlookupResultDBreload = UFDB_ALLOW;
1076    ufdbNewGV.URLlookupResultFatalError = UFDB_ALLOW;
1077    strcpy( ufdbNewGV.loadingDatabaseRedirect, "http://cgibin.urlfilterdb.com/cgi-bin/URLblocked.cgi?"
1078                 "category=loading-database" );
1079    ufdbNewGV.SquidVersion = ufdbStrdup( UFDB_DEFAULT_SQUID_VERSION );
1080 
1081    ufdbNewGV.showURLdetails = 0;
1082    ufdbNewGV.logURLdetails = 0;
1083    ufdbNewGV.uploadCrashReports = 1;
1084    ufdbNewGV.lookupReverseIP = 0;
1085    ufdbNewGV.useAlsoIPv6onWan = 1;
1086    ufdbNewGV.debugSkype = 0;
1087    ufdbNewGV.debugGtalk = 0;
1088    ufdbNewGV.debugYahooMsg = 0;
1089    ufdbNewGV.debugAim = 0;
1090    ufdbNewGV.debugFBchat = 0;
1091    ufdbNewGV.debugCitrixOnline = 0;
1092    ufdbNewGV.debugRegexp = 0;
1093    ufdbNewGV.debugExternalScripts = 0;
1094    ufdbNewGV.expressionOptimisation = 1;
1095    ufdbNewGV.refreshUserlistInterval = UFDB_DEFAULT_REFRESH_USERLIST;
1096    ufdbNewGV.refreshIPlistInterval = UFDB_DEFAULT_REFRESH_IPLIST;
1097    ufdbNewGV.refreshDomainlistInterval = UFDB_DEFAULT_REFRESH_DOMAINLIST;
1098    ufdbNewGV.analyseUncategorisedURLs = 1;
1099    ufdbNewGV.logUncategorisedURLs = 0;
1100    ufdbNewGV.uploadStats = 1;
1101    ufdbNewGV.safeSearch = 1;
1102    ufdbNewGV.skipSafeCategory = 0;
1103    ufdbNewGV.stripDomainFromUsername = 0;
1104    ufdbNewGV.tunnelCheckMethod = UFDB_API_HTTPS_CHECK_OFF;
1105    ufdbNewGV.SquidUsesActiveBumping = 0;
1106    ufdbNewGV.httpsWithHostname = 0;
1107    ufdbNewGV.httpsOfficialCertificate = 0;
1108    ufdbNewGV.unknownProtocolOverHttps = 1;
1109    ufdbNewGV.httpsNoSSLv2 = 1;
1110    ufdbNewGV.httpsNoSSLv3 = 1;
1111    ufdbNewGV.httpdPort = 0;
1112    ufdbNewGV.dateOfCheckedDB[0] = '\0';
1113    strcpy( ufdbNewGV.httpdInterface, "all" );
1114    strcpy( ufdbNewGV.httpdImagesDirectory, "." );
1115 
1116    ufdbNewGV.databaseStatus = UFDB_API_STATUS_DATABASE_OK;
1117 
1118    (void) yyparse();		/* parse the configuration file ********************/
1119    fclose( yyin );
1120 
1121    ufdbNewGV.databaseLoadTime = time( NULL );
1122 
1123    /*
1124     * For analysis of uncategorised URLs we also load a "checked" category
1125     * that contains URLs that are reviewed and checked not to be part of
1126     * any other category.
1127     */
1128    char * dbhome;
1129    char   dbfname[1024];
1130 
1131    dbhome = ufdbNewGV.databaseDirectory;
1132 
1133    /* TODO: check if "checked" was not already loaded... */
1134    if (ufdbNewGV.analyseUncategorisedURLs > 0)
1135    {
1136       int status;
1137       sprintf( dbfname, "%s/checked/domains.ufdb", dbhome );
1138       status = UFDBloadDatabase( &ufdbNewGV, &ufdbNewGV.checkedDB, dbfname );
1139       if (status != UFDB_API_OK  &&  status != UFDB_API_STATUS_DATABASE_OLD)
1140       {
1141          ufdbNewGV.checkedDB.mem = NULL;
1142          ufdbNewGV.checkedDB.index = NULL;
1143          ufdbNewGV.checkedDB.table.nNextLevels = 0;
1144          if (ufdbGV.debug)
1145             ufdbLogMessage( "The URL database appears not be from URLfilterDB. No problem." );
1146       }
1147       else
1148       {
1149          if (ufdbGV.debug)
1150             ufdbLogMessage( "table \"checked\" is loaded: %s %8.8s",
1151                             ufdbNewGV.checkedDB.date, ufdbNewGV.checkedDB.flags );
1152          sprintf( dbfname, "%s/checked/expressions", dbhome );
1153          (void) UFDBloadExpressions( &ufdbNewGV.checkedExpressions, dbfname );
1154          ufdbLogMessage( "The implicitly allowed URL category 'checked' is loaded." );
1155       }
1156    }
1157 
1158    if (ufdbNewGV.defaultAcl == NULL)
1159       ufdbLogFatalError( "\"default\" ACL is not defined" );
1160    else if (ufdbNewGV.defaultAcl->pass == NULL)
1161       ufdbLogError( "\"default\" ACL is empty: by default all URLs are blocked." );
1162 
1163    /* A configuration file may have a source definition which is not used in the ACL list.
1164     * This raises the question "what to do with this ?".
1165     * If we do nothing, the source is matched but has no ACL definition and is silently skipped.
1166     * Most likely the administrator overlooked something so a warning is useful.
1167     */
1168    source = ufdbNewGV.sourceList;
1169    if (source == NULL  &&  ufdbNewGV.defaultAcl == NULL)
1170       ufdbLogFatalError( "There are no sources and there is no default ACL" );
1171 
1172    for ( ; source != NULL;  source = source->next)
1173    {
1174       int found;
1175 
1176       found = 0;
1177       for (acl = ufdbNewGV.aclList;  acl != NULL;  acl = acl->next)
1178       {
1179          if (strcmp( acl->name, source->name ) == 0)
1180 	 {
1181 	    found = 1;
1182 	    break;
1183 	 }
1184       }
1185       if (!found)
1186       {
1187          ufdbLogError( "source \"%s\" has no ACL.  *****\n"
1188 	               "This most likely may lead to unexpected results when the source is matched.  *****\n"
1189 		       "It is strongly suggested to remove the source definition OR add the source to the acl.  *****",
1190 		       source->name );
1191 	 source->active = 0;
1192       }
1193       else if (acl->pass == NULL)
1194       {
1195          ufdbLogMessage( "source \"%s\" has an empty \"pass\" in its ACL *****", source->name );
1196       }
1197    }
1198 
1199    for (cat = ufdbNewGV.catList;  cat != NULL;  cat = cat->next)
1200    {
1201       if (cat->options == 0  &&  cat->domainlist == NULL  &&  cat->execdomainlist == NULL  &&
1202           cat->expressionlist == NULL  &&  cat->rewrite == NULL  &&  cat->name != NULL)
1203       {
1204          ufdbLogError( "category \"%s\" has no content definition  *****", cat->name );
1205       }
1206    }
1207 
1208    if (ufdbNewGV.adminEmail != NULL  &&  ufdbNewGV.emailServer == NULL)
1209    {
1210       ufdbLogError( "No email server is defined; cannot send email to \"%s\"  *****", ufdbNewGV.adminEmail );
1211    }
1212    if (ufdbNewGV.adminEmail != NULL  &&  ufdbNewGV.emailServer != NULL  &&  ufdbNewGV.senderEmail == NULL)
1213    {
1214       ufdbLogMessage( "WARNING: No sender email address is defined; using \"%s\" as sender",
1215                       ufdbNewGV.adminEmail );
1216    }
1217 
1218    if (strncmp( ufdbNewGV.SquidVersion, "5.", 2 ) != 0  &&
1219        strncmp( ufdbNewGV.SquidVersion, "4.", 2 ) != 0  &&
1220        strncmp( ufdbNewGV.SquidVersion, "3.5", 3 ) != 0  &&
1221        strncmp( ufdbNewGV.SquidVersion, "3.4", 3 ) != 0  &&
1222        strncmp( ufdbNewGV.SquidVersion, "3.3", 3 ) != 0  &&
1223        strncmp( ufdbNewGV.SquidVersion, "3.2", 3 ) != 0  &&
1224        strncmp( ufdbNewGV.SquidVersion, "3.1", 3 ) != 0  &&
1225        strncmp( ufdbNewGV.SquidVersion, "3.0", 3 ) != 0  &&
1226        strncmp( ufdbNewGV.SquidVersion, "2.7", 3 ) != 0  &&
1227        strncmp( ufdbNewGV.SquidVersion, "2.6", 3 ) != 0)
1228    {
1229       ufdbLogFatalError( "Unsupported Squid version \"%s\"  *****\n"
1230                          "The supported values for squid-version are: "
1231                          "5.x, 4.x, 3.5, 3.4, 3.3, 3.2, 3.1, 3.0, 2.7, 2.6  *****\n"
1232 			 "If the version of Squid is higher, it is recommended to upgrade ufdbGuard.  *****\n"
1233 			 "Alternatively set squid-version to the highest supported version",
1234 			 ufdbNewGV.SquidVersion );
1235    }
1236    if (strncmp( ufdbNewGV.SquidVersion, "5.", 2 ) == 0  ||
1237        strncmp( ufdbNewGV.SquidVersion, "4.", 2 ) == 0  ||
1238        strncmp( ufdbNewGV.SquidVersion, "3.5", 3 ) == 0  ||
1239        strncmp( ufdbNewGV.SquidVersion, "3.4", 3 ) == 0)
1240       ufdbNewGV.SquidHelperProtocol = UFDB_SQUID_HELPER_PROTOCOL3;
1241    else if (strncmp( ufdbNewGV.SquidVersion, "2.6", 3 ) == 0  ||
1242             strncmp( ufdbNewGV.SquidVersion, "2.7", 3 ) == 0)
1243       ufdbNewGV.SquidHelperProtocol = UFDB_SQUID_HELPER_PROTOCOL1;
1244    else
1245       ufdbNewGV.SquidHelperProtocol = UFDB_SQUID_HELPER_PROTOCOL2;	/* 3.0 - 3.3 */
1246 
1247    BuildImplicitPassLists( &ufdbNewGV );
1248 
1249    return 1;
1250 }
1251 
1252 
setDBhome(char * dbhome)1253 static void setDBhome( char * dbhome )
1254 {
1255    struct stat dirbuf;
1256 
1257    if (strlen(dbhome) > sizeof(ufdbNewGV.databaseDirectory)-1)
1258    {
1259       ufdbLogFatalError( "maximum length for dbhome is %d", (int) sizeof(ufdbNewGV.databaseDirectory)-1 );
1260       ufdbFree( dbhome );
1261       return;
1262    }
1263    strcpy( ufdbNewGV.databaseDirectory, dbhome );
1264 
1265    if (stat( dbhome, &dirbuf ) != 0)
1266    {
1267       ufdbLogFatalError( "dbhome %s: directory does not exist or access rights are insufficient", dbhome );
1268    }
1269    else
1270    {
1271       if (!S_ISDIR(dirbuf.st_mode))
1272          ufdbLogFatalError( "dbhome: %s is not a directory", dbhome );
1273    }
1274 
1275    ufdbFree( dbhome );
1276 }
1277 
1278 
setAdministrator(char * value)1279 static void setAdministrator( char * value )
1280 {
1281    char * p;
1282 
1283    if (strlen(value) > sizeof(ufdbNewGV.administrator)-1)
1284    {
1285       ufdbLogError( "maximum length for administrator is %d", (int) sizeof(ufdbNewGV.administrator)-1 );
1286       return;
1287    }
1288 
1289    while ((p = (char *) strchr( value, '?' )) != NULL)
1290       *p = '_';
1291    while ((p = (char *) strchr( value, '&' )) != NULL)
1292       *p = '_';
1293 
1294    strcpy( ufdbNewGV.administrator, value );
1295    ufdbFree( value );
1296 }
1297 
1298 
setLogdir(char * value)1299 static void setLogdir( char * value )
1300 {
1301    if (ufdbNewGV.logDir != NULL)
1302       ufdbFree( ufdbNewGV.logDir );
1303    ufdbNewGV.logDir = value;
1304 
1305    ufdbSetGlobalErrorLogFile( ufdbNewGV.logDir, NULL, 0 );
1306 }
1307 
1308 
setPidfile(char * value)1309 static void setPidfile( char * value )
1310 {
1311    if (ufdbNewGV.pidFilename != NULL)
1312       ufdbFree( ufdbNewGV.pidFilename );
1313    ufdbNewGV.pidFilename = value;
1314 }
1315 
1316 
setRefreshUserlist(int nmin)1317 static void setRefreshUserlist( int nmin )
1318 {
1319    if (nmin < 5  ||  nmin > 24*60)
1320    {
1321       ufdbLogError( "refreshuserlist must have a value between 5 and %d, resetting to %d.",
1322                     24*60, UFDB_DEFAULT_REFRESH_USERLIST );
1323       nmin = UFDB_DEFAULT_REFRESH_USERLIST;
1324    }
1325    ufdbNewGV.refreshUserlistInterval = nmin;
1326 }
1327 
1328 
setRefreshIPlist(int nmin)1329 static void setRefreshIPlist( int nmin )
1330 {
1331    if (nmin < 5  ||  nmin > 24*60)
1332    {
1333       ufdbLogError( "refreshiplist must have a value between 5 and %d, resetting to %d.",
1334                     24*60, UFDB_DEFAULT_REFRESH_IPLIST );
1335       nmin = UFDB_DEFAULT_REFRESH_IPLIST;
1336    }
1337    ufdbNewGV.refreshIPlistInterval = nmin;
1338 }
1339 
1340 
setRefreshDomainlist(int nmin)1341 static void setRefreshDomainlist( int nmin )
1342 {
1343    if (nmin < 5  ||  nmin > 24*60)
1344    {
1345       ufdbLogError( "refreshdomainlist must have a value between 5 and %d, resetting to %d.",
1346                     24*60, UFDB_DEFAULT_REFRESH_DOMAINLIST );
1347       nmin = UFDB_DEFAULT_REFRESH_DOMAINLIST;
1348    }
1349    ufdbNewGV.refreshDomainlistInterval = nmin;
1350 }
1351 
1352 
1353 /*
1354  * Source functions
1355  */
1356 
defSource(char * source)1357 static void defSource(
1358    char *          source )
1359 {
1360    struct Source * sp;
1361 
1362    if (ufdbGV.debug)
1363       ufdbLogMessage( "defSource: defining source \"%s\"", source );
1364 
1365    if ((struct Source *) defSourceFindName(ufdbNewGV.sourceList,source) != NULL)
1366    {
1367       ufdbLogFatalError( "line %d: source %s is already defined in configuration file %s",
1368 			 lineno, source, ufdbNewGV.configFile );
1369       ufdbFree( source );
1370       return;
1371    }
1372 
1373    sp = (struct Source *) ufdbMallocAligned( UFDB_CACHELINE_SIZE, sizeof(struct Source) );
1374    sp->name = source;
1375    sp->active = 1;
1376    sp->evaluationMethod = UFDB_EVAL_OR;
1377    sp->ipv4hosts = NULL;
1378    sp->ipv4 = NULL;
1379    sp->ipv6hosts = NULL;
1380    sp->ipv6 = NULL;
1381    sp->domainDb = NULL;
1382    sp->userDb = NULL;
1383    sp->time = NULL;
1384    sp->within = UFDB_ACL_NONE;
1385    sp->cont_search = 0;
1386    sp->sarg0 = NULL;
1387    sp->execiplistCommand = NULL;
1388    sp->next = NULL;
1389    sp->nblocks = 0;
1390    sp->nmatches = 0;
1391 
1392    if (ufdbNewGV.sourceList == NULL)
1393    {
1394       if (ufdbGV.debug)
1395          ufdbLogMessage( "defSource: SourceList is set to point to \"%s\"", source );
1396       ufdbNewGV.sourceList = sp;
1397       ufdbNewGV.lastSource = sp;
1398    }
1399    else
1400    {
1401       ufdbNewGV.lastSource->next = sp;
1402       ufdbNewGV.lastSource = sp;
1403    }
1404 }
1405 
1406 
defSourceEnd(void)1407 static void defSourceEnd( void )
1408 {
1409    struct Source * s;
1410 
1411    s = ufdbNewGV.lastSource;
1412    if (s->ipv4 == NULL  &&  s->ipv4hosts == NULL  &&
1413        s->ipv6 == NULL  &&  s->ipv6hosts == NULL  &&
1414        s->domainDb == NULL  &&  s->userDb == NULL)
1415    {
1416       if (s->execiplistCommand == NULL)
1417       {
1418 	 ufdbLogError( "source \"%s\" missing active content, set inactive  *****", s->name );
1419 	 s->time = NULL;
1420 	 s->active = 0;
1421       }
1422       else
1423 	 ufdbLogError( "source \"%s\" has an execiplist command but the list is empty and set inactive   *****",
1424 	               s->name );
1425    }
1426 }
1427 
1428 
ufdbSourceUser(char * user)1429 static void ufdbSourceUser(
1430    char *          user )
1431 {
1432    char *          lc;
1433    struct Source * sp;
1434 
1435    sp = ufdbNewGV.lastSource;
1436    if (sp->userDb != NULL  &&  sp->userDb->type != SGDBTYPE_USERLIST)
1437    {
1438       ufdbLogFatalError( "user \"%s\" in source \"%s\" on line %d: "
1439                          "found mix of exec and non-exec userlists",
1440                          user, sp->name, lineno );
1441       return;
1442    }
1443    if (sp->userDb == NULL)
1444    {
1445       sp->userDb = (struct sgDb *) ufdbMalloc( sizeof(struct sgDb) );
1446       sp->userDb->dbhome = NULL;
1447       sp->userDb->dbcp = (void *) UFDBmemDBinit();
1448       sp->userDb->type = SGDBTYPE_USERLIST;
1449       sp->userDb->entries = 0;
1450    }
1451 
1452    for (lc = user;  *lc != '\0';  lc++)    // convert username to lowercase
1453    {
1454       if (*lc <= 'Z'  &&  *lc >= 'A')
1455          *lc += 'a' - 'A';
1456    }
1457 
1458    if (ufdbGV.debug > 1)
1459       ufdbLogMessage( "ufdbSourceUser: adding user \"%s\"", user );
1460 
1461    sp->userDb->entries++;
1462    UFDBmemDBinsert( (struct UFDBmemDB *) sp->userDb->dbcp, user, NULL );
1463    ufdbFree( user );
1464 }
1465 
1466 
ufdbSourceUnixGroup(char * groupName)1467 static void ufdbSourceUnixGroup(
1468    char *          groupName  )
1469 {
1470    struct Source * sp;
1471    struct group *  grp;
1472    int             n;
1473    char *          user;
1474 
1475    sp = ufdbNewGV.lastSource;
1476    if (sp->userDb != NULL  &&  sp->userDb->type != SGDBTYPE_USERLIST)
1477    {
1478       ufdbLogFatalError( "unix group \"%s\" in source \"%s\" on line %d: "
1479                          "found mix of exec and non-exec userlists",
1480                          groupName, sp->name, lineno );
1481       return;
1482    }
1483    if (sp->userDb == NULL)
1484    {
1485       sp->userDb = (struct sgDb *) ufdbMalloc( sizeof(struct sgDb) );
1486       sp->userDb->dbhome = NULL;
1487       sp->userDb->dbcp = (void *) UFDBmemDBinit();
1488       sp->userDb->type = SGDBTYPE_USERLIST;
1489       sp->userDb->entries = 0;
1490    }
1491 
1492    n = 0;
1493    setgrent();
1494    errno = 0;
1495    grp = getgrnam( groupName );
1496    if (grp != NULL)
1497    {
1498       if (ufdbGV.debug > 1)
1499          ufdbLogMessage( "found group \"%s\"", grp->gr_name );
1500 
1501       while ((user = grp->gr_mem[n]) != NULL)
1502       {
1503 	 char * lc;
1504 
1505 	 if (ufdbGV.debug > 1)
1506 	    ufdbLogMessage( "group \"%s\" has user \"%s\"", grp->gr_name, user );
1507 	 for (lc = user;  *lc != '\0';  lc++)  	// convert username to lowercase
1508 	 {
1509 	    if (*lc <= 'Z'  &&  *lc >= 'A')
1510 	       *lc += 'a' - 'A';
1511 	 }
1512 	 UFDBmemDBinsert( (struct UFDBmemDB *) sp->userDb->dbcp, user, NULL );
1513 	 sp->userDb->entries++;
1514 	 n++;
1515       }
1516       ufdbLogMessage( "source \"%s\": unix group \"%s\" has %d members", sp->name, groupName, n );
1517       if (n == 0)
1518 	 ufdbLogMessage( "WARNING: source \"%s\": \"%s\" is an empty group  +++++", sp->name, groupName );
1519    }
1520    else
1521    {
1522       ufdbLogFatalError( "source \"%s\": \"%s\" is not a unix group", sp->name, groupName );
1523       if (errno)
1524 	 ufdbLogError( "ufdbSourceUnixGroup: getgrnam returned error %d: %s", errno, strerror(errno) );
1525    }
1526    endgrent();
1527 }
1528 
1529 
ufdbSourceGroup(int groupType,char * groupName)1530 static void ufdbSourceGroup(
1531    int    groupType,
1532    char * groupName  )
1533 {
1534    switch (groupType)
1535    {
1536    case UFDB_GROUPTYPE_UNIX:
1537       ufdbSourceUnixGroup( groupName );
1538       break;
1539    default:
1540       ufdbLogFatalError( "ufdbSourceGroup: unknown group type %d", groupType );
1541    }
1542 
1543    ufdbFree( groupName );
1544 }
1545 
1546 
UFDBretrieveExecDomainlist(struct Category * cat)1547 struct sgDb * UFDBretrieveExecDomainlist( struct Category * cat )       // uses ufdbGV
1548 {
1549    char *        t;
1550    FILE *        fp;
1551    struct sgDb * newdb;
1552    int           exitcode;
1553    struct stat   Stat0;
1554    struct stat   Stat1;
1555    char          line[1024];
1556    char          basefilename[1024];
1557    char          fullfilename[1024];
1558    char          command[3000];
1559 
1560    if (cat == NULL)
1561    {
1562       ufdbLogFatalError( "UFDBretrieveExecDomainlist: category is NULL" );
1563       return NULL;
1564    }
1565 
1566    if (cat->domainlist == NULL)
1567    {
1568       ufdbLogError( "will not execute command for category %s since domainlist is undefined", cat->name );
1569       return NULL;
1570    }
1571 
1572    if (cat->execdomainlist == NULL)
1573    {
1574       ufdbLogError( "can't execute command for category %s since execdomainlist is undefined", cat->name );
1575       return NULL;
1576    }
1577 
1578    if (cat->domainlist[0] == '/')
1579    {
1580       strcpy( basefilename, cat->domainlist );
1581    }
1582    else
1583    {
1584       char * dbhome;
1585       dbhome = ufdbGV.databaseDirectory;
1586       sprintf( basefilename, "%s/%s", dbhome, cat->domainlist );
1587    }
1588    strcpy( fullfilename, basefilename );
1589    strcat( fullfilename, UFDBfileSuffix );
1590 
1591    if (stat( fullfilename, &Stat0 ) != 0)
1592    {
1593       Stat0.st_mtime = 0;
1594       ufdbLogMessage( "category %s: pre-execdomainlist: domainlist does not exist (%s)",
1595                       cat->name, fullfilename );
1596    }
1597 
1598    /* The script that we will call needs the directory where the files reside,
1599     * so the popen-command contains a "cd" just before executing the script.
1600     * (note that we are multithreaded and cannot use chdir() here)
1601     */
1602    strcpy( command, "cd " );
1603    strcat( command, basefilename );
1604    t = strrchr( command, '/' );
1605    if (t != NULL)
1606       *t = '\0';
1607    strcat( command, " ; " );
1608    strcat( command, cat->execdomainlist );
1609 
1610    errno = 0;
1611    if ((fp = popen(command,"r")) == NULL)
1612    {
1613       ufdbLogFatalError( "category %s: can't execute command of execdomainlist \"%s\": popen failed: %s  *****",
1614                          cat->name, cat->execdomainlist, strerror(errno) );
1615       return NULL;
1616    }
1617 
1618    while (UFDBfgetsNoNL(line,sizeof(line)-1,fp) != NULL)
1619    {
1620       ufdbLogMessage( "execdomainlist for %s produced: %s", cat->name, line );
1621    }
1622    errno = 0;
1623    exitcode = pclose( fp );
1624 
1625    if (exitcode == -1  &&  errno == ECHILD)
1626       exitcode = 0;
1627       /* most likely the child handler thread already did a wait() */
1628 
1629    if (exitcode == 0)
1630    {
1631       if (ufdbGV.debug || ufdbGV.debugExternalScripts)
1632          ufdbLogMessage( "   execdomainlist for %s: exit 0 (OK)", cat->name );
1633    }
1634    else if (exitcode == -1)
1635    {
1636       ufdbLogError( "execdomainlist for %s: pclose error: %d (%s)", cat->name, errno, strerror(errno) );
1637       return NULL;
1638    }
1639    else if (exitcode > 0)
1640    {
1641       ufdbLogError( "execdomainlist for %s: command (%s) terminated with an error exit code %d",
1642                     cat->name, cat->execdomainlist, exitcode );
1643       return NULL;
1644    }
1645 
1646    /* The command defined by 'execdomainlist' has been executed and the exit code was 0 (OK), so
1647     * now it is verified if the file timestamp of domains.ufdb is newer.
1648     */
1649 
1650    if (stat( fullfilename, &Stat1 ) != 0)
1651    {
1652       ufdbLogError( "execdomainlist for %s: command failed to produce a .ufdb file (%s)",
1653                     cat->name, fullfilename );
1654       return NULL;
1655    }
1656 
1657    newdb = (struct sgDb *) ufdbCalloc( sizeof(struct sgDb), 1 );
1658    newdb->dbhome = NULL;
1659    newdb->dbcp = NULL;
1660    newdb->type = SGDBTYPE_DOMAINLIST;
1661 
1662    sgDbInit( newdb, basefilename );
1663    if (newdb->dbcp == NULL)
1664    {
1665       ufdbLogError( "execdomainlist for %s: command produced a .ufdb file but I failed to load it", cat->name );
1666       ufdbFree( newdb );
1667       return NULL;
1668    }
1669 
1670    if (ufdbGV.debug || ufdbGV.debugExternalScripts)
1671       ufdbLogMessage( "execdomainlist for %s: new URL table is loaded", cat->name );
1672 
1673    return newdb;
1674 }
1675 
1676 
ufdbSourceExecUserList(char * command)1677 static void ufdbSourceExecUserList(
1678    char *          command )            // must be malloced
1679 {
1680    time_t          t0, te;
1681    struct Source * sp;
1682 
1683    /* execuserlist is used to dynamically retrieve a list of usernames and
1684     * is executed every 15 minutes to refresh the list of usernames.
1685     */
1686 
1687    sp = ufdbNewGV.lastSource;
1688    if (sp->userDb != NULL  &&  sp->userDb->type != SGDBTYPE_EXECUSERLIST)
1689    {
1690       ufdbLogFatalError( "execuserlist \"%s\" in source \"%s\" on line %d: "
1691                          "found mix of exec and non-exec userlists",
1692                          command, sp->name, lineno );
1693       return;
1694    }
1695    if (sp->userDb != NULL)
1696    {
1697       ufdbLogFatalError( "execuserlist \"%s\" in source \"%s\" on line %d: "
1698                          "more than one execuserlist is not supported.  Merge the userlists in the script.",
1699                          command, sp->name, lineno );
1700       return;
1701    }
1702    ufdbLogMessage( "execuserlist \"%s\"", command );
1703 
1704    t0 = time( NULL );
1705    sp->userDb = UFDBretrieveExecUserlist( &ufdbNewGV, command );
1706    sp->sarg0 = command;				/* command is already malloc-ed */
1707    te = time( NULL );
1708 
1709    if (te - t0 > 4)
1710       ufdbLogMessage( "WARNING: it took %ld seconds to execute \"%s\"  *****", te - t0, command );
1711 
1712    if (ufdbGV.debug>1 || ufdbGV.debugExternalScripts)
1713    {
1714       UFDBmemDBprintUserDB( "user", (struct UFDBmemDB *) sp->userDb->dbcp );
1715    }
1716 }
1717 
1718 
ufdbSourceExecIPList(char * command)1719 static void ufdbSourceExecIPList(
1720    char *          command )            // must be malloced
1721 {
1722    time_t          t0, te;
1723    struct Source * sp;
1724 
1725    /* execiplist is used to dynamically retrieve a list of IP addresses and
1726     * is executed every 15 minutes to refresh the list of IP addresses.
1727     */
1728 
1729    sp = ufdbNewGV.lastSource;
1730    if (sp->ipv4 != NULL  ||  sp->ipv6 != NULL)
1731    {
1732       ufdbLogFatalError( "execiplist \"%s\" in source \"%s\" on line %d: "
1733                          "found mix of exec and non-exec iplists",
1734                          command, sp->name, lineno );
1735       return;
1736    }
1737    ufdbLogMessage( "execiplist \"%s\"", command );
1738 
1739    t0 = time( NULL );
1740    UFDBretrieveExecIPlist( &ufdbNewGV, command, &sp->ipv4hosts, &sp->ipv4, &sp->ipv6hosts, &sp->ipv6 );
1741    sp->execiplistCommand = command;			/* command is already malloc-ed */
1742    te = time( NULL );
1743 
1744    if (te - t0 > 4)
1745       ufdbLogMessage( "WARNING: it took %ld seconds to execute \"%s\"  *****", te - t0, command );
1746 
1747    if (ufdbGV.debug>1 || ufdbGV.debugExternalScripts)
1748    {
1749       UFDBlogIPv4( sp->ipv4 );
1750       UFDBlogIPv6( sp->ipv6 );
1751    }
1752 }
1753 
1754 
ufdbSourceUserList(char * file)1755 static void ufdbSourceUserList(
1756    char * file  )
1757 {
1758    char * dbhome;
1759    char * f;
1760    FILE * fp;
1761    char * p;
1762    char * c;
1763    char * s;
1764    char * lc;
1765    char * lineptr;
1766    int    ullineno;
1767    struct sgDb * udb;
1768    struct Source * sp;
1769    char   line[10000];
1770 
1771    sp = ufdbNewGV.lastSource;
1772    if (ufdbGV.debug)
1773       ufdbLogMessage( "ufdbSourceUserList: source \"%s\"  file \"%s\"",
1774        		      sp->name != NULL ? sp->name : "nosource", file );
1775    udb = sp->userDb;
1776    if (udb != NULL  &&  udb->type != SGDBTYPE_USERLIST)
1777    {
1778       ufdbLogFatalError( "userlist \"%s\" in source \"%s\" on line %d: "
1779                          "found mix of exec and non-exec userlists",
1780                          file, sp->name, lineno );
1781       return;
1782    }
1783    if (udb == NULL)
1784    {
1785       udb = sp->userDb = (struct sgDb *) ufdbMalloc( sizeof(struct sgDb) );
1786       udb->dbhome = NULL;
1787       udb->dbcp = (void *) UFDBmemDBinit();
1788       udb->type = SGDBTYPE_USERLIST;
1789       udb->entries = 0;
1790    }
1791 
1792    dbhome = ufdbNewGV.databaseDirectory;
1793 
1794    if (file[0] == '/')
1795       f = file;
1796    else
1797    {
1798       f = (char *) ufdbMalloc( strlen(dbhome) + strlen(file) + 2 );
1799       strcpy( f, dbhome );
1800       strcat( f, "/" );
1801       strcat( f, file );
1802       ufdbFree( file );
1803    }
1804 
1805    if ((fp = fopen(f,"r")) == NULL)
1806    {
1807       ufdbLogError( "line %d: can't open userlist %s: %s  *****", lineno, f, strerror(errno) );
1808       ufdbFree( f );
1809       return;
1810    }
1811    ullineno = 0;
1812 
1813    ufdbLogMessage( "userlist \"%s\"", f );
1814 
1815    while (fgets(line,sizeof(line),fp) != NULL)
1816    {
1817       ullineno++;
1818       if (line[0] == '#')			/* skip comments */
1819          continue;
1820 
1821       p = strchr( line, '\n' );
1822       if (p != NULL)
1823       {
1824          *p = '\0';
1825          if (p != line)
1826          {
1827   	    if (*(p - 1) == '\r') 		/* remove ^M  */
1828   	       *(p-1) = '\0';
1829          }
1830       }
1831 
1832       if (line[0] == '\0')
1833       {
1834          ufdbLogError( "userlist %s: line %d: line is empty", f, ullineno );
1835          continue;
1836       }
1837 
1838       c = strchr( line, '#' );		        /* remove comment */
1839       if (c != NULL)
1840          *c = '\0';
1841 
1842       p = strtok_r( line, " \t,", &lineptr );
1843       if (p == NULL  ||  *p == '\0')
1844          continue;
1845 
1846       /* we may have a passwd-style formatted file. Ignore ':' and everything else that follows. */
1847       if ((s = strchr(p,':')) != NULL)
1848          *s = '\0';
1849 
1850       do
1851       {
1852          for (lc = p;  *lc != '\0';  lc++)      // convert username to lowercase
1853          {
1854   	    if (*lc <= 'Z'  &&  *lc >= 'A')
1855   	       *lc += 'a' - 'A';
1856          }
1857          if (*p != '\0')
1858  	 {
1859 	    udb->entries++;
1860   	    UFDBmemDBinsert( (struct UFDBmemDB *) udb->dbcp, p, NULL );
1861          }
1862       } while ((p = strtok_r(NULL," \t,",&lineptr)) != NULL  &&  *p != '\0');
1863    }
1864    fclose( fp );
1865 
1866    if (ufdbGV.debug > 1)
1867    {
1868       ufdbLogMessage( "just read userlist \"%s\"", f );
1869       UFDBmemDBprintUserDB( "user", (struct UFDBmemDB *) udb->dbcp );
1870    }
1871 
1872    ufdbFree( f );
1873 }
1874 
1875 
ufdbSourceUserQuota(const char * seconds,const char * sporadic,const char * renew)1876 static void ufdbSourceUserQuota(
1877    const char * seconds,
1878    const char * sporadic,
1879    const char * renew )
1880 {
1881    if (seconds == NULL || sporadic == NULL || renew == NULL)
1882       { ; }      // prevent compiler warning
1883 
1884    ufdbLogError( "line %d: userquota is not supported", lineno );
1885 }
1886 
1887 
defSourceDomain(char * domain)1888 static void defSourceDomain(
1889    char * domain )
1890 {
1891    struct Source * sp;
1892 
1893    sp = ufdbNewGV.lastSource;
1894    if (sp->domainDb == NULL)
1895       sp->domainDb = (struct sgDb *) UFDBmemDBinit();
1896 
1897    if (ufdbGV.debug)
1898       ufdbLogMessage( "defSourceDomain \"%s\"", domain );
1899 
1900    UFDBmemDBinsert( (struct UFDBmemDB *) sp->domainDb, domain, NULL );
1901 
1902    ufdbFree( domain );
1903 }
1904 
1905 
ufdbSourceEval(int method)1906 static void ufdbSourceEval(
1907    int method  )
1908 {
1909    if (ufdbNewGV.lastSource != NULL)
1910    {
1911       if (ufdbGV.debug)
1912          ufdbLogMessage( "ufdbSourceEval %d: %s", method,
1913 	                 method == UFDB_EVAL_OR ? "evaluate-or" : "evaluate-and" );
1914       ufdbNewGV.lastSource->evaluationMethod = method;
1915    }
1916 }
1917 
1918 
defSourceTime(char * name,int within)1919 static void defSourceTime(
1920    char *         name,
1921    int            within )
1922 {
1923    struct ufdbTime *  t;
1924 
1925    if ((t = sgTimeFindName(name)) == NULL)
1926    {
1927       ufdbLogFatalError( "line %d: time \"%s\" is not defined in configuration file %s",
1928 		         lineno, name, ufdbNewGV.configFile );
1929       ufdbFree( name );
1930       return;
1931    }
1932 
1933    ufdbNewGV.lastSource->within = within;
1934    ufdbNewGV.lastSource->time = t;
1935    ufdbFree( name );
1936 }
1937 
1938 
defSourceFindName(struct Source * slist,const char * name)1939 static struct Source * defSourceFindName(
1940    struct Source * slist,
1941    const char *    name )
1942 {
1943    for ( ;  slist != NULL;  slist = slist->next)
1944    {
1945       if (strcmp(name,slist->name) == 0)
1946          return slist;
1947    }
1948    return NULL;
1949 }
1950 
1951 
defSourceIPV4List(char * file)1952 static void defSourceIPV4List(
1953    char * file )
1954 {
1955    char * dbhome;
1956    char * f;
1957    FILE * fp;
1958    char * p;
1959    char * c;
1960    char * cidr;
1961    int    i;
1962    int    l = 0;
1963    char * lineptr;
1964    char   line[UFDB_MAX_URL_LENGTH];
1965 
1966    dbhome = ufdbNewGV.databaseDirectory;
1967 
1968    if (file[0] == '/')
1969    {
1970       f = file;
1971    }
1972    else
1973    {
1974       f = (char *) ufdbMalloc( strlen(dbhome) + strlen(file) + 2 );
1975       strcpy( f, dbhome );
1976       strcat( f, "/" );
1977       strcat( f, file );
1978       ufdbFree( file );
1979    }
1980 
1981    if ((fp = fopen(f,"r")) == NULL)
1982    {
1983       ufdbLogError( "line %d: can't open ipv4list %s: %s", lineno, f, strerror(errno) );
1984       ufdbFree( f );
1985       return;
1986    }
1987 
1988    ufdbLogMessage( "ipv4list \"%s\"", f );
1989 
1990    while (fgets(line,sizeof(line),fp) != NULL)
1991    {
1992       l++;
1993       if (*line == '#')
1994          continue;
1995       p = strchr( line, '\n' );
1996       if (p != NULL && p != line)
1997       {
1998          if (*(p - 1) == '\r') 		        // remove ^M
1999             p--;
2000          *p = '\0';
2001       }
2002       c = strchr( line, '#' );
2003       p = strtok_r( line, " \t,", &lineptr );
2004       do {
2005          if (c != NULL && p >= c) 		// find the comment
2006             break;
2007          i = strspn( p, ".0123456789/-" );
2008          if (i == 0)
2009             break;
2010          *(p + i) = '\0';
2011          if ((cidr = strchr(p,'/')) != NULL)
2012          {
2013             *cidr = '\0';
2014             cidr++;
2015             if (strchr(cidr,'.') == NULL)
2016                sgIpv4( p, SG_IPTYPE_CIDR, f, l );
2017             else
2018                sgIpv4( p, SG_IPTYPE_CLASS, f, l );
2019           }
2020           else if ((cidr = strchr(p,'-')) != NULL)
2021           {
2022              sgIpv4( p, SG_IPTYPE_RANGE, f, l );
2023           }
2024           else
2025           {
2026              sgIpv4( p, SG_IPTYPE_HOST, f, l );
2027           }
2028       } while ((p = strtok_r(NULL," \t,",&lineptr)) != NULL);
2029    }
2030 
2031    fclose( fp );
2032    ufdbFree( f );
2033 }
2034 
2035 
defSourceIPV6List(char * file)2036 static void defSourceIPV6List(
2037    char * file )
2038 {
2039    char * dbhome;
2040    char * f;
2041    FILE * fp;
2042    char * p;
2043    char * c;
2044    int    i;
2045    int    l = 0;
2046    char * lineptr;
2047    char   line[UFDB_MAX_URL_LENGTH];
2048 
2049    dbhome = ufdbNewGV.databaseDirectory;
2050 
2051    if (file[0] == '/')
2052    {
2053       f = file;
2054    }
2055    else
2056    {
2057       f = (char *) ufdbMalloc( strlen(dbhome) + strlen(file) + 2 );
2058       strcpy( f, dbhome );
2059       strcat( f, "/" );
2060       strcat( f, file );
2061       ufdbFree( file );
2062    }
2063 
2064    if ((fp = fopen(f,"r")) == NULL)
2065    {
2066       ufdbLogError( "line %d: can't open ipv6list %s: %s", lineno, f, strerror(errno) );
2067       ufdbFree( f );
2068       return;
2069    }
2070 
2071    ufdbLogMessage( "ipv6list \"%s\"", f );
2072 
2073    while (fgets(line,sizeof(line),fp) != NULL)
2074    {
2075       l++;
2076       if (*line == '#')
2077          continue;
2078       p = strchr( line, '\n' );
2079       if (p != NULL && p != line)
2080       {
2081          if (*(p - 1) == '\r') 		        // remove ^M
2082             p--;
2083          *p = '\0';
2084       }
2085       c = strchr( line, '#' );
2086       p = strtok_r( line, " \t,", &lineptr );
2087       do {
2088          if (c != NULL && p >= c) 		// find the comment
2089             break;
2090          i = strspn( p, ":.0123456789ABCDEFabcdef/" );
2091          if (i == 0)
2092             break;
2093          *(p + i) = '\0';
2094          if (strchr(p,'/') != NULL)
2095          {
2096             sgIpv6( p, SG_IPV6TYPE_CIDR, f, l );
2097          }
2098          else
2099          {
2100             sgIpv6( p, SG_IPV6TYPE_HOST, f, l );
2101          }
2102       } while ((p = strtok_r(NULL," \t,",&lineptr)) != NULL);
2103    }
2104 
2105    fclose( fp );
2106    ufdbFree( f );
2107 }
2108 
2109 
2110 /* category block functions */
2111 
ufdbCategory(char * cat)2112 void ufdbCategory(
2113   char *            cat )
2114 {
2115   struct Category * sp;
2116 
2117 #if UFDB_DEBUG
2118    ufdbLogMessage( "ufdbCategory %s", cat );
2119 #endif
2120 
2121   if (ufdbNewGV.catList != NULL)
2122   {
2123     if ((struct Category *) ufdbCategoryFindByName(&ufdbNewGV,cat) != NULL)
2124     {
2125       ufdbLogFatalError( "line %d: category %s is already defined in configuration file %s",
2126 		         lineno, cat, ufdbNewGV.configFile );
2127       ufdbFree( cat );
2128       return;
2129     }
2130   }
2131 
2132   sp = (struct Category *) ufdbMallocAligned( UFDB_CACHELINE_SIZE, sizeof(struct Category) );
2133   sp->active = 1;
2134   sp->within = UFDB_ACL_NONE;
2135   sp->activeBumping = UFDB_ACTIVE_BUMPING_NOTSET;
2136   sp->blockBumpedConnect = 0;
2137   sp->options = 0;
2138   sp->name = cat;
2139   sp->domainlist = NULL;
2140   sp->domainlistDb = NULL;
2141   sp->execdomainlist = NULL;
2142   sp->expressionlist = NULL;
2143   sp->regExp = NULL;
2144   sp->redirect = NULL;
2145   sp->time = NULL;
2146   sp->rewrite = NULL;
2147   sp->next = NULL;
2148   sp->nblocks = 0;
2149   sp->nmatches = 0;
2150 
2151   if (ufdbNewGV.catList == NULL) {
2152     ufdbNewGV.catList = sp;
2153     ufdbNewGV.lastCat = sp;
2154   } else {
2155     ufdbNewGV.lastCat->next = sp;
2156     ufdbNewGV.lastCat = sp;
2157   }
2158 
2159    /* category "security" is a special case: the options cannot be set default to 0
2160     * because the default value for allow-unknown-protocol-over-https is ON.
2161     */
2162    if (strcmp( cat, "security" ) == 0)
2163       sp->options = UFDB_OPT_UNKNOWN_PROTOCOL_OVER_HTTPS;
2164 }
2165 
2166 
ufdbCategoryEnd(void)2167 void ufdbCategoryEnd( void )
2168 {
2169   struct Category * d;
2170 
2171   d = ufdbNewGV.lastCat;
2172   if (d->domainlist == NULL  &&  d->expressionlist == NULL  &&
2173       d->redirect == NULL  &&  d->rewrite == NULL  &&
2174       d->options == 0)
2175   {
2176     ufdbLogError( "category \"%s\" is missing content, set inactive  *****", d->name );
2177     d->time = NULL;
2178     d->active = 0;
2179   }
2180 }
2181 
2182 
ufdbCategoryOption(int value,int option)2183 static void ufdbCategoryOption( int value, int option )
2184 {
2185    struct Category * sp;
2186 
2187    sp = ufdbNewGV.lastCat;
2188    if (value)
2189       sp->options = sp->options | option;
2190    else
2191       sp->options = sp->options & (~option);
2192 
2193    if (option == UFDB_OPT_HTTPS_WITH_HOSTNAME)
2194       ufdbNewGV.httpsWithHostname = value;
2195    else if (option == UFDB_OPT_HTTPS_OFFICAL_CERTIFICATE)
2196       ufdbNewGV.httpsOfficialCertificate = value;
2197    else if (option == UFDB_OPT_SKYPE_OVER_HTTPS)
2198       ufdbNewGV.SkypeOverHttps = value;
2199    else if (option == UFDB_OPT_GTALK_OVER_HTTPS)
2200       ufdbNewGV.GtalkOverHttps = value;
2201    else if (option == UFDB_OPT_YAHOOMSG_OVER_HTTPS)
2202       ufdbNewGV.YahooMsgOverHttps = value;
2203    else if (option == UFDB_OPT_AIM_OVER_HTTPS)
2204       ufdbNewGV.AimOverHttps = value;
2205    else if (option == UFDB_OPT_FBCHAT_OVER_HTTPS)
2206       ufdbNewGV.FBchatOverHttps = value;
2207    else if (option == UFDB_OPT_CITRIXONLINE_OVER_HTTPS)
2208       ufdbNewGV.CitrixOnlineOverHttps = value;
2209    else if (option == UFDB_OPT_ANYDESK_OVER_HTTPS)
2210       ufdbNewGV.AnydeskOverHttps = value;
2211    else if (option == UFDB_OPT_TEAMVIEWER_OVER_HTTPS)
2212       ufdbNewGV.TeamviewerOverHttps = value;
2213    else if (option == UFDB_OPT_PROHIBIT_INSECURE_SSLV2)
2214       ufdbNewGV.httpsNoSSLv2 = value;
2215    else if (option == UFDB_OPT_PROHIBIT_INSECURE_SSLV3)
2216       ufdbNewGV.httpsNoSSLv3 = value;
2217    else if (option == UFDB_OPT_UNKNOWN_PROTOCOL_OVER_HTTPS)
2218       ufdbNewGV.unknownProtocolOverHttps = value;
2219    else if (option == UFDB_OPT_SAFE_SEARCH)
2220       ;
2221    else if (option == UFDB_OPT_YOUTUBE_EDUFILTER)
2222       ;
2223    else
2224       ufdbLogError( "ufdbCategoryOption: unrecognised option %d *****", option );
2225 
2226    if (ufdbGV.debug > 1)
2227       ufdbLogMessage( "ufdbCategoryOption: %s: option %d set to %d", sp->name, option, value );
2228 }
2229 
2230 
ufdbCategoryDomainList(char * domainlist)2231 void ufdbCategoryDomainList(
2232   char * domainlist )
2233 {
2234   struct Category * sp;
2235   const char *      dbhome;
2236         char *      dl;
2237   const char *      name;
2238 
2239   if (ufdbGV.debug > 1)
2240      ufdbLogMessage( "ufdbCategoryDomainList %s", domainlist==NULL ? "NULL" : domainlist );
2241 
2242   if (ufdbNewGV.terminating)
2243      return;
2244 
2245   sp = ufdbNewGV.lastCat;
2246 
2247   if (sp->domainlistDb != NULL)
2248   {
2249      ufdbLogFatalError( "line %d: specify only one domainlist per category in configuration file %s",
2250 		        lineno, ufdbNewGV.configFile );
2251      return;
2252   }
2253 
2254   dbhome = ufdbNewGV.databaseDirectory;
2255 
2256   if (domainlist == NULL)
2257   {
2258     name = sp->name;
2259     dl = (char *) ufdbMalloc( sizeof("/dest/") + strlen(name) + sizeof("/domainlist") + 2 );
2260     strcpy(dl,"/dest/");
2261     strcat(dl,name);
2262     strcat(dl,"/domainlist");
2263 
2264     sp->domainlist = (char *) ufdbMalloc( strlen(dbhome) + strlen(dl) + 2 );
2265     strcpy(sp->domainlist,dbhome);
2266     strcat(sp->domainlist,"/");
2267     strcat(sp->domainlist,dl);
2268   }
2269   else
2270   {
2271     if (domainlist[0] == '/')
2272       sp->domainlist = domainlist;
2273     else
2274     {
2275        sp->domainlist = (char *) ufdbMalloc( strlen(dbhome) + strlen(domainlist) + 2 );
2276        strcpy( sp->domainlist, dbhome );
2277        strcat( sp->domainlist, "/" );
2278        strcat( sp->domainlist, domainlist );
2279        ufdbFree( (void*) domainlist );
2280     }
2281   }
2282 
2283   sp->domainlistDb = (struct sgDb *) ufdbCalloc( 1, sizeof(struct sgDb) );
2284   sp->domainlistDb->type = SGDBTYPE_DOMAINLIST;
2285   ufdbLogMessage( "loading URL table from \"%s\"", sp->domainlist );
2286   sgDbInit( sp->domainlistDb, sp->domainlist );
2287   if (sp->domainlistDb->dbcp == NULL  ||
2288       ((struct UFDBmemTable *) sp->domainlistDb->dbcp)->table.nNextLevels == 0)
2289   {
2290     ufdbLogError( "URL database table \"%s\" is empty and is ignored   *****", sp->domainlist );
2291     ufdbFreeDomainDb( sp->domainlistDb );
2292     sp->domainlistDb = NULL;
2293   }
2294 }
2295 
2296 
ufdbCategoryExecDomainList(char * command)2297 static void ufdbCategoryExecDomainList(
2298    char * command )
2299 {
2300    struct Category * sp;
2301 
2302 #if UFDB_DEBUG
2303    ufdbLogMessage( "ufdbCategoryExecDomainList %s", command );
2304 #endif
2305 
2306    sp = ufdbNewGV.lastCat;
2307 
2308    if (sp == NULL)
2309    {
2310       ufdbLogFatalError( "execdomainlist is not within a URL category" );
2311       return;
2312    }
2313    sp->execdomainlist = command;
2314 
2315    if (ufdbGV.debug)
2316       ufdbLogMessage( "execdomainlist for category %s: \"%s\"", sp->name, command );
2317 }
2318 
2319 
ufdbFreeDomainDb(struct sgDb * dbp)2320 void ufdbFreeDomainDb(
2321    struct sgDb * dbp )
2322 {
2323    struct UFDBmemTable * mt;
2324 
2325    if (dbp == NULL)
2326       return;
2327 
2328    mt = (struct UFDBmemTable *) dbp->dbcp;
2329    if (mt != NULL)
2330    {
2331       if (mt->index != NULL)
2332       {
2333 	 ufdbFree( mt->index );				/* version 2.x */
2334 	 ufdbFree( mt->table.nextLevel );
2335       }
2336       else
2337 	 UFDBfreeTableIndex_1_2( &(mt->table) ); 	/* version 1.2 */
2338 #if HAVE_MADVISE && !UFDB_BARE_METAL_SUPPORT && __linux__
2339       if (mt->madvisedSize)
2340          madvise( mt->mem, mt->madvisedSize, MADV_NORMAL );
2341 #endif
2342       if (mt->memStatus == UFDB_MEMSTATUS_MALLOC)
2343          ufdbFree( (void *) mt->mem );
2344       ufdbFree( mt );
2345    }
2346    ufdbFree( dbp );
2347 }
2348 
2349 
ufdbFreeIpv4List(struct Ipv4 * ipv4)2350 void ufdbFreeIpv4List( struct Ipv4 * ipv4 )
2351 {
2352    struct Ipv4 * tmp;
2353 
2354    while (ipv4 != NULL)
2355    {
2356       tmp = ipv4->next;
2357       ufdbFree( (void*) ipv4 );
2358       ipv4 = tmp;
2359    }
2360 }
2361 
2362 
ufdbFreeIpv6List(struct Ipv6 * ipv6)2363 void ufdbFreeIpv6List( struct Ipv6 * ipv6 )
2364 {
2365    struct Ipv6 * tmp;
2366 
2367    while (ipv6 != NULL)
2368    {
2369       tmp = ipv6->next;
2370       ufdbFree( (void*) ipv6 );
2371       ipv6 = tmp;
2372    }
2373 }
2374 
2375 
ufdbFreeSourceList(struct Source * src)2376 static void ufdbFreeSourceList( struct Source * src )
2377 {
2378    struct Source * tmp;
2379 
2380    while (src != NULL)
2381    {
2382       if (src->ipv4hosts != NULL)
2383          UFDBmemDBdeleteDB( src->ipv4hosts );
2384       ufdbFreeIpv4List( src->ipv4 );
2385       if (src->ipv6hosts != NULL)
2386          UFDBmemDBdeleteDB( src->ipv6hosts );
2387       ufdbFreeIpv6List( src->ipv6 );
2388 
2389       ufdbFree( (void*) src->name );
2390       if (src->domainDb != NULL)
2391 	 UFDBmemDBdeleteDB( (struct UFDBmemDB *) src->domainDb );
2392 
2393       /* execuserlist content is saved during a reload except when ufdbguardd terminates */
2394       if (src->userDb != NULL  &&  src->userDb->type != SGDBTYPE_EXECUSERLIST)
2395       {
2396 	 UFDBmemDBdeleteDB( (struct UFDBmemDB *) src->userDb->dbcp );
2397 	 ufdbFree( (void*) src->userDb );
2398       }
2399       ufdbFree( (void*) src->sarg0 );
2400       ufdbFree( (void*) src->execiplistCommand );
2401 
2402       tmp = src->next;
2403       ufdbFree( (void*) src );
2404       src = tmp;
2405    }
2406 }
2407 
2408 
ufdbFreeAclCategoryList(struct AclCategory * ac)2409 static void ufdbFreeAclCategoryList( struct AclCategory * ac )
2410 {
2411    struct AclCategory * tmp;
2412 
2413    while (ac != NULL)
2414    {
2415       ufdbFree( (void*) ac->name );
2416       tmp = ac->next;
2417       ufdbFree( (void*) ac );
2418       ac = tmp;
2419    }
2420 }
2421 
2422 
ufdbFreeAclList(struct Acl * acl)2423 static void ufdbFreeAclList( struct Acl * acl )
2424 {
2425    struct Acl * tmp;
2426 
2427    while (acl != NULL)
2428    {
2429       ufdbFree( (void*) acl->name );
2430       ufdbFreeAclCategoryList( acl->pass );
2431       ufdbFreeAclCategoryList( acl->implicitPass );
2432       ufdbFree( (void*) acl->redirect );
2433       tmp = acl->next;
2434       ufdbFree( (void*) acl );
2435       acl = tmp;
2436    }
2437 }
2438 
2439 
ufdbFreeCategoryList(struct Category * cat)2440 static void ufdbFreeCategoryList( struct Category * cat )
2441 {
2442    struct Category * tmp;
2443 
2444    while (cat != NULL)
2445    {
2446       if (ufdbGV.debug > 1  ||  ufdbGV.debugRegexp)
2447          ufdbLogMessage( "ufdbFreeCategoryList: freeing regexp of category %s", cat->name );
2448       ufdbFreeRegExprList( cat->regExp );
2449       ufdbFree( (void*) cat->name );
2450       ufdbFree( (void*) cat->domainlist );
2451       ufdbFreeDomainDb( cat->domainlistDb );
2452       ufdbFree( (void*) cat->execdomainlist );
2453       ufdbFree( (void*) cat->expressionlist );
2454       ufdbFree( (void*) cat->redirect );
2455       /* ufdbFree( cat->rewrite );  freed in ufdbFreeRewriteList() */
2456       tmp = cat->next;
2457       ufdbFree( (void*) cat );
2458       cat = tmp;
2459    }
2460 }
2461 
2462 
ufdbFreeRewriteList(struct sgRewrite * p)2463 static void ufdbFreeRewriteList(
2464    struct sgRewrite * p )
2465 {
2466    struct sgRewrite * tmp;
2467 
2468    while (p != NULL)
2469    {
2470       ufdbFreeRegExprList( p->rewrite );
2471       ufdbFree( (void*) p->name );
2472       tmp = p->next;
2473       ufdbFree( (void*) p );
2474       p = tmp;
2475    }
2476 }
2477 
2478 
ufdbFreeTimeElement(struct TimeElement * p)2479 static void ufdbFreeTimeElement(
2480    struct TimeElement * p )
2481 {
2482    struct TimeElement * tmp;
2483 
2484    while (p != NULL)
2485    {
2486       tmp = p->next;
2487       ufdbFree( (void*) p );
2488       p = tmp;
2489    }
2490 }
2491 
2492 
ufdbFreeTime(struct ufdbTime * p)2493 static void ufdbFreeTime(
2494    struct ufdbTime * p )
2495 {
2496    struct ufdbTime * tmp;
2497 
2498    while (p != NULL)
2499    {
2500       ufdbFree( (void*) p->name );
2501       ufdbFreeTimeElement( p->element );
2502       tmp = p->next;
2503       ufdbFree( (void*) p );
2504       p = tmp;
2505    }
2506 }
2507 
2508 
ufdbFreeLastBits(struct ufdbGV * gv)2509 void ufdbFreeLastBits( struct ufdbGV * gv )
2510 {
2511    /* we may need these variables during a reload */
2512    if (gv->pidFilename != NULL)
2513    {
2514       ufdbFree( (void*) gv->pidFilename );
2515       gv->pidFilename = NULL;
2516    }
2517    if (gv->emailServer != NULL)
2518    {
2519       ufdbFree( (void*) gv->emailServer );
2520       gv->emailServer = NULL;
2521    }
2522    if (gv->myHostname != NULL)
2523    {
2524       ufdbFree( (void*) gv->myHostname );
2525       gv->myHostname = NULL;
2526    }
2527    if (gv->adminEmail != NULL)
2528    {
2529       ufdbFree( (void*) gv->adminEmail );
2530       gv->adminEmail = NULL;
2531    }
2532    if (gv->senderEmail != NULL)
2533    {
2534       ufdbFree( (void*) gv->senderEmail );
2535       gv->senderEmail = NULL;
2536    }
2537    if (gv->externalStatusCommand != NULL)
2538    {
2539       ufdbFree( (void*) gv->externalStatusCommand );
2540       gv->externalStatusCommand = NULL;
2541    }
2542 }
2543 
2544 
ufdbFreeAllMemory(struct ufdbGV * gv)2545 void ufdbFreeAllMemory( struct ufdbGV * gv )
2546 {
2547    if (gv->logDir != NULL)
2548       ufdbFree( gv->logDir );
2549    gv->logDir = NULL;
2550 
2551    ufdbFreeRewriteList( gv->rewrite );
2552    gv->rewrite = NULL;
2553    gv->lastRewrite = NULL;
2554    gv->lastRewriteRegExec = NULL;
2555 
2556    ufdbFreeCategoryList( gv->catList );
2557    gv->catList = NULL;
2558    gv->lastCat = NULL;
2559 
2560    ufdbFreeAclList( gv->aclList );
2561    gv->aclList = NULL;
2562    gv->lastAcl = NULL;
2563    gv->defaultAcl = NULL;
2564    gv->lastAclCategory = NULL;
2565 
2566    ufdbFreeSourceList( (struct Source *) gv->sourceList );
2567    gv->sourceList = NULL;
2568    gv->lastSource = NULL;
2569 
2570    /* free ufdbNewGV.checkedDB */
2571    if (gv->checkedDB.mem != NULL)
2572    {
2573       if (gv->checkedDB.index != NULL)
2574       {
2575          ufdbFree( (void*) gv->checkedDB.index );
2576 	 ufdbFree( gv->checkedDB.table.nextLevel );
2577       }
2578       else
2579 	 UFDBfreeTableIndex_1_2( &(gv->checkedDB.table) );
2580 #if HAVE_MADVISE && !UFDB_BARE_METAL_SUPPORT && __linux__
2581       if (gv->checkedDB.madvisedSize)
2582          madvise( gv->checkedDB.mem, gv->checkedDB.madvisedSize, MADV_NORMAL );
2583 #endif
2584       gv->checkedDB.madvisedSize = 0;
2585       if (gv->checkedDB.memStatus == UFDB_MEMSTATUS_MALLOC)
2586          ufdbFree( (void*) gv->checkedDB.mem );
2587    }
2588    gv->checkedDB.table.nextLevel = NULL;
2589    gv->checkedDB.table.nNextLevels = 0;
2590    gv->checkedDB.mem = NULL;
2591    gv->checkedDB.index = NULL;
2592 
2593    if (gv->checkedExpressions != NULL)
2594    {
2595       ufdbFreeRegExprList( gv->checkedExpressions );
2596       gv->checkedExpressions = NULL;
2597    }
2598 
2599    ufdbFreeTime( gv->timeList );
2600    gv->timeList = NULL;
2601    gv->lastTime = NULL;
2602 
2603    gv->lastTimeElement = NULL;
2604    gv->timeElement = NULL;
2605 
2606    gv->lastRegExpDest = NULL;
2607 
2608    ufdbFree( (void*) gv->SquidVersion );
2609    gv->SquidVersion = NULL;
2610 }
2611 
2612 
ufdbCategoryUrlList(char * urllist)2613 static void ufdbCategoryUrlList(
2614    char * urllist )
2615 {
2616    if (urllist == NULL)
2617       urllist = (char *) "-";
2618    ufdbLogError( "line %d: \"urllist %s\" is deprecated and ignored *****\n"
2619                  "ufdbGenTable should be called with the -u option to include URLs\n"
2620 		 "ufdbGenTable combines URLs and domains in one table file so only the domainlist is required",
2621 		 lineno, urllist );
2622    if (urllist[0] != '-')
2623       ufdbFree( (void*) urllist );
2624 }
2625 
2626 
ufdbCategoryExpressionList(char * exprlist,const char * chcase)2627 void ufdbCategoryExpressionList(
2628   char * exprlist,
2629   const char * chcase  )
2630 {
2631   FILE * fp;
2632   char * dbhome;
2633   char * dl;
2634   const char * name;
2635   char * p;
2636   int    flags;
2637   struct stat         statbuf;
2638   struct Category *   sp;
2639   struct ufdbRegExp * regexp;
2640   char   buf[UFDB_MAX_URL_LENGTH];
2641   char   errbuf[256];
2642 
2643 #if UFDB_DEBUG
2644    ufdbLogMessage( "ufdbCategoryExpressionList %s", exprlist );
2645 #endif
2646 
2647   flags = REG_EXTENDED | REG_NOSUB;
2648   sp = ufdbNewGV.lastCat;
2649   dbhome = ufdbNewGV.databaseDirectory;
2650 
2651   if (exprlist == NULL)
2652   {
2653     name = sp->name;
2654     dl = (char *) ufdbMalloc( sizeof("/dest/") + strlen(name) + sizeof("/expressionlist") + 2 );
2655     strcpy(dl,"/dest/");
2656     strcat(dl,name);
2657     strcat(dl,"/expressionlist");
2658 
2659     flags |= REG_ICASE; 	/* default case insensitive */
2660 
2661     sp->expressionlist = (char *) ufdbMalloc( strlen(dbhome) + strlen(dl) + 2 );
2662     strcpy(sp->expressionlist,dbhome);
2663     strcat(sp->expressionlist,"/");
2664     strcat(sp->expressionlist,dl);
2665   }
2666   else
2667   {
2668     if (exprlist[0] == '/')
2669     {
2670       sp->expressionlist = exprlist;
2671     }
2672     else
2673     {
2674        sp->expressionlist = (char *) ufdbMalloc( strlen(dbhome) + strlen(exprlist) + 2 );
2675        strcpy( sp->expressionlist, dbhome );
2676        strcat( sp->expressionlist, "/" );
2677        strcat( sp->expressionlist, exprlist );
2678        ufdbFree( (void*) exprlist );
2679     }
2680     if (*chcase == 'i')
2681        flags |= REG_ICASE;     /* set case insensitive */
2682   }
2683 
2684   ufdbLogMessage( "loading regular expressions from \"%s\"", sp->expressionlist );
2685 
2686   if ((fp = fopen(sp->expressionlist, "r")) == NULL)
2687   {
2688     ufdbLogFatalError( "cannot open expression list %s: %s", sp->expressionlist, strerror(errno) );
2689     return;
2690   }
2691 
2692   if (0 == fstat( fileno(fp), &statbuf ))
2693   {
2694      if (!S_ISREG(statbuf.st_mode))
2695      {
2696         ufdbLogFatalError( "expression list %s: not a regular file", sp->expressionlist );
2697 	fclose( fp );
2698 	return;
2699      }
2700   }
2701 
2702   while (!feof(fp)  &&  fgets(buf, sizeof(buf), fp) != NULL)
2703   {
2704     if (buf[0] == '#')
2705        continue;
2706 
2707     p = (char *) strchr( buf, '\n' );
2708     if (p != NULL  &&  p != buf)
2709     {
2710       if (*(p-1) == '\r') 	/* removing ^M  */
2711 	p--;
2712       *p = '\0';
2713     }
2714     /* TO-DO: warn about leading and trailing spaces */
2715     regexp = ufdbNewPatternBuffer( buf, flags );
2716     if (regexp->error)
2717     {
2718       regerror( regexp->error, (regex_t*) regexp->compiled[0], errbuf, sizeof(errbuf) );
2719       ufdbLogError( "regular expression error in %s:\n%s : %s   *****", sp->expressionlist, errbuf, buf );
2720     }
2721     if (ufdbNewGV.lastCat->regExp == NULL)
2722     {
2723       ufdbNewGV.lastCat->regExp = regexp;
2724       ufdbNewGV.lastRegExpDest = regexp;
2725     }
2726     else
2727     {
2728       ufdbNewGV.lastRegExpDest->next = regexp;
2729       ufdbNewGV.lastRegExpDest = regexp;
2730     }
2731   }
2732   fclose( fp );
2733 
2734   if (ufdbNewGV.expressionOptimisation)
2735      ufdbNewGV.lastCat->regExp = UFDBoptimizeExprList( sp->expressionlist, ufdbNewGV.lastCat->regExp );
2736 }
2737 
2738 
ufdbCategoryCACertsFile(char * cacertsFile)2739 static void ufdbCategoryCACertsFile(
2740    char *              cacertsFile )
2741 {
2742    struct Category *   cat;
2743 
2744    cat = ufdbNewGV.lastCat;
2745    if (cat == NULL  ||  strcmp( cat->name, "security" ) != 0)
2746    {
2747       ufdbLogFatalError( "cacerts can only be defined inside the \"security\" category" );
2748       return;
2749    }
2750 
2751    if (*cacertsFile == '/')
2752       strcpy( ufdbNewGV.CAcertsFile, cacertsFile );
2753    else
2754    {
2755       char * dbh;
2756       dbh = ufdbNewGV.databaseDirectory;
2757       strcpy( ufdbNewGV.CAcertsFile, dbh );
2758       strcat( ufdbNewGV.CAcertsFile, "/" );
2759       strcat( ufdbNewGV.CAcertsFile, cacertsFile );
2760    }
2761 }
2762 
2763 
ufdbCategoryCACertsDir(char * cacertsDir)2764 static void ufdbCategoryCACertsDir(
2765    char *              cacertsDir )
2766 {
2767    struct Category *   cat;
2768 
2769    cat = ufdbNewGV.lastCat;
2770    if (cat == NULL  ||  strcmp( cat->name, "security" ) != 0)
2771    {
2772       ufdbLogFatalError( "cacerts-dir can only be defined inside the \"security\" category" );
2773       return;
2774    }
2775 
2776    if (*cacertsDir == '/')
2777       strcpy( ufdbNewGV.CAcertsDir, cacertsDir );
2778    else
2779    {
2780       char * dbh;
2781       dbh = ufdbNewGV.databaseDirectory;
2782       strcpy( ufdbNewGV.CAcertsDir, dbh );
2783       strcat( ufdbNewGV.CAcertsDir, "/" );
2784       strcat( ufdbNewGV.CAcertsDir, cacertsDir );
2785    }
2786 }
2787 
2788 
ufdbCategoryRedirect(char * value)2789 static void ufdbCategoryRedirect(
2790    char * value )
2791 {
2792    struct Category * sp;
2793 
2794 #if UFDB_DEBUG
2795    ufdbLogMessage( "ufdbCategoryRedirect %s", value );
2796 #endif
2797 
2798    sp = ufdbNewGV.lastCat;
2799    sp->redirect = value;
2800    /* TODO: check that "localhost" is not here if no 302 is used */
2801 }
2802 
2803 
ufdbCategoryRewrite(char * value)2804 static void ufdbCategoryRewrite(
2805   char *              value )
2806 {
2807   struct sgRewrite *  rewrite;
2808 
2809   if ((rewrite = sgRewriteFindName(value)) == NULL)
2810   {
2811     ufdbLogFatalError( "line %d: rewrite %s is not defined in configuration file %s",
2812 		       lineno, value, ufdbNewGV.configFile );
2813     return;
2814   }
2815 
2816   ufdbNewGV.lastCat->rewrite = rewrite;
2817 }
2818 
2819 
ufdbCategoryBlockConnect(int flag)2820 static void ufdbCategoryBlockConnect(
2821    int flag  )
2822 {
2823    ufdbNewGV.lastCat->blockBumpedConnect = flag;
2824 
2825    if (ufdbGV.debug > 1)
2826       ufdbLogMessage( "ufdbCategoryBlockConnect: category \"%s\" : block-bumped-connect %s",
2827                       ufdbNewGV.lastCat->name, flag ? "on" : "off" );
2828 }
2829 
2830 
ufdbCategoryActiveBumping(int flag)2831 static void ufdbCategoryActiveBumping(
2832    int flag  )
2833 {
2834    ufdbNewGV.lastCat->activeBumping = flag ? UFDB_ACTIVE_BUMPING_ON : UFDB_ACTIVE_BUMPING_OFF;
2835 
2836    if (ufdbGV.debug > 1)
2837       ufdbLogMessage( "ufdbCategoryActiveBumping: category \"%s\" : squid-uses-active-bumping %s",
2838                       ufdbNewGV.lastCat->name, flag ? "on" : "off" );
2839 }
2840 
2841 
ufdbCategoryTime(char * name,int within)2842 static void ufdbCategoryTime(
2843    char *       name,
2844    int  	within )
2845 {
2846    struct ufdbTime * t;
2847 
2848    if ((t = sgTimeFindName(name)) == NULL)
2849    {
2850       ufdbLogFatalError( "line %d: time \"%s\" is not defined in configuration file %s",
2851    		         lineno, name, ufdbNewGV.configFile );
2852       ufdbFree( (void*) name );
2853       return;
2854    }
2855 
2856    ufdbNewGV.lastCat->within = within;
2857    ufdbNewGV.lastCat->time = t;
2858    ufdbFree( (void*) name );
2859 }
2860 
2861 
ufdbCategoryFindByName(struct ufdbGV * gv,const char * name)2862 struct Category * ufdbCategoryFindByName(
2863    struct ufdbGV *   gv,
2864    const char *      name )
2865 {
2866    struct Category * p;
2867 
2868    for (p = gv->catList;  p != NULL;  p = p->next)
2869    {
2870       if (strcmp(name,p->name) == 0)
2871          return p;
2872    }
2873    return NULL;
2874 }
2875 
2876 
sgRewrite(char * rewrite)2877 static void sgRewrite(
2878   char * rewrite )
2879 {
2880   struct sgRewrite * rew;
2881 
2882 #if UFDB_DEBUG
2883    ufdbLogMessage( "sgRewrite %s", rewrite );
2884 #endif
2885 
2886   if (ufdbNewGV.rewrite != NULL)
2887   {
2888     if ((struct sgRewrite *) sgRewriteFindName(rewrite) != NULL)
2889     {
2890       ufdbLogFatalError( "line %d: rewrite \"%s\" is not defined in configuration file %s",
2891 		         lineno, rewrite, ufdbNewGV.configFile );
2892       ufdbFree( (void*) rewrite );
2893       return;
2894     }
2895   }
2896 
2897   rew = (struct sgRewrite *) ufdbMalloc( sizeof(struct sgRewrite) );
2898   rew->name = rewrite;
2899   rew->active = 1;
2900   rew->rewrite = NULL;
2901   rew->time = NULL;
2902   rew->within = UFDB_ACL_NONE;
2903   rew->next = NULL;
2904 
2905   if (ufdbNewGV.rewrite == NULL)
2906   {
2907     ufdbNewGV.rewrite = rew;
2908     ufdbNewGV.lastRewrite = rew;
2909   }
2910   else
2911   {
2912     ufdbNewGV.lastRewrite->next = rew;
2913     ufdbNewGV.lastRewrite = rew;
2914   }
2915 }
2916 
2917 
sgRewriteTime(char * name,int within)2918 static void sgRewriteTime(
2919   char * name,
2920   int    within )
2921 {
2922   struct ufdbTime * t;
2923 
2924 #if UFDB_DEBUG
2925    ufdbLogMessage( "sgRewriteTime %s %d", name, within );
2926 #endif
2927 
2928   if ((t = sgTimeFindName(name)) == NULL)
2929   {
2930     ufdbLogFatalError( "line %d: time \"%s\" is not defined in configuration file %s",
2931 		       lineno, name, ufdbNewGV.configFile );
2932     ufdbFree( (void*) name );
2933     return;
2934   }
2935 
2936   ufdbNewGV.lastRewrite->within = within;
2937   ufdbNewGV.lastRewrite->time = t;
2938   ufdbFree( (void*) name );
2939 }
2940 
2941 
sgRewriteSubstitute(char * string)2942 static void sgRewriteSubstitute(
2943   char * string )
2944 {
2945   char * pattern;
2946   char * subst = NULL;
2947   char * p;
2948   int    flags = REG_EXTENDED;
2949   int    global = 0;
2950   char * httpcode = NULL;
2951   struct ufdbRegExp * regexp;
2952   char   errbuf[256];
2953 
2954   pattern = string + 2; 	/* skipping s@ */
2955   p = pattern;
2956   while ((p = strchr(p,'@')) != NULL)
2957   {
2958     if (*(p - 1) != '\\')
2959     {
2960       *p = '\0';
2961       subst = p + 1;
2962       break;
2963     }
2964     p++;
2965   }
2966 
2967   p = strrchr( subst, '@' );
2968   while (p != NULL  &&  *p != '\0')
2969   {
2970     if (*p == 'r' )
2971       httpcode =  (char *) REDIRECT_TEMPORARILY;
2972     if (*p == 'R' )
2973       httpcode =  (char *) REDIRECT_PERMANENT;
2974     if (*p == 'i' || *p == 'I')
2975       flags |= REG_ICASE;
2976     if (*p == 'g')
2977       global = 1;
2978     *p = '\0'; 		/* removes @i from string */
2979     p++;
2980   }
2981 
2982   regexp = ufdbNewPatternBuffer( pattern, flags );
2983   if (regexp->error)
2984   {
2985       regerror( regexp->error, (regex_t*) regexp->compiled[0], errbuf, sizeof(errbuf) );
2986       ufdbLogError( "line %d: regular expression error in %s:\n%s   *****", lineno, pattern, errbuf );
2987   }
2988   else {
2989     regexp->substitute = ufdbStrdup( subst );
2990   }
2991 
2992   if (ufdbNewGV.lastRewrite->rewrite == NULL)
2993     ufdbNewGV.lastRewrite->rewrite = regexp;
2994   else
2995     ufdbNewGV.lastRewriteRegExec->next = regexp;
2996   regexp->httpcode = httpcode;
2997   regexp->global = global;
2998   ufdbNewGV.lastRewriteRegExec = regexp;
2999 }
3000 
3001 
sgRewriteFindName(const char * name)3002 static struct sgRewrite * sgRewriteFindName(
3003    const char * name )
3004 {
3005    struct sgRewrite * p;
3006 
3007    for (p = ufdbNewGV.rewrite;  p != NULL;  p = p->next)
3008    {
3009       if (strcmp(name,p->name) == 0)
3010          return p;
3011    }
3012    return NULL;
3013 }
3014 
3015 
3016 /*
3017  * Time functions
3018  */
3019 
3020 /*
3021  * sgTime - parse configuration time statement.
3022  */
sgTime(char * name)3023 static void sgTime(
3024   char *        name )
3025 {
3026   struct ufdbTime * t;
3027 
3028   if (ufdbGV.debug > 1)
3029      ufdbLogMessage( "sgTime %s", name );
3030 
3031   if (ufdbNewGV.timeList != NULL)
3032   {
3033     if ((struct ufdbTime *) sgTimeFindName(name) != NULL)
3034     {
3035       ufdbLogFatalError( "line %d: time \"%s\" is not defined in configuration file %s",
3036 		         lineno, name, ufdbNewGV.configFile );
3037       ufdbFree( (void*) name );
3038       return;
3039     }
3040   }
3041   else
3042     numTimeElements = 0;
3043 
3044   t = (struct ufdbTime *) ufdbMalloc( sizeof(struct ufdbTime) );
3045   t->name = name;
3046   t->active = 1;
3047   t->element = NULL;
3048   t->next = NULL;
3049 
3050   ufdbNewGV.timeElement = NULL;
3051   ufdbNewGV.lastTimeElement = NULL;
3052   if (ufdbNewGV.timeList == NULL)
3053   {
3054     ufdbNewGV.timeList = t;
3055     ufdbNewGV.lastTime = t;
3056   }
3057   else
3058   {
3059     ufdbNewGV.lastTime->next = t;
3060     ufdbNewGV.lastTime = t;
3061   }
3062 }
3063 
3064 
3065 /*
3066  * sgTimeElementInit - initialise parsing of a configuration time element.
3067  */
sgTimeElementInit(void)3068 static void sgTimeElementInit( void )
3069 {
3070    struct TimeElement * te;
3071 
3072    te = (struct TimeElement *) ufdbCalloc( 1, sizeof(struct TimeElement) );
3073    numTimeElements++;
3074 
3075    if (ufdbNewGV.lastTime->element == NULL)
3076      ufdbNewGV.lastTime->element = te;
3077    if (ufdbNewGV.lastTimeElement != NULL)
3078      ufdbNewGV.lastTimeElement->next = te;
3079    ufdbNewGV.lastTimeElement = te;
3080 }
3081 
3082 
3083 /*
3084  * sgTimeElementEnd - finalise parsing of configuration time element.
3085  */
sgTimeElementEnd(void)3086 static void sgTimeElementEnd( void )
3087 {
3088   time_switch = 0;
3089   date_switch = 0;
3090 
3091   if (ufdbNewGV.lastTimeElement->fromdate != 0)
3092   {
3093     if (ufdbNewGV.lastTimeElement->todate == 0)
3094       ufdbNewGV.lastTimeElement->todate = ufdbNewGV.lastTimeElement->fromdate + 86399;
3095     else
3096       ufdbNewGV.lastTimeElement->todate = ufdbNewGV.lastTimeElement->todate + 86399;
3097   }
3098 
3099   if (ufdbNewGV.lastTimeElement->from == 0  &&  ufdbNewGV.lastTimeElement->to == 0)
3100     ufdbNewGV.lastTimeElement->to = 1439;  /* set time to 23:59 */
3101 }
3102 
3103 
3104 /*
3105  * sgTimeElementAdd - add configuration time element.
3106  */
sgTimeElementAdd(char * element,char type)3107 static void sgTimeElementAdd(
3108   char * element,
3109   char   type )
3110 {
3111   struct TimeElement * te;
3112   char * p;
3113   char   wday;
3114   int    h, m, Y, M, D;
3115   time_t sec;
3116   char * lineptr;
3117 
3118   wday = 0;
3119   M = 0;
3120   D = -1;
3121   te = ufdbNewGV.lastTimeElement;
3122 
3123   switch (type)
3124   {
3125   case T_WEEKDAY:
3126     p = strtok_r( element, " \t,", &lineptr );
3127     do {
3128       if (*p == '*') {
3129 	wday = 0x7F;
3130       } else if (!strncmp(p,"sun",3)) {
3131 	wday = wday | 0x01;
3132       } else if (!strncmp(p,"mon",3)) {
3133 	wday = wday | 0x02;
3134       } else if (!strncmp(p,"tue",3)) {
3135 	wday = wday | 0x04;
3136       } else if (!strncmp(p,"wed",3)) {
3137 	wday = wday | 0x08;
3138       } else if (!strncmp(p,"thu",3)) {
3139 	wday = wday | 0x10;
3140       } else if (!strncmp(p,"fri",3)) {
3141 	wday = wday | 0x20;
3142       } else if (!strncmp(p,"sat",3)) {
3143 	wday = wday | 0x40;
3144       }
3145       p = strtok_r( NULL, " \t,", &lineptr );
3146     } while (p != NULL);
3147     te->wday = wday;
3148     break;
3149 
3150   case T_TVAL:
3151     h = -1;
3152     m = -1;
3153     sscanf( element, "%d:%d", &h, &m );
3154     if ((h < 0 || h > 24) || (m < 0 || m > 59))
3155     {
3156       ufdbLogFatalError( "line %d: time format error in %s", lineno, ufdbNewGV.configFile );
3157       h = 0;
3158       m = 0;
3159     }
3160     if (time_switch == 0)
3161     {
3162       time_switch++;
3163       te->from = (h * 60) + m ;
3164     }
3165     else
3166     {
3167       time_switch = 0;
3168       te->to = (h * 60) + m ;
3169     }
3170     break;
3171 
3172   case T_DVAL:
3173     sec = date2sec( element );
3174     if (sec == -1)
3175     {
3176       ufdbLogFatalError( "line %d: date format error in %s", lineno, ufdbNewGV.configFile );
3177       sec = 1;
3178     }
3179     if (date_switch == 0) {
3180       date_switch++;
3181       te->fromdate = sec;
3182     } else {
3183       date_switch = 0;
3184       te->todate = sec;
3185     }
3186     break;
3187 
3188   case T_DVALCRON:
3189     p = strtok_r( element, "-./", &lineptr );
3190     Y = atoi(p);
3191     if (*p == '*')
3192       Y = -1;
3193     else
3194       Y = atoi(p);
3195     while ((p=strtok_r(NULL,"-./",&lineptr)) != NULL)
3196     {
3197       if (*p == '*')
3198 	if (M == 0)
3199 	  M = -1;
3200 	else
3201 	  D = -1;
3202       else
3203 	if (M == 0)
3204 	  M = atoi(p);
3205 	else
3206 	  D = atoi(p);
3207     }
3208     te->y = Y;
3209     te->m = M;
3210     te->d = D;
3211     break;
3212 
3213   case T_WEEKLY:
3214     p = element;
3215     while (*p != '\0')
3216     {
3217       switch (*p) {
3218       case 'S':
3219       case 's':
3220 	wday = wday | 0x01;
3221 	break;
3222       case 'M':
3223       case 'm':
3224 	wday = wday | 0x02;
3225 	break;
3226       case 'T':
3227       case 't':
3228 	wday = wday | 0x04;
3229 	break;
3230       case 'W':
3231       case 'w':
3232 	wday = wday | 0x08;
3233 	break;
3234       case 'H':
3235       case 'h':
3236 	wday = wday | 0x10;
3237 	break;
3238       case 'F':
3239       case 'f':
3240 	wday = wday | 0x20;
3241 	break;
3242       case 'A':
3243       case 'a':
3244 	wday = wday | 0x40;
3245 	break;
3246       default:
3247 	ufdbLogFatalError( "line %d: weekday format error in %s", lineno, ufdbNewGV.configFile );
3248 	break;
3249       }
3250       p++;
3251     }
3252     te->wday = wday;
3253     break;
3254   }
3255 
3256   ufdbFree( (void*) element );
3257 }
3258 
3259 
3260 /*
3261  * lookup a ufdbTime element by name.
3262  */
sgTimeFindName(const char * name)3263 static struct ufdbTime * sgTimeFindName(
3264    const char * name )
3265 {
3266    struct ufdbTime * p;
3267 
3268    for (p = ufdbNewGV.timeList;  p != NULL;  p = p->next)
3269    {
3270       if (strcmp(name,p->name) == 0)
3271          return p;
3272    }
3273    return NULL;
3274 }
3275 
3276 
3277 /*
3278  * sgTimeCmp - Time array sort function.
3279  */
sgTimeCmp(const void * a,const void * b)3280 static int sgTimeCmp( const void * a, const void * b )
3281 {
3282    const int * aa = (const int *) a;
3283    const int * bb = (const int *) b;
3284 
3285    return *aa - *bb;
3286 }
3287 
3288 
3289 /*
3290  * _getSortedTimeElementTimes - produce a sorted array of time element times
3291  */
_getSortedTimeElementTimes(void)3292 static void _getSortedTimeElementTimes( void )           // uses ufdbGV
3293 {
3294    struct ufdbTime *    p;
3295    struct TimeElement * te;
3296    int                  i, j;
3297    int                  totalNumElems;
3298 
3299    if (ufdbGV.debug > 1)
3300       ufdbLogMessage( "_getSortedTimeElementEvents" );
3301 
3302    if (ufdbGV.timeList == NULL)
3303       return;
3304 
3305    /* find total number of time elements */
3306    totalNumElems = 0;
3307    for (p = ufdbGV.timeList;  p != NULL;  p = p->next)
3308       for (te = p->element;  te != NULL;  te = te->next)
3309          totalNumElems++;
3310 
3311    TimeElementsEvents = (int *) ufdbCalloc( totalNumElems * 2 , sizeof(int) );
3312 
3313    i = 0;
3314    for (p = ufdbGV.timeList;  p != NULL;  p = p->next)
3315    {
3316       for (te = p->element;  te != NULL;  te = te->next)
3317       {
3318          TimeElementsEvents[i++] = te->from == 0 ? 1440 : te->from;
3319          TimeElementsEvents[i++] = te->to == 0   ? 1440 : te->to;
3320       }
3321    }
3322 
3323    qsort( TimeElementsEvents, totalNumElems * 2, sizeof(int), sgTimeCmp );
3324 
3325    if (ufdbGV.debug > 1)
3326       ufdbLogMessage( "   _getSortedTimeElementEvents: after qsort" );
3327 
3328    /* remove identical time elements */
3329    for (i=1,j=1;  i < totalNumElems * 2;  i++)
3330    {
3331       if (TimeElementsEvents[i] > TimeElementsEvents[i-1])
3332       {
3333 	 TimeElementsEvents[j] = TimeElementsEvents[i];
3334          j++;
3335       }
3336    }
3337 
3338    numTimeElements = j;			/* #unique time elements */
3339 }
3340 
3341 
3342 /*
3343  * _TimeEvaluateElements - evaluate all elements if they match the current time/date and mark them active.
3344  */
_TimeEvaluateElements(struct tm * tm_now,time_t now)3345 static void _TimeEvaluateElements(              // uses ufdbGV
3346   struct tm * tm_now,
3347   time_t      now )
3348 {
3349   struct ufdbTime *    tlist;
3350   struct TimeElement * te;
3351   int                  min;
3352 
3353   if (ufdbGV.debug > 1)
3354      ufdbLogMessage( "      _TimeEvaluateElements" );
3355 
3356   for (tlist = ufdbGV.timeList;  tlist != NULL;  tlist = tlist->next)
3357   {
3358     tlist->active = 0;
3359     for (te = tlist->element;  te != NULL;  te = te->next)
3360     {
3361       if (te->wday != 0) 			/* check wday */
3362       {
3363 	if (((1 << tm_now->tm_wday) & te->wday) != 0)
3364 	{
3365 	  min = (tm_now->tm_hour * 60) + tm_now->tm_min;
3366 	  if (min >= te->from  &&  min < te->to)
3367 	  {
3368 	    tlist->active = 1;
3369 	    break;
3370 	  }
3371 	}
3372       }
3373       else if (te->fromdate != 0)		/* check date */
3374       {
3375 	if (now >= te->fromdate  &&  now <= te->todate)
3376 	{
3377 	  min = (tm_now->tm_hour * 60) + tm_now->tm_min;
3378 	  if (min >= te->from  &&  min < te->to)
3379 	  {
3380 	    tlist->active = 1;
3381 	    break;
3382 	  }
3383 	}
3384       }
3385       else					/* check crondate */
3386       {
3387 	if (te->y == -1  ||  te->y == (tm_now->tm_year + 1900))
3388 	{
3389 	  if (te->m == -1  ||  te->m == (tm_now->tm_mon + 1))
3390 	  {
3391 	    if (te->d == -1  ||  te->d == (tm_now->tm_mday))
3392 	    {
3393 	      min = (tm_now->tm_hour * 60) + tm_now->tm_min;
3394 	      if (min >= te->from  &&  min < te->to)
3395 	      {
3396 		tlist->active = 1;
3397 		break;
3398 	      }
3399 	    }
3400 	  }
3401 	}
3402       }
3403     }
3404     if (ufdbGV.debug > 1)
3405        ufdbLogMessage( "      _TimeEvaluateElements: time %s is %sactive", tlist->name, tlist->active?"":"not " );
3406   }
3407 }
3408 
3409 
3410 /*
3411  * _TimeSetAclSrcDestRew - mark all acl/source/dest/rew (in)active.
3412  */
_TimeSetAclSrcDestRew(void)3413 static void _TimeSetAclSrcDestRew( void )               // uses ufdbGV
3414 {
3415   struct Acl *         acl;
3416   struct Category *    cat;
3417   struct Source *      s;
3418   struct sgRewrite *   rew;
3419   int                  a;
3420 
3421   if (ufdbGV.debug > 1)
3422      ufdbLogMessage( "   _TimeSetAclSrcDestRew" );
3423 
3424   for (acl = ufdbGV.aclList;  acl != NULL;  acl = acl->next)
3425   {
3426     if (acl->time != NULL  &&  acl->within != UFDB_ACL_ELSE)
3427     {
3428       /* Be careful here: we are multithreaded and other threads use the value
3429        * of acl->active at the same time.
3430        */
3431       a = acl->time->active;
3432       if (acl->within == UFDB_ACL_OUTSIDE)
3433 	a = !a;
3434       if (acl->next != NULL  &&  acl->next->within == UFDB_ACL_ELSE)
3435 	acl->next->active = !a;
3436       acl->active = a;
3437     }
3438 #if 0
3439     if (acl->pass == NULL)
3440        acl->active = 0;			/* it can have a 'continue' so do not make it inactive */
3441 #endif
3442     if (ufdbGV.debug > 1)
3443        ufdbLogMessage( "      _TimeSetAclSrcDestRew: acl %s %s is %sactive", acl->name,
3444 		       acl->within==UFDB_ACL_ELSE ? "ELSE" :
3445 			  acl->within==UFDB_ACL_WITHIN ? "WITHIN" :
3446 			     acl->within==UFDB_ACL_OUTSIDE ? "OUTSIDE" : "",
3447 		       acl->active?"":"not " );
3448   }
3449 
3450   for (cat = ufdbGV.catList;  cat != NULL;  cat = cat->next)
3451   {
3452     if (cat->time != NULL)
3453     {
3454       cat->active = cat->time->active;
3455       if (cat->within == UFDB_ACL_OUTSIDE)
3456 	cat->active = !cat->active;
3457     }
3458     if (ufdbGV.debug > 1)
3459        ufdbLogMessage( "      _TimeSetAclSrcDestRew: category %s is %sactive", cat->name, cat->active?"":"not " );
3460   }
3461 
3462   for (s = ufdbGV.sourceList;  s != NULL;  s = s->next)
3463   {
3464     if (s->time != NULL)
3465     {
3466       s->active = s->time->active;
3467       if (s->within == UFDB_ACL_OUTSIDE)
3468 	s->active = !s->active;
3469     }
3470     if (ufdbGV.debug > 1)
3471        ufdbLogMessage( "      _TimeSetAclSrcDestRew: source %s is %sactive", s->name, s->active?"":"not " );
3472   }
3473 
3474   for (rew = ufdbGV.rewrite; rew != NULL; rew = rew->next)
3475   {
3476     if (rew->time != NULL)
3477     {
3478       rew->active = rew->time->active;
3479       if (rew->within == UFDB_ACL_OUTSIDE)
3480 	 rew->active = !rew->active;
3481     }
3482     if (ufdbGV.debug > 1)
3483        ufdbLogMessage( "      _TimeSetAclSrcDestRew: rewrite %s is %sactive", rew->name, rew->active?"":"not " );
3484   }
3485 }
3486 
3487 
sgAlarm(int s)3488 static void sgAlarm( int s )
3489 {
3490    if (s) { ; }         // prevent compiler warning
3491    // do nothing; there is a thread that waits for a SIGALRM which calls ufdbHandleAlarmForTimeEvents()
3492 }
3493 
3494 
3495 /*
3496  *  ufdbHandleAlarmForTimeEvents - the alarm for the next time event went off so
3497  *                                 recalculate the time-dependent active state.
3498  */
ufdbHandleAlarmForTimeEvents(int why)3499 void ufdbHandleAlarmForTimeEvents(              // uses ufdbGV
3500    int        why )
3501 {
3502    time_t     now;
3503    struct tm  tm_now;
3504    int        m;
3505    int        tindex;
3506    int        lastval;
3507 
3508    if (ufdbGV.debug)
3509       ufdbLogMessage( "ufdbHandleAlarmForTimeEvents( why=%s )", why==UFDB_PARAM_INIT ? "init" : "alarm" );
3510 
3511    if (why == UFDB_PARAM_INIT)
3512       ufdbLogMessage( "time definitions are used; evaluating current ACLs" );
3513    else
3514    {
3515       ufdbLogMessage( "alarm went off to recalculate time ACLs" );
3516       if (ufdbGV.terminating)
3517       {
3518 	 ufdbLogMessage( "This alarm is ignored because ufdbguardd is exiting" );
3519 	 return;
3520       }
3521       if (ufdbGV.reconfig)
3522       {
3523 	 ufdbLogMessage( "This alarm is ignored because the configuration is being reloaded"
3524 	                 " and a new alarm is set to go off in 15 seconds" );
3525 	 alarm( 15 );
3526 	 return;
3527       }
3528    }
3529 
3530    if (ufdbGV.timeList == NULL)
3531    {
3532       return;
3533    }
3534 
3535    // NOTE: _getSortedTimeElementTimes() mallocs TimeElementsEvents[] and this function frees it.
3536    _getSortedTimeElementTimes();
3537 
3538    now = UFDBtime() + 30;
3539    localtime_r( &now, &tm_now );
3540    m = (tm_now.tm_hour * 60) + tm_now.tm_min;
3541 
3542    lastval = 0;
3543    for (tindex = 0;  tindex < numTimeElements;  tindex++)
3544    {
3545 #if UFDB_TIME_DEBUG
3546       if (ufdbGV.debug > 1)
3547          ufdbLogMessage( "   TimeElementsEvents[%d] = %d", tindex, TimeElementsEvents[tindex] );
3548 #endif
3549       lastval = TimeElementsEvents[tindex];
3550       if (TimeElementsEvents[tindex] >= m)
3551          break;
3552    }
3553 
3554    if (ufdbGV.debug > 1)
3555       ufdbLogMessage( "   ufdbHandleAlarmForTimeEvents: m = %d  tindex = %d, lastval = %d", m, tindex, lastval );
3556 
3557    if (lastval < m)
3558       m = (((1440 - m) + TimeElementsEvents[0]) * 60) - tm_now.tm_sec;
3559    else
3560       m = ((lastval - m) * 60) - tm_now.tm_sec;
3561 
3562    if (m <= 0)
3563       m = 30;
3564 
3565    _TimeEvaluateElements( &tm_now, now );
3566    _TimeSetAclSrcDestRew();
3567 
3568    ufdbFree( (void*) TimeElementsEvents );
3569    TimeElementsEvents = NULL;
3570 
3571    ufdbLogMessage( "next alarm is in %d seconds", (unsigned int) m );
3572    ufdbSetSignalHandler( SIGALRM, sgAlarm );
3573    (void) alarm( (unsigned int) m );
3574 }
3575 
3576 
3577 /*
3578  * sgTimeElementClone - copy a time specification.
3579  */
sgTimeElementClone(void)3580 static void sgTimeElementClone( void )
3581 {
3582   struct TimeElement * te;
3583   struct TimeElement * tmp;
3584 
3585   te = ufdbNewGV.lastTimeElement;
3586   if (te == NULL)
3587   {
3588     ufdbLogFatalError( "No previous TimeElement in sgTimeElementClone !" );
3589     return;
3590   }
3591   else
3592   {
3593     sgTimeElementInit();
3594     ufdbNewGV.lastTimeElement->wday = te->wday;
3595     ufdbNewGV.lastTimeElement->from = te->from;
3596     ufdbNewGV.lastTimeElement->to = te->to;
3597     ufdbNewGV.lastTimeElement->y = te->y;
3598     ufdbNewGV.lastTimeElement->m = te->m;
3599     ufdbNewGV.lastTimeElement->d = te->d;
3600     ufdbNewGV.lastTimeElement->fromdate = te->fromdate;
3601     ufdbNewGV.lastTimeElement->todate = te->todate;
3602     tmp = ufdbNewGV.lastTimeElement;
3603     ufdbNewGV.lastTimeElement = te;
3604     sgTimeElementEnd();
3605     ufdbNewGV.lastTimeElement = tmp;
3606   }
3607 }
3608 
3609 
3610 /*
3611  * IP functions
3612  */
3613 
sgIpv4Last(struct Source * s)3614 static struct Ipv4 * sgIpv4Last(
3615    struct Source * s )
3616 {
3617    struct Ipv4 * ipv4;
3618    struct Ipv4 * last;
3619 
3620    last = NULL;
3621    for (ipv4 = s->ipv4;  ipv4 != NULL;  ipv4 = ipv4->next)
3622       last = ipv4;
3623 
3624    return last;
3625 }
3626 
3627 
sgIpv4(const char * addr,int type,const char * file,int lineno)3628 static void sgIpv4(
3629    const char *   addr,
3630    int            type,
3631    const char *   file,
3632    int            lineno )
3633 {
3634    struct Ipv4 *  ipv4;
3635    char *         addr2;
3636    char *         cidr;
3637    char *         end;
3638    unsigned int   octet;
3639 
3640 #if 1
3641    if (ufdbGV.debug> 1)
3642       ufdbLogMessage( "   sgIpv4( %s %d %s %d )", addr, type, file, lineno );
3643 #endif
3644 
3645    switch (type)
3646    {
3647    case SG_IPTYPE_HOST:
3648 	 if (ufdbNewGV.lastSource->ipv4hosts == NULL)
3649 	    ufdbNewGV.lastSource->ipv4hosts = UFDBmemDBinit();
3650 	 UFDBmemDBinsert( ufdbNewGV.lastSource->ipv4hosts, addr, NULL );
3651   	 break;
3652 
3653    case SG_IPTYPE_RANGE:
3654 	 addr2 = strchr( addr, '-' );
3655 	 *addr2 = '\0';
3656 	 end = addr2;
3657 	 while (*(end-1) == ' '  ||  *(end-1) == '\t')
3658 	 {
3659 	    *(end-1) = '\0';
3660 	    end--;
3661 	 }
3662 	 addr2++;
3663 	 while (*addr2 == ' ' || *addr2 == '\t')
3664 	    addr2++;
3665 
3666          ipv4 = (struct Ipv4 *) ufdbMalloc( sizeof(struct Ipv4) );
3667 	 ipv4->type = SG_IPTYPE_RANGE;
3668          ipv4->net_is_set = 1;
3669 	 ipv4->next = NULL;
3670 	 if (ufdbNewGV.lastSource->ipv4 == NULL)
3671 	    ufdbNewGV.lastSource->ipv4 = ipv4;
3672 	 else
3673 	    sgIpv4Last( ufdbNewGV.lastSource )->next = ipv4;
3674 
3675 	 if (sgConvDot(addr,&ipv4->net) == NULL)
3676 	 {
3677 	    ufdbLogFatalError( "IPv4 address error in %s line %d: %s - %s", file, lineno, addr, addr2 );
3678 	    ipv4->net = 0;
3679 	    ipv4->net_is_set = 0;
3680 	 }
3681 	 if (sgConvDot(addr2,&ipv4->mask) == NULL)
3682 	 {
3683 	    ufdbLogFatalError( "IPv4 address error in %s line %d: %s - %s", file, lineno, addr, addr2 );
3684 	    ipv4->mask = 0;
3685 	    ipv4->net_is_set = 0;
3686 	 }
3687 
3688 	 if ((unsigned int) ipv4->net > (unsigned int) ipv4->mask)
3689 	     ufdbLogFatalError( "IPv4 range error in %s line %d: %s - %s", file, lineno, addr, addr2 );
3690   	 break;
3691 
3692    case SG_IPTYPE_CLASS:
3693 	 addr2 = strchr( addr, '/' );
3694 	 *addr2 = '\0';
3695 	 addr2++;
3696          ipv4 = (struct Ipv4 *) ufdbMalloc( sizeof(struct Ipv4) );
3697 	 ipv4->type = SG_IPTYPE_CLASS;
3698          ipv4->net_is_set = 1;
3699 	 ipv4->next = NULL;
3700 	 if (ufdbNewGV.lastSource->ipv4 == NULL)
3701 	    ufdbNewGV.lastSource->ipv4 = ipv4;
3702 	 else
3703 	    sgIpv4Last( ufdbNewGV.lastSource )->next = ipv4;
3704 
3705 	 if (sgConvDot(addr,&ipv4->net) == NULL)
3706 	 {
3707 	    ufdbLogFatalError( "IPv4 address error in %s line %d: %s/%s", file, lineno, addr, addr2 );
3708 	    ipv4->net = 0;
3709 	    ipv4->net_is_set = 0;
3710 	 }
3711 	 if (sgConvDot(addr2,&ipv4->mask) == NULL)
3712 	 {
3713 	    ufdbLogFatalError( "IPv4 address error in %s line %d: %s/%s", file, lineno, addr, addr2 );
3714 	    ipv4->mask = 0;
3715 	 }
3716   	 break;
3717 
3718    case SG_IPTYPE_CIDR:
3719 	 cidr = strchr( addr, '/' );
3720 	 *cidr = '\0';
3721 	 cidr++;
3722 	 octet = (unsigned long) atoi( cidr );
3723 	 if (octet > 32)
3724 	 {
3725 	    ufdbLogFatalError( "IPv4 address CIDR out of range in %s line %d: %s/%s",
3726 	                       file, lineno, addr, cidr );
3727 	    octet = 32;
3728 	 }
3729          ipv4 = (struct Ipv4 *) ufdbMalloc( sizeof(struct Ipv4) );
3730 	 ipv4->type = SG_IPTYPE_CIDR;
3731          ipv4->net_is_set = 1;
3732 	 ipv4->next = NULL;
3733 	 if (ufdbNewGV.lastSource->ipv4 == NULL)
3734 	    ufdbNewGV.lastSource->ipv4 = ipv4;
3735 	 else
3736 	    sgIpv4Last( ufdbNewGV.lastSource )->next = ipv4;
3737 
3738 	 if (sgConvDot(addr,&ipv4->net) == NULL)
3739 	 {
3740 	    ufdbLogFatalError( "IPv4 address error in %s line %d: %s/%s", file, lineno, addr, cidr );
3741 	    ipv4->net = 0;
3742 	    ipv4->net_is_set = 0;
3743 	 }
3744 	 if (octet == 32)
3745 	    ipv4->mask = 0xffffffff;
3746 	 else
3747 	    ipv4->mask = 0xffffffff ^ (0xffffffff >> octet);
3748 	 ipv4->net = ipv4->net & ipv4->mask;
3749   	 break;
3750    }
3751 }
3752 
3753 
sgIpv6Last(struct Source * s)3754 static struct Ipv6 * sgIpv6Last(
3755    struct Source * s )
3756 {
3757    struct Ipv6 * ipv6;
3758    struct Ipv6 * last;
3759 
3760    last = NULL;
3761    for (ipv6 = s->ipv6;  ipv6 != NULL;  ipv6 = ipv6->next)
3762       last = ipv6;
3763 
3764    return last;
3765 }
3766 
3767 
3768 
sgIpv6(const char * addr,int type,const char * file,int line)3769 static void sgIpv6(
3770    const char * addr,
3771    int          type,
3772    const char * file,
3773    int          line  )
3774 {
3775 #if 1
3776    if (ufdbGV.debug > 1)
3777       ufdbLogMessage( "   sgIpv6( %s %d  %s %d/%d )", addr, type, file, line, lineno );
3778 #endif
3779 
3780    if (type == SG_IPV6TYPE_HOST)
3781    {
3782       struct in6_addr dummy;
3783       if (!sgValidateIPv6( addr, &dummy ))
3784       {
3785          ufdbLogFatalError( "incorrect IPv6 address \"%s\" in %s line %d", addr, file, lineno );
3786       }
3787       else
3788       {
3789          if (ufdbNewGV.lastSource->ipv6hosts == NULL)
3790 	    ufdbNewGV.lastSource->ipv6hosts = UFDBmemDBinit();
3791 	 UFDBmemDBinsert( ufdbNewGV.lastSource->ipv6hosts, addr, NULL );
3792       }
3793    }
3794    else if (type == SG_IPV6TYPE_CIDR)
3795    {
3796       char * s = (char*) strchr( addr, '/' );
3797       if (s == NULL)
3798       {
3799          ufdbLogFatalError( "IPv6 net has no '/' in \"%s\" in %s line %d", addr, file, lineno );
3800       }
3801       else
3802       {
3803 	 struct Ipv6 *  ipv6;
3804 
3805 	 ipv6 = (struct Ipv6 *) ufdbMalloc( sizeof(struct Ipv6) );
3806 	 ipv6->type = SG_IPV6TYPE_CIDR;
3807 	 ipv6->next = NULL;
3808 	 if (ufdbNewGV.lastSource->ipv6 == NULL)
3809 	    ufdbNewGV.lastSource->ipv6 = ipv6;
3810 	 else
3811 	    sgIpv6Last( ufdbNewGV.lastSource )->next = ipv6;
3812 
3813          *s = '\0';
3814          ipv6->cidr = (unsigned long) atoi( s+1 );
3815          if (ipv6->cidr < 1  ||  ipv6->cidr > 128)
3816          {
3817             ufdbLogFatalError( "IPv6 address CIDR out of range \"%s\" in %s line %d", addr, file, lineno );
3818             ipv6->cidr = 128;
3819          }
3820          if (!sgValidateIPv6( addr, &ipv6->ipv6 ))
3821          {
3822             ufdbLogFatalError( "incorrect IPv6 address \"%s\" in %s line %d", addr, file, lineno );
3823          }
3824          *s = '/';
3825       }
3826    }
3827    else
3828    {
3829       ufdbLogFatalError( "sgIpv6 called with unsupported type %d in %s line %d: %s", type, file, lineno, addr );
3830    }
3831 }
3832 
3833 
3834 /*
3835  * ACL functions
3836  */
3837 
ufdbAcl(const char * name,const char * value,int within)3838 static void ufdbAcl(
3839    const char *    name,
3840    const char *    value,
3841    int             within )
3842 {
3843    struct Acl *    acl;
3844    struct Source * source;
3845 
3846 #if UFDB_DEBUG
3847    ufdbLogMessage( "ufdbAcl name=\"%s\" value=\"%s\" within=%d line=%d",
3848                    name==NULL?"NULL":name,  value==NULL?"NULL":value, within, lineno );
3849 #endif
3850 
3851    if (ufdbNewGV.aclList != NULL)
3852    {
3853       if (ufdbAclFindByName(&ufdbNewGV,name) != NULL)
3854       {
3855 	 ufdbLogFatalError( "line %d: ACL \"%s\" is already defined in configuration file %s",
3856 			    lineno, name, ufdbNewGV.configFile );
3857       }
3858    }
3859 
3860    if (within == UFDB_ACL_ELSE)
3861    {
3862       if (ufdbNewGV.lastAcl == NULL)
3863       {
3864          ufdbLogFatalError( "line %d: ACL \"else\" has no parent ACL", lineno );
3865          return;
3866       }
3867       if (name == NULL)
3868          name = ufdbStrdup( ufdbNewGV.lastAcl->name );
3869    }
3870 
3871    acl = (struct Acl *) ufdbMalloc( sizeof(struct Acl) );
3872 
3873    source = NULL;
3874    if (strcmp(name,"default") == 0)
3875    {
3876       ufdbNewGV.defaultAcl = acl;
3877    }
3878    else
3879    {
3880       if ((source = defSourceFindName(ufdbNewGV.sourceList,name)) == NULL)
3881       {
3882          ufdbLogFatalError( "line %d: ACL source \"%s\" is not defined in configuration file %s",
3883                             lineno, name, ufdbNewGV.configFile );
3884          if (ufdbNewGV.fastRefresh  &&  ufdbGV.fastRefresh  &&
3885              defSourceFindName(ufdbGV.sourceList,name) != NULL)
3886             ufdbLogError( "But ACL source \"%s\" is defined in the old configuration  *****", name );
3887          if (ufdbGV.debug > 1)
3888             UFDBlogConfig( &ufdbGV );
3889          ufdbFree( (void*) name );
3890          ufdbFree( (void*) acl );
3891          return;
3892       }
3893    }
3894 
3895    acl->name = name;
3896    acl->active = within == UFDB_ACL_ELSE ? 0 : 1;
3897    acl->source = source;
3898    acl->pass = NULL;
3899    acl->implicitPass = NULL;
3900    acl->hasTerminatorNone = 0;
3901    acl->rewriteDefault = 1;
3902    acl->rewrite = NULL;
3903    acl->redirect = NULL;
3904    acl->time = NULL;
3905    acl->within = within;
3906    acl->next = NULL;
3907 
3908    if (value != NULL)
3909    {
3910       struct ufdbTime * t;
3911       if ((t = sgTimeFindName(value)) == NULL)
3912       {
3913          ufdbLogFatalError( "line %d: ACL time %s is not defined in configuration file %s",
3914                             lineno, value, ufdbNewGV.configFile );
3915          return;
3916       }
3917       acl->time = t;
3918    }
3919 
3920    if (ufdbNewGV.aclList == NULL)
3921    {
3922       ufdbNewGV.aclList = acl;
3923       ufdbNewGV.lastAcl = acl;
3924    }
3925    else
3926    {
3927       ufdbNewGV.lastAcl->next = acl;
3928       ufdbNewGV.lastAcl = acl;
3929    }
3930 }
3931 
3932 
ufdbAclSetValue(const char * what,const char * value,int allowed)3933 static void ufdbAclSetValue(
3934   const char *         what,
3935   const char *         value,
3936   int                  allowed )
3937 {
3938   struct Category *    cat = NULL;
3939   struct AclCategory * aclcat;
3940   int                  type;
3941 
3942 #if UFDB_DEBUG
3943    ufdbLogMessage( "ufdbAclSetValue %s %s%s", what, allowed ? "" : "!", value );
3944 #endif
3945 
3946   if (ufdbNewGV.lastAcl == NULL)
3947   {
3948      ufdbLogError( "error in configuration file on line %d: "
3949                    "cannot set value for \"%s\" because there is no defined ACL",
3950                    lineno, what );
3951      ufdbFree( (void*) value );
3952      return;
3953   }
3954 
3955   type = ACL_TYPE_TERMINATOR;
3956 
3957   if (strcmp(what,"pass") == 0)
3958   {
3959     if (strcmp(value,"any")==0 || strcmp(value,"all")==0)
3960     {
3961        if (!allowed)
3962           ufdbLogFatalError( "error in configuration file on line %d: do not use '!any' or '!all'.  "
3963                              "Use 'none' instead.", lineno );
3964        allowed = 1;
3965     }
3966     else if (strcmp(value,"none") == 0)
3967     {
3968        if (!allowed)
3969           ufdbLogFatalError( "error in configuration file on line %d: do not use '!none'.  "
3970                              "Use 'any' instead.", lineno );
3971        allowed = 0;
3972     }
3973     else if (strcmp(value,"in-addr") == 0) {
3974       type = ACL_TYPE_INADDR;
3975     }
3976     else
3977     {
3978       if ((cat = ufdbCategoryFindByName(&ufdbNewGV,value)) == NULL)
3979       {
3980 	 ufdbLogFatalError( "ACL category \"%s\" (line %d) is not defined in configuration file %s",
3981 			    value, lineno, ufdbNewGV.configFile );
3982          if (ufdbNewGV.fastRefresh  &&  ufdbGV.fastRefresh  &&
3983              ufdbCategoryFindByName(&ufdbGV,value) != NULL)
3984             ufdbLogError( "But category \"%s\" is defined in the old configuration  *****", value );
3985          if (ufdbGV.debug > 1)
3986             UFDBlogConfig( &ufdbGV );
3987 	 ufdbFree( (void*) value );
3988          return;
3989       }
3990       type = ACL_TYPE_DEFAULT;
3991     }
3992 
3993     aclcat = (struct AclCategory *) ufdbMallocAligned( UFDB_CACHELINE_SIZE, sizeof(struct AclCategory) );
3994     aclcat->name = value;
3995     aclcat->cat = cat;
3996     aclcat->access = allowed;
3997     aclcat->type = type;
3998     aclcat->next = NULL;
3999     aclcat->nblocks = 0;
4000     aclcat->nmatches = 0;
4001 
4002     if (ufdbNewGV.lastAcl->pass == NULL) {
4003       ufdbNewGV.lastAcl->pass = aclcat;
4004     } else {
4005       ufdbNewGV.lastAclCategory->next = aclcat;
4006     }
4007     ufdbNewGV.lastAclCategory = aclcat;
4008   }
4009   else if (strcmp(what,"redirect") == 0)
4010   {
4011     if (strcmp(value,"default") != 0)
4012     {
4013       ufdbNewGV.lastAcl->redirect = value;
4014     }
4015     else
4016     {
4017       ufdbNewGV.lastAcl->redirect = NULL;
4018       ufdbFree( (void*) value );
4019     }
4020   }
4021   else if (strcmp(what,"rewrite") == 0)
4022   {
4023     if (strcmp(value,"none") == 0)
4024     {
4025       ufdbNewGV.lastAcl->rewriteDefault = 0;
4026       ufdbNewGV.lastAcl->rewrite = NULL;
4027     }
4028     else
4029     {
4030       struct sgRewrite * rewrite;
4031 
4032       if ((rewrite = sgRewriteFindName(value)) == NULL)
4033       {
4034 	ufdbLogFatalError( "rewrite %s is not defined in configuration file %s",
4035 			   value, ufdbNewGV.configFile );
4036       }
4037       ufdbNewGV.lastAcl->rewriteDefault = 0;
4038       ufdbNewGV.lastAcl->rewrite = rewrite;
4039     }
4040     ufdbFree( (void*) value );
4041   }
4042 }
4043 
4044 
ufdbAclFindByName(struct ufdbGV * gv,const char * name)4045 struct Acl * ufdbAclFindByName(
4046   struct ufdbGV * gv,
4047   const char *    name )
4048 {
4049   struct Acl * p;
4050 
4051   if (name == NULL)
4052      return NULL;
4053 
4054   for (p = gv->aclList;  p != NULL;  p = p->next)
4055   {
4056     if (strcmp(name,p->name) == 0)
4057       return p;
4058   }
4059 
4060   return NULL;
4061 }
4062 
4063 
logConfig(void)4064 static void logConfig( void )
4065 {
4066    int    lno;
4067    FILE * fin;
4068    char   line[32678];
4069 
4070    if (ufdbNewGV.configLogged)
4071       return;
4072    ufdbNewGV.configLogged = 1;
4073 
4074    fin = fopen( ufdbNewGV.configFile, "r" );
4075    if (fin == NULL)
4076       return;
4077 
4078    ufdbLogMessage( "======== literal content of config file ========" );
4079    lno = 1;
4080    while (UFDBfgetsNoNL(line,sizeof(line)-1,fin) != NULL)
4081    {
4082       ufdbLogMessage( "%04d: %s", lno, line );
4083       lno++;
4084    }
4085    ufdbLogMessage( "======== end of literal content ========" );
4086    fclose( fin );
4087 }
4088 
4089 
yyerror(const char * s)4090 void yyerror( const char * s )
4091 {
4092    ufdbLogFatalError( "line %d: %s in configuration file %s", lineno, s, ufdbNewGV.configFile );
4093 }
4094 
4095 
yywrap()4096 int yywrap()
4097 {
4098   return 1;
4099 }
4100 
4101