2513 lines
75 KiB
C
2513 lines
75 KiB
C
/**
|
|
* \file server/access.c
|
|
*
|
|
* \brief Access.conf file processing for fwknop server.
|
|
*/
|
|
|
|
/* Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
|
|
* Copyright (C) 2009-2015 fwknop developers and contributors. For a full
|
|
* list of contributors, see the file 'CREDITS'.
|
|
*
|
|
* License (GNU General Public License):
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
* USA
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
#if HAVE_SYS_SOCKET_H
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
#include "fwknopd_common.h"
|
|
#include <arpa/inet.h>
|
|
#include "pwd.h"
|
|
#include "access.h"
|
|
#include "utils.h"
|
|
#include "log_msg.h"
|
|
#include "cmd_cycle.h"
|
|
#include <dirent.h>
|
|
|
|
#define FATAL_ERR -1
|
|
|
|
#ifndef SUCCESS
|
|
#define SUCCESS 1
|
|
#endif
|
|
|
|
#ifdef HAVE_C_UNIT_TESTS /* LCOV_EXCL_START */
|
|
#include "cunit_common.h"
|
|
DECLARE_TEST_SUITE(access, "Access test suite");
|
|
#endif /* LCOV_EXCL_STOP */
|
|
|
|
/**
|
|
* \brief include keys file
|
|
*
|
|
* This function loads only the crypto keys from a given file.
|
|
* It inserts these keys into the active access stanza.
|
|
*
|
|
* \param curr_acc pointer to the current access stanza
|
|
* \param access_filename Pointer to the file containing the keys
|
|
* \param opts fko_srv_options_t Server options struct
|
|
*
|
|
*/
|
|
int
|
|
include_keys_file(acc_stanza_t *, const char *, fko_srv_options_t *);
|
|
|
|
static int do_acc_stanza_init = 1;
|
|
|
|
void enable_acc_stanzas_init(void)
|
|
{
|
|
do_acc_stanza_init = 1;
|
|
return;
|
|
}
|
|
|
|
/* Add an access string entry
|
|
*/
|
|
static void
|
|
add_acc_string(char **var, const char *val, FILE *file_ptr,
|
|
fko_srv_options_t *opts)
|
|
{
|
|
if(var == NULL)
|
|
{
|
|
log_msg(LOG_ERR, "[*] add_acc_string() called with NULL variable");
|
|
if(file_ptr != NULL)
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
|
|
if(*var != NULL)
|
|
free(*var);
|
|
|
|
if((*var = strdup(val)) == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal memory allocation error adding access list entry: %s", *var
|
|
);
|
|
if(file_ptr != NULL)
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Add an access user entry
|
|
*/
|
|
static void
|
|
add_acc_user(char **user_var, uid_t *uid_var, struct passwd **upw,
|
|
const char *val, const char *var_name, FILE *file_ptr,
|
|
fko_srv_options_t *opts)
|
|
{
|
|
struct passwd *pw = NULL;
|
|
|
|
add_acc_string(user_var, val, file_ptr, opts);
|
|
|
|
errno = 0;
|
|
*upw = pw = getpwnam(val);
|
|
|
|
if(*upw == NULL || pw == NULL)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Unable to determine UID for %s: %s.",
|
|
var_name, errno ? strerror(errno) : "Not a user on this system");
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
|
|
*uid_var = pw->pw_uid;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Add an access group entry
|
|
*/
|
|
static void
|
|
add_acc_group(char **group_var, gid_t *gid_var,
|
|
const char *val, const char *var_name, FILE *file_ptr,
|
|
fko_srv_options_t *opts)
|
|
{
|
|
struct passwd *pw = NULL;
|
|
|
|
add_acc_string(group_var, val, file_ptr, opts);
|
|
|
|
errno = 0;
|
|
pw = getpwnam(val);
|
|
|
|
if(pw == NULL)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Unable to determine GID for %s: %s.",
|
|
var_name, errno ? strerror(errno) : "Not a group on this system");
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
|
|
*gid_var = pw->pw_gid;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Decode base64 encoded string into access entry
|
|
*/
|
|
static void
|
|
add_acc_b64_string(char **var, int *len, const char *val, FILE *file_ptr,
|
|
fko_srv_options_t *opts)
|
|
{
|
|
if(var == NULL)
|
|
{
|
|
log_msg(LOG_ERR, "[*] add_acc_b64_string() called with NULL variable");
|
|
if(file_ptr != NULL)
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
|
|
if(*var != NULL)
|
|
free(*var);
|
|
|
|
if((*var = strdup(val)) == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal memory allocation error adding access list entry: %s", *var
|
|
);
|
|
if(file_ptr != NULL)
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
memset(*var, 0x0, strlen(val));
|
|
*len = fko_base64_decode(val, (unsigned char *) *var);
|
|
|
|
if (*len < 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] base64 decoding returned error for: %s", *var
|
|
);
|
|
if(file_ptr != NULL)
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Add an access bool entry (unsigned char of 1 or 0)
|
|
*/
|
|
static unsigned char
|
|
add_acc_bool(unsigned char *var, const char *val)
|
|
{
|
|
return(*var = (strncasecmp(val, "Y", 1) == 0) ? 1 : 0);
|
|
}
|
|
|
|
/* Add expiration time - convert date to epoch seconds
|
|
*/
|
|
static int
|
|
add_acc_expire_time(fko_srv_options_t *opts, time_t *access_expire_time, const char *val)
|
|
{
|
|
struct tm tm;
|
|
|
|
memset(&tm, 0, sizeof(struct tm));
|
|
|
|
if (sscanf(val, "%2d/%2d/%4d", &tm.tm_mon, &tm.tm_mday, &tm.tm_year) != 3)
|
|
{
|
|
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal: invalid date value '%s' (need MM/DD/YYYY) for access stanza expiration time",
|
|
val
|
|
);
|
|
return FATAL_ERR;
|
|
}
|
|
|
|
if(tm.tm_mon > 0)
|
|
tm.tm_mon -= 1; /* 0-11 */
|
|
|
|
/* number of years since 1900
|
|
*/
|
|
if(tm.tm_year > 1900)
|
|
tm.tm_year -= 1900;
|
|
else
|
|
if(tm.tm_year < 100)
|
|
tm.tm_year += 100;
|
|
|
|
*access_expire_time = mktime(&tm);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Add expiration time via epoch seconds defined in access.conf
|
|
*/
|
|
static void
|
|
add_acc_expire_time_epoch(fko_srv_options_t *opts,
|
|
time_t *access_expire_time, const char *val, FILE *file_ptr)
|
|
{
|
|
char *endptr;
|
|
unsigned long expire_time = 0;
|
|
|
|
errno = 0;
|
|
|
|
expire_time = (time_t) strtoul(val, &endptr, 10);
|
|
|
|
if (errno == ERANGE || (errno != 0 && expire_time == 0))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal: invalid epoch seconds value '%s' for access stanza expiration time",
|
|
val
|
|
);
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
|
|
*access_expire_time = (time_t) expire_time;
|
|
|
|
return;
|
|
}
|
|
|
|
#if defined(FIREWALL_FIREWALLD) || defined(FIREWALL_IPTABLES)
|
|
static void
|
|
add_acc_force_nat(fko_srv_options_t *opts, acc_stanza_t *curr_acc,
|
|
const char *val, FILE *file_ptr)
|
|
{
|
|
char ip_str[MAX_IPV4_STR_LEN] = {0};
|
|
|
|
if (sscanf(val, "%15s %5u", ip_str, &curr_acc->force_nat_port) != 2)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal: invalid FORCE_NAT arg '%s', need <IP> <PORT>",
|
|
val
|
|
);
|
|
if(file_ptr != NULL)
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
|
|
if (curr_acc->force_nat_port > MAX_PORT)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal: invalid FORCE_NAT port '%d'", curr_acc->force_nat_port);
|
|
if(file_ptr != NULL)
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
|
|
if(! is_valid_ipv4_addr(ip_str, strlen(ip_str)))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal: invalid FORCE_NAT IP '%s'", ip_str);
|
|
if(file_ptr != NULL)
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
|
|
curr_acc->force_nat = 1;
|
|
|
|
add_acc_string(&(curr_acc->force_nat_ip), ip_str, file_ptr, opts);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
add_acc_force_snat(fko_srv_options_t *opts, acc_stanza_t *curr_acc,
|
|
const char *val, FILE *file_ptr)
|
|
{
|
|
char ip_str[MAX_IPV4_STR_LEN] = {0};
|
|
|
|
if (sscanf(val, "%15s", ip_str) != 1)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal: invalid FORCE_SNAT arg '%s', need <IP>", val);
|
|
if(file_ptr != NULL)
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
|
|
if(! is_valid_ipv4_addr(ip_str, strlen(ip_str)))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal: invalid FORCE_SNAT IP '%s'", ip_str);
|
|
if(file_ptr != NULL)
|
|
fclose(file_ptr);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
|
|
curr_acc->force_snat = 1;
|
|
|
|
add_acc_string(&(curr_acc->force_snat_ip), ip_str, file_ptr, opts);
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Take an IP or Subnet/Mask and convert it to mask for later
|
|
* comparisons of incoming source IPs against this mask.
|
|
*/
|
|
static int
|
|
add_int_ent(acc_int_list_t **ilist, const char *ip)
|
|
{
|
|
char *ndx;
|
|
char ip_str[MAX_IPV4_STR_LEN] = {0};
|
|
char ip_mask_str[MAX_IPV4_STR_LEN] = {0};
|
|
uint32_t mask;
|
|
int is_err, mask_len = 0, need_shift = 1;
|
|
|
|
struct in_addr in;
|
|
struct in_addr mask_in;
|
|
|
|
acc_int_list_t *last_sle, *new_sle, *tmp_sle;
|
|
|
|
if((new_sle = calloc(1, sizeof(acc_int_list_t))) == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal memory allocation error adding stanza source_list entry"
|
|
);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Convert the IP data into the appropriate IP + (optional) mask
|
|
*/
|
|
if(strcasecmp(ip, "ANY") == 0)
|
|
{
|
|
new_sle->maddr = 0x0;
|
|
new_sle->mask = 0x0;
|
|
}
|
|
else
|
|
{
|
|
/* See if we have a subnet component. If so pull out the IP and
|
|
* mask values, then create the final mask value.
|
|
*/
|
|
if((ndx = strchr(ip, '/')) != NULL)
|
|
{
|
|
if(((ndx-ip)) >= MAX_IPV4_STR_LEN)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Error parsing string to IP");
|
|
free(new_sle);
|
|
new_sle = NULL;
|
|
return 0;
|
|
}
|
|
|
|
mask_len = strlen(ip) - (ndx-ip+1);
|
|
|
|
if(mask_len > 2)
|
|
{
|
|
if(mask_len >= MIN_IPV4_STR_LEN && mask_len < MAX_IPV4_STR_LEN)
|
|
{
|
|
/* IP formatted mask
|
|
*/
|
|
strlcpy(ip_mask_str, (ip + (ndx-ip) + 1), mask_len+1);
|
|
if(inet_aton(ip_mask_str, &mask_in) == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal error parsing IP mask to int for: %s", ip_mask_str
|
|
);
|
|
free(new_sle);
|
|
new_sle = NULL;
|
|
return 0;
|
|
}
|
|
mask = ntohl(mask_in.s_addr);
|
|
need_shift = 0;
|
|
}
|
|
else
|
|
{
|
|
log_msg(LOG_ERR, "[*] Invalid IP mask str '%s'.", ndx+1);
|
|
free(new_sle);
|
|
new_sle = NULL;
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(mask_len > 0)
|
|
{
|
|
/* CIDR mask
|
|
*/
|
|
mask = strtol_wrapper(ndx+1, 1, 32, NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err != FKO_SUCCESS)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Invalid IP mask str '%s'.", ndx+1);
|
|
free(new_sle);
|
|
new_sle = NULL;
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log_msg(LOG_ERR, "[*] Missing mask value.");
|
|
free(new_sle);
|
|
new_sle = NULL;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
strlcpy(ip_str, ip, (ndx-ip)+1);
|
|
}
|
|
else
|
|
{
|
|
mask = 32;
|
|
if(strnlen(ip, MAX_IPV4_STR_LEN+1) >= MAX_IPV4_STR_LEN)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Error parsing string to IP");
|
|
free(new_sle);
|
|
new_sle = NULL;
|
|
return 0;
|
|
}
|
|
strlcpy(ip_str, ip, sizeof(ip_str));
|
|
}
|
|
|
|
if(inet_aton(ip_str, &in) == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal error parsing IP to int for: %s", ip_str
|
|
);
|
|
|
|
free(new_sle);
|
|
new_sle = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Store our mask converted from CIDR to a 32-bit value.
|
|
*/
|
|
if(mask == 32)
|
|
new_sle->mask = 0xFFFFFFFF;
|
|
else if(need_shift && (mask > 0 && mask < 32))
|
|
new_sle->mask = (0xFFFFFFFF << (32 - mask));
|
|
else
|
|
new_sle->mask = mask;
|
|
|
|
/* Store our masked address for comparisons with future incoming
|
|
* packets.
|
|
*/
|
|
new_sle->maddr = ntohl(in.s_addr) & new_sle->mask;
|
|
}
|
|
|
|
/* If this is not the first entry, we walk our pointer to the
|
|
* end of the list.
|
|
*/
|
|
if(*ilist == NULL)
|
|
{
|
|
*ilist = new_sle;
|
|
}
|
|
else
|
|
{
|
|
tmp_sle = *ilist;
|
|
|
|
do {
|
|
last_sle = tmp_sle;
|
|
} while((tmp_sle = tmp_sle->next));
|
|
|
|
last_sle->next = new_sle;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Expand the access SOURCE string to a list of masks.
|
|
*/
|
|
static int
|
|
expand_acc_int_list(acc_int_list_t **ilist, char *ip)
|
|
{
|
|
char *ndx, *start;
|
|
char buf[ACCESS_BUF_LEN] = {0};
|
|
int res = 1;
|
|
|
|
start = ip;
|
|
|
|
for(ndx = start; *ndx; ndx++)
|
|
{
|
|
if(*ndx == ',')
|
|
{
|
|
/* Skip over any leading whitespace.
|
|
*/
|
|
while(isspace((int)(unsigned char)*start))
|
|
start++;
|
|
|
|
if(((ndx-start)+1) >= ACCESS_BUF_LEN)
|
|
return 0;
|
|
|
|
strlcpy(buf, start, (ndx-start)+1);
|
|
|
|
res = add_int_ent(ilist, buf);
|
|
if(res == 0)
|
|
return res;
|
|
|
|
start = ndx+1;
|
|
}
|
|
}
|
|
|
|
/* Skip over any leading whitespace (once again for the last in the list).
|
|
*/
|
|
while(isspace((int)(unsigned char)*start))
|
|
start++;
|
|
|
|
if(((ndx-start)+1) >= ACCESS_BUF_LEN)
|
|
return 0;
|
|
|
|
strlcpy(buf, start, (ndx-start)+1);
|
|
|
|
res = add_int_ent(ilist, buf);
|
|
|
|
return res;
|
|
}
|
|
|
|
static int
|
|
parse_proto_and_port(char *pstr, int *proto, int *port)
|
|
{
|
|
char *ndx;
|
|
char proto_str[ACCESS_BUF_LEN] = {0};
|
|
int is_err;
|
|
|
|
/* Parse the string into its components.
|
|
*/
|
|
if((ndx = strchr(pstr, '/')) == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Parse error on access port entry: %s", pstr);
|
|
|
|
return(-1);
|
|
}
|
|
|
|
if(((ndx - pstr)+1) >= ACCESS_BUF_LEN)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Parse error on access port entry: %s", pstr);
|
|
return(-1);
|
|
}
|
|
|
|
strlcpy(proto_str, pstr, (ndx - pstr)+1);
|
|
|
|
*port = strtol_wrapper(ndx+1, 0, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err != FKO_SUCCESS)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Invalid port '%s' in access request, must be in [%d,%d]",
|
|
pstr, 0, MAX_PORT);
|
|
return(-1);
|
|
}
|
|
|
|
if(strcasecmp(proto_str, "tcp") == 0)
|
|
*proto = PROTO_TCP;
|
|
else if(strcasecmp(proto_str, "udp") == 0)
|
|
*proto = PROTO_UDP;
|
|
else
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Invalid protocol in access port entry: %s", pstr);
|
|
return(-1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/* Take a proto/port string and convert it to appropriate integer values
|
|
* for comparisons of incoming SPA requests.
|
|
*/
|
|
static int
|
|
add_port_list_ent(acc_port_list_t **plist, char *port_str)
|
|
{
|
|
int proto_int, port;
|
|
|
|
acc_port_list_t *last_plist, *new_plist, *tmp_plist;
|
|
|
|
/* Parse the string into its components and continue only if there
|
|
* are no problems with the incoming string.
|
|
*/
|
|
if(parse_proto_and_port(port_str, &proto_int, &port) != 0)
|
|
return 0;
|
|
|
|
if((new_plist = calloc(1, sizeof(acc_port_list_t))) == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal memory allocation error adding stanza port_list entry"
|
|
);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* If this is not the first entry, we walk our pointer to the
|
|
* end of the list.
|
|
*/
|
|
if(*plist == NULL)
|
|
{
|
|
*plist = new_plist;
|
|
}
|
|
else
|
|
{
|
|
tmp_plist = *plist;
|
|
|
|
do {
|
|
last_plist = tmp_plist;
|
|
} while((tmp_plist = tmp_plist->next));
|
|
|
|
last_plist->next = new_plist;
|
|
}
|
|
|
|
new_plist->proto = proto_int;
|
|
new_plist->port = port;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Add a string list entry to the given acc_string_list.
|
|
*/
|
|
static int
|
|
add_string_list_ent(acc_string_list_t **stlist, const char *str_str)
|
|
{
|
|
acc_string_list_t *last_stlist, *new_stlist, *tmp_stlist;
|
|
|
|
if((new_stlist = calloc(1, sizeof(acc_string_list_t))) == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal memory allocation error creating string list entry"
|
|
);
|
|
return FATAL_ERR;
|
|
}
|
|
|
|
/* If this is not the first entry, we walk our pointer to the
|
|
* end of the list.
|
|
*/
|
|
if(*stlist == NULL)
|
|
{
|
|
*stlist = new_stlist;
|
|
}
|
|
else
|
|
{
|
|
tmp_stlist = *stlist;
|
|
|
|
do {
|
|
last_stlist = tmp_stlist;
|
|
} while((tmp_stlist = tmp_stlist->next));
|
|
|
|
last_stlist->next = new_stlist;
|
|
}
|
|
|
|
if(new_stlist->str != NULL)
|
|
free(new_stlist->str);
|
|
|
|
new_stlist->str = strdup(str_str);
|
|
|
|
if(new_stlist->str == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal memory allocation error adding string list entry item"
|
|
);
|
|
return FATAL_ERR;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* Expand a proto/port access string to a list of access proto-port struct.
|
|
*/
|
|
int
|
|
expand_acc_port_list(acc_port_list_t **plist, char *plist_str)
|
|
{
|
|
char *ndx, *start;
|
|
char buf[ACCESS_BUF_LEN] = {0};
|
|
|
|
start = plist_str;
|
|
|
|
for(ndx = start; *ndx != '\0'; ndx++)
|
|
{
|
|
if(*ndx == ',')
|
|
{
|
|
/* Skip over any leading whitespace.
|
|
*/
|
|
while(isspace((int)(unsigned char)*start))
|
|
start++;
|
|
|
|
if(((ndx-start)+1) >= ACCESS_BUF_LEN)
|
|
return 0;
|
|
|
|
strlcpy(buf, start, (ndx-start)+1);
|
|
|
|
if(add_port_list_ent(plist, buf) == 0)
|
|
return 0;
|
|
|
|
start = ndx+1;
|
|
}
|
|
}
|
|
|
|
/* Skip over any leading whitespace (once again for the last in the list).
|
|
*/
|
|
while(isspace((int)(unsigned char)*start))
|
|
start++;
|
|
|
|
if(((ndx-start)+1) >= ACCESS_BUF_LEN)
|
|
return 0;
|
|
|
|
strlcpy(buf, start, (ndx-start)+1);
|
|
|
|
if(add_port_list_ent(plist, buf) == 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Expand a comma-separated string into a simple acc_string_list.
|
|
*/
|
|
static int
|
|
expand_acc_string_list(acc_string_list_t **stlist, char *stlist_str)
|
|
{
|
|
char *ndx, *start;
|
|
char buf[MAX_LINE_LEN] = {0};
|
|
|
|
start = stlist_str;
|
|
|
|
for(ndx = start; *ndx; ndx++)
|
|
{
|
|
if(*ndx == ',')
|
|
{
|
|
/* Skip over any leading whitespace.
|
|
*/
|
|
while(isspace((int)(unsigned char)*start))
|
|
start++;
|
|
|
|
if(((ndx-start)+1) >= MAX_LINE_LEN)
|
|
return FATAL_ERR;
|
|
|
|
strlcpy(buf, start, (ndx-start)+1);
|
|
if(add_string_list_ent(stlist, buf) != SUCCESS)
|
|
return FATAL_ERR;
|
|
|
|
start = ndx+1;
|
|
}
|
|
}
|
|
|
|
/* Skip over any leading whitespace (once again for the last in the list).
|
|
*/
|
|
while(isspace((int)(unsigned char)*start))
|
|
start++;
|
|
|
|
if(((ndx-start)+1) >= MAX_LINE_LEN)
|
|
return FATAL_ERR;
|
|
|
|
strlcpy(buf, start, (ndx-start)+1);
|
|
|
|
if(add_string_list_ent(stlist, buf) != SUCCESS)
|
|
return FATAL_ERR;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* Free the acc source_list
|
|
*/
|
|
static void
|
|
free_acc_int_list(acc_int_list_t *sle)
|
|
{
|
|
acc_int_list_t *last_sle;
|
|
|
|
while(sle != NULL)
|
|
{
|
|
last_sle = sle;
|
|
sle = last_sle->next;
|
|
|
|
free(last_sle);
|
|
}
|
|
}
|
|
|
|
/* Free a port_list
|
|
*/
|
|
void
|
|
free_acc_port_list(acc_port_list_t *ple)
|
|
{
|
|
acc_port_list_t *last_ple;
|
|
|
|
while(ple != NULL)
|
|
{
|
|
last_ple = ple;
|
|
ple = last_ple->next;
|
|
|
|
free(last_ple);
|
|
}
|
|
}
|
|
|
|
/* Free a string_list
|
|
*/
|
|
static void
|
|
free_acc_string_list(acc_string_list_t *stl)
|
|
{
|
|
acc_string_list_t *last_stl;
|
|
|
|
while(stl != NULL)
|
|
{
|
|
last_stl = stl;
|
|
stl = last_stl->next;
|
|
|
|
free(last_stl->str);
|
|
free(last_stl);
|
|
}
|
|
}
|
|
|
|
static void
|
|
zero_buf_wrapper(char *buf, int len)
|
|
{
|
|
|
|
if(zero_buf(buf, len) != FKO_SUCCESS)
|
|
log_msg(LOG_ERR,
|
|
"[*] Could not zero out sensitive data buffer.");
|
|
|
|
return;
|
|
}
|
|
|
|
/* Free any allocated content of an access stanza.
|
|
*
|
|
* NOTE: If a new access.conf parameter is created, and it is a string
|
|
* value, it also needs to be added to the list of items to check
|
|
* and free below.
|
|
*/
|
|
static void
|
|
free_acc_stanza_data(acc_stanza_t *acc)
|
|
{
|
|
|
|
if(acc->source != NULL)
|
|
{
|
|
free(acc->source);
|
|
free_acc_int_list(acc->source_list);
|
|
}
|
|
|
|
if(acc->destination != NULL)
|
|
{
|
|
free(acc->destination);
|
|
free_acc_int_list(acc->destination_list);
|
|
}
|
|
|
|
if(acc->open_ports != NULL)
|
|
{
|
|
free(acc->open_ports);
|
|
free_acc_port_list(acc->oport_list);
|
|
}
|
|
|
|
if(acc->restrict_ports != NULL)
|
|
{
|
|
free(acc->restrict_ports);
|
|
free_acc_port_list(acc->rport_list);
|
|
}
|
|
|
|
if(acc->force_nat_ip != NULL)
|
|
free(acc->force_nat_ip);
|
|
|
|
if(acc->force_snat_ip != NULL)
|
|
free(acc->force_snat_ip);
|
|
|
|
if(acc->key != NULL)
|
|
{
|
|
zero_buf_wrapper(acc->key, acc->key_len);
|
|
free(acc->key);
|
|
}
|
|
|
|
if(acc->key_base64 != NULL)
|
|
{
|
|
zero_buf_wrapper(acc->key_base64, strlen(acc->key_base64));
|
|
free(acc->key_base64);
|
|
}
|
|
|
|
if(acc->hmac_key != NULL)
|
|
{
|
|
zero_buf_wrapper(acc->hmac_key, acc->hmac_key_len);
|
|
free(acc->hmac_key);
|
|
}
|
|
|
|
if(acc->hmac_key_base64 != NULL)
|
|
{
|
|
zero_buf_wrapper(acc->hmac_key_base64, strlen(acc->hmac_key_base64));
|
|
free(acc->hmac_key_base64);
|
|
}
|
|
|
|
if(acc->cmd_sudo_exec_user != NULL)
|
|
free(acc->cmd_sudo_exec_user);
|
|
|
|
if(acc->cmd_sudo_exec_group != NULL)
|
|
free(acc->cmd_sudo_exec_group);
|
|
|
|
if(acc->cmd_exec_user != NULL)
|
|
free(acc->cmd_exec_user);
|
|
|
|
if(acc->cmd_exec_group != NULL)
|
|
free(acc->cmd_exec_group);
|
|
|
|
if(acc->require_username != NULL)
|
|
free(acc->require_username);
|
|
|
|
if(acc->cmd_cycle_open != NULL)
|
|
free(acc->cmd_cycle_open);
|
|
|
|
if(acc->cmd_cycle_close != NULL)
|
|
free(acc->cmd_cycle_close);
|
|
|
|
if(acc->gpg_home_dir != NULL)
|
|
free(acc->gpg_home_dir);
|
|
|
|
if(acc->gpg_exe != NULL)
|
|
free(acc->gpg_exe);
|
|
|
|
if(acc->gpg_decrypt_id != NULL)
|
|
free(acc->gpg_decrypt_id);
|
|
|
|
if(acc->gpg_decrypt_pw != NULL)
|
|
free(acc->gpg_decrypt_pw);
|
|
|
|
if(acc->gpg_remote_id != NULL)
|
|
{
|
|
free(acc->gpg_remote_id);
|
|
free_acc_string_list(acc->gpg_remote_id_list);
|
|
}
|
|
if(acc->gpg_remote_fpr != NULL)
|
|
{
|
|
free(acc->gpg_remote_fpr);
|
|
free_acc_string_list(acc->gpg_remote_fpr_list);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Expand any access entries that may be multi-value.
|
|
*/
|
|
static void
|
|
expand_acc_ent_lists(fko_srv_options_t *opts)
|
|
{
|
|
acc_stanza_t *acc = opts->acc_stanzas;
|
|
|
|
/* We need to do this for each stanza.
|
|
*/
|
|
while(acc)
|
|
{
|
|
/* Expand the source string to 32-bit integer IP + masks for each entry.
|
|
*/
|
|
if(expand_acc_int_list(&(acc->source_list), acc->source) == 0)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Fatal invalid SOURCE in access stanza");
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
|
|
if(acc->destination != NULL && strlen(acc->destination))
|
|
{
|
|
if(expand_acc_int_list(&(acc->destination_list), acc->destination) == 0)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Fatal invalid DESTINATION in access stanza");
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* Now expand the open_ports string.
|
|
*/
|
|
if(acc->open_ports != NULL && strlen(acc->open_ports))
|
|
{
|
|
if(expand_acc_port_list(&(acc->oport_list), acc->open_ports) == 0)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Fatal invalid OPEN_PORTS in access stanza");
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
if(acc->restrict_ports != NULL && strlen(acc->restrict_ports))
|
|
{
|
|
if(expand_acc_port_list(&(acc->rport_list), acc->restrict_ports) == 0)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Fatal invalid RESTRICT_PORTS in access stanza");
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* Expand the GPG_REMOTE_ID string.
|
|
*/
|
|
if(acc->gpg_remote_id != NULL && strlen(acc->gpg_remote_id))
|
|
{
|
|
if(expand_acc_string_list(&(acc->gpg_remote_id_list),
|
|
acc->gpg_remote_id) != SUCCESS)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Fatal invalid GPG_REMOTE_ID list in access stanza");
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* Expand the GPG_FINGERPRINT_ID string.
|
|
*/
|
|
if(acc->gpg_remote_fpr != NULL && strlen(acc->gpg_remote_fpr))
|
|
{
|
|
if(expand_acc_string_list(&(acc->gpg_remote_fpr_list),
|
|
acc->gpg_remote_fpr) != SUCCESS)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Fatal invalid GPG_FINGERPRINT_ID list in access stanza");
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
acc = acc->next;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void
|
|
free_acc_stanzas(fko_srv_options_t *opts)
|
|
{
|
|
acc_stanza_t *acc, *last_acc;
|
|
|
|
/* Free any resources first (in case of reconfig). Assume non-NULL
|
|
* entry needs to be freed.
|
|
*/
|
|
acc = opts->acc_stanzas;
|
|
|
|
while(acc != NULL)
|
|
{
|
|
last_acc = acc;
|
|
acc = last_acc->next;
|
|
|
|
free_acc_stanza_data(last_acc);
|
|
free(last_acc);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* \brief Frees the final access stanza
|
|
*
|
|
* This function walks the access stanza list and frees the last member
|
|
*
|
|
* \param opts pointer to the server options struct
|
|
*
|
|
*/
|
|
|
|
void
|
|
free_last_acc_stanza(fko_srv_options_t *opts)
|
|
{
|
|
acc_stanza_t *tmp_root = opts->acc_stanzas;
|
|
|
|
//deal with edge cases first, like a null list
|
|
if (tmp_root == NULL)
|
|
return;
|
|
|
|
//check for only one element
|
|
if (tmp_root->next == NULL)
|
|
{
|
|
free_acc_stanza_data(tmp_root);
|
|
free(tmp_root);
|
|
opts->acc_stanzas = NULL;
|
|
return;
|
|
}
|
|
|
|
//more than one element uses the general case
|
|
while (tmp_root->next->next != NULL)
|
|
{
|
|
tmp_root = tmp_root->next;
|
|
}
|
|
|
|
free_acc_stanza_data(tmp_root->next);
|
|
free(tmp_root->next);
|
|
tmp_root->next = NULL;
|
|
return;
|
|
}
|
|
|
|
/* Wrapper for free_acc_stanzas(), we may put additional initialization
|
|
* code here.
|
|
*/
|
|
static void
|
|
acc_stanza_init(fko_srv_options_t *opts)
|
|
{
|
|
if(do_acc_stanza_init)
|
|
{
|
|
log_msg(LOG_DEBUG, "Initialize access stanzas");
|
|
|
|
/* Free any resources first (in case of reconfig). Assume non-NULL
|
|
* entry needs to be freed.
|
|
*/
|
|
free_acc_stanzas(opts);
|
|
|
|
/* Make sure to only initialize access stanzas once.
|
|
*/
|
|
do_acc_stanza_init = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Add a new stanza bay allocating the required memory at the required
|
|
* location, yada-yada-yada.
|
|
*/
|
|
static acc_stanza_t*
|
|
acc_stanza_add(fko_srv_options_t *opts)
|
|
{
|
|
acc_stanza_t *acc = opts->acc_stanzas;
|
|
acc_stanza_t *new_acc = calloc(1, sizeof(acc_stanza_t));
|
|
acc_stanza_t *last_acc;
|
|
|
|
if(new_acc == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Fatal memory allocation error adding access stanza"
|
|
);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
|
|
/* If this is not the first acc entry, we walk our acc pointer to the
|
|
* end of the existing list.
|
|
*/
|
|
if(acc == NULL)
|
|
{
|
|
opts->acc_stanzas = new_acc;
|
|
}
|
|
else
|
|
{
|
|
do {
|
|
last_acc = acc;
|
|
} while((acc = acc->next));
|
|
|
|
last_acc->next = new_acc;
|
|
}
|
|
|
|
return(new_acc);
|
|
}
|
|
|
|
/* Scan the access options for entries that have not been set, but need
|
|
* a default value.
|
|
*/
|
|
static void
|
|
set_acc_defaults(fko_srv_options_t *opts)
|
|
{
|
|
acc_stanza_t *acc = opts->acc_stanzas;
|
|
int i=1;
|
|
|
|
if(!acc)
|
|
return;
|
|
|
|
while(acc)
|
|
{
|
|
/* set default fw_access_timeout if necessary
|
|
*/
|
|
if(acc->fw_access_timeout < 1)
|
|
acc->fw_access_timeout = DEF_FW_ACCESS_TIMEOUT;
|
|
|
|
/* set default max_fw_timeout if necessary
|
|
*/
|
|
if(acc->max_fw_timeout < 1)
|
|
acc->max_fw_timeout = DEF_MAX_FW_TIMEOUT;
|
|
|
|
if(acc->max_fw_timeout < acc->fw_access_timeout)
|
|
log_msg(LOG_INFO,
|
|
"Warning: MAX_FW_TIMEOUT < FW_ACCESS_TIMEOUT, honoring MAX_FW_TIMEOUT for stanza source: '%s' (#%d)",
|
|
acc->source, i
|
|
);
|
|
|
|
/* set default gpg keyring path if necessary
|
|
*/
|
|
if(acc->gpg_decrypt_pw != NULL)
|
|
{
|
|
if(acc->gpg_home_dir == NULL)
|
|
add_acc_string(&(acc->gpg_home_dir),
|
|
opts->config[CONF_GPG_HOME_DIR], NULL, opts);
|
|
|
|
if(! acc->gpg_require_sig)
|
|
{
|
|
if (acc->gpg_disable_sig)
|
|
{
|
|
log_msg(LOG_INFO,
|
|
"Warning: GPG_REQUIRE_SIG should really be enabled for stanza source: '%s' (#%d)",
|
|
acc->source, i
|
|
);
|
|
}
|
|
else
|
|
{
|
|
/* Make this the default unless explicitly disabled
|
|
*/
|
|
acc->gpg_require_sig = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (acc->gpg_disable_sig)
|
|
{
|
|
log_msg(LOG_INFO,
|
|
"Warning: GPG_REQUIRE_SIG and GPG_DISABLE_SIG are both set, will check sigs (stanza source: '%s' #%d)",
|
|
acc->source, i
|
|
);
|
|
}
|
|
}
|
|
|
|
/* If signature checking is enabled, make sure we either have sig ID's or
|
|
* fingerprint ID's to check
|
|
*/
|
|
if(! acc->gpg_disable_sig
|
|
&& (acc->gpg_remote_id == NULL && acc->gpg_remote_fpr == NULL))
|
|
{
|
|
log_msg(LOG_INFO,
|
|
"Warning: Must have either sig ID's or fingerprints to check via GPG_REMOTE_ID or GPG_FINGERPRINT_ID (stanza source: '%s' #%d)",
|
|
acc->source, i
|
|
);
|
|
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
if(acc->encryption_mode == FKO_ENC_MODE_UNKNOWN)
|
|
acc->encryption_mode = FKO_DEFAULT_ENC_MODE;
|
|
|
|
/* if we're using an HMAC key and the HMAC digest type was not
|
|
* set for HMAC_DIGEST_TYPE, then assume it's SHA256
|
|
*/
|
|
|
|
if(acc->hmac_type == FKO_HMAC_UNKNOWN
|
|
&& acc->hmac_key_len > 0 && acc->hmac_key != NULL)
|
|
{
|
|
acc->hmac_type = FKO_DEFAULT_HMAC_MODE;
|
|
}
|
|
|
|
acc = acc->next;
|
|
i++;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Perform some sanity checks on an acc stanza data.
|
|
*/
|
|
static int
|
|
acc_data_is_valid(fko_srv_options_t *opts,
|
|
struct passwd *user_pw, struct passwd *sudo_user_pw,
|
|
acc_stanza_t * const acc)
|
|
{
|
|
if(acc == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] acc_data_is_valid() called with NULL acc stanza");
|
|
return(0);
|
|
}
|
|
|
|
if(((acc->key == NULL || acc->key_len == 0)
|
|
&& ((acc->gpg_decrypt_pw == NULL || !strlen(acc->gpg_decrypt_pw))
|
|
&& acc->gpg_allow_no_pw == 0))
|
|
|| (acc->use_rijndael == 0 && acc->use_gpg == 0 && acc->gpg_allow_no_pw == 0))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] No keys found for access stanza source: '%s'", acc->source
|
|
);
|
|
return(0);
|
|
}
|
|
|
|
if(acc->use_rijndael && acc->key != NULL)
|
|
{
|
|
if((acc->encryption_mode == FKO_ENC_MODE_CBC_LEGACY_IV)
|
|
&& (acc->key_len > 16))
|
|
{
|
|
log_msg(LOG_INFO,
|
|
"Warning: truncating encryption key in legacy mode to 16 bytes for access stanza source: '%s'",
|
|
acc->source
|
|
);
|
|
acc->key_len = 16;
|
|
}
|
|
}
|
|
|
|
if((acc->hmac_key_len) != 0 && (acc->hmac_key != NULL))
|
|
{
|
|
if((acc->key != NULL) && (acc->key_len != 0)
|
|
&& (acc->key_len == acc->hmac_key_len))
|
|
{
|
|
if(memcmp(acc->key, acc->hmac_key, acc->hmac_key_len) == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] The encryption passphrase and HMAC key should not be identical for access stanza source: '%s'",
|
|
acc->source
|
|
);
|
|
return(0);
|
|
}
|
|
}
|
|
else if((acc->gpg_allow_no_pw == 0)
|
|
&& acc->gpg_decrypt_pw != NULL
|
|
&& (strlen(acc->gpg_decrypt_pw) == acc->hmac_key_len))
|
|
{
|
|
if(memcmp(acc->gpg_decrypt_pw, acc->hmac_key, acc->hmac_key_len) == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] The encryption passphrase and HMAC key should not be identical for access stanza source: '%s'",
|
|
acc->source
|
|
);
|
|
return(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(FIREWALL_FIREWALLD) || defined(FIREWALL_IPTABLES)
|
|
if((acc->force_snat == 1 || acc->force_masquerade == 1)
|
|
&& acc->force_nat == 0)
|
|
{
|
|
if(acc->forward_all == 1)
|
|
{
|
|
add_acc_force_nat(opts, acc, "0.0.0.0 0", NULL);
|
|
}
|
|
else
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] FORCE_SNAT/FORCE_MASQUERADE requires either FORCE_NAT or FORWARD_ALL: '%s'",
|
|
acc->source
|
|
);
|
|
return(0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(acc->require_source_address == 0)
|
|
{
|
|
log_msg(LOG_INFO,
|
|
"Warning: REQUIRE_SOURCE_ADDRESS not enabled for access stanza source: '%s'",
|
|
acc->source
|
|
);
|
|
}
|
|
|
|
if(user_pw != NULL && acc->cmd_exec_uid != 0 && acc->cmd_exec_gid == 0)
|
|
{
|
|
log_msg(LOG_INFO,
|
|
"Setting gid to group associated with CMD_EXEC_USER '%s' for setgid() execution in stanza source: '%s'",
|
|
acc->cmd_exec_user,
|
|
acc->source
|
|
);
|
|
acc->cmd_exec_gid = user_pw->pw_gid;
|
|
}
|
|
|
|
if(sudo_user_pw != NULL
|
|
&& acc->cmd_sudo_exec_uid != 0 && acc->cmd_sudo_exec_gid == 0)
|
|
{
|
|
log_msg(LOG_INFO,
|
|
"Setting gid to group associated with CMD_SUDO_EXEC_USER '%s' in stanza source: '%s'",
|
|
acc->cmd_sudo_exec_user,
|
|
acc->source
|
|
);
|
|
acc->cmd_sudo_exec_gid = sudo_user_pw->pw_gid;
|
|
}
|
|
|
|
if(acc->cmd_cycle_open != NULL)
|
|
{
|
|
if(acc->cmd_cycle_close == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Cannot set CMD_CYCLE_OPEN without also setting CMD_CYCLE_CLOSE: '%s'",
|
|
acc->source
|
|
);
|
|
return(0);
|
|
}
|
|
|
|
/* Allow the string "NONE" to short-circuit close command execution.
|
|
*/
|
|
if(strncmp(acc->cmd_cycle_close, "NONE", 4) == 0)
|
|
acc->cmd_cycle_do_close = 0;
|
|
|
|
if(acc->cmd_cycle_timer == 0 && acc->cmd_cycle_do_close)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Must set the CMD_CYCLE_TIMER for command cycle functionality: '%s'",
|
|
acc->source
|
|
);
|
|
return(0);
|
|
}
|
|
if(strlen(acc->cmd_cycle_open) >= CMD_CYCLE_BUFSIZE)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] CMD_CYCLE_OPEN command is too long: '%s'",
|
|
acc->source
|
|
);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
if(acc->cmd_cycle_close != NULL)
|
|
{
|
|
if(acc->cmd_cycle_open == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Cannot set CMD_CYCLE_CLOSE without also setting CMD_CYCLE_OPEN: '%s'",
|
|
acc->source
|
|
);
|
|
return(0);
|
|
}
|
|
if(strlen(acc->cmd_cycle_close) >= CMD_CYCLE_BUFSIZE)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] CMD_CYCLE_CLOSE command is too long: '%s'",
|
|
acc->source
|
|
);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/* For any non-command cycle stanza, we enable global firewall handling
|
|
*/
|
|
if(acc->cmd_cycle_open == NULL)
|
|
opts->enable_fw = 1;
|
|
|
|
return(1);
|
|
}
|
|
|
|
int
|
|
parse_access_folder(fko_srv_options_t *opts, char *access_folder, int *depth)
|
|
{
|
|
char *extension;
|
|
DIR *dir_ptr;
|
|
|
|
char include_file[MAX_PATH_LEN] = {0};
|
|
struct dirent *dp;
|
|
|
|
chop_char(access_folder, PATH_SEP);
|
|
|
|
dir_ptr = opendir(access_folder);
|
|
|
|
//grab the file names in the directory and loop through them
|
|
if (dir_ptr == NULL)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Access folder: '%s' could not be opened.", access_folder);
|
|
return EXIT_FAILURE;
|
|
}
|
|
while ((dp = readdir(dir_ptr)) != NULL) {
|
|
extension = (strrchr(dp->d_name, '.')); // Capture just the extension
|
|
if (extension && !strncmp(extension, ".conf", 5))
|
|
{
|
|
if (strlen(access_folder) + 1 + strlen(dp->d_name) > MAX_PATH_LEN - 1) //Bail out rather than write past the end of include_file
|
|
{
|
|
closedir(dir_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
strlcpy(include_file, access_folder, sizeof(include_file)); //construct the full path
|
|
strlcat(include_file, "/", sizeof(include_file));
|
|
strlcat(include_file, dp->d_name, sizeof(include_file));
|
|
if (parse_access_file(opts, include_file, depth) == EXIT_FAILURE)
|
|
{
|
|
closedir(dir_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
closedir(dir_ptr);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/* Read and parse the access file, popluating the access data as we go.
|
|
*/
|
|
int
|
|
parse_access_file(fko_srv_options_t *opts, char *access_filename, int *depth)
|
|
{
|
|
FILE *file_ptr;
|
|
char *ndx;
|
|
int is_err;
|
|
unsigned int num_lines = 0;
|
|
|
|
char access_line_buf[MAX_LINE_LEN] = {0};
|
|
char var[MAX_LINE_LEN] = {0};
|
|
char val[MAX_LINE_LEN] = {0};
|
|
|
|
struct passwd *user_pw = NULL;
|
|
struct passwd *sudo_user_pw = NULL;
|
|
|
|
acc_stanza_t *curr_acc = NULL;
|
|
|
|
/* This allows us to limit include depth, and also tracks when we've returned to the root access.conf file.
|
|
*/
|
|
(*depth)++;
|
|
|
|
if ((file_ptr = fopen(access_filename, "r")) == NULL)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Could not open access file: %s",
|
|
access_filename);
|
|
perror(NULL);
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
#if HAVE_FILENO
|
|
if(verify_file_perms_ownership(access_filename, fileno(file_ptr)) != 1)
|
|
#else
|
|
if(verify_file_perms_ownership(access_filename, -1) != 1)
|
|
#endif
|
|
{
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
log_msg(LOG_DEBUG, "Opened access file: %s", access_filename);
|
|
|
|
/* Initialize the access list
|
|
*/
|
|
acc_stanza_init(opts);
|
|
|
|
/* Now walk through access file pulling the access entries into the
|
|
* current stanza.
|
|
*/
|
|
while ((fgets(access_line_buf, MAX_LINE_LEN, file_ptr)) != NULL)
|
|
{
|
|
num_lines++;
|
|
access_line_buf[MAX_LINE_LEN-1] = '\0';
|
|
|
|
/* Get past comments and empty lines (note: we only look at the
|
|
* first character.
|
|
*/
|
|
if(IS_EMPTY_LINE(access_line_buf[0]))
|
|
continue;
|
|
|
|
if(sscanf(access_line_buf, "%s %[^;\n\r]", var, val) != 2)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Invalid access file entry in %s at line %i.\n - '%s'",
|
|
access_filename, num_lines, access_line_buf
|
|
);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Remove any colon that may be on the end of the var
|
|
*/
|
|
if((ndx = strrchr(var, ':')) != NULL)
|
|
*ndx = '\0';
|
|
|
|
/* Even though sscanf should automatically add a terminating
|
|
* NULL byte, an assumption is made that the input arrays are
|
|
* big enough, so we'll force a terminating NULL byte regardless
|
|
*/
|
|
var[MAX_LINE_LEN-1] = 0x0;
|
|
val[MAX_LINE_LEN-1] = 0x0;
|
|
|
|
/* Remove any trailing whitespace from the value
|
|
*/
|
|
chop_whitespace(val);
|
|
|
|
if (opts->verbose > 3)
|
|
log_msg(LOG_DEBUG,
|
|
"ACCESS FILE: %s, LINE: %s\tVar: %s, Val: '%s'",
|
|
access_filename, access_line_buf, var, val
|
|
);
|
|
|
|
/* Process the entry.
|
|
*
|
|
* NOTE: If a new access.conf parameter is created. It also needs
|
|
* to be accounted for in the following if/if else construct.
|
|
*/
|
|
if(CONF_VAR_IS(var, "%include"))
|
|
{
|
|
if ((*depth) < MAX_DEPTH)
|
|
{
|
|
log_msg(LOG_DEBUG, "[+] Processing include directive for file: '%s'", val);
|
|
if (parse_access_file(opts, val, depth) == EXIT_FAILURE)
|
|
{
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log_msg(LOG_ERR, "[*] Refusing to go deeper than 3 levels. Lost in Limbo: '%s'",
|
|
access_filename);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if(CONF_VAR_IS(var, "%include_folder"))
|
|
{
|
|
log_msg(LOG_DEBUG, "[+] Processing include_folder directive for: '%s'", val);
|
|
if (parse_access_folder(opts, val, depth) == EXIT_FAILURE)
|
|
{
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if(CONF_VAR_IS(var, "SOURCE"))
|
|
{
|
|
/* If this is not the first stanza, sanity check the previous
|
|
* stanza for the minimum required data.
|
|
*/
|
|
if(curr_acc != NULL) {
|
|
if(!acc_data_is_valid(opts, user_pw, sudo_user_pw, curr_acc))
|
|
{
|
|
log_msg(LOG_ERR, "[*] Data error in access file: '%s'",
|
|
access_filename);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
/* Start new stanza.
|
|
*/
|
|
curr_acc = acc_stanza_add(opts);
|
|
add_acc_string(&(curr_acc->source), val, file_ptr, opts);
|
|
}
|
|
else if (curr_acc == NULL)
|
|
{
|
|
/* The stanza must start with the "SOURCE" variable
|
|
*/
|
|
continue;
|
|
}
|
|
else if(CONF_VAR_IS(var, "%include_keys")) //Only valid options from this file are those defining keys.
|
|
{
|
|
// This directive is only valid within a SOURCE stanza
|
|
log_msg(LOG_DEBUG, "[+] Processing include_keys directive for: '%s'", val);
|
|
include_keys_file(curr_acc, val, opts);
|
|
if(!acc_data_is_valid(opts, user_pw, sudo_user_pw, curr_acc))
|
|
{
|
|
log_msg(LOG_DEBUG, "[*] Data error in included keyfile: '%s', skipping stanza.", val);
|
|
free_last_acc_stanza(opts);
|
|
curr_acc = NULL;
|
|
}
|
|
}
|
|
else if(CONF_VAR_IS(var, "DESTINATION"))
|
|
add_acc_string(&(curr_acc->destination), val, file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "OPEN_PORTS"))
|
|
add_acc_string(&(curr_acc->open_ports), val, file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "RESTRICT_PORTS"))
|
|
add_acc_string(&(curr_acc->restrict_ports), val, file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "KEY"))
|
|
{
|
|
if(strcasecmp(val, "__CHANGEME__") == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] KEY value is not properly set in stanza source '%s' in access file: '%s'",
|
|
curr_acc->source, access_filename);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_string(&(curr_acc->key), val, file_ptr, opts);
|
|
curr_acc->key_len = strlen(curr_acc->key);
|
|
add_acc_bool(&(curr_acc->use_rijndael), "Y");
|
|
}
|
|
else if(CONF_VAR_IS(var, "KEY_BASE64"))
|
|
{
|
|
if(strcasecmp(val, "__CHANGEME__") == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] KEY_BASE64 value is not properly set in stanza source '%s' in access file: '%s'",
|
|
curr_acc->source, access_filename);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (! is_base64((unsigned char *) val, strlen(val)))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] KEY_BASE64 argument '%s' doesn't look like base64-encoded data.",
|
|
val);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_string(&(curr_acc->key_base64), val, file_ptr, opts);
|
|
add_acc_b64_string(&(curr_acc->key), &(curr_acc->key_len),
|
|
curr_acc->key_base64, file_ptr, opts);
|
|
add_acc_bool(&(curr_acc->use_rijndael), "Y");
|
|
}
|
|
/* HMAC digest type */
|
|
else if(CONF_VAR_IS(var, "HMAC_DIGEST_TYPE"))
|
|
{
|
|
curr_acc->hmac_type = hmac_digest_strtoint(val);
|
|
if(curr_acc->hmac_type < 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] HMAC_DIGEST_TYPE argument '%s' must be one of {md5,sha1,sha256,sha384,sha512,sha3_256,sha3_512}",
|
|
val);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if(CONF_VAR_IS(var, "HMAC_KEY_BASE64"))
|
|
{
|
|
if(strcasecmp(val, "__CHANGEME__") == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] HMAC_KEY_BASE64 value is not properly set in stanza source '%s' in access file: '%s'",
|
|
curr_acc->source, opts->config[CONF_ACCESS_FILE]);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (! is_base64((unsigned char *) val, strlen(val)))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] HMAC_KEY_BASE64 argument '%s' doesn't look like base64-encoded data.",
|
|
val);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_string(&(curr_acc->hmac_key_base64), val, file_ptr, opts);
|
|
add_acc_b64_string(&(curr_acc->hmac_key), &(curr_acc->hmac_key_len),
|
|
curr_acc->hmac_key_base64, file_ptr, opts);
|
|
}
|
|
else if(CONF_VAR_IS(var, "HMAC_KEY"))
|
|
{
|
|
if(strcasecmp(val, "__CHANGEME__") == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] HMAC_KEY value is not properly set in stanza source '%s' in access file: '%s'",
|
|
curr_acc->source, opts->config[CONF_ACCESS_FILE]);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_string(&(curr_acc->hmac_key), val, file_ptr, opts);
|
|
curr_acc->hmac_key_len = strlen(curr_acc->hmac_key);
|
|
}
|
|
else if(CONF_VAR_IS(var, "FW_ACCESS_TIMEOUT"))
|
|
{
|
|
curr_acc->fw_access_timeout = strtol_wrapper(val, 0,
|
|
RCHK_MAX_FW_TIMEOUT, NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err != FKO_SUCCESS)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] FW_ACCESS_TIMEOUT value not in range.");
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if(CONF_VAR_IS(var, "MAX_FW_TIMEOUT"))
|
|
{
|
|
curr_acc->max_fw_timeout = strtol_wrapper(val, 0,
|
|
RCHK_MAX_FW_TIMEOUT, NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err != FKO_SUCCESS)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] MAX_FW_TIMEOUT value not in range.");
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if(CONF_VAR_IS(var, "ENCRYPTION_MODE"))
|
|
{
|
|
if((curr_acc->encryption_mode = enc_mode_strtoint(val)) < 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Unrecognized ENCRYPTION_MODE '%s', use {CBC,CTR,legacy,Asymmetric}",
|
|
val);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if(CONF_VAR_IS(var, "ENABLE_CMD_EXEC"))
|
|
{
|
|
add_acc_bool(&(curr_acc->enable_cmd_exec), val);
|
|
}
|
|
else if(CONF_VAR_IS(var, "ENABLE_CMD_SUDO_EXEC"))
|
|
{
|
|
add_acc_bool(&(curr_acc->enable_cmd_sudo_exec), val);
|
|
}
|
|
else if(CONF_VAR_IS(var, "CMD_SUDO_EXEC_USER"))
|
|
add_acc_user(&(curr_acc->cmd_sudo_exec_user),
|
|
&(curr_acc->cmd_sudo_exec_uid), &sudo_user_pw,
|
|
val, "CMD_SUDO_EXEC_USER", file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "CMD_SUDO_EXEC_GROUP"))
|
|
add_acc_group(&(curr_acc->cmd_sudo_exec_group),
|
|
&(curr_acc->cmd_sudo_exec_gid), val,
|
|
"CMD_SUDO_EXEC_GROUP", file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "CMD_EXEC_USER"))
|
|
add_acc_user(&(curr_acc->cmd_exec_user),
|
|
&(curr_acc->cmd_exec_uid), &user_pw,
|
|
val, "CMD_EXEC_USER", file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "CMD_EXEC_GROUP"))
|
|
add_acc_group(&(curr_acc->cmd_exec_group),
|
|
&(curr_acc->cmd_exec_gid), val,
|
|
"CMD_EXEC_GROUP", file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "CMD_CYCLE_OPEN"))
|
|
{
|
|
add_acc_string(&(curr_acc->cmd_cycle_open), val, file_ptr, opts);
|
|
curr_acc->cmd_cycle_do_close = 1; /* default, will be validated */
|
|
}
|
|
else if(CONF_VAR_IS(var, "CMD_CYCLE_CLOSE"))
|
|
add_acc_string(&(curr_acc->cmd_cycle_close), val, file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "CMD_CYCLE_TIMER"))
|
|
{
|
|
curr_acc->cmd_cycle_timer = strtol_wrapper(val,
|
|
RCHK_MIN_CMD_CYCLE_TIMER, RCHK_MAX_CMD_CYCLE_TIMER,
|
|
NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err != FKO_SUCCESS)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] CMD_CYCLE_TIMER value not in range [1,%d].",
|
|
RCHK_MAX_CMD_CYCLE_TIMER);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if(CONF_VAR_IS(var, "REQUIRE_USERNAME"))
|
|
add_acc_string(&(curr_acc->require_username), val, file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "REQUIRE_SOURCE_ADDRESS"))
|
|
add_acc_bool(&(curr_acc->require_source_address), val);
|
|
else if(CONF_VAR_IS(var, "REQUIRE_SOURCE")) /* synonym for REQUIRE_SOURCE_ADDRESS */
|
|
add_acc_bool(&(curr_acc->require_source_address), val);
|
|
else if(CONF_VAR_IS(var, "GPG_HOME_DIR"))
|
|
{
|
|
if (is_valid_dir(val))
|
|
{
|
|
add_acc_string(&(curr_acc->gpg_home_dir), val, file_ptr, opts);
|
|
}
|
|
else
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] GPG_HOME_DIR directory '%s' stat()/existence problem in stanza source '%s' in access file: '%s'",
|
|
val, curr_acc->source, access_filename);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if(CONF_VAR_IS(var, "GPG_EXE"))
|
|
add_acc_string(&(curr_acc->gpg_exe), val, file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "GPG_DECRYPT_ID"))
|
|
add_acc_string(&(curr_acc->gpg_decrypt_id), val, file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "GPG_DECRYPT_PW"))
|
|
{
|
|
if(strcasecmp(val, "__CHANGEME__") == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] GPG_DECRYPT_PW value is not properly set in stanza source '%s' in access file: '%s'",
|
|
curr_acc->source, access_filename);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_string(&(curr_acc->gpg_decrypt_pw), val, file_ptr, opts);
|
|
add_acc_bool(&(curr_acc->use_gpg), "Y");
|
|
}
|
|
else if(CONF_VAR_IS(var, "GPG_ALLOW_NO_PW"))
|
|
{
|
|
add_acc_bool(&(curr_acc->gpg_allow_no_pw), val);
|
|
if(curr_acc->gpg_allow_no_pw == 1)
|
|
{
|
|
add_acc_bool(&(curr_acc->use_gpg), "Y");
|
|
add_acc_string(&(curr_acc->gpg_decrypt_pw), "", file_ptr, opts);
|
|
}
|
|
}
|
|
else if(CONF_VAR_IS(var, "GPG_REQUIRE_SIG"))
|
|
{
|
|
add_acc_bool(&(curr_acc->gpg_require_sig), val);
|
|
}
|
|
else if(CONF_VAR_IS(var, "GPG_DISABLE_SIG"))
|
|
{
|
|
add_acc_bool(&(curr_acc->gpg_disable_sig), val);
|
|
}
|
|
else if(CONF_VAR_IS(var, "GPG_IGNORE_SIG_VERIFY_ERROR"))
|
|
{
|
|
add_acc_bool(&(curr_acc->gpg_ignore_sig_error), val);
|
|
}
|
|
else if(CONF_VAR_IS(var, "GPG_REMOTE_ID"))
|
|
add_acc_string(&(curr_acc->gpg_remote_id), val, file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "GPG_FINGERPRINT_ID"))
|
|
add_acc_string(&(curr_acc->gpg_remote_fpr), val, file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "ACCESS_EXPIRE"))
|
|
{
|
|
if (add_acc_expire_time(opts, &(curr_acc->access_expire_time), val) != 1)
|
|
{
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if(CONF_VAR_IS(var, "ACCESS_EXPIRE_EPOCH"))
|
|
add_acc_expire_time_epoch(opts,
|
|
&(curr_acc->access_expire_time), val, file_ptr);
|
|
else if(CONF_VAR_IS(var, "FORCE_NAT"))
|
|
{
|
|
#if FIREWALL_FIREWALLD
|
|
if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_FORWARDING], "Y", 1) !=0
|
|
&& (strncasecmp(opts->config[CONF_ENABLE_FIREWD_LOCAL_NAT], "Y", 1) !=0 ))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] FORCE_NAT requires either ENABLE_FIREWD_FORWARDING or ENABLE_FIREWD_LOCAL_NAT in fwknopd.conf");
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_force_nat(opts, curr_acc, val, file_ptr);
|
|
#elif FIREWALL_IPTABLES
|
|
if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1) !=0
|
|
&& (strncasecmp(opts->config[CONF_ENABLE_IPT_LOCAL_NAT], "Y", 1) !=0 ))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] FORCE_NAT requires either ENABLE_IPT_FORWARDING or ENABLE_IPT_LOCAL_NAT in fwknopd.conf");
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_force_nat(opts, curr_acc, val, file_ptr);
|
|
#else
|
|
log_msg(LOG_ERR,
|
|
"[*] FORCE_NAT not supported.");
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
#endif
|
|
}
|
|
else if(CONF_VAR_IS(var, "FORCE_SNAT"))
|
|
{
|
|
#if FIREWALL_FIREWALLD
|
|
if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_FORWARDING], "Y", 1) !=0
|
|
&& (strncasecmp(opts->config[CONF_ENABLE_FIREWD_LOCAL_NAT], "Y", 1) !=0 ))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] FORCE_SNAT requires either ENABLE_FIREWD_FORWARDING or ENABLE_FIREWD_LOCAL_NAT in fwknopd.conf");
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_force_snat(opts, curr_acc, val, file_ptr);
|
|
#elif FIREWALL_IPTABLES
|
|
if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1) !=0
|
|
&& (strncasecmp(opts->config[CONF_ENABLE_IPT_LOCAL_NAT], "Y", 1) !=0 ))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] FORCE_SNAT requires either ENABLE_IPT_FORWARDING or ENABLE_IPT_LOCAL_NAT in fwknopd.conf");
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_force_snat(opts, curr_acc, val, file_ptr);
|
|
#else
|
|
log_msg(LOG_ERR,
|
|
"[*] FORCE_SNAT not supported.");
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
#endif
|
|
}
|
|
else if(CONF_VAR_IS(var, "FORCE_MASQUERADE"))
|
|
{
|
|
add_acc_bool(&(curr_acc->force_masquerade), val);
|
|
add_acc_bool(&(curr_acc->force_snat), val);
|
|
}
|
|
else if(CONF_VAR_IS(var, "DISABLE_DNAT"))
|
|
{
|
|
add_acc_bool(&(curr_acc->disable_dnat), val);
|
|
}
|
|
else if(CONF_VAR_IS(var, "FORWARD_ALL"))
|
|
{
|
|
add_acc_bool(&(curr_acc->forward_all), val);
|
|
}
|
|
else
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Ignoring unknown access parameter: '%s' in %s",
|
|
var, access_filename
|
|
);
|
|
}
|
|
}
|
|
|
|
fclose(file_ptr);
|
|
if(*depth > 0)
|
|
(*depth)--;
|
|
|
|
if(*depth == 0) //means we just closed the root access.conf
|
|
{
|
|
if(curr_acc != NULL)
|
|
{
|
|
if(!acc_data_is_valid(opts, user_pw, sudo_user_pw, curr_acc))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Data error in access file: '%s'",
|
|
access_filename);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if (opts->acc_stanzas == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Could not find valid SOURCE stanza in access file: '%s'",
|
|
opts->config[CONF_ACCESS_FILE]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Expand our the expandable fields into their respective data buckets.
|
|
*/
|
|
expand_acc_ent_lists(opts);
|
|
|
|
/* Make sure default values are set where needed.
|
|
*/
|
|
set_acc_defaults(opts);
|
|
}
|
|
else // this is an %included file
|
|
{
|
|
/* If this file had a stanza, check the last one.
|
|
*
|
|
*
|
|
*/
|
|
if(curr_acc != NULL)
|
|
{
|
|
if(!acc_data_is_valid(opts, user_pw, sudo_user_pw, curr_acc))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Data error in access file: '%s'",
|
|
access_filename);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int valid_access_stanzas(acc_stanza_t *acc)
|
|
{
|
|
if(acc == NULL)
|
|
return 0;
|
|
|
|
/* This is a basic check to ensure at least one access stanza
|
|
* with the "source" variable populated, and this function is only
|
|
* called after all access.conf files are processed. This allows
|
|
* %include_folder processing to proceed against directories that
|
|
* have files that are not access.conf files. Additional stronger
|
|
* validations are done in acc_data_is_valid(), but this function
|
|
* is only called when a "SOURCE" variable has been parsed out of
|
|
* the file.
|
|
*/
|
|
while(acc)
|
|
{
|
|
if(acc->source == NULL || acc->source[0] == '\0')
|
|
return 0;
|
|
acc = acc->next;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
compare_addr_list(acc_int_list_t *ip_list, const uint32_t ip)
|
|
{
|
|
int match = 0;
|
|
|
|
while(ip_list)
|
|
{
|
|
if((ip & ip_list->mask) == (ip_list->maddr & ip_list->mask))
|
|
{
|
|
match = 1;
|
|
break;
|
|
}
|
|
|
|
ip_list = ip_list->next;
|
|
}
|
|
|
|
return(match);
|
|
}
|
|
|
|
/**
|
|
* \brief Compares port lists
|
|
*
|
|
* Compare the contents of 2 port lists. Return true on a match.
|
|
* Match depends on the match_any flag. if match_any is 1 then any
|
|
* entry in the incoming data need only match one item to return true.
|
|
* Otherwise all entries in the incoming data must have a corresponding
|
|
* match in the access port_list.
|
|
*
|
|
* \param acc Pointer to the acc_stanza_t struct that holds the access stanzas
|
|
* \param port_str pointer to the
|
|
*
|
|
* \return Returns true on a match
|
|
*
|
|
*/
|
|
static int
|
|
compare_port_list(acc_port_list_t *in, acc_port_list_t *ac, const int match_any)
|
|
{
|
|
int a_cnt = 0;
|
|
int i_cnt = 0;
|
|
|
|
acc_port_list_t *tlist;
|
|
while(in)
|
|
{
|
|
i_cnt++;
|
|
|
|
tlist = ac;
|
|
while(tlist)
|
|
{
|
|
if(in->proto == tlist->proto && in->port == tlist->port)
|
|
{
|
|
a_cnt++;
|
|
if(match_any == 1)
|
|
return(1);
|
|
}
|
|
tlist = tlist->next;
|
|
}
|
|
in = in->next;
|
|
}
|
|
|
|
return(i_cnt == a_cnt);
|
|
}
|
|
|
|
/* Take a proto/port string (or multiple comma-separated strings) and check
|
|
* them against the list for the given access stanza.
|
|
*
|
|
* Return 1 if we are allowed
|
|
*/
|
|
int
|
|
acc_check_port_access(acc_stanza_t *acc, char *port_str)
|
|
{
|
|
int res = 1, ctr = 0;
|
|
|
|
char buf[ACCESS_BUF_LEN] = {0};
|
|
char *ndx, *start;
|
|
|
|
acc_port_list_t *o_pl = acc->oport_list;
|
|
acc_port_list_t *r_pl = acc->rport_list;
|
|
|
|
acc_port_list_t *in_pl = NULL;
|
|
|
|
start = port_str;
|
|
|
|
/* Create our own internal port_list from the incoming SPA data
|
|
* for comparison.
|
|
*/
|
|
for(ndx = start; *ndx != '\0'; ndx++)
|
|
{
|
|
if(*ndx == ',')
|
|
{
|
|
if((ctr >= ACCESS_BUF_LEN)
|
|
|| (((ndx-start)+1) >= ACCESS_BUF_LEN))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Unable to create acc_port_list from incoming data: %s",
|
|
port_str
|
|
);
|
|
free_acc_port_list(in_pl);
|
|
return(0);
|
|
}
|
|
strlcpy(buf, start, (ndx-start)+1);
|
|
if(add_port_list_ent(&in_pl, buf) == 0)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Invalid proto/port string");
|
|
free_acc_port_list(in_pl);
|
|
return(0);
|
|
}
|
|
|
|
start = ndx+1;
|
|
ctr = 0;
|
|
}
|
|
ctr++;
|
|
}
|
|
if((ctr >= ACCESS_BUF_LEN)
|
|
|| (((ndx-start)+1) >= ACCESS_BUF_LEN))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Unable to create acc_port_list from incoming data: %s",
|
|
port_str
|
|
);
|
|
free_acc_port_list(in_pl);
|
|
return(0);
|
|
}
|
|
strlcpy(buf, start, (ndx-start)+1);
|
|
if(add_port_list_ent(&in_pl, buf) == 0)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Invalid proto/port string");
|
|
free_acc_port_list(in_pl);
|
|
return 0;
|
|
}
|
|
|
|
if(in_pl == NULL)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Unable to create acc_port_list from incoming data: %s", port_str
|
|
);
|
|
return(0);
|
|
}
|
|
|
|
/* Start with restricted ports (if any). Any match (even if only one
|
|
* entry) means not allowed.
|
|
*/
|
|
if((acc->rport_list != NULL) && (compare_port_list(in_pl, r_pl, 1)))
|
|
{
|
|
res = 0;
|
|
goto cleanup_and_bail;
|
|
}
|
|
|
|
/* For open port list, all must match.
|
|
*/
|
|
if((acc->oport_list != NULL) && (!compare_port_list(in_pl, o_pl, 0)))
|
|
res = 0;
|
|
|
|
cleanup_and_bail:
|
|
free_acc_port_list(in_pl);
|
|
return(res);
|
|
}
|
|
|
|
/* Dump the configuration
|
|
*/
|
|
void
|
|
dump_access_list(const fko_srv_options_t *opts)
|
|
{
|
|
int i = 0;
|
|
|
|
acc_stanza_t *acc = opts->acc_stanzas;
|
|
|
|
fprintf(stdout, "Current fwknopd access settings:\n");
|
|
|
|
if(!acc)
|
|
{
|
|
fprintf(stderr, "\n ** No Access Settings Defined **\n\n");
|
|
return;
|
|
}
|
|
|
|
while(acc)
|
|
{
|
|
fprintf(stdout,
|
|
"SOURCE (%i): %s\n"
|
|
"==============================================================\n"
|
|
" DESTINATION: %s\n"
|
|
" OPEN_PORTS: %s\n"
|
|
" RESTRICT_PORTS: %s\n"
|
|
" KEY: %s\n"
|
|
" KEY_BASE64: %s\n"
|
|
" KEY_LEN: %d\n"
|
|
" HMAC_KEY: %s\n"
|
|
" HMAC_KEY_BASE64: %s\n"
|
|
" HMAC_KEY_LEN: %d\n"
|
|
" HMAC_DIGEST_TYPE: %d\n"
|
|
" FW_ACCESS_TIMEOUT: %i\n"
|
|
" MAX_FW_TIMEOUT: %i\n"
|
|
" ENABLE_CMD_EXEC: %s\n"
|
|
" ENABLE_CMD_SUDO_EXEC: %s\n"
|
|
" CMD_SUDO_EXEC_USER: %s\n"
|
|
" CMD_SUDO_EXEC_GROUP: %s\n"
|
|
" CMD_EXEC_USER: %s\n"
|
|
" CMD_EXEC_GROUP: %s\n"
|
|
" CMD_CYCLE_OPEN: %s\n"
|
|
" CMD_CYCLE_CLOSE: %s\n"
|
|
" CMD_CYCLE_TIMER: %i\n"
|
|
" REQUIRE_USERNAME: %s\n"
|
|
" REQUIRE_SOURCE_ADDRESS: %s\n"
|
|
" FORCE_NAT (ip): %s\n"
|
|
" FORCE_NAT (proto): %s\n"
|
|
" FORCE_NAT (port): %d\n"
|
|
" FORCE_SNAT (ip): %s\n"
|
|
" FORCE_MASQUERADE: %s\n"
|
|
" DISABLE_DNAT: %s\n"
|
|
" FORWARD_ALL: %s\n"
|
|
" ACCESS_EXPIRE: %s" /* asctime() adds a newline */
|
|
" GPG_HOME_DIR: %s\n"
|
|
" GPG_EXE: %s\n"
|
|
" GPG_DECRYPT_ID: %s\n"
|
|
" GPG_DECRYPT_PW: %s\n"
|
|
" GPG_REQUIRE_SIG: %s\n"
|
|
"GPG_IGNORE_SIG_VERIFY_ERROR: %s\n"
|
|
" GPG_REMOTE_ID: %s\n"
|
|
" GPG_FINGERPRINT_ID: %s\n",
|
|
++i,
|
|
acc->source,
|
|
(acc->destination == NULL) ? "<not set>" : acc->destination,
|
|
(acc->open_ports == NULL) ? "<not set>" : acc->open_ports,
|
|
(acc->restrict_ports == NULL) ? "<not set>" : acc->restrict_ports,
|
|
(acc->key == NULL) ? "<not set>" : "<see the access.conf file>",
|
|
(acc->key_base64 == NULL) ? "<not set>" : "<see the access.conf file>",
|
|
acc->key_len ? acc->key_len : 0,
|
|
(acc->hmac_key == NULL) ? "<not set>" : "<see the access.conf file>",
|
|
(acc->hmac_key_base64 == NULL) ? "<not set>" : "<see the access.conf file>",
|
|
acc->hmac_key_len ? acc->hmac_key_len : 0,
|
|
acc->hmac_type,
|
|
acc->fw_access_timeout,
|
|
acc->max_fw_timeout,
|
|
acc->enable_cmd_exec ? "Yes" : "No",
|
|
acc->enable_cmd_sudo_exec ? "Yes" : "No",
|
|
(acc->cmd_sudo_exec_user == NULL) ? "<not set>" : acc->cmd_sudo_exec_user,
|
|
(acc->cmd_sudo_exec_group == NULL) ? "<not set>" : acc->cmd_sudo_exec_group,
|
|
(acc->cmd_exec_user == NULL) ? "<not set>" : acc->cmd_exec_user,
|
|
(acc->cmd_exec_group == NULL) ? "<not set>" : acc->cmd_exec_group,
|
|
(acc->cmd_cycle_open == NULL) ? "<not set>" : acc->cmd_cycle_open,
|
|
(acc->cmd_cycle_close == NULL) ? "<not set>" : acc->cmd_cycle_close,
|
|
acc->cmd_cycle_timer,
|
|
(acc->require_username == NULL) ? "<not set>" : acc->require_username,
|
|
acc->require_source_address ? "Yes" : "No",
|
|
acc->force_nat ? acc->force_nat_ip : "<not set>",
|
|
acc->force_nat && acc->force_nat_proto != NULL ? acc->force_nat_proto : "<not set>",
|
|
acc->force_nat ? acc->force_nat_port : 0,
|
|
acc->force_snat ? acc->force_snat_ip : "<not set>",
|
|
acc->force_masquerade ? "Yes" : "No",
|
|
acc->disable_dnat ? "Yes" : "No",
|
|
acc->forward_all ? "Yes" : "No",
|
|
(acc->access_expire_time > 0) ? asctime(localtime(&acc->access_expire_time)) : "<not set>\n",
|
|
(acc->gpg_home_dir == NULL) ? "<not set>" : acc->gpg_home_dir,
|
|
(acc->gpg_exe == NULL) ? "<not set>" : acc->gpg_exe,
|
|
(acc->gpg_decrypt_id == NULL) ? "<not set>" : acc->gpg_decrypt_id,
|
|
(acc->gpg_decrypt_pw == NULL) ? "<not set>" : "<see the access.conf file>",
|
|
acc->gpg_require_sig ? "Yes" : "No",
|
|
acc->gpg_ignore_sig_error ? "Yes" : "No",
|
|
(acc->gpg_remote_id == NULL) ? "<not set>" : acc->gpg_remote_id,
|
|
(acc->gpg_remote_fpr == NULL) ? "<not set>" : acc->gpg_remote_fpr
|
|
);
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
acc = acc->next;
|
|
}
|
|
|
|
fprintf(stdout, "\n");
|
|
fflush(stdout);
|
|
}
|
|
|
|
int
|
|
include_keys_file(acc_stanza_t *curr_acc, const char *access_filename, fko_srv_options_t *opts)
|
|
{
|
|
FILE *file_ptr;
|
|
unsigned int num_lines = 0;
|
|
|
|
char access_line_buf[MAX_LINE_LEN] = {0};
|
|
char var[MAX_LINE_LEN] = {0};
|
|
char val[MAX_LINE_LEN] = {0};
|
|
char *ndx;
|
|
|
|
log_msg(LOG_INFO, "Including key file: '%s'", access_filename);
|
|
|
|
if ((file_ptr = fopen(access_filename, "r")) == NULL)
|
|
{
|
|
log_msg(LOG_ERR, "[*] Could not open access file: %s",
|
|
access_filename);
|
|
perror(NULL);
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
while ((fgets(access_line_buf, MAX_LINE_LEN, file_ptr)) != NULL)
|
|
{
|
|
num_lines++;
|
|
access_line_buf[MAX_LINE_LEN-1] = '\0';
|
|
|
|
/* Get past comments and empty lines (note: we only look at the
|
|
* first character.
|
|
*/
|
|
if(IS_EMPTY_LINE(access_line_buf[0]))
|
|
continue;
|
|
|
|
if(sscanf(access_line_buf, "%s %[^;\n\r]", var, val) != 2)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] Invalid access file entry in %s at line %i.\n - '%s'",
|
|
access_filename, num_lines, access_line_buf
|
|
);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Remove the colon if present
|
|
*/
|
|
if((ndx = strrchr(var, ':')) != NULL)
|
|
*ndx = '\0';
|
|
|
|
if(CONF_VAR_IS(var, "KEY"))
|
|
{
|
|
if(strcasecmp(val, "__CHANGEME__") == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] KEY value is not properly set in stanza source '%s' in access file: '%s'",
|
|
curr_acc->source, access_filename);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_string(&(curr_acc->key), val, file_ptr, opts);
|
|
curr_acc->key_len = strlen(curr_acc->key);
|
|
add_acc_bool(&(curr_acc->use_rijndael), "Y");
|
|
}
|
|
else if(CONF_VAR_IS(var, "KEY_BASE64"))
|
|
{
|
|
if(strcasecmp(val, "__CHANGEME__") == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] KEY_BASE64 value is not properly set in stanza source '%s' in access file: '%s'",
|
|
curr_acc->source, access_filename);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (! is_base64((unsigned char *) val, strlen(val)))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] KEY_BASE64 argument '%s' doesn't look like base64-encoded data.",
|
|
val);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_string(&(curr_acc->key_base64), val, file_ptr, opts);
|
|
add_acc_b64_string(&(curr_acc->key), &(curr_acc->key_len),
|
|
curr_acc->key_base64, file_ptr, opts);
|
|
add_acc_bool(&(curr_acc->use_rijndael), "Y");
|
|
}
|
|
else if(CONF_VAR_IS(var, "HMAC_KEY_BASE64"))
|
|
{
|
|
if(strcasecmp(val, "__CHANGEME__") == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] HMAC_KEY_BASE64 value is not properly set in stanza source '%s' in access file: '%s'",
|
|
curr_acc->source, opts->config[CONF_ACCESS_FILE]);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (! is_base64((unsigned char *) val, strlen(val)))
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] HMAC_KEY_BASE64 argument '%s' doesn't look like base64-encoded data.",
|
|
val);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_string(&(curr_acc->hmac_key_base64), val, file_ptr, opts);
|
|
add_acc_b64_string(&(curr_acc->hmac_key), &(curr_acc->hmac_key_len),
|
|
curr_acc->hmac_key_base64, file_ptr, opts);
|
|
}
|
|
else if(CONF_VAR_IS(var, "HMAC_KEY"))
|
|
{
|
|
if(strcasecmp(val, "__CHANGEME__") == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] HMAC_KEY value is not properly set in stanza source '%s' in access file: '%s'",
|
|
curr_acc->source, opts->config[CONF_ACCESS_FILE]);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_string(&(curr_acc->hmac_key), val, file_ptr, opts);
|
|
curr_acc->hmac_key_len = strlen(curr_acc->hmac_key);
|
|
}
|
|
else if(CONF_VAR_IS(var, "GPG_DECRYPT_ID"))
|
|
add_acc_string(&(curr_acc->gpg_decrypt_id), val, file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "GPG_DECRYPT_PW"))
|
|
{
|
|
if(strcasecmp(val, "__CHANGEME__") == 0)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"[*] GPG_DECRYPT_PW value is not properly set in stanza source '%s' in access file: '%s'",
|
|
curr_acc->source, access_filename);
|
|
fclose(file_ptr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
add_acc_string(&(curr_acc->gpg_decrypt_pw), val, file_ptr, opts);
|
|
add_acc_bool(&(curr_acc->use_gpg), "Y");
|
|
}
|
|
else if(CONF_VAR_IS(var, "GPG_ALLOW_NO_PW"))
|
|
{
|
|
add_acc_bool(&(curr_acc->gpg_allow_no_pw), val);
|
|
if(curr_acc->gpg_allow_no_pw == 1)
|
|
{
|
|
add_acc_bool(&(curr_acc->use_gpg), "Y");
|
|
add_acc_string(&(curr_acc->gpg_decrypt_pw), "", file_ptr, opts);
|
|
}
|
|
}
|
|
else if(CONF_VAR_IS(var, "GPG_REQUIRE_SIG"))
|
|
{
|
|
add_acc_bool(&(curr_acc->gpg_require_sig), val);
|
|
}
|
|
else if(CONF_VAR_IS(var, "GPG_DISABLE_SIG"))
|
|
{
|
|
add_acc_bool(&(curr_acc->gpg_disable_sig), val);
|
|
}
|
|
else if(CONF_VAR_IS(var, "GPG_IGNORE_SIG_VERIFY_ERROR"))
|
|
{
|
|
add_acc_bool(&(curr_acc->gpg_ignore_sig_error), val);
|
|
}
|
|
else if(CONF_VAR_IS(var, "GPG_REMOTE_ID"))
|
|
add_acc_string(&(curr_acc->gpg_remote_id), val, file_ptr, opts);
|
|
else if(CONF_VAR_IS(var, "GPG_FINGERPRINT_ID"))
|
|
add_acc_string(&(curr_acc->gpg_remote_fpr), val, file_ptr, opts);
|
|
else
|
|
log_msg(LOG_INFO, "Ignoring invalid entry: '%s'", var);
|
|
}
|
|
fclose(file_ptr);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
#ifdef HAVE_C_UNIT_TESTS /* LCOV_EXCL_START */
|
|
|
|
DECLARE_UTEST(compare_port_list, "check compare_port_list function")
|
|
{
|
|
acc_port_list_t *in1_pl = NULL;
|
|
acc_port_list_t *in2_pl = NULL;
|
|
acc_port_list_t *acc_pl = NULL;
|
|
|
|
/* Match any test */
|
|
free_acc_port_list(in1_pl);
|
|
free_acc_port_list(acc_pl);
|
|
expand_acc_port_list(&in1_pl, "udp/6002");
|
|
expand_acc_port_list(&in2_pl, "udp/6002, udp/6003");
|
|
expand_acc_port_list(&acc_pl, "udp/6002, udp/6003");
|
|
CU_ASSERT(compare_port_list(in1_pl, acc_pl, 1) == 1); /* Only one match is needed from access port list - 1 */
|
|
CU_ASSERT(compare_port_list(in2_pl, acc_pl, 1) == 1); /* Only match is needed from access port list - 2 */
|
|
CU_ASSERT(compare_port_list(in1_pl, acc_pl, 0) == 1); /* All ports must match access port list - 1 */
|
|
CU_ASSERT(compare_port_list(in2_pl, acc_pl, 0) == 1); /* All ports must match access port list - 2 */
|
|
CU_ASSERT(compare_port_list(acc_pl, in1_pl, 0) == 0); /* All ports must match in1 port list - 1 */
|
|
CU_ASSERT(compare_port_list(acc_pl, in2_pl, 0) == 1); /* All ports must match in2 port list - 2 */
|
|
}
|
|
|
|
int register_ts_access(void)
|
|
{
|
|
ts_init(&TEST_SUITE(access), TEST_SUITE_DESCR(access), NULL, NULL);
|
|
ts_add_utest(&TEST_SUITE(access), UTEST_FCT(compare_port_list), UTEST_DESCR(compare_port_list));
|
|
|
|
return register_ts(&TEST_SUITE(access));
|
|
}
|
|
#endif /* HAVE_C_UNIT_TESTS */ /* LCOV_EXCL_STOP */
|
|
/***EOF***/
|