new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / syscheckd / win-registry.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All rights 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 #ifdef WIN32
11
12 #include "shared.h"
13 #include "syscheck.h"
14 #include "os_crypto/md5/md5_op.h"
15 #include "os_crypto/sha1/sha1_op.h"
16 #include "os_crypto/md5_sha1/md5_sha1_op.h"
17
18 /* Default values */
19 #define MAX_KEY_LENGTH   255
20 #define MAX_KEY         2048
21 #define MAX_VALUE_NAME 16383
22
23 /* Places to story the registry values */
24 #define SYS_WIN_REG     "syscheck/syscheckregistry.db"
25 #define SYS_REG_TMP     "syscheck/syscheck_sum.tmp"
26
27 /* Global variables */
28 HKEY sub_tree;
29 int ig_count = 0;
30 int run_count = 0;
31
32 /* Prototypes */
33 void os_winreg_open_key(char *subkey, char *fullkey_name);
34
35
36 int os_winreg_changed(char *key, char *md5, char *sha1)
37 {
38     char buf[MAX_LINE + 1];
39
40     buf[MAX_LINE] = '\0';
41
42     /* Seek to the beginning of the db */
43     fseek(syscheck.reg_fp, 0, SEEK_SET);
44
45     while (fgets(buf, MAX_LINE, syscheck.reg_fp) != NULL) {
46         if ((buf[0] != '#') && (buf[0] != ' ') && (buf[0] != '\n')) {
47             char *n_buf;
48
49             /* Remove the \n before reading */
50             n_buf = strchr(buf, '\n');
51             if (n_buf == NULL) {
52                 continue;
53             }
54
55             *n_buf = '\0';
56
57             n_buf = strchr(buf, ' ');
58             if (n_buf == NULL) {
59                 continue;
60             }
61
62             if (strcmp(n_buf + 1, key) != 0) {
63                 continue;
64             }
65
66             /* Entry found, check if checksum is the same */
67             *n_buf = '\0';
68             if ((strncmp(buf, md5, sizeof(os_md5) - 1) == 0) &&
69                     (strcmp(buf + sizeof(os_md5) - 1, sha1) == 0)) {
70                 /* File didn't change */
71                 return (0);
72             }
73
74             /* File did change */
75             return (1);
76         }
77     }
78
79     fseek(syscheck.reg_fp, 0, SEEK_END);
80     fprintf(syscheck.reg_fp, "%s%s %s\n", md5, sha1, key);
81     return (1);
82 }
83
84 /* Notify of registry changes */
85 int notify_registry(char *msg, __attribute__((unused)) int send_now)
86 {
87     if (SendMSG(syscheck.queue, msg,
88                 SYSCHECK_REG, SYSCHECK_MQ) < 0) {
89         merror(QUEUE_SEND, ARGV0);
90
91         if ((syscheck.queue = StartMQ(DEFAULTQPATH, WRITE)) < 0) {
92             ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
93         }
94
95         /* If we reach here, we can try to send it again */
96         SendMSG(syscheck.queue, msg, SYSCHECK_REG, SYSCHECK_MQ);
97     }
98
99     return (0);
100 }
101
102 /* Check if the registry entry is valid */
103 char *os_winreg_sethkey(char *reg_entry)
104 {
105     char *ret = NULL;
106     char *tmp_str;
107
108     /* Get only the sub tree first */
109     tmp_str = strchr(reg_entry, '\\');
110     if (tmp_str) {
111         *tmp_str = '\0';
112         ret = tmp_str + 1;
113     }
114
115     /* Set sub tree */
116     if (strcmp(reg_entry, "HKEY_LOCAL_MACHINE") == 0) {
117         sub_tree = HKEY_LOCAL_MACHINE;
118     } else if (strcmp(reg_entry, "HKEY_CLASSES_ROOT") == 0) {
119         sub_tree = HKEY_CLASSES_ROOT;
120     } else if (strcmp(reg_entry, "HKEY_CURRENT_CONFIG") == 0) {
121         sub_tree = HKEY_CURRENT_CONFIG;
122     } else if (strcmp(reg_entry, "HKEY_USERS") == 0) {
123         sub_tree = HKEY_USERS;
124     } else {
125         /* Return tmp_str to the previous value */
126         if (tmp_str && (*tmp_str == '\0')) {
127             *tmp_str = '\\';
128         }
129         return (NULL);
130     }
131
132     /* Check if ret has nothing else */
133     if (ret && (*ret == '\0')) {
134         ret = NULL;
135     }
136
137     /* Fix tmp_str and the real name of the registry */
138     if (tmp_str && (*tmp_str == '\0')) {
139         *tmp_str = '\\';
140     }
141
142     return (ret);
143 }
144
145 /* Query the key and get all its values */
146 void os_winreg_querykey(HKEY hKey, char *p_key, char *full_key_name)
147 {
148     int rc;
149     DWORD i, j;
150
151     /* QueryInfo and EnumKey variables */
152     TCHAR sub_key_name_b[MAX_KEY_LENGTH + 2];
153     TCHAR class_name_b[MAX_PATH + 1];
154     DWORD sub_key_name_s;
155     DWORD class_name_s = MAX_PATH;
156
157     /* Number of sub keys */
158     DWORD subkey_count = 0;
159
160     /* Number of values */
161     DWORD value_count;
162
163     /* Variables for RegEnumValue */
164     TCHAR value_buffer[MAX_VALUE_NAME + 1];
165     TCHAR data_buffer[MAX_VALUE_NAME + 1];
166     DWORD value_size;
167     DWORD data_size;
168
169     /* Data type for RegEnumValue */
170     DWORD data_type = 0;
171
172     /* Initializing the memory for some variables */
173     class_name_b[0] = '\0';
174     class_name_b[MAX_PATH] = '\0';
175     sub_key_name_b[0] = '\0';
176     sub_key_name_b[MAX_KEY_LENGTH] = '\0';
177     sub_key_name_b[MAX_KEY_LENGTH + 1] = '\0';
178
179     /* We use the class_name, subkey_count and the value count */
180     rc = RegQueryInfoKey(hKey, class_name_b, &class_name_s, NULL,
181                          &subkey_count, NULL, NULL, &value_count,
182                          NULL, NULL, NULL, NULL);
183
184     /* Check return code of QueryInfo */
185     if (rc != ERROR_SUCCESS) {
186         return;
187     }
188
189     /* Check if we have sub keys */
190     if (subkey_count) {
191         /* Open each subkey and call open_key */
192         for (i = 0; i < subkey_count; i++) {
193             sub_key_name_s = MAX_KEY_LENGTH;
194             rc = RegEnumKeyEx(hKey, i, sub_key_name_b, &sub_key_name_s,
195                               NULL, NULL, NULL, NULL);
196
197             /* Checking for the rc */
198             if (rc == ERROR_SUCCESS) {
199                 char new_key[MAX_KEY + 2];
200                 char new_key_full[MAX_KEY + 2];
201                 new_key[MAX_KEY + 1] = '\0';
202                 new_key_full[MAX_KEY + 1] = '\0';
203
204                 if (p_key) {
205                     snprintf(new_key, MAX_KEY,
206                              "%s\\%s", p_key, sub_key_name_b);
207                     snprintf(new_key_full, MAX_KEY,
208                              "%s\\%s", full_key_name, sub_key_name_b);
209                 } else {
210                     snprintf(new_key, MAX_KEY, "%s", sub_key_name_b);
211                     snprintf(new_key_full, MAX_KEY,
212                              "%s\\%s", full_key_name, sub_key_name_b);
213                 }
214
215                 /* Open subkey */
216                 os_winreg_open_key(new_key, new_key_full);
217             }
218         }
219     }
220
221     /* Get values (if available) */
222     if (value_count) {
223         /* md5 and sha1 sum */
224         os_md5 mf_sum;
225         os_sha1 sf_sum;
226         FILE *checksum_fp;
227         char *mt_data;
228
229         /* Clear the values for value_size and data_size */
230         value_buffer[MAX_VALUE_NAME] = '\0';
231         data_buffer[MAX_VALUE_NAME] = '\0';
232         checksum_fp = fopen(SYS_REG_TMP, "w");
233         if (!checksum_fp) {
234             printf(FOPEN_ERROR, ARGV0, SYS_REG_TMP, errno, strerror(errno));
235             return;
236         }
237
238         /* Get each value */
239         for (i = 0; i < value_count; i++) {
240             value_size = MAX_VALUE_NAME;
241             data_size = MAX_VALUE_NAME;
242
243             value_buffer[0] = '\0';
244             data_buffer[0] = '\0';
245
246             rc = RegEnumValue(hKey, i, value_buffer, &value_size,
247                               NULL, &data_type, (LPBYTE)data_buffer, &data_size);
248
249             /* No more values available */
250             if (rc != ERROR_SUCCESS) {
251                 break;
252             }
253
254             /* Check if no value name is specified */
255             if (value_buffer[0] == '\0') {
256                 value_buffer[0] = '@';
257                 value_buffer[1] = '\0';
258             }
259
260             /* Write value name and data in the file (for checksum later) */
261             fprintf(checksum_fp, "%s=", value_buffer);
262             switch (data_type) {
263                 case REG_SZ:
264                 case REG_EXPAND_SZ:
265                     fprintf(checksum_fp, "%s\n", data_buffer);
266                     break;
267                 case REG_MULTI_SZ:
268                     /* Print multiple strings */
269                     mt_data = data_buffer;
270
271                     while (*mt_data) {
272                         fprintf(checksum_fp, "%s ", mt_data);
273                         mt_data += strlen(mt_data) + 1;
274                     }
275                     fprintf(checksum_fp, "\n");
276                     break;
277                 case REG_DWORD:
278                     fprintf(checksum_fp, "%08x\n", (unsigned int)*data_buffer);
279                     break;
280                 default:
281                     for (j = 0; j < data_size; j++) {
282                         fprintf(checksum_fp, "%02x",
283                                 (unsigned int)data_buffer[j]);
284                     }
285                     fprintf(checksum_fp, "\n");
286                     break;
287             }
288         }
289
290         /* Generate checksum of the values */
291         fclose(checksum_fp);
292
293         if (OS_MD5_SHA1_File(SYS_REG_TMP, syscheck.prefilter_cmd, mf_sum, sf_sum, OS_TEXT) == -1) {
294             merror(FOPEN_ERROR, ARGV0, SYS_REG_TMP, errno, strerror(errno));
295             return;
296         }
297
298         /* Look for p_key on the reg db */
299         if (os_winreg_changed(full_key_name, mf_sum, sf_sum)) {
300             char reg_changed[MAX_LINE + 1];
301             snprintf(reg_changed, MAX_LINE, "0:0:0:0:%s:%s %s",
302                      mf_sum, sf_sum, full_key_name);
303
304             /* Notify server */
305             notify_registry(reg_changed, 0);
306         }
307
308         ig_count++;
309     }
310 }
311
312 /* Open the registry key */
313 void os_winreg_open_key(char *subkey, char *full_key_name)
314 {
315     int i = 0;
316     HKEY oshkey;
317
318     /* Sleep X every Y files */
319     if (ig_count >= syscheck.sleep_after) {
320         sleep(syscheck.tsleep + 1);
321         ig_count = 1;
322     }
323     ig_count++;
324
325     /* Registry ignore list */
326     if (full_key_name && syscheck.registry_ignore) {
327         while (syscheck.registry_ignore[i] != NULL) {
328             if (strcasecmp(syscheck.registry_ignore[i], full_key_name) == 0) {
329                 return;
330             }
331             i++;
332         }
333     } else if (full_key_name && syscheck.registry_ignore_regex) {
334         i = 0;
335         while (syscheck.registry_ignore_regex[i] != NULL) {
336             if (OSMatch_Execute(full_key_name, strlen(full_key_name),
337                                 syscheck.registry_ignore_regex[i])) {
338                 return;
339             }
340             i++;
341         }
342     }
343
344     if (RegOpenKeyEx(sub_tree, subkey, 0, KEY_READ, &oshkey) != ERROR_SUCCESS) {
345         merror(SK_REG_OPEN, ARGV0, subkey);
346         return;
347     }
348
349     os_winreg_querykey(oshkey, subkey, full_key_name);
350     RegCloseKey(oshkey);
351     return;
352 }
353
354 /* Main function to read the registry */
355 void os_winreg_check()
356 {
357     int i = 0;
358     char *rk;
359
360     /* Debug entries */
361     debug1("%s: DEBUG: Starting os_winreg_check", ARGV0);
362
363     /* Zero ig_count before checking */
364     ig_count = 1;
365
366     /* Check if the registry fp is open */
367     if (syscheck.reg_fp == NULL) {
368         syscheck.reg_fp = fopen(SYS_WIN_REG, "w+");
369         if (!syscheck.reg_fp) {
370             merror(FOPEN_ERROR, ARGV0, SYS_WIN_REG, errno, strerror(errno));
371             return;
372         }
373     }
374
375     /* Get sub class and a valid registry entry */
376     while (syscheck.registry[i] != NULL) {
377         sub_tree = NULL;
378         rk = NULL;
379
380         /* Ignored entries are zeroed */
381         if (*syscheck.registry[i] == '\0') {
382             i++;
383             continue;
384         }
385
386         /* Read syscheck registry entry */
387         debug1("%s: DEBUG: Attempt to read: %s", ARGV0, syscheck.registry[i]);
388
389         rk = os_winreg_sethkey(syscheck.registry[i]);
390         if (sub_tree == NULL) {
391             merror(SK_INV_REG, ARGV0, syscheck.registry[i]);
392             *syscheck.registry[i] = '\0';
393             i++;
394             continue;
395         }
396
397         os_winreg_open_key(rk, syscheck.registry[i]);
398         i++;
399         sleep(syscheck.tsleep * 5);
400     }
401
402     /* Notify of db completed */
403     if (run_count > 1) {
404         sleep(syscheck.tsleep * 5);
405         notify_registry(HC_SK_DB_COMPLETED, 1);
406     }
407
408     run_count++;
409     return;
410 }
411 #endif /* WIN32 */
412