/* SAMHAIN file system integrity testing */ /* Copyright (C) 2006 Rainer Wichmann */ /* */ /* This program is free software; you can redistribute it */ /* and/or modify */ /* it under the terms of the GNU General Public License as */ /* published by */ /* the Free Software Foundation; either version 2 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /*************************************************************************** * * This file provides a module for samhain to check for open ports * on the local machine. * */ /* #define TEST_ONLY */ #ifndef TEST_ONLY #include "config_xor.h" #endif #include #include #include #include #include #include #include #include #include #define PORTCHK_VERSION "1.0" #if defined(TEST_ONLY) || (defined(SH_USE_PORTCHECK) && (defined (SH_WITH_CLIENT) || defined (SH_STANDALONE))) #define PORTMAP #include #ifdef HAVE_RPC_RPCENT_H #include #endif #include #include #include /* * struct pmaplist { * struct pmap pml_map; * struct pmaplist *pml_next; * }; */ /* struct pmap { * long unsigned pm_prog; * long unsigned pm_vers; * long unsigned pm_prot; * long unsigned pm_port; * }; */ /* TIME_WAIT ? 60-240 seconds */ /* the size of an interface string */ #define SH_INTERFACE_SIZE 16 #define SH_PORT_NOT 0 #define SH_PORT_REQ 1 #define SH_PORT_OPT 2 #define SH_PORT_IGN 3 #define SH_PORT_MISS 0 #define SH_PORT_ISOK 1 #define SH_PORT_UNKN 2 #define SH_PORT_NOREPT 0 #define SH_PORT_REPORT 1 struct sh_portentry { int port; char interface[SH_INTERFACE_SIZE]; char * service; char * error; int flag; /* required or not */ int status; /* missing or not */ struct sh_portentry * next; }; static struct sh_portentry * portlist_tcp = NULL; 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; #if !defined(TEST_ONLY) #define FIL__ _("sh_portcheck.c") #include "samhain.h" #include "sh_error.h" #include "sh_mem.h" #include "sh_calls.h" #include "sh_utils.h" #include "sh_modules.h" #include "sh_pthread.h" static int sh_portchk_severity = SH_ERR_SEVERE; #endif /* Exported interface to add ignoreable ports as 'iface:portlist' */ static int sh_portchk_add_ignore (const char * str); /* Exported interface to add required ports as 'iface:portlist' */ static int sh_portchk_add_required (const char * str); /* Exported interface to add optional ports as 'iface:portlist' */ static int sh_portchk_add_optional (const char * str); /* Exported interface to add an ethernet interface */ static int sh_portchk_add_interface (const char * str); #ifndef TEST_ONLY static int sh_portchk_set_interval (const char * c) { int retval = 0; long val; SL_ENTER(_("sh_portchk_set_interval")); val = strtol (c, (char **)NULL, 10); if (val <= 0) { sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS, _("port check interval"), c); retval = -1; } val = (val <= 0 ? 60 : val); sh_portchk_interval = (time_t) val; SL_RETURN(0, _("sh_portchk_set_interval")); } 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) { return sh_util_flagval(str, &sh_portchk_check_udp); } static int sh_portchk_set_severity (const char * str) { char tmp[32]; tmp[0] = '='; tmp[1] = '\0'; sl_strlcat (tmp, str, 32); return sh_error_set_level (tmp, &sh_portchk_severity); } sh_rconf sh_portchk_table[] = { { N_("severityportcheck"), sh_portchk_set_severity, }, { N_("portcheckrequired"), sh_portchk_add_required, }, { N_("portcheckoptional"), sh_portchk_add_optional, }, { N_("portcheckignore"), sh_portchk_add_ignore, }, { N_("portcheckactive"), sh_portchk_set_active, }, { N_("portcheckinterface"), sh_portchk_add_interface, }, { N_("portcheckinterval"), sh_portchk_set_interval, }, { N_("portcheckudp"), sh_portchk_set_udp, }, { NULL, NULL } }; #endif /* Interface to initialize port check */ int sh_portchk_init (); /* Interface to reset port check */ int sh_portchk_reset (); /* Interface to run port check */ int sh_portchk_check (); static char * check_services (int port, char * proto); #ifdef TEST_ONLY static int portchk_debug = 0; #define SH_ALLOC malloc #define SH_FREE free #define sh_util_strdup strdup #define sl_strlcpy strncpy #define _(a) a #else static int portchk_debug = 0; #endif static void sh_portchk_add_to_list (char * proto, int port, struct in_addr haddr, char * service, int flag, int status) { struct sh_portentry * new = SH_ALLOC (sizeof(struct sh_portentry)); if (portchk_debug) fprintf(stderr, _("add to list: port %d/%s %d %d (%s)\n"), port, proto, flag, status, service ? service : _("undef")); new->port = port; sl_strlcpy (new->interface, inet_ntoa(haddr), SH_INTERFACE_SIZE); new->status = status; new->flag = flag; new->error = NULL; if (service) new->service = sh_util_strdup (service); else new->service = NULL; if (0 == strcmp(proto, "tcp")) { new->next = portlist_tcp; portlist_tcp = new; } else { new->next = portlist_udp; portlist_udp = new; } return; } /* Reset the list by setting all entries to UNKN. * In the next cycle we will check, and set found ports to ISOK. * Thereafter, we check for entries that are still UNKN. */ static void sh_portchk_reset_lists () { struct sh_portentry * portlist; portlist = portlist_tcp; while (portlist) { if (portlist->status != SH_PORT_MISS) portlist->status = SH_PORT_UNKN; portlist = portlist->next; } portlist = portlist_udp; while (portlist) { if (portlist->status != SH_PORT_MISS) portlist->status = SH_PORT_UNKN; portlist = portlist->next; } return; } static struct sh_portentry * sh_portchk_kill_list (struct sh_portentry * head) { if (head) { if (head->next) sh_portchk_kill_list (head->next); if (head->service) SH_FREE(head->service); SH_FREE(head); } return NULL; } /* check the list of open ports for any that are marked as UNKN */ static void sh_portchk_check_list (struct sh_portentry ** head, char * proto, int report) { struct sh_portentry * ptr = *head; struct sh_portentry * pre = *head; char errbuf[256]; while (ptr) { if (portchk_debug && report) fprintf(stderr, _("check list: port %d/%s %d %d\n"), ptr->port, proto, ptr->flag, ptr->status); if (ptr->status == SH_PORT_UNKN) { /* Don't report missing ports that are marked as optional */ if (ptr->flag != SH_PORT_OPT && ptr->flag != SH_PORT_IGN) { snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceMissing] port %s:%d/%s (%s)"), ptr->interface, ptr->port, proto, ptr->service ? ptr->service : check_services(ptr->port, proto)); #ifdef TEST_ONLY if (report == SH_PORT_REPORT) fprintf(stderr, _("%s\n"), errbuf); #else if (report == SH_PORT_REPORT) sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, MSG_PORT_REPORT, errbuf); #endif } ptr->status = SH_PORT_MISS; if ((ptr->flag != SH_PORT_REQ) && (ptr->flag != SH_PORT_OPT) && (ptr->flag != SH_PORT_IGN)) { if (portchk_debug && report) fprintf(stderr, _("removing: port %d/%s %d %d\n"), ptr->port, proto, ptr->flag, ptr->status); if (ptr == *head) { *head = ptr->next; if (ptr->service) SH_FREE(ptr->service); SH_FREE(ptr); ptr = *head; pre = *head; continue; } else if (ptr->next == NULL) { pre->next = NULL; if (ptr->service) SH_FREE(ptr->service); SH_FREE(ptr); return; } else { pre->next = ptr->next; if (ptr->service) SH_FREE(ptr->service); SH_FREE(ptr); ptr = pre->next; continue; } } } pre = ptr; ptr = ptr->next; } return; } static struct sh_portentry * sh_portchk_get_from_list (char * proto, int port, struct in_addr haddr, char * service) { struct sh_portentry * portlist; char iface_all[8]; sl_strlcpy (iface_all, _("0.0.0.0"), sizeof(iface_all)); if (0 == strcmp(proto, "tcp")) portlist = portlist_tcp; else portlist = portlist_udp; if (service) { while (portlist) { if (portlist->service && 0 == strcmp(service, portlist->service) && (0 == strcmp(portlist->interface, inet_ntoa(haddr)) || 0 == strcmp(portlist->interface, iface_all))) return portlist; portlist = portlist->next; } } else { while (portlist) { if (port == portlist->port && (0 == strcmp(portlist->interface, inet_ntoa(haddr)) || 0 == strcmp(portlist->interface, iface_all))) return portlist; portlist = portlist->next; } } return NULL; } static void sh_portchk_cmp_to_list (char * proto, int port, struct in_addr haddr, char * service) { struct sh_portentry * portent; char errbuf[256]; portent = sh_portchk_get_from_list (proto, port, haddr, service); if (service) { if (!portent) { snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceNew] port %s:%d/%s (%s)"), inet_ntoa(haddr), port, proto, service); #ifdef TEST_ONLY fprintf(stderr, _("open port: %s:%d/%s (%s)\n"), inet_ntoa(haddr), port, proto, service); #else sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, MSG_PORT_REPORT, errbuf); #endif /* * was not there, thus it is not in 'required' or 'optional' list */ sh_portchk_add_to_list (proto, port, haddr, service, SH_PORT_NOT, SH_PORT_ISOK); } else if (portent->status == SH_PORT_MISS && portent->flag != SH_PORT_IGN) { snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceRestarted] port %s:%d/%s to %d/%s (%s)"), inet_ntoa(haddr), portent->port, proto, port, proto, service); #ifdef TEST_ONLY fprintf(stderr, _("service: %s\n"), errbuf); #else sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, MSG_PORT_REPORT, errbuf); #endif portent->status = SH_PORT_ISOK; } else if (port != portent->port && (-1) != portent->port) { snprintf (errbuf, sizeof(errbuf), _("POLICY [ServicePortSwitch] port %s:%d/%s to %d/%s (%s)"), inet_ntoa(haddr), portent->port, proto, port, proto, service); #ifdef TEST_ONLY fprintf(stderr, _("service: %s\n"), errbuf); #else sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, MSG_PORT_REPORT, errbuf); #endif portent->port = port; portent->status = SH_PORT_ISOK; } else { portent->status = SH_PORT_ISOK; } } else { if (!portent) { snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceNew] port %s:%d/%s (%s)"), inet_ntoa(haddr), port, proto, check_services(port, proto)); #ifdef TEST_ONLY fprintf(stderr, _("open port: %s:%d/%s (%s)\n"), inet_ntoa(haddr), port, proto, check_services(port, proto)); #else sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, MSG_PORT_REPORT, errbuf); #endif /* was not there, thus it is not in 'required' or 'optional' list */ sh_portchk_add_to_list (proto, port, haddr, service, SH_PORT_NOT, SH_PORT_ISOK); } else if (portent->status == SH_PORT_MISS && portent->flag != SH_PORT_IGN) { snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceRestarted] port %s:%d/%s (%s)"), inet_ntoa(haddr), port, proto, check_services(port, proto)); #ifdef TEST_ONLY fprintf(stderr, _("port : %s\n"), errbuf); #else sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, MSG_PORT_REPORT, errbuf); #endif portent->status = SH_PORT_ISOK; } else { portent->status = SH_PORT_ISOK; } } return; } /* Returns a static buffer containing the name of the service * running on port (from /etc/services) * Returns NULL on failure */ static char * check_services (int port, char * proto) { static char buf[256]; struct servent * service = getservbyport(htons(port), proto); if (service && service->s_name && service->s_name[0] != '\0') { snprintf (buf, sizeof(buf), _("maybe_%s"), service->s_name); } else { snprintf (buf, sizeof(buf), _("unknown")); } return buf; } /* Returns a static buffer containing the name of the service * running on port at
(from portmap daemon) * Returns NULL on failure */ static char * check_rpc_list (int port, struct sockaddr_in * address, unsigned long prot) { struct pmaplist * head; struct rpcent *r; static char buf[256]; head = pmap_getmaps(address); if (head) { do /* while (head != NULL) */ { if ((head->pml_map.pm_prot == prot) && (port == (int)head->pml_map.pm_port)) { r = getrpcbynumber((int)head->pml_map.pm_prog); if (r && r->r_name && r->r_name[0] != '\0') { snprintf (buf, sizeof(buf), "%s", r->r_name); return buf; } else { snprintf (buf, sizeof(buf), "RPC_%lu", (unsigned long)head->pml_map.pm_prog); return buf; } } head = head->pml_next; } while (head != NULL); } return NULL; } static int check_port_udp_internal (int fd, int port, struct in_addr haddr) { struct sockaddr_in sinr; /* struct in_addr haddr; */ int retval; char * p; char buf[8]; #ifndef TEST_ONLY char errmsg[256]; int nerr; #endif char errbuf[SH_ERRBUF_SIZE]; /* inet_aton(interface, &haddr); */ sinr.sin_family = AF_INET; sinr.sin_port = htons (port); sinr.sin_addr = haddr; do { retval = connect(fd, (struct sockaddr *) &sinr, sizeof(sinr)); } while (retval < 0 && errno == EINTR); if (retval == -1) { #ifdef TEST_ONLY if (portchk_debug) perror(_("connect")); #else nerr = errno; sl_snprintf(errmsg, sizeof(errmsg), _("check port: %5d/udp on %15s: %s"), port, inet_ntoa(haddr), sh_error_message(errno, errbuf, sizeof(errbuf))); sh_error_handle((-1), FIL__, __LINE__, nerr, MSG_E_SUBGEN, errmsg, _("connect")); #endif } else { do { retval = send (fd, buf, 0, 0); } while (retval < 0 && errno == EINTR); if (retval == -1 && errno == ECONNREFUSED) { if (portchk_debug) fprintf(stderr, _("check port: %5d/udp on %15s established/time_wait\n"), port, inet_ntoa(haddr)); } else { /* Only the second send() may catch the error */ do { retval = send (fd, buf, 0, 0); } while (retval < 0 && errno == EINTR); if (retval == -1 && errno == ECONNREFUSED) { if (portchk_debug) fprintf(stderr, _("check port: %5d/udp on %15s established/time_wait\n"), port, inet_ntoa(haddr)); } else if (retval != -1) { /* Try to get service name from portmap */ p = check_rpc_list (port, &sinr, IPPROTO_UDP); sh_portchk_cmp_to_list ("udp", port, haddr, p ? p : NULL); /* If not an RPC service, try to get name from /etc/services */ if (!p) p = check_services(port, "udp"); if (portchk_debug) fprintf(stderr, _("check port: %5d/udp on %15s open %s\n"), port, inet_ntoa(haddr), p); } } } close (fd); return 0; } static int check_port_tcp_internal (int fd, int port, struct in_addr haddr) { struct sockaddr_in sinr; /* struct in_addr haddr; */ int retval; int flags; char * p; #ifndef TEST_ONLY char errmsg[256]; int nerr; #endif char errbuf[SH_ERRBUF_SIZE]; /* inet_aton(interface, &haddr); */ sinr.sin_family = AF_INET; sinr.sin_port = htons (port); sinr.sin_addr = haddr; do { retval = connect(fd, (struct sockaddr *) &sinr, sizeof(sinr)); } while (retval < 0 && errno == EINTR); if (retval == -1 && errno == ECONNREFUSED) { if (portchk_debug) fprintf(stderr, _("check port: %5d on %15s established/time_wait\n"), port, inet_ntoa(haddr)); } else if (retval == -1) { #ifdef TEST_ONLY if (portchk_debug) perror(_("connect")); #else nerr = errno; sl_snprintf(errmsg, sizeof(errmsg), _("check port: %5d/tcp on %15s: %s"), port, inet_ntoa(haddr), sh_error_message(errno, errbuf, sizeof(errbuf))); sh_error_handle((-1), FIL__, __LINE__, nerr, MSG_E_SUBGEN, errmsg, _("connect")); #endif } else { /* Try to get service name from portmap */ p = check_rpc_list (port, &sinr, IPPROTO_TCP); sh_portchk_cmp_to_list ("tcp", port, haddr, p ? p : NULL); /* If not an RPC service, try to get name from /etc/services */ if (!p) p = check_services(port, "tcp"); if (portchk_debug) fprintf(stderr, _("check port: %5d on %15s open %s\n"), port, inet_ntoa(haddr), p); #if !defined(O_NONBLOCK) #if defined(O_NDELAY) #define O_NONBLOCK O_NDELAY #else #define O_NONBLOCK 0 #endif #endif /* prepare to close connection gracefully */ if (port == 22) /* ssh */ { flags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0); retry_fcntl(FIL__, __LINE__, fd, F_SETFL, flags | O_NONBLOCK); write (fd, _("SSH-2.0-Foobar"), 14); write (fd, "\r\n", 2); } else if (port == 25) /* smtp */ { flags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0); retry_fcntl(FIL__, __LINE__, fd, F_SETFL, flags | O_NONBLOCK); write (fd, _("QUIT"), 4); write (fd, "\r\n", 2); } else if (port == 79) /* finger */ { flags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0); retry_fcntl(FIL__, __LINE__, fd, F_SETFL, flags | O_NONBLOCK); write (fd, "\r\n", 2); } else if (port == 110) /* pop3 */ { flags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0); retry_fcntl(FIL__, __LINE__, fd, F_SETFL, flags | O_NONBLOCK); write (fd, _("QUIT"), 4); write (fd, "\r\n", 2); } else if (port == 143) /* imap */ { flags = retry_fcntl(FIL__, __LINE__, fd, F_GETFL, 0); retry_fcntl(FIL__, __LINE__, fd, F_SETFL, flags | O_NONBLOCK); write (fd, _("A01 LOGOUT"), 10); write (fd, "\r\n", 2); } } close (fd); return 0; } /* typedef uint32_t in_addr_t; * struct in_addr * { * in_addr_t s_addr; * }; */ #define SH_IFACE_MAX 16 struct portchk_interfaces { struct in_addr iface[SH_IFACE_MAX]; int used; }; static struct portchk_interfaces iface_list; static int iface_initialized = 0; #ifdef TEST_ONLY static char * portchk_hostname = NULL; #else static char * portchk_hostname = sh.host.name; #endif int sh_portchk_init () { struct hostent * hent; int i = 0; char errbuf[256]; if (portchk_debug) fprintf(stderr, _("checking ports on: %s\n"), portchk_hostname ? portchk_hostname : _("NULL")); if (!portchk_hostname) return -1; if (sh_portchk_active == S_FALSE) return -1; if (iface_initialized == 0) { iface_list.used = 0; iface_initialized = 1; } SH_MUTEX_LOCK(mutex_resolv); hent = gethostbyname(portchk_hostname); while (hent && hent->h_addr_list[i] && (iface_list.used < SH_IFACE_MAX)) { memcpy (&(iface_list.iface[iface_list.used].s_addr), hent->h_addr_list[i], sizeof(in_addr_t)); ++iface_list.used; ++i; } SH_MUTEX_UNLOCK(mutex_resolv); for (i = 0; i < iface_list.used; ++i) { sl_snprintf(errbuf, sizeof(errbuf), _("interface: %s"), inet_ntoa(iface_list.iface[i])); sh_error_handle(SH_ERR_INFO, FIL__, __LINE__, 0, MSG_E_SUBGEN, errbuf, _("sh_portchk_init")); } return 0; } #if !defined(TEST_ONLY) int sh_portchk_reconf () { iface_initialized = 0; sh_portchk_active = 1; sh_portchk_check_udp = 1; portlist_udp = sh_portchk_kill_list (portlist_udp); portlist_tcp = sh_portchk_kill_list (portlist_tcp); return 0; } int sh_portchk_cleanup () { return sh_portchk_reconf (); } int sh_portchk_timer (time_t tcurrent) { static time_t lastcheck = 0; SL_ENTER(_("sh_portchk_timer")); if ((time_t) (tcurrent - lastcheck) >= sh_portchk_interval) { lastcheck = tcurrent; SL_RETURN((-1), _("sh_portchk_timer")); } SL_RETURN(0, _("sh_portchk_timer")); } #endif static int check_port_generic (int port, int type, int protocol) { int i = 0; int sock = -1; int flag = 1; /* non-zero to enable an option */ struct in_addr haddr; char errbuf[SH_ERRBUF_SIZE]; /* Check all interfaces for this host */ while (i < iface_list.used) { if ((sock = socket(AF_INET, type, protocol)) < 0 ) { #ifdef TEST_ONLY if (portchk_debug) perror(_("socket")); #else sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN, sh_error_message(errno, errbuf, sizeof(errbuf)), _("socket")); #endif } if ( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &flag, sizeof(flag)) < 0 ) { #ifdef TEST_ONLY if (portchk_debug) perror(_("setsockopt")); #else sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN, sh_error_message(errno, errbuf, sizeof(errbuf)),_("setsockopt")); #endif } memcpy (&(haddr.s_addr), &(iface_list.iface[i].s_addr), sizeof(in_addr_t)); if (protocol == IPPROTO_TCP) check_port_tcp_internal(sock, port, haddr); else check_port_udp_internal(sock, port, haddr); ++i; } return 0; } static int check_port_udp (int port) { return check_port_generic(port, SOCK_DGRAM, IPPROTO_UDP); } static int check_port_tcp (int port) { return check_port_generic(port, SOCK_STREAM, IPPROTO_TCP); } static int sh_portchk_scan_ports_generic (int min_port, int max_port, int type, int protocol) { /* int min_port = 1024; int max_port = 65535; */ int port; int retval; int sock = -1; int flag = 1; /* non-zero to enable an option */ struct sockaddr_in addr; int addrlen = sizeof(addr); char errbuf[SH_ERRBUF_SIZE]; if (min_port == -1) min_port = 0; if (max_port == -1) max_port = 65535; for (port = min_port; port <= max_port; ++port) { if ((sock = socket(AF_INET, type, protocol)) < 0 ) { #ifdef TEST_ONLY if (portchk_debug) perror(_("socket")); #else sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN, sh_error_message(errno, errbuf, sizeof(errbuf)), _("socket")); #endif } if ( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &flag, sizeof(flag)) < 0 ) { #ifdef TEST_ONLY if (portchk_debug) perror(_("setsockopt")); #else sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN, sh_error_message(errno, errbuf, sizeof(errbuf)),_("setsockopt")); #endif } addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = INADDR_ANY; retval = bind (sock, (struct sockaddr *) &addr, addrlen); if (retval == 0) { /* we can bind the port, thus it is unused */ close (sock); } else { if (errno == EINVAL || errno == EADDRINUSE) { /* try to connect to the port */ if (protocol == IPPROTO_TCP) check_port_tcp(port); else check_port_udp(port); } else { #ifdef TEST_ONLY if (portchk_debug) perror(_("bind")); #else sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN, sh_error_message(errno, errbuf, sizeof(errbuf)), _("bind")); #endif } close (sock); } } return 0; } static int sh_portchk_scan_ports_tcp (int min_port, int max_port) { return sh_portchk_scan_ports_generic (min_port, max_port, SOCK_STREAM, IPPROTO_TCP); } static int sh_portchk_scan_ports_udp (int min_port, int max_port) { return sh_portchk_scan_ports_generic (min_port, max_port, SOCK_DGRAM, IPPROTO_UDP); } /* Subroutine to add an interface */ static int sh_portchk_add_interface (const char * str) { struct in_addr haddr; char errbuf[256]; if (iface_initialized == 0) { iface_list.used = 0; iface_initialized = 1; } if (0 == inet_aton(str, &haddr)) return -1; if (iface_list.used == SH_IFACE_MAX) return -1; sl_snprintf(errbuf, sizeof(errbuf), _("interface: %s"), inet_ntoa(haddr)); sh_error_handle(SH_ERR_INFO, FIL__, __LINE__, 0, MSG_E_SUBGEN, errbuf, _("sh_portchk_add_interface")); memcpy (&(iface_list.iface[iface_list.used].s_addr), &(haddr.s_addr), sizeof(in_addr_t)); ++iface_list.used; return 0; } /* Subroutine to add a required or optional port/service */ static int sh_portchk_add_required_port_generic (char * service, char * interface, int type) { char buf[256]; char proto[4]; char * p; char * endptr; unsigned long int port; struct in_addr haddr; struct sh_portentry * portent; if (0 == inet_aton(interface, &haddr)) return -1; sl_strlcpy (buf, service, sizeof(buf)); p = strchr(buf, '/'); if (!p) return -1; if (0 == strcmp(p, _("/tcp"))) sl_strlcpy(proto, _("tcp"), sizeof(proto)); else if (0 == strcmp(p, _("/udp"))) sl_strlcpy(proto, _("udp"), sizeof(proto)); else return -1; *p = '\0'; port = strtoul(buf, &endptr, 0); if (*endptr != '\0') { portent = sh_portchk_get_from_list (proto, -1, haddr, buf); if (!portent) sh_portchk_add_to_list (proto, -1, haddr, buf, type, SH_PORT_UNKN); else { #ifdef TEST_ONLY fprintf(stderr, "** WARNING: duplicate port definition %s/%s\n", buf, proto); #else sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN, _("duplicate port definition"), _("sh_portchk_add_required_port_generic")); #endif return -1; } } else if (port <= 65535) { portent = sh_portchk_get_from_list (proto, port, haddr, NULL); if (!portent) sh_portchk_add_to_list (proto, port, haddr, NULL, type, SH_PORT_UNKN); else { #ifdef TEST_ONLY fprintf(stderr, "** WARNING: duplicate port definition %lu/%s\n", port, proto); #else sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN, _("duplicate port definition"), _("sh_portchk_add_required_port_generic")); #endif return -1; } } else return -1; return 0; } /* Internal interface to add required or optional ports as 'iface:portlist' */ static int sh_portchk_add_required_generic (const char * str, int type) { size_t len; size_t ll = 0; char * interface = NULL; char * list; char * p; #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_STRTOK_R) char * saveptr; #endif if (!str) return -1; if (strchr(str, ':')) { len = strlen(str); for (ll = 0; ll < len; ++ll) { if (str[ll] == ':' || str[ll] == ' ' || str[ll] == '\t') { interface = SH_ALLOC(ll+1); sl_strlcpy(interface, str, ll+1); interface[ll] = '\0'; while (str[ll] == ':' || str[ll] == ' ' || str[ll] == '\t') ++ll; break; } } } else { interface = SH_ALLOC(8); sl_strlcpy(interface, _("0.0.0.0"), 8); interface[7] = '\0'; while (str[ll] == ' ' || str[ll] == '\t') ++ll; } if (!interface) return -1; if (str[ll] == '\0') { SH_FREE(interface); return -1; } if (portchk_debug) fprintf(stderr, "add ports for interface: %s\n", interface); list = sh_util_strdup(&str[ll]); #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_STRTOK_R) p = strtok_r (list, " ,\t", &saveptr); #else p = strtok (list, " ,\t"); #endif if (!p) { SH_FREE(interface); SH_FREE(list); return -1; } while (p) { if (-1 == sh_portchk_add_required_port_generic (p, interface, type)) { SH_FREE(interface); SH_FREE(list); return -1; } #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_STRTOK_R) p = strtok_r (NULL, " ,\t", &saveptr); #else p = strtok (NULL, " ,\t"); #endif } SH_FREE(interface); SH_FREE(list); return 0; } /* User interface to add required ports as 'iface:portlist' */ static int sh_portchk_add_required (const char * str) { return sh_portchk_add_required_generic (str, SH_PORT_REQ); } /* User interface to add optional ports as 'iface:portlist' */ static int sh_portchk_add_optional (const char * str) { return sh_portchk_add_required_generic (str, SH_PORT_OPT); } /* User interface to add ignoreable ports as 'iface:portlist' */ static int sh_portchk_add_ignore (const char * str) { return sh_portchk_add_required_generic (str, SH_PORT_IGN); } /* Interface to run port check */ int sh_portchk_check () { int min_port = 0; if (sh_portchk_active != S_FALSE) { sh_portchk_reset_lists(); if (0 != geteuid()) { min_port = 1024; #ifdef TEST_ONLY fprintf(stderr, "** WARNING not scanning ports < 1024\n"); #else sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN, _("not scanning ports below 1024"), _("sh_portchk_check")); #endif } if (sh_portchk_check_udp == 1) sh_portchk_scan_ports_udp(min_port, -1); sh_portchk_scan_ports_tcp(min_port, -1); sh_portchk_check_list (&portlist_tcp, "tcp", SH_PORT_REPORT); if (sh_portchk_check_udp == 1) sh_portchk_check_list (&portlist_udp, "udp", SH_PORT_REPORT); } return 0; } #endif #ifdef SH_CUTEST #include "CuTest.h" void Test_portcheck_lists (CuTest *tc) { #if defined(SH_USE_PORTCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE)) struct in_addr haddr_local; struct sh_portentry * portent; CuAssertTrue(tc, 0 != inet_aton("127.0.0.1", &haddr_local)); sh_portchk_add_to_list ("tcp", 8000, haddr_local, NULL, SH_PORT_NOT, SH_PORT_UNKN); portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, NULL); CuAssertPtrNotNull(tc, portent); CuAssertTrue(tc, portent->port == 8000); CuAssertTrue(tc, 0 == strcmp("127.0.0.1", portent->interface)); CuAssertTrue(tc, portent->status == SH_PORT_UNKN); CuAssertTrue(tc, portent->flag == SH_PORT_NOT); sh_portchk_check_list (&portlist_tcp, "tcp", SH_PORT_NOREPT); CuAssertTrue(tc, NULL == portlist_tcp); sh_portchk_add_to_list ("tcp", 8000, haddr_local, NULL, SH_PORT_REQ, SH_PORT_UNKN); sh_portchk_add_to_list ("tcp", 8001, haddr_local, NULL, SH_PORT_NOT, SH_PORT_UNKN); sh_portchk_add_to_list ("tcp", 8002, haddr_local, NULL, SH_PORT_REQ, SH_PORT_UNKN); sh_portchk_add_to_list ("tcp", 8003, haddr_local, NULL, SH_PORT_NOT, SH_PORT_UNKN); sh_portchk_add_to_list ("tcp", 8004, haddr_local, NULL, SH_PORT_IGN, SH_PORT_UNKN); sh_portchk_add_to_list ("tcp", -1, haddr_local, "foo1", SH_PORT_NOT, SH_PORT_UNKN); sh_portchk_add_to_list ("tcp", -1, haddr_local, "foo2", SH_PORT_REQ, SH_PORT_UNKN); sh_portchk_add_to_list ("tcp", -1, haddr_local, "foo3", SH_PORT_NOT, SH_PORT_UNKN); sh_portchk_add_to_list ("tcp", -1, haddr_local, "foo4", SH_PORT_REQ, SH_PORT_UNKN); sh_portchk_add_to_list ("tcp", -1, haddr_local, "foo5", SH_PORT_IGN, SH_PORT_UNKN); sh_portchk_check_list (&portlist_tcp, "tcp", SH_PORT_NOREPT); CuAssertPtrNotNull(tc, portlist_tcp); portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, NULL); CuAssertPtrNotNull(tc, portent); portent = sh_portchk_get_from_list("tcp", 8001, haddr_local, NULL); CuAssertTrue(tc, NULL == portent); portent = sh_portchk_get_from_list("tcp", 8002, haddr_local, NULL); CuAssertPtrNotNull(tc, portent); portent = sh_portchk_get_from_list("tcp", 8003, haddr_local, NULL); CuAssertTrue(tc, NULL == portent); portent = sh_portchk_get_from_list("tcp", 8004, haddr_local, NULL); CuAssertPtrNotNull(tc, portent); portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, "foo1"); CuAssertTrue(tc, NULL == portent); portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, "foo2"); CuAssertPtrNotNull(tc, portent); CuAssertTrue(tc, 0 == strcmp(portent->service, "foo2")); portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, "foo3"); CuAssertTrue(tc, NULL == portent); portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, "foo4"); CuAssertPtrNotNull(tc, portent); CuAssertTrue(tc, 0 == strcmp(portent->service, "foo4")); portent = sh_portchk_get_from_list("tcp", 8000, haddr_local, "foo5"); CuAssertPtrNotNull(tc, portent); CuAssertTrue(tc, 0 == strcmp(portent->service, "foo5")); #else (void) tc; /* fix compiler warning */ #endif return; } #endif #ifdef TEST_ONLY void usage (char * pname) { printf ("%s [-r|--required interface:portlist][-o|--optional interface:portlist][--no-udp][-d|--debug] hostname\n\n", pname); printf (" Check local host for open ports; Version %s\n\n", PORTCHK_VERSION); printf (" Interface: Numeric address for an interface, e.g. 127.0.0.1\n"); printf (" Portlist: List of ports or services, e.g. 22/tcp,nfs/udp,nlockmgr/udp\n"); printf (" required -> must be open\n"); printf (" optional -> may be open or closed\n"); printf (" RPC services must be specified with service **name**, others with **port number**\n\n"); printf (" Example:\n"); printf (" %s --required 192.168.1.2:22/tcp,nfs/udp,nlockmgr/udp\n\n", pname); return; } int main(int argc, char *argv[]) { char * pname = argv[0]; /* test_lists(); portlist_tcp = sh_portchk_kill_list (portlist_tcp); portlist_udp = sh_portchk_kill_list (portlist_udp); */ // sh_portchk_add_required ("127.0.0.1 : nlockmgr/tcp, 5308/tcp, nfs/tcp"); while (argc > 1 && argv[1][0] == '-') { if (0 == strcmp(argv[1], "--help") || 0 == strcmp(argv[1], "-h")) { usage(pname); exit (0); } else if (0 == strcmp(argv[1], "--required") || 0 == strcmp(argv[1], "-r")) { if (argc < 3) { usage(pname); exit (1); } sh_portchk_add_required (argv[2]); --argc; ++argv; } else if (0 == strcmp(argv[1], "--optional") || 0 == strcmp(argv[1], "-o")) { if (argc < 3) { usage(pname); exit (1); } sh_portchk_add_optional (argv[2]); --argc; ++argv; } else if (0 == strcmp(argv[1], "--no-udp")) { sh_portchk_check_udp = 0; } else if (0 == strcmp(argv[1], "--debug") || 0 == strcmp(argv[1], "-d")) { portchk_debug = 1; } else { usage(pname); exit (1); } --argc; ++argv; } if (argc < 2) { usage(pname); exit (1); } portchk_hostname = argv[1]; if (0 != sh_portchk_init ()) { usage(pname); exit (1); } sh_portchk_check(); return 0; } #endif