Branch data Line data Source code
1 : : /*
2 : : ******************************************************************************
3 : : *
4 : : * File: access.c
5 : : *
6 : : * Purpose: Access.conf file processing for fwknop server.
7 : : *
8 : : * Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
9 : : * Copyright (C) 2009-2014 fwknop developers and contributors. For a full
10 : : * list of contributors, see the file 'CREDITS'.
11 : : *
12 : : * License (GNU General Public License):
13 : : *
14 : : * This program is free software; you can redistribute it and/or
15 : : * modify it under the terms of the GNU General Public License
16 : : * as published by the Free Software Foundation; either version 2
17 : : * of the License, or (at your option) any later version.
18 : : *
19 : : * This program is distributed in the hope that it will be useful,
20 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 : : * GNU General Public License for more details.
23 : : *
24 : : * You should have received a copy of the GNU General Public License
25 : : * along with this program; if not, write to the Free Software
26 : : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 : : * USA
28 : : *
29 : : ******************************************************************************
30 : : */
31 : : #include <sys/stat.h>
32 : :
33 : : #if HAVE_SYS_SOCKET_H
34 : : #include <sys/socket.h>
35 : : #endif
36 : :
37 : : #include "fwknopd_common.h"
38 : : #include <arpa/inet.h>
39 : : #include "pwd.h"
40 : : #include "access.h"
41 : : #include "utils.h"
42 : : #include "log_msg.h"
43 : :
44 : : #define FATAL_ERR -1
45 : :
46 : : #ifndef SUCCESS
47 : : #define SUCCESS 1
48 : : #endif
49 : :
50 : : #ifdef HAVE_C_UNIT_TESTS
51 : : #include "cunit_common.h"
52 : : DECLARE_TEST_SUITE(access, "Access test suite");
53 : : #endif
54 : :
55 : : /* Add an access string entry
56 : : */
57 : : static int
58 : 60353 : add_acc_string(char **var, const char *val)
59 : : {
60 [ - + ]: 60353 : if(var == NULL)
61 : : {
62 : 0 : log_msg(LOG_ERR, "[*] add_acc_string() called with NULL variable");
63 : 0 : return FATAL_ERR;
64 : : }
65 : :
66 [ + + ]: 60353 : if(*var != NULL)
67 : 8674 : free(*var);
68 : :
69 [ - + ]: 60353 : if((*var = strdup(val)) == NULL)
70 : : {
71 : 0 : log_msg(LOG_ERR,
72 : : "[*] Fatal memory allocation error adding access list entry: %s", *var
73 : : );
74 : 0 : return FATAL_ERR;
75 : : }
76 : : return SUCCESS;
77 : : }
78 : :
79 : : /* Decode base64 encoded string into access entry
80 : : */
81 : : static int
82 : 0 : add_acc_b64_string(char **var, int *len, const char *val)
83 : : {
84 [ # # ]: 0 : if((*var = strdup(val)) == NULL)
85 : : {
86 : 0 : log_msg(LOG_ERR,
87 : : "[*] Fatal memory allocation error adding access list entry: %s", *var
88 : : );
89 : 0 : return FATAL_ERR;
90 : : }
91 : 0 : memset(*var, 0x0, strlen(val));
92 : 0 : *len = fko_base64_decode(val, (unsigned char *) *var);
93 : :
94 [ # # ]: 0 : if (*len < 0)
95 : : {
96 : 0 : log_msg(LOG_ERR,
97 : : "[*] base64 decoding returned error for: %s", *var
98 : : );
99 : 0 : return FATAL_ERR;
100 : : }
101 : : return SUCCESS;
102 : : }
103 : :
104 : : /* Add an access bool entry (unsigned char of 1 or 0)
105 : : */
106 : : static unsigned char
107 : 31731 : add_acc_bool(unsigned char *var, const char *val)
108 : : {
109 : 31731 : return(*var = (strncasecmp(val, "Y", 1) == 0) ? 1 : 0);
110 : : }
111 : :
112 : : /* Add expiration time - convert date to epoch seconds
113 : : */
114 : : static int
115 : 1490 : add_acc_expire_time(fko_srv_options_t *opts, time_t *access_expire_time, const char *val)
116 : : {
117 : : struct tm tm;
118 : :
119 : : memset(&tm, 0, sizeof(struct tm));
120 : :
121 [ + + ]: 745 : if (sscanf(val, "%2d/%2d/%4d", &tm.tm_mon, &tm.tm_mday, &tm.tm_year) != 3)
122 : : {
123 : :
124 : 10 : log_msg(LOG_ERR,
125 : : "[*] Fatal: invalid date value '%s' (need MM/DD/YYYY) for access stanza expiration time",
126 : : val
127 : : );
128 : : return FATAL_ERR;
129 : : }
130 : :
131 [ + + ]: 735 : if(tm.tm_mon > 0)
132 : 604 : tm.tm_mon -= 1; /* 0-11 */
133 : :
134 : : /* number of years since 1900
135 : : */
136 [ + + ]: 735 : if(tm.tm_year > 1900)
137 : 300 : tm.tm_year -= 1900;
138 : : else
139 [ + + ]: 435 : if(tm.tm_year < 100)
140 : 344 : tm.tm_year += 100;
141 : :
142 : 735 : *access_expire_time = mktime(&tm);
143 : :
144 : : return 1;
145 : : }
146 : :
147 : : /* Add expiration time via epoch seconds defined in access.conf
148 : : */
149 : : static int
150 : 0 : add_acc_expire_time_epoch(fko_srv_options_t *opts, time_t *access_expire_time, const char *val)
151 : : {
152 : : char *endptr;
153 : 0 : unsigned long expire_time = 0;
154 : :
155 : 0 : errno = 0;
156 : :
157 : 0 : expire_time = (time_t) strtoul(val, &endptr, 10);
158 : :
159 [ # # ][ # # ]: 0 : if (errno == ERANGE || (errno != 0 && expire_time == 0))
[ # # ]
160 : : {
161 : 0 : log_msg(LOG_ERR,
162 : : "[*] Fatal: invalid epoch seconds value '%s' for access stanza expiration time",
163 : : val
164 : : );
165 : : return FATAL_ERR;
166 : : }
167 : :
168 : 0 : *access_expire_time = (time_t) expire_time;
169 : :
170 : : return 1;
171 : : }
172 : :
173 : : #if defined(FIREWALL_FIREWALLD) || defined(FIREWALL_IPTABLES)
174 : : static int
175 : 0 : add_acc_force_nat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, const char *val)
176 : : {
177 : 0 : char ip_str[MAX_IPV4_STR_LEN] = {0};
178 : :
179 [ # # ]: 0 : if (sscanf(val, "%15s %5u", ip_str, &curr_acc->force_nat_port) != 2)
180 : : {
181 : 0 : log_msg(LOG_ERR,
182 : : "[*] Fatal: invalid FORCE_NAT arg '%s', need <IP> <PORT>",
183 : : val
184 : : );
185 : : return FATAL_ERR;
186 : : }
187 : :
188 [ # # ]: 0 : if (curr_acc->force_nat_port > MAX_PORT)
189 : : {
190 : 0 : log_msg(LOG_ERR,
191 : : "[*] Fatal: invalid FORCE_NAT port '%d'", curr_acc->force_nat_port);
192 : : return FATAL_ERR;
193 : : }
194 : :
195 [ # # ]: 0 : if(! is_valid_ipv4_addr(ip_str))
196 : : {
197 : 0 : log_msg(LOG_ERR,
198 : : "[*] Fatal: invalid FORCE_NAT IP '%s'", ip_str);
199 : : return FATAL_ERR;
200 : : }
201 : :
202 : 0 : curr_acc->force_nat = 1;
203 : :
204 : 0 : return add_acc_string(&(curr_acc->force_nat_ip), ip_str);
205 : : }
206 : :
207 : : static int
208 : 0 : add_acc_force_snat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, const char *val)
209 : : {
210 : 0 : char ip_str[MAX_IPV4_STR_LEN] = {0};
211 : :
212 [ # # ]: 0 : if (sscanf(val, "%15s", ip_str) != 1)
213 : : {
214 : 0 : log_msg(LOG_ERR,
215 : : "[*] Fatal: invalid FORCE_SNAT arg '%s', need <IP>", val);
216 : : return FATAL_ERR;
217 : : }
218 : :
219 [ # # ]: 0 : if(! is_valid_ipv4_addr(ip_str))
220 : : {
221 : 0 : log_msg(LOG_ERR,
222 : : "[*] Fatal: invalid FORCE_SNAT IP '%s'", ip_str);
223 : : return FATAL_ERR;
224 : : }
225 : :
226 : 0 : curr_acc->force_snat = 1;
227 : :
228 : 0 : return add_acc_string(&(curr_acc->force_snat_ip), ip_str);
229 : : }
230 : :
231 : : #endif
232 : :
233 : : /* Take an IP or Subnet/Mask and convert it to mask for later
234 : : * comparisons of incoming source IPs against this mask.
235 : : */
236 : : static int
237 : 19680 : add_int_ent(acc_int_list_t **ilist, const char *ip)
238 : : {
239 : : char *ndx;
240 : 19680 : char ip_str[MAX_IPV4_STR_LEN] = {0};
241 : 19680 : char ip_mask_str[MAX_IPV4_STR_LEN] = {0};
242 : : uint32_t mask;
243 : 19680 : int is_err, mask_len = 0, need_shift = 1;
244 : :
245 : : struct in_addr in;
246 : : struct in_addr mask_in;
247 : :
248 : : acc_int_list_t *last_sle, *new_sle, *tmp_sle;
249 : :
250 [ - + ]: 19680 : if((new_sle = calloc(1, sizeof(acc_int_list_t))) == NULL)
251 : : {
252 : 0 : log_msg(LOG_ERR,
253 : : "[*] Fatal memory allocation error adding stanza source_list entry"
254 : : );
255 : 0 : exit(EXIT_FAILURE);
256 : : }
257 : :
258 : : /* Convert the IP data into the appropriate IP + (optional) mask
259 : : */
260 [ + + ]: 19680 : if(strcasecmp(ip, "ANY") == 0)
261 : : {
262 : 1285 : new_sle->maddr = 0x0;
263 : 1285 : new_sle->mask = 0x0;
264 : : }
265 : : else
266 : : {
267 : : /* See if we have a subnet component. If so pull out the IP and
268 : : * mask values, then create the final mask value.
269 : : */
270 [ + + ]: 18395 : if((ndx = strchr(ip, '/')) != NULL)
271 : : {
272 [ + + ]: 1616 : if(((ndx-ip)) >= MAX_IPV4_STR_LEN)
273 : : {
274 : 1 : log_msg(LOG_ERR, "[*] Error parsing string to IP");
275 : 1 : free(new_sle);
276 : 1 : new_sle = NULL;
277 : 1 : return 0;
278 : : }
279 : :
280 : 1615 : mask_len = strlen(ip) - (ndx-ip+1);
281 : :
282 [ + + ]: 1615 : if(mask_len > 2)
283 : : {
284 [ + + ]: 1505 : if(mask_len >= MIN_IPV4_STR_LEN && mask_len < MAX_IPV4_STR_LEN)
285 : : {
286 : : /* IP formatted mask
287 : : */
288 : 1499 : strlcpy(ip_mask_str, (ip + (ndx-ip) + 1), mask_len+1);
289 [ + + ]: 1499 : if(inet_aton(ip_mask_str, &mask_in) == 0)
290 : : {
291 : 3 : log_msg(LOG_ERR,
292 : : "[*] Fatal error parsing IP mask to int for: %s", ip_mask_str
293 : : );
294 : 3 : free(new_sle);
295 : 3 : new_sle = NULL;
296 : 3 : return 0;
297 : : }
298 : 1496 : mask = ntohl(mask_in.s_addr);
299 : 1496 : need_shift = 0;
300 : : }
301 : : else
302 : : {
303 : 6 : log_msg(LOG_ERR, "[*] Invalid IP mask str '%s'.", ndx+1);
304 : 6 : free(new_sle);
305 : 6 : new_sle = NULL;
306 : 6 : return 0;
307 : : }
308 : : }
309 : : else
310 : : {
311 [ + + ]: 110 : if(mask_len > 0)
312 : : {
313 : : /* CIDR mask
314 : : */
315 : 109 : mask = strtol_wrapper(ndx+1, 1, 32, NO_EXIT_UPON_ERR, &is_err);
316 [ + + ]: 109 : if(is_err != FKO_SUCCESS)
317 : : {
318 : 4 : log_msg(LOG_ERR, "[*] Invalid IP mask str '%s'.", ndx+1);
319 : 4 : free(new_sle);
320 : 4 : new_sle = NULL;
321 : 4 : return 0;
322 : : }
323 : : }
324 : : else
325 : : {
326 : 1 : log_msg(LOG_ERR, "[*] Missing mask value.");
327 : 1 : free(new_sle);
328 : 1 : new_sle = NULL;
329 : 1 : return 0;
330 : : }
331 : : }
332 : :
333 : 1601 : strlcpy(ip_str, ip, (ndx-ip)+1);
334 : : }
335 : : else
336 : : {
337 : 16779 : mask = 32;
338 [ + + ]: 16779 : if(strnlen(ip, MAX_IPV4_STR_LEN+1) >= MAX_IPV4_STR_LEN)
339 : : {
340 : 12 : log_msg(LOG_ERR, "[*] Error parsing string to IP");
341 : 12 : free(new_sle);
342 : 12 : new_sle = NULL;
343 : 12 : return 0;
344 : : }
345 : 16767 : strlcpy(ip_str, ip, sizeof(ip_str));
346 : : }
347 : :
348 [ + + ]: 18368 : if(inet_aton(ip_str, &in) == 0)
349 : : {
350 : 121 : log_msg(LOG_ERR,
351 : : "[*] Fatal error parsing IP to int for: %s", ip_str
352 : : );
353 : :
354 : 121 : free(new_sle);
355 : 121 : new_sle = NULL;
356 : :
357 : 121 : return 0;
358 : : }
359 : :
360 : : /* Store our mask converted from CIDR to a 32-bit value.
361 : : */
362 [ + + ]: 18247 : if(mask == 32)
363 : 16651 : new_sle->mask = 0xFFFFFFFF;
364 [ + + ][ + - ]: 1596 : else if(need_shift && (mask > 0 && mask < 32))
365 : 104 : new_sle->mask = (0xFFFFFFFF << (32 - mask));
366 : : else
367 : 1492 : new_sle->mask = mask;
368 : :
369 : : /* Store our masked address for comparisons with future incoming
370 : : * packets.
371 : : */
372 : 18247 : new_sle->maddr = ntohl(in.s_addr) & new_sle->mask;
373 : : }
374 : :
375 : : /* If this is not the first entry, we walk our pointer to the
376 : : * end of the list.
377 : : */
378 [ + + ]: 19532 : if(*ilist == NULL)
379 : : {
380 : 19532 : *ilist = new_sle;
381 : : }
382 : : else
383 : : {
384 : : tmp_sle = *ilist;
385 : :
386 : : do {
387 : 2784 : last_sle = tmp_sle;
388 [ + + ]: 2784 : } while((tmp_sle = tmp_sle->next));
389 : :
390 : 980 : last_sle->next = new_sle;
391 : : }
392 : :
393 : : return 1;
394 : : }
395 : :
396 : : /* Expand the access SOURCE string to a list of masks.
397 : : */
398 : : static int
399 : 18645 : expand_acc_int_list(acc_int_list_t **ilist, char *ip)
400 : : {
401 : : char *ndx, *start;
402 : 18645 : char buf[ACCESS_BUF_LEN] = {0};
403 : 18645 : int res = 1;
404 : :
405 : 18645 : start = ip;
406 : :
407 [ + + ]: 212089 : for(ndx = start; *ndx; ndx++)
408 : : {
409 [ + + ]: 193457 : if(*ndx == ',')
410 : : {
411 : : /* Skip over any leading whitespace.
412 : : */
413 [ + + ]: 5171 : while(isspace(*start))
414 : 4118 : start++;
415 : :
416 [ + + ]: 1053 : if(((ndx-start)+1) >= ACCESS_BUF_LEN)
417 : : return 0;
418 : :
419 : 1052 : strlcpy(buf, start, (ndx-start)+1);
420 : :
421 : 1052 : res = add_int_ent(ilist, buf);
422 [ + + ]: 1052 : if(res == 0)
423 : 12 : return res;
424 : :
425 : 1040 : start = ndx+1;
426 : : }
427 : : }
428 : :
429 : : /* Skip over any leading whitespace (once again for the last in the list).
430 : : */
431 [ + + ]: 18833 : while(isspace(*start))
432 : 201 : start++;
433 : :
434 [ + + ]: 18632 : if(((ndx-start)+1) >= ACCESS_BUF_LEN)
435 : : return 0;
436 : :
437 : 18628 : strlcpy(buf, start, (ndx-start)+1);
438 : :
439 : 18628 : res = add_int_ent(ilist, buf);
440 : :
441 : 18628 : return res;
442 : : }
443 : :
444 : : static int
445 : 80 : parse_proto_and_port(char *pstr, int *proto, int *port)
446 : : {
447 : : char *ndx;
448 : 80 : char proto_str[ACCESS_BUF_LEN] = {0};
449 : : int is_err;
450 : :
451 : : /* Parse the string into its components.
452 : : */
453 [ + + ]: 80 : if((ndx = strchr(pstr, '/')) == NULL)
454 : : {
455 : 36 : log_msg(LOG_ERR,
456 : : "[*] Parse error on access port entry: %s", pstr);
457 : :
458 : 36 : return(-1);
459 : : }
460 : :
461 [ - + ]: 44 : if(((ndx - pstr)+1) >= ACCESS_BUF_LEN)
462 : : {
463 : 0 : log_msg(LOG_ERR,
464 : : "[*] Parse error on access port entry: %s", pstr);
465 : 0 : return(-1);
466 : : }
467 : :
468 : 44 : strlcpy(proto_str, pstr, (ndx - pstr)+1);
469 : :
470 : 44 : *port = strtol_wrapper(ndx+1, 0, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
471 [ + + ]: 44 : if(is_err != FKO_SUCCESS)
472 : : {
473 : 1 : log_msg(LOG_ERR,
474 : : "[*] Invalid port '%s' in access request, must be in [%d,%d]",
475 : : pstr, 0, MAX_PORT);
476 : 1 : return(-1);
477 : : }
478 : :
479 [ - + ]: 43 : if(strcasecmp(proto_str, "tcp") == 0)
480 : 0 : *proto = PROTO_TCP;
481 [ - + ]: 43 : else if(strcasecmp(proto_str, "udp") == 0)
482 : 0 : *proto = PROTO_UDP;
483 : : else
484 : : {
485 : 43 : log_msg(LOG_ERR,
486 : : "[*] Invalid protocol in access port entry: %s", pstr);
487 : 43 : return(-1);
488 : : }
489 : :
490 : : return(0);
491 : : }
492 : :
493 : : /* Take a proto/port string and convert it to appropriate integer values
494 : : * for comparisons of incoming SPA requests.
495 : : */
496 : : static int
497 : 80 : add_port_list_ent(acc_port_list_t **plist, char *port_str)
498 : : {
499 : : int proto_int, port;
500 : :
501 : : acc_port_list_t *last_plist, *new_plist, *tmp_plist;
502 : :
503 : : /* Parse the string into its components and continue only if there
504 : : * are no problems with the incoming string.
505 : : */
506 [ - + ]: 80 : if(parse_proto_and_port(port_str, &proto_int, &port) != 0)
507 : : return 0;
508 : :
509 [ # # ]: 0 : if((new_plist = calloc(1, sizeof(acc_port_list_t))) == NULL)
510 : : {
511 : 0 : log_msg(LOG_ERR,
512 : : "[*] Fatal memory allocation error adding stanza port_list entry"
513 : : );
514 : 0 : exit(EXIT_FAILURE);
515 : : }
516 : :
517 : : /* If this is not the first entry, we walk our pointer to the
518 : : * end of the list.
519 : : */
520 [ # # ]: 0 : if(*plist == NULL)
521 : : {
522 : 0 : *plist = new_plist;
523 : : }
524 : : else
525 : : {
526 : : tmp_plist = *plist;
527 : :
528 : : do {
529 : 0 : last_plist = tmp_plist;
530 [ # # ]: 0 : } while((tmp_plist = tmp_plist->next));
531 : :
532 : 0 : last_plist->next = new_plist;
533 : : }
534 : :
535 : 0 : new_plist->proto = proto_int;
536 : 0 : new_plist->port = port;
537 : :
538 : 0 : return 1;
539 : : }
540 : :
541 : : /* Add a string list entry to the given acc_string_list.
542 : : */
543 : : static int
544 : 0 : add_string_list_ent(acc_string_list_t **stlist, const char *str_str)
545 : : {
546 : : acc_string_list_t *last_stlist, *new_stlist, *tmp_stlist;
547 : :
548 [ # # ]: 0 : if((new_stlist = calloc(1, sizeof(acc_string_list_t))) == NULL)
549 : : {
550 : 0 : log_msg(LOG_ERR,
551 : : "[*] Fatal memory allocation error creating string list entry"
552 : : );
553 : 0 : return FATAL_ERR;
554 : : }
555 : :
556 : : /* If this is not the first entry, we walk our pointer to the
557 : : * end of the list.
558 : : */
559 [ # # ]: 0 : if(*stlist == NULL)
560 : : {
561 : 0 : *stlist = new_stlist;
562 : : }
563 : : else
564 : : {
565 : : tmp_stlist = *stlist;
566 : :
567 : : do {
568 : 0 : last_stlist = tmp_stlist;
569 [ # # ]: 0 : } while((tmp_stlist = tmp_stlist->next));
570 : :
571 : 0 : last_stlist->next = new_stlist;
572 : : }
573 : :
574 [ # # ]: 0 : if(new_stlist->str != NULL)
575 : 0 : free(new_stlist->str);
576 : :
577 : 0 : new_stlist->str = strdup(str_str);
578 : :
579 [ # # ]: 0 : if(new_stlist->str == NULL)
580 : : {
581 : 0 : log_msg(LOG_ERR,
582 : : "[*] Fatal memory allocation error adding string list entry item"
583 : : );
584 : 0 : return FATAL_ERR;
585 : : }
586 : : return SUCCESS;
587 : : }
588 : :
589 : : /* Expand a proto/port access string to a list of access proto-port struct.
590 : : */
591 : : int
592 : 83 : expand_acc_port_list(acc_port_list_t **plist, char *plist_str)
593 : : {
594 : : char *ndx, *start;
595 : 83 : char buf[ACCESS_BUF_LEN] = {0};
596 : :
597 : 83 : start = plist_str;
598 : :
599 [ + + ]: 628 : for(ndx = start; *ndx != '\0'; ndx++)
600 : : {
601 [ + + ]: 547 : if(*ndx == ',')
602 : : {
603 : : /* Skip over any leading whitespace.
604 : : */
605 [ - + ]: 2 : while(isspace(*start))
606 : 0 : start++;
607 : :
608 [ + + ]: 2 : if(((ndx-start)+1) >= ACCESS_BUF_LEN)
609 : : return 0;
610 : :
611 : 1 : strlcpy(buf, start, (ndx-start)+1);
612 : :
613 [ - + ]: 1 : if(add_port_list_ent(plist, buf) == 0)
614 : : return 0;
615 : :
616 : 0 : start = ndx+1;
617 : : }
618 : : }
619 : :
620 : : /* Skip over any leading whitespace (once again for the last in the list).
621 : : */
622 [ - + ]: 81 : while(isspace(*start))
623 : 0 : start++;
624 : :
625 [ + + ]: 81 : if(((ndx-start)+1) >= ACCESS_BUF_LEN)
626 : : return 0;
627 : :
628 : 79 : strlcpy(buf, start, (ndx-start)+1);
629 : :
630 [ - + ]: 79 : if(add_port_list_ent(plist, buf) == 0)
631 : : return 0;
632 : :
633 : 0 : return 1;
634 : : }
635 : :
636 : : /* Expand a comma-separated string into a simple acc_string_list.
637 : : */
638 : : static int
639 : 0 : expand_acc_string_list(acc_string_list_t **stlist, char *stlist_str)
640 : : {
641 : : char *ndx, *start;
642 : 0 : char buf[MAX_LINE_LEN] = {0};
643 : :
644 : 0 : start = stlist_str;
645 : :
646 [ # # ]: 0 : for(ndx = start; *ndx; ndx++)
647 : : {
648 [ # # ]: 0 : if(*ndx == ',')
649 : : {
650 : : /* Skip over any leading whitespace.
651 : : */
652 [ # # ]: 0 : while(isspace(*start))
653 : 0 : start++;
654 : :
655 [ # # ]: 0 : if(((ndx-start)+1) >= MAX_LINE_LEN)
656 : : return FATAL_ERR;
657 : :
658 : 0 : strlcpy(buf, start, (ndx-start)+1);
659 [ # # ]: 0 : if(add_string_list_ent(stlist, buf) != SUCCESS)
660 : : return FATAL_ERR;
661 : :
662 : 0 : start = ndx+1;
663 : : }
664 : : }
665 : :
666 : : /* Skip over any leading whitespace (once again for the last in the list).
667 : : */
668 [ # # ]: 0 : while(isspace(*start))
669 : 0 : start++;
670 : :
671 [ # # ]: 0 : if(((ndx-start)+1) >= MAX_LINE_LEN)
672 : : return FATAL_ERR;
673 : :
674 : 0 : strlcpy(buf, start, (ndx-start)+1);
675 : :
676 [ # # ]: 0 : if(add_string_list_ent(stlist, buf) != SUCCESS)
677 : : return FATAL_ERR;
678 : :
679 : 0 : return SUCCESS;
680 : : }
681 : :
682 : : /* Free the acc source_list
683 : : */
684 : : static void
685 : 23951 : free_acc_int_list(acc_int_list_t *sle)
686 : : {
687 : : acc_int_list_t *last_sle;
688 : :
689 [ + + ]: 43483 : while(sle != NULL)
690 : : {
691 : 19532 : last_sle = sle;
692 : 19532 : sle = last_sle->next;
693 : :
694 : 19532 : free(last_sle);
695 : : }
696 : 23951 : }
697 : :
698 : : /* Free a port_list
699 : : */
700 : : void
701 : 1761 : free_acc_port_list(acc_port_list_t *ple)
702 : : {
703 : : acc_port_list_t *last_ple;
704 : :
705 [ - + ]: 1761 : while(ple != NULL)
706 : : {
707 : 0 : last_ple = ple;
708 : 0 : ple = last_ple->next;
709 : :
710 : 0 : free(last_ple);
711 : : }
712 : 1761 : }
713 : :
714 : : /* Free a string_list
715 : : */
716 : : static void
717 : 0 : free_acc_string_list(acc_string_list_t *stl)
718 : : {
719 : : acc_string_list_t *last_stl;
720 : :
721 [ # # ]: 0 : while(stl != NULL)
722 : : {
723 : 0 : last_stl = stl;
724 : 0 : stl = last_stl->next;
725 : :
726 : 0 : free(last_stl->str);
727 : 0 : free(last_stl);
728 : : }
729 : 0 : }
730 : :
731 : : static void
732 : 25576 : zero_buf_wrapper(char *buf, int len)
733 : : {
734 : :
735 [ - + ]: 25576 : if(zero_buf(buf, len) != FKO_SUCCESS)
736 : 0 : log_msg(LOG_ERR,
737 : : "[*] Could not zero out sensitive data buffer.");
738 : :
739 : 25576 : return;
740 : : }
741 : :
742 : : /* Free any allocated content of an access stanza.
743 : : *
744 : : * NOTE: If a new access.conf parameter is created, and it is a string
745 : : * value, it also needs to be added to the list of items to check
746 : : * and free below.
747 : : */
748 : : static void
749 : 23951 : free_acc_stanza_data(acc_stanza_t *acc)
750 : : {
751 : :
752 [ + - ]: 23951 : if(acc->source != NULL)
753 : : {
754 : 23951 : free(acc->source);
755 : 23951 : free_acc_int_list(acc->source_list);
756 : : }
757 : :
758 [ - + ]: 23951 : if(acc->destination != NULL)
759 : : {
760 : 0 : free(acc->destination);
761 : 0 : free_acc_int_list(acc->destination_list);
762 : : }
763 : :
764 [ + + ]: 23951 : if(acc->open_ports != NULL)
765 : : {
766 : 1173 : free(acc->open_ports);
767 : 1173 : free_acc_port_list(acc->oport_list);
768 : : }
769 : :
770 [ + + ]: 23951 : if(acc->restrict_ports != NULL)
771 : : {
772 : 588 : free(acc->restrict_ports);
773 : 588 : free_acc_port_list(acc->rport_list);
774 : : }
775 : :
776 [ - + ]: 23951 : if(acc->force_nat_ip != NULL)
777 : 0 : free(acc->force_nat_ip);
778 : :
779 [ - + ]: 23951 : if(acc->force_snat_ip != NULL)
780 : 0 : free(acc->force_snat_ip);
781 : :
782 [ + + ]: 23951 : if(acc->key != NULL)
783 : : {
784 : 23851 : zero_buf_wrapper(acc->key, acc->key_len);
785 : 23851 : free(acc->key);
786 : : }
787 : :
788 [ - + ]: 23951 : if(acc->key_base64 != NULL)
789 : : {
790 : 0 : zero_buf_wrapper(acc->key_base64, strlen(acc->key_base64));
791 : 0 : free(acc->key_base64);
792 : : }
793 : :
794 [ + + ]: 23951 : if(acc->hmac_key != NULL)
795 : : {
796 : 1725 : zero_buf_wrapper(acc->hmac_key, acc->hmac_key_len);
797 : 1725 : free(acc->hmac_key);
798 : : }
799 : :
800 [ - + ]: 23951 : if(acc->hmac_key_base64 != NULL)
801 : : {
802 : 0 : zero_buf_wrapper(acc->hmac_key_base64, strlen(acc->hmac_key_base64));
803 : 0 : free(acc->hmac_key_base64);
804 : : }
805 : :
806 [ - + ]: 23951 : if(acc->cmd_exec_user != NULL)
807 : 0 : free(acc->cmd_exec_user);
808 : :
809 [ - + ]: 23951 : if(acc->cmd_exec_group != NULL)
810 : 0 : free(acc->cmd_exec_group);
811 : :
812 [ + + ]: 23951 : if(acc->require_username != NULL)
813 : 391 : free(acc->require_username);
814 : :
815 [ - + ]: 23951 : if(acc->gpg_home_dir != NULL)
816 : 0 : free(acc->gpg_home_dir);
817 : :
818 [ - + ]: 23951 : if(acc->gpg_exe != NULL)
819 : 0 : free(acc->gpg_exe);
820 : :
821 [ - + ]: 23951 : if(acc->gpg_decrypt_id != NULL)
822 : 0 : free(acc->gpg_decrypt_id);
823 : :
824 [ - + ]: 23951 : if(acc->gpg_decrypt_pw != NULL)
825 : 0 : free(acc->gpg_decrypt_pw);
826 : :
827 [ - + ]: 23951 : if(acc->gpg_remote_id != NULL)
828 : : {
829 : 0 : free(acc->gpg_remote_id);
830 : 0 : free_acc_string_list(acc->gpg_remote_id_list);
831 : : }
832 [ - + ]: 23951 : if(acc->gpg_remote_fpr != NULL)
833 : : {
834 : 0 : free(acc->gpg_remote_fpr);
835 : 0 : free_acc_string_list(acc->gpg_remote_fpr_list);
836 : : }
837 : 23951 : return;
838 : : }
839 : :
840 : : /* Expand any access entries that may be multi-value.
841 : : */
842 : : static void
843 : 1319 : expand_acc_ent_lists(fko_srv_options_t *opts)
844 : : {
845 : 1319 : acc_stanza_t *acc = opts->acc_stanzas;
846 : :
847 : : /* We need to do this for each stanza.
848 : : */
849 [ + + ]: 19728 : while(acc)
850 : : {
851 : : /* Expand the source string to 32-bit integer IP + masks for each entry.
852 : : */
853 [ + + ]: 18645 : if(expand_acc_int_list(&(acc->source_list), acc->source) == 0)
854 : : {
855 : 153 : log_msg(LOG_ERR, "[*] Fatal invalid SOURCE in access stanza");
856 : 153 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
857 : : }
858 : :
859 [ - + ][ # # ]: 18492 : if(acc->destination != NULL && strlen(acc->destination))
860 : : {
861 [ # # ]: 0 : if(expand_acc_int_list(&(acc->destination_list), acc->destination) == 0)
862 : : {
863 : 0 : log_msg(LOG_ERR, "[*] Fatal invalid DESTINATION in access stanza");
864 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
865 : : }
866 : : }
867 : :
868 : : /* Now expand the open_ports string.
869 : : */
870 [ + + ][ + - ]: 18492 : if(acc->open_ports != NULL && strlen(acc->open_ports))
871 : : {
872 [ + - ]: 67 : if(expand_acc_port_list(&(acc->oport_list), acc->open_ports) == 0)
873 : : {
874 : 67 : log_msg(LOG_ERR, "[*] Fatal invalid OPEN_PORTS in access stanza");
875 : 67 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
876 : : }
877 : : }
878 : :
879 [ + + ][ + - ]: 18425 : if(acc->restrict_ports != NULL && strlen(acc->restrict_ports))
880 : : {
881 [ + - ]: 16 : if(expand_acc_port_list(&(acc->rport_list), acc->restrict_ports) == 0)
882 : : {
883 : 16 : log_msg(LOG_ERR, "[*] Fatal invalid RESTRICT_PORTS in access stanza");
884 : 16 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
885 : : }
886 : : }
887 : :
888 : : /* Expand the GPG_REMOTE_ID string.
889 : : */
890 [ - + ][ # # ]: 18409 : if(acc->gpg_remote_id != NULL && strlen(acc->gpg_remote_id))
891 : : {
892 [ # # ]: 0 : if(expand_acc_string_list(&(acc->gpg_remote_id_list),
893 : : acc->gpg_remote_id) != SUCCESS)
894 : : {
895 : 0 : log_msg(LOG_ERR, "[*] Fatal invalid GPG_REMOTE_ID list in access stanza");
896 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
897 : : }
898 : : }
899 : :
900 : : /* Expand the GPG_FINGERPRINT_ID string.
901 : : */
902 [ - + ][ # # ]: 18409 : if(acc->gpg_remote_fpr != NULL && strlen(acc->gpg_remote_fpr))
903 : : {
904 [ # # ]: 0 : if(expand_acc_string_list(&(acc->gpg_remote_fpr_list),
905 : : acc->gpg_remote_fpr) != SUCCESS)
906 : : {
907 : 0 : log_msg(LOG_ERR, "[*] Fatal invalid GPG_FINGERPRINT_ID list in access stanza");
908 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
909 : : }
910 : : }
911 : :
912 : 18409 : acc = acc->next;
913 : : }
914 : 1083 : return;
915 : : }
916 : :
917 : : void
918 : 3443 : free_acc_stanzas(fko_srv_options_t *opts)
919 : : {
920 : : acc_stanza_t *acc, *last_acc;
921 : :
922 : : /* Free any resources first (in case of reconfig). Assume non-NULL
923 : : * entry needs to be freed.
924 : : */
925 : 3443 : acc = opts->acc_stanzas;
926 : :
927 [ + + ]: 27394 : while(acc != NULL)
928 : : {
929 : 23951 : last_acc = acc;
930 : 23951 : acc = last_acc->next;
931 : :
932 : 23951 : free_acc_stanza_data(last_acc);
933 : 23951 : free(last_acc);
934 : : }
935 : :
936 : 3443 : return;
937 : : }
938 : :
939 : : /* Wrapper for free_acc_stanzas(), we may put additional initialization
940 : : * code here.
941 : : */
942 : : static void
943 : 1629 : acc_stanza_init(fko_srv_options_t *opts)
944 : : {
945 : : /* Free any resources first (in case of reconfig). Assume non-NULL
946 : : * entry needs to be freed.
947 : : */
948 : 1629 : free_acc_stanzas(opts);
949 : :
950 : 1629 : return;
951 : : }
952 : :
953 : : /* Add a new stanza bay allocating the required memory at the required
954 : : * location, yada-yada-yada.
955 : : */
956 : : static acc_stanza_t*
957 : 23951 : acc_stanza_add(fko_srv_options_t *opts)
958 : : {
959 : 23951 : acc_stanza_t *acc = opts->acc_stanzas;
960 : 23951 : acc_stanza_t *new_acc = calloc(1, sizeof(acc_stanza_t));
961 : : acc_stanza_t *last_acc;
962 : :
963 [ - + ]: 23951 : if(new_acc == NULL)
964 : : {
965 : 0 : log_msg(LOG_ERR,
966 : : "[*] Fatal memory allocation error adding access stanza"
967 : : );
968 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
969 : : }
970 : :
971 : : /* If this is not the first acc entry, we walk our acc pointer to the
972 : : * end of the existing list.
973 : : */
974 [ + + ]: 23951 : if(acc == NULL)
975 : : {
976 : 23951 : opts->acc_stanzas = new_acc;
977 : : }
978 : : else
979 : : {
980 : : do {
981 : 1761178 : last_acc = acc;
982 [ + + ]: 1761178 : } while((acc = acc->next));
983 : :
984 : 22345 : last_acc->next = new_acc;
985 : : }
986 : :
987 : 23951 : return(new_acc);
988 : : }
989 : :
990 : : /* Scan the access options for entries that have not been set, but need
991 : : * a default value.
992 : : */
993 : : static void
994 : 1083 : set_acc_defaults(fko_srv_options_t *opts)
995 : : {
996 : 1083 : acc_stanza_t *acc = opts->acc_stanzas;
997 : 1083 : int i=1;
998 : :
999 [ + - ]: 1083 : if(!acc)
1000 : : return;
1001 : :
1002 [ + + ]: 10272 : while(acc)
1003 : : {
1004 : : /* set default fw_access_timeout if necessary
1005 : : */
1006 [ + + ]: 9189 : if(acc->fw_access_timeout < 1)
1007 : 8124 : acc->fw_access_timeout = DEF_FW_ACCESS_TIMEOUT;
1008 : :
1009 : : /* set default gpg keyring path if necessary
1010 : : */
1011 [ - + ]: 9189 : if(acc->gpg_decrypt_pw != NULL)
1012 : : {
1013 [ # # ]: 0 : if(acc->gpg_home_dir == NULL)
1014 [ # # ]: 0 : if(add_acc_string(&(acc->gpg_home_dir), opts->config[CONF_GPG_HOME_DIR]) != SUCCESS)
1015 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1016 : :
1017 [ # # ]: 0 : if(! acc->gpg_require_sig)
1018 : : {
1019 [ # # ]: 0 : if (acc->gpg_disable_sig)
1020 : : {
1021 : 0 : log_msg(LOG_INFO,
1022 : : "Warning: GPG_REQUIRE_SIG should really be enabled for stanza source: '%s' (#%d)",
1023 : : acc->source, i
1024 : : );
1025 : : }
1026 : : else
1027 : : {
1028 : : /* Make this the default unless explicitly disabled
1029 : : */
1030 : 0 : acc->gpg_require_sig = 1;
1031 : : }
1032 : : }
1033 : : else
1034 : : {
1035 [ # # ]: 0 : if (acc->gpg_disable_sig)
1036 : : {
1037 : 0 : log_msg(LOG_INFO,
1038 : : "Warning: GPG_REQUIRE_SIG and GPG_DISABLE_SIG are both set, will check sigs (stanza source: '%s' #%d)",
1039 : : acc->source, i
1040 : : );
1041 : : }
1042 : : }
1043 : :
1044 : : /* If signature checking is enabled, make sure we either have sig ID's or
1045 : : * fingerprint ID's to check
1046 : : */
1047 [ # # ]: 0 : if(! acc->gpg_disable_sig
1048 [ # # ][ # # ]: 0 : && (acc->gpg_remote_id == NULL && acc->gpg_remote_fpr == NULL))
1049 : : {
1050 : 0 : log_msg(LOG_INFO,
1051 : : "Warning: Must have either sig ID's or fingerprints to check via GPG_REMOTE_ID or GPG_FINGERPRINT_ID (stanza source: '%s' #%d)",
1052 : : acc->source, i
1053 : : );
1054 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1055 : : }
1056 : : }
1057 : :
1058 [ + + ]: 9189 : if(acc->encryption_mode == FKO_ENC_MODE_UNKNOWN)
1059 : 9083 : acc->encryption_mode = FKO_DEFAULT_ENC_MODE;
1060 : :
1061 : : /* if we're using an HMAC key and the HMAC digest type was not
1062 : : * set for HMAC_DIGEST_TYPE, then assume it's SHA256
1063 : : */
1064 : :
1065 [ + + ]: 9189 : if(acc->hmac_type == FKO_HMAC_UNKNOWN
1066 [ + + ][ + - ]: 9099 : && acc->hmac_key_len > 0 && acc->hmac_key != NULL)
1067 : : {
1068 : 285 : acc->hmac_type = FKO_DEFAULT_HMAC_MODE;
1069 : : }
1070 : :
1071 : 9189 : acc = acc->next;
1072 : 9189 : i++;
1073 : : }
1074 : : return;
1075 : : }
1076 : :
1077 : : /* Perform some sanity checks on an acc stanza data.
1078 : : */
1079 : : static int
1080 : 23712 : acc_data_is_valid(fko_srv_options_t *opts, struct passwd *user_pw, acc_stanza_t * const acc)
1081 : : {
1082 [ - + ]: 23712 : if(acc == NULL)
1083 : : {
1084 : 0 : log_msg(LOG_ERR,
1085 : : "[*] acc_data_is_valid() called with NULL acc stanza");
1086 : : return(0);
1087 : : }
1088 : :
1089 [ + + ][ - + ]: 23712 : if(((acc->key == NULL || acc->key_len == 0)
1090 [ - + ][ # # ]: 47 : && ((acc->gpg_decrypt_pw == NULL || !strlen(acc->gpg_decrypt_pw))
1091 [ - + ]: 47 : && acc->gpg_allow_no_pw == 0))
1092 [ - + ][ # # ]: 23665 : || (acc->use_rijndael == 0 && acc->use_gpg == 0 && acc->gpg_allow_no_pw == 0))
1093 : : {
1094 : 47 : log_msg(LOG_ERR,
1095 : : "[*] No keys found for access stanza source: '%s'", acc->source
1096 : : );
1097 : : return(0);
1098 : : }
1099 : :
1100 [ + - ][ + - ]: 23665 : if(acc->use_rijndael && acc->key != NULL)
1101 : : {
1102 [ + + ]: 23665 : if((acc->encryption_mode == FKO_ENC_MODE_CBC_LEGACY_IV)
1103 [ + + ]: 591 : && (acc->key_len > 16))
1104 : : {
1105 : 71 : log_msg(LOG_INFO,
1106 : : "Warning: truncating encryption key in legacy mode to 16 bytes for access stanza source: '%s'",
1107 : : acc->source
1108 : : );
1109 : 71 : acc->key_len = 16;
1110 : : }
1111 : : }
1112 : :
1113 [ + + ][ + - ]: 23665 : if((acc->hmac_key_len) != 0 && (acc->hmac_key != NULL))
1114 : : {
1115 [ + - ][ + - ]: 1676 : if((acc->key != NULL) && (acc->key_len != 0)
1116 [ + + ]: 1676 : && (acc->key_len == acc->hmac_key_len))
1117 : : {
1118 [ + + ]: 272 : if(memcmp(acc->key, acc->hmac_key, acc->hmac_key_len) == 0)
1119 : : {
1120 : 1 : log_msg(LOG_ERR,
1121 : : "[*] The encryption passphrase and HMAC key should not be identical for access stanza source: '%s'",
1122 : : acc->source
1123 : : );
1124 : : return(0);
1125 : : }
1126 : : }
1127 [ + - ]: 1404 : else if((acc->gpg_allow_no_pw == 0)
1128 [ - + ]: 1404 : && acc->gpg_decrypt_pw != NULL
1129 [ # # ]: 0 : && (strlen(acc->gpg_decrypt_pw) == acc->hmac_key_len))
1130 : : {
1131 [ # # ]: 0 : if(memcmp(acc->gpg_decrypt_pw, acc->hmac_key, acc->hmac_key_len) == 0)
1132 : : {
1133 : 0 : log_msg(LOG_ERR,
1134 : : "[*] The encryption passphrase and HMAC key should not be identical for access stanza source: '%s'",
1135 : : acc->source
1136 : : );
1137 : : return(0);
1138 : : }
1139 : : }
1140 : : }
1141 : :
1142 : : #if defined(FIREWALL_FIREWALLD) || defined(FIREWALL_IPTABLES)
1143 [ + - ][ - + ]: 23664 : if((acc->force_snat == 1 || acc->force_masquerade == 1)
1144 [ # # ]: 0 : && acc->force_nat == 0)
1145 : : {
1146 [ # # ]: 0 : if(acc->forward_all == 1)
1147 : : {
1148 : 0 : add_acc_force_nat(opts, acc, "0.0.0.0 0");
1149 : : }
1150 : : else
1151 : : {
1152 : 0 : log_msg(LOG_ERR,
1153 : : "[*] FORCE_SNAT/FORCE_MASQUERADE requires either FORCE_NAT or FORWARD_ALL: '%s'",
1154 : : acc->source
1155 : : );
1156 : : return(0);
1157 : : }
1158 : : }
1159 : : #endif
1160 : :
1161 [ + + ]: 23664 : if(acc->require_source_address == 0)
1162 : : {
1163 : 23514 : log_msg(LOG_INFO,
1164 : : "Warning: REQUIRE_SOURCE_ADDRESS not enabled for access stanza source: '%s'",
1165 : : acc->source
1166 : : );
1167 : : }
1168 : :
1169 [ - + ][ # # ]: 23664 : if(user_pw != NULL && acc->cmd_exec_uid != 0 && acc->cmd_exec_gid == 0)
[ # # ]
1170 : : {
1171 : 0 : log_msg(LOG_INFO,
1172 : : "Setting gid to group associated with CMD_EXEC_USER '%s' for setgid() execution in stanza source: '%s'",
1173 : : acc->cmd_exec_user,
1174 : : acc->source
1175 : : );
1176 : 0 : acc->cmd_exec_gid = user_pw->pw_gid;
1177 : : }
1178 : :
1179 : : return(1);
1180 : : }
1181 : :
1182 : : /* Read and parse the access file, popluating the access data as we go.
1183 : : */
1184 : : void
1185 : 1629 : parse_access_file(fko_srv_options_t *opts)
1186 : : {
1187 : : FILE *file_ptr;
1188 : : char *ndx;
1189 : 1629 : int got_source = 0, is_err;
1190 : 1629 : unsigned int num_lines = 0;
1191 : :
1192 : 1629 : char access_line_buf[MAX_LINE_LEN] = {0};
1193 : 1629 : char var[MAX_LINE_LEN] = {0};
1194 : 1629 : char val[MAX_LINE_LEN] = {0};
1195 : :
1196 : 1629 : struct passwd *pw = NULL;
1197 : 1629 : struct passwd *user_pw = NULL;
1198 : : struct stat st;
1199 : :
1200 : 1629 : acc_stanza_t *curr_acc = NULL;
1201 : :
1202 : : /* First see if the access file exists. If it doesn't, complain
1203 : : * and bail.
1204 : : */
1205 [ - + ]: 1629 : if(stat(opts->config[CONF_ACCESS_FILE], &st) != 0)
1206 : : {
1207 : 0 : log_msg(LOG_ERR, "[*] Access file: '%s' was not found.",
1208 : : opts->config[CONF_ACCESS_FILE]);
1209 : :
1210 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1211 : : }
1212 : :
1213 [ - + ]: 1629 : if(verify_file_perms_ownership(opts->config[CONF_ACCESS_FILE]) != 1)
1214 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1215 : :
1216 : : /* A note on security here: Coverity flags the following fopen() as a
1217 : : * Time of check time of use (TOCTOU) bug with a low priority due to the
1218 : : * previous stat() call above. I.e., the access.conf file on disk could
1219 : : * have been changed between the stat() and the fopen() causing a TOCTOU
1220 : : * bug. While technically this is true, the return value of fopen() is
1221 : : * also checked below so stat() success does not imply we assume fopen()
1222 : : * success. Also, we could just remove the stat() and
1223 : : * verify_file_perms_ownership() calls above to "fix" the bug, but this
1224 : : * would actually make things easier for an attacker that has already
1225 : : * compromised the local system since access.conf could be changed to, say,
1226 : : * a symbolic link (for which verify_file_perms_ownership() throws a
1227 : : * warning), and then there is no race at all before the fopen(). I.e.
1228 : : * forcing an attacker to do the race makes things harder for them.
1229 : : */
1230 [ - + ]: 1629 : if ((file_ptr = fopen(opts->config[CONF_ACCESS_FILE], "r")) == NULL)
1231 : : {
1232 : 0 : log_msg(LOG_ERR, "[*] Could not open access file: %s",
1233 : : opts->config[CONF_ACCESS_FILE]);
1234 : 0 : perror(NULL);
1235 : :
1236 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1237 : : }
1238 : :
1239 : : /* Initialize the access list.
1240 : : */
1241 : 1629 : acc_stanza_init(opts);
1242 : :
1243 : : /* Now walk through access file pulling the access entries into the
1244 : : * current stanza.
1245 : : */
1246 [ + + ]: 115039 : while ((fgets(access_line_buf, MAX_LINE_LEN, file_ptr)) != NULL)
1247 : : {
1248 : 112062 : num_lines++;
1249 : 112062 : access_line_buf[MAX_LINE_LEN-1] = '\0';
1250 : :
1251 : : /* Get past comments and empty lines (note: we only look at the
1252 : : * first character.
1253 : : */
1254 [ + + ][ + - ]: 112062 : if(IS_EMPTY_LINE(access_line_buf[0]))
[ + + ][ + + ]
1255 : 8742 : continue;
1256 : :
1257 [ + + ]: 103320 : if(sscanf(access_line_buf, "%s %[^;\n\r]", var, val) != 2)
1258 : : {
1259 : 124 : log_msg(LOG_ERR,
1260 : : "[*] Invalid access file entry in %s at line %i.\n - '%s'",
1261 : : opts->config[CONF_ACCESS_FILE], num_lines, access_line_buf
1262 : : );
1263 : 124 : fclose(file_ptr);
1264 : 124 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1265 : : }
1266 : :
1267 : : /* Remove any colon that may be on the end of the var
1268 : : */
1269 [ + + ]: 103196 : if((ndx = strrchr(var, ':')) != NULL)
1270 : 266 : *ndx = '\0';
1271 : :
1272 : : /* Even though sscanf should automatically add a terminating
1273 : : * NULL byte, an assumption is made that the input arrays are
1274 : : * big enough, so we'll force a terminating NULL byte regardless
1275 : : */
1276 : 103196 : var[MAX_LINE_LEN-1] = 0x0;
1277 : 103196 : val[MAX_LINE_LEN-1] = 0x0;
1278 : :
1279 [ + + ]: 103196 : if (opts->verbose > 3)
1280 : 3 : log_msg(LOG_DEBUG,
1281 : : "ACCESS FILE: %s, LINE: %s\tVar: %s, Val: '%s'",
1282 : : opts->config[CONF_ACCESS_FILE], access_line_buf, var, val
1283 : : );
1284 : :
1285 : : /* Process the entry.
1286 : : *
1287 : : * NOTE: If a new access.conf parameter is created. It also needs
1288 : : * to be accounted for in the following if/if else construct.
1289 : : */
1290 [ + + ]: 103196 : if(CONF_VAR_IS(var, "SOURCE"))
1291 : : {
1292 : : /* If this is not the first stanza, sanity check the previous
1293 : : * stanza for the minimum required data.
1294 : : */
1295 [ + + ]: 23979 : if(curr_acc != NULL) {
1296 [ + + ]: 22373 : if(!acc_data_is_valid(opts, user_pw, curr_acc))
1297 : : {
1298 : 28 : log_msg(LOG_ERR, "[*] Data error in access file: '%s'",
1299 : : opts->config[CONF_ACCESS_FILE]);
1300 : 28 : fclose(file_ptr);
1301 : 28 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1302 : : }
1303 : : }
1304 : :
1305 : : /* Start new stanza.
1306 : : */
1307 : 23951 : curr_acc = acc_stanza_add(opts);
1308 : :
1309 [ - + ]: 23951 : if(add_acc_string(&(curr_acc->source), val) != SUCCESS)
1310 : : {
1311 : 0 : fclose(file_ptr);
1312 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1313 : : }
1314 : :
1315 : 23951 : got_source++;
1316 : : }
1317 [ + + ]: 79217 : else if (curr_acc == NULL)
1318 : : {
1319 : : /* The stanza must start with the "SOURCE" variable
1320 : : */
1321 : 636 : continue;
1322 : : }
1323 [ - + ]: 78581 : else if(CONF_VAR_IS(var, "DESTINATION"))
1324 : : {
1325 [ # # ]: 0 : if(add_acc_string(&(curr_acc->destination), val) != SUCCESS)
1326 : : {
1327 : 0 : fclose(file_ptr);
1328 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1329 : : }
1330 : : }
1331 [ + + ]: 78581 : else if(CONF_VAR_IS(var, "OPEN_PORTS"))
1332 : : {
1333 [ - + ]: 1638 : if(add_acc_string(&(curr_acc->open_ports), val) != SUCCESS)
1334 : : {
1335 : 0 : fclose(file_ptr);
1336 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1337 : : }
1338 : : }
1339 [ + + ]: 76943 : else if(CONF_VAR_IS(var, "RESTRICT_PORTS"))
1340 : : {
1341 [ - + ]: 680 : if(add_acc_string(&(curr_acc->restrict_ports), val) != SUCCESS)
1342 : : {
1343 : 0 : fclose(file_ptr);
1344 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1345 : : }
1346 : : }
1347 [ + + ]: 76263 : else if(CONF_VAR_IS(var, "KEY"))
1348 : : {
1349 [ - + ]: 31386 : if(strcasecmp(val, "__CHANGEME__") == 0)
1350 : : {
1351 : 0 : log_msg(LOG_ERR,
1352 : : "[*] KEY value is not properly set in stanza source '%s' in access file: '%s'",
1353 : : curr_acc->source, opts->config[CONF_ACCESS_FILE]);
1354 : 0 : fclose(file_ptr);
1355 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1356 : : }
1357 [ - + ]: 31386 : if(add_acc_string(&(curr_acc->key), val) != SUCCESS)
1358 : : {
1359 : 0 : fclose(file_ptr);
1360 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1361 : : }
1362 : 31386 : curr_acc->key_len = strlen(curr_acc->key);
1363 : 31386 : add_acc_bool(&(curr_acc->use_rijndael), "Y");
1364 : : }
1365 [ - + ]: 44877 : else if(CONF_VAR_IS(var, "KEY_BASE64"))
1366 : : {
1367 [ # # ]: 0 : if(strcasecmp(val, "__CHANGEME__") == 0)
1368 : : {
1369 : 0 : log_msg(LOG_ERR,
1370 : : "[*] KEY_BASE64 value is not properly set in stanza source '%s' in access file: '%s'",
1371 : : curr_acc->source, opts->config[CONF_ACCESS_FILE]);
1372 : 0 : fclose(file_ptr);
1373 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1374 : : }
1375 [ # # ]: 0 : if (! is_base64((unsigned char *) val, strlen(val)))
1376 : : {
1377 : 0 : log_msg(LOG_ERR,
1378 : : "[*] KEY_BASE64 argument '%s' doesn't look like base64-encoded data.",
1379 : : val);
1380 : 0 : fclose(file_ptr);
1381 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1382 : : }
1383 [ # # ]: 0 : if(add_acc_string(&(curr_acc->key_base64), val) != SUCCESS)
1384 : : {
1385 : 0 : fclose(file_ptr);
1386 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1387 : : }
1388 [ # # ]: 0 : if(add_acc_b64_string(&(curr_acc->key),
1389 : 0 : &(curr_acc->key_len), curr_acc->key_base64) != SUCCESS)
1390 : : {
1391 : 0 : fclose(file_ptr);
1392 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1393 : : }
1394 : 0 : add_acc_bool(&(curr_acc->use_rijndael), "Y");
1395 : : }
1396 : : /* HMAC digest type */
1397 [ + + ]: 44877 : else if(CONF_VAR_IS(var, "HMAC_DIGEST_TYPE"))
1398 : : {
1399 : 705 : curr_acc->hmac_type = hmac_digest_strtoint(val);
1400 [ + + ]: 705 : if(curr_acc->hmac_type < 0)
1401 : : {
1402 : 9 : log_msg(LOG_ERR,
1403 : : "[*] HMAC_DIGEST_TYPE argument '%s' must be one of {md5,sha1,sha256,sha384,sha512}",
1404 : : val);
1405 : 9 : fclose(file_ptr);
1406 : 9 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1407 : : }
1408 : : }
1409 [ - + ]: 44172 : else if(CONF_VAR_IS(var, "HMAC_KEY_BASE64"))
1410 : : {
1411 [ # # ]: 0 : if(strcasecmp(val, "__CHANGEME__") == 0)
1412 : : {
1413 : 0 : log_msg(LOG_ERR,
1414 : : "[*] HMAC_KEY_BASE64 value is not properly set in stanza source '%s' in access file: '%s'",
1415 : : curr_acc->source, opts->config[CONF_ACCESS_FILE]);
1416 : 0 : fclose(file_ptr);
1417 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1418 : : }
1419 [ # # ]: 0 : if (! is_base64((unsigned char *) val, strlen(val)))
1420 : : {
1421 : 0 : log_msg(LOG_ERR,
1422 : : "[*] HMAC_KEY_BASE64 argument '%s' doesn't look like base64-encoded data.",
1423 : : val);
1424 : 0 : fclose(file_ptr);
1425 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1426 : : }
1427 [ # # ]: 0 : if(add_acc_string(&(curr_acc->hmac_key_base64), val) != SUCCESS)
1428 : : {
1429 : 0 : fclose(file_ptr);
1430 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1431 : : }
1432 [ # # ]: 0 : if(add_acc_b64_string(&(curr_acc->hmac_key),
1433 : 0 : &(curr_acc->hmac_key_len), curr_acc->hmac_key_base64) != SUCCESS)
1434 : : {
1435 : 0 : fclose(file_ptr);
1436 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1437 : : }
1438 : : }
1439 [ + + ]: 44172 : else if(CONF_VAR_IS(var, "HMAC_KEY"))
1440 : : {
1441 [ - + ]: 2207 : if(strcasecmp(val, "__CHANGEME__") == 0)
1442 : : {
1443 : 0 : log_msg(LOG_ERR,
1444 : : "[*] HMAC_KEY value is not properly set in stanza source '%s' in access file: '%s'",
1445 : : curr_acc->source, opts->config[CONF_ACCESS_FILE]);
1446 : 0 : fclose(file_ptr);
1447 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1448 : : }
1449 [ - + ]: 2207 : if(add_acc_string(&(curr_acc->hmac_key), val) != SUCCESS)
1450 : : {
1451 : 0 : fclose(file_ptr);
1452 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1453 : : }
1454 : 2207 : curr_acc->hmac_key_len = strlen(curr_acc->hmac_key);
1455 : : }
1456 [ + + ]: 41965 : else if(CONF_VAR_IS(var, "FW_ACCESS_TIMEOUT"))
1457 : : {
1458 : 2249 : curr_acc->fw_access_timeout = strtol_wrapper(val, 0,
1459 : : RCHK_MAX_FW_TIMEOUT, NO_EXIT_UPON_ERR, &is_err);
1460 [ + + ]: 2249 : if(is_err != FKO_SUCCESS)
1461 : : {
1462 : 3 : log_msg(LOG_ERR,
1463 : : "[*] FW_ACCESS_TIMEOUT value not in range.");
1464 : 3 : fclose(file_ptr);
1465 : 3 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1466 : : }
1467 : : }
1468 [ + + ]: 39716 : else if(CONF_VAR_IS(var, "ENCRYPTION_MODE"))
1469 : : {
1470 [ + + ]: 799 : if((curr_acc->encryption_mode = enc_mode_strtoint(val)) < 0)
1471 : : {
1472 : 9 : log_msg(LOG_ERR,
1473 : : "[*] Unrecognized ENCRYPTION_MODE '%s', use {CBC,CTR,legacy,Asymmetric}",
1474 : : val);
1475 : 9 : fclose(file_ptr);
1476 : 9 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1477 : : }
1478 : : }
1479 [ - + ]: 38917 : else if(CONF_VAR_IS(var, "ENABLE_CMD_EXEC"))
1480 : : {
1481 : 0 : add_acc_bool(&(curr_acc->enable_cmd_exec), val);
1482 : : }
1483 [ - + ]: 38917 : else if(CONF_VAR_IS(var, "CMD_EXEC_USER"))
1484 : : {
1485 [ # # ]: 0 : if(add_acc_string(&(curr_acc->cmd_exec_user), val) != SUCCESS)
1486 : : {
1487 : 0 : fclose(file_ptr);
1488 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1489 : : }
1490 : :
1491 : 0 : errno = 0;
1492 : 0 : user_pw = pw = getpwnam(val);
1493 : :
1494 [ # # ]: 0 : if(pw == NULL)
1495 : : {
1496 [ # # ]: 0 : log_msg(LOG_ERR, "[*] Unable to determine UID for CMD_EXEC_USER: %s.",
1497 : 0 : errno ? strerror(errno) : "Not a user on this system");
1498 : 0 : fclose(file_ptr);
1499 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1500 : : }
1501 : :
1502 : 0 : curr_acc->cmd_exec_uid = pw->pw_uid;
1503 : : }
1504 [ - + ]: 38917 : else if(CONF_VAR_IS(var, "CMD_EXEC_GROUP"))
1505 : : {
1506 [ # # ]: 0 : if(add_acc_string(&(curr_acc->cmd_exec_group), val) != SUCCESS)
1507 : : {
1508 : 0 : fclose(file_ptr);
1509 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1510 : : }
1511 : :
1512 : 0 : errno = 0;
1513 : 0 : pw = getpwnam(val);
1514 : :
1515 [ # # ]: 0 : if(pw == NULL)
1516 : : {
1517 [ # # ]: 0 : log_msg(LOG_ERR, "[*] Unable to determine GID for CMD_EXEC_GROUP: %s.",
1518 : 0 : errno ? strerror(errno) : "Not a group on this system");
1519 : 0 : fclose(file_ptr);
1520 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1521 : : }
1522 : :
1523 : 0 : curr_acc->cmd_exec_gid = pw->pw_gid;
1524 : : }
1525 [ + + ]: 38917 : else if(CONF_VAR_IS(var, "REQUIRE_USERNAME"))
1526 : : {
1527 [ - + ]: 491 : if(add_acc_string(&(curr_acc->require_username), val) != SUCCESS)
1528 : : {
1529 : 0 : fclose(file_ptr);
1530 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1531 : : }
1532 : : }
1533 [ + + ]: 38426 : else if(CONF_VAR_IS(var, "REQUIRE_SOURCE_ADDRESS"))
1534 : : {
1535 : 268 : add_acc_bool(&(curr_acc->require_source_address), val);
1536 : : }
1537 [ + + ]: 38158 : else if(CONF_VAR_IS(var, "REQUIRE_SOURCE")) /* synonym for REQUIRE_SOURCE_ADDRESS */
1538 : : {
1539 : 77 : add_acc_bool(&(curr_acc->require_source_address), val);
1540 : : }
1541 [ - + ]: 38081 : else if(CONF_VAR_IS(var, "GPG_HOME_DIR"))
1542 : : {
1543 [ # # ]: 0 : if (is_valid_dir(val))
1544 : : {
1545 [ # # ]: 0 : if(add_acc_string(&(curr_acc->gpg_home_dir), val) != SUCCESS)
1546 : : {
1547 : 0 : fclose(file_ptr);
1548 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1549 : : }
1550 : : }
1551 : : else
1552 : : {
1553 : 0 : log_msg(LOG_ERR,
1554 : : "[*] GPG_HOME_DIR directory '%s' stat()/existence problem in stanza source '%s' in access file: '%s'",
1555 : : val, curr_acc->source, opts->config[CONF_ACCESS_FILE]);
1556 : 0 : fclose(file_ptr);
1557 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1558 : : }
1559 : : }
1560 [ - + ]: 38081 : else if(CONF_VAR_IS(var, "GPG_EXE"))
1561 : : {
1562 [ # # ]: 0 : if(add_acc_string(&(curr_acc->gpg_exe), val) != SUCCESS)
1563 : : {
1564 : 0 : fclose(file_ptr);
1565 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1566 : : }
1567 : : }
1568 [ - + ]: 38081 : else if(CONF_VAR_IS(var, "GPG_DECRYPT_ID"))
1569 : : {
1570 [ # # ]: 0 : if(add_acc_string(&(curr_acc->gpg_decrypt_id), val) != SUCCESS)
1571 : : {
1572 : 0 : fclose(file_ptr);
1573 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1574 : : }
1575 : : }
1576 [ - + ]: 38081 : else if(CONF_VAR_IS(var, "GPG_DECRYPT_PW"))
1577 : : {
1578 [ # # ]: 0 : if(strcasecmp(val, "__CHANGEME__") == 0)
1579 : : {
1580 : 0 : log_msg(LOG_ERR,
1581 : : "[*] GPG_DECRYPT_PW value is not properly set in stanza source '%s' in access file: '%s'",
1582 : : curr_acc->source, opts->config[CONF_ACCESS_FILE]);
1583 : 0 : fclose(file_ptr);
1584 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1585 : : }
1586 [ # # ]: 0 : if(add_acc_string(&(curr_acc->gpg_decrypt_pw), val) != SUCCESS)
1587 : : {
1588 : 0 : fclose(file_ptr);
1589 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1590 : : }
1591 : 0 : add_acc_bool(&(curr_acc->use_gpg), "Y");
1592 : : }
1593 [ - + ]: 38081 : else if(CONF_VAR_IS(var, "GPG_ALLOW_NO_PW"))
1594 : : {
1595 : 0 : add_acc_bool(&(curr_acc->gpg_allow_no_pw), val);
1596 [ # # ]: 0 : if(curr_acc->gpg_allow_no_pw == 1)
1597 : : {
1598 : 0 : add_acc_bool(&(curr_acc->use_gpg), "Y");
1599 [ # # ]: 0 : if(add_acc_string(&(curr_acc->gpg_decrypt_pw), "") != SUCCESS)
1600 : : {
1601 : 0 : fclose(file_ptr);
1602 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1603 : : }
1604 : : }
1605 : : }
1606 [ - + ]: 38081 : else if(CONF_VAR_IS(var, "GPG_REQUIRE_SIG"))
1607 : : {
1608 : 0 : add_acc_bool(&(curr_acc->gpg_require_sig), val);
1609 : : }
1610 [ - + ]: 38081 : else if(CONF_VAR_IS(var, "GPG_DISABLE_SIG"))
1611 : : {
1612 : 0 : add_acc_bool(&(curr_acc->gpg_disable_sig), val);
1613 : : }
1614 [ - + ]: 38081 : else if(CONF_VAR_IS(var, "GPG_IGNORE_SIG_VERIFY_ERROR"))
1615 : : {
1616 : 0 : add_acc_bool(&(curr_acc->gpg_ignore_sig_error), val);
1617 : : }
1618 [ - + ]: 38081 : else if(CONF_VAR_IS(var, "GPG_REMOTE_ID"))
1619 : : {
1620 [ # # ]: 0 : if(add_acc_string(&(curr_acc->gpg_remote_id), val) != SUCCESS)
1621 : : {
1622 : 0 : fclose(file_ptr);
1623 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1624 : : }
1625 : : }
1626 [ - + ]: 38081 : else if(CONF_VAR_IS(var, "GPG_FINGERPRINT_ID"))
1627 : : {
1628 [ # # ]: 0 : if(add_acc_string(&(curr_acc->gpg_remote_fpr), val) != SUCCESS)
1629 : : {
1630 : 0 : fclose(file_ptr);
1631 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1632 : : }
1633 : : }
1634 [ + + ]: 38081 : else if(CONF_VAR_IS(var, "ACCESS_EXPIRE"))
1635 : : {
1636 [ + + ]: 745 : if (add_acc_expire_time(opts, &(curr_acc->access_expire_time), val) != 1)
1637 : : {
1638 : 10 : fclose(file_ptr);
1639 : 10 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1640 : : }
1641 : : }
1642 [ - + ]: 37336 : else if(CONF_VAR_IS(var, "ACCESS_EXPIRE_EPOCH"))
1643 : : {
1644 [ # # ]: 0 : if (add_acc_expire_time_epoch(opts, &(curr_acc->access_expire_time), val) != 1)
1645 : : {
1646 : 0 : fclose(file_ptr);
1647 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1648 : : }
1649 : : }
1650 [ + + ]: 37336 : else if(CONF_VAR_IS(var, "FORCE_NAT"))
1651 : : {
1652 : : #if FIREWALL_FIREWALLD
1653 [ + - ]: 97 : if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_FORWARDING], "Y", 1) !=0 )
1654 : : {
1655 : 97 : log_msg(LOG_ERR,
1656 : : "[*] FORCE_NAT requires ENABLE_FIREWD_FORWARDING to be enabled in fwknopd.conf");
1657 : 97 : fclose(file_ptr);
1658 : 97 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1659 : : }
1660 [ # # ]: 0 : if(add_acc_force_nat(opts, curr_acc, val) != SUCCESS)
1661 : : {
1662 : 0 : fclose(file_ptr);
1663 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1664 : : }
1665 : : #elif FIREWALL_IPTABLES
1666 : : if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1) !=0 )
1667 : : {
1668 : : log_msg(LOG_ERR,
1669 : : "[*] FORCE_NAT requires ENABLE_IPT_FORWARDING to be enabled in fwknopd.conf");
1670 : : fclose(file_ptr);
1671 : : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1672 : : }
1673 : : if(add_acc_force_nat(opts, curr_acc, val) != SUCCESS)
1674 : : {
1675 : : fclose(file_ptr);
1676 : : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1677 : : }
1678 : : #else
1679 : : log_msg(LOG_ERR,
1680 : : "[*] FORCE_NAT not supported.");
1681 : : fclose(file_ptr);
1682 : : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1683 : : #endif
1684 : : }
1685 [ + + ]: 37239 : else if(CONF_VAR_IS(var, "FORCE_SNAT"))
1686 : : {
1687 : : #if FIREWALL_FIREWALLD
1688 [ + - ]: 1 : if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_FORWARDING], "Y", 1) !=0 )
1689 : : {
1690 : 1 : log_msg(LOG_ERR,
1691 : : "[*] FORCE_SNAT requires ENABLE_FIREWD_FORWARDING to be enabled in fwknopd.conf");
1692 : 1 : fclose(file_ptr);
1693 : 1 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1694 : : }
1695 [ # # ]: 0 : if(add_acc_force_snat(opts, curr_acc, val) != SUCCESS)
1696 : : {
1697 : 0 : fclose(file_ptr);
1698 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1699 : : }
1700 : : #elif FIREWALL_IPTABLES
1701 : : if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1) !=0 )
1702 : : {
1703 : : log_msg(LOG_ERR,
1704 : : "[*] FORCE_SNAT requires ENABLE_IPT_FORWARDING to be enabled in fwknopd.conf");
1705 : : fclose(file_ptr);
1706 : : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1707 : : }
1708 : : if(add_acc_force_snat(opts, curr_acc, val) != SUCCESS)
1709 : : {
1710 : : fclose(file_ptr);
1711 : : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1712 : : }
1713 : : #else
1714 : : log_msg(LOG_ERR,
1715 : : "[*] FORCE_SNAT not supported.");
1716 : : fclose(file_ptr);
1717 : : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1718 : : #endif
1719 : : }
1720 [ - + ]: 37238 : else if(CONF_VAR_IS(var, "FORCE_MASQUERADE"))
1721 : : {
1722 : 0 : add_acc_bool(&(curr_acc->force_masquerade), val);
1723 : 0 : add_acc_bool(&(curr_acc->force_snat), val);
1724 : : }
1725 [ - + ]: 37238 : else if(CONF_VAR_IS(var, "DISABLE_DNAT"))
1726 : : {
1727 : 0 : add_acc_bool(&(curr_acc->disable_dnat), val);
1728 : : }
1729 [ - + ]: 37238 : else if(CONF_VAR_IS(var, "FORWARD_ALL"))
1730 : : {
1731 : 0 : add_acc_bool(&(curr_acc->forward_all), val);
1732 : : }
1733 : : else
1734 : : {
1735 : 111781 : log_msg(LOG_ERR,
1736 : : "[*] Ignoring unknown access parameter: '%s' in %s",
1737 : : var, opts->config[CONF_ACCESS_FILE]
1738 : : );
1739 : : }
1740 : : }
1741 : :
1742 : 1348 : fclose(file_ptr);
1743 : :
1744 : : /* Basic check to ensure that we got at least one SOURCE stanza with
1745 : : * a valid KEY defined (valid meaning it has a value that is not
1746 : : * "__CHANGEME__".
1747 : : */
1748 [ + + ]: 1348 : if (got_source == 0)
1749 : : {
1750 : 9 : log_msg(LOG_ERR,
1751 : : "[*] Could not find valid SOURCE stanza in access file: '%s'",
1752 : : opts->config[CONF_ACCESS_FILE]);
1753 : 9 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1754 : : }
1755 : :
1756 : : /* Sanity check the last stanza
1757 : : */
1758 [ + + ]: 1339 : if(!acc_data_is_valid(opts, user_pw, curr_acc))
1759 : : {
1760 : 20 : log_msg(LOG_ERR,
1761 : : "[*] Data error in access file: '%s'",
1762 : : opts->config[CONF_ACCESS_FILE]);
1763 : 20 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1764 : : }
1765 : :
1766 : : /* Expand our the expandable fields into their respective data buckets.
1767 : : */
1768 : 1319 : expand_acc_ent_lists(opts);
1769 : :
1770 : : /* Make sure default values are set where needed.
1771 : : */
1772 : 1083 : set_acc_defaults(opts);
1773 : :
1774 : 1083 : return;
1775 : : }
1776 : :
1777 : : int
1778 : 0 : compare_addr_list(acc_int_list_t *ip_list, const uint32_t ip)
1779 : : {
1780 : 0 : int match = 0;
1781 : :
1782 [ # # ]: 0 : while(ip_list)
1783 : : {
1784 [ # # ]: 0 : if((ip & ip_list->mask) == (ip_list->maddr & ip_list->mask))
1785 : : {
1786 : : match = 1;
1787 : : break;
1788 : : }
1789 : :
1790 : 0 : ip_list = ip_list->next;
1791 : : }
1792 : :
1793 : 0 : return(match);
1794 : : }
1795 : :
1796 : : /* Compare the contents of 2 port lists. Return true on a match.
1797 : : * Match depends on the match_any flag. if match_any is 1 then any
1798 : : * entry in the incoming data need only match one item to return true.
1799 : : * Otherwise all entries in the incoming data must have a corresponding
1800 : : * match in the access port_list.
1801 : : */
1802 : : static int
1803 : 0 : compare_port_list(acc_port_list_t *in, acc_port_list_t *ac, const int match_any)
1804 : : {
1805 : 0 : int a_cnt = 0;
1806 : 0 : int i_cnt = 0;
1807 : :
1808 : : acc_port_list_t *tlist;
1809 [ # # ]: 0 : while(in)
1810 : : {
1811 : 0 : i_cnt++;
1812 : :
1813 : 0 : tlist = ac;
1814 [ # # ]: 0 : while(tlist)
1815 : : {
1816 [ # # ]: 0 : if(in->proto == tlist->proto && in->port == tlist->port)
1817 : : {
1818 : 0 : a_cnt++;
1819 [ # # ]: 0 : if(match_any == 1)
1820 : : return(1);
1821 : : }
1822 : 0 : tlist = tlist->next;
1823 : : }
1824 : 0 : in = in->next;
1825 : : }
1826 : :
1827 : 0 : return(i_cnt == a_cnt);
1828 : : }
1829 : :
1830 : : /* Take a proto/port string (or mulitple comma-separated strings) and check
1831 : : * them against the list for the given access stanza.
1832 : : *
1833 : : * Return 1 if we are allowed
1834 : : */
1835 : : int
1836 : 0 : acc_check_port_access(acc_stanza_t *acc, char *port_str)
1837 : : {
1838 : 0 : int res = 1, ctr = 0;
1839 : :
1840 : 0 : char buf[ACCESS_BUF_LEN] = {0};
1841 : : char *ndx, *start;
1842 : :
1843 : 0 : acc_port_list_t *o_pl = acc->oport_list;
1844 : 0 : acc_port_list_t *r_pl = acc->rport_list;
1845 : :
1846 : 0 : acc_port_list_t *in_pl = NULL;
1847 : :
1848 : 0 : start = port_str;
1849 : :
1850 : : /* Create our own internal port_list from the incoming SPA data
1851 : : * for comparison.
1852 : : */
1853 [ # # ]: 0 : for(ndx = start; *ndx != '\0'; ndx++)
1854 : : {
1855 [ # # ]: 0 : if(*ndx == ',')
1856 : : {
1857 [ # # ]: 0 : if((ctr >= ACCESS_BUF_LEN)
1858 [ # # ]: 0 : || (((ndx-start)+1) >= ACCESS_BUF_LEN))
1859 : : {
1860 : 0 : log_msg(LOG_ERR,
1861 : : "[*] Unable to create acc_port_list from incoming data: %s",
1862 : : port_str
1863 : : );
1864 : 0 : free_acc_port_list(in_pl);
1865 : 0 : return(0);
1866 : : }
1867 : 0 : strlcpy(buf, start, (ndx-start)+1);
1868 [ # # ]: 0 : if(add_port_list_ent(&in_pl, buf) == 0)
1869 : : {
1870 : 0 : log_msg(LOG_ERR, "[*] Invalid proto/port string");
1871 : 0 : free_acc_port_list(in_pl);
1872 : 0 : return(0);
1873 : : }
1874 : :
1875 : 0 : start = ndx+1;
1876 : 0 : ctr = 0;
1877 : : }
1878 : 0 : ctr++;
1879 : : }
1880 [ # # ]: 0 : if((ctr >= ACCESS_BUF_LEN)
1881 [ # # ]: 0 : || (((ndx-start)+1) >= ACCESS_BUF_LEN))
1882 : : {
1883 : 0 : log_msg(LOG_ERR,
1884 : : "[*] Unable to create acc_port_list from incoming data: %s",
1885 : : port_str
1886 : : );
1887 : 0 : free_acc_port_list(in_pl);
1888 : 0 : return(0);
1889 : : }
1890 : 0 : strlcpy(buf, start, (ndx-start)+1);
1891 [ # # ]: 0 : if(add_port_list_ent(&in_pl, buf) == 0)
1892 : : {
1893 : 0 : log_msg(LOG_ERR, "[*] Invalid proto/port string");
1894 : 0 : free_acc_port_list(in_pl);
1895 : 0 : return 0;
1896 : : }
1897 : :
1898 [ # # ]: 0 : if(in_pl == NULL)
1899 : : {
1900 : 0 : log_msg(LOG_ERR,
1901 : : "[*] Unable to create acc_port_list from incoming data: %s", port_str
1902 : : );
1903 : 0 : return(0);
1904 : : }
1905 : :
1906 : : /* Start with restricted ports (if any). Any match (even if only one
1907 : : * entry) means not allowed.
1908 : : */
1909 [ # # ][ # # ]: 0 : if((acc->rport_list != NULL) && (compare_port_list(in_pl, r_pl, 1)))
1910 : : {
1911 : : res = 0;
1912 : : goto cleanup_and_bail;
1913 : : }
1914 : :
1915 : : /* For open port list, all must match.
1916 : : */
1917 [ # # ][ # # ]: 0 : if((acc->oport_list != NULL) && (!compare_port_list(in_pl, o_pl, 0)))
1918 : 0 : res = 0;
1919 : :
1920 : : cleanup_and_bail:
1921 : 0 : free_acc_port_list(in_pl);
1922 : 0 : return(res);
1923 : : }
1924 : :
1925 : : /* Dump the configuration
1926 : : */
1927 : : void
1928 : 634 : dump_access_list(const fko_srv_options_t *opts)
1929 : : {
1930 : 634 : int i = 0;
1931 : :
1932 : 634 : acc_stanza_t *acc = opts->acc_stanzas;
1933 : :
1934 : 634 : fprintf(stdout, "Current fwknopd access settings:\n");
1935 : :
1936 [ + - ]: 634 : if(!acc)
1937 : : {
1938 : 0 : fprintf(stderr, "\n ** No Access Settings Defined **\n\n");
1939 : 634 : return;
1940 : : }
1941 : :
1942 [ + + ]: 9374 : while(acc)
1943 : : {
1944 [ - + ][ - + ]: 8865 : fprintf(stdout,
[ + - ][ + - ]
[ - + ][ - + ]
[ - + ][ - + ]
[ + + ][ + - ]
[ + - ][ + - ]
[ - + ][ - + ]
[ - + ][ - + ]
[ + + ][ + + ]
[ - + ][ + - ]
[ - + ][ + + ]
[ - + ][ + - ]
[ - + ][ - + ]
[ - + ]
1945 : : "SOURCE (%i): %s\n"
1946 : : "==============================================================\n"
1947 : : " DESTINATION: %s\n"
1948 : : " OPEN_PORTS: %s\n"
1949 : : " RESTRICT_PORTS: %s\n"
1950 : : " KEY: %s\n"
1951 : : " KEY_BASE64: %s\n"
1952 : : " KEY_LEN: %d\n"
1953 : : " HMAC_KEY: %s\n"
1954 : : " HMAC_KEY_BASE64: %s\n"
1955 : : " HMAC_KEY_LEN: %d\n"
1956 : : " HMAC_DIGEST_TYPE: %d\n"
1957 : : " FW_ACCESS_TIMEOUT: %i\n"
1958 : : " ENABLE_CMD_EXEC: %s\n"
1959 : : " CMD_EXEC_USER: %s\n"
1960 : : " REQUIRE_USERNAME: %s\n"
1961 : : " REQUIRE_SOURCE_ADDRESS: %s\n"
1962 : : " FORCE_NAT (ip): %s\n"
1963 : : " FORCE_NAT (proto): %s\n"
1964 : : " FORCE_NAT (port): %d\n"
1965 : : " FORCE_SNAT (ip): %s\n"
1966 : : " FORCE_MASQUERADE: %s\n"
1967 : : " DISABLE_DNAT: %s\n"
1968 : : " FORWARD_ALL: %s\n"
1969 : : " ACCESS_EXPIRE: %s" /* asctime() adds a newline */
1970 : : " GPG_HOME_DIR: %s\n"
1971 : : " GPG_EXE: %s\n"
1972 : : " GPG_DECRYPT_ID: %s\n"
1973 : : " GPG_DECRYPT_PW: %s\n"
1974 : : " GPG_REQUIRE_SIG: %s\n"
1975 : : "GPG_IGNORE_SIG_VERIFY_ERROR: %s\n"
1976 : : " GPG_REMOTE_ID: %s\n"
1977 : : " GPG_FINGERPRINT_ID: %s\n",
1978 : : ++i,
1979 : : acc->source,
1980 : 8740 : (acc->destination == NULL) ? "<not set>" : acc->destination,
1981 : 8740 : (acc->open_ports == NULL) ? "<not set>" : acc->open_ports,
1982 : 8740 : (acc->restrict_ports == NULL) ? "<not set>" : acc->restrict_ports,
1983 : 8740 : (acc->key == NULL) ? "<not set>" : "<see the access.conf file>",
1984 : 8740 : (acc->key_base64 == NULL) ? "<not set>" : "<see the access.conf file>",
1985 : : acc->key_len ? acc->key_len : 0,
1986 : 8740 : (acc->hmac_key == NULL) ? "<not set>" : "<see the access.conf file>",
1987 : 8740 : (acc->hmac_key_base64 == NULL) ? "<not set>" : "<see the access.conf file>",
1988 : : acc->hmac_key_len ? acc->hmac_key_len : 0,
1989 : : acc->hmac_type,
1990 : : acc->fw_access_timeout,
1991 : 8740 : acc->enable_cmd_exec ? "Yes" : "No",
1992 : 8740 : (acc->cmd_exec_user == NULL) ? "<not set>" : acc->cmd_exec_user,
1993 : 8740 : (acc->require_username == NULL) ? "<not set>" : acc->require_username,
1994 : 8740 : acc->require_source_address ? "Yes" : "No",
1995 : : acc->force_nat ? acc->force_nat_ip : "<not set>",
1996 [ # # ]: 0 : acc->force_nat && acc->force_nat_proto != NULL ? acc->force_nat_proto : "<not set>",
1997 : 8740 : acc->force_nat ? acc->force_nat_port : 0,
1998 : 8740 : acc->force_snat ? acc->force_snat_ip : "<not set>",
1999 : 8740 : acc->force_masquerade ? "Yes" : "No",
2000 : 8740 : acc->disable_dnat ? "Yes" : "No",
2001 : 8740 : acc->forward_all ? "Yes" : "No",
2002 : 125 : (acc->access_expire_time > 0) ? asctime(localtime(&acc->access_expire_time)) : "<not set>\n",
2003 : 8740 : (acc->gpg_home_dir == NULL) ? "<not set>" : acc->gpg_home_dir,
2004 : 8740 : (acc->gpg_exe == NULL) ? "<not set>" : acc->gpg_exe,
2005 : 8740 : (acc->gpg_decrypt_id == NULL) ? "<not set>" : acc->gpg_decrypt_id,
2006 : 8740 : (acc->gpg_decrypt_pw == NULL) ? "<not set>" : "<see the access.conf file>",
2007 : 8740 : acc->gpg_require_sig ? "Yes" : "No",
2008 : 8740 : acc->gpg_ignore_sig_error ? "Yes" : "No",
2009 : 8740 : (acc->gpg_remote_id == NULL) ? "<not set>" : acc->gpg_remote_id,
2010 : 8740 : (acc->gpg_remote_fpr == NULL) ? "<not set>" : acc->gpg_remote_fpr
2011 : : );
2012 : :
2013 : 8740 : fprintf(stdout, "\n");
2014 : :
2015 : 8740 : acc = acc->next;
2016 : : }
2017 : :
2018 : 634 : fprintf(stdout, "\n");
2019 : 634 : fflush(stdout);
2020 : : }
2021 : : #ifdef HAVE_C_UNIT_TESTS
2022 : :
2023 : : DECLARE_UTEST(compare_port_list, "check compare_port_list function")
2024 : : {
2025 : : acc_port_list_t *in1_pl = NULL;
2026 : : acc_port_list_t *in2_pl = NULL;
2027 : : acc_port_list_t *acc_pl = NULL;
2028 : :
2029 : : /* Match any test */
2030 : : free_acc_port_list(in1_pl);
2031 : : free_acc_port_list(acc_pl);
2032 : : add_port_list_ent(&in1_pl, "udp/6002");
2033 : : add_port_list_ent(&in2_pl, "udp/6002, udp/6003");
2034 : : add_port_list_ent(&acc_pl, "udp/6002, udp/6003");
2035 : : CU_ASSERT(compare_port_list(in1_pl, acc_pl, 1) == 1); /* Only one match is needed from access port list - 1 */
2036 : : CU_ASSERT(compare_port_list(in2_pl, acc_pl, 1) == 1); /* Only match is needed from access port list - 2 */
2037 : : CU_ASSERT(compare_port_list(in1_pl, acc_pl, 0) == 1); /* All ports must match access port list - 1 */
2038 : : CU_ASSERT(compare_port_list(in2_pl, acc_pl, 0) == 1); /* All ports must match access port list - 2 */
2039 : : CU_ASSERT(compare_port_list(acc_pl, in1_pl, 0) == 0); /* All ports must match in1 port list - 1 */
2040 : : CU_ASSERT(compare_port_list(acc_pl, in2_pl, 0) == 1); /* All ports must match in2 port list - 2 */
2041 : : }
2042 : :
2043 : : int register_ts_access(void)
2044 : : {
2045 : : ts_init(&TEST_SUITE(access), TEST_SUITE_DESCR(access), NULL, NULL);
2046 : : ts_add_utest(&TEST_SUITE(access), UTEST_FCT(compare_port_list), UTEST_DESCR(compare_port_list));
2047 : :
2048 : : return register_ts(&TEST_SUITE(access));
2049 : : }
2050 : : #endif /* HAVE_C_UNIT_TESTS */
2051 : :
2052 : : /***EOF***/
|