diff options
Diffstat (limited to 'src/sh_portcheck.c')
-rw-r--r-- | src/sh_portcheck.c | 759 |
1 files changed, 638 insertions, 121 deletions
diff --git a/src/sh_portcheck.c b/src/sh_portcheck.c index 507f028..106d9cb 100644 --- a/src/sh_portcheck.c +++ b/src/sh_portcheck.c @@ -32,15 +32,20 @@ #include <stdio.h> #include <string.h> +#include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> +#ifdef HAVE_IFADDRS_H +#include <ifaddrs.h> +#include <netdb.h> +#endif #include <errno.h> #include <unistd.h> #include <fcntl.h> -#define PORTCHK_VERSION "1.0" +#define PORTCHK_VERSION "1.1" #if defined(TEST_ONLY) || (defined(SH_USE_PORTCHECK) && (defined (SH_WITH_CLIENT) || defined (SH_STANDALONE))) @@ -134,9 +139,11 @@ static struct sh_portentry * portlist_udp = NULL; #define SH_PORTCHK_INTERVAL 300 -static int sh_portchk_check_udp = 1; -static int sh_portchk_active = 1; -static int sh_portchk_interval = SH_PORTCHK_INTERVAL; +static int sh_portchk_check_udp = 1; +static int sh_portchk_active = 1; +static int sh_portchk_same_ports = 1; +static int sh_portchk_transients = 1; +static int sh_portchk_interval = SH_PORTCHK_INTERVAL; static int sh_portchk_minport = -1; static int sh_portchk_maxport = -1; @@ -150,6 +157,9 @@ struct sh_port { static struct sh_port * blacklist_tcp = NULL; static struct sh_port * blacklist_udp = NULL; +static struct sh_port * transient_tcp = NULL; +static struct sh_port * transient_udp = NULL; + SH_MUTEX_STATIC(mutex_port_check, PTHREAD_MUTEX_INITIALIZER); static int sh_portchk_severity = SH_ERR_SEVERE; @@ -181,10 +191,21 @@ static int sh_portchk_add_blacklist (const char * str); */ static int sh_portchk_add_interface (const char * str); +#if defined(HAVE_IFADDRS_H) +/* Exported interface to add an ethernet device + */ +static int sh_portchk_add_device (const char * str); +#endif + /* verify whether port/interface is blacklisted (do not check) */ static int sh_portchk_is_blacklisted(int port, struct sh_sockaddr * haddr, int proto); +/* verify whether port/interface is transient (used as source port hence no check required) + */ +static int sh_portchk_is_transient(int port, struct sh_sockaddr * haddr, int proto); +static int sh_portchk_transient(int port, struct sh_sockaddr * haddr, int proto); + #ifndef TEST_ONLY static int sh_portchk_set_interval (const char * c) @@ -232,27 +253,37 @@ static int sh_portchk_set_port_minmax (const char * c, int * setthis) } -static int sh_portchk_set_minport (const char * str) +static int sh_portchk_set_minport (const char * str) { return sh_portchk_set_port_minmax (str, &sh_portchk_minport); } -static int sh_portchk_set_maxport (const char * str) +static int sh_portchk_set_maxport (const char * str) { return sh_portchk_set_port_minmax (str, &sh_portchk_maxport); } -static int sh_portchk_set_active (const char * str) +static int sh_portchk_set_active (const char * str) { return sh_util_flagval(str, &sh_portchk_active); } -static int sh_portchk_set_udp (const char * str) +static int sh_portchk_set_udp (const char * str) { return sh_util_flagval(str, &sh_portchk_check_udp); } +#if defined(SH_ALLOW_RESTORE) +static int sh_portchk_set_transients (const char * str) +{ + return sh_util_flagval(str, &sh_portchk_transients); +} -static int sh_portchk_set_severity (const char * str) +static int sh_portchk_set_same_ports (const char * str) +{ + return sh_util_flagval(str, &sh_portchk_same_ports); +} +#endif +static int sh_portchk_set_severity (const char * str) { char tmp[32]; tmp[0] = '='; tmp[1] = '\0'; @@ -285,6 +316,12 @@ sh_rconf sh_portchk_table[] = { N_("portcheckactive"), sh_portchk_set_active, }, +#if defined(HAVE_IFADDRS_H) + { + N_("portcheckdevice"), + sh_portchk_add_device, + }, +#endif { N_("portcheckinterface"), sh_portchk_add_interface, @@ -305,6 +342,16 @@ sh_rconf sh_portchk_table[] = { N_("portcheckudp"), sh_portchk_set_udp, }, +#if defined(SH_ALLOW_RESTORE) + { + N_("portchecktransients"), + sh_portchk_set_transients, + }, + { + N_("portchecksameports"), + sh_portchk_set_same_ports, + }, +#endif { NULL, NULL @@ -382,6 +429,7 @@ static char * sh_getrpcbynumber (int number, char * buf, size_t len) sh_string_destroy(&s); sl_fclose(FIL__, __LINE__, fp); } + /* cppcheck-suppress resourceLeak */ return NULL; } #endif @@ -432,6 +480,7 @@ static char * sh_getservbyport (int port, const char * proto_in, char * buf, siz sh_string_destroy(&s); sl_fclose(FIL__, __LINE__, fp); } + /* cppcheck-suppress resourceLeak */ return NULL; } @@ -523,6 +572,19 @@ static struct sh_port * sh_portchk_kill_blacklist (struct sh_port * head) return NULL; } +static struct sh_port * sh_portchk_kill_transient (struct sh_port * head) +{ + if (head) + { + if (head->next) + sh_portchk_kill_transient (head->next); + + SH_FREE(head->paddr); + SH_FREE(head); + } + return NULL; +} + /* These variables are not used anywhere. They only exist * to assign &pre, &ptr to them, which keeps gcc from * putting it into a register, and avoids the 'clobbered @@ -538,7 +600,7 @@ static void sh_portchk_check_list (struct sh_portentry ** head, { struct sh_portentry * ptr = *head; struct sh_portentry * pre = *head; - char errbuf[256]; + char errbuf[512]; /* Take the address to keep gcc from putting them into registers. * Avoids the 'clobbered by longjmp' warning. @@ -629,6 +691,7 @@ static struct sh_portentry * sh_portchk_get_from_list (int proto, int port, struct sh_portentry * portlist; char str_addr[SH_IP_BUF]; + if (proto == IPPROTO_TCP) portlist = portlist_tcp; else @@ -667,7 +730,7 @@ static void sh_portchk_cmp_to_list (int proto, int port, struct sh_sockaddr * paddr, char * service) { struct sh_portentry * portent; - char errbuf[256]; + char errbuf[512]; portent = sh_portchk_get_from_list (proto, port, paddr, service); @@ -886,6 +949,9 @@ static char * check_rpc_list (int port, struct sockaddr_in * address, return NULL; } +void * sh_dummy_950_p = NULL; +void * sh_dummy_951_p = NULL; + static int check_port_udp_internal (int fd, int port, struct sh_sockaddr * paddr) { int retval; @@ -893,11 +959,17 @@ static int check_port_udp_internal (int fd, int port, struct sh_sockaddr * paddr char buf[8]; #ifndef TEST_ONLY char errmsg[256]; - int nerr; + volatile int nerr; #endif char errbuf[SH_ERRBUF_SIZE]; char ipbuf[SH_IP_BUF]; + struct sh_sockaddr saddr; + socklen_t slen = 0; + volatile int sport = 0; + + sh_dummy_950_p = (void*) &p; + sh_ipvx_set_port(paddr, port); do { @@ -923,85 +995,155 @@ static int check_port_udp_internal (int fd, int port, struct sh_sockaddr * paddr } else { - do { - retval = send (fd, buf, 0, 0); - } while (retval < 0 && errno == EINTR); + /* Register the used source port as transient. This will avoid + * the issue of lingering source ports being reported as a spurious + * service. The lingering source port is effectvely a race condition. + * + * Also use this code to obtain the source port. Sometimes Samhain + * reports on a port where it connects back to itself. In that case + * source and destination port are the same. + * + * AGH, 23 Apr 2017 (www.2024sight.com). + */ - if (retval == -1 && errno == ECONNREFUSED) - { - sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); - if (portchk_debug) - fprintf(stderr, _("check port_udp: %5d/udp on %15s established/time_wait\n"), - port, ipbuf); - } - else - { - /* Only the second send() may catch the error - */ - do { +#if defined(USE_IPVX) + if (paddr->ss_family == AF_INET) + { + saddr.ss_family = AF_INET; + slen = sizeof( struct sockaddr_in ); + retval = getsockname(fd, (struct sockaddr *)&(saddr.sin), &slen); + } + else + { + saddr.ss_family = AF_INET6; + slen = sizeof( struct sockaddr_in6 ); + retval = getsockname(fd, (struct sockaddr *)&(saddr.sin6), &slen); + } +#else + saddr.ss_family = AF_INET; + slen = sizeof( struct sockaddr_in ); + retval = getsockname(fd, (struct sockaddr *)&(saddr.sin), &slen); +#endif + + if ( retval == 0 ) + { + sport = sh_ipvx_get_port(&saddr); + sh_portchk_transient(sport, &saddr, IPPROTO_UDP); + } + else + { +#ifdef TEST_ONLY + if (portchk_debug) + perror(_("getsockname")); +#else + sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); + nerr = errno; + sl_snprintf(errmsg, sizeof(errmsg), _("source port transient for %15s:%d/udp: %s"), + ipbuf, port, sh_error_message(errno, errbuf, sizeof(errbuf))); + SH_MUTEX_LOCK(mutex_thread_nolog); + sh_error_handle((-1), FIL__, __LINE__, nerr, MSG_E_SUBGEN, errmsg, _("getsockname")); + SH_MUTEX_UNLOCK(mutex_thread_nolog); +#endif + } + + if (( sport != port ) || ( sh_portchk_same_ports == S_FALSE )) + { + do { retval = send (fd, buf, 0, 0); - } while (retval < 0 && errno == EINTR); + } while (retval < 0 && errno == EINTR); - if (retval == -1 && errno == ECONNREFUSED) + if (retval == -1 && errno == ECONNREFUSED) { sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); if (portchk_debug) - fprintf(stderr, _("check port: %5d/udp on %15s established/time_wait\n"), - port, ipbuf); + fprintf(stderr, _("check port_udp: %5d/udp on %15s established/time_wait\n"), + port, ipbuf); } - else if (retval != -1) + else { - /* Try to get service name from portmap + /* Only the second send() may catch the error */ - if (paddr->ss_family == AF_INET) - { - p = check_rpc_list (port, - (struct sockaddr_in *) sh_ipvx_sockaddr_cast(paddr), - IPPROTO_UDP); - } + do { + retval = send (fd, buf, 0, 0); + } while (retval < 0 && errno == EINTR); + + if (retval == -1 && errno == ECONNREFUSED) + { + sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); + if (portchk_debug) + fprintf(stderr, _("check port: %5d/udp on %15s established/time_wait\n"), + port, ipbuf); + } + else if (retval != -1) + { + /* Try to get service name from portmap + */ + if (paddr->ss_family == AF_INET) + { + p = check_rpc_list (port, + (struct sockaddr_in *) sh_ipvx_sockaddr_cast(paddr), + IPPROTO_UDP); + } - sh_portchk_cmp_to_list (IPPROTO_UDP, port, paddr, p ? p : NULL); + sh_portchk_cmp_to_list (IPPROTO_UDP, port, paddr, p ? p : NULL); - /* If not an RPC service, try to get name from /etc/services - */ - if (!p) - p = check_services(port, IPPROTO_UDP); + /* If not an RPC service, try to get name from /etc/services + */ + if (!p) + p = check_services(port, IPPROTO_UDP); - if (portchk_debug) - { - sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); - fprintf(stderr, _("check port_udp: %5d/udp on %15s open %s\n"), - port, ipbuf, p); - } + if (portchk_debug) + { + sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); + fprintf(stderr, _("check port_udp: %5d/udp on %15s open %s\n"), + port, ipbuf, p); + } + } + else + { + if (portchk_debug) + { + sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); + fprintf(stderr, _("check port_udp: %5d/udp on %15s ERRNO %d\n"), + port, ipbuf, errno); + } + } } - else - { - if (portchk_debug) - { - sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); - fprintf(stderr, _("check port_udp: %5d/udp on %15s ERRNO %d\n"), - port, ipbuf, errno); - } - } - } + } + else + { + if (portchk_debug) + { + sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); + fprintf(stderr, _("check port_udp: %5d/udp on %15s same source and destination port\n"), + port, ipbuf); + } + } } sl_close_fd (FIL__, __LINE__, fd); + sh_dummy_950_p = NULL; return 0; } static int check_port_tcp_internal (int fd, int port, struct sh_sockaddr * paddr) { - int retval; + volatile int retval; int flags; char * p = NULL; #ifndef TEST_ONLY char errmsg[256]; - int nerr; + volatile int nerr; #endif char errbuf[SH_ERRBUF_SIZE]; char ipbuf[SH_IP_BUF]; + struct sh_sockaddr saddr; + socklen_t slen = 0; + volatile int sport = 0; + + sh_dummy_951_p = (void*) &p; + sh_ipvx_set_port(paddr, port); do { @@ -1035,28 +1177,92 @@ static int check_port_tcp_internal (int fd, int port, struct sh_sockaddr * paddr } else { - /* Try to get service name from portmap + /* Register the used source port as transient. This will avoid + * the issue of lingering source ports being reported as a spurious + * service. The lingering source port is effectively a race condition. + * + * Also use this code to obtain the source port. Sometimes Samhain + * reports on a port where it connects back to itself. In that case + * source and destination port are the same. + * + * AGH, 23 Apr 2017 (www.2024sight.com). */ + +#if defined(USE_IPVX) if (paddr->ss_family == AF_INET) - { - p = check_rpc_list (port, - (struct sockaddr_in *) sh_ipvx_sockaddr_cast(paddr), - IPPROTO_TCP); - } + { + saddr.ss_family = AF_INET; + slen = sizeof( struct sockaddr_in ); + retval = getsockname(fd, (struct sockaddr *)&(saddr.sin), &slen); + } + else + { + saddr.ss_family = AF_INET6; + slen = sizeof( struct sockaddr_in6 ); + retval = getsockname(fd, (struct sockaddr *)&(saddr.sin6), &slen); + } +#else + saddr.ss_family = AF_INET; + slen = sizeof( struct sockaddr_in ); + retval = getsockname(fd, (struct sockaddr *)&(saddr.sin), &slen); +#endif + + if ( retval == 0 ) + { + sport = sh_ipvx_get_port(&saddr); + sh_portchk_transient(sport, &saddr, IPPROTO_TCP); + } + else + { +#ifdef TEST_ONLY + if (portchk_debug) + perror(_("getsockname")); +#else + sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); + nerr = errno; + sl_snprintf(errmsg, sizeof(errmsg), _("source port transient for %15s:%d/tcp: %s"), + ipbuf, port, sh_error_message(errno, errbuf, sizeof(errbuf))); + SH_MUTEX_LOCK(mutex_thread_nolog); + sh_error_handle((-1), FIL__, __LINE__, nerr, MSG_E_SUBGEN, + errmsg, _("getsockname")); + SH_MUTEX_UNLOCK(mutex_thread_nolog); +#endif + } - sh_portchk_cmp_to_list (IPPROTO_TCP, port, paddr, p ? p : NULL); + if (( sport != port ) || ( sh_portchk_same_ports == S_FALSE )) + { + /* Try to get service name from portmap + */ + if (paddr->ss_family == AF_INET) + { + p = check_rpc_list (port, + (struct sockaddr_in *) sh_ipvx_sockaddr_cast(paddr), + IPPROTO_TCP); + } - /* If not an RPC service, try to get name from /etc/services - */ - if (!p) - p = check_services(port, IPPROTO_TCP); + sh_portchk_cmp_to_list (IPPROTO_TCP, port, paddr, p ? p : NULL); - if (portchk_debug) - { - sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); - fprintf(stderr, _("check port_tcp: %5d on %15s open %s\n"), - port, ipbuf, p); - } + /* If not an RPC service, try to get name from /etc/services + */ + if (!p) + p = check_services(port, IPPROTO_TCP); + + if (portchk_debug) + { + sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); + fprintf(stderr, _("check port_tcp: %5d on %15s open %s\n"), + port, ipbuf, p); + } + } + else + { + if (portchk_debug) + { + sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr); + fprintf(stderr, _("check port_udp: %5d/tcp on %15s same source and destination port\n"), + port, ipbuf); + } + } #if !defined(O_NONBLOCK) #if defined(O_NDELAY) @@ -1108,6 +1314,7 @@ static int check_port_tcp_internal (int fd, int port, struct sh_sockaddr * paddr port); } sl_close_fd (FIL__, __LINE__, fd); + sh_dummy_951_p = NULL; return 0; } @@ -1118,14 +1325,17 @@ static int check_port_tcp_internal (int fd, int port, struct sh_sockaddr * paddr * }; */ -#define SH_IFACE_MAX 16 +#define SH_IFACE_MAX 64 +#define SH_IFACE_ADDR 0 +#define SH_IFACE_DEV 1 struct portchk_interfaces { - struct sh_sockaddr iface[SH_IFACE_MAX]; - int used; + struct sh_sockaddr iface; + int type; }; -static struct portchk_interfaces iface_list; +static struct portchk_interfaces iface_list[SH_IFACE_MAX]; +static int iface_list_used = 0; static int iface_initialized = 0; #ifdef TEST_ONLY @@ -1158,7 +1368,7 @@ static int sh_portchk_init_internal (void) SH_MUTEX_LOCK(mutex_port_check); if (iface_initialized == 0) { - iface_list.used = 0; + iface_list_used = 0; iface_initialized = 1; } @@ -1166,7 +1376,7 @@ static int sh_portchk_init_internal (void) SH_MUTEX_LOCK(mutex_resolv); hent = sh_gethostbyname(portchk_hostname); i = 0; - while (hent && hent->h_addr_list[i] && (iface_list.used < SH_IFACE_MAX)) + while (hent && hent->h_addr_list[i] && (iface_list_used < SH_IFACE_MAX)) { struct sockaddr_in sin; struct sh_sockaddr iface_tmp; @@ -1174,31 +1384,32 @@ static int sh_portchk_init_internal (void) memcpy(&(sin.sin_addr.s_addr), hent->h_addr_list[i], sizeof(in_addr_t)); sh_ipvx_save(&iface_tmp, AF_INET, (struct sockaddr *)&sin); - for (j = 0; j < iface_list.used; ++j) + for (j = 0; j < iface_list_used; ++j) { - if (0 == sh_ipvx_cmp(&iface_tmp, &(iface_list.iface[j]))) + if (0 == sh_ipvx_cmp(&iface_tmp, &(iface_list[j].iface))) { goto next_iface; } } - sh_ipvx_save(&(iface_list.iface[iface_list.used]), + sh_ipvx_save(&(iface_list[iface_list_used].iface), AF_INET, (struct sockaddr *)&sin); + iface_list[iface_list_used].type = SH_IFACE_ADDR; if (portchk_debug) { char buf[256]; - sh_ipvx_ntoa(buf, sizeof(buf), &(iface_list.iface[iface_list.used])); + sh_ipvx_ntoa(buf, sizeof(buf), &(iface_list[iface_list_used].iface)); fprintf(stderr, _("added interface[%d]: %s\n"), i, buf); } - ++iface_list.used; + ++iface_list_used; next_iface: ++i; } SH_MUTEX_UNLOCK(mutex_resolv); #else - memset(&hints, '\0', sizeof(hints)); + memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_ADDRCONFIG; @@ -1207,37 +1418,37 @@ static int sh_portchk_init_internal (void) struct addrinfo *p = res; struct sh_sockaddr iface_tmp; - while ((p != NULL) && (iface_list.used < SH_IFACE_MAX)) + while ((p != NULL) && (iface_list_used < SH_IFACE_MAX)) { sh_ipvx_save(&iface_tmp, p->ai_family, p->ai_addr); - for (j = 0; j < iface_list.used; ++j) + for (j = 0; j < iface_list_used; ++j) { if (portchk_debug) { char buf1[256], buf2[256]; - sh_ipvx_ntoa(buf1, sizeof(buf1), &(iface_list.iface[j])); + sh_ipvx_ntoa(buf1, sizeof(buf1), &(iface_list[j].iface)); sh_ipvx_ntoa(buf2, sizeof(buf2), &iface_tmp); fprintf(stderr, _("check interface[%d]: %s vs %s\n"), j, buf1, buf2); } - if (0 == sh_ipvx_cmp(&iface_tmp, &(iface_list.iface[j]))) + if (0 == sh_ipvx_cmp(&iface_tmp, &(iface_list[j].iface))) { if (portchk_debug) fprintf(stderr, _("skipping interface[%d]\n"), j); goto next_iface; } } - sh_ipvx_save(&(iface_list.iface[iface_list.used]), + sh_ipvx_save(&(iface_list[iface_list_used].iface), p->ai_family, p->ai_addr); - + iface_list[iface_list_used].type = SH_IFACE_ADDR; if (portchk_debug) { char buf[256]; - sh_ipvx_ntoa(buf, sizeof(buf), &(iface_list.iface[iface_list.used])); - fprintf(stderr, _("added interface[%d]: %s\n"), iface_list.used, buf); + sh_ipvx_ntoa(buf, sizeof(buf), &(iface_list[iface_list_used].iface)); + fprintf(stderr, _("added interface[%d]: %s\n"), iface_list_used, buf); } - ++iface_list.used; + ++iface_list_used; next_iface: p = p->ai_next; @@ -1246,9 +1457,9 @@ static int sh_portchk_init_internal (void) } #endif - for (i = 0; i < iface_list.used; ++i) + for (i = 0; i < iface_list_used; ++i) { - sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), &(iface_list.iface[i])); + sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), &(iface_list[i].iface)); sl_snprintf(errbuf, sizeof(errbuf), _("added interface: %s"), ipbuf); SH_MUTEX_LOCK(mutex_thread_nolog); @@ -1284,13 +1495,14 @@ int sh_portchk_init (struct mod_type * arg) else if (arg != NULL && arg->initval == SH_MOD_THREAD && (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE)) { + (void) sh_portchk_init_internal(); return SH_MOD_THREAD; } #endif return sh_portchk_init_internal(); } - +static void dev_list_kill(); #if !defined(TEST_ONLY) int sh_portchk_reconf (void) @@ -1304,6 +1516,8 @@ int sh_portchk_reconf (void) sh_portchk_minport = -1; sh_portchk_maxport = -1; + dev_list_kill(); + portlist_udp = sh_portchk_kill_list (portlist_udp); portlist_tcp = sh_portchk_kill_list (portlist_tcp); @@ -1344,9 +1558,9 @@ static int check_port_generic (int port, int domain, int type, int protocol) /* Check all interfaces for this host */ - while (i < iface_list.used) + while (i < iface_list_used) { - memcpy(&paddr, &(iface_list.iface[i]), sizeof(paddr)); + memcpy(&paddr, &(iface_list[i].iface), sizeof(paddr)); if (paddr.ss_family != domain) { @@ -1360,6 +1574,12 @@ static int check_port_generic (int port, int domain, int type, int protocol) continue; } + if (0 != sh_portchk_is_transient(port, &paddr, protocol)) + { + ++i; + continue; + } + if ((sock = socket(paddr.ss_family, type, protocol)) < 0 ) { ++i; @@ -1549,6 +1769,12 @@ static int sh_portchk_scan_ports_generic (int min_port, int max_port_arg, sl_close_fd (FIL__, __LINE__, sock); } } + + if (protocol == IPPROTO_TCP) + transient_tcp=sh_portchk_kill_transient(transient_tcp); + else + transient_udp=sh_portchk_kill_transient(transient_udp); + return 0; } @@ -1576,7 +1802,7 @@ static int sh_portchk_scan_ports_udp (int min_port, int max_port) */ void * sh_dummy_1564_str = NULL; /* fix clobbered by.. warning */ -static int sh_portchk_add_interface (const char * str) +static int sh_portchk_add_interface_int (const char * str, int type) { struct sh_sockaddr saddr; char errbuf[256]; @@ -1586,7 +1812,7 @@ static int sh_portchk_add_interface (const char * str) if (iface_initialized == 0) { - iface_list.used = 0; + iface_list_used = 0; iface_initialized = 1; } @@ -1608,7 +1834,7 @@ static int sh_portchk_add_interface (const char * str) if (0 == sh_ipvx_aton(buf, &saddr)) return -1; - if (iface_list.used == SH_IFACE_MAX) + if (iface_list_used == SH_IFACE_MAX) return -1; sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), &saddr); @@ -1618,8 +1844,9 @@ static int sh_portchk_add_interface (const char * str) errbuf, _("sh_portchk_add_interface")); SH_MUTEX_UNLOCK(mutex_thread_nolog); - memcpy (&(iface_list.iface[iface_list.used]), &(saddr), sizeof(saddr)); - ++iface_list.used; + memcpy (&(iface_list[iface_list_used].iface), &(saddr), sizeof(saddr)); + iface_list[iface_list_used].type = type; + ++iface_list_used; } } while (*str); @@ -1627,6 +1854,230 @@ static int sh_portchk_add_interface (const char * str) return 0; } +static int sh_portchk_add_interface (const char * str) +{ + return sh_portchk_add_interface_int (str, SH_IFACE_ADDR); +} + +#if defined(HAVE_IFADDRS_H) +/* + * subroutines to add a device + */ +void * sh_dummy_1651_ifa = NULL; /* fix clobbered by.. warning */ + +static int portchk_add_device_int (const char * buf) +{ + struct ifaddrs *ifaddr, *ifa; + int family; +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif + char host[NI_MAXHOST]; + + sh_dummy_1651_ifa = (void*) &ifa; + + if (getifaddrs(&ifaddr) == -1) + { + volatile int nerr = errno; + char errbuf[SH_ERRBUF_SIZE]; + sh_error_message(errno, errbuf, sizeof(errbuf)); + SH_MUTEX_LOCK(mutex_thread_nolog); + sh_error_handle((-1), FIL__, __LINE__, nerr, MSG_E_SUBGEN, + errbuf, _("getifaddrs")); + SH_MUTEX_UNLOCK(mutex_thread_nolog); + return -1; + } + + for ( ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == NULL) + continue; + + if (strcmp(ifa->ifa_name, buf) == 0) + { + volatile int s = 0; + family = ifa->ifa_addr->sa_family; + + if (family == AF_INET) + { + s = getnameinfo( ifa->ifa_addr, sizeof(struct sockaddr_in), + host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST ); + + if (s == 0) + { + if (sh_portchk_add_interface_int(host, SH_IFACE_DEV) < 0) + { + freeifaddrs(ifaddr); + return -1; + } + } + } + +#if defined(USE_IPVX) + if (family == AF_INET6) + { + s = getnameinfo( ifa->ifa_addr, sizeof(struct sockaddr_in6), + host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST ); + + if (s == 0) + { + if (sh_portchk_add_interface_int(host, SH_IFACE_DEV) < 0) + { + freeifaddrs(ifaddr); + return -1; + } + } + } +#endif + + if (s != 0) + { + char errbuf[SH_ERRBUF_SIZE]; + sl_strlcpy(errbuf, buf, sizeof(errbuf)); + sl_strlcat(errbuf, ": ", sizeof(errbuf)); + sl_strlcat(errbuf, gai_strerror(s), sizeof(errbuf)); + SH_MUTEX_LOCK(mutex_thread_nolog); + sh_error_handle((-1), FIL__, __LINE__, s, MSG_E_SUBGEN, + errbuf, _("getnameinfo")); + SH_MUTEX_UNLOCK(mutex_thread_nolog); + } + + } + } + + freeifaddrs(ifaddr); + return 0; +} + +struct added_dev { + char dev[64]; + struct added_dev * next; +}; + +static struct added_dev * dev_list = NULL; + +static void dev_list_add (char * buf) +{ + struct added_dev * new = SH_ALLOC(sizeof(struct added_dev)); + sl_strlcpy(new->dev, buf, 64); + new->next = dev_list; + dev_list = new; + return; +} + +static void dev_list_kill () +{ + struct added_dev * old; + struct added_dev * new = dev_list; + dev_list = NULL; + + while (new) + { + old = new; + new = new->next; + SH_FREE(old); + } + return; +} + +static int sh_portchk_add_device (const char * str) +{ + char buf[64]; + + do { + + while (*str == ',' || *str == ' ' || *str == '\t') ++str; + + if (*str) + { + unsigned int i = 0; + while (*str && i < (sizeof(buf)-1) && + *str != ',' && *str != ' ' && *str != '\t') { + buf[i] = *str; ++str; ++i; + } + buf[i] = '\0'; + + if (portchk_add_device_int (buf) < 0) + return -1; + + dev_list_add(buf); + } + } while (*str); + + return 0; +} + +static int iface_comp (const void *a, const void *b) +{ + const struct portchk_interfaces * aa = (const struct portchk_interfaces *) a; + const struct portchk_interfaces * bb = (const struct portchk_interfaces *) b; + return (aa->type - bb->type); +} + +static void iface_qsort() +{ + qsort(&iface_list[0], iface_list_used, sizeof(struct portchk_interfaces), + iface_comp); + return; +} + +static void recheck_devices() +{ + if (dev_list) + { + struct added_dev * dev = dev_list; + int i, j; + + if (portchk_debug) + { + for (j = 0; j < iface_list_used; ++j) + { + char buf[SH_IP_BUF]; + struct portchk_interfaces * aa = &(iface_list[j]); + sh_ipvx_ntoa(buf, sizeof(buf), &(aa->iface)); + fprintf(stderr, _("presort: iface[%d] type(%d) %s\n"), j, iface_list[j].type, buf); + } + } + + iface_qsort(); + + if (portchk_debug) + { + for (j = 0; j < iface_list_used; ++j) + { + char buf[SH_IP_BUF]; + struct portchk_interfaces * aa = &(iface_list[j]); + sh_ipvx_ntoa(buf, sizeof(buf), &(aa->iface)); + fprintf(stderr, _("postsor: iface[%d] type(%d) %s\n"), j, iface_list[j].type, buf); + } + } + + i = 0; + for (j = 0; j < iface_list_used; ++j) + if (iface_list[j].type == SH_IFACE_DEV) ++i; + iface_list_used -= i; + + if (portchk_debug) + { + for (j = 0; j < iface_list_used; ++j) + { + char buf[SH_IP_BUF]; + struct portchk_interfaces * aa = &(iface_list[j]); + sh_ipvx_ntoa(buf, sizeof(buf), &(aa->iface)); + fprintf(stderr, _("postdel: iface[%d] type(%d) %s\n"), j, iface_list[j].type, buf); + } + } + + while (dev) + { + portchk_add_device_int (dev->dev); + dev = dev->next; + } + } + return; +} +#endif + /* verify whether port/interface is blacklisted (do not check) */ static int sh_portchk_is_blacklisted(int port, struct sh_sockaddr * saddr, @@ -1641,14 +2092,8 @@ static int sh_portchk_is_blacklisted(int port, struct sh_sockaddr * saddr, while (head) { - if (head->port == port) - { - if (sh_ipvx_isany(head->paddr) || - 0 == sh_ipvx_cmp(head->paddr, saddr)) - return 1; - else - return 0; - } + if (head->port == port && ( sh_ipvx_isany(head->paddr) || 0 == sh_ipvx_cmp(head->paddr, saddr) )) + return 1; head = head->next; } return 0; @@ -1694,8 +2139,75 @@ static int sh_portchk_blacklist(int port, struct sh_sockaddr * saddr, int proto) } return 0; } + + +/* verify whether port/interface is transient (used as source port + *hence no check required) + */ +static int sh_portchk_is_transient(int port, struct sh_sockaddr * saddr, + int proto) +{ + struct sh_port * head; - + if (proto == IPPROTO_TCP) + head = transient_tcp; + else + head = transient_udp; + + while (head) + { + if (head->port == port && ( sh_ipvx_isany(head->paddr) || 0 == sh_ipvx_cmp(head->paddr, saddr) )) + return 1; + head = head->next; + } + return 0; +} + + +static int sh_portchk_transient(int port, struct sh_sockaddr * saddr, int proto) +{ + struct sh_port * transient; + struct sh_port * head; + + if (sh_portchk_transients == S_FALSE) + return 0; + + if (proto == IPPROTO_TCP) + head = transient_tcp; + else + head = transient_udp; + + transient = head; + + while (transient) + { + if (transient->port == port && + 0 == sh_ipvx_cmp(head->paddr, saddr)) + return -1; + transient = transient->next; + } + + transient = SH_ALLOC (sizeof(struct sh_port)); + transient->paddr = SH_ALLOC (sizeof(struct sh_sockaddr)); + transient->port = port; + memcpy(transient->paddr, saddr, sizeof(struct sh_sockaddr)); + transient->next = head; + + if (proto == IPPROTO_TCP) + transient_tcp = transient; + else + transient_udp = transient; + + if (portchk_debug) + { + int checkit = sh_portchk_is_transient(port, saddr, proto); + fprintf(stderr, _("port transient: %d %s\n"), port, + (checkit == 1) ? _("ok") : _("fail")); + } + return 0; +} + + /* Subroutine to add a required or optional port/service */ static int sh_portchk_add_required_port_generic (char * service, @@ -1717,9 +2229,9 @@ static int sh_portchk_add_required_port_generic (char * service, p = strchr(buf, '/'); if (!p) return -1; - if (0 == strcmp(p, _("/tcp"))) + if (0 == strcasecmp(p, _("/tcp"))) proto = IPPROTO_TCP; - else if (0 == strcmp(p, _("/udp"))) + else if (0 == strcasecmp(p, _("/udp"))) proto = IPPROTO_UDP; else return -1; @@ -1927,6 +2439,11 @@ int sh_portchk_check () SH_MUTEX_UNLOCK(mutex_thread_nolog); sh_portchk_reset_lists(); + +#if defined(HAVE_IFADDRS_H) + recheck_devices(); +#endif + if ((0 != geteuid()) && (min_port < 1024)) { min_port = 1024; |