diff --git a/android/project/jni/config.h b/android/project/jni/config.h index a005368e..75d8a9db 100644 --- a/android/project/jni/config.h +++ b/android/project/jni/config.h @@ -32,6 +32,9 @@ /* Path to firewall command executable (it should match the firewall type). */ #define FIREWALL_EXE "/sbin/iptables" +/* Path to firewall command executable for IPv6 (it should match the firewall type). */ +#define FIREWALL_EXE_IPV6 "/sbin/ip6tables" + /* The firewall type: ipf. */ /* #undef FIREWALL_IPF */ diff --git a/client/config_init.c b/client/config_init.c index 90fd924a..268ac64b 100644 --- a/client/config_init.c +++ b/client/config_init.c @@ -867,9 +867,9 @@ create_fwknoprc(const char *rcfile) "#FW_TIMEOUT 30\n" "#SPA_SERVER_PORT 62201\n" "#SPA_SERVER_PROTO udp\n" - "#ALLOW_IP \n" + "#ALLOW_IP \n" "#SPOOF_USER \n" - "#SPOOF_SOURCE_IP \n" + "#SPOOF_SOURCE_IP \n" "#TIME_OFFSET 0\n" "#USE_GPG N\n" "#GPG_HOMEDIR /path/to/.gnupg\n" @@ -982,7 +982,7 @@ parse_rc_param(fko_cli_options_t *options, const char *var_name, char * val) else /* Assume IP address and validate */ { strlcpy(options->allow_ip_str, val, sizeof(options->allow_ip_str)); - if(! is_valid_ipv4_addr(options->allow_ip_str, strlen(options->allow_ip_str))) + if(! is_valid_ip_addr(options->allow_ip_str, strlen(options->allow_ip_str), AF_INET)) parse_error = -1; } } @@ -1846,7 +1846,8 @@ validate_options(fko_cli_options_t *options) exit(EXIT_FAILURE); } else if(options->verbose - && strncmp(options->allow_ip_str, "0.0.0.0", strlen("0.0.0.0")) == 0) + && (strncmp(options->allow_ip_str, "0.0.0.0", strlen("0.0.0.0")) == 0 + || strncmp(options->allow_ip_str, "::", strlen("::")) == 0)) { log_msg(LOG_VERBOSITY_WARNING, "[-] WARNING: Should use -a or -R to harden SPA against potential MITM attacks"); @@ -1861,7 +1862,7 @@ validate_options(fko_cli_options_t *options) { options->resolve_ip_http_https = 0; - if(! is_valid_ipv4_addr(options->allow_ip_str, strlen(options->allow_ip_str))) + if(! is_valid_ip_addr(options->allow_ip_str, strlen(options->allow_ip_str), AF_UNSPEC)) { log_msg(LOG_VERBOSITY_ERROR, "Invalid allow IP specified for SPA access"); @@ -1871,7 +1872,7 @@ validate_options(fko_cli_options_t *options) if (options->spoof_ip_src_str[0] != 0x00) { - if(! is_valid_ipv4_addr(options->spoof_ip_src_str, strlen(options->spoof_ip_src_str))) + if(! is_valid_ip_addr(options->spoof_ip_src_str, strlen(options->spoof_ip_src_str), AF_UNSPEC)) { log_msg(LOG_VERBOSITY_ERROR, "Invalid spoof IP"); exit(EXIT_FAILURE); diff --git a/client/fwknop.c b/client/fwknop.c index 020c627f..011a9087 100644 --- a/client/fwknop.c +++ b/client/fwknop.c @@ -774,12 +774,6 @@ set_nat_access(fko_ctx_t ctx, fko_cli_options_t *options, const char * const acc if (nat_access_buf[0] == 0x0 && options->nat_access_str[0] != 0x0) { - /* Force the ':' (if any) to a ',' - */ - ndx = strchr(options->nat_access_str, ':'); - if (ndx != NULL) - *ndx = ','; - ndx = strchr(options->nat_access_str, ','); if (ndx != NULL) { @@ -820,7 +814,7 @@ set_nat_access(fko_ctx_t ctx, fko_cli_options_t *options, const char * const acc } - if (is_valid_ipv4_addr(options->nat_access_str, hostlen) || is_valid_hostname(options->nat_access_str, hostlen)) + if (is_valid_ip_addr(options->nat_access_str, hostlen, AF_UNSPEC) || is_valid_hostname(options->nat_access_str, hostlen)) { snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE, options->nat_access_str, access_port); diff --git a/client/fwknop_common.h b/client/fwknop_common.h index 52134de7..45fc75bc 100644 --- a/client/fwknop_common.h +++ b/client/fwknop_common.h @@ -87,8 +87,8 @@ typedef struct fko_cli_options int no_save_args; int use_hmac; char spa_server_str[MAX_SERVER_STR_LEN]; /* may be a hostname */ - char allow_ip_str[MAX_IPV4_STR_LEN]; - char spoof_ip_src_str[MAX_IPV4_STR_LEN]; + char allow_ip_str[MAX_IPV46_STR_LEN]; + char spoof_ip_src_str[MAX_IPV46_STR_LEN]; char spoof_user[MAX_USERNAME_LEN]; int rand_port; char gpg_recipient_key[MAX_GPG_KEY_ID]; diff --git a/client/http_resolve_host.c b/client/http_resolve_host.c index 1ca124ea..70f0b3df 100644 --- a/client/http_resolve_host.c +++ b/client/http_resolve_host.c @@ -58,13 +58,12 @@ struct url static int try_url(struct url *url, fko_cli_options_t *options) { - int sock=-1, sock_success=0, res, error, http_buf_len, i; + int sock=-1, sock_success=0, i, res, error, http_buf_len; int bytes_read = 0, position = 0; - int o1, o2, o3, o4; struct addrinfo *result=NULL, *rp, hints; char http_buf[HTTP_MAX_REQUEST_LEN] = {0}; char http_response[HTTP_MAX_RESPONSE_LEN] = {0}; - char *ndx; + char *ndx, c; #ifdef WIN32 WSADATA wsa_data; @@ -197,45 +196,47 @@ try_url(struct url *url, fko_cli_options_t *options) } ndx += 4; - /* Walk along the content to try to find the end of the IP address. - * Note: We are expecting the content to be just an IP address - * (possibly followed by whitespace or other not-digit value). + /* Walk along the content to try to find the end of the IP address. + * Note: We are expecting the content to be just an IP address + * (possibly followed by whitespace or other not-digit value). + */ + for(i=0; i= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) && c != '.' && c != ':') + break; + } + + /* Terminate at the first non-digit and non-dot. */ - for(i=0; i= 0 && o1 <= 255 - && o2 >= 0 && o2 <= 255 - && o3 >= 0 && o3 <= 255 - && o4 >= 0 && o4 <= 255) - { - strlcpy(options->allow_ip_str, ndx, sizeof(options->allow_ip_str)); - - log_msg(LOG_VERBOSITY_INFO, - "\n[+] Resolved external IP (via http://%s%s) as: %s", - url->host, - url->path, - options->allow_ip_str); + *(ndx+i) = '\0'; - return(1); - } - else + /* Try to parse the content as an IP address. */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_flags = AI_NUMERICHOST | AI_CANONNAME; + error = getaddrinfo(ndx, NULL, &hints, &result); + if (error != 0) { log_msg(LOG_VERBOSITY_ERROR, "[-] From http://%s%s\n Invalid IP (%s) in HTTP response:\n\n%s", url->host, url->path, ndx, http_response); return(-1); } + for (rp = result; rp != NULL; rp = rp->ai_next) { + /* the canonical value is in the first structure returned */ + strlcpy(options->allow_ip_str, + rp->ai_canonname, sizeof(options->allow_ip_str)); + break; + } + freeaddrinfo(result); + + log_msg(LOG_VERBOSITY_INFO, + "\n[+] Resolved external IP (via http://%s%s) as: %s", + url->host, + url->path, + options->allow_ip_str); + + return(1); } static int @@ -323,8 +324,9 @@ parse_url(char *res_url, struct url* url) int resolve_ip_https(fko_cli_options_t *options) { - int o1, o2, o3, o4, got_resp=0, i=0; - char *ndx, resp[MAX_IPV4_STR_LEN+1] = {0}; + int got_resp=0, error; + char resp[MAX_IPV4_STR_LEN+1] = {0}; + struct addrinfo *result=NULL, *rp, hints; struct url url; /* for validation only */ char wget_ssl_cmd[MAX_URL_PATH_LEN] = {0}; /* for verbose logging only */ @@ -493,32 +495,35 @@ resolve_ip_https(fko_cli_options_t *options) pclose(wget); #endif - if(got_resp) + if(! got_resp) { - ndx = resp; - for(i=0; i= 0 && o1 <= 255 - && o2 >= 0 && o2 <= 255 - && o3 >= 0 && o3 <= 255 - && o4 >= 0 && o4 <= 255) - { - strlcpy(options->allow_ip_str, ndx, sizeof(options->allow_ip_str)); + log_msg(LOG_VERBOSITY_ERROR, + "[-] Could not resolve IP via: '%s'", wget_ssl_cmd); + return -1; + } - log_msg(LOG_VERBOSITY_INFO, - "\n[+] Resolved external IP (via '%s') as: %s", - wget_ssl_cmd, options->allow_ip_str); - return 1; - } + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_flags = AI_NUMERICHOST | AI_CANONNAME; + error = getaddrinfo(resp, NULL, &hints, &result); + if (error != 0) + { + log_msg(LOG_VERBOSITY_ERROR, + "[-] Could not resolve IP via: '%s'", wget_ssl_cmd); + return(-1); } - log_msg(LOG_VERBOSITY_ERROR, - "[-] Could not resolve IP via: '%s'", wget_ssl_cmd); - return -1; + for (rp = result; rp != NULL; rp = rp->ai_next) { + /* the canonical value is in the first structure returned */ + strlcpy(options->allow_ip_str, + rp->ai_canonname, sizeof(options->allow_ip_str)); + break; + } + freeaddrinfo(result); + + log_msg(LOG_VERBOSITY_INFO, + "\n[+] Resolved external IP (via '%s') as: %s", + wget_ssl_cmd, options->allow_ip_str); + return 1; } int diff --git a/client/spa_comm.c b/client/spa_comm.c index aae4a97e..b8aad8e0 100644 --- a/client/spa_comm.c +++ b/client/spa_comm.c @@ -82,7 +82,7 @@ chksum(unsigned short *buf, int nbytes) /* Send the SPA data via UDP packet. */ static int -send_spa_packet_tcp_or_udp(const char *spa_data, const int sd_len, +send_spa_packet_tcp_or_udp(const char *spa_data, const size_t sd_len, const fko_cli_options_t *options) { int sock=-1, sock_success=0, res=0, error; @@ -98,7 +98,7 @@ send_spa_packet_tcp_or_udp(const char *spa_data, const int sd_len, memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET; /* Allow IPv4 only */ + hints.ai_family = AF_UNSPEC; if (options->spa_proto == FKO_PROTO_UDP) { @@ -201,7 +201,7 @@ send_spa_packet_tcp_or_udp(const char *spa_data, const int sd_len, /* Send the SPA data via raw TCP packet. */ static int -send_spa_packet_tcp_raw(const char *spa_data, const int sd_len, +send_spa_packet_tcp_raw(const char *spa_data, const size_t sd_len, const struct sockaddr_in *saddr, const struct sockaddr_in *daddr, const fko_cli_options_t *options) { @@ -230,7 +230,7 @@ send_spa_packet_tcp_raw(const char *spa_data, const int sd_len, return res; } - sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW); + sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW); if (sock < 0) { log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_tcp_raw: create socket: ", strerror(errno)); @@ -300,7 +300,7 @@ send_spa_packet_tcp_raw(const char *spa_data, const int sd_len, else if(res != sd_len + hdrlen) /* account for the header ?*/ { log_msg(LOG_VERBOSITY_WARNING, - "[#] Warning: bytes sent (%i) not spa data length (%i).", + "[#] Warning: bytes sent (%i) not spa data length (%zi).", res, sd_len ); } @@ -315,7 +315,7 @@ send_spa_packet_tcp_raw(const char *spa_data, const int sd_len, /* Send the SPA data via raw UDP packet. */ static int -send_spa_packet_udp_raw(const char *spa_data, const int sd_len, +send_spa_packet_udp_raw(const char *spa_data, const size_t sd_len, const struct sockaddr_in *saddr, const struct sockaddr_in *daddr, const fko_cli_options_t *options) { @@ -344,7 +344,7 @@ send_spa_packet_udp_raw(const char *spa_data, const int sd_len, return res; } - sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW); + sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW); if (sock < 0) { log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_udp_raw: create socket: ", strerror(errno)); @@ -400,7 +400,7 @@ send_spa_packet_udp_raw(const char *spa_data, const int sd_len, else if(res != sd_len + hdrlen) /* account for the header ?*/ { log_msg(LOG_VERBOSITY_WARNING, - "[#] Warning: bytes sent (%i) not spa data length (%i).", + "[#] Warning: bytes sent (%i) not spa data length (%zi).", res, sd_len ); } @@ -415,7 +415,7 @@ send_spa_packet_udp_raw(const char *spa_data, const int sd_len, /* Send the SPA data via ICMP packet. */ static int -send_spa_packet_icmp(const char *spa_data, const int sd_len, +send_spa_packet_icmp(const char *spa_data, const size_t sd_len, const struct sockaddr_in *saddr, const struct sockaddr_in *daddr, const fko_cli_options_t *options) { @@ -427,9 +427,9 @@ send_spa_packet_icmp(const char *spa_data, const int sd_len, char pkt_data[2048] = {0}; struct iphdr *iph = (struct iphdr *) pkt_data; - struct icmphdr *icmph = (struct icmphdr *) (pkt_data + sizeof (struct iphdr)); + struct icmphdr *icmph = (struct icmphdr *) (pkt_data + sizeof (*iph)); - int hdrlen = sizeof(struct iphdr) + sizeof(struct icmphdr); + const size_t hdrlen = sizeof(struct iphdr) + sizeof(struct icmphdr); /* Values for setsockopt. */ @@ -443,7 +443,7 @@ send_spa_packet_icmp(const char *spa_data, const int sd_len, return res; } - sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW); + sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW); if (sock < 0) { @@ -519,12 +519,13 @@ send_spa_packet_icmp(const char *spa_data, const int sd_len, /* Send the SPA data packet via an HTTP request */ static int -send_spa_packet_http(const char *spa_data, const int sd_len, +send_spa_packet_http(const char *spa_data, const size_t sd_len, fko_cli_options_t *options) { - char http_buf[HTTP_MAX_REQUEST_LEN] = {0}, *spa_data_copy = NULL; - char *ndx = options->http_proxy; - int i, proxy_port = 0, is_err; + char http_buf[HTTP_MAX_REQUEST_LEN] = {0}, *spa_data_copy = NULL; + char *ndx = options->http_proxy; + int proxy_port = 0, is_err; + size_t i; spa_data_copy = malloc(sd_len+1); if (spa_data_copy == NULL) @@ -622,11 +623,12 @@ send_spa_packet_http(const char *spa_data, const int sd_len, int send_spa_packet(fko_ctx_t ctx, fko_cli_options_t *options) { - int res, sd_len; + int res; char *spa_data; struct sockaddr_in saddr, daddr; char ip_str[INET_ADDRSTRLEN] = {0}; /* String used to contain the ip address of an hostname */ struct addrinfo hints; /* Structure used to set hints to resolve hostname */ + size_t sd_len; #ifdef WIN32 WSADATA wsa_data; #endif @@ -687,14 +689,14 @@ send_spa_packet(fko_ctx_t ctx, fko_cli_options_t *options) if (options->spa_src_port) saddr.sin_port = htons(options->spa_src_port); else - saddr.sin_port = INADDR_ANY; + saddr.sin_port = 0; if (options->spoof_ip_src_str[0] != 0x00) { saddr.sin_addr.s_addr = inet_addr(options->spoof_ip_src_str); } else saddr.sin_addr.s_addr = INADDR_ANY; /* default */ - if (saddr.sin_addr.s_addr == -1) + if (saddr.sin_addr.s_addr == INADDR_NONE) { log_msg(LOG_VERBOSITY_ERROR, "Could not set source IP."); return -1; @@ -719,7 +721,7 @@ send_spa_packet(fko_ctx_t ctx, fko_cli_options_t *options) if (resolve_dst_addr(options->spa_server_str, &hints, ip_str, sizeof(ip_str), options) != 0) { - log_msg(LOG_VERBOSITY_ERROR, "[*] Unable to resolve %s as an ip address", + log_msg(LOG_VERBOSITY_ERROR, "[*] Unable to resolve %s as an IP address", options->spa_server_str); return -1; } diff --git a/common/fko_util.c b/common/fko_util.c index ca9a58b7..ddd6898b 100644 --- a/common/fko_util.c +++ b/common/fko_util.c @@ -35,9 +35,15 @@ #ifndef WIN32 /* for inet_aton() IP validation */ - #include - #include - #include +# if HAVE_SYS_SOCKET_H +# include +#endif +# if HAVE_NETINET_IN_H +# include +# endif +# if HAVE_ARPA_INET_H +# include +# endif #endif /* Check for a FKO error returned by a function an return the error code */ @@ -119,57 +125,27 @@ is_valid_encoded_msg_len(const int len) return(1); } -/* Validate an IPv4 address +/* Validate an IP address */ int -is_valid_ipv4_addr(const char * const ip_str, const int len) +is_valid_ip_addr(const char * const ip_str, const int len, const int family) { - const char *ndx = ip_str; - char tmp_ip_str[MAX_IPV4_STR_LEN + 1] = {0}; - int dot_ctr = 0, char_ctr = 0; - int res = 1; -#if HAVE_SYS_SOCKET_H - struct in_addr in; -#endif - - if(ip_str == NULL) - return 0; - - if((len > MAX_IPV4_STR_LEN) || (len < MIN_IPV4_STR_LEN)) - return 0; - - while(char_ctr < len) - { - /* If we've hit a null within the given length, then not valid regardless */ - if(*ndx == '\0') - return 0; - - char_ctr++; - - if(*ndx == '.') - dot_ctr++; - else if(isdigit((int)(unsigned char)*ndx) == 0) - { - res = 0; - break; - } - ndx++; + struct addrinfo * result, hints; + int error; + char * p; + + if((p = strndup(ip_str, len)) == NULL) + return 0; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(p, NULL, &hints, &result); + free(p); + if (error) { + return 0; } - - if((res == 1) && (dot_ctr != 3)) - res = 0; - -#if HAVE_SYS_SOCKET_H - /* Stronger IP validation now that we have a candidate that looks - * close enough - */ - if(res == 1) { - strncpy(tmp_ip_str, ip_str, len); - if (inet_aton(tmp_ip_str, &in) == 0) - res = 0; - } -#endif - return(res); + freeaddrinfo(result); + return 1; } /* Validate a hostname @@ -223,9 +199,6 @@ is_valid_hostname(const char * const hostname_str, const int len) if (*ndx == '-') return 0; - if (*ndx == '.') - total_size--; - if (label_size > 63) return 0; @@ -626,7 +599,7 @@ char { char* ns = NULL; if(s) { - ns = calloc(1, len + 1); + ns = malloc(len + 1); if(ns) { ns[len] = 0; // strncpy to be pedantic about modification in multithreaded @@ -748,7 +721,7 @@ add_argv(char **argv_new, int *argc_new, const char *new_arg) int buf_size = 0; buf_size = strlen(new_arg) + 1; - argv_new[*argc_new] = calloc(1, buf_size); + argv_new[*argc_new] = malloc(buf_size); if(argv_new[*argc_new] == NULL) return 0; @@ -1062,10 +1035,10 @@ get_in_addr(struct sockaddr *sa) * @return 0 if successful, 1 if an error occurred. */ int -ipv4_resolve(const char *dns_str, char *ip_str) +ip_resolve(const char *dns_str, char *ip_str, int family) { int error; /* Function error return code */ - size_t ip_bufsize = MAX_IPV4_STR_LEN; + size_t ip_bufsize = MAX_IPV46_STR_LEN; struct addrinfo hints; struct addrinfo *result; /* Result of getaddrinfo() */ struct addrinfo *rp; /* Element of the linked list returned by getaddrinfo() */ @@ -1074,7 +1047,7 @@ ipv4_resolve(const char *dns_str, char *ip_str) struct sockaddr_in *in; char *win_ip; #else - struct sockaddr_in *sai_remote; /* Remote host information as a sockaddr_in structure */ + char *sai_remote; /* Remote host information */ #endif #if WIN32 @@ -1088,7 +1061,7 @@ ipv4_resolve(const char *dns_str, char *ip_str) #endif memset(&hints, 0 , sizeof(hints)); - hints.ai_family = AF_INET; + hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; @@ -1107,15 +1080,17 @@ ipv4_resolve(const char *dns_str, char *ip_str) memset(ip_str, 0, ip_bufsize); #if WIN32 && WINVER <= 0x0600 - /* On older Windows systems (anything before Vista?), - * we use inet_ntoa for now. - */ - in = (struct sockaddr_in*)(rp->ai_addr); - win_ip = inet_ntoa(in->sin_addr); - - if (win_ip != NULL && (strlcpy(ip_str, win_ip, ip_bufsize) > 0)) + if(rp->ai_family != AF_INET) + continue; + /* On older Windows systems (anything before Vista?), + * we use inet_ntoa for now. + */ + in = (struct sockaddr_in*)(rp->ai_addr); + win_ip = inet_ntoa(in->sin_addr); + + if (win_ip != NULL && (strlcpy(ip_str, win_ip, ip_bufsize) > 0)) #else - sai_remote = (struct sockaddr_in *)get_in_addr((struct sockaddr *)(rp->ai_addr)); + sai_remote = get_in_addr((struct sockaddr *)(rp->ai_addr)); if (inet_ntop(rp->ai_family, sai_remote, ip_str, ip_bufsize) != NULL) #endif { @@ -1169,15 +1144,15 @@ DECLARE_UTEST(test_hostname_validator, "test the is_valid_hostname function") strcpy(test_hostname, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.b"); CU_ASSERT(is_valid_hostname(test_hostname, strlen(test_hostname)) == 0); } -DECLARE_UTEST(test_ipv4_validator, "test the is_valid_ipv4_addr function") +DECLARE_UTEST(test_ipv4_validator, "test the is_valid_ip_addr function") { char test_str[32]; strcpy(test_str, "1.2.3.4"); - CU_ASSERT(is_valid_ipv4_addr(test_str, strlen(test_str))); + CU_ASSERT(is_valid_ip_addr(test_str, strlen(test_str), AF_INET)); strcpy(test_str, "127.0.0.2"); - CU_ASSERT(is_valid_ipv4_addr(test_str, 9)); + CU_ASSERT(is_valid_ip_addr(test_str, 9, AF_INET)); strcpy(test_str, "1.2.3.400"); - CU_ASSERT(is_valid_ipv4_addr(test_str, strlen(test_str)) == 0); + CU_ASSERT(is_valid_ip_addr(test_str, strlen(test_str), AF_INET) == 0); } DECLARE_UTEST(test_count_characters, "test the count_characters function") diff --git a/common/fko_util.h b/common/fko_util.h index c5ad8f56..83f2486b 100644 --- a/common/fko_util.h +++ b/common/fko_util.h @@ -40,7 +40,7 @@ */ int is_valid_encoded_msg_len(const int len); int is_valid_pt_msg_len(const int len); -int is_valid_ipv4_addr(const char * const ip_str, const int len); +int is_valid_ip_addr(const char * const ip_str, const int len, const int family); int is_valid_hostname(const char * const hostname_str, const int len); int is_base64(const unsigned char * const buf, const unsigned short int len); void hex_dump(const unsigned char *data, const int size); @@ -74,7 +74,7 @@ int count_characters(const char *str, const char match, int len); int strtoargv(const char * const args_str, char **argv_new, int *argc_new); void free_argv(char **argv_new, int *argc_new); -int ipv4_resolve(const char *dns_str, char *ip_str); +int ip_resolve(const char *dns_str, char *ip_str, int family); #if !HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t siz); #endif diff --git a/common/netinet_common.h b/common/netinet_common.h index c761cb45..642557b6 100644 --- a/common/netinet_common.h +++ b/common/netinet_common.h @@ -45,6 +45,9 @@ #if HAVE_NETINET_IN_H #include #endif + #if HAVE_NETINET_IP6_H + #include + #endif #if PLATFORM_NETBSD || PLATFORM_OPENBSD /* for autoconf net/if.h difficulties */ #include #include diff --git a/configure.ac b/configure.ac index 238e7128..4b1858fe 100644 --- a/configure.ac +++ b/configure.ac @@ -369,7 +369,15 @@ AC_HEADER_STDC AC_HEADER_TIME AC_HEADER_RESOLV -AC_CHECK_HEADERS([arpa/inet.h ctype.h endian.h errno.h locale.h netdb.h net/ethernet.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/byteorder.h sys/endian.h sys/ethernet.h sys/socket.h sys/stat.h sys/time.h sys/wait.h termios.h time.h unistd.h]) +AC_CHECK_HEADERS([arpa/inet.h ctype.h endian.h errno.h locale.h netdb.h net/ethernet.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/byteorder.h sys/endian.h sys/ethernet.h sys/socket.h sys/stat.h sys/time.h sys/types.h sys/wait.h termios.h time.h unistd.h]) +AC_CHECK_HEADERS([netinet/ip6.h netinet/icmp6.h], [], [], +[#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +]) # Type checks. # @@ -677,6 +685,24 @@ dnl ] ) +dnl Check for ip6tables +dnl + AC_ARG_WITH([ip6tables], + [AS_HELP_STRING([--with-ip6tables=/path/to/ip6tables], + [Specify path to the ip6tables executable @<:@default=check path@:>@])], + [ + AS_IF([ test "x$withval" = xno ], [], + AS_IF([ test "x$withval" = x -o "x$withval" = xyes ], + [AC_MSG_ERROR([--with-ip6tables requires an argument specifying a path to ip6tables])], + [ FORCE_IP6TABLES_EXE=$withval ] + ) + ) + ], + [ + AC_PATH_PROG(IP6TABLES_EXE, [ip6tables], [], [$APP_PATH]) + ] + ) + dnl Check for ipfw dnl AC_ARG_WITH([ipfw], @@ -736,19 +762,24 @@ dnl AS_IF([test "x$FORCE_FIREWALLD_EXE" != x], [ FIREWALLD_EXE="$FORCE_FIREWALLD_EXE" ],[ - AS_IF([test "x$FORCE_IPTABLES_EXE" != x], [ - IPTABLES_EXE="$FORCE_IPTABLES_EXE" + AS_IF([test "x$FORCE_IPTABLES_EXE" != x -o "x$FORCE_IP6TABLES_EXE" != x], [ + AS_IF([test "x$FORCE_IPTABLES_EXE" != x], [ + IPTABLES_EXE="$FORCE_IPTABLES_EXE"]) + AS_IF([test "x$FORCE_IP6TABLES_EXE" != x], [ + IP6TABLES_EXE="$FORCE_IP6TABLES_EXE"]) FIREWALLD_EXE="" ],[ AS_IF([test "x$FORCE_IPFW_EXE" != x], [ IPFW_EXE="$FORCE_IPFW_EXE" IPTABLES_EXE="" + IP6TABLES_EXE="" FIREWALLD_EXE="" ],[ AS_IF([test "x$FORCE_PF_EXE" != x], [ PF_EXE="$FORCE_PF_EXE" IPFW_EXE="" IPTABLES_EXE="" + IP6TABLES_EXE="" FIREWALLD_EXE="" ],[ AS_IF([test "x$FORCE_IPF_EXE" != x], [ @@ -756,6 +787,7 @@ dnl PF_EXE="" IPFW_EXE="" IPTABLES_EXE="" + IP6TABLES_EXE="" FIREWALLD_EXE="" ] ] @@ -774,10 +806,11 @@ dnl FIREWALL_EXE=$FIREWALLD_EXE AC_DEFINE_UNQUOTED([FIREWALL_FIREWALLD], [1], [The firewall type: firewalld.]) ],[ - AS_IF([test "x$IPTABLES_EXE" != x], [ + AS_IF([test "x$IPTABLES_EXE" != x -o "x$IP6TABLES_EXE" != x], [ FW_DEF="FW_IPTABLES" FIREWALL_TYPE="iptables" FIREWALL_EXE=$IPTABLES_EXE + FIREWALL_EXE_IPV6=$IP6TABLES_EXE AC_DEFINE_UNQUOTED([FIREWALL_IPTABLES], [1], [The firewall type: iptables.]) ],[ AS_IF([test "x$IPFW_EXE" != x], [ @@ -807,6 +840,9 @@ dnl AC_DEFINE_UNQUOTED([FIREWALL_EXE], ["$FIREWALL_EXE"], [Path to firewall command executable (it should match the firewall type).]) + AC_DEFINE_UNQUOTED([FIREWALL_EXE_IPV6], ["$FIREWALL_EXE_IPV6"], + [Path to firewall command executable for IPv6 (it should match the firewall type).]) + ], [test "$want_server" = no], [ use_ndbm=no @@ -841,8 +877,11 @@ echo " if [test "$want_server" = "yes" ]; then echo " Server support: firewall type: $FIREWALL_TYPE - firewall program path: $FIREWALL_EXE -" + firewall program path: $FIREWALL_EXE" +if [test "$FIREWALL_TYPE" = "iptables" ]; then + echo " firewall program path: $FIREWALL_EXE_IPV6 (for IPv6)" + fi +echo if [test "$want_udp_server" = "yes" ]; then echo " UDP server mode enabled, no libpcap dependency " diff --git a/doc/fwknopd.man.asciidoc b/doc/fwknopd.man.asciidoc index 24669bff..440a4851 100644 --- a/doc/fwknopd.man.asciidoc +++ b/doc/fwknopd.man.asciidoc @@ -46,6 +46,10 @@ COMMAND-LINE OPTIONS option is not usually needed because the ``PCAP_INTF'' keyword in the 'fwknopd.conf' file defines the sniffing interface. +*-6, --ipv6*:: + Bind the UDP and TCP sockets on IPv6 addresses. This does not affect PCAP + mode. + *-f, --foreground*:: Run *fwknopd* in the foreground instead of becoming a daemon. When run in the foreground, message that would go to the log would instead be diff --git a/extras/openwrt/package/fwknop/Makefile b/extras/openwrt/package/fwknop/Makefile index 22dc5a20..dbfd7a7c 100644 --- a/extras/openwrt/package/fwknop/Makefile +++ b/extras/openwrt/package/fwknop/Makefile @@ -40,7 +40,8 @@ endef CONFIGURE_ARGS += \ --disable-client \ --without-gpgme \ - --with-iptables=/usr/sbin/iptables + --with-iptables=/usr/sbin/iptables \ + --with-ip6tables=/usr/sbin/ip6tables @@ -108,6 +109,7 @@ define Build/Configure --with-gpgme \ --with-gpg=/usr/bin/gpg \ --with-iptables=/usr/sbin/iptables \ + --with-ip6tables=/usr/sbin/ip6tables \ --with-sh=/bin/sh \ ) endef diff --git a/iphone/Classes/config.h b/iphone/Classes/config.h index 61beea02..50a0945c 100644 --- a/iphone/Classes/config.h +++ b/iphone/Classes/config.h @@ -26,6 +26,9 @@ Copyright (C) Max Kastanas 2010 /* Path to firewall command executable (it should match the firewall type). */ #define FIREWALL_EXE "/sbin/iptables" +/* Path to firewall command executable for IPv6 (it should match the firewall type). */ +#define FIREWALL_EXE_IPV6 "/sbin/ip6tables" + /* The firewall type: ipf. */ /* #undef FIREWALL_IPF */ diff --git a/lib/fko_limits.h b/lib/fko_limits.h index 5f02cc18..2a326008 100644 --- a/lib/fko_limits.h +++ b/lib/fko_limits.h @@ -59,6 +59,12 @@ #define MAX_IPV4_STR_LEN 16 #define MIN_IPV4_STR_LEN 7 +#define MAX_IPV46_STR_LEN 40 +#define MIN_IPV46_STR_LEN 3 + +#define MAX_IPV6_STR_LEN 40 +#define MIN_IPV6_STR_LEN 3 + #define MAX_PROTO_STR_LEN 4 /* tcp, udp, icmp for now */ #define MAX_PORT_STR_LEN 5 #define MAX_PORT 65535 diff --git a/lib/fko_message.c b/lib/fko_message.c index 2faa7052..02859bc2 100644 --- a/lib/fko_message.c +++ b/lib/fko_message.c @@ -35,7 +35,7 @@ static int have_allow_ip(const char *msg) { const char *ndx = msg; - char ip_str[MAX_IPV4_STR_LEN]; + char ip_str[MAX_IPV46_STR_LEN]; int dot_ctr = 0, char_ctr = 0; int res = FKO_SUCCESS; @@ -43,14 +43,14 @@ have_allow_ip(const char *msg) { ip_str[char_ctr] = *ndx; char_ctr++; - if(char_ctr >= MAX_IPV4_STR_LEN) + if(char_ctr >= sizeof(ip_str)) { res = FKO_ERROR_INVALID_ALLOW_IP; break; } - if(*ndx == '.') + if(*ndx == '.' || *ndx == ':') dot_ctr++; - else if(isdigit((int)(unsigned char)*ndx) == 0) + else if(isdigit((int)(unsigned char)*ndx) == 0 && !((*ndx >= 'a' && *ndx <= 'f') || (*ndx >= 'A' && *ndx <= 'F'))) { res = FKO_ERROR_INVALID_ALLOW_IP; break; @@ -58,13 +58,13 @@ have_allow_ip(const char *msg) ndx++; } - if(char_ctr < MAX_IPV4_STR_LEN) + if(char_ctr < sizeof(ip_str)) ip_str[char_ctr] = '\0'; else res = FKO_ERROR_INVALID_ALLOW_IP; if(res == FKO_SUCCESS) - if (! is_valid_ipv4_addr(ip_str, strlen(ip_str))) + if (! is_valid_ip_addr(ip_str, strlen(ip_str), AF_UNSPEC)) res = FKO_ERROR_INVALID_ALLOW_IP; return(res); diff --git a/perl/FKO/lib/FKO.pm b/perl/FKO/lib/FKO.pm index 6e79d71d..a1a2608c 100644 --- a/perl/FKO/lib/FKO.pm +++ b/perl/FKO/lib/FKO.pm @@ -614,7 +614,7 @@ can be found at http://www.cipherdyne.org/fwknop. =item B The C method creates the I object. With no arguments, it creates -creates and empty I object ready to be popluated with data (i.e. create +creates and empty I object ready to be populated with data (i.e. create a new SPA data packet to send). You can also pass existing encoded/encrypted I data, a decryption diff --git a/server/access.c b/server/access.c index d2abee91..551a1d24 100644 --- a/server/access.c +++ b/server/access.c @@ -297,7 +297,7 @@ add_acc_force_nat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } - if(! is_valid_ipv4_addr(ip_str, strlen(ip_str))) + if(! is_valid_ip_addr(ip_str, strlen(ip_str), AF_INET)) { log_msg(LOG_ERR, "[*] Fatal: invalid FORCE_NAT IP '%s'", ip_str); @@ -327,7 +327,7 @@ add_acc_force_snat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } - if(! is_valid_ipv4_addr(ip_str, strlen(ip_str))) + if(! is_valid_ip_addr(ip_str, strlen(ip_str), AF_INET)) { log_msg(LOG_ERR, "[*] Fatal: invalid FORCE_SNAT IP '%s'", ip_str); @@ -351,13 +351,16 @@ 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; + char ip_str[MAX_IPV46_STR_LEN] = {0}; + char ip_mask_str[MAX_IPV46_STR_LEN] = {0}; + uint32_t mask; + int is_err, mask_len = 0, need_shift = 1; - struct in_addr in; - struct in_addr mask_in; + struct in_addr in; + struct in_addr mask_in; + struct addrinfo *ai, hints; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; acc_int_list_t *last_sle, *new_sle, *tmp_sle; @@ -373,8 +376,8 @@ add_int_ent(acc_int_list_t **ilist, const char *ip) */ if(strcasecmp(ip, "ANY") == 0) { - new_sle->maddr = 0x0; - new_sle->mask = 0x0; + new_sle->family = AF_UNSPEC; + memset(&new_sle->acc_int, 0, sizeof(new_sle->acc_int)); } else { @@ -383,7 +386,7 @@ add_int_ent(acc_int_list_t **ilist, const char *ip) */ if((ndx = strchr(ip, '/')) != NULL) { - if(((ndx-ip)) >= MAX_IPV4_STR_LEN) + if(((ndx-ip)) >= MAX_IPV46_STR_LEN) { log_msg(LOG_ERR, "[*] Error parsing string to IP"); free(new_sle); @@ -393,7 +396,7 @@ add_int_ent(acc_int_list_t **ilist, const char *ip) mask_len = strlen(ip) - (ndx-ip+1); - if(mask_len > 2) + if(mask_len > 3) { if(mask_len >= MIN_IPV4_STR_LEN && mask_len < MAX_IPV4_STR_LEN) { @@ -420,36 +423,32 @@ add_int_ent(acc_int_list_t **ilist, const char *ip) 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 + else if(mask_len > 0) { + /* CIDR mask + */ + mask = strtol_wrapper(ndx+1, 1, 128, NO_EXIT_UPON_ERR, &is_err); + if(is_err != FKO_SUCCESS) { - log_msg(LOG_ERR, "[*] Missing mask value."); + 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) + if(strnlen(ip, MAX_IPV46_STR_LEN+1) >= MAX_IPV46_STR_LEN) { log_msg(LOG_ERR, "[*] Error parsing string to IP"); free(new_sle); @@ -459,31 +458,70 @@ add_int_ent(acc_int_list_t **ilist, const char *ip) strlcpy(ip_str, ip, sizeof(ip_str)); } - if(inet_aton(ip_str, &in) == 0) + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST | AI_CANONNAME; + if(getaddrinfo(ip_str, NULL, &hints, &ai) != 0) { log_msg(LOG_ERR, "[*] Fatal error parsing IP to int for: %s", ip_str ); - free(new_sle); new_sle = NULL; - return 0; } + switch(ai->ai_family) + { + case AF_INET: + sin = (struct sockaddr_in *)ai->ai_addr; + in = sin->sin_addr; - /* 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; + /* Store our mask converted from CIDR to a 32-bit value. + */ + if(mask > 32) + { + log_msg(LOG_ERR, "[*] Invalid IP mask '%u'.", mask); + freeaddrinfo(ai); + free(new_sle); + new_sle = NULL; + return 0; + } + else if(mask == 32) + new_sle->acc_int.inet.mask = 0xFFFFFFFF; + else if(need_shift && (mask > 0 && mask < 32)) + new_sle->acc_int.inet.mask = (0xFFFFFFFF << (32 - mask)); + else + new_sle->acc_int.inet.mask = mask; + + /* Store our masked address for comparisons with future incoming + * packets. + */ + new_sle->acc_int.inet.maddr = ntohl(in.s_addr) & new_sle->acc_int.inet.mask; + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)ai->ai_addr; + new_sle->acc_int.inet6.maddr = sin6->sin6_addr; + if(mask > 128) + { + log_msg(LOG_ERR, "[*] Invalid IPv6 prefix '%u'.", mask); + freeaddrinfo(ai); + free(new_sle); + new_sle = NULL; + return 0; + } + new_sle->acc_int.inet6.prefix = mask; + break; + default: + log_msg(LOG_ERR, + "[*] Unsupported family parsing IP to int for: %s", ip_str + ); + free(new_sle); + new_sle = NULL; + freeaddrinfo(ai); + return 0; + } + new_sle->family = ai->ai_family; + freeaddrinfo(ai); } /* If this is not the first entry, we walk our pointer to the @@ -2069,23 +2107,61 @@ int valid_access_stanzas(acc_stanza_t *acc) return 1; } +static int +compare_addr_list_ipv4(acc_int_list_t *ip_list, uint32_t ip) +{ + for(; ip_list; ip_list = ip_list->next) + { + if(ip_list->family == AF_UNSPEC) + return 1; + if(ip_list->family != AF_INET6) + continue; + if((ip & ip_list->acc_int.inet.mask) == (ip_list->acc_int.inet.maddr & ip_list->acc_int.inet.mask)) + return 1; + } + return 0; +} + +static int +compare_addr_list_ipv6(acc_int_list_t *ip_list, struct in6_addr *ip6) +{ + for(; ip_list; ip_list = ip_list->next) + { + if(ip_list->family == AF_UNSPEC) + return 1; + if(ip_list->family != AF_INET6) + continue; + if(memcmp(&ip_list->acc_int.inet6.maddr, ip6, sizeof(*ip6)) == 0) + return 1; + } + return 0; +} + int -compare_addr_list(acc_int_list_t *ip_list, const uint32_t ip) +compare_addr_list(acc_int_list_t *ip_list, int family, ...) { - int match = 0; + int res; + va_list ap; + uint32_t ip; + struct in6_addr * ip6; - while(ip_list) + va_start(ap, family); + switch(family) { - if((ip & ip_list->mask) == (ip_list->maddr & ip_list->mask)) - { - match = 1; + case AF_INET: + ip = va_arg(ap, uint32_t); + res = compare_addr_list_ipv4(ip_list, ip); + break; + case AF_INET6: + ip6 = va_arg(ap, struct in6_addr *); + res = compare_addr_list_ipv6(ip_list, ip6); + break; + default: + res = 0; break; - } - - ip_list = ip_list->next; } - - return(match); + va_end(ap); + return res; } /** diff --git a/server/access.h b/server/access.h index 02dc9b19..8b37a4cf 100644 --- a/server/access.h +++ b/server/access.h @@ -36,9 +36,9 @@ /** * \def ACCESS_BUF_LEN * - * \brief Allow strings as large as 123.123.123.123/255.255.255.255 + * \brief Allow strings as large as 1234:5678:9abc:deff:ffff:ffff:ffff:ffff/128 */ -#define ACCESS_BUF_LEN 33 +#define ACCESS_BUF_LEN 45 /** * \def MAX_DEPTH @@ -114,7 +114,7 @@ int valid_access_stanzas(acc_stanza_t *acc); * \return Returns true on a match * */ -int compare_addr_list(acc_int_list_t *source_list, const uint32_t ip); +int compare_addr_list(acc_int_list_t *source_list, int family, ...); /** * \brief Check for a proto-port string diff --git a/server/cmd_opts.h b/server/cmd_opts.h index a2feed5d..97aa6f9b 100644 --- a/server/cmd_opts.h +++ b/server/cmd_opts.h @@ -142,6 +142,7 @@ static char *config_map[NUMBER_OF_CONFIG_ENTRIES] = { "GPG_EXE", "SUDO_EXE", "FIREWALL_EXE", + "FIREWALL_EXE_IPV6", "VERBOSE", #if AFL_FUZZING "AFL_PKT_FILE", @@ -181,9 +182,9 @@ enum { /* Our getopt_long options string. */ #if USE_LIBNETFILTER_QUEUE - #define GETOPTS_OPTION_STRING "Aa:c:C:d:Dfhi:Kl:nO:p:P:Rr:StUvV" + #define GETOPTS_OPTION_STRING "Aa:c:C:d:Dfhi:6Kl:nO:p:P:Rr:StUvV" #else - #define GETOPTS_OPTION_STRING "Aa:c:C:d:Dfhi:Kl:O:p:P:Rr:StUvV" + #define GETOPTS_OPTION_STRING "Aa:c:C:d:Dfhi:6Kl:O:p:P:Rr:StUvV" #endif /* Our program command-line options... @@ -206,6 +207,7 @@ static struct option cmd_opts[] = {"fault-injection-tag", 1, NULL, FAULT_INJECTION_TAG}, {"help", 0, NULL, 'h'}, {"interface", 1, NULL, 'i'}, + {"ipv6", 0, NULL, '6'}, {"key-gen", 0, NULL, 'k'}, {"key-gen-file", 1, NULL, KEY_GEN_FILE }, {"key-len", 1, NULL, KEY_LEN }, diff --git a/server/config_init.c b/server/config_init.c index 58519094..36bf2511 100644 --- a/server/config_init.c +++ b/server/config_init.c @@ -554,7 +554,7 @@ validate_options(fko_srv_options_t *opts) */ if(opts->config[CONF_SNAT_TRANSLATE_IP] != NULL) { - if(! is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP]))) + if(! is_valid_ip_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP]), AF_INET)) { log_msg(LOG_ERR, "Invalid IPv4 addr for SNAT_TRANSLATE_IP" @@ -697,7 +697,7 @@ validate_options(fko_srv_options_t *opts) */ if(opts->config[CONF_SNAT_TRANSLATE_IP] != NULL) { - if(! is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP]))) + if(! is_valid_ip_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP]), AF_INET)) { log_msg(LOG_ERR, "Invalid IPv4 addr for SNAT_TRANSLATE_IP" @@ -1031,6 +1031,16 @@ validate_options(fko_srv_options_t *opts) clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } +#if FIREWALL_IPTABLES + if(opts->config[CONF_FIREWALL_EXE_IPV6] == NULL) + { + log_msg(LOG_ERR, + "[*] No firewall command executable is set for IPv6. Please check FIREWALL_EXE_IPV6 in fwknopd.conf." + ); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } +#endif + return; } @@ -1048,6 +1058,9 @@ set_preconfig_entries(fko_srv_options_t *opts) #ifdef FIREWALL_EXE set_config_entry(opts, CONF_FIREWALL_EXE, FIREWALL_EXE); #endif +#ifdef FIREWALL_EXE_IPV6 + set_config_entry(opts, CONF_FIREWALL_EXE_IPV6, FIREWALL_EXE_IPV6); +#endif } @@ -1372,6 +1385,9 @@ config_init(fko_srv_options_t *opts, int argc, char **argv) case 'i': set_config_entry(opts, CONF_PCAP_INTF, optarg); break; + case '6': + opts->family = AF_INET6; + break; case FIREWD_DISABLE_CHECK_SUPPORT: opts->firewd_disable_check_support = 1; break; @@ -1498,6 +1514,7 @@ usage(void) " a background daemon).\n" " -i, --interface - Specify interface to listen for incoming SPA\n" " packets.\n" + " -6, --ipv6 - Start the server in IPv6 mode (TCP/UDP).\n" " -C, --packet-limit - Limit the number of candidate SPA packets to\n" " process and exit when this limit is reached.\n" " -d, --digest-file - Specify an alternate digest.cache file.\n" diff --git a/server/fw_util.h b/server/fw_util.h index 01bc20dd..e3231e57 100644 --- a/server/fw_util.h +++ b/server/fw_util.h @@ -39,6 +39,7 @@ #define EXPIRE_COMMENT_PREFIX "_exp_" #define TMP_COMMENT "__TMPCOMMENT__" #define DUMMY_IP "127.0.0.2" +#define DUMMY_IPV6 "::2" #if FIREWALL_FIREWALLD #include "fw_util_firewalld.h" diff --git a/server/fw_util_firewalld.c b/server/fw_util_firewalld.c index 2b872853..1c3f4c3a 100644 --- a/server/fw_util_firewalld.c +++ b/server/fw_util_firewalld.c @@ -1402,7 +1402,7 @@ static void snat_rule(const fko_srv_options_t * const opts, /* Add SNAT or MASQUERADE rules. */ - if(acc->force_snat && acc->force_snat_ip != NULL && is_valid_ipv4_addr(acc->force_snat_ip, strlen(acc->force_snat_ip))) + if(acc->force_snat && acc->force_snat_ip != NULL && is_valid_ip_addr(acc->force_snat_ip, strlen(acc->force_snat_ip), AF_INET)) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[FIREWD_SNAT_ACCESS]); @@ -1410,7 +1410,7 @@ static void snat_rule(const fko_srv_options_t * const opts, "--to-source %s", acc->force_snat_ip); } else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL) - && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP]))) + && is_valid_ip_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP]), AF_INET)) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[FIREWD_SNAT_ACCESS]); @@ -1436,7 +1436,7 @@ static void snat_rule(const fko_srv_options_t * const opts, { /* Add SNAT or MASQUERADE rules. */ - if(acc->force_snat && acc->force_snat_ip != NULL && is_valid_ipv4_addr(acc->force_snat_ip, strlen(acc->force_snat_ip))) + if(acc->force_snat && acc->force_snat_ip != NULL && is_valid_ip_addr(acc->force_snat_ip, strlen(acc->force_snat_ip), AF_INET)) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[FIREWD_SNAT_ACCESS]); @@ -1451,7 +1451,7 @@ static void snat_rule(const fko_srv_options_t * const opts, "--to-ports %i", fst_port); } else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL) - && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP]))) + && is_valid_ip_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP]), AF_INET)) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[FIREWD_SNAT_ACCESS]); @@ -1494,7 +1494,7 @@ int process_spa_request(const fko_srv_options_t * const opts, const acc_stanza_t * const acc, spa_data_t * const spadat) { - char nat_ip[MAX_IPV4_STR_LEN] = {0}; + char nat_ip[MAX_IPV46_STR_LEN] = {0}; char nat_dst[MAX_HOSTNAME_LEN] = {0}; unsigned int nat_port = 0; unsigned int fst_proto; @@ -1561,7 +1561,7 @@ process_spa_request(const fko_srv_options_t * const opts, if((ndx != NULL) && (str_len <= MAX_HOSTNAME_LEN)) { strlcpy(nat_dst, spadat->nat_access, str_len+1); - if(! is_valid_ipv4_addr(nat_dst, str_len)) + if(! is_valid_ip_addr(nat_dst, str_len, AF_INET)) { if(strncasecmp(opts->config[CONF_ENABLE_NAT_DNS], "Y", 1) == 0) { @@ -1571,7 +1571,7 @@ process_spa_request(const fko_srv_options_t * const opts, free_acc_port_list(port_list); return res; } - if (ipv4_resolve(nat_dst, nat_ip) == 0) + if (ip_resolve(nat_dst, nat_ip, AF_INET) == 0) { log_msg(LOG_INFO, "Resolved NAT IP in SPA message"); } diff --git a/server/fw_util_iptables.c b/server/fw_util_iptables.c index 8d0c1848..341bb2bf 100644 --- a/server/fw_util_iptables.c +++ b/server/fw_util_iptables.c @@ -56,6 +56,15 @@ zero_cmd_buffers(void) memset(cmd_out, 0x0, STANDARD_CMD_OUT_BUFSIZE); } +static char const * +any_ip(int ipv6) +{ + if(ipv6) + return IPT_ANY_IPV6; + else + return IPT_ANY_IP; +} + static int pid_status = 0; static int @@ -67,7 +76,8 @@ rule_exists_no_chk_support(const fko_srv_options_t * const opts, const unsigned int port, const char * const natip, const unsigned int nat_port, - const unsigned int exp_ts) + const unsigned int exp_ts, + int ipv6) { int rule_exists=0; char ipt_line_buf[CMD_BUFSIZE] = {0}; @@ -92,7 +102,7 @@ rule_exists_no_chk_support(const fko_srv_options_t * const opts, #endif snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS, - opts->fw_config->fw_command, + ipv6 ? opts->fw_config->fw_command6 : opts->fw_config->fw_command, fwc->table, fwc->to_chain ); @@ -175,18 +185,18 @@ rule_exists_no_chk_support(const fko_srv_options_t * const opts, if(rule_exists) log_msg(LOG_DEBUG, "rule_exists_no_chk_support() %s %u -> %s expires: %u rule already exists", - proto_search, port, srcip, exp_ts); + proto_search, port, srcip, exp_ts, ipv6); else log_msg(LOG_DEBUG, "rule_exists_no_chk_support() %s %u -> %s expires: %u rule does not exist", - proto_search, port, srcip, exp_ts); + proto_search, port, srcip, exp_ts, ipv6); return(rule_exists); } static int rule_exists_chk_support(const fko_srv_options_t * const opts, - const char * const chain, const char * const rule) + const char * const chain, const char * const rule, int ipv6) { int rule_exists = 0; int res = 0; @@ -194,7 +204,8 @@ rule_exists_chk_support(const fko_srv_options_t * const opts, zero_cmd_buffers(); snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_CHK_RULE_ARGS, - opts->fw_config->fw_command, chain, rule); + ipv6 ? opts->fw_config->fw_command6 : opts->fw_config->fw_command, + chain, rule); res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, NO_TIMEOUT, &pid_status, opts); @@ -231,16 +242,17 @@ rule_exists(const fko_srv_options_t * const opts, const unsigned int port, const char * const nat_ip, const unsigned int nat_port, - const unsigned int exp_ts) + const unsigned int exp_ts, + int ipv6) { int rule_exists = 0; if(have_ipt_chk_support == 1) - rule_exists = rule_exists_chk_support(opts, fwc->to_chain, rule); + rule_exists = rule_exists_chk_support(opts, fwc->to_chain, rule, ipv6); else rule_exists = rule_exists_no_chk_support(opts, fwc, proto, srcip, (opts->fw_config->use_destination ? dstip : NULL), port, - nat_ip, nat_port, exp_ts); + nat_ip, nat_port, exp_ts, ipv6); if(rule_exists == 1) log_msg(LOG_DEBUG, "rule_exists() Rule : '%s' in %s already exists", @@ -253,7 +265,7 @@ rule_exists(const fko_srv_options_t * const opts, } static void -ipt_chk_support(const fko_srv_options_t * const opts) +ipt_chk_support(const fko_srv_options_t * const opts, int ipv6) { int res = 1; struct fw_chain *in_chain = &(opts->fw_config->chain[IPT_INPUT_ACCESS]); @@ -265,10 +277,11 @@ ipt_chk_support(const fko_srv_options_t * const opts) * delete the rule, and return. */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_TMP_CHK_RULE_ARGS, - opts->fw_config->fw_command, + ipv6 ? opts->fw_config->fw_command6 : opts->fw_config->fw_command, in_chain->table, in_chain->from_chain, 1, /* first rule */ + ipv6 ? DUMMY_IPV6 : DUMMY_IP, in_chain->target ); @@ -284,9 +297,10 @@ ipt_chk_support(const fko_srv_options_t * const opts) /* Now see if '-C' works - any output indicates failure */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_TMP_VERIFY_CHK_ARGS, - opts->fw_config->fw_command, + ipv6 ? opts->fw_config->fw_command6 : opts->fw_config->fw_command, in_chain->table, in_chain->from_chain, + ipv6 ? DUMMY_IPV6 : DUMMY_IP, in_chain->target ); @@ -313,19 +327,17 @@ ipt_chk_support(const fko_srv_options_t * const opts) zero_cmd_buffers(); snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS, - opts->fw_config->fw_command, + ipv6 ? opts->fw_config->fw_command6 : opts->fw_config->fw_command, in_chain->table, in_chain->from_chain, 1 ); run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, NO_TIMEOUT, &pid_status, opts); - - return; } static int -comment_match_exists(const fko_srv_options_t * const opts) +comment_match_exists(const fko_srv_options_t * const opts, int ipv6) { int res = 1; char *ndx = NULL; @@ -338,10 +350,11 @@ comment_match_exists(const fko_srv_options_t * const opts) * the rule and return true. */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_TMP_COMMENT_ARGS, - opts->fw_config->fw_command, + ipv6 ? opts->fw_config->fw_command6 : opts->fw_config->fw_command, in_chain->table, in_chain->from_chain, 1, /* first rule */ + ipv6 ? DUMMY_IPV6 : DUMMY_IP, in_chain->target ); @@ -355,7 +368,7 @@ comment_match_exists(const fko_srv_options_t * const opts) zero_cmd_buffers(); snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS, - opts->fw_config->fw_command, + ipv6 ? opts->fw_config->fw_command6 : opts->fw_config->fw_command, in_chain->table, in_chain->from_chain ); @@ -381,7 +394,7 @@ comment_match_exists(const fko_srv_options_t * const opts) zero_cmd_buffers(); snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS, - opts->fw_config->fw_command, + ipv6 ? opts->fw_config->fw_command6 : opts->fw_config->fw_command, in_chain->table, in_chain->from_chain, 1 @@ -394,14 +407,14 @@ comment_match_exists(const fko_srv_options_t * const opts) } static int -add_jump_rule(const fko_srv_options_t * const opts, const int chain_num) +add_jump_rule(const fko_srv_options_t * const opts, const int chain_num, int ipv6) { int res = 0, rv = 0; zero_cmd_buffers(); snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_JUMP_RULE_ARGS, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, fwc.chain[chain_num].table, fwc.chain[chain_num].from_chain, fwc.chain[chain_num].jump_rule_pos, @@ -429,14 +442,14 @@ add_jump_rule(const fko_srv_options_t * const opts, const int chain_num) } static int -chain_exists(const fko_srv_options_t * const opts, const int chain_num) +chain_exists(const fko_srv_options_t * const opts, const int chain_num, int ipv6) { int res = 0; zero_cmd_buffers(); snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_CHAIN_EXISTS_ARGS, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, fwc.chain[chain_num].table, fwc.chain[chain_num].to_chain ); @@ -460,7 +473,7 @@ chain_exists(const fko_srv_options_t * const opts, const int chain_num) } static int -jump_rule_exists_chk_support(const fko_srv_options_t * const opts, const int chain_num) +jump_rule_exists_chk_support(const fko_srv_options_t * const opts, const int chain_num, int ipv6) { int exists = 0; char rule_buf[CMD_BUFSIZE] = {0}; @@ -470,7 +483,7 @@ jump_rule_exists_chk_support(const fko_srv_options_t * const opts, const int cha fwc.chain[chain_num].to_chain ); - if(rule_exists_chk_support(opts, fwc.chain[chain_num].from_chain, rule_buf) == 1) + if(rule_exists_chk_support(opts, fwc.chain[chain_num].from_chain, rule_buf, ipv6) == 1) { log_msg(LOG_DEBUG, "jump_rule_exists_chk_support() jump rule found"); exists = 1; @@ -512,12 +525,12 @@ jump_rule_exists_no_chk_support(const fko_srv_options_t * const opts, } static int -jump_rule_exists(const fko_srv_options_t * const opts, const int chain_num) +jump_rule_exists(const fko_srv_options_t * const opts, const int chain_num, int ipv6) { int exists = 0; if(have_ipt_chk_support == 1) - exists = jump_rule_exists_chk_support(opts, chain_num); + exists = jump_rule_exists_chk_support(opts, chain_num, ipv6); else exists = jump_rule_exists_no_chk_support(opts, chain_num); @@ -567,6 +580,36 @@ fw_dump_rules(const fko_srv_options_t * const opts) got_err++; } } + + /* the same with IPv6 */ + for(i=0; i < NUM_FWKNOP_ACCESS_TYPES; i++) + { + if(fwc.chain[i].target[0] == '\0') + continue; + + zero_cmd_buffers(); + + /* Create the list command + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_ALL_RULES_ARGS, + opts->fw_config->fw_command6, + ch[i].table + ); + + res = run_extcmd(cmd_buf, NULL, 0, NO_STDERR, + NO_TIMEOUT, &pid_status, opts); + + log_msg(LOG_DEBUG, "fw_dump_rules() CMD: '%s' (res: %d)", + cmd_buf, res); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_ERR, "fw_dump_rules() Error %i from cmd:'%s': %s", + res, cmd_buf, err_buf); + got_err++; + } + } } else { @@ -605,6 +648,40 @@ fw_dump_rules(const fko_srv_options_t * const opts) got_err++; } } + + /* the same with IPv6 */ + for(i=0; i < NUM_FWKNOP_ACCESS_TYPES; i++) + { + if(fwc.chain[i].target[0] == '\0') + continue; + + zero_cmd_buffers(); + + /* Create the list command + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS, + opts->fw_config->fw_command6, + ch[i].table, + ch[i].to_chain + ); + + fprintf(stdout, "\n"); + fflush(stdout); + + res = run_extcmd(cmd_buf, NULL, 0, NO_STDERR, + NO_TIMEOUT, &pid_status, opts); + + log_msg(LOG_DEBUG, "fw_dump_rules() CMD: '%s' (res: %d)", + cmd_buf, res); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_ERR, "fw_dump_rules() Error %i from cmd:'%s': %s", + res, cmd_buf, err_buf); + got_err++; + } + } } return(got_err); @@ -613,7 +690,7 @@ fw_dump_rules(const fko_srv_options_t * const opts) /* Quietly flush and delete all fwknop custom chains. */ static void -delete_all_chains(const fko_srv_options_t * const opts) +delete_all_chains(const fko_srv_options_t * const opts, int ipv6) { int i, res, cmd_ctr = 0; @@ -626,12 +703,12 @@ delete_all_chains(const fko_srv_options_t * const opts) * is there. */ cmd_ctr = 0; - while(cmd_ctr < CMD_LOOP_TRIES && (jump_rule_exists(opts, i) == 1)) + while(cmd_ctr < CMD_LOOP_TRIES && (jump_rule_exists(opts, i, ipv6) == 1)) { zero_cmd_buffers(); snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_JUMP_RULE_ARGS, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, fwc.chain[i].table, fwc.chain[i].from_chain, fwc.chain[i].to_chain @@ -657,7 +734,7 @@ delete_all_chains(const fko_srv_options_t * const opts) /* Now flush and remove the chain. */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_FLUSH_CHAIN_ARGS, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, fwc.chain[i].table, fwc.chain[i].to_chain ); @@ -677,7 +754,7 @@ delete_all_chains(const fko_srv_options_t * const opts) zero_cmd_buffers(); snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_CHAIN_ARGS, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, fwc.chain[i].table, fwc.chain[i].to_chain ); @@ -703,7 +780,7 @@ delete_all_chains(const fko_srv_options_t * const opts) /* Delete the rule to direct traffic to the NFQ chain. */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, opts->config[CONF_NFQ_TABLE], "INPUT", 1 @@ -724,7 +801,7 @@ delete_all_chains(const fko_srv_options_t * const opts) /* Flush the NFQ chain */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_FLUSH_CHAIN_ARGS, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, opts->config[CONF_NFQ_TABLE], opts->config[CONF_NFQ_CHAIN] ); @@ -744,7 +821,7 @@ delete_all_chains(const fko_srv_options_t * const opts) /* Delete the NF_QUEUE chains and rules */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_CHAIN_ARGS, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, opts->config[CONF_NFQ_TABLE], opts->config[CONF_NFQ_CHAIN] ); @@ -760,11 +837,10 @@ delete_all_chains(const fko_srv_options_t * const opts) log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); } #endif - return; } static int -create_chain(const fko_srv_options_t * const opts, const int chain_num) +create_chain(const fko_srv_options_t * const opts, const int chain_num, int ipv6) { int res = 0, rv = 0; @@ -773,7 +849,7 @@ create_chain(const fko_srv_options_t * const opts, const int chain_num) /* Create the custom chain. */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NEW_CHAIN_ARGS, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, fwc.chain[chain_num].table, fwc.chain[chain_num].to_chain ); @@ -796,18 +872,18 @@ create_chain(const fko_srv_options_t * const opts, const int chain_num) } static int -mk_chain(const fko_srv_options_t * const opts, const int chain_num) +mk_chain(const fko_srv_options_t * const opts, const int chain_num, int ipv6) { int err = 0; /* Make sure the required chain and jump rule exist */ - if(! chain_exists(opts, chain_num)) - if(! create_chain(opts, chain_num)) + if(! chain_exists(opts, chain_num, ipv6)) + if(! create_chain(opts, chain_num, ipv6)) err++; - if (! jump_rule_exists(opts, chain_num)) - if(! add_jump_rule(opts, chain_num)) + if (! jump_rule_exists(opts, chain_num, ipv6)) + if(! add_jump_rule(opts, chain_num, ipv6)) err++; return err; @@ -816,7 +892,7 @@ mk_chain(const fko_srv_options_t * const opts, const int chain_num) /* Create the fwknop custom chains (at least those that are configured). */ static int -create_fw_chains(const fko_srv_options_t * const opts) +create_fw_chains(const fko_srv_options_t * const opts, int ipv6) { int i, got_err = 0; #if USE_LIBNETFILTER_QUEUE @@ -828,7 +904,7 @@ create_fw_chains(const fko_srv_options_t * const opts) if(fwc.chain[i].target[0] == '\0') continue; - got_err += mk_chain(opts, i); + got_err += mk_chain(opts, i, ipv6); } #if USE_LIBNETFILTER_QUEUE @@ -839,7 +915,7 @@ create_fw_chains(const fko_srv_options_t * const opts) /* Create the NF_QUEUE chains and rules */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NEW_CHAIN_ARGS, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, opts->config[CONF_NFQ_TABLE], opts->config[CONF_NFQ_CHAIN] ); @@ -862,7 +938,7 @@ create_fw_chains(const fko_srv_options_t * const opts) /* Create the rule to direct traffic to the NFQ chain. */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_JUMP_RULE_ARGS, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, opts->config[CONF_NFQ_TABLE], "INPUT", 1, @@ -891,7 +967,7 @@ create_fw_chains(const fko_srv_options_t * const opts) if(strlen(opts->config[CONF_NFQ_INTERFACE]) > 0) { snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NFQ_ADD_ARGS_WITH_IF, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, opts->config[CONF_NFQ_TABLE], opts->config[CONF_NFQ_CHAIN], opts->config[CONF_NFQ_INTERFACE], @@ -902,7 +978,7 @@ create_fw_chains(const fko_srv_options_t * const opts) else { snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NFQ_ADD_ARGS, - fwc.fw_command, + ipv6 ? fwc.fw_command6 : fwc.fw_command, opts->config[CONF_NFQ_TABLE], opts->config[CONF_NFQ_CHAIN], opts->config[CONF_NFQ_PORT], @@ -968,7 +1044,7 @@ set_fw_chain_conf(const int type, const char * const conf_str) && *ndx != ' ' && *ndx != ',' && *ndx != '_' - && isalnum(*ndx) == 0) + && isalnum((int)(unsigned char)*ndx) == 0) { log_msg(LOG_ERR, "[*] Custom chain config parse error: " "invalid character '%c' for chain type %i, " @@ -1031,6 +1107,7 @@ fw_config_init(fko_srv_options_t * const opts) /* Set our firewall exe command path (iptables in most cases). */ strlcpy(fwc.fw_command, opts->config[CONF_FIREWALL_EXE], sizeof(fwc.fw_command)); + strlcpy(fwc.fw_command6, opts->config[CONF_FIREWALL_EXE_IPV6], sizeof(fwc.fw_command6)); #if HAVE_LIBFIU fiu_return_on("fw_config_init", 0); @@ -1101,16 +1178,22 @@ fw_initialize(const fko_srv_options_t * const opts) if(opts->ipt_disable_check_support) have_ipt_chk_support = 0; else - ipt_chk_support(opts); + { + ipt_chk_support(opts, 0); + ipt_chk_support(opts, 1); + } /* Flush the chains (just in case) so we can start fresh. */ if(strncasecmp(opts->config[CONF_FLUSH_IPT_AT_INIT], "Y", 1) == 0) - delete_all_chains(opts); + { + delete_all_chains(opts, 0); + delete_all_chains(opts, 1); + } /* Now create any configured chains. */ - if(create_fw_chains(opts) != 0) + if(create_fw_chains(opts, 0) != 0 || create_fw_chains(opts, 1) != 0) { log_msg(LOG_WARNING, "fw_initialize() Warning: Errors detected during fwknop custom chain creation"); @@ -1121,7 +1204,7 @@ fw_initialize(const fko_srv_options_t * const opts) */ if(strncasecmp(opts->config[CONF_ENABLE_IPT_COMMENT_CHECK], "Y", 1) == 0) { - if(comment_match_exists(opts) == 1) + if(comment_match_exists(opts, 0) == 1 && comment_match_exists(opts, 1) == 1) { log_msg(LOG_INFO, "iptables 'comment' match is available"); } @@ -1142,13 +1225,14 @@ fw_cleanup(const fko_srv_options_t * const opts) && opts->fw_flush == 0) return(0); - delete_all_chains(opts); + delete_all_chains(opts, 0); + delete_all_chains(opts, 1); return(0); } static int create_rule(const fko_srv_options_t * const opts, - const char * const fw_chain, const char * const fw_rule) + const char * const fw_chain, const char * const fw_rule, int ipv6) { int res = 0; @@ -1156,10 +1240,12 @@ create_rule(const fko_srv_options_t * const opts, if (strncasecmp(opts->config[CONF_ENABLE_RULE_PREPEND], "Y", 1) == 0) { snprintf(cmd_buf, CMD_BUFSIZE-1, "%s -I %s %s", - opts->fw_config->fw_command, fw_chain, fw_rule); + ipv6 ? opts->fw_config->fw_command6 : opts->fw_config->fw_command, + fw_chain, fw_rule); } else { snprintf(cmd_buf, CMD_BUFSIZE-1, "%s -A %s %s", - opts->fw_config->fw_command, fw_chain, fw_rule); + ipv6 ? opts->fw_config->fw_command6 : opts->fw_config->fw_command, + fw_chain, fw_rule); } res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, NO_TIMEOUT, &pid_status, opts); @@ -1195,7 +1281,8 @@ ipt_rule(const fko_srv_options_t * const opts, const unsigned int exp_ts, const time_t now, const char * const msg, - const char * const access_msg) + const char * const access_msg, + int ipv6) { char rule_buf[CMD_BUFSIZE] = {0}; @@ -1220,15 +1307,15 @@ ipt_rule(const fko_srv_options_t * const opts, /* Check to make sure that the chain and jump rule exist */ - mk_chain(opts, chain->type); + mk_chain(opts, chain->type, ipv6); if(rule_exists(opts, chain, rule_buf, proto, srcip, - dstip, port, nat_ip, nat_port, exp_ts) == 0) + dstip, port, nat_ip, nat_port, exp_ts, ipv6) == 0) { - if(create_rule(opts, chain->to_chain, rule_buf)) + if(create_rule(opts, chain->to_chain, rule_buf, ipv6)) { log_msg(LOG_INFO, "Added %s rule to %s for %s -> %s %s, expires at %u", - msg, chain->to_chain, srcip, (dstip == NULL) ? IPT_ANY_IP : dstip, + msg, chain->to_chain, srcip, (dstip == NULL) ? any_ip(ipv6) : dstip, access_msg, exp_ts ); @@ -1241,8 +1328,6 @@ ipt_rule(const fko_srv_options_t * const opts, chain->next_expire = exp_ts; } } - - return; } static void forward_access_rule(const fko_srv_options_t * const opts, @@ -1254,7 +1339,8 @@ static void forward_access_rule(const fko_srv_options_t * const opts, const unsigned int fst_port, spa_data_t * const spadat, const unsigned int exp_ts, - const time_t now) + const time_t now, + int ipv6) { char rule_buf[CMD_BUFSIZE] = {0}; @@ -1277,7 +1363,7 @@ static void forward_access_rule(const fko_srv_options_t * const opts, */ ipt_rule(opts, rule_buf, NULL, spadat->use_src_ip, NULL, ANY_PROTO, ANY_PORT, NULL, NAT_ANY_PORT, - fwd_chain, exp_ts, now, "FORWARD ALL", "*/*"); + fwd_chain, exp_ts, now, "FORWARD ALL", "*/*", ipv6); } else { @@ -1293,9 +1379,8 @@ static void forward_access_rule(const fko_srv_options_t * const opts, */ ipt_rule(opts, rule_buf, NULL, spadat->use_src_ip, NULL, fst_proto, nat_port, NULL, NAT_ANY_PORT, - fwd_chain, exp_ts, now, "FORWARD", spadat->spa_message_remain); + fwd_chain, exp_ts, now, "FORWARD", spadat->spa_message_remain, ipv6); } - return; } static void dnat_rule(const fko_srv_options_t * const opts, @@ -1307,7 +1392,8 @@ static void dnat_rule(const fko_srv_options_t * const opts, const unsigned int fst_port, spa_data_t * const spadat, const unsigned int exp_ts, - const time_t now) + const time_t now, + int ipv6) { char rule_buf[CMD_BUFSIZE] = {0}; @@ -1321,7 +1407,7 @@ static void dnat_rule(const fko_srv_options_t * const opts, snprintf(rule_buf, CMD_BUFSIZE-1, IPT_DNAT_ALL_RULE_ARGS, dnat_chain->table, spadat->use_src_ip, - (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), + (fwc.use_destination ? spadat->pkt_destination_ip : any_ip(ipv6)), exp_ts, dnat_chain->target, nat_ip @@ -1331,17 +1417,17 @@ static void dnat_rule(const fko_srv_options_t * const opts, */ ipt_rule(opts, rule_buf, NULL, spadat->use_src_ip, NULL, ANY_PROTO, ANY_PORT, NULL, NAT_ANY_PORT, - dnat_chain, exp_ts, now, "DNAT ALL", "*/*"); + dnat_chain, exp_ts, now, "DNAT ALL", "*/*", ipv6); } else { memset(rule_buf, 0, CMD_BUFSIZE); - snprintf(rule_buf, CMD_BUFSIZE-1, IPT_DNAT_RULE_ARGS, + snprintf(rule_buf, CMD_BUFSIZE-1, ipv6 ? IPT_DNAT_RULE_ARGS_IPV6 : IPT_DNAT_RULE_ARGS, dnat_chain->table, fst_proto, spadat->use_src_ip, - (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), + (fwc.use_destination ? spadat->pkt_destination_ip : any_ip(ipv6)), fst_port, exp_ts, dnat_chain->target, @@ -1350,11 +1436,10 @@ static void dnat_rule(const fko_srv_options_t * const opts, ); ipt_rule(opts, rule_buf, NULL, spadat->use_src_ip, - (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), + (fwc.use_destination ? spadat->pkt_destination_ip : any_ip(ipv6)), fst_proto, fst_port, nat_ip, nat_port, dnat_chain, exp_ts, now, - "DNAT", spadat->spa_message_remain); + "DNAT", spadat->spa_message_remain, ipv6); } - return; } static void snat_rule(const fko_srv_options_t * const opts, @@ -1365,7 +1450,8 @@ static void snat_rule(const fko_srv_options_t * const opts, const unsigned int fst_port, spa_data_t * const spadat, const unsigned int exp_ts, - const time_t now) + const time_t now, + int ipv6) { char rule_buf[CMD_BUFSIZE] = {0}; char snat_target[SNAT_TARGET_BUFSIZE] = {0}; @@ -1385,7 +1471,7 @@ static void snat_rule(const fko_srv_options_t * const opts, /* Add SNAT or MASQUERADE rules. */ - if(acc->force_snat && acc->force_snat_ip != NULL && is_valid_ipv4_addr(acc->force_snat_ip, strlen(acc->force_snat_ip))) + if(acc->force_snat && acc->force_snat_ip != NULL && is_valid_ip_addr(acc->force_snat_ip, strlen(acc->force_snat_ip), AF_INET)) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]); @@ -1393,7 +1479,7 @@ static void snat_rule(const fko_srv_options_t * const opts, "--to-source %s", acc->force_snat_ip); } else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL) - && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP]))) + && is_valid_ip_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP]), AF_INET)) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]); @@ -1413,13 +1499,13 @@ static void snat_rule(const fko_srv_options_t * const opts, ipt_rule(opts, rule_buf, NULL, spadat->use_src_ip, NULL, ANY_PROTO, ANY_PORT, NULL, NAT_ANY_PORT, - snat_chain, exp_ts, now, "SNAT ALL", "*/*"); + snat_chain, exp_ts, now, "SNAT ALL", "*/*", ipv6); } else { /* Add SNAT or MASQUERADE rules. */ - if(acc->force_snat && acc->force_snat_ip != NULL && is_valid_ipv4_addr(acc->force_snat_ip, strlen(acc->force_snat_ip))) + if(acc->force_snat && acc->force_snat_ip != NULL && is_valid_ip_addr(acc->force_snat_ip, strlen(acc->force_snat_ip), AF_INET)) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]); @@ -1434,7 +1520,7 @@ static void snat_rule(const fko_srv_options_t * const opts, "--to-ports %i", fst_port); } else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL) - && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP]))) + && is_valid_ip_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP]), AF_INET)) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]); @@ -1464,9 +1550,8 @@ static void snat_rule(const fko_srv_options_t * const opts, ipt_rule(opts, rule_buf, NULL, spadat->use_src_ip, NULL, fst_proto, nat_port, nat_ip, nat_port, snat_chain, exp_ts, now, "SNAT", - spadat->spa_message_remain); + spadat->spa_message_remain, ipv6); } - return; } /****************************************************************************/ @@ -1477,7 +1562,7 @@ int process_spa_request(const fko_srv_options_t * const opts, const acc_stanza_t * const acc, spa_data_t * const spadat) { - char nat_ip[MAX_IPV4_STR_LEN] = {0}; + char nat_ip[MAX_IPV46_STR_LEN] = {0}; char nat_dst[MAX_HOSTNAME_LEN] = {0}; unsigned int nat_port = 0; @@ -1498,6 +1583,9 @@ process_spa_request(const fko_srv_options_t * const opts, time_t now; unsigned int exp_ts; + /* XXX set adequately per SPA message */ + int ipv6 = (opts->family == AF_INET6) ? 1 : 0; + /* Parse and expand our access message. */ if(expand_acc_port_list(&port_list, spadat->spa_message_remain) != 1) @@ -1545,7 +1633,7 @@ process_spa_request(const fko_srv_options_t * const opts, if((ndx != NULL) && (str_len <= MAX_HOSTNAME_LEN)) { strlcpy(nat_dst, spadat->nat_access, str_len+1); - if(! is_valid_ipv4_addr(nat_dst, str_len)) + if(! is_valid_ip_addr(nat_dst, str_len, ipv6 ? AF_INET6 : AF_INET)) { if(strncasecmp(opts->config[CONF_ENABLE_NAT_DNS], "Y", 1) == 0) { @@ -1555,7 +1643,7 @@ process_spa_request(const fko_srv_options_t * const opts, free_acc_port_list(port_list); return res; } - if (ipv4_resolve(nat_dst, nat_ip) == 0) + if (ip_resolve(nat_dst, nat_ip, ipv6 ? AF_INET6 : AF_INET) == 0) { log_msg(LOG_INFO, "Resolved NAT IP in SPA message"); } @@ -1576,7 +1664,7 @@ process_spa_request(const fko_srv_options_t * const opts, } else { - strlcpy(nat_ip, nat_dst, MAX_IPV4_STR_LEN); + strlcpy(nat_ip, nat_dst, MAX_IPV46_STR_LEN); } nat_port = strtol_wrapper(ndx+1, 0, MAX_PORT, @@ -1601,29 +1689,29 @@ process_spa_request(const fko_srv_options_t * const opts, || spadat->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG) { ipt_rule(opts, NULL, IPT_RULE_ARGS, spadat->use_src_ip, - (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), + (fwc.use_destination ? spadat->pkt_destination_ip : any_ip(ipv6)), fst_proto, nat_port, nat_ip, nat_port, in_chain, exp_ts, - now, "local NAT", spadat->spa_message_remain); + now, "local NAT", spadat->spa_message_remain, ipv6); } else if(strlen(fwd_chain->to_chain)) { /* FORWARD access rule */ forward_access_rule(opts, acc, fwd_chain, nat_ip, - nat_port, fst_proto, fst_port, spadat, exp_ts, now); + nat_port, fst_proto, fst_port, spadat, exp_ts, now, ipv6); } /* DNAT rule */ if(strlen(dnat_chain->to_chain) && !acc->disable_dnat) dnat_rule(opts, acc, dnat_chain, nat_ip, - nat_port, fst_proto, fst_port, spadat, exp_ts, now); + nat_port, fst_proto, fst_port, spadat, exp_ts, now, ipv6); /* SNAT rule */ if(acc->force_snat || strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1) == 0) snat_rule(opts, acc, nat_ip, nat_port, - fst_proto, fst_port, spadat, exp_ts, now); + fst_proto, fst_port, spadat, exp_ts, now, ipv6); } else /* Non-NAT request - this is the typical case. */ { @@ -1632,9 +1720,9 @@ process_spa_request(const fko_srv_options_t * const opts, while(ple != NULL) { ipt_rule(opts, NULL, IPT_RULE_ARGS, spadat->use_src_ip, - (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), + (fwc.use_destination ? spadat->pkt_destination_ip : any_ip(ipv6)), ple->proto, ple->port, NULL, NAT_ANY_PORT, - in_chain, exp_ts, now, "access", spadat->spa_message_remain); + in_chain, exp_ts, now, "access", spadat->spa_message_remain, ipv6); /* We need to make a corresponding OUTPUT rule if out_chain target * is not NULL. @@ -1642,9 +1730,9 @@ process_spa_request(const fko_srv_options_t * const opts, if(strlen(out_chain->to_chain)) { ipt_rule(opts, NULL, IPT_OUT_RULE_ARGS, spadat->use_src_ip, - (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), + (fwc.use_destination ? spadat->pkt_destination_ip : any_ip(ipv6)), ple->proto, ple->port, NULL, NAT_ANY_PORT, - out_chain, exp_ts, now, "OUTPUT", spadat->spa_message_remain); + out_chain, exp_ts, now, "OUTPUT", spadat->spa_message_remain, ipv6); } ple = ple->next; } @@ -1660,7 +1748,7 @@ process_spa_request(const fko_srv_options_t * const opts, static void rm_expired_rules(const fko_srv_options_t * const opts, const char * const ipt_output_buf, - char *ndx, struct fw_chain *ch, int cpos, time_t now) + char *ndx, struct fw_chain *ch, int cpos, time_t now, int ipv6) { char exp_str[12] = {0}; char rule_num_str[6] = {0}; @@ -1758,7 +1846,7 @@ rm_expired_rules(const fko_srv_options_t * const opts, zero_cmd_buffers(); snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS, - opts->fw_config->fw_command, + ipv6 ? opts->fw_config->fw_command6 : opts->fw_config->fw_command, ch[cpos].table, ch[cpos].to_chain, rule_num - rn_offset /* account for position of previously @@ -1808,16 +1896,14 @@ rm_expired_rules(const fko_srv_options_t * const opts, ch[cpos].next_expire = 0; else if(min_exp) ch[cpos].next_expire = min_exp; - - return; } /* Iterate over the configure firewall access chains and purge expired * firewall rules. */ -void -check_firewall_rules(const fko_srv_options_t * const opts, - const int chk_rm_all) +static void +check_firewall_rules_do(const fko_srv_options_t * const opts, + const int chk_rm_all, int ipv6) { char *ndx; char ipt_output_buf[STANDARD_CMD_OUT_BUFSIZE] = {0}; @@ -1853,7 +1939,7 @@ check_firewall_rules(const fko_srv_options_t * const opts, * mechanism. */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS, - opts->fw_config->fw_command, + ipv6 ? opts->fw_config->fw_command6 : opts->fw_config->fw_command, ch[i].table, ch[i].to_chain ); @@ -1891,10 +1977,16 @@ check_firewall_rules(const fko_srv_options_t * const opts, continue; } - rm_expired_rules(opts, ipt_output_buf, ndx, ch, i, now); + rm_expired_rules(opts, ipt_output_buf, ndx, ch, i, now, ipv6); } +} - return; +void +check_firewall_rules(const fko_srv_options_t * const opts, + const int chk_rm_all) +{ + check_firewall_rules_do(opts, chk_rm_all, 0); + check_firewall_rules_do(opts, chk_rm_all, 1); } int @@ -1913,7 +2005,7 @@ validate_ipt_chain_conf(const char * const chain_str) && *ndx != ' ' && *ndx != ',' && *ndx != '_' - && isalnum(*ndx) == 0) + && isalnum((int)(unsigned char)*ndx) == 0) { rv = 0; break; diff --git a/server/fw_util_iptables.h b/server/fw_util_iptables.h index 9869f6e7..7903b73e 100644 --- a/server/fw_util_iptables.h +++ b/server/fw_util_iptables.h @@ -46,12 +46,13 @@ #define IPT_FWD_RULE_ARGS "-t %s -p %i -s %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" SH_REDIR #define IPT_FWD_ALL_RULE_ARGS "-t %s -s %s -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" SH_REDIR #define IPT_DNAT_RULE_ARGS "-t %s -p %i -s %s -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s --to-destination %s:%i" SH_REDIR +#define IPT_DNAT_RULE_ARGS_IPV6 "-t %s -p %i -s %s -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s --to-destination [%s]:%i" SH_REDIR #define IPT_DNAT_ALL_RULE_ARGS "-t %s -s %s -d %s -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s --to-destination %s" SH_REDIR #define IPT_SNAT_RULE_ARGS "-t %s -p %i -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s %s" SH_REDIR #define IPT_SNAT_ALL_RULE_ARGS "-t %s -s %s -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s %s" SH_REDIR -#define IPT_TMP_COMMENT_ARGS "-t %s -I %s %i -s " DUMMY_IP " -m comment --comment " TMP_COMMENT " -j %s" SH_REDIR -#define IPT_TMP_CHK_RULE_ARGS "-t %s -I %s %i -s " DUMMY_IP " -p udp -j %s" SH_REDIR -#define IPT_TMP_VERIFY_CHK_ARGS "-t %s -C %s -s " DUMMY_IP " -p udp -j %s" SH_REDIR +#define IPT_TMP_COMMENT_ARGS "-t %s -I %s %i -s %s -m comment --comment " TMP_COMMENT " -j %s" SH_REDIR +#define IPT_TMP_CHK_RULE_ARGS "-t %s -I %s %i -s %s -p udp -j %s" SH_REDIR +#define IPT_TMP_VERIFY_CHK_ARGS "-t %s -C %s -s %s -p udp -j %s" SH_REDIR #define IPT_DEL_RULE_ARGS "-t %s -D %s %i" SH_REDIR #define IPT_NEW_CHAIN_ARGS "-t %s -N %s" SH_REDIR #define IPT_FLUSH_CHAIN_ARGS "-t %s -F %s" SH_REDIR @@ -63,6 +64,7 @@ #define IPT_LIST_RULES_ARGS "-t %s -L %s --line-numbers -n" SH_REDIR #define IPT_LIST_ALL_RULES_ARGS "-t %s -v -n -L --line-numbers" SH_REDIR #define IPT_ANY_IP "0.0.0.0/0" +#define IPT_ANY_IPV6 "::/0" #if USE_LIBNETFILTER_QUEUE #define IPT_NFQ_ADD_ARGS "-t %s -A %s -p udp -m udp --dport %s -j NFQUEUE --queue-num %s" diff --git a/server/fwknopd.c b/server/fwknopd.c index d7b1abff..e475a5e8 100644 --- a/server/fwknopd.c +++ b/server/fwknopd.c @@ -259,7 +259,7 @@ main(int argc, char **argv) if(opts.enable_udp_server || strncasecmp(opts.config[CONF_ENABLE_UDP_SERVER], "Y", 1) == 0) { - if(run_udp_server(&opts) < 0) + if(run_udp_server(&opts, (opts.family != AF_UNSPEC) ? opts.family : AF_INET) < 0) { log_msg(LOG_ERR, "Fatal run_udp_server() error"); clean_exit(&opts, FW_CLEANUP, EXIT_FAILURE); @@ -280,7 +280,7 @@ main(int argc, char **argv) */ if(strncasecmp(opts.config[CONF_ENABLE_TCP_SERVER], "Y", 1) == 0) { - if(run_tcp_server(&opts) < 0) + if(run_tcp_server(&opts, (opts.family != AF_UNSPEC) ? opts.family : AF_INET) < 0) { log_msg(LOG_ERR, "Fatal run_tcp_server() error"); clean_exit(&opts, FW_CLEANUP, EXIT_FAILURE); diff --git a/server/fwknopd.conf b/server/fwknopd.conf index 647ecf05..6fd328bc 100644 --- a/server/fwknopd.conf +++ b/server/fwknopd.conf @@ -559,5 +559,6 @@ # #FIREWALL_EXE /bin/firewall-cmd; #FIREWALL_EXE /sbin/iptables; +#FIREWALL_EXE_IPV6 /sbin/ip6tables; ###EOF### diff --git a/server/fwknopd_common.h b/server/fwknopd_common.h index 29959952..4342c70b 100644 --- a/server/fwknopd_common.h +++ b/server/fwknopd_common.h @@ -32,9 +32,12 @@ #include "common.h" -#if PLATFORM_OPENBSD +#if HAVE_NETINET_IN_H #include #endif +#if HAVE_NETINET_IP6_H + #include +#endif #if HAVE_SYS_STAT_H #include @@ -344,6 +347,7 @@ enum { CONF_GPG_EXE, CONF_SUDO_EXE, CONF_FIREWALL_EXE, + CONF_FIREWALL_EXE_IPV6, CONF_VERBOSE, #if AFL_FUZZING CONF_AFL_PKT_FILE, @@ -358,8 +362,20 @@ enum { */ typedef struct acc_int_list { - unsigned int maddr; - unsigned int mask; + int family; + union + { + struct + { + unsigned int maddr; + unsigned int mask; + } inet; + struct + { + struct in6_addr maddr; + unsigned int prefix; + } inet6; + } acc_int; struct acc_int_list *next; } acc_int_list_t; @@ -553,6 +569,7 @@ typedef struct cmd_cycle_list struct fw_config { struct fw_chain chain[NUM_FWKNOP_ACCESS_TYPES]; char fw_command[MAX_PATH_LEN]; + char fw_command6[MAX_PATH_LEN]; /* Flag for setting destination field in rule */ @@ -600,12 +617,28 @@ typedef struct spa_pkt_info { unsigned int packet_data_len; unsigned int packet_proto; - unsigned int packet_src_ip; - unsigned int packet_dst_ip; + unsigned int packet_family; + union + { + struct + { + unsigned int src_ip; + unsigned int dst_ip; + } inet; +#if HAVE_NETINET_IP6_H + struct + { + struct in6_addr src_ip; + struct in6_addr dst_ip; + } inet6; +#endif + } packet_addr; unsigned short packet_src_port; unsigned short packet_dst_port; unsigned char packet_data[MAX_SPA_PACKET_LEN+1]; } spa_pkt_info_t; +#define packet_src_ip packet_addr.inet.src_ip +#define packet_dst_ip packet_addr.inet.dst_ip /* Struct for (processed and verified) SPA data used by the server. */ @@ -616,10 +649,10 @@ typedef struct spa_data char *version; short message_type; char *spa_message; - char spa_message_src_ip[MAX_IPV4_STR_LEN]; - char pkt_source_ip[MAX_IPV4_STR_LEN]; - char pkt_source_xff_ip[MAX_IPV4_STR_LEN]; - char pkt_destination_ip[MAX_IPV4_STR_LEN]; + char spa_message_src_ip[MAX_IPV46_STR_LEN]; + char pkt_source_ip[MAX_IPV46_STR_LEN]; + char pkt_source_xff_ip[MAX_IPV46_STR_LEN]; + char pkt_destination_ip[MAX_IPV46_STR_LEN]; char spa_message_remain[1024]; /* --DSS FIXME: arbitrary bounds */ char *nat_access; char *server_auth; @@ -657,6 +690,7 @@ typedef struct fko_srv_options unsigned char enable_nfq_capture; /* Enable Netfilter Queue capture mode */ unsigned char enable_fw; /* Command modes by themselves don't need firewall support. */ + int family; /* Family restriction (TCP/UDP) */ unsigned char firewd_disable_check_support; /* Don't use firewall-cmd ... -C */ unsigned char ipt_disable_check_support; /* Don't use iptables -C */ diff --git a/server/incoming_spa.c b/server/incoming_spa.c index 968162c9..fedfb097 100644 --- a/server/incoming_spa.c +++ b/server/incoming_spa.c @@ -119,7 +119,7 @@ preprocess_spa_data(const fko_srv_options_t *opts, spa_pkt_info_t *spa_pkt, spa_ xff -= i - 1; - if (!is_valid_ipv4_addr(xff, strlen(xff))) + if (!is_valid_ip_addr(xff, strlen(xff), AF_UNSPEC)) log_msg(LOG_WARNING, "Error parsing X-Forwarded-For header: value '%s' is not an IP address", xff); @@ -254,7 +254,7 @@ get_raw_digest(char **digest, char *pkt_data) return res; } -/* Popluate a spa_data struct from an initialized (and populated) FKO context. +/* Populate a spa_data struct from an initialized (and populated) FKO context. */ static int get_spa_data_fields(fko_ctx_t ctx, spa_data_t *spdat) @@ -347,14 +347,28 @@ check_stanza_expiration(acc_stanza_t *acc, spa_data_t *spadat, * source IP */ static int -is_src_match(acc_stanza_t *acc, const uint32_t ip) +is_src_match(acc_stanza_t *acc, const spa_pkt_info_t *spa_pkt) { - while (acc) + switch (spa_pkt->packet_family) { - if(compare_addr_list(acc->source_list, ip)) - return 1; + case AF_INET: + while (acc) + { + if(compare_addr_list(acc->source_list, AF_INET, ntohl(spa_pkt->packet_addr.inet.src_ip))) + return 1; + + acc = acc->next; + } + break; + case AF_INET6: + while (acc) + { + if(compare_addr_list(acc->source_list, AF_INET6, &spa_pkt->packet_addr.inet6.src_ip)) + return 1; - acc = acc->next; + acc = acc->next; + } + break; } return 0; } @@ -363,7 +377,7 @@ static int src_check(fko_srv_options_t *opts, spa_pkt_info_t *spa_pkt, spa_data_t *spadat, char **raw_digest) { - if (is_src_match(opts->acc_stanzas, ntohl(spa_pkt->packet_src_ip))) + if (is_src_match(opts->acc_stanzas, spa_pkt)) { if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0) { @@ -427,16 +441,32 @@ static int src_dst_check(acc_stanza_t *acc, spa_pkt_info_t *spa_pkt, spa_data_t *spadat, const int stanza_num) { - if(! compare_addr_list(acc->source_list, ntohl(spa_pkt->packet_src_ip)) || - (acc->destination_list != NULL - && ! compare_addr_list(acc->destination_list, ntohl(spa_pkt->packet_dst_ip)))) + switch (spa_pkt->packet_family) { - log_msg(LOG_DEBUG, - "(stanza #%d) SPA packet (%s -> %s) filtered by SOURCE and/or DESTINATION criteria", - stanza_num, spadat->pkt_source_ip, spadat->pkt_destination_ip); - return 0; + case AF_INET: + if(! compare_addr_list(acc->source_list, AF_INET, ntohl(spa_pkt->packet_src_ip)) || + (acc->destination_list != NULL + && ! compare_addr_list(acc->destination_list, AF_INET, ntohl(spa_pkt->packet_dst_ip)))) + { + log_msg(LOG_DEBUG, + "(stanza #%d) SPA packet (%s -> %s) filtered by SOURCE and/or DESTINATION criteria", + stanza_num, spadat->pkt_source_ip, spadat->pkt_destination_ip); + return 0; + } + return 1; + case AF_INET6: + if(! compare_addr_list(acc->source_list, AF_INET6, &spa_pkt->packet_addr.inet6.src_ip) || + (acc->destination_list != NULL + && ! compare_addr_list(acc->destination_list, AF_INET6, &spa_pkt->packet_addr.inet6.dst_ip))) + { + log_msg(LOG_DEBUG, + "(stanza #%d) SPA packet (%s -> %s) filtered by SOURCE and/or DESTINATION criteria", + stanza_num, spadat->pkt_source_ip, spadat->pkt_destination_ip); + return 0; + } + return 1; } - return 1; + return 0; } /* Process command messages @@ -930,11 +960,29 @@ incoming_spa(fko_srv_options_t *opts) */ acc_stanza_t *acc = opts->acc_stanzas; - inet_ntop(AF_INET, &(spa_pkt->packet_src_ip), - spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip)); + /* Verify the network family if relevant */ + if(opts->family != AF_UNSPEC && opts->family != spa_pkt->packet_family) + return; - inet_ntop(AF_INET, &(spa_pkt->packet_dst_ip), - spadat.pkt_destination_ip, sizeof(spadat.pkt_destination_ip)); + switch(spa_pkt->packet_family) + { + case AF_INET: + inet_ntop(AF_INET, &(spa_pkt->packet_addr.inet.src_ip), + spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip)); + + inet_ntop(AF_INET, &(spa_pkt->packet_addr.inet.dst_ip), + spadat.pkt_destination_ip, sizeof(spadat.pkt_destination_ip)); + break; + case AF_INET6: + inet_ntop(AF_INET6, &(spa_pkt->packet_addr.inet6.src_ip), + spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip)); + + inet_ntop(AF_INET6, &(spa_pkt->packet_addr.inet6.dst_ip), + spadat.pkt_destination_ip, sizeof(spadat.pkt_destination_ip)); + break; + default: + return; + } /* At this point, we want to validate and (if needed) preprocess the * SPA data and/or to be reasonably sure we have a SPA packet (i.e @@ -1088,8 +1136,8 @@ incoming_spa(fko_srv_options_t *opts) continue; } - if((spa_ip_demark-spadat.spa_message) < MIN_IPV4_STR_LEN-1 - || (spa_ip_demark-spadat.spa_message) > MAX_IPV4_STR_LEN) + if((spa_ip_demark-spadat.spa_message) < MIN_IPV46_STR_LEN-1 + || (spa_ip_demark-spadat.spa_message) > MAX_IPV46_STR_LEN) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet", @@ -1100,7 +1148,7 @@ incoming_spa(fko_srv_options_t *opts) strlcpy(spadat.spa_message_src_ip, spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1); - if(! is_valid_ipv4_addr(spadat.spa_message_src_ip, strlen(spadat.spa_message_src_ip))) + if(! is_valid_ip_addr(spadat.spa_message_src_ip, strlen(spadat.spa_message_src_ip), AF_UNSPEC)) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet", diff --git a/server/nfq_capture.c b/server/nfq_capture.c index 3b72e89a..e7c773f0 100644 --- a/server/nfq_capture.c +++ b/server/nfq_capture.c @@ -195,7 +195,7 @@ nfq_capture(fko_srv_options_t *opts) /* Attempt to restart tcp server ? */ usleep(1000000); - run_tcp_server(opts); + run_tcp_server(opts, (opts->family != AF_UNSPEC) ? opts->family : AF_INET); } } diff --git a/server/pcap_capture.c b/server/pcap_capture.c index e25904b5..6d616956 100644 --- a/server/pcap_capture.c +++ b/server/pcap_capture.c @@ -211,7 +211,7 @@ pcap_capture(fko_srv_options_t *opts) /* Attempt to restart tcp server ? */ usleep(1000000); - run_tcp_server(opts); + run_tcp_server(opts, (opts->family != AF_UNSPEC) ? opts->family : AF_INET); } } diff --git a/server/process_packet.c b/server/process_packet.c index 32e99cb2..962766d3 100644 --- a/server/process_packet.c +++ b/server/process_packet.c @@ -41,54 +41,65 @@ #include "log_msg.h" +static void +process_packet_ethernet(fko_srv_options_t * opts, const unsigned char * packet, + unsigned short pkt_len, int caplen, int offset); +static void +process_packet_ipv4(fko_srv_options_t * opts, const unsigned char * packet, + int offset, unsigned char const * fr_end); +static void +process_packet_ipv6(fko_srv_options_t * opts, const unsigned char * packet, + int offset, unsigned char const * fr_end); +static void +process_packet_loop(fko_srv_options_t * opts, const unsigned char * packet, + unsigned short pkt_len, int caplen); +static void +process_packet_raw(fko_srv_options_t * opts, const unsigned char * packet, + unsigned short pkt_len, int caplen); + + void process_packet(PROCESS_PKT_ARGS_TYPE *args, PACKET_HEADER_META, const unsigned char *packet) { - struct ether_header *eth_p; - struct iphdr *iph_p; - struct tcphdr *tcph_p; - struct udphdr *udph_p; - struct icmphdr *icmph_p; - - unsigned char *pkt_data; - unsigned short pkt_data_len; - unsigned char *pkt_end; - unsigned char *fr_end; - - unsigned int ip_hdr_words; + fko_srv_options_t *opts = (fko_srv_options_t *)args; - unsigned char proto; - unsigned int src_ip; - unsigned int dst_ip; + int offset = opts->data_link_offset; - unsigned short src_port = 0; - unsigned short dst_port = 0; +#if USE_LIBPCAP + if (offset == sizeof(struct ether_header)) + process_packet_ethernet(opts, packet, packet_header->len, + packet_header->caplen, offset); + else if (offset == 4) + process_packet_loop(opts, packet, packet_header->len, + packet_header->caplen); + else if (offset == 0) + process_packet_raw(opts, packet, packet_header->len, + packet_header->caplen); +#else + process_packet_raw(opts, packet, pkt_len, pkt_len); +#endif +} - unsigned short eth_type; - fko_srv_options_t *opts = (fko_srv_options_t *)args; +static void +process_packet_ethernet(fko_srv_options_t * opts, const unsigned char * packet, + unsigned short pkt_len, int caplen, int offset) +{ + struct ether_header *eth_p; - int offset = opts->data_link_offset; + unsigned char *fr_end; -#if USE_LIBPCAP - unsigned short pkt_len = packet_header->len; + unsigned short eth_type; /* Gotta have a complete ethernet header. */ - if (packet_header->caplen < ETHER_HDR_LEN) + if (caplen < ETHER_HDR_LEN) return; /* Determine packet end. */ - fr_end = (unsigned char *) packet + packet_header->caplen; -#else - /* This is coming from NFQ and we get the packet lentgh as an arg. - */ - if (pkt_len < ETHER_HDR_LEN) - return; - fr_end = (unsigned char *) packet + pkt_len; -#endif + fr_end = (unsigned char *) packet + caplen; /* This is a hack to determine if we are using the linux cooked * interface. We base it on the offset being 16 which is the @@ -127,6 +138,35 @@ process_packet(PROCESS_PKT_ARGS_TYPE *args, PACKET_HEADER_META, if (! ETHER_IS_VALID_LEN(pkt_len) ) return; + if (eth_type == ETHERTYPE_IP) + process_packet_ipv4(opts, packet, offset, fr_end); + else if (eth_type == ETHERTYPE_IPV6) + process_packet_ipv6(opts, packet, offset, fr_end); +} + + +static void +process_packet_ipv4(fko_srv_options_t * opts, const unsigned char * packet, + int offset, unsigned char const * fr_end) +{ + struct iphdr *iph_p; + struct tcphdr *tcph_p; + struct udphdr *udph_p; + struct icmphdr *icmph_p; + + unsigned char *pkt_data; + unsigned short pkt_data_len; + unsigned char *pkt_end; + + unsigned int ip_hdr_words; + + unsigned char proto; + unsigned int src_ip; + unsigned int dst_ip; + + unsigned short src_port = 0; + unsigned short dst_port = 0; + /* Pull the IP header. */ iph_p = (struct iphdr*)(packet + offset); @@ -172,7 +212,7 @@ process_packet(PROCESS_PKT_ARGS_TYPE *args, PACKET_HEADER_META, pkt_data = ((unsigned char*)(tcph_p+1))+((tcph_p->doff)<<2)-sizeof(struct tcphdr); - pkt_data_len = (pkt_end-(unsigned char*)iph_p)-(pkt_data-(unsigned char*)iph_p); + pkt_data_len = pkt_end - pkt_data; } else if (proto == IPPROTO_UDP) { @@ -184,7 +224,7 @@ process_packet(PROCESS_PKT_ARGS_TYPE *args, PACKET_HEADER_META, dst_port = ntohs(udph_p->dest); pkt_data = ((unsigned char*)(udph_p + 1)); - pkt_data_len = (pkt_end-(unsigned char*)iph_p)-(pkt_data-(unsigned char*)iph_p); + pkt_data_len = pkt_end - pkt_data; } else if (proto == IPPROTO_ICMP) { @@ -193,7 +233,7 @@ process_packet(PROCESS_PKT_ARGS_TYPE *args, PACKET_HEADER_META, icmph_p = (struct icmphdr*)((unsigned char*)iph_p + (ip_hdr_words << 2)); pkt_data = ((unsigned char*)(icmph_p + 1)); - pkt_data_len = (pkt_end-(unsigned char*)iph_p)-(pkt_data-(unsigned char*)iph_p); + pkt_data_len = pkt_end - pkt_data; } else @@ -225,14 +265,174 @@ process_packet(PROCESS_PKT_ARGS_TYPE *args, PACKET_HEADER_META, opts->spa_pkt.packet_data_len = pkt_data_len; opts->spa_pkt.packet_proto = proto; + opts->spa_pkt.packet_family = AF_INET; opts->spa_pkt.packet_src_ip = src_ip; opts->spa_pkt.packet_dst_ip = dst_ip; opts->spa_pkt.packet_src_port = src_port; opts->spa_pkt.packet_dst_port = dst_port; incoming_spa(opts); +} + + +static void +process_packet_ipv6(fko_srv_options_t * opts, const unsigned char * packet, + int offset, unsigned char const * fr_end) +{ + struct ip6_hdr *iph_p; + struct tcphdr *tcph_p; + struct udphdr *udph_p; + + unsigned char *pkt_data; + unsigned short pkt_data_len; + unsigned char *pkt_end; + + unsigned char proto; + struct in6_addr src_ip; + struct in6_addr dst_ip; + + unsigned short src_port = 0; + unsigned short dst_port = 0; + + /* Pull the IPv6 header. + */ + iph_p = (struct ip6_hdr*)(packet + offset); + + /* If IPv6 header is past calculated packet end, bail. + */ + if ((unsigned char*)(iph_p + 1) > fr_end) + return; + + /* Make sure to calculate the packet end based on the length in the + * IP header. This allows additional bytes that may be added to the + * frame (such as a 4-byte Ethernet Frame Check Sequence) to not + * interfere with SPA operations. + */ + pkt_end = ((unsigned char*)(iph_p + 1))+ntohs(iph_p->ip6_plen); + if(pkt_end > fr_end) + return; + + /* Now, find the packet data payload (depending on IPPROTO). + */ + src_ip = iph_p->ip6_src; + dst_ip = iph_p->ip6_dst; + + /* FIXME look for the last header */ + proto = iph_p->ip6_nxt; + + if (proto == IPPROTO_TCP) + { + /* Process TCP packet + */ + tcph_p = (struct tcphdr*)(iph_p + 1); + + src_port = ntohs(tcph_p->source); + dst_port = ntohs(tcph_p->dest); + + pkt_data = ((unsigned char*)(tcph_p+1))+((tcph_p->doff)<<2)-sizeof(struct tcphdr); + + pkt_data_len = pkt_end - pkt_data; + } + else if (proto == IPPROTO_UDP) + { + /* Process UDP packet + */ + udph_p = (struct udphdr*)(iph_p + 1); + + src_port = ntohs(udph_p->source); + dst_port = ntohs(udph_p->dest); + + pkt_data = ((unsigned char*)(udph_p + 1)); + pkt_data_len = pkt_end - pkt_data; + } + + else + return; + + /* + * Now we have data. For now, we are not checking IP or port values. We + * are relying on the pcap filter. This may change so we do retain the IP + * addresses and ports just in case. We just go ahead and queue the + * data. + */ + + /* Expect the data to be at least the minimum required size. This check + * will weed out a lot of things like small TCP ACK's if the user has a + * permissive pcap filter + */ + if(pkt_data_len < MIN_SPA_DATA_SIZE) + return; + + /* Expect the data to not be too large + */ + if(pkt_data_len > MAX_SPA_PACKET_LEN) + return; + + /* Copy the packet for SPA processing + */ + strlcpy((char *)opts->spa_pkt.packet_data, (char *)pkt_data, pkt_data_len+1); + opts->spa_pkt.packet_data_len = pkt_data_len; + opts->spa_pkt.packet_proto = proto; + opts->spa_pkt.packet_family = AF_INET6; + opts->spa_pkt.packet_addr.inet6.src_ip = src_ip; + opts->spa_pkt.packet_addr.inet6.dst_ip = dst_ip; + opts->spa_pkt.packet_src_port = src_port; + opts->spa_pkt.packet_dst_port = dst_port; + + incoming_spa(opts); +} + + +static void +process_packet_loop(fko_srv_options_t * opts, const unsigned char * packet, + unsigned short pkt_len, int caplen) +{ + uint32_t family; + + if (caplen < sizeof(family)) + return; + + memcpy(&family, packet, sizeof(family)); + + if (family == AF_INET) + process_packet_ipv4(opts, packet, 4, packet + caplen); + else if (family == AF_INET6) + process_packet_ipv6(opts, packet, 4, packet + caplen); +} + - return; +static void +process_packet_raw(fko_srv_options_t * opts, const unsigned char * packet, + unsigned short pkt_len, int caplen) +{ + struct iphdr *iph_p; + + unsigned char const *fr_end; + + /* Gotta have a complete ethernet header. + */ + if (caplen < ETHER_HDR_LEN) + return; + + /* Determine packet end. + */ + fr_end = (unsigned char *) packet + caplen; + + /* Tentatively pull an IP header. + */ + iph_p = (struct iphdr*)packet; + + /* If IP header is past calculated packet end, bail. + */ + if ((unsigned char*)(iph_p + 1) > fr_end) + return; + + /* Obtain the IP version. + */ + if (iph_p->version == 6) + process_packet_ipv6(opts, packet, 0, fr_end); + else + process_packet_ipv4(opts, packet, 0, fr_end); } diff --git a/server/tcp_server.c b/server/tcp_server.c index 7ba55bf1..6eaff064 100644 --- a/server/tcp_server.c +++ b/server/tcp_server.c @@ -51,17 +51,20 @@ * the child process or -1 if there is a fork error. */ int -run_tcp_server(fko_srv_options_t *opts) +run_tcp_server(fko_srv_options_t *opts, const int family) { #if !CODE_COVERAGE pid_t pid, ppid; #endif - int s_sock, c_sock, sfd_flags, clen, selval; + int s_sock, c_sock, sfd_flags, selval; + socklen_t slen, clen; int reuse_addr = 1, rv=1; fd_set sfd_set; - struct sockaddr_in saddr, caddr; + struct sockaddr_in saddr4, caddr4; + struct sockaddr_in6 saddr6, caddr6; + struct sockaddr *saddr, *caddr; struct timeval tv; - char sipbuf[MAX_IPV4_STR_LEN] = {0}; + char sipbuf[MAX_IPV46_STR_LEN] = {0}; log_msg(LOG_INFO, "Kicking off TCP server to listen on port %i.", opts->tcpserv_port); @@ -94,7 +97,7 @@ run_tcp_server(fko_srv_options_t *opts) /* Now, let's make a TCP server */ - if ((s_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + if ((s_sock = socket(family, SOCK_STREAM, IPPROTO_TCP)) < 0) { log_msg(LOG_ERR, "run_tcp_server: socket() failed: %s", strerror(errno)); @@ -135,13 +138,34 @@ run_tcp_server(fko_srv_options_t *opts) #endif /* Construct local address structure */ - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; /* Internet address family */ - saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ - saddr.sin_port = htons(opts->tcpserv_port); /* Local port */ + if(family == AF_INET) + { + memset(&saddr4, 0, sizeof(saddr4)); + saddr4.sin_family = family; /* Internet address family */ + saddr4.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ + saddr4.sin_port = htons(opts->tcpserv_port); /* Local port */ + saddr = (struct sockaddr *)&saddr4; + slen = sizeof(saddr4); + caddr = (struct sockaddr *)&caddr4; + } else if(family == AF_INET6) { + memset(&saddr6, 0, sizeof(saddr6)); + saddr6.sin6_family = family; /* Internet address family */ + saddr6.sin6_addr = in6addr_any; /* Any incoming interface */ + saddr6.sin6_port = htons(opts->tcpserv_port); /* Local port */ + saddr = (struct sockaddr *)&saddr6; + slen = sizeof(saddr6); + caddr = (struct sockaddr *)&caddr6; + } + else + { + log_msg(LOG_ERR, "run_tcp_server: unsupported protocol family (%d)", + family); + close(s_sock); + return -1; + } /* Bind to the local address */ - if (bind(s_sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) + if (bind(s_sock, saddr, slen) < 0) { log_msg(LOG_ERR, "run_tcp_server: bind() failed: %s", strerror(errno)); @@ -172,8 +196,6 @@ run_tcp_server(fko_srv_options_t *opts) */ while(1) { - clen = sizeof(caddr); - /* Initialize and setup the socket for select. */ FD_SET(s_sock, &sfd_set); @@ -215,7 +237,8 @@ run_tcp_server(fko_srv_options_t *opts) /* Wait for a client to connect */ - if((c_sock = accept(s_sock, (struct sockaddr *) &caddr, (socklen_t *)&clen)) < 0) + clen = (family == AF_INET) ? sizeof(caddr) : sizeof(caddr6); + if((c_sock = accept(s_sock, caddr, &clen)) < 0) { log_msg(LOG_ERR, "run_tcp_server: accept() failed: %s", strerror(errno)); @@ -225,8 +248,11 @@ run_tcp_server(fko_srv_options_t *opts) if(opts->verbose) { - memset(sipbuf, 0x0, MAX_IPV4_STR_LEN); - inet_ntop(AF_INET, &(caddr.sin_addr.s_addr), sipbuf, MAX_IPV4_STR_LEN); + memset(sipbuf, 0, sizeof(sipbuf)); + if(family == AF_INET) + inet_ntop(family, &caddr4.sin_addr.s_addr, sipbuf, sizeof(sipbuf)); + else if(family == AF_INET6) + inet_ntop(family, &caddr6.sin6_addr, sipbuf, sizeof(sipbuf)); log_msg(LOG_INFO, "tcp_server: Got TCP connection from %s.", sipbuf); } diff --git a/server/tcp_server.h b/server/tcp_server.h index 13db296e..f4264ed7 100644 --- a/server/tcp_server.h +++ b/server/tcp_server.h @@ -32,7 +32,7 @@ /* Function prototypes */ -int run_tcp_server(fko_srv_options_t *opts); +int run_tcp_server(fko_srv_options_t *opts, const int family); #endif /* TCP_SERVER_H */ diff --git a/server/udp_server.c b/server/udp_server.c index d5e837fe..fa77cbc7 100644 --- a/server/udp_server.c +++ b/server/udp_server.c @@ -50,14 +50,15 @@ #include int -run_udp_server(fko_srv_options_t *opts) +run_udp_server(fko_srv_options_t *opts, const int family) { int s_sock, sfd_flags, selval, pkt_len; int rv=1, chk_rm_all=0; fd_set sfd_set; struct sockaddr_in saddr, caddr; + struct sockaddr_in6 saddr6, caddr6; struct timeval tv; - char sipbuf[MAX_IPV4_STR_LEN] = {0}; + char sipbuf[MAX_IPV46_STR_LEN] = {0}; char dgram_msg[MAX_SPA_PACKET_LEN+1] = {0}; socklen_t clen; @@ -66,7 +67,7 @@ run_udp_server(fko_srv_options_t *opts) /* Now, let's make a UDP server */ - if ((s_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + if ((s_sock = socket(family, SOCK_DGRAM, 0)) < 0) { log_msg(LOG_ERR, "run_udp_server: socket() failed: %s", strerror(errno)); @@ -95,13 +96,29 @@ run_udp_server(fko_srv_options_t *opts) } /* Construct local address structure */ - memset(&saddr, 0x0, sizeof(saddr)); - saddr.sin_family = AF_INET; /* Internet address family */ - saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ - saddr.sin_port = htons(opts->udpserv_port); /* Local port */ + if(family == AF_INET) + { + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = family; /* Internet address family */ + saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ + saddr.sin_port = htons(opts->udpserv_port); /* Local port */ + } else if(family == AF_INET6) { + memset(&saddr6, 0, sizeof(saddr6)); + saddr6.sin6_family = family; /* Internet address family */ + saddr6.sin6_addr = in6addr_any; /* Any incoming interface */ + saddr6.sin6_port = htons(opts->udpserv_port); /* Local port */ + } + else + { + log_msg(LOG_ERR, "run_udp_server: unsupported protocol family (%d)", + family); + close(s_sock); + return -1; + } /* Bind to the local address */ - if (bind(s_sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) + if ((family == AF_INET && bind(s_sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) + || (family == AF_INET6 && bind(s_sock, (struct sockaddr *) &saddr6, sizeof(saddr6)) < 0)) { log_msg(LOG_ERR, "run_udp_server: bind() failed: %s", strerror(errno)); @@ -184,17 +201,27 @@ run_udp_server(fko_srv_options_t *opts) /* If we make it here then there is a datagram to process */ - clen = sizeof(caddr); - - pkt_len = recvfrom(s_sock, dgram_msg, MAX_SPA_PACKET_LEN, - 0, (struct sockaddr *)&caddr, &clen); + if(family == AF_INET) { + clen = sizeof(caddr); + pkt_len = recvfrom(s_sock, dgram_msg, MAX_SPA_PACKET_LEN, + 0, (struct sockaddr *)&caddr, &clen); + } + else if(family == AF_INET6) + { + clen = sizeof(caddr6); + pkt_len = recvfrom(s_sock, dgram_msg, MAX_SPA_PACKET_LEN, + 0, (struct sockaddr *)&caddr6, &clen); + } dgram_msg[pkt_len] = 0x0; if(opts->verbose) { - memset(sipbuf, 0x0, MAX_IPV4_STR_LEN); - inet_ntop(AF_INET, &(caddr.sin_addr.s_addr), sipbuf, MAX_IPV4_STR_LEN); + memset(sipbuf, 0, sizeof(sipbuf)); + if(family == AF_INET) + inet_ntop(family, &caddr.sin_addr.s_addr, sipbuf, sizeof(sipbuf)); + else if(family == AF_INET6) + inet_ntop(family, &caddr6.sin6_addr, sipbuf, sizeof(sipbuf)); log_msg(LOG_INFO, "udp_server: Got UDP datagram (%d bytes) from: %s", pkt_len, sipbuf); } @@ -208,15 +235,26 @@ run_udp_server(fko_srv_options_t *opts) strlcpy((char *)opts->spa_pkt.packet_data, dgram_msg, pkt_len+1); opts->spa_pkt.packet_data_len = pkt_len; opts->spa_pkt.packet_proto = IPPROTO_UDP; - opts->spa_pkt.packet_src_ip = caddr.sin_addr.s_addr; - opts->spa_pkt.packet_dst_ip = saddr.sin_addr.s_addr; - opts->spa_pkt.packet_src_port = ntohs(caddr.sin_port); - opts->spa_pkt.packet_dst_port = ntohs(saddr.sin_port); + opts->spa_pkt.packet_family = family; + if(family == AF_INET) + { + opts->spa_pkt.packet_src_ip = caddr.sin_addr.s_addr; + opts->spa_pkt.packet_dst_ip = saddr.sin_addr.s_addr; + opts->spa_pkt.packet_src_port = ntohs(caddr.sin_port); + opts->spa_pkt.packet_dst_port = ntohs(saddr.sin_port); + } + else if(family == AF_INET6) + { + opts->spa_pkt.packet_addr.inet6.src_ip = caddr6.sin6_addr; + opts->spa_pkt.packet_addr.inet6.dst_ip = saddr6.sin6_addr; + opts->spa_pkt.packet_src_port = ntohs(caddr6.sin6_port); + opts->spa_pkt.packet_dst_port = ntohs(saddr6.sin6_port); + } incoming_spa(opts); } - memset(dgram_msg, 0x0, sizeof(dgram_msg)); + memset(dgram_msg, 0, sizeof(dgram_msg)); opts->packet_ctr += 1; if(opts->foreground == 1 && opts->verbose > 2) diff --git a/server/udp_server.h b/server/udp_server.h index dcbdfec2..29ffb685 100644 --- a/server/udp_server.h +++ b/server/udp_server.h @@ -32,7 +32,7 @@ /* Function prototypes */ -int run_udp_server(fko_srv_options_t *opts); +int run_udp_server(fko_srv_options_t *opts, const int family); #endif /* UDP_SERVER_H */ diff --git a/test/afl/test-cases/server-conf/iptables/fwknopd.conf b/test/afl/test-cases/server-conf/iptables/fwknopd.conf index 25af65a8..6adbda6b 100644 --- a/test/afl/test-cases/server-conf/iptables/fwknopd.conf +++ b/test/afl/test-cases/server-conf/iptables/fwknopd.conf @@ -43,5 +43,6 @@ DIGEST_FILE /var/run/fwknop/digest.cache GPG_HOME_DIR /root/.gnupg GPG_EXE /usr/bin/gpg FIREWALL_EXE /sbin/iptables +FIREWALL_EXE_IPV6 /sbin/ip6tables VERBOSE Y #FAULT_INJECTION_TAG diff --git a/test/run-test-suite.sh b/test/run-test-suite.sh index 56a71abc..feb77e49 100755 --- a/test/run-test-suite.sh +++ b/test/run-test-suite.sh @@ -9,6 +9,6 @@ # to work properly. # -LD_LIBRARY_PATH=../lib/.libs DYLD_LIBRARY_PATH=../lib/.libs ./test-fwknop.pl $@ +LD_LIBRARY_PATH=../lib/.libs DYLD_LIBRARY_PATH=../lib/.libs ./test-fwknop.pl "$@" exit diff --git a/test/test-fwknop.pl b/test/test-fwknop.pl index 0f87987a..8b55c7b6 100755 --- a/test/test-fwknop.pl +++ b/test/test-fwknop.pl @@ -1,4 +1,4 @@ -#!/usr/bin/perl -w +#!/usr/bin/env perl -w # # This is the main driver program for the fwknop test suite. Test definitions # are imported from the tests/ directory.