1 /*
2  *  Detect environment for bytecode.
3  *
4  *  Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
5  *  Copyright (C) 2009-2013 Sourcefire, Inc.
6  *
7  *  Authors: Török Edvin
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License version 2 as
11  *  published by the Free Software Foundation.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  *  MA 02110-1301, USA.
22  */
23 
24 #if HAVE_CONFIG_H
25 #include "clamav-config.h"
26 #endif
27 
28 #include "clamav.h"
29 #include "target.h"
30 
31 #include "bytecode_detect.h"
32 #include "others.h"
33 #include <string.h>
34 #include <stdio.h>
35 #include <errno.h>
36 
37 #ifdef HAVE_UNAME_SYSCALL
38 #include <sys/utsname.h>
39 #endif
40 
41 #define CHECK_ARCH(a) \
42     if (!strcmp(TARGET_ARCH_TYPE, #a)) env->arch = arch_##a
43 
44 extern int have_clamjit;
45 
cli_print_environment(struct cli_environment * env)46 static void cli_print_environment(struct cli_environment *env)
47 {
48     uint32_t id_a = env->platform_id_a;
49     uint32_t id_b = env->platform_id_b;
50     uint32_t id_c = env->platform_id_c;
51     /* the bytecode instruction that exactly identifies this platform */
52     /* the space separated groups can be a concrete value, or 0xff for ANY */
53     cli_dbgmsg("environment detected:\n");
54     cli_dbgmsg("check_platform(0x%08x, 0x%08x, 0x%08x)\n",
55                id_a, id_b, id_c);
56     cli_dbgmsg("check_platform(0x%02x  %01x  %01x  %02x  %02x,"
57                "0x%01x  %01x       %02x %02x %02x,"
58                "0x%02x    %02x %02x %02x)\n",
59                env->os_category, env->arch, env->compiler,
60                env->functionality_level,
61                env->dconf_level,
62                env->big_endian,
63                env->sizeof_ptr,
64                (env->cpp_version >> 16) & 0xff,
65                (env->cpp_version >> 8) & 0xff,
66                env->cpp_version & 0xff,
67                env->os_features,
68                (env->c_version >> 16) & 0xff,
69                (env->c_version >> 8) & 0xff,
70                env->c_version & 0xff);
71     cli_dbgmsg("check_platform( OS CPU COM FL DCONF,BE PTR CXX VV.VV.VV, FLG CC VV.VV.VV)\n");
72     cli_dbgmsg("Engine version: %s\n", env->engine_version);
73     cli_dbgmsg("Host triple: %s\n", env->triple);
74     cli_dbgmsg("Host CPU: %s\n", env->cpu);
75     cli_dbgmsg("OS: %s\n", env->sysname);
76     cli_dbgmsg("OS release: %s\n", env->release);
77     cli_dbgmsg("OS version: %s\n", env->version);
78     cli_dbgmsg("OS hardware: %s\n", env->machine);
79     cli_dbgmsg("OS LLVM category: %d\n", env->os);
80     cli_dbgmsg("Has JIT compiled: %d\n", env->has_jit_compiled);
81     cli_dbgmsg("------------------------------------------------------\n");
82 }
83 
84 #ifdef __linux__
85 
detect_PaX(void)86 static int detect_PaX(void)
87 {
88     char line[128];
89     int pax = 0;
90     FILE *f = fopen("/proc/self/status", "r");
91     if (!f)
92         return 0;
93     while (fgets(line, sizeof(line), f)) {
94         if (!memcmp(line, "PaX:", 4)) {
95             pax = 1;
96             if (!strchr(line, 'm'))
97                 pax = 2;
98             break;
99         }
100     }
101     fclose(f);
102     return pax;
103 }
104 
detect_SELinux(void)105 static int detect_SELinux(void)
106 {
107     char line[128];
108     int selinux = 0;
109     int enforce = 0;
110     FILE *f     = fopen("/proc/filesystems", "r");
111     if (!f) {
112         f = fopen("/selinux/enforce", "r");
113         if (!f && errno == EACCES)
114             return 2;
115         if (f) {
116             if (fscanf(f, "%d", &enforce) == 1)
117                 selinux = 2;
118             fclose(f);
119         }
120         return selinux;
121     }
122     while (fgets(line, sizeof(line), f)) {
123         if (strstr(line, "selinuxfs\n")) {
124             selinux = 1;
125             break;
126         }
127     }
128     fclose(f);
129     if (!selinux)
130         return 0;
131 
132     f = fopen("/selinux/enforce", "r");
133     if (f) {
134         if (fscanf(f, "%d", &enforce) == 1) {
135             if (enforce == 1)
136                 selinux = 2;
137             if (enforce == -1)
138                 selinux = 0;
139         }
140         fclose(f);
141     }
142     return selinux;
143 }
144 
detect_os_features(uint8_t * os_features)145 static void detect_os_features(uint8_t *os_features)
146 {
147     int features = 0;
148     switch (detect_PaX()) {
149         case 2:
150             features |= 1 << feature_pax_mprotect;
151             /* fall through */
152         case 1:
153             features |= 1 << feature_pax;
154             break;
155         default:
156             break;
157     }
158     switch (detect_SELinux()) {
159         case 2:
160             features |= 1 << feature_selinux_enforcing;
161             /* fall through */
162         case 1:
163             features |= 1 << feature_selinux;
164             break;
165         default:
166             break;
167     }
168 
169     *os_features = features;
170 }
171 #else
detect_os_features(uint8_t * os_features)172 static void detect_os_features(uint8_t *os_features)
173 {
174     *os_features = 0;
175 }
176 #endif
177 
178 /* OS features :
179  * Linux: PaX << 2| SELinux << 1| mmap-RWX
180  * Other:                         mmap-RWX */
181 
cli_detect_environment(struct cli_environment * env)182 void cli_detect_environment(struct cli_environment *env)
183 {
184     memset(env, 0, sizeof(*env));
185 #if WORDS_BIGENDIAN == 0
186     env->big_endian = 0;
187 #else
188     env->big_endian  = 1;
189 #endif
190     env->sizeof_ptr = sizeof(void *);
191 
192     /* -- Detect arch -- */
193     CHECK_ARCH(i386);
194     else CHECK_ARCH(x86_64);
195     else if (!strcmp(TARGET_ARCH_TYPE, "amd64")) env->arch = arch_x86_64;
196     else if (!strcmp(TARGET_ARCH_TYPE, "ppc")) env->arch   = arch_ppc32; /* llvm will fix ppc64 */
197     else CHECK_ARCH(arm);
198     else CHECK_ARCH(sparc);
199     else CHECK_ARCH(sparc64);
200     else CHECK_ARCH(mips);
201     else CHECK_ARCH(mips64);
202     else CHECK_ARCH(alpha);
203     else CHECK_ARCH(hppa1);
204     else CHECK_ARCH(hppa2);
205     else CHECK_ARCH(m68k);
206     else env->arch = arch_unknown;
207 
208     /* -- Detect OS -- */
209 #ifdef C_AIX
210     env->os_category = os_aix;
211 #elif defined(C_BEOS)
212     env->os_category = os_beos;
213     /* DARWIN must come before BSD since it defines both */
214 #elif defined(C_DARWIN)
215     env->os_category = os_darwin;
216 #elif defined(C_BSD)
217     env->os_category = os_bsd;
218 #elif defined(C_GNU_HURD)
219     env->os_category = os_gnu_hurd;
220 #elif defined(C_HPUX)
221     env->os_category = os_hpux;
222 #elif defined(C_INTERIX)
223     env->os_category = os_interix;
224 #elif defined(C_IRIX)
225     env->os_category = os_irix;
226 #elif defined(C_KFREEBSD_GNU)
227     env->os_category = os_kfreebsd_gnu;
228 #elif defined(C_LINUX)
229     env->os_category = os_linux;
230 #elif defined(C_OS2)
231     env->os_category = os_os2;
232 #elif defined(C_OSF)
233     env->os_category = os_osf;
234 #elif defined(C_QNX6)
235     env->os_category = os_qnx6;
236 #elif defined(C_SOLARIS)
237     env->os_category = os_solaris;
238 #elif defined(_WIN64)
239     env->os_category = os_win64;
240 #elif defined(_WIN32)
241     env->os_category = os_win32;
242 #else
243     env->os_category = os_generic;
244 #endif
245 
246     env->os = llvm_os_UnknownOS;
247     /* -- Detect compiler -- */
248 
249     /* check GNUC last, because some other compilers might define it */
250 #ifdef __INTEL_COMPILER
251     env->compiler  = compiler_intel;
252     env->c_version = __INTEL_COMPILER;
253 #elif defined(_MSC_VER)
254     env->compiler  = compiler_msc;
255     env->c_version = _MSC_VER;
256 #elif defined(__SUNPRO_C)
257     env->compiler    = compiler_sun;
258     env->c_version   = __SUNPRO_C;
259 #elif defined(__GNUC__)
260 
261 #ifdef __clang__
262     env->compiler    = compiler_clang;
263 #elif defined(__llvm__)
264     env->compiler = compiler_llvm;
265 #else
266     env->compiler = compiler_gnuc;
267 #endif
268     env->c_version =
269         MAKE_VERSION(0, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
270 
271 #else
272     env->compiler    = compiler_other;
273     env->c_version   = 0;
274 #endif
275     env->cpp_version = 0;
276 
277     env->has_jit_compiled = have_clamjit;
278 
279     /* engine */
280     env->functionality_level = cl_retflevel();
281     env->dconf_level         = CL_FLEVEL_DCONF;
282 
283     INIT_STRFIELD(env->engine_version, cl_retver());
284 #ifdef HAVE_UNAME_SYSCALL
285     {
286         struct utsname name;
287         if (uname(&name) == 0) {
288             INIT_STRFIELD(env->sysname, name.sysname);
289             INIT_STRFIELD(env->release, name.release);
290             INIT_STRFIELD(env->version, name.version);
291             INIT_STRFIELD(env->machine, name.machine);
292         }
293     }
294 #endif
295 #ifdef _WIN32
296     {
297         OSVERSIONINFOEX info;
298         info.dwOSVersionInfoSize = sizeof(info);
299         if (GetVersionEx((OSVERSIONINFO *)&info) != 0 && info.dwPlatformId == VER_PLATFORM_WIN32_NT) {
300             if (info.wProductType == VER_NT_WORKSTATION)
301                 INIT_STRFIELD(env->sysname, "Microsoft Windows");
302             else
303                 INIT_STRFIELD(env->sysname, "Microsoft Windows Server");
304             snprintf((char *)env->release, sizeof(env->release), "%d.%d SP%d.%d",
305                      info.dwMajorVersion, info.dwMinorVersion,
306                      info.wServicePackMajor, info.wServicePackMinor);
307             snprintf((char *)env->version, sizeof(env->version), "Build %d",
308                      info.dwBuildNumber);
309         }
310     }
311 
312 #endif
313     if (!env->sysname[0]) {
314         INIT_STRFIELD(env->sysname, TARGET_OS_TYPE);
315     }
316 
317     detect_os_features(&env->os_features);
318 
319     cli_detect_env_jit(env);
320 
321     env->platform_id_a = (env->os_category << 24) | (env->arch << 20) |
322                          (env->compiler << 16) | (env->functionality_level << 8) |
323                          (env->dconf_level);
324     env->platform_id_b = (env->big_endian << 28) | (env->sizeof_ptr << 24) |
325                          env->cpp_version;
326     env->platform_id_c = (env->os_features << 24) | env->c_version;
327     cli_print_environment(env);
328 }
329