summaryrefslogtreecommitdiffstats
path: root/src/sh_portcheck.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sh_portcheck.c')
-rw-r--r--src/sh_portcheck.c759
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;