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)) ? ¤t_settings->current_options->nihdns_primary_server_list : ¤t_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