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
19 #include "config-qrv.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include "spamdyke-qrv.h"
27 #include "fs-qrv.h"
28 #include "log-qrv.h"
29
30 /*
31 * When passed an entire command line string (e.g. from a .qmail file), this
32 * function finds just the command itself, allowing for escaping characters
33 * and quoting.
34 *
35 * RETURNS:
36 * characters copied to return_text
37 */
find_command(char * input_text,char * return_text,int size_return_text)38 int find_command(char *input_text, char *return_text, int size_return_text)
39 {
40 int return_value;
41 int i;
42 int escape;
43 int inside_quote;
44
45 return_value = 0;
46 escape = 0;
47 inside_quote = 0;
48
49 if ((input_text != NULL) &&
50 (return_text != NULL))
51 {
52 for (i = 0; input_text[i] != '\0'; i++)
53 if (!isspace((int)input_text[i]))
54 break;
55
56 for (; (input_text[i] != '\0') && (return_value < size_return_text); i++)
57 if (isspace((int)input_text[i]) &&
58 !inside_quote &&
59 !escape)
60 break;
61 else if (escape)
62 {
63 return_text[return_value] = input_text[i];
64 return_value++;
65 escape = 0;
66 }
67 else if (input_text[i] == PATH_ESCAPE_CHAR)
68 escape = 1;
69 else if (input_text[i] == PATH_QUOTE_CHAR)
70 inside_quote = !inside_quote;
71 else
72 {
73 return_text[return_value] = input_text[i];
74 return_value++;
75 }
76
77 return_text[return_value] = '\0';
78 }
79
80 return(return_value);
81 }
82
83 /*
84 * Return value:
85 * NULL: no match
86 * pointer to match within haystack: match
87 */
find_case_insensitive_needle(char * haystack,char * needle)88 char *find_case_insensitive_needle(char *haystack, char *needle)
89 {
90 char *return_value;
91 int i;
92 char tmp_buf[MAX_BUF + 1];
93
94 if ((haystack != NULL) &&
95 (needle != NULL))
96 {
97 for (i = 0; (i < MAX_BUF) && (needle[i] != '\0'); i++)
98 tmp_buf[i] = tolower((int)needle[i]);
99 tmp_buf[i] = '\0';
100
101 return_value = strstr(haystack, tmp_buf);
102 }
103 else
104 return_value = NULL;
105
106 return(return_value);
107 }
108
109 /*
110 * EXPECTS:
111 * target_string = string content to search for, must not be NULL or zero length
112 * strlen_target_string = length of target_string
113 * target_entry = string content to search within, must not be NULL
114 * strlen_target_entry = length of target_entry
115 * 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.
116 * 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
117 * 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
118 * "/.,". NULL disables this feature.
119 * end_wildcard = like start_wildcard but at the end of target_string instead of the start
120 * end_wildcard_matches = like start_wildcard_matches but at the end of target_string instead of the start
121 *
122 * Return value:
123 * 0: no match
124 * 1: match found
125 */
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)126 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)
127 {
128 int return_value;
129 int check_start;
130 int check_end;
131 char *tmp_string;
132 char old_end_char;
133 char *tmp_entry;
134
135 return_value = 0;
136 check_start = 0;
137 check_end = 0;
138
139 if ((target_entry != NULL) &&
140 (strlen_target_entry > 0))
141 {
142 old_end_char = target_entry[strlen_target_entry - 1];
143 if ((end_wildcard != '\0') &&
144 (target_entry[strlen_target_entry - 1] == end_wildcard))
145 {
146 strlen_target_entry--;
147 target_entry[strlen_target_entry] = '\0';
148 check_end = 1;
149 }
150
151 if ((start_wildcard != '\0') &&
152 (target_entry[0] == start_wildcard))
153 {
154 strlen_target_entry--;
155 tmp_entry = target_entry + 1;
156 check_start = 1;
157 }
158 else
159 tmp_entry = target_entry;
160
161 tmp_string = find_case_insensitive_needle(target_string, tmp_entry);
162
163 while ((tmp_string != NULL) &&
164 (return_value == 0))
165 if (((check_start &&
166 ((start_wildcard_matches == NULL) ||
167 (strchr(start_wildcard_matches, (tmp_string - 1)[0]) != NULL))) ||
168 (!check_start &&
169 (tmp_string == target_string))) &&
170 ((check_end &&
171 ((end_wildcard_matches == NULL) ||
172 (strchr(end_wildcard_matches, tmp_string[strlen_target_entry]) != NULL))) ||
173 (!check_end &&
174 (((tmp_string - target_string) + strlen_target_entry) == strlen_target_string))))
175 return_value = 1;
176 else
177 tmp_string = find_case_insensitive_needle(tmp_string + 1, tmp_entry);
178
179 strlen_target_entry += check_end + check_start;
180 target_entry[strlen_target_entry - 1] = old_end_char;
181 }
182
183 return(return_value);
184 }
185
186 /*
187 * EXPECTS:
188 * search_filename = the file to search, line by line
189 * target_string = string content to search for, must not be NULL or zero length
190 * strlen_target_string = length of target_string
191 * 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.
192 * 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
193 * 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
194 * "/.,". NULL disables this feature.
195 * end_wildcard = like start_wildcard but at the end of target_string instead of the start
196 * end_wildcard_matches = like start_wildcard_matches but at the end of target_string instead of the start
197 *
198 * Return value:
199 * ERROR: -1
200 * NOT FOUND: 0
201 * FOUND: matching line number
202 */
search_file(struct qrv_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)203 int search_file(struct qrv_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)
204 {
205 int return_value;
206 FILE *tmp_file;
207 char tmp_buf[MAX_FILE_BUF + 1];
208 int line_num;
209 int i;
210 int strlen_buf;
211 char lower_start_wildcard;
212 char lower_end_wildcard;
213 char lower_target_string[MAX_BUF + 1];
214 int strlen_lower_target_string;
215 struct stat tmp_stat;
216
217 return_value = 0;
218
219 if ((target_string != NULL) &&
220 (strlen_target_string > 0))
221 {
222 QRV_LOG_EXCESSIVE(current_settings, LOG_EXCESSIVE_SEARCH_FILE, search_filename, strlen_target_string, target_string);
223
224 if ((tmp_file = fopen(search_filename, "r")) != NULL)
225 {
226 line_num = 0;
227
228 lower_start_wildcard = (start_wildcard != '\0') ? tolower((int)start_wildcard) : start_wildcard;
229 lower_end_wildcard = (end_wildcard != '\0') ? tolower((int)end_wildcard) : end_wildcard;
230
231 strlen_lower_target_string = MINVAL(MAX_BUF, strlen_target_string);
232 for (i = 0; i < strlen_lower_target_string; i++)
233 lower_target_string[i] = tolower((int)target_string[i]);
234
235 while (!feof(tmp_file) &&
236 (line_num < MAX_FILE_LINES))
237 {
238 if ((fscanf(tmp_file, "%" STRINGIFY(MAX_FILE_BUF) "[^\r\n]", tmp_buf) == 1) &&
239 (tmp_buf[0] != COMMENT_DELIMITER) &&
240 ((strlen_buf = strlen(tmp_buf)) > 0) &&
241 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))
242 {
243 return_value = line_num + 1;
244 break;
245 }
246
247 fscanf(tmp_file, "%*1[\r\n]");
248 line_num++;
249 }
250
251 if (line_num == MAX_FILE_LINES)
252 QRV_LOG_VERBOSE(current_settings, LOG_VERBOSE_FILE_TOO_LONG "%s", MAX_FILE_LINES, search_filename);
253
254 fclose(tmp_file);
255 }
256 else if ((stat(search_filename, &tmp_stat) == -1) &&
257 (errno == ENOENT))
258 return_value = 0;
259 else
260 {
261 QRV_LOG_ERROR(current_settings, LOG_ERROR_OPEN_SEARCH "%s: %s", search_filename, strerror(errno));
262 return_value = -1;
263 }
264 }
265
266 return(return_value);
267 }
268
269 /*
270 * RETURNS:
271 * -1 = error
272 * 0 = no match
273 * >0 = line number where match was found
274 */
search_virtualdomains_file(struct qrv_settings * current_settings,char * search_filename,char * target_domain,int strlen_target_domain,char * return_entry,int * size_return_entry)275 int search_virtualdomains_file(struct qrv_settings *current_settings, char *search_filename, char *target_domain, int strlen_target_domain, char *return_entry, int *size_return_entry)
276 {
277 int return_value;
278 FILE *tmp_file;
279 char tmp_buf[MAX_FILE_BUF + 1];
280 int line_num;
281 int i;
282 int strlen_buf;
283 char lower_target_domain[MAX_BUF + 1];
284 int strlen_lower_target_domain;
285
286 return_value = 0;
287
288 if ((search_filename != NULL) &&
289 (target_domain != NULL) &&
290 (strlen_target_domain > 0))
291 {
292 if ((tmp_file = fopen(search_filename, "r")) != NULL)
293 {
294 line_num = 0;
295
296 strlen_lower_target_domain = MINVAL(MAX_BUF, strlen_target_domain);
297 for (i = 0; i < strlen_lower_target_domain; i++)
298 lower_target_domain[i] = tolower((int)target_domain[i]);
299
300 while (!feof(tmp_file) &&
301 (line_num < MAX_FILE_LINES))
302 {
303 if ((fscanf(tmp_file, "%" STRINGIFY(MAX_FILE_BUF) "[^\r\n]", tmp_buf) == 1) &&
304 (tmp_buf[0] != COMMENT_DELIMITER) &&
305 ((strlen_buf = strlen(tmp_buf)) > 0) &&
306 (strlen_buf > strlen_lower_target_domain) &&
307 !strncmp(tmp_buf, lower_target_domain, strlen_lower_target_domain) &&
308 (tmp_buf[strlen_lower_target_domain] == VIRTUALDOMAINS_DELIMITER))
309 {
310 return_value = line_num + 1;
311
312 if ((return_entry != NULL) &&
313 (size_return_entry != NULL) &&
314 ((*size_return_entry) > 0))
315 {
316 *size_return_entry = MINVAL(strlen_buf - (strlen_lower_target_domain + 1), *size_return_entry);
317 memcpy(return_entry, tmp_buf + strlen_lower_target_domain + 1, *size_return_entry);
318 return_entry[*size_return_entry] = '\0';
319 }
320
321 break;
322 }
323
324 fscanf(tmp_file, "%*1[\r\n]");
325 line_num++;
326 }
327
328 if (line_num == MAX_FILE_LINES)
329 QRV_LOG_VERBOSE(current_settings, LOG_VERBOSE_FILE_TOO_LONG "%s", MAX_FILE_LINES, search_filename);
330
331 fclose(tmp_file);
332 }
333 else
334 {
335 QRV_LOG_ERROR(current_settings, LOG_ERROR_OPEN_SEARCH "%s: %s", search_filename, strerror(errno));
336 return_value = -1;
337 }
338 }
339
340 return(return_value);
341 }
342
343 /*
344 * start_line and end_line are 1-based.
345 * if end_line is -1, return_content will be realloc()ed as lines are read.
346 * if end_line is not -1, return_content must have at least ((end_line - start_line) + start_index + 1) entries preallocated.
347 * individual entries will always be allocated.
348 *
349 * Return value:
350 * ERROR: -1
351 * SUCCESS: number of lines read, excluding skipped lines (1-based)
352 */
read_file(struct qrv_settings * current_settings,char * target_filename,char *** return_content,int start_index,int start_line,int end_line,int all_lines)353 int read_file(struct qrv_settings *current_settings, char *target_filename, char ***return_content, int start_index, int start_line, int end_line, int all_lines)
354 {
355 int return_value;
356 int i;
357 int line_num;
358 int usable_line_num;
359 int strlen_line;
360 int zero_start;
361 FILE *tmp_file;
362 char file_buf[MAX_FILE_BUF + 1];
363 char **tmp_array;
364 char *tmp_char;
365
366 return_value = 0;
367 line_num = 0;
368 usable_line_num = 0;
369
370 if ((target_filename != NULL) &&
371 (target_filename[0] != '\0') &&
372 (return_content != NULL))
373 {
374 if ((tmp_file = fopen(target_filename, "r")) != NULL)
375 {
376 QRV_LOG_EXCESSIVE(current_settings, LOG_EXCESSIVE_OPEN_FILE, target_filename);
377
378 zero_start = (start_line - 1) - start_index;
379
380 if ((end_line == -1) &&
381 (start_index == 0))
382 *return_content = NULL;
383
384 while (!feof(tmp_file) &&
385 (line_num < MAX_FILE_LINES) &&
386 ((end_line == -1) ||
387 (line_num < end_line)))
388 {
389 if ((fscanf(tmp_file, "%" STRINGIFY(MAX_FILE_BUF) "[^\r\n]", file_buf) == 1) &&
390 ((strlen_line = strlen(file_buf)) || 1) &&
391 (all_lines ||
392 ((file_buf[0] != COMMENT_DELIMITER) &&
393 (strlen_line > 0))) &&
394 (line_num >= zero_start))
395 {
396 QRV_LOG_EXCESSIVE(current_settings, LOG_EXCESSIVE_READ_LINE, strlen_line, target_filename, line_num + 1, file_buf);
397
398 if (end_line == -1)
399 {
400 if ((tmp_array = (char **)realloc(*return_content, sizeof(char *) * ((line_num - zero_start) + 2))) != NULL)
401 {
402 tmp_array[line_num - zero_start] = NULL;
403 tmp_array[(line_num - zero_start) + 1] = NULL;
404 *return_content = tmp_array;
405 }
406 else
407 {
408 QRV_LOG_ERROR(current_settings, LOG_ERROR_MALLOC, sizeof(char *) * ((line_num - zero_start) + 2));
409 return_value = -1;
410 break;
411 }
412 }
413
414 if ((tmp_char = (char *)malloc(sizeof(char) * (strlen_line + 1))) != NULL)
415 {
416 (*return_content)[line_num - zero_start] = tmp_char;
417 memcpy((*return_content)[line_num - zero_start], file_buf, sizeof(char) * strlen_line);
418 (*return_content)[line_num - zero_start][strlen_line] = '\0';
419 }
420 else
421 {
422 QRV_LOG_ERROR(current_settings, LOG_ERROR_MALLOC, sizeof(char) * (strlen_line + 1));
423 return_value = -1;
424 break;
425 }
426
427 usable_line_num++;
428 }
429
430 fscanf(tmp_file, "%*1[\r\n]");
431 line_num++;
432 }
433
434 if (line_num == MAX_FILE_LINES)
435 QRV_LOG_VERBOSE(current_settings, LOG_VERBOSE_FILE_TOO_LONG "%s", MAX_FILE_LINES, target_filename);
436
437 fclose(tmp_file);
438
439 if (return_value == 0)
440 return_value = usable_line_num + 1;
441 else
442 {
443 if ((*return_content) != NULL)
444 {
445 for (i = start_index; i < (line_num - zero_start); i++)
446 if ((*return_content)[i] != NULL)
447 free((*return_content)[i]);
448
449 (*return_content)[start_index] = NULL;
450 }
451
452 if ((end_line == -1) &&
453 (start_index == 0))
454 {
455 free(*return_content);
456 *return_content = NULL;
457 }
458 }
459 }
460 else
461 {
462 QRV_LOG_ERROR(current_settings, LOG_ERROR_OPEN "%s: %s", target_filename, strerror(errno));
463 return_value = -1;
464 }
465 }
466
467 return(return_value);
468 }
469
470 /*
471 * Return value:
472 * ERROR: -1
473 * SUCCESS: length of returned line
474 */
read_file_first_line(struct qrv_settings * current_settings,char * target_filename,char ** return_content)475 int read_file_first_line(struct qrv_settings *current_settings, char *target_filename, char **return_content)
476 {
477 int return_value;
478 char *tmp_array[2];
479 char **tmp_ptr;
480
481 return_value = 0;
482
483 tmp_array[0] = NULL;
484 tmp_array[1] = NULL;
485
486 /*
487 * Without this, read_file() crashes on the statement (*return_content)[0] = tmp_char;
488 * Why?
489 */
490 tmp_ptr = tmp_array;
491
492 if ((return_content != NULL) &&
493 (read_file(current_settings, target_filename, (char ***)&tmp_ptr, 0, 1, 1, 0) != -1))
494 {
495 *return_content = tmp_array[0];
496 return_value = (tmp_array[0] != NULL) ? strlen(tmp_array[0]) : 0;
497 }
498
499 return(return_value);
500 }
501
502 /*
503 * Expects:
504 * strlen_username is the length of the username if not NULL-terminated, or -1 if NULL-terminated
505 * return_address is a preallocated buffer
506 * max_return_address is the size of return_address, >= 0
507 *
508 * Return value:
509 * return_address, filled with the reassembled address OR missing_data if the address is empty
510 */
reassemble_address(char * target_username,int strlen_username,char * target_domain,char * missing_data,char * return_address,int max_return_address,int * strlen_return_address)511 char *reassemble_address(char *target_username, int strlen_username, char *target_domain, char *missing_data, char *return_address, int max_return_address, int *strlen_return_address)
512 {
513 int tmp_strlen;
514
515 tmp_strlen = 0;
516
517 if ((return_address != NULL) &&
518 (max_return_address >= 0))
519 {
520 if ((target_username != NULL) &&
521 (target_username[0] != '\0'))
522 if ((target_domain != NULL) &&
523 (target_domain[0] != '\0'))
524 {
525 if (strlen_username == -1)
526 tmp_strlen = SNPRINTF(return_address, max_return_address, "%s@%s", target_username, target_domain);
527 else
528 tmp_strlen = SNPRINTF(return_address, max_return_address, "%.*s@%s", strlen_username, target_username, target_domain);
529 }
530 else
531 if (strlen_username == -1)
532 tmp_strlen = SNPRINTF(return_address, max_return_address, "%s", target_username);
533 else
534 tmp_strlen = SNPRINTF(return_address, max_return_address, "%.*s", strlen_username, target_username);
535 else if ((target_domain != NULL) &&
536 (target_domain[0] != '\0'))
537 tmp_strlen = SNPRINTF(return_address, max_return_address, "@%s", target_domain);
538 else if (missing_data != NULL)
539 tmp_strlen = SNPRINTF(return_address, max_return_address, "%s", missing_data);
540 else
541 return_address[0] = '\0';
542 }
543
544 if (strlen_return_address != NULL)
545 *strlen_return_address = tmp_strlen;
546
547 return(return_address);
548 }
549
550 /*
551 * EXPECTS:
552 * type_flag should be a value within the S_IFMT mask in stat.h -- S_IFDIR, S_IFREG, etc
553 * permission_flags should be an OR of FILE_PERMISSION_READ, FILE_PERMISSION_WRITE or FILE_PERMISSION_EXECUTE, not bitshifted for user/group/other.
554 * if target_uid is -1, geteuid() will be used
555 * if target_gid is -1, getegid() and secondary groups will be used
556 *
557 * RETURNS:
558 * -1: error
559 * 0: file does not exist, is the wrong type or does not have permissions
560 * 1: file exists, is the correct type and has the permissions
561 */
check_path_perms(struct qrv_settings * current_settings,char * target_path,int type_flag,int permission_flags,struct stat * target_stat,int target_uid,int target_gid)562 int check_path_perms(struct qrv_settings *current_settings, char *target_path, int type_flag, int permission_flags, struct stat *target_stat, int target_uid, int target_gid)
563 {
564 int return_value;
565 int i;
566 struct stat *tmp_stat;
567 struct stat internal_stat;
568 int found_match;
569 int num_groups;
570 gid_t *tmp_gid;
571
572 return_value = 0;
573 tmp_gid = NULL;
574
575 if (target_path != NULL)
576 {
577 tmp_stat = (target_stat != NULL) ? target_stat : &internal_stat;
578 if (!stat(target_path, tmp_stat))
579 {
580 QRV_LOG_EXCESSIVE(current_settings, LOG_EXCESSIVE_FILE_STAT, tmp_stat->st_mode, type_flag, tmp_stat->st_uid, tmp_stat->st_gid, target_path);
581
582 if (((type_flag == 0) ||
583 ((tmp_stat->st_mode & S_IFMT) == type_flag)) &&
584 (((permission_flags & FILE_PERMISSION_SETUID) == 0) ||
585 (tmp_stat->st_mode & S_ISUID)) &&
586 (((permission_flags & FILE_PERMISSION_SETGID) == 0) ||
587 (tmp_stat->st_mode & S_ISGID)) &&
588 (((permission_flags & FILE_PERMISSION_STICKY) == 0) ||
589 (tmp_stat->st_mode & S_ISVTX)))
590 {
591 if ((permission_flags & FILE_PERMISSION_READ) ||
592 (permission_flags & FILE_PERMISSION_WRITE) ||
593 (permission_flags & FILE_PERMISSION_EXECUTE))
594 {
595 if (((target_uid != -1) &&
596 (tmp_stat->st_uid == target_uid)) ||
597 ((target_uid == -1) &&
598 (tmp_stat->st_uid == geteuid())))
599 {
600 if ((((permission_flags & FILE_PERMISSION_READ) == 0) ||
601 (tmp_stat->st_mode & S_IRUSR)) &&
602 (((permission_flags & FILE_PERMISSION_WRITE) == 0) ||
603 (tmp_stat->st_mode & S_IWUSR)) &&
604 (((permission_flags & FILE_PERMISSION_EXECUTE) == 0) ||
605 (tmp_stat->st_mode & S_IXUSR)))
606 return_value = 1;
607 }
608 else
609 {
610 found_match = 0;
611
612 if (target_gid != -1)
613 {
614 if (tmp_stat->st_gid == target_gid)
615 found_match = 1;
616 }
617 else if (tmp_stat->st_gid == getegid())
618 found_match = 1;
619 else if ((num_groups = getgroups(0, NULL)) > 0)
620 {
621 if ((tmp_gid = (gid_t *)malloc(sizeof(gid_t) * num_groups)) != NULL)
622 {
623 if (getgroups(num_groups, tmp_gid) > 0)
624 for (i = 0; i < num_groups; i++)
625 if (tmp_stat->st_gid == tmp_gid[i])
626 {
627 /* Secondary group owns the directory. */
628 found_match = 1;
629 break;
630 }
631
632 free(tmp_gid);
633 tmp_gid = NULL;
634 }
635 else
636 {
637 QRV_LOG_ERROR(current_settings, LOG_ERROR_MALLOC, (long)(sizeof(gid_t) * num_groups));
638 return_value = -1;
639 }
640 }
641
642 if (found_match)
643 {
644 if ((((permission_flags & FILE_PERMISSION_READ) == 0) ||
645 (tmp_stat->st_mode & S_IRGRP)) &&
646 (((permission_flags & FILE_PERMISSION_WRITE) == 0) ||
647 (tmp_stat->st_mode & S_IWGRP)) &&
648 (((permission_flags & FILE_PERMISSION_EXECUTE) == 0) ||
649 (tmp_stat->st_mode & S_IXGRP)))
650 return_value = 1;
651 }
652 else
653 if ((((permission_flags & FILE_PERMISSION_READ) == 0) ||
654 (tmp_stat->st_mode & S_IROTH)) &&
655 (((permission_flags & FILE_PERMISSION_WRITE) == 0) ||
656 (tmp_stat->st_mode & S_IWOTH)) &&
657 (((permission_flags & FILE_PERMISSION_EXECUTE) == 0) ||
658 (tmp_stat->st_mode & S_IXOTH)))
659 return_value = 1;
660 }
661 }
662 else
663 return_value = 1;
664 }
665 }
666 else if ((errno == ENOENT) ||
667 (errno == ENOTDIR))
668 {
669 QRV_LOG_EXCESSIVE(current_settings, LOG_EXCESSIVE_FILE_STAT_FAIL, target_path, strerror(errno));
670 return_value = 0;
671 }
672 else
673 {
674 QRV_LOG_ERROR(current_settings, LOG_ERROR_STAT_ERRNO, target_path, strerror(errno));
675 return_value = -1;
676 }
677 }
678
679 return(return_value);
680 }
681
682 /*
683 * Return value:
684 * NOT FOUND: 0
685 * FOUND: 1
686 */
find_path(struct qrv_settings * current_settings,char * filename,char * return_filename,int size_return_filename)687 int find_path(struct qrv_settings *current_settings, char *filename, char *return_filename, int size_return_filename)
688 {
689 int return_value;
690 int strlen_filename;
691 char *tmp_start;
692 char *tmp_end;
693 char new_filename[MAX_BUF + 1];
694 struct stat tmp_stat;
695
696 return_value = 0;
697
698 if ((filename != NULL) &&
699 (return_filename != NULL) &&
700 (size_return_filename > 0))
701 {
702 if (stat(filename, &tmp_stat) == 0)
703 {
704 strlen_filename = MINVAL(size_return_filename, strlen(filename));
705 memcpy(return_filename, filename, sizeof(char) * strlen_filename);
706 return_filename[strlen_filename] = '\0';
707
708 return_value = 1;
709 }
710 else if (strchr(filename, DIR_DELIMITER) == NULL)
711 {
712 tmp_start = current_settings->path;
713 tmp_end = NULL;
714 while (tmp_start != NULL)
715 {
716 if ((tmp_end = strchr(tmp_start, ENVIRONMENT_SEPARATOR)) != NULL)
717 {
718 strlen_filename = SNPRINTF(new_filename, MAX_BUF, "%.*s" DIR_DELIMITER_STR "%s", (int)(tmp_end - tmp_start), tmp_start, filename);
719 tmp_start = tmp_end + 1;
720 }
721 else
722 {
723 strlen_filename = SNPRINTF(new_filename, MAX_BUF, "%s" DIR_DELIMITER_STR "%s", tmp_start, filename);
724 tmp_start = NULL;
725 }
726
727 if (stat(new_filename, &tmp_stat) == 0)
728 {
729 if (strlen_filename > size_return_filename)
730 strlen_filename = size_return_filename;
731
732 memcpy(return_filename, new_filename, sizeof(char) * strlen_filename);
733 return_filename[strlen_filename] = '\0';
734
735 return_value = 1;
736 break;
737 }
738 }
739 }
740 }
741
742 return(return_value);
743 }
744
745 /*
746 * EXPECTS:
747 * type_flag should be a value within the S_IFMT mask in stat.h -- S_IFDIR, S_IFREG, etc
748 * permission_flags should be an OR of FILE_PERMISSION_READ, FILE_PERMISSION_WRITE or FILE_PERMISSION_EXECUTE, not bitshifted for user/group/other.
749 *
750 * RETURNS:
751 * -1: error
752 * 0: file does not exist, is the wrong type or does not have permissions
753 * 1: file exists, is the correct type and has the permissions
754 */
find_path_perms(struct qrv_settings * current_settings,char * target_path,int type_flag,int permission_flags,int target_uid,int target_gid)755 int find_path_perms(struct qrv_settings *current_settings, char *target_path, int type_flag, int permission_flags, int target_uid, int target_gid)
756 {
757 int return_value;
758 char tmp_cwd[MAX_PATH + 1];
759 char tmp_path[MAX_PATH + 1];
760
761 return_value = 0;
762
763 if (target_path != NULL)
764 {
765 if (target_path[0] == DIR_DELIMITER)
766 return_value = check_path_perms(current_settings, target_path, type_flag, permission_flags, NULL, target_uid, target_gid);
767 else
768 {
769 if (strchr(target_path, DIR_DELIMITER) != NULL)
770 {
771 if (getcwd(tmp_cwd, MAX_PATH) != NULL)
772 {
773 snprintf(tmp_path, MAX_PATH, "%s%c%s", tmp_cwd, DIR_DELIMITER, target_path);
774 return_value = check_path_perms(current_settings, tmp_path, type_flag, permission_flags, NULL, target_uid, target_gid);
775 }
776 else
777 {
778 QRV_LOG_ERROR(current_settings, LOG_ERROR_GETCWD, strerror(errno));
779 return_value = -1;
780 }
781 }
782 else if (find_path(current_settings, target_path, tmp_path, MAX_PATH))
783 return_value = check_path_perms(current_settings, tmp_path, type_flag, permission_flags, NULL, target_uid, target_gid);
784 }
785 }
786
787 return(return_value);
788 }
789