25791d7bf13045b83e5137c97635732a601b1829
[ossec-hids.git] / src / syscheckd / win-registry.c
1 /* @(#) $Id: ./src/syscheckd/win-registry.c, 2011/09/08 dcid Exp $
2  */
3
4 /* Copyright (C) 2009 Trend Micro Inc.
5  * All rights reserved.
6  *
7  * This program is a free software; you can redistribute it
8  * and/or modify it under the terms of the GNU General Public
9  * License (version 2) as published by the FSF - Free Software
10  * Foundation.
11  *
12  * License details at the LICENSE file included with OSSEC or
13  * online at: http://www.ossec.net/en/licensing.html
14  */
15
16
17 /* Windows only */
18 #ifdef WIN32
19
20
21 #include "shared.h"
22 #include "syscheck.h"
23 #include "os_crypto/md5/md5_op.h"
24 #include "os_crypto/sha1/sha1_op.h"
25 #include "os_crypto/md5_sha1/md5_sha1_op.h"
26
27
28 /* Default values */
29 #define MAX_KEY_LENGTH 255
30 #define MAX_KEY 2048
31 #define MAX_VALUE_NAME 16383
32
33 /* Places to story the registry values. */
34 #define SYS_WIN_REG     "syscheck/syscheckregistry.db"
35 #define SYS_REG_TMP     "syscheck/syscheck_sum.tmp"
36
37
38
39 /* Global variables */
40 HKEY sub_tree;
41 int ig_count = 0;
42 int run_count = 0;
43
44
45
46
47 /** Function prototypes 8*/
48 void os_winreg_open_key(char *subkey, char *fullkey_name);
49
50
51 int os_winreg_changed(char *key, char *md5, char *sha1)
52 {
53     char buf[MAX_LINE +1];
54
55     buf[MAX_LINE] = '\0';
56
57
58     /* Seeking to the beginning of the db */
59     fseek(syscheck.reg_fp, 0, SEEK_SET);
60
61     while(fgets(buf, MAX_LINE, syscheck.reg_fp) != NULL)
62     {
63         if((buf[0] != '#') && (buf[0] != ' ') && (buf[0] != '\n'))
64         {
65             char *n_buf;
66
67             /* Removing the \n before reading */
68             n_buf = strchr(buf, '\n');
69             if(n_buf == NULL)
70                 continue;
71
72             *n_buf = '\0';
73
74             n_buf = strchr(buf, ' ');
75             if(n_buf == NULL)
76                 continue;
77
78             if(strcmp(n_buf +1, key) != 0)
79                 continue;
80
81             /* Entry found, checking if checksum is the same */
82             *n_buf = '\0';
83             if((strncmp(buf, md5, sizeof(os_md5) -1) == 0)&&
84                (strcmp(buf + sizeof(os_md5) -1, sha1) == 0))
85             {
86                 /* File didn't change. */
87                 return(0);
88             }
89
90             /* File did changed */
91             return(1);
92         }
93     }
94
95     fseek(syscheck.reg_fp, 0, SEEK_END);
96     fprintf(syscheck.reg_fp, "%s%s %s\n", md5, sha1, key);
97     return(1);
98 }
99
100
101 /** int notify_registry(char *msg)
102  * Notifies of registry changes.
103  */
104 int notify_registry(char *msg, int send_now)
105 {
106     if(SendMSG(syscheck.queue, msg,
107                SYSCHECK_REG, SYSCHECK_MQ) < 0)
108     {
109         merror(QUEUE_SEND, ARGV0);
110
111         if((syscheck.queue = StartMQ(DEFAULTQPATH,WRITE)) < 0)
112         {
113             ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
114         }
115
116         /* If we reach here, we can try to send it again */
117         SendMSG(syscheck.queue, msg, SYSCHECK_REG, SYSCHECK_MQ);
118     }
119
120     return(0);
121 }
122
123
124 /** char *os_winreg_sethkey(char *reg_entry)
125  * Checks if the registry entry is valid.
126  */
127 char *os_winreg_sethkey(char *reg_entry)
128 {
129     char *ret = NULL;
130     char *tmp_str;
131
132     /* Getting only the sub tree first */
133     tmp_str = strchr(reg_entry, '\\');
134     if(tmp_str)
135     {
136         *tmp_str = '\0';
137         ret = tmp_str+1;
138     }
139
140     /* Setting sub tree */
141     if(strcmp(reg_entry, "HKEY_LOCAL_MACHINE") == 0)
142     {
143         sub_tree = HKEY_LOCAL_MACHINE;
144     }
145     else if(strcmp(reg_entry, "HKEY_CLASSES_ROOT") == 0)
146     {
147         sub_tree = HKEY_CLASSES_ROOT;
148     }
149     else if(strcmp(reg_entry, "HKEY_CURRENT_CONFIG") == 0)
150     {
151         sub_tree = HKEY_CURRENT_CONFIG;
152     }
153     else if(strcmp(reg_entry, "HKEY_USERS") == 0)
154     {
155         sub_tree = HKEY_USERS;
156     }
157     else
158     {
159         /* Returning tmp_str to the previous value */
160         if(tmp_str && (*tmp_str == '\0'))
161             *tmp_str = '\\';
162         return(NULL);
163     }
164
165     /* Checking if ret has nothing else. */
166     if(ret && (*ret == '\0'))
167         ret = NULL;
168
169     /* fixing tmp_str and the real name of the registry */
170     if(tmp_str && (*tmp_str == '\0'))
171             *tmp_str = '\\';
172
173     return(ret);
174 }
175
176
177 /* void os_winreg_querykey(HKEY hKey, char *p_key)
178  * Query the key and get all its values.
179  */
180 void os_winreg_querykey(HKEY hKey, char *p_key, char *full_key_name)
181 {
182     int i, rc;
183     DWORD j;
184
185     /* QueryInfo and EnumKey variables */
186     TCHAR sub_key_name_b[MAX_KEY_LENGTH +2];
187     TCHAR class_name_b[MAX_PATH +1];
188     DWORD sub_key_name_s;
189     DWORD class_name_s = MAX_PATH;
190
191     /* Number of sub keys */
192     DWORD subkey_count = 0;
193
194     /* Number of values */
195     DWORD value_count;
196
197     /* Variables for RegEnumValue */
198     TCHAR value_buffer[MAX_VALUE_NAME +1];
199     TCHAR data_buffer[MAX_VALUE_NAME +1];
200     DWORD value_size;
201     DWORD data_size;
202
203     /* Data type for RegEnumValue */
204     DWORD data_type = 0;
205
206
207     /* Initializing the memory for some variables */
208     class_name_b[0] = '\0';
209     class_name_b[MAX_PATH] = '\0';
210     sub_key_name_b[0] = '\0';
211     sub_key_name_b[MAX_KEY_LENGTH] = '\0';
212     sub_key_name_b[MAX_KEY_LENGTH +1] = '\0';
213
214
215     /* We use the class_name, subkey_count and the value count. */
216     rc = RegQueryInfoKey(hKey, class_name_b, &class_name_s, NULL,
217             &subkey_count, NULL, NULL, &value_count,
218             NULL, NULL, NULL, NULL);
219
220     /* Check return code of QueryInfo */
221     if(rc != ERROR_SUCCESS)
222     {
223         return;
224     }
225
226
227
228     /* Checking if we have sub keys */
229     if(subkey_count)
230     {
231         /* We open each subkey and call open_key */
232         for(i=0;i<subkey_count;i++)
233         {
234             sub_key_name_s = MAX_KEY_LENGTH;
235             rc = RegEnumKeyEx(hKey, i, sub_key_name_b, &sub_key_name_s,
236                               NULL, NULL, NULL, NULL);
237
238             /* Checking for the rc. */
239             if(rc == ERROR_SUCCESS)
240             {
241                 char new_key[MAX_KEY + 2];
242                 char new_key_full[MAX_KEY + 2];
243                 new_key[MAX_KEY +1] = '\0';
244                 new_key_full[MAX_KEY +1] = '\0';
245
246                 if(p_key)
247                 {
248                     snprintf(new_key, MAX_KEY,
249                              "%s\\%s", p_key, sub_key_name_b);
250                     snprintf(new_key_full, MAX_KEY,
251                              "%s\\%s", full_key_name, sub_key_name_b);
252                 }
253                 else
254                 {
255                     snprintf(new_key, MAX_KEY, "%s", sub_key_name_b);
256                     snprintf(new_key_full, MAX_KEY,
257                              "%s\\%s", full_key_name, sub_key_name_b);
258                 }
259
260                 /* Opening subkey */
261                 os_winreg_open_key(new_key, new_key_full);
262             }
263         }
264     }
265
266     /* Getting Values (if available) */
267     if (value_count)
268     {
269         /* md5 and sha1 sum */
270         os_md5 mf_sum;
271         os_sha1 sf_sum;
272
273         FILE *checksum_fp;
274
275         char *mt_data;
276
277
278         /* Clearing the values for value_size and data_size */
279         value_buffer[MAX_VALUE_NAME] = '\0';
280         data_buffer[MAX_VALUE_NAME] = '\0';
281         checksum_fp = fopen(SYS_REG_TMP, "w");
282         if(!checksum_fp)
283         {
284             printf(FOPEN_ERROR, ARGV0, SYS_REG_TMP);
285             return;
286         }
287
288         /* Getting each value */
289         for(i=0;i<value_count;i++)
290         {
291             value_size = MAX_VALUE_NAME;
292             data_size = MAX_VALUE_NAME;
293
294             value_buffer[0] = '\0';
295             data_buffer[0] = '\0';
296
297             rc = RegEnumValue(hKey, i, value_buffer, &value_size,
298                     NULL, &data_type, (LPBYTE)data_buffer, &data_size);
299
300             /* No more values available */
301             if(rc != ERROR_SUCCESS)
302             {
303                 break;
304             }
305
306             /* Checking if no value name is specified */
307             if(value_buffer[0] == '\0')
308             {
309                 value_buffer[0] = '@';
310                 value_buffer[1] = '\0';
311             }
312
313             /* Writing valud name and data in the file (for checksum later) */
314             fprintf(checksum_fp, "%s=", value_buffer);
315             switch(data_type)
316             {
317                 case REG_SZ:
318                 case REG_EXPAND_SZ:
319                     fprintf(checksum_fp, "%s\n", data_buffer);
320                     break;
321                 case REG_MULTI_SZ:
322                     /* Printing multiple strings */
323                     mt_data = data_buffer;
324
325                     while(*mt_data)
326                     {
327                         fprintf(checksum_fp, "%s ", mt_data);
328                         mt_data += strlen(mt_data) +1;
329                     }
330                     fprintf(checksum_fp, "\n");
331                     break;
332                 case REG_DWORD:
333                     fprintf(checksum_fp, "%08x\n",(unsigned int)*data_buffer);
334                     break;
335                 default:
336                     for(j = 0;j<data_size;j++)
337                     {
338                         fprintf(checksum_fp, "%02x",
339                                 (unsigned int)data_buffer[j]);
340                     }
341                     fprintf(checksum_fp, "\n");
342                     break;      
343             }
344         }
345
346         /* Generating checksum of the values */
347         fclose(checksum_fp);
348
349         if(OS_MD5_SHA1_File(SYS_REG_TMP, syscheck.prefilter_cmd, mf_sum, sf_sum) == -1)
350         {
351             merror(FOPEN_ERROR, ARGV0, SYS_REG_TMP);
352             return;
353         }
354
355
356         /* Looking for p_key on the reg db */
357         if(os_winreg_changed(full_key_name, mf_sum, sf_sum))
358         {
359             char reg_changed[MAX_LINE +1];
360             snprintf(reg_changed, MAX_LINE, "0:0:0:0:%s:%s %s",
361                                   mf_sum, sf_sum, full_key_name);
362
363             /* Notifying server */
364             notify_registry(reg_changed, 0);
365         }
366
367         ig_count++;
368     }
369 }
370
371
372 /* int os_winreg_open_key(char *subkey)
373  * Open the registry key
374  */
375 void os_winreg_open_key(char *subkey, char *full_key_name)
376 {
377     int i = 0;  
378     HKEY oshkey;
379
380     /* sleep X every Y files */
381     if(ig_count >= syscheck.sleep_after)
382     {
383         sleep(syscheck.tsleep +1);
384         ig_count = 1;
385     }
386     ig_count++;
387
388
389     /* Registry ignore list */
390     if(full_key_name && syscheck.registry_ignore)
391     {
392         while(syscheck.registry_ignore[i] != NULL)
393         {
394             if(strcasecmp(syscheck.registry_ignore[i], full_key_name) == 0)
395             {
396                 return;
397             }
398             i++;
399         }
400     }
401     else if(full_key_name && syscheck.registry_ignore_regex)
402     {
403         i = 0;
404         while(syscheck.registry_ignore_regex[i] != NULL)
405         {
406             if(OSMatch_Execute(full_key_name, strlen(full_key_name),
407                                syscheck.registry_ignore_regex[i]))
408             {
409                 return;
410             }
411             i++;
412         }
413     }
414
415
416     if(RegOpenKeyEx(sub_tree, subkey, 0, KEY_READ, &oshkey) != ERROR_SUCCESS)
417     {
418         merror(SK_REG_OPEN, ARGV0, subkey);
419         return;
420     }
421
422     os_winreg_querykey(oshkey, subkey, full_key_name);
423     RegCloseKey(oshkey);
424     return;
425 }
426
427
428 /** void os_winreg_check()
429  * Main function to read the registry.
430  */
431 void os_winreg_check()
432 {
433     int i = 0;
434     char *rk;
435
436     /* Debug entries */
437     debug1("%s: DEBUG: Starting os_winreg_check", ARGV0);
438
439
440     /* Zeroing ig_count before checking */
441     ig_count = 1;
442
443
444     /* Checking if the registry fp is open */
445     if(syscheck.reg_fp == NULL)
446     {
447         syscheck.reg_fp = fopen(SYS_WIN_REG, "w+");
448         if(!syscheck.reg_fp)
449         {
450             merror(FOPEN_ERROR, ARGV0, SYS_WIN_REG);
451             return;
452         }
453     }
454
455
456     /* Getting sub class and a valid registry entry */
457     while(syscheck.registry[i] != NULL)
458     {
459         sub_tree = NULL;
460         rk = NULL;
461
462         /* Ignored entries are zeroed */
463         if(*syscheck.registry[i] == '\0')
464         {
465             i++;
466             continue;
467         }
468
469
470         /* Reading syscheck registry entry */
471         debug1("%s: DEBUG: Attempt to read: %s", ARGV0, syscheck.registry[i]);
472
473
474         rk = os_winreg_sethkey(syscheck.registry[i]);
475         if(sub_tree == NULL)
476         {
477             merror(SK_INV_REG, ARGV0, syscheck.registry[i]);
478             *syscheck.registry[i] = '\0';
479             i++;
480             continue;
481         }
482
483         os_winreg_open_key(rk, syscheck.registry[i]);
484         i++;
485         sleep(syscheck.tsleep *5);
486     }
487
488
489     /* Notify of db completed. */
490     if(run_count > 1)
491     {
492         sleep(syscheck.tsleep *5);
493         notify_registry(HC_SK_DB_COMPLETED, 1);
494     }
495
496     run_count++;
497     return;
498 }
499
500
501 #endif /* WIN32 */
502
503 /* EOF */