source: releases/0.10.0/src/reglookup.c@ 286

Last change on this file since 286 was 141, checked in by tim, 16 years ago

improved details of an error message
turned off debugging symbols in Makefile

  • Property svn:keywords set to Id
File size: 16.8 KB
Line 
1/*
2 * A utility to read a Windows NT and later registry files.
3 *
4 * Copyright (C) 2005-2009 Timothy D. Morgan
5 * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * $Id: reglookup.c 141 2009-02-10 02:35:24Z tim $
21 */
22
23
24#include <stdlib.h>
25#include <sysexits.h>
26#include <stdio.h>
27#include <string.h>
28#include <strings.h>
29#include <time.h>
30#include "../include/regfi.h"
31#include "../include/void_stack.h"
32
33/* Globals, influenced by command line parameters */
34bool print_verbose = false;
35bool print_security = false;
36bool print_header = true;
37bool path_filter_enabled = false;
38bool type_filter_enabled = false;
39char* path_filter = NULL;
40int type_filter;
41char* registry_file = NULL;
42
43/* Other globals */
44REGFI_FILE* f;
45
46
47/* XXX: A hack to share some functions with reglookup-recover.c.
48 * Should move these into a proper library at some point.
49 */
50#include "common.c"
51
52
53void printValue(const REGFI_VK_REC* vk, char* prefix)
54{
55 char* quoted_value = NULL;
56 char* quoted_name = NULL;
57 char* conv_error = NULL;
58 const char* str_type = NULL;
59 uint32 size = vk->data_size;
60
61 /* Microsoft's documentation indicates that "available memory" is
62 * the limit on value sizes. Annoying. We limit it to 1M which
63 * should rarely be exceeded, unless the file is corrupt or
64 * malicious. For more info, see:
65 * http://msdn2.microsoft.com/en-us/library/ms724872.aspx
66 */
67 if(size > REGFI_VK_MAX_DATA_LENGTH)
68 {
69 fprintf(stderr, "WARN: value data size %d larger than "
70 "%d, truncating...\n", size, REGFI_VK_MAX_DATA_LENGTH);
71 size = REGFI_VK_MAX_DATA_LENGTH;
72 }
73
74 quoted_name = quote_string(vk->valuename, key_special_chars);
75 if (quoted_name == NULL)
76 { /* Value names are NULL when we're looking at the "(default)" value.
77 * Currently we just return a 0-length string to try an eliminate
78 * ambiguity with a literal "(default)" value. The data type of a line
79 * in the output allows one to differentiate between the parent key and
80 * this value.
81 */
82 quoted_name = malloc(1*sizeof(char));
83 if(quoted_name == NULL)
84 bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
85 quoted_name[0] = '\0';
86 }
87
88 if(vk->data == NULL)
89 {
90 if(print_verbose)
91 fprintf(stderr, "INFO: While quoting value for '%s/%s', "
92 "data pointer was NULL.\n", prefix, quoted_name);
93 }
94 else
95 {
96 quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
97 if(quoted_value == NULL)
98 {
99 if(conv_error == NULL)
100 fprintf(stderr, "WARN: Could not quote value for '%s/%s'. "
101 "Memory allocation failure likely.\n", prefix, quoted_name);
102 else
103 fprintf(stderr, "WARN: Could not quote value for '%s/%s'. "
104 "Returned error: %s\n", prefix, quoted_name, conv_error);
105 }
106 else if(conv_error != NULL && print_verbose)
107 fprintf(stderr, "INFO: While quoting value for '%s/%s', "
108 "warning returned: %s\n", prefix, quoted_name, conv_error);
109 }
110
111 str_type = regfi_type_val2str(vk->type);
112 if(print_security)
113 {
114 if(str_type == NULL)
115 printf("%s/%s,0x%.8X,%s,,,,,\n", prefix, quoted_name,
116 vk->type, quoted_value);
117 else
118 printf("%s/%s,%s,%s,,,,,\n", prefix, quoted_name,
119 str_type, quoted_value);
120 }
121 else
122 {
123 if(str_type == NULL)
124 printf("%s/%s,0x%.8X,%s,\n", prefix, quoted_name,
125 vk->type, quoted_value);
126 else
127 printf("%s/%s,%s,%s,\n", prefix, quoted_name,
128 str_type, quoted_value);
129 }
130
131 if(quoted_value != NULL)
132 free(quoted_value);
133 if(quoted_name != NULL)
134 free(quoted_name);
135 if(conv_error != NULL)
136 free(conv_error);
137}
138
139
140char** splitPath(const char* s)
141{
142 char** ret_val;
143 const char* cur = s;
144 char* next = NULL;
145 char* copy;
146 uint32 ret_cur = 0;
147
148 ret_val = (char**)malloc((REGFI_MAX_DEPTH+1+1)*sizeof(char**));
149 if (ret_val == NULL)
150 return NULL;
151 ret_val[0] = NULL;
152
153 /* We return a well-formed, 0-length, path even when input is icky. */
154 if (s == NULL)
155 return ret_val;
156
157 while((next = strchr(cur, '/')) != NULL)
158 {
159 if ((next-cur) > 0)
160 {
161 copy = (char*)malloc((next-cur+1)*sizeof(char));
162 if(copy == NULL)
163 bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
164
165 memcpy(copy, cur, next-cur);
166 copy[next-cur] = '\0';
167 ret_val[ret_cur++] = copy;
168 if(ret_cur < (REGFI_MAX_DEPTH+1+1))
169 ret_val[ret_cur] = NULL;
170 else
171 bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
172 }
173 cur = next+1;
174 }
175
176 /* Grab last element, if path doesn't end in '/'. */
177 if(strlen(cur) > 0)
178 {
179 copy = strdup(cur);
180 ret_val[ret_cur++] = copy;
181 if(ret_cur < (REGFI_MAX_DEPTH+1+1))
182 ret_val[ret_cur] = NULL;
183 else
184 bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
185 }
186
187 return ret_val;
188}
189
190
191void freePath(char** path)
192{
193 uint32 i;
194
195 if(path == NULL)
196 return;
197
198 for(i=0; path[i] != NULL; i++)
199 free(path[i]);
200
201 free(path);
202}
203
204
205/* Returns a quoted path from an iterator's stack */
206char* iter2Path(REGFI_ITERATOR* i)
207{
208 const REGFI_ITER_POSITION* cur;
209 uint32 buf_left = 127;
210 uint32 buf_len = buf_left+1;
211 uint32 name_len = 0;
212 uint32 grow_amt;
213 char* buf;
214 char* new_buf;
215 char* name;
216 const char* cur_name;
217 void_stack_iterator* iter;
218
219 buf = (char*)malloc((buf_len)*sizeof(char));
220 if (buf == NULL)
221 return NULL;
222 buf[0] = '\0';
223
224 iter = void_stack_iterator_new(i->key_positions);
225 if (iter == NULL)
226 {
227 free(buf);
228 return NULL;
229 }
230
231 /* skip root element */
232 if(void_stack_size(i->key_positions) < 1)
233 {
234 buf[0] = '/';
235 buf[1] = '\0';
236 return buf;
237 }
238 cur = void_stack_iterator_next(iter);
239
240 do
241 {
242 cur = void_stack_iterator_next(iter);
243 if (cur == NULL)
244 cur_name = i->cur_key->keyname;
245 else
246 cur_name = cur->nk->keyname;
247
248 buf[buf_len-buf_left-1] = '/';
249 buf_left -= 1;
250 name = quote_string(cur_name, key_special_chars);
251 name_len = strlen(name);
252 if(name_len+1 > buf_left)
253 {
254 grow_amt = (uint32)(buf_len/2);
255 buf_len += name_len+1+grow_amt-buf_left;
256 if((new_buf = realloc(buf, buf_len)) == NULL)
257 {
258 free(name);
259 free(buf);
260 free(iter);
261 return NULL;
262 }
263 buf = new_buf;
264 buf_left = grow_amt + name_len + 1;
265 }
266 strncpy(buf+(buf_len-buf_left-1), name, name_len);
267 buf_left -= name_len;
268 buf[buf_len-buf_left-1] = '\0';
269 free(name);
270 } while(cur != NULL);
271
272 return buf;
273}
274
275
276void printValueList(REGFI_ITERATOR* iter, char* prefix)
277{
278 const REGFI_VK_REC* value;
279
280 value = regfi_iterator_first_value(iter);
281 while(value != NULL)
282 {
283 if(!type_filter_enabled || (value->type == type_filter))
284 printValue(value, prefix);
285 value = regfi_iterator_next_value(iter);
286 printMsgs(iter->f);
287 }
288}
289
290
291void printKey(REGFI_ITERATOR* iter, char* full_path)
292{
293 static char empty_str[1] = "";
294 char* owner = NULL;
295 char* group = NULL;
296 char* sacl = NULL;
297 char* dacl = NULL;
298 char* quoted_classname;
299 char* error_msg = NULL;
300 char mtime[20];
301 time_t tmp_time[1];
302 struct tm* tmp_time_s = NULL;
303 const REGFI_SK_REC* sk;
304 const REGFI_NK_REC* k = regfi_iterator_cur_key(iter);
305
306 *tmp_time = nt_time_to_unix(&k->mtime);
307 tmp_time_s = gmtime(tmp_time);
308 strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
309
310 if(print_security && (sk=regfi_iterator_cur_sk(iter)))
311 {
312 owner = regfi_get_owner(sk->sec_desc);
313 group = regfi_get_group(sk->sec_desc);
314 sacl = regfi_get_sacl(sk->sec_desc);
315 dacl = regfi_get_dacl(sk->sec_desc);
316 if(owner == NULL)
317 owner = empty_str;
318 if(group == NULL)
319 group = empty_str;
320 if(sacl == NULL)
321 sacl = empty_str;
322 if(dacl == NULL)
323 dacl = empty_str;
324
325 if(k->classname != NULL)
326 {
327 quoted_classname = quote_unicode((uint8*)k->classname, k->classname_length,
328 key_special_chars, &error_msg);
329 if(quoted_classname == NULL)
330 {
331 if(error_msg == NULL)
332 fprintf(stderr, "ERROR: Could not quote classname"
333 " for key '%s' due to unknown error.\n", full_path);
334 else
335 {
336 fprintf(stderr, "ERROR: Could not quote classname"
337 " for key '%s' due to error: %s\n", full_path, error_msg);
338 free(error_msg);
339 }
340 }
341 else if (error_msg != NULL)
342 {
343 if(print_verbose)
344 fprintf(stderr, "INFO: While converting classname"
345 " for key '%s': %s.\n", full_path, error_msg);
346 free(error_msg);
347 }
348 }
349 else
350 quoted_classname = empty_str;
351
352 printMsgs(iter->f);
353 printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime,
354 owner, group, sacl, dacl, quoted_classname);
355
356 if(owner != empty_str)
357 free(owner);
358 if(group != empty_str)
359 free(group);
360 if(sacl != empty_str)
361 free(sacl);
362 if(dacl != empty_str)
363 free(dacl);
364 if(quoted_classname != empty_str)
365 free(quoted_classname);
366 }
367 else
368 printf("%s,KEY,,%s\n", full_path, mtime);
369}
370
371
372void printKeyTree(REGFI_ITERATOR* iter)
373{
374 const REGFI_NK_REC* root = NULL;
375 const REGFI_NK_REC* cur = NULL;
376 const REGFI_NK_REC* sub = NULL;
377 char* path = NULL;
378 int key_type = regfi_type_str2val("KEY");
379 bool print_this = true;
380
381 root = cur = regfi_iterator_cur_key(iter);
382 sub = regfi_iterator_first_subkey(iter);
383 printMsgs(iter->f);
384
385 if(root == NULL)
386 bailOut(EX_DATAERR, "ERROR: root cannot be NULL.\n");
387
388 do
389 {
390 if(print_this)
391 {
392 path = iter2Path(iter);
393 if(path == NULL)
394 bailOut(EX_OSERR, "ERROR: Could not construct iterator's path.\n");
395
396 if(!type_filter_enabled || (key_type == type_filter))
397 printKey(iter, path);
398 if(!type_filter_enabled || (key_type != type_filter))
399 printValueList(iter, path);
400
401 free(path);
402 }
403
404 if(sub == NULL)
405 {
406 if(cur != root)
407 {
408 /* We're done with this sub-tree, going up and hitting other branches. */
409 if(!regfi_iterator_up(iter))
410 {
411 printMsgs(iter->f);
412 bailOut(EX_DATAERR, "ERROR: could not traverse iterator upward.\n");
413 }
414
415 cur = regfi_iterator_cur_key(iter);
416 if(cur == NULL)
417 {
418 printMsgs(iter->f);
419 bailOut(EX_DATAERR, "ERROR: unexpected NULL for key.\n");
420 }
421
422 sub = regfi_iterator_next_subkey(iter);
423 }
424 print_this = false;
425 }
426 else
427 { /* We have unexplored sub-keys.
428 * Let's move down and print this first sub-tree out.
429 */
430 if(!regfi_iterator_down(iter))
431 {
432 printMsgs(iter->f);
433 bailOut(EX_DATAERR, "ERROR: could not traverse iterator downward.\n");
434 }
435
436 cur = sub;
437 sub = regfi_iterator_first_subkey(iter);
438 print_this = true;
439 }
440 printMsgs(iter->f);
441 } while(!((cur == root) && (sub == NULL)));
442
443 if(print_verbose)
444 fprintf(stderr, "INFO: Finished printing key tree.\n");
445}
446
447
448/* XXX: What if there is BOTH a value AND a key with that name??
449 * What if there are multiple keys/values with the same name??
450 */
451/*
452 * Returns 0 if path was not found.
453 * Returns 1 if path was found as value.
454 * Returns 2 if path was found as key.
455 * Returns less than 0 on other error.
456 */
457int retrievePath(REGFI_ITERATOR* iter, char** path)
458{
459 const REGFI_VK_REC* value;
460 char* tmp_path_joined;
461 const char** tmp_path;
462 uint32 i;
463
464 if(path == NULL)
465 return -1;
466
467 /* One extra for any value at the end, and one more for NULL */
468 tmp_path = (const char**)malloc(sizeof(const char**)*(REGFI_MAX_DEPTH+1+1));
469 if(tmp_path == NULL)
470 return -2;
471
472 /* Strip any potential value name at end of path */
473 for(i=0;
474 (path[i] != NULL) && (path[i+1] != NULL) && (i < REGFI_MAX_DEPTH+1);
475 i++)
476 { tmp_path[i] = path[i]; }
477 tmp_path[i] = NULL;
478
479 if(print_verbose)
480 fprintf(stderr, "INFO: Attempting to retrieve specified path: %s\n",
481 path_filter);
482
483 /* Special check for '/' path filter */
484 if(path[0] == NULL)
485 {
486 if(print_verbose)
487 fprintf(stderr, "INFO: Found final path element as root key.\n");
488 free(tmp_path);
489 return 2;
490 }
491
492 if(!regfi_iterator_walk_path(iter, tmp_path))
493 {
494 printMsgs(iter->f);
495 free(tmp_path);
496 return 0;
497 }
498
499 if(regfi_iterator_find_value(iter, path[i]))
500 {
501 if(print_verbose)
502 fprintf(stderr, "INFO: Found final path element as value.\n");
503
504 value = regfi_iterator_cur_value(iter);
505 printMsgs(iter->f);
506 tmp_path_joined = iter2Path(iter);
507
508 if((value == NULL) || (tmp_path_joined == NULL))
509 bailOut(EX_OSERR, "ERROR: Unexpected error before printValue.\n");
510
511 if(!type_filter_enabled || (value->type == type_filter))
512 printValue(value, tmp_path_joined);
513
514 free(tmp_path);
515 free(tmp_path_joined);
516 return 1;
517 }
518 else if(regfi_iterator_find_subkey(iter, path[i]))
519 {
520 printMsgs(iter->f);
521 if(print_verbose)
522 fprintf(stderr, "INFO: Found final path element as key.\n");
523
524 if(!regfi_iterator_down(iter))
525 {
526 printMsgs(iter->f);
527 bailOut(EX_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
528 }
529
530 return 2;
531 }
532 printMsgs(iter->f);
533
534 if(print_verbose)
535 fprintf(stderr, "INFO: Could not find last element of path.\n");
536
537 return 0;
538}
539
540
541static void usage(void)
542{
543 fprintf(stderr, "Usage: reglookup [-v] [-s]"
544 " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
545 " <REGISTRY_FILE>\n");
546 fprintf(stderr, "Version: %s\n", REGLOOKUP_VERSION);
547 fprintf(stderr, "Options:\n");
548 fprintf(stderr, "\t-v\t sets verbose mode.\n");
549 fprintf(stderr, "\t-h\t enables header row. (default)\n");
550 fprintf(stderr, "\t-H\t disables header row.\n");
551 fprintf(stderr, "\t-s\t enables security descriptor output.\n");
552 fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
553 fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
554 fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
555 fprintf(stderr, "\n");
556}
557
558
559int main(int argc, char** argv)
560{
561 char** path = NULL;
562 REGFI_ITERATOR* iter;
563 int retr_path_ret;
564 uint32 argi, arge;
565
566 /* Process command line arguments */
567 if(argc < 2)
568 {
569 usage();
570 bailOut(EX_USAGE, "ERROR: Requires at least one argument.\n");
571 }
572
573 arge = argc-1;
574 for(argi = 1; argi < arge; argi++)
575 {
576 if (strcmp("-p", argv[argi]) == 0)
577 {
578 if(++argi >= arge)
579 {
580 usage();
581 bailOut(EX_USAGE, "ERROR: '-p' option requires parameter.\n");
582 }
583 if((path_filter = strdup(argv[argi])) == NULL)
584 bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
585
586 path_filter_enabled = true;
587 }
588 else if (strcmp("-t", argv[argi]) == 0)
589 {
590 if(++argi >= arge)
591 {
592 usage();
593 bailOut(EX_USAGE, "ERROR: '-t' option requires parameter.\n");
594 }
595 if((type_filter = regfi_type_str2val(argv[argi])) < 0)
596 {
597 fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
598 bailOut(EX_USAGE, "");
599 }
600 type_filter_enabled = true;
601 }
602 else if (strcmp("-h", argv[argi]) == 0)
603 print_header = true;
604 else if (strcmp("-H", argv[argi]) == 0)
605 print_header = false;
606 else if (strcmp("-s", argv[argi]) == 0)
607 print_security = true;
608 else if (strcmp("-S", argv[argi]) == 0)
609 print_security = false;
610 else if (strcmp("-v", argv[argi]) == 0)
611 print_verbose = true;
612 else
613 {
614 usage();
615 fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
616 bailOut(EX_USAGE, "");
617 }
618 }
619 if((registry_file = strdup(argv[argi])) == NULL)
620 bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
621
622 f = regfi_open(registry_file);
623 if(f == NULL)
624 {
625 fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
626 bailOut(EX_NOINPUT, "");
627 }
628
629 if(print_verbose)
630 regfi_set_message_mask(f, REGFI_MSG_INFO|REGFI_MSG_WARN|REGFI_MSG_ERROR);
631
632 iter = regfi_iterator_new(f);
633 if(iter == NULL)
634 bailOut(EX_OSERR, "ERROR: Couldn't create registry iterator.\n");
635
636 if(print_header)
637 {
638 if(print_security)
639 printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL,CLASS\n");
640 else
641 printf("PATH,TYPE,VALUE,MTIME\n");
642 }
643
644 if(path_filter_enabled && path_filter != NULL)
645 path = splitPath(path_filter);
646
647 if(path != NULL)
648 {
649 retr_path_ret = retrievePath(iter, path);
650 printMsgs(iter->f);
651 freePath(path);
652
653 if(retr_path_ret == 0)
654 fprintf(stderr, "WARN: Specified path '%s' not found.\n", path_filter);
655 else if (retr_path_ret == 2)
656 printKeyTree(iter);
657 else if(retr_path_ret < 0)
658 {
659 fprintf(stderr, "ERROR: retrievePath() returned %d.\n",
660 retr_path_ret);
661 bailOut(EX_DATAERR,"ERROR: Unknown error occurred in retrieving path.\n");
662 }
663 }
664 else
665 printKeyTree(iter);
666
667 regfi_iterator_free(iter);
668 regfi_close(f);
669
670 return 0;
671}
Note: See TracBrowser for help on using the repository browser.