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