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

Last change on this file since 293 was 96, checked in by tim, 18 years ago

last minute fixes for filter bugs

  • Property svn:keywords set to Id
File size: 24.6 KB
Line 
1/*
2 * A utility to read a Windows NT/2K/XP/2K3 registry file, using
3 * Gerald Carter''s regfio interface.
4 *
5 * Copyright (C) 2005-2007 Timothy D. Morgan
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 2 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 96 2007-03-29 01:41:33Z tim $
22 */
23
24
25#include <stdlib.h>
26#include <sysexits.h>
27#include <stdio.h>
28#include <string.h>
29#include <strings.h>
30#include <time.h>
31#include <iconv.h>
32#include "../include/regfi.h"
33#include "../include/void_stack.h"
34
35/* Globals, influenced by command line parameters */
36bool print_verbose = false;
37bool print_security = false;
38bool print_header = true;
39bool path_filter_enabled = false;
40bool type_filter_enabled = false;
41char* path_filter = NULL;
42int type_filter;
43char* registry_file = NULL;
44
45/* Other globals */
46const char* key_special_chars = ",\"\\/";
47const char* subfield_special_chars = ",\"\\|";
48const char* common_special_chars = ",\"\\";
49
50iconv_t conv_desc;
51
52
53void bailOut(int code, char* message)
54{
55 fprintf(stderr, message);
56 exit(code);
57}
58
59
60/* Returns a newly malloc()ed string which contains original buffer,
61 * except for non-printable or special characters are quoted in hex
62 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
63 * character. A null terminator is added, since only ascii, not binary,
64 * is returned.
65 */
66static char* quote_buffer(const unsigned char* str,
67 unsigned int len, const char* special)
68{
69 unsigned int i, added_len;
70 unsigned int num_written = 0;
71
72 unsigned int buf_len = sizeof(char)*(len+1);
73 char* ret_val = malloc(buf_len);
74 char* tmp_buf;
75
76 if(ret_val == NULL)
77 return NULL;
78
79 for(i=0; i<len; i++)
80 {
81 if(buf_len <= (num_written+5))
82 {
83 /* Expand the buffer by the memory consumption rate seen so far
84 * times the amount of input left to process. The expansion is bounded
85 * below by a minimum safety increase, and above by the maximum possible
86 * output string length. This should minimize both the number of
87 * reallocs() and the amount of wasted memory.
88 */
89 added_len = (len-i)*num_written/(i+1);
90 if((buf_len+added_len) > (len*4+1))
91 buf_len = len*4+1;
92 else
93 {
94 if (added_len < 5)
95 buf_len += 5;
96 else
97 buf_len += added_len;
98 }
99
100 tmp_buf = realloc(ret_val, buf_len);
101 if(tmp_buf == NULL)
102 {
103 free(ret_val);
104 return NULL;
105 }
106 ret_val = tmp_buf;
107 }
108
109 if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
110 {
111 num_written += snprintf(ret_val + num_written, buf_len - num_written,
112 "\\x%.2X", str[i]);
113 }
114 else
115 ret_val[num_written++] = str[i];
116 }
117 ret_val[num_written] = '\0';
118
119 return ret_val;
120}
121
122
123/* Returns a newly malloc()ed string which contains original string,
124 * except for non-printable or special characters are quoted in hex
125 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
126 * character.
127 */
128static char* quote_string(const char* str, const char* special)
129{
130 unsigned int len;
131
132 if(str == NULL)
133 return NULL;
134
135 len = strlen(str);
136 return quote_buffer((const unsigned char*)str, len, special);
137}
138
139
140/*
141 * Convert from UTF-16LE to ASCII. Accepts a Unicode buffer, uni, and
142 * it's length, uni_max. Writes ASCII to the buffer ascii, whose size
143 * is ascii_max. Writes at most (ascii_max-1) bytes to ascii, and null
144 * terminates the string. Returns the length of the string stored in
145 * ascii. On error, returns a negative errno code.
146 */
147static int uni_to_ascii(unsigned char* uni, char* ascii,
148 unsigned int uni_max, unsigned int ascii_max)
149{
150 char* inbuf = (char*)uni;
151 char* outbuf = ascii;
152 size_t in_len = (size_t)uni_max;
153 size_t out_len = (size_t)(ascii_max-1);
154 int ret;
155
156 /* Set up conversion descriptor. */
157 conv_desc = iconv_open("US-ASCII", "UTF-16LE");
158
159 ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
160 if(ret == -1)
161 {
162 iconv_close(conv_desc);
163 return -errno;
164 }
165 *outbuf = '\0';
166
167 iconv_close(conv_desc);
168 return strlen(ascii);
169}
170
171
172/*
173 * Convert a data value to a string for display. Returns NULL on error,
174 * and the string to display if there is no error, or a non-fatal
175 * error. On any error (fatal or non-fatal) occurs, (*error_msg) will
176 * be set to a newly allocated string, containing an error message. If
177 * a memory allocation failure occurs while generating the error
178 * message, both the return value and (*error_msg) will be NULL. It
179 * is the responsibility of the caller to free both a non-NULL return
180 * value, and a non-NULL (*error_msg).
181 */
182static char* data_to_ascii(unsigned char *datap, uint32 len, uint32 type,
183 char** error_msg)
184{
185 char* asciip;
186 char* ascii;
187 unsigned char* cur_str;
188 char* cur_ascii;
189 char* cur_quoted;
190 char* tmp_err;
191 const char* str_type;
192 uint32 i;
193 uint32 cur_str_len;
194 uint32 ascii_max, cur_str_max;
195 uint32 str_rem, cur_str_rem, alen;
196 int ret_err;
197 unsigned short num_nulls;
198
199 *error_msg = NULL;
200
201 switch (type)
202 {
203 case REG_SZ:
204 case REG_EXPAND_SZ:
205 /* REG_LINK is a symbolic link, stored as a unicode string. */
206 case REG_LINK:
207 ascii_max = sizeof(char)*(len+1);
208 ascii = malloc(ascii_max);
209 if(ascii == NULL)
210 return NULL;
211
212 /* Sometimes values have binary stored in them. If the unicode
213 * conversion fails, just quote it raw.
214 */
215 ret_err = uni_to_ascii(datap, ascii, len, ascii_max);
216 if(ret_err < 0)
217 {
218 tmp_err = strerror(-ret_err);
219 str_type = regfi_type_val2str(type);
220 *error_msg = (char*)malloc(65+strlen(str_type)+strlen(tmp_err)+1);
221 if(*error_msg == NULL)
222 {
223 free(ascii);
224 return NULL;
225 }
226 sprintf(*error_msg, "Unicode conversion failed on %s field; "
227 "printing as binary. Error: %s", str_type, tmp_err);
228
229 cur_quoted = quote_buffer(datap, len, common_special_chars);
230 }
231 else
232 cur_quoted = quote_string(ascii, common_special_chars);
233 free(ascii);
234 if(cur_quoted == NULL)
235 {
236 *error_msg = (char*)malloc(27+1);
237 if(*error_msg != NULL)
238 strcpy(*error_msg, "Buffer could not be quoted.");
239 }
240 return cur_quoted;
241 break;
242
243 case REG_DWORD:
244 ascii_max = sizeof(char)*(8+2+1);
245 ascii = malloc(ascii_max);
246 if(ascii == NULL)
247 return NULL;
248
249 snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X",
250 datap[0], datap[1], datap[2], datap[3]);
251 return ascii;
252 break;
253
254 case REG_DWORD_BE:
255 ascii_max = sizeof(char)*(8+2+1);
256 ascii = malloc(ascii_max);
257 if(ascii == NULL)
258 return NULL;
259
260 snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X",
261 datap[3], datap[2], datap[1], datap[0]);
262 return ascii;
263 break;
264
265 case REG_QWORD:
266 ascii_max = sizeof(char)*(16+2+1);
267 ascii = malloc(ascii_max);
268 if(ascii == NULL)
269 return NULL;
270
271 snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X",
272 datap[7], datap[6], datap[5], datap[4],
273 datap[3], datap[2], datap[1], datap[0]);
274 return ascii;
275 break;
276
277
278 /* XXX: this MULTI_SZ parser is pretty inefficient. Should be
279 * redone with fewer malloc calls and better string concatenation.
280 */
281 case REG_MULTI_SZ:
282 ascii_max = sizeof(char)*(len*4+1);
283 cur_str_max = sizeof(char)*(len+1);
284 cur_str = malloc(cur_str_max);
285 cur_ascii = malloc(cur_str_max);
286 ascii = malloc(ascii_max);
287 if(ascii == NULL || cur_str == NULL || cur_ascii == NULL)
288 return NULL;
289
290 /* Reads until it reaches 4 consecutive NULLs,
291 * which is two nulls in unicode, or until it reaches len, or until we
292 * run out of buffer. The latter should never happen, but we shouldn't
293 * trust our file to have the right lengths/delimiters.
294 */
295 asciip = ascii;
296 num_nulls = 0;
297 str_rem = ascii_max;
298 cur_str_rem = cur_str_max;
299 cur_str_len = 0;
300
301 for(i=0; (i < len) && str_rem > 0; i++)
302 {
303 *(cur_str+cur_str_len) = *(datap+i);
304 if(*(cur_str+cur_str_len) == 0)
305 num_nulls++;
306 else
307 num_nulls = 0;
308 cur_str_len++;
309
310 if(num_nulls == 2)
311 {
312 ret_err = uni_to_ascii(cur_str, cur_ascii, cur_str_len-1, cur_str_max);
313 if(ret_err < 0)
314 {
315 /* XXX: should every sub-field error be enumerated? */
316 if(*error_msg == NULL)
317 {
318 tmp_err = strerror(-ret_err);
319 *error_msg = (char*)malloc(90+strlen(tmp_err)+1);
320 if(*error_msg == NULL)
321 {
322 free(cur_str);
323 free(cur_ascii);
324 free(ascii);
325 return NULL;
326 }
327 sprintf(*error_msg, "Unicode conversion failed on at least one "
328 "MULTI_SZ sub-field; printing as binary. Error: %s",
329 tmp_err);
330 }
331 cur_quoted = quote_buffer(cur_str, cur_str_len-1,
332 subfield_special_chars);
333 }
334 else
335 cur_quoted = quote_string(cur_ascii, subfield_special_chars);
336
337 alen = snprintf(asciip, str_rem, "%s", cur_quoted);
338 asciip += alen;
339 str_rem -= alen;
340 free(cur_quoted);
341
342 if(*(datap+i+1) == 0 && *(datap+i+2) == 0)
343 break;
344 else
345 {
346 if(str_rem > 0)
347 {
348 asciip[0] = '|';
349 asciip[1] = '\0';
350 asciip++;
351 str_rem--;
352 }
353 memset(cur_str, 0, cur_str_max);
354 cur_str_len = 0;
355 num_nulls = 0;
356 /* To eliminate leading nulls in subsequent strings. */
357 i++;
358 }
359 }
360 }
361 *asciip = 0;
362 free(cur_str);
363 free(cur_ascii);
364 return ascii;
365 break;
366
367 /* XXX: Dont know what to do with these yet, just print as binary... */
368 default:
369 fprintf(stderr, "WARNING: Unrecognized registry data type (0x%.8X); quoting as binary.\n", type);
370
371 case REG_NONE:
372 case REG_RESOURCE_LIST:
373 case REG_FULL_RESOURCE_DESCRIPTOR:
374 case REG_RESOURCE_REQUIREMENTS_LIST:
375
376 case REG_BINARY:
377 return quote_buffer(datap, len, common_special_chars);
378 break;
379 }
380
381 return NULL;
382}
383
384
385/* XXX: Each chunk must be unquoted after it is split out.
386 * Quoting syntax may need to be standardized and pushed into the API
387 * to deal with this issue and others.
388 */
389char** splitPath(const char* s)
390{
391 char** ret_val;
392 const char* cur = s;
393 char* next = NULL;
394 char* copy;
395 uint32 ret_cur = 0;
396
397 ret_val = (char**)malloc((REGF_MAX_DEPTH+1+1)*sizeof(char**));
398 if (ret_val == NULL)
399 return NULL;
400 ret_val[0] = NULL;
401
402 /* We return a well-formed, 0-length, path even when input is icky. */
403 if (s == NULL)
404 return ret_val;
405
406 while((next = strchr(cur, '/')) != NULL)
407 {
408 if ((next-cur) > 0)
409 {
410 copy = (char*)malloc((next-cur+1)*sizeof(char));
411 if(copy == NULL)
412 bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
413
414 memcpy(copy, cur, next-cur);
415 copy[next-cur] = '\0';
416 ret_val[ret_cur++] = copy;
417 if(ret_cur < (REGF_MAX_DEPTH+1+1))
418 ret_val[ret_cur] = NULL;
419 else
420 bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
421 }
422 cur = next+1;
423 }
424
425 /* Grab last element, if path doesn't end in '/'. */
426 if(strlen(cur) > 0)
427 {
428 copy = strdup(cur);
429 ret_val[ret_cur++] = copy;
430 if(ret_cur < (REGF_MAX_DEPTH+1+1))
431 ret_val[ret_cur] = NULL;
432 else
433 bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
434 }
435
436 return ret_val;
437}
438
439
440void freePath(char** path)
441{
442 uint32 i;
443
444 if(path == NULL)
445 return;
446
447 for(i=0; path[i] != NULL; i++)
448 free(path[i]);
449
450 free(path);
451}
452
453
454/* Returns a quoted path from an iterator's stack */
455/* XXX: Some way should be found to integrate this into regfi's API
456 * The problem is that the escaping is sorta reglookup-specific.
457 */
458char* iter2Path(REGFI_ITERATOR* i)
459{
460 const REGFI_ITER_POSITION* cur;
461 uint32 buf_left = 127;
462 uint32 buf_len = buf_left+1;
463 uint32 name_len = 0;
464 uint32 grow_amt;
465 char* buf;
466 char* new_buf;
467 char* name;
468 const char* cur_name;
469 void_stack_iterator* iter;
470
471 buf = (char*)malloc((buf_len)*sizeof(char));
472 if (buf == NULL)
473 return NULL;
474 buf[0] = '\0';
475
476 iter = void_stack_iterator_new(i->key_positions);
477 if (iter == NULL)
478 {
479 free(buf);
480 return NULL;
481 }
482
483 /* skip root element */
484 if(void_stack_size(i->key_positions) < 1)
485 {
486 buf[0] = '/';
487 buf[1] = '\0';
488 return buf;
489 }
490 cur = void_stack_iterator_next(iter);
491
492 do
493 {
494 cur = void_stack_iterator_next(iter);
495 if (cur == NULL)
496 cur_name = i->cur_key->keyname;
497 else
498 cur_name = cur->nk->keyname;
499
500 buf[buf_len-buf_left-1] = '/';
501 buf_left -= 1;
502 name = quote_string(cur_name, key_special_chars);
503 name_len = strlen(name);
504 if(name_len+1 > buf_left)
505 {
506 grow_amt = (uint32)(buf_len/2);
507 buf_len += name_len+1+grow_amt-buf_left;
508 if((new_buf = realloc(buf, buf_len)) == NULL)
509 {
510 free(buf);
511 free(iter);
512 return NULL;
513 }
514 buf = new_buf;
515 buf_left = grow_amt + name_len + 1;
516 }
517 strncpy(buf+(buf_len-buf_left-1), name, name_len);
518 buf_left -= name_len;
519 buf[buf_len-buf_left-1] = '\0';
520 free(name);
521 } while(cur != NULL);
522
523 return buf;
524}
525
526
527void printValue(const REGF_VK_REC* vk, char* prefix)
528{
529 char* quoted_value = NULL;
530 char* quoted_name = NULL;
531 char* conv_error = NULL;
532 const char* str_type = NULL;
533 uint32 size;
534 uint8 tmp_buf[4];
535
536 /* Thanks Microsoft for making this process so straight-forward!!! */
537 /* XXX: this logic should be abstracted and pushed into the regfi
538 * interface. This includes the size limits.
539 */
540 size = (vk->data_size & ~VK_DATA_IN_OFFSET);
541 if(vk->data_size & VK_DATA_IN_OFFSET)
542 {
543 tmp_buf[0] = (uint8)((vk->data_off >> 3) & 0xFF);
544 tmp_buf[1] = (uint8)((vk->data_off >> 2) & 0xFF);
545 tmp_buf[2] = (uint8)((vk->data_off >> 1) & 0xFF);
546 tmp_buf[3] = (uint8)(vk->data_off & 0xFF);
547 if(size > 4)
548 {
549 fprintf(stderr, "WARNING: value stored in offset larger than 4. "
550 "Truncating...\n");
551 size = 4;
552 }
553 quoted_value = data_to_ascii(tmp_buf, 4, vk->type, &conv_error);
554 }
555 else
556 {
557 /* Microsoft's documentation indicates that "available memory" is
558 * the limit on value sizes. Annoying. We limit it to 1M which
559 * should rarely be exceeded, unless the file is corrupt or
560 * malicious. For more info, see:
561 * http://msdn2.microsoft.com/en-us/library/ms724872.aspx
562 */
563 if(size > VK_MAX_DATA_LENGTH)
564 {
565 fprintf(stderr, "WARNING: value data size %d larger than "
566 "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
567 size = VK_MAX_DATA_LENGTH;
568 }
569
570 quoted_value = data_to_ascii(vk->data, vk->data_size,
571 vk->type, &conv_error);
572 }
573
574 /* XXX: Sometimes value names can be NULL in registry. Need to
575 * figure out why and when, and generate the appropriate output
576 * for that condition.
577 */
578 quoted_name = quote_string(vk->valuename, common_special_chars);
579 if (quoted_name == NULL)
580 {
581 quoted_name = malloc(1*sizeof(char));
582 if(quoted_name == NULL)
583 bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
584 quoted_name[0] = '\0';
585 }
586
587 if(quoted_value == NULL)
588 {
589 if(conv_error == NULL)
590 fprintf(stderr, "WARNING: Could not quote value for '%s/%s'. "
591 "Memory allocation failure likely.\n", prefix, quoted_name);
592 else
593 fprintf(stderr, "WARNING: Could not quote value for '%s/%s'. "
594 "Returned error: %s\n", prefix, quoted_name, conv_error);
595 }
596 /* XXX: should these always be printed? */
597 else if(conv_error != NULL && print_verbose)
598 fprintf(stderr, "VERBOSE: While quoting value for '%s/%s', "
599 "warning returned: %s\n", prefix, quoted_name, conv_error);
600
601 str_type = regfi_type_val2str(vk->type);
602 if(print_security)
603 {
604 if(str_type == NULL)
605 printf("%s/%s,0x%.8X,%s,,,,,\n", prefix, quoted_name,
606 vk->type, quoted_value);
607 else
608 printf("%s/%s,%s,%s,,,,,\n", prefix, quoted_name,
609 str_type, quoted_value);
610 }
611 else
612 {
613 if(str_type == NULL)
614 printf("%s/%s,0x%.8X,%s,\n", prefix, quoted_name,
615 vk->type, quoted_value);
616 else
617 printf("%s/%s,%s,%s,\n", prefix, quoted_name,
618 str_type, quoted_value);
619 }
620
621 if(quoted_value != NULL)
622 free(quoted_value);
623 if(quoted_name != NULL)
624 free(quoted_name);
625 if(conv_error != NULL)
626 free(conv_error);
627}
628
629
630void printValueList(REGFI_ITERATOR* i, char* prefix)
631{
632 const REGF_VK_REC* value;
633
634 value = regfi_iterator_first_value(i);
635 while(value != NULL)
636 {
637 if(!type_filter_enabled || (value->type == type_filter))
638 printValue(value, prefix);
639 value = regfi_iterator_next_value(i);
640 }
641}
642
643
644void printKey(const REGF_NK_REC* k, char* full_path)
645{
646 static char empty_str[1] = "";
647 char* owner = NULL;
648 char* group = NULL;
649 char* sacl = NULL;
650 char* dacl = NULL;
651 char mtime[20];
652 time_t tmp_time[1];
653 struct tm* tmp_time_s = NULL;
654
655 *tmp_time = nt_time_to_unix(&k->mtime);
656 tmp_time_s = gmtime(tmp_time);
657 strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
658
659 if(print_security)
660 {
661 owner = regfi_get_owner(k->sec_desc->sec_desc);
662 group = regfi_get_group(k->sec_desc->sec_desc);
663 sacl = regfi_get_sacl(k->sec_desc->sec_desc);
664 dacl = regfi_get_dacl(k->sec_desc->sec_desc);
665 if(owner == NULL)
666 owner = empty_str;
667 if(group == NULL)
668 group = empty_str;
669 if(sacl == NULL)
670 sacl = empty_str;
671 if(dacl == NULL)
672 dacl = empty_str;
673
674 printf("%s,KEY,,%s,%s,%s,%s,%s\n", full_path, mtime,
675 owner, group, sacl, dacl);
676
677 if(owner != empty_str)
678 free(owner);
679 if(group != empty_str)
680 free(group);
681 if(sacl != empty_str)
682 free(sacl);
683 if(dacl != empty_str)
684 free(dacl);
685 }
686 else
687 printf("%s,KEY,,%s\n", full_path, mtime);
688}
689
690
691void printKeyTree(REGFI_ITERATOR* iter)
692{
693 const REGF_NK_REC* root = NULL;
694 const REGF_NK_REC* cur = NULL;
695 const REGF_NK_REC* sub = NULL;
696 char* path = NULL;
697 int key_type = regfi_type_str2val("KEY");
698 bool print_this = true;
699
700 root = cur = regfi_iterator_cur_key(iter);
701 sub = regfi_iterator_first_subkey(iter);
702
703 if(root == NULL)
704 bailOut(EX_DATAERR, "ERROR: root cannot be NULL.\n");
705
706 do
707 {
708 if(print_this)
709 {
710 path = iter2Path(iter);
711 if(path == NULL)
712 bailOut(EX_OSERR, "ERROR: Could not construct iterator's path.\n");
713
714 if(!type_filter_enabled || (key_type == type_filter))
715 printKey(cur, path);
716 if(!type_filter_enabled || (key_type != type_filter))
717 printValueList(iter, path);
718
719 free(path);
720 }
721
722 if(sub == NULL)
723 {
724 if(cur != root)
725 {
726 /* We're done with this sub-tree, going up and hitting other branches. */
727 if(!regfi_iterator_up(iter))
728 bailOut(EX_DATAERR, "ERROR: could not traverse iterator upward.\n");
729
730 cur = regfi_iterator_cur_key(iter);
731 if(cur == NULL)
732 bailOut(EX_DATAERR, "ERROR: unexpected NULL for key.\n");
733
734 sub = regfi_iterator_next_subkey(iter);
735 }
736 print_this = false;
737 }
738 else
739 { /* We have unexplored sub-keys.
740 * Let's move down and print this first sub-tree out.
741 */
742 if(!regfi_iterator_down(iter))
743 bailOut(EX_DATAERR, "ERROR: could not traverse iterator downward.\n");
744
745 cur = sub;
746 sub = regfi_iterator_first_subkey(iter);
747 print_this = true;
748 }
749 } while(!((cur == root) && (sub == NULL)));
750
751 if(print_verbose)
752 fprintf(stderr, "VERBOSE: Finished printing key tree.\n");
753}
754
755
756/*
757 * Returns 0 if path was not found.
758 * Returns 1 if path was found as value.
759 * Returns 2 if path was found as key.
760 * Returns less than 0 on other error.
761 */
762int retrievePath(REGFI_ITERATOR* iter, char** path)
763{
764 const REGF_VK_REC* value;
765 char* tmp_path_joined;
766 const char** tmp_path;
767 uint32 i;
768
769 if(path == NULL)
770 return -1;
771
772 /* One extra for any value at the end, and one more for NULL */
773 tmp_path = (const char**)malloc(sizeof(const char**)*(REGF_MAX_DEPTH+1+1));
774 if(tmp_path == NULL)
775 return -2;
776
777 /* Strip any potential value name at end of path */
778 for(i=0;
779 (path[i] != NULL) && (path[i+1] != NULL)
780 && (i < REGF_MAX_DEPTH+1+1);
781 i++)
782 tmp_path[i] = path[i];
783
784 tmp_path[i] = NULL;
785
786 if(print_verbose)
787 fprintf(stderr, "VERBOSE: Attempting to retrieve specified path: %s\n",
788 path_filter);
789
790 /* Special check for '/' path filter */
791 if(path[0] == NULL)
792 {
793 if(print_verbose)
794 fprintf(stderr, "VERBOSE: Found final path element as root key.\n");
795 return 2;
796 }
797
798 if(!regfi_iterator_walk_path(iter, tmp_path))
799 {
800 free(tmp_path);
801 return 0;
802 }
803
804 if(regfi_iterator_find_value(iter, path[i]))
805 {
806 if(print_verbose)
807 fprintf(stderr, "VERBOSE: Found final path element as value.\n");
808
809 value = regfi_iterator_cur_value(iter);
810 tmp_path_joined = iter2Path(iter);
811
812 if((value == NULL) || (tmp_path_joined == NULL))
813 bailOut(EX_OSERR, "ERROR: Unexpected error before printValue.\n");
814
815 printValue(value, tmp_path_joined);
816
817 free(tmp_path);
818 free(tmp_path_joined);
819 return 1;
820 }
821 else if(regfi_iterator_find_subkey(iter, path[i]))
822 {
823 if(print_verbose)
824 fprintf(stderr, "VERBOSE: Found final path element as key.\n");
825
826 if(!regfi_iterator_down(iter))
827 bailOut(EX_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
828
829 return 2;
830 }
831
832 if(print_verbose)
833 fprintf(stderr, "VERBOSE: Could not find last element of path.\n");
834
835 return 0;
836}
837
838
839static void usage(void)
840{
841 fprintf(stderr, "Usage: reglookup [-v] [-s]"
842 " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
843 " <REGISTRY_FILE>\n");
844 fprintf(stderr, "Version: 0.4.0\n");
845 fprintf(stderr, "Options:\n");
846 fprintf(stderr, "\t-v\t sets verbose mode.\n");
847 fprintf(stderr, "\t-h\t enables header row. (default)\n");
848 fprintf(stderr, "\t-H\t disables header row.\n");
849 fprintf(stderr, "\t-s\t enables security descriptor output.\n");
850 fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
851 fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
852 fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
853 fprintf(stderr, "\n");
854}
855
856
857int main(int argc, char** argv)
858{
859 char** path = NULL;
860 REGF_FILE* f;
861 REGFI_ITERATOR* iter;
862 int retr_path_ret;
863 uint32 argi, arge;
864
865 /* Process command line arguments */
866 if(argc < 2)
867 {
868 usage();
869 bailOut(EX_USAGE, "ERROR: Requires at least one argument.\n");
870 }
871
872 arge = argc-1;
873 for(argi = 1; argi < arge; argi++)
874 {
875 if (strcmp("-p", argv[argi]) == 0)
876 {
877 if(++argi >= arge)
878 {
879 usage();
880 bailOut(EX_USAGE, "ERROR: '-p' option requires parameter.\n");
881 }
882 if((path_filter = strdup(argv[argi])) == NULL)
883 bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
884
885 path_filter_enabled = true;
886 }
887 else if (strcmp("-t", argv[argi]) == 0)
888 {
889 if(++argi >= arge)
890 {
891 usage();
892 bailOut(EX_USAGE, "ERROR: '-t' option requires parameter.\n");
893 }
894 if((type_filter = regfi_type_str2val(argv[argi])) < 0)
895 {
896 fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
897 bailOut(EX_USAGE, "");
898 }
899 type_filter_enabled = true;
900 }
901 else if (strcmp("-h", argv[argi]) == 0)
902 print_header = true;
903 else if (strcmp("-H", argv[argi]) == 0)
904 print_header = false;
905 else if (strcmp("-s", argv[argi]) == 0)
906 print_security = true;
907 else if (strcmp("-S", argv[argi]) == 0)
908 print_security = false;
909 else if (strcmp("-v", argv[argi]) == 0)
910 print_verbose = true;
911 else
912 {
913 usage();
914 fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
915 bailOut(EX_USAGE, "");
916 }
917 }
918 if((registry_file = strdup(argv[argi])) == NULL)
919 bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
920
921 f = regfi_open(registry_file);
922 if(f == NULL)
923 {
924 fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
925 bailOut(EX_NOINPUT, "");
926 }
927
928 iter = regfi_iterator_new(f);
929 if(iter == NULL)
930 bailOut(EX_OSERR, "ERROR: Couldn't create registry iterator.\n");
931
932 if(print_header)
933 {
934 if(print_security)
935 printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL\n");
936 else
937 printf("PATH,TYPE,VALUE,MTIME\n");
938 }
939
940 if(path_filter_enabled && path_filter != NULL)
941 path = splitPath(path_filter);
942
943 if(path != NULL)
944 {
945 retr_path_ret = retrievePath(iter, path);
946 freePath(path);
947
948 if(retr_path_ret == 0)
949 fprintf(stderr, "WARNING: specified path not found.\n");
950 else if (retr_path_ret == 2)
951 printKeyTree(iter);
952 else if(retr_path_ret < 0)
953 {
954 fprintf(stderr, "ERROR: retrievePath() returned %d.\n",
955 retr_path_ret);
956 bailOut(EX_DATAERR,"ERROR: Unknown error occurred in retrieving path.\n");
957 }
958 }
959 else
960 printKeyTree(iter);
961
962 regfi_iterator_free(iter);
963 regfi_close(f);
964
965 return 0;
966}
Note: See TracBrowser for help on using the repository browser.