1 /*
2   spamdyke -- a filter for stopping spam at connection time.
3   Copyright (C) 2015 Sam Clippinger (samc (at) silence (dot) org)
4 
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License version 2 as
7   published by the Free Software Foundation.
8 
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13 
14   You should have received a copy of the GNU General Public License
15   along with this program; if not, write to the Free Software
16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <syslog.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #include <ctype.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <fnmatch.h>
28 #include <unistd.h>
29 #include "spamdyke.h"
30 #include "environment.h"
31 #include "log.h"
32 #include "configuration.h"
33 #include "search_fs.h"
34 
35 /*
36  * strlen_target_path may be -1 if target_path is nul-terminated.
37  *
38  * Return value:
39  *   destination_buf
40  */
canonicalize_path(char * destination_buf,int maxlen_destination_buf,char * target_path,int strlen_target_path)41 char *canonicalize_path(char *destination_buf, int maxlen_destination_buf, char *target_path, int strlen_target_path)
42   {
43   char *filename_alphabet = ALPHABET_FILENAME;
44   int i;
45 
46   i = 0;
47 
48   if ((destination_buf != NULL) &&
49       (maxlen_destination_buf > 0))
50     {
51     if (target_path != NULL)
52       for (i = 0; ((i < maxlen_destination_buf) && (((strlen_target_path >= 0) && (i < strlen_target_path)) || ((strlen_target_path == -1) && (target_path[i] != '\0')))); i++)
53         destination_buf[i] = (strchr(filename_alphabet, target_path[i]) != NULL) ? tolower((int)target_path[i]) : REPLACEMENT_FILENAME;
54 
55     destination_buf[i] = '\0';
56     }
57 
58   return(destination_buf);
59   }
60 
61 /*
62  * Return value:
63  *   NOT FOUND: 0
64  *   FOUND: 1
65  */
find_path(struct filter_settings * current_settings,char * filename,char * envp[],char * return_filename,int size_return_filename)66 int find_path(struct filter_settings *current_settings, char *filename, char *envp[], char *return_filename, int size_return_filename)
67   {
68   int return_value;
69   int strlen_filename;
70   char *path;
71   char *tmp_start;
72   char *tmp_end;
73   char new_filename[MAX_BUF + 1];
74   struct stat tmp_stat;
75 
76   return_value = 0;
77 
78   if ((filename != NULL) &&
79       (return_filename != NULL) &&
80       (size_return_filename > 0))
81     {
82     if (stat(filename, &tmp_stat) == 0)
83       {
84       strlen_filename = MINVAL(size_return_filename, strlen(filename));
85       memcpy(return_filename, filename, sizeof(char) * strlen_filename);
86       return_filename[strlen_filename] = '\0';
87 
88       return_value = 1;
89       }
90     else if (strchr(filename, DIR_DELIMITER) == NULL)
91       {
92       if (((path = find_environment_variable(current_settings, envp, ENVIRONMENT_PATH, STRLEN(ENVIRONMENT_PATH), NULL)) == NULL) ||
93           (path[0] == '\0'))
94         {
95         SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_PATH_DEFAULT, DEFAULT_PATH);
96         path = DEFAULT_PATH;
97         }
98 
99       tmp_start = path;
100       tmp_end = NULL;
101       while (tmp_start != NULL)
102         {
103         if ((tmp_end = strchr(tmp_start, ENVIRONMENT_DELIMITER)) != NULL)
104           {
105           strlen_filename = SNPRINTF(new_filename, MAX_BUF, "%.*s" DIR_DELIMITER_STR "%s", (int)(tmp_end - tmp_start), tmp_start, filename);
106           tmp_start = tmp_end + 1;
107           }
108         else
109           {
110           strlen_filename = SNPRINTF(new_filename, MAX_BUF, "%s" DIR_DELIMITER_STR "%s", tmp_start, filename);
111           tmp_start = NULL;
112           }
113 
114         if (stat(new_filename, &tmp_stat) == 0)
115           {
116           if (strlen_filename > size_return_filename)
117             strlen_filename = size_return_filename;
118 
119           memcpy(return_filename, new_filename, sizeof(char) * strlen_filename);
120           return_filename[strlen_filename] = '\0';
121 
122           return_value = 1;
123           break;
124           }
125         }
126       }
127     }
128 
129   return(return_value);
130   }
131 
132 /*
133  * When passed an entire command line string (e.g. from a .qmail file), this
134  * function finds just the command itself, allowing for escaping characters
135  * and quoting.
136  *
137  * RETURNS:
138  *   characters copied to return_text
139  */
find_command(char * input_text,char * return_text,int size_return_text)140 int find_command(char *input_text, char *return_text, int size_return_text)
141   {
142   int return_value;
143   int i;
144   int escape;
145   int inside_quote;
146 
147   return_value = 0;
148   escape = 0;
149   inside_quote = 0;
150 
151   if ((input_text != NULL) &&
152       (return_text != NULL))
153     {
154     for (i = 0; input_text[i] != '\0'; i++)
155       if (!isspace((int)input_text[i]))
156         break;
157 
158     for (; (input_text[i] != '\0') && (return_value < size_return_text); i++)
159       if (isspace((int)input_text[i]) &&
160           !inside_quote &&
161           !escape)
162         break;
163       else if (escape)
164         {
165         return_text[return_value] = input_text[i];
166         return_value++;
167         escape = 0;
168         }
169       else if (input_text[i] == PATH_ESCAPE_CHAR)
170         escape = 1;
171       else if (input_text[i] == PATH_QUOTE_CHAR)
172         inside_quote = !inside_quote;
173       else
174         {
175         return_text[return_value] = input_text[i];
176         return_value++;
177         }
178 
179     return_text[return_value] = '\0';
180     }
181 
182   return(return_value);
183   }
184 
185 /*
186  * Return value:
187  *   NULL: no match
188  *   pointer to match within haystack: match
189  */
find_case_insensitive_needle(char * haystack,char * needle)190 char *find_case_insensitive_needle(char *haystack, char *needle)
191   {
192   char *return_value;
193   int i;
194   char tmp_buf[MAX_BUF + 1];
195 
196   if ((haystack != NULL) &&
197       (needle != NULL))
198     {
199     for (i = 0; (i < MAX_BUF) && (needle[i] != '\0'); i++)
200       tmp_buf[i] = tolower((int)needle[i]);
201     tmp_buf[i] = '\0';
202 
203     return_value = strstr(haystack, tmp_buf);
204     }
205   else
206     return_value = NULL;
207 
208   return(return_value);
209   }
210 
211 /*
212  * EXPECTS:
213  *   target_string = string content to search for, must not be NULL or zero length
214  *   strlen_target_string = length of target_string
215  *   target_entry = string content to search within, must not be NULL
216  *   strlen_target_entry = length of target_entry
217  *   start_wildcard = wildcard character that, if present as the first character in target_string, allows target_string to match in the middle of target_entry.  \0 disables this feature.
218  *   start_wildcard_matches = if start_wildcard matches the first character in target_string, a match is not found unless the character in target_entry immediately preceding the location is found
219  *     within start_wildcard_matches.  E.g. start_wildcard is '.', target_string is ".foo" and target_entry is "bar-foo-baz".  A match is found if start_wildcard_matches is "-!#" but not if it is
220  *     "/.,".  NULL disables this feature.
221  *   end_wildcard = like start_wildcard but at the end of target_string instead of the start
222  *   end_wildcard_matches = like start_wildcard_matches but at the end of target_string instead of the start
223  *
224  * Return value:
225  *   0: no match
226  *   1: match found
227  */
examine_entry(char * target_string,int strlen_target_string,char * target_entry,int strlen_target_entry,char start_wildcard,char * start_wildcard_matches,char end_wildcard,char * end_wildcard_matches)228 int examine_entry(char *target_string, int strlen_target_string, char *target_entry, int strlen_target_entry, char start_wildcard, char *start_wildcard_matches, char end_wildcard, char *end_wildcard_matches)
229   {
230   int return_value;
231   int check_start;
232   int check_end;
233   char *tmp_string;
234   char old_end_char;
235   char *tmp_entry;
236 
237   return_value = 0;
238   check_start = 0;
239   check_end = 0;
240 
241   if ((target_entry != NULL) &&
242       (strlen_target_entry > 0))
243     {
244     old_end_char = target_entry[strlen_target_entry - 1];
245     if ((end_wildcard != '\0') &&
246         (target_entry[strlen_target_entry - 1] == end_wildcard))
247       {
248       strlen_target_entry--;
249       target_entry[strlen_target_entry] = '\0';
250       check_end = 1;
251       }
252 
253     if ((start_wildcard != '\0') &&
254         (target_entry[0] == start_wildcard))
255       {
256       strlen_target_entry--;
257       tmp_entry = target_entry + 1;
258       check_start = 1;
259       }
260     else
261       tmp_entry = target_entry;
262 
263     tmp_string = find_case_insensitive_needle(target_string, tmp_entry);
264 
265     while ((tmp_string != NULL) &&
266            (return_value == 0))
267       if (((check_start &&
268             ((start_wildcard_matches == NULL) ||
269              (tmp_string == target_string) ||
270              (strchr(start_wildcard_matches, (tmp_string - 1)[0]) != NULL))) ||
271            (!check_start &&
272             (tmp_string == target_string))) &&
273           ((check_end &&
274             ((end_wildcard_matches == NULL) ||
275              (strchr(end_wildcard_matches, tmp_string[strlen_target_entry]) != NULL))) ||
276            (!check_end &&
277             (((tmp_string - target_string) + strlen_target_entry) == strlen_target_string))))
278         return_value = 1;
279       else
280         tmp_string = find_case_insensitive_needle(tmp_string + 1, tmp_entry);
281 
282     strlen_target_entry += check_end + check_start;
283     target_entry[strlen_target_entry - 1] = old_end_char;
284     }
285 
286   return(return_value);
287   }
288 
289 /*
290  * EXPECTS:
291  *   search_filename = the file to search, line by line
292  *   target_string = string content to search for, must not be NULL or zero length
293  *   strlen_target_string = length of target_string
294  *   start_wildcard = wildcard character that, if present as the first character in target_string, allows target_string to match in the middle of a line.  \0 disables this feature.
295  *   start_wildcard_matches = if start_wildcard matches the first character in target_string, a match is not found unless the character in the line immediately preceding the location is found
296  *     within start_wildcard_matches.  E.g. start_wildcard is '.', target_string is ".foo" and the line is "bar-foo-baz".  A match is found if start_wildcard_matches is "-!#" but not if it is
297  *     "/.,".  NULL disables this feature.
298  *   end_wildcard = like start_wildcard but at the end of target_string instead of the start
299  *   end_wildcard_matches = like start_wildcard_matches but at the end of target_string instead of the start
300  *
301  * Return value:
302  *   ERROR: -1
303  *   NOT FOUND: 0
304  *   FOUND: matching line number
305  */
search_file(struct filter_settings * current_settings,char * search_filename,char * target_string,int strlen_target_string,char start_wildcard,char * start_wildcard_matches,char end_wildcard,char * end_wildcard_matches)306 int search_file(struct filter_settings *current_settings, char *search_filename, char *target_string, int strlen_target_string, char start_wildcard, char *start_wildcard_matches, char end_wildcard, char *end_wildcard_matches)
307   {
308   int return_value;
309   FILE *tmp_file;
310   char tmp_buf[MAX_FILE_BUF + 1];
311   int line_num;
312   int i;
313   int strlen_buf;
314   char lower_start_wildcard;
315   char lower_end_wildcard;
316   char lower_target_string[MAX_BUF + 1];
317   int strlen_lower_target_string;
318 
319   return_value = 0;
320 
321   if ((target_string != NULL) &&
322       (strlen_target_string > 0))
323     {
324     if ((tmp_file = fopen(search_filename, "r")) != NULL)
325       {
326       line_num = 0;
327 
328       lower_start_wildcard = (start_wildcard != '\0') ? tolower((int)start_wildcard) : start_wildcard;
329       lower_end_wildcard = (end_wildcard != '\0') ? tolower((int)end_wildcard) : end_wildcard;
330 
331       strlen_lower_target_string = MINVAL(MAX_BUF, strlen_target_string);
332       for (i = 0; i < strlen_lower_target_string; i++)
333         lower_target_string[i] = tolower((int)target_string[i]);
334 
335       while (!feof(tmp_file) &&
336              (line_num < MAX_FILE_LINES))
337         {
338         if ((fscanf(tmp_file, "%" STRINGIFY(MAX_FILE_BUF) "[^\r\n]", tmp_buf) == 1) &&
339             (tmp_buf[0] != COMMENT_DELIMITER) &&
340             ((strlen_buf = strlen(tmp_buf)) > 0) &&
341             examine_entry(lower_target_string, strlen_lower_target_string, tmp_buf, strlen_buf, lower_start_wildcard, start_wildcard_matches, lower_end_wildcard, end_wildcard_matches))
342           {
343           return_value = line_num + 1;
344           break;
345           }
346 
347         fscanf(tmp_file, "%*1[\r\n]");
348         line_num++;
349         }
350 
351       if (line_num == MAX_FILE_LINES)
352         SPAMDYKE_LOG_VERBOSE(current_settings, LOG_VERBOSE_FILE_TOO_LONG "%s", MAX_FILE_LINES, search_filename);
353 
354       fclose(tmp_file);
355       }
356     else
357       {
358       SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_OPEN_SEARCH "%s: %s", search_filename, strerror(errno));
359       return_value = -1;
360       }
361     }
362 
363   return(return_value);
364   }
365 
366 /*
367  * Return value:
368  *   0: no match
369  *   1: match found
370  */
sub_examine_tcprules_entry(char * destination,int size_destination,char * env_info,int target_ip_ints[4],char * target_entry,int strlen_target_entry,char * target_name,int strlen_target_name)371 int sub_examine_tcprules_entry(char *destination, int size_destination, char *env_info, int target_ip_ints[4], char *target_entry, int strlen_target_entry, char *target_name, int strlen_target_name)
372   {
373   int return_value;
374   int j;
375   int k;
376   char tmp_buf[MAX_FILE_BUF + 1];
377   char *loc_cmd;
378   char *loc_info;
379   char *loc_start;
380   char *tmp_name;
381   char ip_octets[4][8];
382   char mask_octets[4][4];
383   int ip_ints[4];
384   int range_ints[4];
385   int mask_ints[4];
386   int sscanf_result;
387   int network_size;
388 
389   return_value = 0;
390 
391   snprintf(tmp_buf, MAX_FILE_BUF, "%s", target_entry);
392 
393   if ((loc_cmd = strchr(tmp_buf, TCPRULES_ENVIRONMENT)) != NULL)
394     {
395     if (loc_cmd == tmp_buf)
396       {
397       if (destination != NULL)
398         snprintf(destination, size_destination, "%s", loc_cmd + 1);
399 
400       return_value = 1;
401       }
402     else
403       {
404       loc_cmd[0] = '\0';
405       loc_cmd++;
406       }
407     }
408 
409   if (!return_value)
410     {
411     if ((loc_info = strchr(tmp_buf, TCPRULES_INFO)) != NULL)
412       {
413       loc_info[0] = '\0';
414       if ((env_info != NULL) &&
415           (strcasecmp(tmp_buf, env_info) == 0))
416         loc_start = loc_info + 1;
417       else
418         return_value = -1;
419       }
420     else
421       loc_start = tmp_buf;
422     }
423 
424   if (!return_value)
425     {
426     for (j = 0; j < 4; j++)
427       {
428       ip_octets[j][0] = '\0';
429       ip_ints[j] = 0;
430       range_ints[j] = -1;
431       mask_octets[j][0] = '\0';
432       mask_ints[j] = 0;
433       }
434 
435     if (loc_start[0] == TCPRULES_NAME)
436       {
437       for (j = 0; tmp_buf[j] != '\0'; j++)
438         tmp_buf[j] = tolower((int)tmp_buf[j]);
439 
440       if ((strlen_target_name > 0) &&
441           ((loc_start[1] == '\0') ||
442            (((tmp_name = find_case_insensitive_needle(target_name, loc_start + 1)) != NULL) &&
443             ((tmp_name == target_name) ||
444              (loc_start[1] == '.') ||
445              ((tmp_name - 1)[0] == '.')))))
446         {
447         if (destination != NULL)
448           {
449           if (loc_cmd != NULL)
450             snprintf(destination, size_destination, "%s", loc_cmd);
451           else
452             destination[0] = '\0';
453           }
454 
455         return_value = 1;
456         }
457       }
458     else if (((sscanf_result = sscanf(loc_start, "%7[0-9-].%7[0-9-].%7[0-9-].%7[0-9-]/%3[0-9].%3[0-9].%3[0-9].%3[0-9]", ip_octets[0], ip_octets[1], ip_octets[2], ip_octets[3], mask_octets[0], mask_octets[1], mask_octets[2], mask_octets[3])) > 0) &&
459              (sscanf_result <= 4))
460       {
461       for (j = 0; j < sscanf_result; j++)
462         if ((sscanf(ip_octets[j], "%3d-%3d", &ip_ints[j], &range_ints[j]) == 0) ||
463             (ip_ints[j] < 0) ||
464             (ip_ints[j] > 255) ||
465             ((range_ints[j] == -1) &&
466              (ip_ints[j] != target_ip_ints[j])) ||
467             ((range_ints[j] >= ip_ints[j]) &&
468              (range_ints[j] <= 255) &&
469              ((ip_ints[j] > target_ip_ints[j]) ||
470               (range_ints[j] < target_ip_ints[j]))))
471           break;
472 
473       if (j == sscanf_result)
474         {
475         if (destination != NULL)
476           {
477           if (loc_cmd != NULL)
478             snprintf(destination, size_destination, "%s", loc_cmd);
479           else
480             destination[0] = '\0';
481           }
482 
483         return_value = 1;
484         }
485       }
486     else if (sscanf_result == 5)
487       {
488       for (j = 0; j < 4; j++)
489         if ((sscanf(ip_octets[j], "%3d-%3d", &ip_ints[j], &range_ints[j]) == 0) ||
490             (ip_ints[j] < 0) ||
491             (ip_ints[j] > 255))
492           break;
493 
494       if ((j == 4) &&
495           (sscanf(mask_octets[0], "%d", &network_size) == 1) &&
496           (network_size >= 0) &&
497           (network_size <= 32))
498         {
499         for (k = 0; k < network_size; k++)
500           mask_ints[k / 8] |= 0x80 >> (k % 8);
501 
502         if (((mask_ints[0] & target_ip_ints[0]) == (mask_ints[0] & ip_ints[0])) &&
503             ((mask_ints[1] & target_ip_ints[1]) == (mask_ints[1] & ip_ints[1])) &&
504             ((mask_ints[2] & target_ip_ints[2]) == (mask_ints[2] & ip_ints[2])) &&
505             ((mask_ints[3] & target_ip_ints[3]) == (mask_ints[3] & ip_ints[3])))
506           return_value = 1;
507         }
508       }
509     else if (sscanf_result == 8)
510       {
511       for (j = 0; j < 4; j++)
512         if ((sscanf(ip_octets[j], "%3d-%d", &ip_ints[j], &range_ints[j]) == 0) ||
513             (ip_ints[j] < 0) ||
514             (ip_ints[j] > 255))
515           break;
516 
517       if (j == 4)
518         {
519         for (j = 0; j < 4; j++)
520           if ((sscanf(mask_octets[j], "%d", &mask_ints[j]) != 1) ||
521               (mask_ints[j] < 0) ||
522               (mask_ints[j] > 255))
523             break;
524 
525         if ((j == 4) &&
526             ((mask_ints[0] & target_ip_ints[0]) == (mask_ints[0] & ip_ints[0])) &&
527             ((mask_ints[1] & target_ip_ints[1]) == (mask_ints[1] & ip_ints[1])) &&
528             ((mask_ints[2] & target_ip_ints[2]) == (mask_ints[2] & ip_ints[2])) &&
529             ((mask_ints[3] & target_ip_ints[3]) == (mask_ints[3] & ip_ints[3])))
530           {
531           if (destination != NULL)
532             {
533             if (loc_cmd != NULL)
534               snprintf(destination, size_destination, "%s", loc_cmd);
535             else
536               destination[0] = '\0';
537             }
538 
539           return_value = 1;
540           }
541         }
542       }
543     }
544 
545   return((return_value == 1) ? 1 : 0);
546   }
547 
548 /*
549  * Return value:
550  *   0: no match
551  *   1: match found
552  */
examine_tcprules_entry(struct filter_settings * current_settings,char * destination,int size_destination,char * target_entry,int strlen_target_entry,char * target_ip,char * target_name,int strlen_target_name)553 int examine_tcprules_entry(struct filter_settings *current_settings, char *destination, int size_destination, char *target_entry, int strlen_target_entry, char *target_ip, char *target_name, int strlen_target_name)
554   {
555   int return_value;
556   char *env_info;
557   char ip_octets[4][7];
558   int target_ip_ints[4];
559 
560   return_value = 0;
561 
562   if ((target_ip != NULL) &&
563       (sscanf(target_ip, "%3[0-9].%3[0-9].%3[0-9].%3[0-9]", ip_octets[0], ip_octets[1], ip_octets[2], ip_octets[3]) == 4) &&
564       (sscanf(ip_octets[0], "%d", &target_ip_ints[0]) == 1) &&
565       (target_ip_ints[0] >= 0) &&
566       (target_ip_ints[0] <= 255) &&
567       (sscanf(ip_octets[1], "%d", &target_ip_ints[1]) == 1) &&
568       (target_ip_ints[1] >= 0) &&
569       (target_ip_ints[1] <= 255) &&
570       (sscanf(ip_octets[2], "%d", &target_ip_ints[2]) == 1) &&
571       (target_ip_ints[2] >= 0) &&
572       (target_ip_ints[2] <= 255) &&
573       (sscanf(ip_octets[3], "%d", &target_ip_ints[3]) == 1) &&
574       (target_ip_ints[3] >= 0) &&
575       (target_ip_ints[3] <= 255))
576     {
577     env_info = find_environment_variable(current_settings, current_settings->current_environment, ENVIRONMENT_REMOTE_INFO, STRLEN(ENVIRONMENT_REMOTE_INFO), NULL);
578 
579     return_value = sub_examine_tcprules_entry(destination, size_destination, env_info, target_ip_ints, target_entry, strlen_target_entry, target_name, strlen_target_name);
580     }
581 
582   return(return_value);
583   }
584 
585 /*
586  * Return value:
587  *   ERROR: -1
588  *   NOT FOUND: 0
589  *   FOUND: matching line number
590  */
search_tcprules_file(struct filter_settings * current_settings,char * destination,int size_destination,char * search_filename,char * target_ip,char * target_name,int strlen_target_name)591 int search_tcprules_file(struct filter_settings *current_settings, char *destination, int size_destination, char *search_filename, char *target_ip, char *target_name, int strlen_target_name)
592   {
593   int return_value;
594   int line_num;
595   FILE *tmp_file;
596   char tmp_buf[MAX_FILE_BUF + 1];
597   char *env_info;
598   char ip_octets[4][7];
599   int target_ip_ints[4];
600 
601   return_value = 0;
602 
603   if ((target_ip != NULL) &&
604       (sscanf(target_ip, "%3[0-9].%3[0-9].%3[0-9].%3[0-9]", ip_octets[0], ip_octets[1], ip_octets[2], ip_octets[3]) == 4) &&
605       (sscanf(ip_octets[0], "%d", &target_ip_ints[0]) == 1) &&
606       (target_ip_ints[0] >= 0) &&
607       (target_ip_ints[0] <= 255) &&
608       (sscanf(ip_octets[1], "%d", &target_ip_ints[1]) == 1) &&
609       (target_ip_ints[1] >= 0) &&
610       (target_ip_ints[1] <= 255) &&
611       (sscanf(ip_octets[2], "%d", &target_ip_ints[2]) == 1) &&
612       (target_ip_ints[2] >= 0) &&
613       (target_ip_ints[2] <= 255) &&
614       (sscanf(ip_octets[3], "%d", &target_ip_ints[3]) == 1) &&
615       (target_ip_ints[3] >= 0) &&
616       (target_ip_ints[3] <= 255))
617     {
618     if ((tmp_file = fopen(search_filename, "r")) != NULL)
619       {
620       line_num = 0;
621 
622       env_info = find_environment_variable(current_settings, current_settings->current_environment, ENVIRONMENT_REMOTE_INFO, STRLEN(ENVIRONMENT_REMOTE_INFO), NULL);
623 
624       while (!feof(tmp_file) &&
625              (line_num < MAX_FILE_LINES))
626         {
627         if ((fscanf(tmp_file, "%" STRINGIFY(MAX_FILE_BUF) "[^\r\n]", tmp_buf) == 1) &&
628             (tmp_buf[0] != '\0') &&
629             (tmp_buf[0] != COMMENT_DELIMITER) &&
630             sub_examine_tcprules_entry(destination, size_destination, env_info, target_ip_ints, tmp_buf, strlen(tmp_buf), target_name, strlen_target_name))
631           {
632           return_value = line_num + 1;
633           break;
634           }
635 
636         fscanf(tmp_file, "%*1[\r\n]");
637         line_num++;
638         }
639 
640       if (line_num == MAX_FILE_LINES)
641         SPAMDYKE_LOG_VERBOSE(current_settings, LOG_VERBOSE_FILE_TOO_LONG "%s", MAX_FILE_LINES, search_filename);
642 
643       fclose(tmp_file);
644       }
645     else
646       {
647       SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_OPEN_SEARCH "%s: %s", search_filename, strerror(errno));
648       return_value = -1;
649       }
650     }
651 
652   return(return_value);
653   }
654 
655 /*
656  * Return value:
657  *   ERROR: 0
658  *   SUCCESS: 1
659  */
load_resolver_file(struct filter_settings * current_settings,char * search_filename,int * return_default_port)660 int load_resolver_file(struct filter_settings *current_settings, char *search_filename, int *return_default_port)
661   {
662   int return_value;
663   FILE *tmp_file;
664   char tmp_buf[MAX_FILE_BUF + 1];
665   char *search_buf;
666   int line_num;
667   int i;
668   int orig_strlen_buf;
669   int strlen_buf;
670   char port[6];
671   char timeout[6];
672   int target_port;
673   int target_timeout;
674   int total_timeout;
675   int query_timeout;
676   int found_match;
677   char ip_octets[4][4];
678   int ip_ints[4];
679 
680   return_value = 1;
681   total_timeout = 0;
682   query_timeout = 0;
683 
684   if (search_filename != NULL)
685     {
686     if ((tmp_file = fopen(search_filename, "r")) != NULL)
687       {
688       line_num = 0;
689 
690       while (!feof(tmp_file) &&
691              (line_num < MAX_FILE_LINES))
692         {
693         if ((fscanf(tmp_file, "%" STRINGIFY(MAX_FILE_BUF) "[^\r\n]", tmp_buf) == 1) &&
694             ((orig_strlen_buf = strlen(tmp_buf)) > 0))
695           {
696           strlen_buf = 0;
697 
698           for (i = 0; (i < orig_strlen_buf) && isspace((int)tmp_buf[i]); i++);
699           for (; (i < orig_strlen_buf) && (tmp_buf[i] != RESOLVER_FILE_COMMENT_DELIMITER_1) && (tmp_buf[i] != RESOLVER_FILE_COMMENT_DELIMITER_2); i++)
700             {
701             tmp_buf[strlen_buf] = tolower((int)tmp_buf[i]);
702             strlen_buf++;
703             }
704           tmp_buf[i] = '\0';
705 
706           search_buf = tmp_buf;
707 
708           if (!strncmp(search_buf, NIHDNS_RESOLV_NAMESERVER, STRLEN(NIHDNS_RESOLV_NAMESERVER)))
709             {
710             for (search_buf += STRLEN(NIHDNS_RESOLV_NAMESERVER); (search_buf[0] != '\0') && isspace((int)search_buf[0]); search_buf++);
711 
712             if ((sscanf(search_buf, "%3[0-9].%3[0-9].%3[0-9].%3[0-9]", ip_octets[0], ip_octets[1], ip_octets[2], ip_octets[3]) == 4) &&
713                 (sscanf(ip_octets[0], "%d", &ip_ints[0]) == 1) &&
714                 (ip_ints[0] > 0) &&
715                 (ip_ints[0] <= 255) &&
716                 (sscanf(ip_octets[1], "%d", &ip_ints[1]) == 1) &&
717                 (ip_ints[1] >= 0) &&
718                 (ip_ints[1] <= 255) &&
719                 (sscanf(ip_octets[2], "%d", &ip_ints[2]) == 1) &&
720                 (ip_ints[2] >= 0) &&
721                 (ip_ints[2] <= 255) &&
722                 (sscanf(ip_octets[3], "%d", &ip_ints[3]) == 1) &&
723                 (ip_ints[3] >= 0) &&
724                 (ip_ints[3] <= 255))
725               {
726               found_match = 0;
727               if (current_settings->current_options->nihdns_primary_server_list != NULL)
728                 for (i = 0; current_settings->current_options->nihdns_primary_server_list[i] != NULL; i++)
729                   if (strcmp(current_settings->current_options->nihdns_primary_server_list[i], search_buf) == 0)
730                     {
731                     found_match = 1;
732                     break;
733                     }
734 
735               if (!found_match &&
736                   (current_settings->current_options->nihdns_secondary_server_list != NULL))
737                 for (i = 0; current_settings->current_options->nihdns_secondary_server_list[i] != NULL; i++)
738                   if (strcmp(current_settings->current_options->nihdns_secondary_server_list[i], search_buf) == 0)
739                     {
740                     found_match = 1;
741                     break;
742                     }
743 
744               if (!found_match)
745                 {
746                 append_string(current_settings, ((current_settings->current_options->nihdns_primary_server_list == NULL) || (current_settings->current_options->nihdns_primary_server_list[0] == NULL)) ? &current_settings->current_options->nihdns_primary_server_list : &current_settings->current_options->nihdns_secondary_server_list, search_buf, strlen(search_buf));
747                 SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_RESOLV_NS_LOAD, search_filename, line_num + 1, search_buf);
748                 }
749               else
750                 SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_RESOLV_NS_LOAD_DUPLICATE, search_filename, line_num + 1, search_buf);
751               }
752             else
753               SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_RESOLV_NS_BAD, search_buf);
754             }
755           else if (!strncmp(search_buf, NIHDNS_RESOLV_PORT, STRLEN(NIHDNS_RESOLV_PORT)))
756             {
757             for (search_buf += STRLEN(NIHDNS_RESOLV_PORT); (search_buf[0] != '\0') && isspace((int)search_buf[0]); search_buf++);
758 
759             target_port = 0;
760             if ((sscanf(search_buf, "%5[0-9]", port) == 1) &&
761                 (sscanf(port, "%d", &target_port) == 1) &&
762                 (target_port > 0) &&
763                 (target_port <= 65536))
764               {
765               if (return_default_port != NULL)
766                 *return_default_port = target_port;
767 
768               SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_RESOLV_PORT, search_filename, line_num + 1, target_port);
769               }
770             else
771               SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_RESOLV_PORT_BAD, search_filename, line_num + 1, tmp_buf);
772             }
773           else if (!strncmp(search_buf, NIHDNS_RESOLV_TIMEOUT, STRLEN(NIHDNS_RESOLV_TIMEOUT)))
774             {
775             for (search_buf += STRLEN(NIHDNS_RESOLV_TIMEOUT); (search_buf[0] != '\0') && isspace((int)search_buf[0]); search_buf++);
776 
777             target_timeout = 0;
778             if ((sscanf(search_buf, "%5[0-9]", timeout) == 1) &&
779                 (sscanf(timeout, "%d", &target_timeout) == 1) &&
780                 (target_timeout > 0) &&
781                 (target_timeout <= 65536))
782               {
783               total_timeout = target_timeout;
784 
785               SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_RESOLV_GLOBAL_TIMEOUT, search_filename, line_num + 1, total_timeout);
786               }
787             else
788               SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_RESOLV_GLOBAL_TIMEOUT_BAD, search_filename, line_num + 1, tmp_buf);
789             }
790           else if (!strncmp(search_buf, NIHDNS_RESOLV_OPTIONS, STRLEN(NIHDNS_RESOLV_OPTIONS)))
791             {
792             while (search_buf[0] != '\0')
793               {
794               for (search_buf += STRLEN(NIHDNS_RESOLV_OPTIONS); (search_buf[0] != '\0') && isspace((int)search_buf[0]); search_buf++);
795 
796               if (!strncmp(search_buf, NIHDNS_RESOLV_OPTION_TIMEOUT, STRLEN(NIHDNS_RESOLV_OPTION_TIMEOUT)))
797                 {
798                 search_buf += STRLEN(NIHDNS_RESOLV_OPTION_TIMEOUT);
799                 target_timeout = 0;
800                 if ((sscanf(search_buf, "%5[0-9]", timeout) == 1) &&
801                     (sscanf(timeout, "%d", &target_timeout) == 1) &&
802                     (target_timeout > 0) &&
803                     (target_timeout <= 65536))
804                   {
805                   query_timeout = target_timeout;
806 
807                   SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_RESOLV_QUERY_TIMEOUT, search_filename, line_num + 1, query_timeout);
808                   }
809                 else
810                   SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_RESOLV_QUERY_TIMEOUT_BAD, search_filename, line_num + 1, tmp_buf);
811                 }
812 
813               for (; (search_buf[0] != '\0') && !isspace((int)search_buf[0]); search_buf++);
814               }
815             }
816           else
817             SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_RESOLV_IGNORED, search_filename, line_num + 1, search_buf);
818           }
819 
820         fscanf(tmp_file, "%*1[\r\n]");
821         line_num++;
822         }
823 
824       if (line_num == MAX_FILE_LINES)
825         SPAMDYKE_LOG_VERBOSE(current_settings, LOG_VERBOSE_FILE_TOO_LONG "%s", MAX_FILE_LINES, search_filename);
826 
827       fclose(tmp_file);
828 
829       if ((total_timeout != 0) &&
830           (current_settings->current_options->nihdns_attempts_total > 0))
831         current_settings->current_options->nihdns_timeout_total_secs_system = total_timeout;
832       else if (query_timeout != 0)
833         current_settings->current_options->nihdns_timeout_total_secs_system = query_timeout * current_settings->current_options->nihdns_attempts_total;
834       }
835     else
836       {
837       SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_OPEN_SEARCH "%s: %s", search_filename, strerror(errno));
838       return_value = 0;
839       }
840     }
841   else
842     return_value = 0;
843 
844   return(return_value);
845   }
846 
847 /*
848  * Return value:
849  *   NOT FOUND: NULL
850  *   FOUND: pointer to static buffer containing matching path
851  */
search_domain_directory(struct filter_settings * current_settings,char * start_directory,char * target_domain,int strlen_target_domain)852 char *search_domain_directory(struct filter_settings *current_settings, char *start_directory, char *target_domain, int strlen_target_domain)
853   {
854   static char search_path[MAX_PATH + 1];
855   char *return_value;
856   int i;
857   char *domain_delimiter[3];
858   char *base_domain;
859   struct stat tmp_stat;
860 
861   return_value = NULL;
862 
863   if ((start_directory != NULL) &&
864       (start_directory[0] != '\0') &&
865       (target_domain != NULL) &&
866       (strlen_target_domain > 0))
867     {
868     domain_delimiter[0] = NULL;
869     domain_delimiter[1] = NULL;
870     domain_delimiter[2] = NULL;
871 
872     for (i = strlen_target_domain - 1; i >= 0; i--)
873       if (target_domain[i] == '.')
874         {
875         if (domain_delimiter[0] == NULL)
876           domain_delimiter[0] = target_domain + i + 1;
877         else if (domain_delimiter[1] == NULL)
878           domain_delimiter[1] = target_domain + i + 1;
879         else if (domain_delimiter[2] == NULL)
880           {
881           domain_delimiter[2] = target_domain + i + 1;
882           break;
883           }
884         }
885 
886     if (domain_delimiter[2] != NULL)
887       snprintf(search_path, MAX_PATH, "%s" DIR_DELIMITER_STR "%.*s" DIR_DELIMITER_STR "%c" DIR_DELIMITER_STR "%.*s" DIR_DELIMITER_STR "%.*s" DIR_DELIMITER_STR "%.*s", start_directory, (int)(strlen_target_domain - (domain_delimiter[0] - target_domain)), domain_delimiter[0], domain_delimiter[1][0], (int)((domain_delimiter[0] - domain_delimiter[1]) - 1), domain_delimiter[1], (int)((domain_delimiter[1] - domain_delimiter[2]) - 1), domain_delimiter[2], strlen_target_domain, target_domain);
888     else if (domain_delimiter[1] != NULL)
889       snprintf(search_path, MAX_PATH, "%s" DIR_DELIMITER_STR "%.*s" DIR_DELIMITER_STR "%c" DIR_DELIMITER_STR "%.*s" DIR_DELIMITER_STR "%.*s", start_directory, (int)(strlen_target_domain - (domain_delimiter[0] - target_domain)), domain_delimiter[0], domain_delimiter[1][0], (int)((domain_delimiter[0] - domain_delimiter[1]) - 1), domain_delimiter[1], strlen_target_domain, target_domain);
890     else if (domain_delimiter[0] != NULL)
891       snprintf(search_path, MAX_PATH, "%s" DIR_DELIMITER_STR "%.*s" DIR_DELIMITER_STR "%c" DIR_DELIMITER_STR "%.*s" DIR_DELIMITER_STR "%.*s", start_directory, (int)(strlen_target_domain - (domain_delimiter[0] - target_domain)), domain_delimiter[0], target_domain[0], (int)((domain_delimiter[0] - target_domain) - 1), target_domain, strlen_target_domain, target_domain);
892     else
893       snprintf(search_path, MAX_PATH, "%s" DIR_DELIMITER_STR "%.*s" DIR_DELIMITER_STR "%c" DIR_DELIMITER_STR "%.*s", start_directory, strlen_target_domain, target_domain, target_domain[0], strlen_target_domain, target_domain);
894 
895     SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_DOMAIN_DIR, search_path);
896 
897     if (stat(search_path, &tmp_stat) == 0)
898       return_value = search_path;
899     else if ((base_domain = strchr(target_domain, '.')) != NULL)
900       return_value = search_domain_directory(current_settings, start_directory, base_domain + 1, strlen_target_domain - ((base_domain + 1) - target_domain));
901     }
902 
903   return(return_value);
904   }
905 
906 /*
907  * start_line and end_line are 1-based.
908  * if end_line is -1, return_content will be realloc()ed as lines are read.
909  * if end_line is not -1, return_content must have at least ((end_line - start_line) + start_index + 1) entries preallocated.
910  * individual entries will always be allocated.
911  *
912  * Return value:
913  *   ERROR: -1
914  *   SUCCESS: number of lines read, excluding skipped lines (1-based)
915  */
read_file(struct filter_settings * current_settings,char * target_filename,char *** return_content,int start_index,int start_line,int end_line,int all_lines)916 int read_file(struct filter_settings *current_settings, char *target_filename, char ***return_content, int start_index, int start_line, int end_line, int all_lines)
917   {
918   int return_value;
919   int i;
920   int line_num;
921   int usable_line_num;
922   int strlen_line;
923   int zero_start;
924   FILE *tmp_file;
925   char file_buf[MAX_FILE_BUF + 1];
926   char **tmp_array;
927   char *tmp_char;
928 
929   return_value = 0;
930   line_num = 0;
931   usable_line_num = start_index;
932 
933   if ((target_filename != NULL) &&
934       (target_filename[0] != '\0') &&
935       (return_content != NULL))
936     {
937     if ((tmp_file = fopen(target_filename, "r")) != NULL)
938       {
939       SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_OPEN_FILE, target_filename);
940 
941       zero_start = (start_line - 1) - start_index;
942 
943       if ((end_line == -1) &&
944           (start_index == 0))
945         *return_content = NULL;
946 
947       while (!feof(tmp_file) &&
948              (line_num < MAX_FILE_LINES) &&
949              ((end_line == -1) ||
950               (line_num < end_line)))
951         {
952         if ((fscanf(tmp_file, "%" STRINGIFY(MAX_FILE_BUF) "[^\r\n]", file_buf) == 1) &&
953             ((strlen_line = strlen(file_buf)) || 1) &&
954             (all_lines ||
955              ((file_buf[0] != COMMENT_DELIMITER) &&
956               (strlen_line > 0))) &&
957             (line_num >= zero_start))
958           {
959           SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_READ_LINE, strlen_line, target_filename, line_num + 1, file_buf);
960 
961           if (end_line == -1)
962             {
963             if ((tmp_array = (char **)realloc(*return_content, sizeof(char *) * (usable_line_num + 2))) != NULL)
964               {
965               tmp_array[usable_line_num] = NULL;
966               tmp_array[usable_line_num + 1] = NULL;
967               *return_content = tmp_array;
968               }
969             else
970               {
971               SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_MALLOC, sizeof(char *) * (usable_line_num + 2));
972               return_value = -1;
973               break;
974               }
975             }
976 
977           if ((tmp_char = (char *)malloc(sizeof(char) * (strlen_line + 1))) != NULL)
978             {
979             (*return_content)[usable_line_num] = tmp_char;
980             memcpy((*return_content)[usable_line_num], file_buf, sizeof(char) * strlen_line);
981             (*return_content)[usable_line_num][strlen_line] = '\0';
982             }
983           else
984             {
985             SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_MALLOC, sizeof(char) * (strlen_line + 1));
986             return_value = -1;
987             break;
988             }
989 
990           usable_line_num++;
991           }
992 
993         fscanf(tmp_file, "%*1[\r\n]");
994         line_num++;
995         }
996 
997       if (line_num == MAX_FILE_LINES)
998         SPAMDYKE_LOG_VERBOSE(current_settings, LOG_VERBOSE_FILE_TOO_LONG "%s", MAX_FILE_LINES, target_filename);
999 
1000       fclose(tmp_file);
1001 
1002       if (return_value == 0)
1003         return_value = usable_line_num + 1;
1004       else
1005         {
1006         if ((*return_content) != NULL)
1007           {
1008           for (i = start_index; i < (line_num - zero_start); i++)
1009             if ((*return_content)[i] != NULL)
1010               free((*return_content)[i]);
1011 
1012           (*return_content)[start_index] = NULL;
1013           }
1014 
1015         if ((end_line == -1) &&
1016             (start_index == 0))
1017           {
1018           free(*return_content);
1019           *return_content = NULL;
1020           }
1021         }
1022       }
1023     else
1024       {
1025       SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_OPEN "%s: %s", target_filename, strerror(errno));
1026       return_value = -1;
1027       }
1028     }
1029 
1030   return(return_value);
1031   }
1032 
1033 /*
1034  * Return value:
1035  *   ERROR: -1
1036  *   SUCCESS: length of returned line
1037  */
read_file_first_line(struct filter_settings * current_settings,char * target_filename,char ** return_content)1038 int read_file_first_line(struct filter_settings *current_settings, char *target_filename, char **return_content)
1039   {
1040   int return_value;
1041   char *tmp_array[2];
1042   char **tmp_ptr;
1043 
1044   return_value = 0;
1045 
1046   tmp_array[0] = NULL;
1047   tmp_array[1] = NULL;
1048 
1049   /*
1050    * Without this, read_file() crashes on the statement (*return_content)[0] = tmp_char;
1051    * Why?
1052    */
1053   tmp_ptr = tmp_array;
1054 
1055   if ((return_content != NULL) &&
1056       (read_file(current_settings, target_filename, (char ***)&tmp_ptr, 0, 1, 1, 0) != -1))
1057     {
1058     *return_content = tmp_array[0];
1059     return_value = (tmp_array[0] != NULL) ? strlen(tmp_array[0]) : 0;
1060     }
1061 
1062   return(return_value);
1063   }
1064 
1065 /*
1066  * Expects:
1067  *   return_address is a preallocated buffer
1068  *   max_return_address is the size of return_address, >= 0
1069  *
1070  * Return value:
1071  *   return_address, filled with the reassembled address OR missing_data if the address is empty
1072  */
reassemble_address(char * target_username,char * target_domain,char * missing_data,char * return_address,int max_return_address,int * strlen_return_address)1073 char *reassemble_address(char *target_username, char *target_domain, char *missing_data, char *return_address, int max_return_address, int *strlen_return_address)
1074   {
1075   int tmp_strlen;
1076 
1077   tmp_strlen = 0;
1078 
1079   if ((return_address != NULL) &&
1080       (max_return_address >= 0))
1081     {
1082     if ((target_username != NULL) &&
1083         (target_username[0] != '\0'))
1084       if ((target_domain != NULL) &&
1085           (target_domain[0] != '\0'))
1086         tmp_strlen = SNPRINTF(return_address, max_return_address, "%s@%s", target_username, target_domain);
1087       else
1088         tmp_strlen = SNPRINTF(return_address, max_return_address, "%s", target_username);
1089     else if ((target_domain != NULL) &&
1090              (target_domain[0] != '\0'))
1091       tmp_strlen = SNPRINTF(return_address, max_return_address, "@%s", target_domain);
1092     else if (missing_data != NULL)
1093       tmp_strlen = SNPRINTF(return_address, max_return_address, "%s", missing_data);
1094     else
1095       return_address[0] = '\0';
1096     }
1097 
1098   if (strlen_return_address != NULL)
1099     *strlen_return_address = tmp_strlen;
1100 
1101   return(return_address);
1102   }
1103 
1104 /*
1105  * Reduces any sequences of one or more whitespace characters to a single space
1106  * and also converts the text to lowercase.
1107  *
1108  * Return value:
1109  *   -1: error
1110  *   number of characters copied to target_buf
1111  */
collapse_whitespace(struct filter_settings * current_settings,char * target_buf,int strlen_target_buf,char * input_buf,int strlen_input_buf)1112 int collapse_whitespace(struct filter_settings *current_settings, char *target_buf, int strlen_target_buf, char *input_buf, int strlen_input_buf)
1113   {
1114   int return_value;
1115   int i;
1116   int tmp_strlen;
1117 
1118   return_value = -1;
1119 
1120   if ((target_buf != NULL) &&
1121       (strlen_target_buf > 0))
1122     {
1123     tmp_strlen = 0;
1124 
1125     for (i = 0; (i < strlen_input_buf) && (i < strlen_target_buf); i++)
1126       if (isspace((int)input_buf[i]))
1127         {
1128         if ((tmp_strlen == 0) ||
1129             (target_buf[tmp_strlen - 1] != ' '))
1130           {
1131           target_buf[tmp_strlen] = ' ';
1132           tmp_strlen++;
1133           }
1134         }
1135       else
1136         {
1137         target_buf[tmp_strlen] = tolower((int)input_buf[i]);
1138         tmp_strlen++;
1139         }
1140 
1141     for (i = (tmp_strlen - 1); (i >= 0) && (target_buf[i] == ' '); i--)
1142       tmp_strlen--;
1143 
1144     target_buf[tmp_strlen] = '\0';
1145     return_value = tmp_strlen;
1146 
1147     SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_COLLAPSE_WHITESPACE, strlen_input_buf, strlen_input_buf, input_buf, tmp_strlen, tmp_strlen, target_buf);
1148     }
1149 
1150   return(return_value);
1151   }
1152 
1153 /*
1154  * Return value:
1155  *   0: no match
1156  *   1: match found
1157  */
examine_header(struct filter_settings * current_settings,char * target_header,int strlen_target_header,char * target_entry,int strlen_target_entry)1158 int examine_header(struct filter_settings *current_settings, char *target_header, int strlen_target_header, char *target_entry, int strlen_target_entry)
1159   {
1160   int return_value;
1161   char tmp_header[MAX_NETWORK_BUF + 1];
1162   int strlen_tmp_header;
1163   char tmp_entry[MAX_NETWORK_BUF + 1];
1164   int strlen_tmp_entry;
1165 
1166   return_value = 0;
1167 
1168   if ((target_header != NULL) &&
1169       (strlen_target_header > 0) &&
1170       (target_entry != NULL) &&
1171       (strlen_target_entry > 0) &&
1172       (strchr(target_entry, HEADER_DELIMITER) != NULL) &&
1173       (strchr(target_header, HEADER_DELIMITER) != NULL) &&
1174       ((strlen_tmp_header = collapse_whitespace(current_settings, tmp_header, MAX_NETWORK_BUF, target_header, strlen_target_header)) > 0) &&
1175       ((strlen_tmp_entry = collapse_whitespace(current_settings, tmp_entry, MAX_NETWORK_BUF, target_entry, strlen_target_entry)) > 0) &&
1176       (fnmatch(tmp_entry, tmp_header, 0) == 0))
1177     return_value = 1;
1178 
1179   return(return_value);
1180   }
1181 
1182 /*
1183  * Return value:
1184  *   ERROR: -1
1185  *   NOT FOUND: 0
1186  *   FOUND: matching line number
1187  */
search_header_file(struct filter_settings * current_settings,char * search_filename,char * target_header,int strlen_target_header)1188 int search_header_file(struct filter_settings *current_settings, char *search_filename, char *target_header, int strlen_target_header)
1189   {
1190   int return_value;
1191   FILE *tmp_file;
1192   char tmp_buf[MAX_FILE_BUF + 1];
1193   int line_num;
1194   int strlen_buf;
1195   char tmp_header[MAX_NETWORK_BUF + 1];
1196   int strlen_tmp_header;
1197   char tmp_entry[MAX_NETWORK_BUF + 1];
1198   int strlen_tmp_entry;
1199 
1200   return_value = 0;
1201 
1202   if ((target_header != NULL) &&
1203       (strlen_target_header > 0) &&
1204       (strchr(target_header, HEADER_DELIMITER) != NULL) &&
1205       ((strlen_tmp_header = collapse_whitespace(current_settings, tmp_header, MAX_NETWORK_BUF, target_header, strlen_target_header)) > 0))
1206     {
1207     if ((tmp_file = fopen(search_filename, "r")) != NULL)
1208       {
1209       line_num = 0;
1210 
1211       while (!feof(tmp_file) &&
1212              (line_num < MAX_FILE_LINES))
1213         {
1214         if ((fscanf(tmp_file, "%" STRINGIFY(MAX_FILE_BUF) "[^\r\n]", tmp_buf) == 1) &&
1215             (tmp_buf[0] != COMMENT_DELIMITER) &&
1216             ((strlen_buf = strlen(tmp_buf)) > 0) &&
1217             (strchr(tmp_buf, HEADER_DELIMITER) != NULL) &&
1218             ((strlen_tmp_entry = collapse_whitespace(current_settings, tmp_entry, MAX_NETWORK_BUF, tmp_buf, strlen_buf)) > 0) &&
1219             (fnmatch(tmp_entry, tmp_header, 0) == 0))
1220           {
1221           return_value = line_num + 1;
1222           break;
1223           }
1224 
1225         fscanf(tmp_file, "%*1[\r\n]");
1226         line_num++;
1227         }
1228 
1229       if (line_num == MAX_FILE_LINES)
1230         SPAMDYKE_LOG_VERBOSE(current_settings, LOG_VERBOSE_FILE_TOO_LONG "%s", MAX_FILE_LINES, search_filename);
1231 
1232       fclose(tmp_file);
1233       }
1234     else
1235       {
1236       SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_OPEN_SEARCH "%s: %s", search_filename, strerror(errno));
1237       return_value = -1;
1238       }
1239     }
1240 
1241   return(return_value);
1242   }
1243 
1244 /*
1245  * EXPECTS:
1246  *   type_flag should be a value within the S_IFMT mask in stat.h -- S_IFDIR, S_IFREG, etc
1247  *   permission_flags should be an OR of FILE_PERMISSION_READ, FILE_PERMISSION_WRITE or FILE_PERMISSION_EXECUTE, not bitshifted for user/group/other.
1248  *   if target_uid is -1, geteuid() will be used
1249  *   if target_gid is -1, getegid() and secondary groups will be used
1250  *
1251  * RETURNS:
1252  *  -1: error
1253  *  0: file does not exist, is the wrong type or does not have permissions
1254  *  1: file exists, is the correct type and has the permissions
1255  */
check_path_perms(struct filter_settings * current_settings,char * target_path,int type_flag,int permission_flags,struct stat * target_stat,int target_uid,int target_gid)1256 int check_path_perms(struct filter_settings *current_settings, char *target_path, int type_flag, int permission_flags, struct stat *target_stat, int target_uid, int target_gid)
1257   {
1258   int return_value;
1259   int i;
1260   struct stat *tmp_stat;
1261   struct stat internal_stat;
1262   int found_match;
1263   int num_groups;
1264   gid_t *tmp_gid;
1265 
1266   return_value = 0;
1267   tmp_gid = NULL;
1268 
1269   if (target_path != NULL)
1270     {
1271     tmp_stat = (target_stat != NULL) ? target_stat : &internal_stat;
1272     if (!stat(target_path, tmp_stat))
1273       {
1274       SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_FILE_STAT, tmp_stat->st_mode, type_flag, tmp_stat->st_uid, tmp_stat->st_gid, target_path);
1275 
1276       if (((type_flag == 0) ||
1277            ((tmp_stat->st_mode & S_IFMT) == type_flag)) &&
1278           (((permission_flags & FILE_PERMISSION_SETUID) == 0) ||
1279            (tmp_stat->st_mode & S_ISUID)) &&
1280           (((permission_flags & FILE_PERMISSION_SETGID) == 0) ||
1281            (tmp_stat->st_mode & S_ISGID)) &&
1282           (((permission_flags & FILE_PERMISSION_STICKY) == 0) ||
1283            (tmp_stat->st_mode & S_ISVTX)))
1284         {
1285         if ((permission_flags & FILE_PERMISSION_READ) ||
1286             (permission_flags & FILE_PERMISSION_WRITE) ||
1287             (permission_flags & FILE_PERMISSION_EXECUTE))
1288           {
1289           if (((target_uid != -1) &&
1290                (tmp_stat->st_uid == target_uid)) ||
1291               ((target_uid == -1) &&
1292                (tmp_stat->st_uid == geteuid())))
1293             {
1294             if ((((permission_flags & FILE_PERMISSION_READ) == 0) ||
1295                  (tmp_stat->st_mode & S_IRUSR)) &&
1296                 (((permission_flags & FILE_PERMISSION_WRITE) == 0) ||
1297                  (tmp_stat->st_mode & S_IWUSR)) &&
1298                 (((permission_flags & FILE_PERMISSION_EXECUTE) == 0) ||
1299                  (tmp_stat->st_mode & S_IXUSR)))
1300               return_value = 1;
1301             }
1302           else
1303             {
1304             found_match = 0;
1305 
1306             if (target_gid != -1)
1307               {
1308               if (tmp_stat->st_gid == target_gid)
1309                 found_match = 1;
1310               }
1311             else if (tmp_stat->st_gid == getegid())
1312               found_match = 1;
1313             else if ((num_groups = getgroups(0, NULL)) > 0)
1314               {
1315               if ((tmp_gid = (gid_t *)malloc(sizeof(gid_t) * num_groups)) != NULL)
1316                 {
1317                 if (getgroups(num_groups, tmp_gid) > 0)
1318                   for (i = 0; i < num_groups; i++)
1319                     if (tmp_stat->st_gid == tmp_gid[i])
1320                       {
1321                       /* Secondary group owns the directory. */
1322                       found_match = 1;
1323                       break;
1324                       }
1325 
1326                 free(tmp_gid);
1327                 tmp_gid = NULL;
1328                 }
1329               else
1330                 {
1331                 SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_MALLOC, (long)(sizeof(gid_t) * num_groups));
1332                 return_value = -1;
1333                 }
1334               }
1335 
1336             if (found_match)
1337               {
1338               if ((((permission_flags & FILE_PERMISSION_READ) == 0) ||
1339                    (tmp_stat->st_mode & S_IRGRP)) &&
1340                   (((permission_flags & FILE_PERMISSION_WRITE) == 0) ||
1341                    (tmp_stat->st_mode & S_IWGRP)) &&
1342                   (((permission_flags & FILE_PERMISSION_EXECUTE) == 0) ||
1343                    (tmp_stat->st_mode & S_IXGRP)))
1344                 return_value = 1;
1345               }
1346             else
1347               if ((((permission_flags & FILE_PERMISSION_READ) == 0) ||
1348                    (tmp_stat->st_mode & S_IROTH)) &&
1349                   (((permission_flags & FILE_PERMISSION_WRITE) == 0) ||
1350                    (tmp_stat->st_mode & S_IWOTH)) &&
1351                   (((permission_flags & FILE_PERMISSION_EXECUTE) == 0) ||
1352                    (tmp_stat->st_mode & S_IXOTH)))
1353                 return_value = 1;
1354             }
1355           }
1356         else
1357           return_value = 1;
1358         }
1359       }
1360     else if ((errno == EACCES) ||
1361              (errno == ENOENT) ||
1362              (errno == ENOTDIR))
1363       {
1364       SPAMDYKE_LOG_EXCESSIVE(current_settings, LOG_DEBUGX_FILE_STAT_FAIL, target_path, strerror(errno));
1365       return_value = 0;
1366       }
1367     else
1368       {
1369       SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_STAT_ERRNO, target_path, strerror(errno));
1370       return_value = -1;
1371       }
1372     }
1373 
1374   return(return_value);
1375   }
1376