new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / rootcheck / check_rc_ports.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All right reserved.
3  *
4  * This program is a free software; you can redistribute it
5  * and/or modify it under the terms of the GNU General Public
6  * License (version 2) as published by the FSF - Free Software
7  * Foundation
8  */
9
10 #ifndef WIN32
11
12 #include "shared.h"
13 #include "rootcheck.h"
14
15 #if defined(sun) || defined(__sun__)
16 #define NETSTAT         "netstat -an -P %s | "\
17                         "grep \"[^0-9]%d \" > /dev/null 2>&1"
18 #else
19 #define NETSTAT         "netstat -an | grep \"^%s\" | " \
20                         "grep \"[^0-9]%d \" > /dev/null 2>&1"
21 #endif
22
23 /* Prototypes */
24 static int  run_netstat(int proto, int port);
25 static int  conn_port(int proto, int port);
26 static void test_ports(int proto, int *_errors, int *_total);
27
28
29 static int run_netstat(int proto, int port)
30 {
31     int ret;
32     char nt[OS_SIZE_1024 + 1];
33
34     if (proto == IPPROTO_TCP) {
35         snprintf(nt, OS_SIZE_1024, NETSTAT, "tcp", port);
36     } else if (proto == IPPROTO_UDP) {
37         snprintf(nt, OS_SIZE_1024, NETSTAT, "udp", port);
38     } else {
39         merror("%s: Netstat error (wrong protocol)", ARGV0);
40         return (0);
41     }
42
43     ret = system(nt);
44
45     if (ret == 0) {
46         return (1);
47     } else if (ret == 1) {
48         return (0);
49     }
50
51     return (1);
52 }
53
54 static int conn_port(int proto, int port)
55 {
56     int rc = 0;
57     int ossock;
58     struct sockaddr_in server;
59     struct sockaddr_in6 server6;
60
61     if (proto == IPPROTO_UDP) {
62         if ((ossock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
63             return (0);
64         }
65     } else if (proto == IPPROTO_TCP) {
66         if ((ossock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
67             return (0);
68         }
69     } else {
70         return (0);
71     }
72
73     memset(&server, 0, sizeof(server));
74     server.sin_family = AF_INET;
75     server.sin_port = htons(port);
76     server.sin_addr.s_addr = htonl(INADDR_ANY);
77
78     /* If we can't bind, it means the port is open */
79     if (bind(ossock, (struct sockaddr *) &server, sizeof(server)) < 0) {
80         rc = 1;
81     }
82
83     /* Setting if port is open or closed */
84     if (proto == IPPROTO_TCP) {
85         total_ports_tcp[port] = (char) rc;
86     } else {
87         total_ports_udp[port] = (char) rc;
88     }
89
90     close(ossock);
91
92     /* repeat for IPv6 */
93     if (proto == IPPROTO_UDP) {
94         if ((ossock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
95             return(0);
96         }
97     } else if (proto == IPPROTO_TCP) {
98         if ((ossock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)) < 0) {
99             return(0);
100         }
101     }
102
103
104     memset(&server6, 0, sizeof(server6));
105     server6.sin6_family = AF_INET6;
106     server6.sin6_port = htons( port );
107     memcpy(&server6.sin6_addr.s6_addr, &in6addr_any, sizeof in6addr_any);
108
109
110     /* If we can't bind, it means the port is open */
111     if(bind(ossock, (struct sockaddr *) &server6, sizeof(server6)) < 0) {
112         rc = 1;
113     }
114
115     /* Setting if port is open or closed */
116     if(proto == IPPROTO_TCP) {
117         total_ports_tcp[port] = rc;
118     } else {
119         total_ports_udp[port] = rc;
120     }
121
122     close(ossock);
123
124     return (rc);
125 }
126
127 static void test_ports(int proto, int *_errors, int *_total)
128 {
129     int i;
130
131     for (i = 0; i <= 65535; i++) {
132         (*_total)++;
133         if (conn_port(proto, i)) {
134             /* Check if we can find it using netstat. If not,
135              * check again to see if the port is still being used.
136              */
137             if (run_netstat(proto, i)) {
138                 continue;
139             }
140
141 #ifdef OSSECHIDS
142             /* If we are in the context of OSSEC-HIDS, sleep here (no rush) */
143             debug1("%s: DEBUG: pause for %u", ARGV0, rootcheck.tsleep);
144             sleep(rootcheck.tsleep);
145 #endif
146
147             if (!run_netstat(proto, i) && conn_port(proto, i)) {
148                 char op_msg[OS_SIZE_1024 + 1];
149
150                 (*_errors)++;
151
152                 snprintf(op_msg, OS_SIZE_1024, "Port '%d'(%s) hidden. "
153                          "Kernel-level rootkit or trojaned "
154                          "version of netstat.", i,
155                          (proto == IPPROTO_UDP) ? "udp" : "tcp");
156
157                 notify_rk(ALERT_ROOTKIT_FOUND, op_msg);
158             }
159         }
160
161         if ((*_errors) > 20) {
162             char op_msg[OS_SIZE_1024 + 1];
163
164             snprintf(op_msg, OS_SIZE_1024, "Excessive number of '%s' ports "
165                      "hidden. It maybe a false-positive or "
166                      "something really bad is going on.",
167                      (proto == IPPROTO_UDP) ? "udp" : "tcp" );
168             notify_rk(ALERT_SYSTEM_CRIT, op_msg);
169             return;
170         }
171     }
172
173 }
174
175 void check_rc_ports()
176 {
177     int _errors = 0;
178     int _total = 0;
179
180     int i = 0;
181
182     while (i <= 65535) {
183         total_ports_tcp[i] = 0;
184         total_ports_udp[i] = 0;
185         i++;
186     }
187
188     /* Test both TCP and UDP ports */
189     test_ports(IPPROTO_TCP, &_errors, &_total);
190     test_ports(IPPROTO_UDP, &_errors, &_total);
191
192     if (_errors == 0) {
193         char op_msg[OS_SIZE_1024 + 1];
194
195         snprintf(op_msg, OS_SIZE_1024, "No kernel-level rootkit hiding any port."
196                  "\n      Netstat is acting correctly."
197                  " Analyzed %d ports.", _total);
198         notify_rk(ALERT_OK, op_msg);
199     }
200
201     return;
202 }
203
204 #else /* WIN32 */
205
206 /* Not implemented on Windows */
207 void check_rc_ports()
208 {
209     return;
210 }
211
212 #endif /* WIN32 */
213