source: releases/1.0.0/src/reglookup.c@ 293

Last change on this file since 293 was 261, checked in by tim, 14 years ago

readded windows file descriptor hack
copyright notices

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