source: trunk/src/reglookup.c @ 32

Last change on this file since 32 was 17, checked in by tim, 19 years ago

moved project under trunk. (I am a subversion newbie.)

  • Property svn:keywords set to Id
File size: 51.3 KB
RevLine 
[1]1/*
[4]2 * $Id: reglookup.c 17 2005-06-04 22:08:03Z tim $
[1]3 *
[8]4 * A utility to read a Windows NT/2K etc registry file.
[4]5 *
[1]6 * This code was taken from Richard Sharpe''s editreg utility, in the
7 * Samba CVS tree.  It has since been simplified and turned into a
8 * strictly read-only utility.
9 *
10 * Copyright (C) 2005 Timothy D. Morgan
11 * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
[4]15 * the Free Software Foundation; version 2 of the License.
[1]16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
25 */
26 
27/*************************************************************************
28
[4]29 A note from Richard Sharpe:
30  Many of the ideas in here come from other people and software.
31  I first looked in Wine in misc/registry.c and was also influenced by
32  http://www.wednesday.demon.co.uk/dosreg.html
[1]33
[4]34  Which seems to contain comments from someone else. I reproduce them here
35  incase the site above disappears. It actually comes from
36  http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt.
[1]37 
[4]38 NOTE: the comments he refers to have been moved to doc/winntreg.txt
[1]39
[4]40**************************************************************************/
[1]41
42
43#include <stdio.h>
44#include <stdlib.h>
[7]45#include <stdbool.h>
[1]46#include <errno.h>
47#include <assert.h>
48#include <sys/types.h>
49#include <sys/stat.h>
50#include <unistd.h>
51#include <sys/mman.h>
52#include <strings.h>
53#include <string.h>
54#include <fcntl.h>
55
56#define False 0
57#define True 1
58#define REG_KEY_LIST_SIZE 10
59
60/*
61 * Structures for dealing with the on-disk format of the registry
62 */
63
64#define IVAL(buf) ((unsigned int) \
65                   (unsigned int)*((unsigned char *)(buf)+3)<<24| \
66                   (unsigned int)*((unsigned char *)(buf)+2)<<16| \
67                   (unsigned int)*((unsigned char *)(buf)+1)<<8| \
68                   (unsigned int)*((unsigned char *)(buf)+0))
69
70#define SVAL(buf) ((unsigned short) \
71                   (unsigned short)*((unsigned char *)(buf)+1)<<8| \
72                   (unsigned short)*((unsigned char *)(buf)+0))
73
74#define CVAL(buf) ((unsigned char)*((unsigned char *)(buf)))
75
76#define SIVAL(buf, val) \
77            ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\
78             (((unsigned char *)(buf))[1])=(unsigned char)(((val)>>8)&0xFF),\
79             (((unsigned char *)(buf))[2])=(unsigned char)(((val)>>16)&0xFF),\
80             (((unsigned char *)(buf))[3])=(unsigned char)((val)>>24))
81
82#define SSVAL(buf, val) \
83  ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\
84   (((unsigned char *)(buf))[1])=(unsigned char)((val)>>8))
85
86static int verbose = 0;
87static int print_security = 0;
88static int full_print = 0;
89static const char *def_owner_sid_str = NULL;
90
91/*
92 * These definitions are for the in-memory registry structure.
93 * It is a tree structure that mimics what you see with tools like regedit
94 */
95
96/*
97 * DateTime struct for Windows
98 */
99
100typedef struct date_time_s {
101  unsigned int low, high;
102} NTTIME;
103
104/*
105 * Definition of a Key. It has a name, classname, date/time last modified,
106 * sub-keys, values, and a security descriptor
107 */
108
109#define REG_ROOT_KEY 1
110#define REG_SUB_KEY  2
111#define REG_SYM_LINK 3
112
113typedef struct key_sec_desc_s KEY_SEC_DESC;
114
115typedef struct reg_key_s {
116  char *name;         /* Name of the key                    */
117  char *class_name;
118  int type;           /* One of REG_ROOT_KEY or REG_SUB_KEY */
119  NTTIME last_mod; /* Time last modified                 */
120  struct reg_key_s *owner;
121  struct key_list_s *sub_keys;
122  struct val_list_s *values;
123  KEY_SEC_DESC *security;
124  unsigned int offset;  /* Offset of the record in the file */
125} REG_KEY;
126
127/*
128 * The KEY_LIST struct lists sub-keys.
129 */
130
131typedef struct key_list_s {
132  int key_count;
133  int max_keys;
134  REG_KEY *keys[1];
135} KEY_LIST;
136
137typedef struct val_key_s {
138  char *name;
139  int has_name;
140  int data_type;
141  int data_len;
142  void *data_blk;    /* Might want a separate block */
143} VAL_KEY;
144
145typedef struct val_list_s {
146  int val_count;
147  int max_vals;
148  VAL_KEY *vals[1];
149} VAL_LIST;
150
151#ifndef MAXSUBAUTHS
152#define MAXSUBAUTHS 15
153#endif
154
155typedef struct sid_s {
156  unsigned char ver, auths;
157  unsigned char auth[6];
158  unsigned int sub_auths[MAXSUBAUTHS];
159} sid_t;
160
161typedef struct ace_struct_s {
162  unsigned char type, flags;
163  unsigned int perms;   /* Perhaps a better def is in order */
164  sid_t *trustee;
165} ACE; 
166
167typedef struct acl_struct_s {
168  unsigned short rev, refcnt;
169  unsigned short num_aces;
170  ACE *aces[1];
171} ACL;
172
173typedef struct sec_desc_s {
174  unsigned int rev, type;
175  sid_t *owner, *group;
176  ACL *sacl, *dacl;
177} SEC_DESC;
178
179#define SEC_DESC_NON 0
180#define SEC_DESC_RES 1
181#define SEC_DESC_OCU 2
182#define SEC_DESC_NBK 3
183typedef struct sk_struct SK_HDR;
184struct key_sec_desc_s {
185  struct key_sec_desc_s *prev, *next;
186  int ref_cnt;
187  int state;
188  int offset;
189  SK_HDR *sk_hdr;     /* This means we must keep the registry in memory */
190  SEC_DESC *sec_desc;
191}; 
192
193/*
194 * All of the structures below actually have a four-byte length before them
195 * which always seems to be negative. The following macro retrieves that
196 * size as an integer
197 */
198
199#define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
200
201typedef unsigned int DWORD;
202typedef unsigned short WORD;
203
204#define REG_REGF_ID 0x66676572
205
206typedef struct regf_block {
207  DWORD REGF_ID;     /* regf */
208  DWORD uk1;
209  DWORD uk2;
210  DWORD tim1, tim2;
211  DWORD uk3;             /* 1 */
212  DWORD uk4;             /* 3 */
213  DWORD uk5;             /* 0 */
214  DWORD uk6;             /* 1 */
215  DWORD first_key;       /* offset */
216  unsigned int dblk_size;
217  DWORD uk7[116];        /* 1 */
218  DWORD chksum;
219} REGF_HDR;
220
221typedef struct hbin_sub_struct {
222  DWORD dblocksize;
223  char data[1];
224} HBIN_SUB_HDR;
225
226#define REG_HBIN_ID 0x6E696268
227
228typedef struct hbin_struct {
229  DWORD HBIN_ID; /* hbin */
230  DWORD off_from_first;
231  DWORD off_to_next;
232  DWORD uk1;
233  DWORD uk2;
234  DWORD uk3;
235  DWORD uk4;
236  DWORD blk_size;
237  HBIN_SUB_HDR hbin_sub_hdr;
238} HBIN_HDR;
239
240#define REG_NK_ID 0x6B6E
241
242typedef struct nk_struct {
243  WORD NK_ID;
244  WORD type;
245  DWORD t1, t2;
246  DWORD uk1;
247  DWORD own_off;
248  DWORD subk_num;
249  DWORD uk2;
250  DWORD lf_off;
251  DWORD uk3;
252  DWORD val_cnt;
253  DWORD val_off;
254  DWORD sk_off;
255  DWORD clsnam_off;
256  DWORD unk4[4];
257  DWORD unk5;
258  WORD nam_len;
259  WORD clsnam_len;
260  char key_nam[1];  /* Actual length determined by nam_len */
261} NK_HDR;
262
263#define REG_SK_ID 0x6B73
264
265struct sk_struct {
266  WORD SK_ID;
267  WORD uk1;
268  DWORD prev_off;
269  DWORD next_off;
270  DWORD ref_cnt;
271  DWORD rec_size;
272  char sec_desc[1];
273};
274
275typedef struct ace_struct {
276    unsigned char type;
277    unsigned char flags;
278    unsigned short length;
279    unsigned int perms;
280    sid_t trustee;
281} REG_ACE;
282
283typedef struct acl_struct {
284  WORD rev;
285  WORD size;
286  DWORD num_aces;
287  REG_ACE *aces;   /* One or more ACEs */
288} REG_ACL;
289
290typedef struct sec_desc_rec {
291  WORD rev;
292  WORD type;
293  DWORD owner_off;
294  DWORD group_off;
295  DWORD sacl_off;
296  DWORD dacl_off;
297} REG_SEC_DESC;
298
299typedef struct hash_struct {
300  DWORD nk_off;
301  char hash[4];
302} HASH_REC;
303
304#define REG_LF_ID 0x666C
305
306typedef struct lf_struct {
307  WORD LF_ID;
308  WORD key_count;
309  struct hash_struct hr[1];  /* Array of hash records, depending on key_count */
310} LF_HDR;
311
312typedef DWORD VL_TYPE[1];  /* Value list is an array of vk rec offsets */
313
314#define REG_VK_ID 0x6B76
315
316typedef struct vk_struct {
317  WORD VK_ID;
318  WORD nam_len;
319  DWORD dat_len;    /* If top-bit set, offset contains the data */
320  DWORD dat_off;   
321  DWORD dat_type;
322  WORD flag;        /* =1, has name, else no name (=Default). */
323  WORD unk1;
324  char dat_name[1]; /* Name starts here ... */
325} VK_HDR;
326
327#define REG_TYPE_DELETE    -1
328#define REG_TYPE_NONE      0
329#define REG_TYPE_REGSZ     1
330#define REG_TYPE_EXPANDSZ  2
331#define REG_TYPE_BIN       3 
332#define REG_TYPE_DWORD     4
333#define REG_TYPE_MULTISZ   7
[8]334/* Not a real type in the registry */
335#define REG_TYPE_KEY       255
[1]336
337typedef struct _val_str { 
338  unsigned int val;
339  const char * str;
340} VAL_STR;
341
342/* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
343typedef struct sk_map_s {
344  int sk_off;
345  KEY_SEC_DESC *key_sec_desc;
346} SK_MAP;
347
348/*
349 * This structure keeps track of the output format of the registry
350 */
351#define REG_OUTBLK_HDR 1
352#define REG_OUTBLK_HBIN 2
353
354typedef struct hbin_blk_s {
355  int type, size;
356  struct hbin_blk_s *next;
357  char *data;                /* The data block                */
358  unsigned int file_offset;  /* Offset in file                */
359  unsigned int free_space;   /* Amount of free space in block */
360  unsigned int fsp_off;      /* Start of free space in block  */
361  int complete, stored;
362} HBIN_BLK;
363
364/*
365 * This structure keeps all the registry stuff in one place
366 */
367typedef struct regf_struct_s {
368  int reg_type;
369  char *regfile_name, *outfile_name;
370  int fd;
371  struct stat sbuf;
372  char *base;
373  int modified;
374  NTTIME last_mod_time;
375  REG_KEY *root;  /* Root of the tree for this file */
376  int sk_count, sk_map_size;
377  SK_MAP *sk_map;
378  const char *owner_sid_str;
379  SEC_DESC *def_sec_desc;
380  /*
381   * These next pointers point to the blocks used to contain the
382   * keys when we are preparing to write them to a file
383   */
384  HBIN_BLK *blk_head, *blk_tail, *free_space;
385} REGF;
386
387
388/* Function prototypes */
389
390static int nt_val_list_iterator(REGF *regf,  REG_KEY *key_tree, int bf, 
[7]391                                char *path, int terminal);
[1]392static int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf,
[7]393                           const char *path);
[1]394static REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key);
395static int print_key(const char *path, char *name, char *class_name, int root,
396                     int terminal, int vals, char* newline);
397static int print_val(const char *path, char *val_name, int val_type, 
398                     int data_len, void *data_blk, int terminal, int first, 
399                     int last);
400
[7]401static int print_sec(SEC_DESC *sec_desc);
[1]402
[7]403
404/* Globals */
405
406char* prefix_filter = "";
[8]407int type_filter = 0;
[7]408bool type_filter_enabled = false;
409
410
[1]411unsigned int str_is_prefix(const char* p, const char* s)
412{
413  const char* cp;
414  const char* cs;
415 
416  cs = s;
417  for(cp=p; (*cp) != '\0'; cp++)
418  {
419    if((*cp)!=(*cs))
420      return 0;
421    cs++;
422  }
423
424  return 1;
425}
426
427
[8]428/* Returns a newly malloc()ed string which contains original buffer,
[6]429 * except for non-printable or special characters are quoted in hex
430 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
[8]431 * character.  A null terminator is added, as only ascii, not binary,
432 * is returned.
[6]433 */
434static
[8]435char* quote_buffer(const unsigned char* str, char* special, unsigned int len)
[6]436{
437  unsigned int i;
438  unsigned int num_written=0;
439  unsigned int out_len = sizeof(char)*len+1;
440  char* ret_val = malloc(out_len);
[8]441
[6]442  if(ret_val == NULL)
443    return NULL;
444
445  for(i=0; i<len; i++)
446  {
447    if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
448    {
449      out_len += 3;
[8]450      /* XXX: may not be the most efficient way of getting enough memory. */
[6]451      ret_val = realloc(ret_val, out_len);
452      if(ret_val == NULL)
453        break;
[8]454      num_written += snprintf(ret_val+num_written, (out_len)-num_written,
[6]455                              "\\x%.2X", str[i]);
456    }
457    else
458      ret_val[num_written++] = str[i];
459  }
460  ret_val[num_written] = '\0';
461
462  return ret_val;
463}
464
465
[8]466/* Returns a newly malloc()ed string which contains original string,
467 * except for non-printable or special characters are quoted in hex
468 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
469 * character.
470 */
471static
472char* quote_string(const char* str, char* special)
473{
474  unsigned int len = strlen(str);
475  char* ret_val = quote_buffer((const unsigned char*)str, special, len);
476
477  return ret_val;
478}
479
480
481
[1]482/*
483 * Iterate over the keys, depth first, calling a function for each key
484 * and indicating if it is terminal or non-terminal and if it has values.
485 *
486 * In addition, for each value in the list, call a value list function
487 */
488
489static
490int nt_val_list_iterator(REGF *regf,  REG_KEY *key_tree, int bf, char *path,
[7]491                         int terminal)
[1]492{
493  int i;
494  VAL_LIST* val_list = key_tree->values;
495
[7]496  for (i=0; i<val_list->val_count; i++) 
497  {
498    /*XXX: print_key() is doing nothing right now, can probably be removed. */
499    if (!print_key(path, key_tree->name,
500                   key_tree->class_name,
501                   (key_tree->type == REG_ROOT_KEY),
502                   (key_tree->sub_keys == NULL),
503                   (key_tree->values?(key_tree->values->val_count):0),
504                   "\n") ||
505        !print_val(path, val_list->vals[i]->name,val_list->vals[i]->data_type,
506                   val_list->vals[i]->data_len, val_list->vals[i]->data_blk,
507                   terminal,
508                   (i == 0),
509                   (i == val_list->val_count)))
510    { return 0; }
[1]511  }
512
513  return 1;
514}
515
516static
517int nt_key_list_iterator(REGF *regf, KEY_LIST *key_list, int bf, 
[7]518                         const char *path)
[1]519{
520  int i;
521
522  if (!key_list) 
523    return 1;
524
525  for (i=0; i < key_list->key_count; i++) 
526  {
[7]527    if (!nt_key_iterator(regf, key_list->keys[i], bf, path)) 
[1]528      return 0;
529  }
530  return 1;
531}
532
533static
534int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, 
[7]535                    const char *path)
[1]536{
537  int path_len = strlen(path);
538  char *new_path;
539
540  if (!regf || !key_tree)
541    return -1;
542
[7]543  new_path = (char *)malloc(path_len + 1 + strlen(key_tree->name) + 1);
544  if (!new_path) 
545    return 0; /* Errors? */
546  new_path[0] = '\0';
547  strcat(new_path, path);
548  strcat(new_path, key_tree->name);
549  strcat(new_path, "/");
550
[1]551  /* List the key first, then the values, then the sub-keys */
[7]552  /*printf("prefix_filter: %s, path: %s\n", prefix_filter, path);*/
553  if (str_is_prefix(prefix_filter, new_path))
[1]554  {
[8]555    if (!type_filter_enabled || (type_filter == REG_TYPE_KEY))
[7]556      printf("%s%s:KEY\n", path, key_tree->name);
557
[1]558    /*XXX: print_key() is doing nothing right now, can probably be removed. */
559    if (!print_key(path, key_tree->name,
560                   key_tree->class_name,
561                   (key_tree->type == REG_ROOT_KEY),
562                   (key_tree->sub_keys == NULL),
563                   (key_tree->values?(key_tree->values->val_count):0),
564                   "\n"))
565    { return 0; }
566
567    /*
568     * If we have a security print routine, call it
569     * If the security print routine returns false, stop.
570     */
571    if (key_tree->security && !print_sec(key_tree->security->sec_desc))
572      return 0;
573  }
574
575  /*
576   * Now, iterate through the values in the val_list
577   */
578  if (key_tree->values &&
579      !nt_val_list_iterator(regf, key_tree, bf, new_path, 
[7]580                            (key_tree->values!=NULL)))
[1]581  {
582    free(new_path);
583    return 0;
584  }
585
586  /*
587   * Now, iterate through the keys in the key list
588   */
589  if (key_tree->sub_keys && 
590      !nt_key_list_iterator(regf, key_tree->sub_keys, bf, 
[7]591                            new_path)) 
[1]592  {
593    free(new_path);
594    return 0;
595  } 
596
597  free(new_path);
598  return 1;
599}
600
601
602/*
603 * Find key by name in a list ...
604 * Take the first component and search for that in the list
605 */
606static
607REG_KEY *nt_find_key_in_list_by_name(KEY_LIST *list, char *key)
608{
609  int i;
610  REG_KEY *res = NULL;
611
612  if (!list || !key || !*key) return NULL;
613
614  for (i = 0; i < list->key_count; i++)
615    if ((res = nt_find_key_by_name(list->keys[i], key)))
616      return res;
617 
618  return NULL;
619}
620
621
622/*
623 * Find key by name in a tree ... We will assume absolute names here, but we
624 * need the root of the tree ...
625 */
626static REG_KEY* nt_find_key_by_name(REG_KEY* tree, char* key)
627{
628  char* lname = NULL;
629  char* c1;
630  char* c2;
631  REG_KEY* tmp;
632
633  if (!tree || !key || !*key) 
634    return NULL;
635
636  lname = strdup(key);
637  if (!lname) 
638    return NULL;
639
640  /*
641   * Make sure that the first component is correct ...
642   */
643  c1 = lname;
[7]644  c2 = strchr(c1, '/');
[1]645  if (c2) 
646  { /* Split here ... */
647    *c2 = 0;
648    c2++;
649  }
650 
651  if (strcmp(c1, tree->name) != 0) 
652  { 
653    if (lname)
654      free(lname);
655    return NULL;
656  }
657 
658  if (c2) 
659  {
660    tmp = nt_find_key_in_list_by_name(tree->sub_keys, c2);
661    free(lname);
662    return tmp;
663  }
664  else 
665  {
666    if (lname) 
667      free(lname);
668    return tree;
669  }
670
671  return NULL;
672}
673
674/* Make, delete keys */
675static
676int nt_delete_val_key(VAL_KEY *val_key)
677{
678
679  if (val_key) {
680    if (val_key->name) free(val_key->name);
681    if (val_key->data_blk) free(val_key->data_blk);
682    free(val_key);
683  };
684  return 1;
685}
686
687
688/*
689 * Add a key to the tree ... We walk down the components matching until
690 * we don't find any. There must be a match on the first component ...
691 * We return the key structure for the final component as that is
692 * often where we want to add values ...
693 */
694
695/*
696 * Convert a string of the form S-1-5-x[-y-z-r] to a SID
697 */
698/* MIGHT COME IN HANDY LATER.
699static
700int sid_string_to_sid(sid_t **sid, const char *sid_str)
701{
702  int i = 0;
703  unsigned int auth;
704  const char *lstr;
705
706  *sid = (sid_t *)malloc(sizeof(sid_t));
707  if (!*sid) return 0;
708
709  memset(*sid, 0, sizeof(sid_t));
710
711  if (strncmp(sid_str, "S-1-5", 5)) {
712    fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str);
713    return 0;
714  }
715
716//We only allow strings of form S-1-5...
717
718  (*sid)->ver = 1;
719  (*sid)->auth[5] = 5;
720
721  lstr = sid_str + 5;
722
723  while (1)
724  {
725    if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0)
726    {
727      if (i < 1)
728      {
729        fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i);
730        return 0;
731      }
732      (*sid)->auths=i;
733      return 1;
734    }
735
736    (*sid)->sub_auths[i] = auth;
737    i++;
738    lstr = strchr(lstr + 1, '-');
739  }
740
741  return 1;
742}
743*/
744
745
746/*
747 * We will implement inheritence that is based on what the parent's SEC_DESC
748 * says, but the Owner and Group SIDs can be overwridden from the command line
749 * and additional ACEs can be applied from the command line etc.
750 */
751static
752KEY_SEC_DESC *nt_inherit_security(REG_KEY *key)
753{
754
755  if (!key) return NULL;
756  return key->security;
757}
758
759/*
760 * Add a sub-key
761 */
762static
763REG_KEY *nt_add_reg_key_list(REGF *regf, REG_KEY *key, char * name, int create)
764{
765  int i;
766  REG_KEY *ret = NULL, *tmp = NULL;
767  KEY_LIST *list;
768  char *lname, *c1, *c2;
769
770  if (!key || !name || !*name) return NULL;
771 
772  list = key->sub_keys;
773  if (!list) { /* Create an empty list */
774
775    list = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (REG_KEY_LIST_SIZE - 1) * sizeof(REG_KEY *));
776    list->key_count = 0;
777    list->max_keys = REG_KEY_LIST_SIZE;
778
779  }
780
781  lname = strdup(name);
782  if (!lname) return NULL;
783
784  c1 = lname;
[7]785  c2 = strchr(c1, '/');
[1]786  if (c2) { /* Split here ... */
787    *c2 = 0;
788    c2++;
789  }
790
791  for (i = 0; i < list->key_count; i++) {
792    if (strcmp(list->keys[i]->name, c1) == 0) {
793      ret = nt_add_reg_key_list(regf, list->keys[i], c2, create);
794      free(lname);
795      return ret;
796    }
797  }
798
799  /*
800   * If we reach here we could not find the the first component
801   * so create it ...
802   */
803
804  if (list->key_count < list->max_keys){
805    list->key_count++;
806  }
807  else { /* Create more space in the list ... */
808    if (!(list = (KEY_LIST *)realloc(list, sizeof(KEY_LIST) + 
809                                     (list->max_keys + REG_KEY_LIST_SIZE - 1) 
810                                     * sizeof(REG_KEY *))))
811      goto error;
812
813    list->max_keys += REG_KEY_LIST_SIZE;
814    list->key_count++;
815  }
816
817  /*
818   * add the new key at the new slot
[8]819   * XXX: Sort the list someday
[1]820   */
821
822  /*
823   * We want to create the key, and then do the rest
824   */
825
826  tmp = (REG_KEY *)malloc(sizeof(REG_KEY)); 
827
828  memset(tmp, 0, sizeof(REG_KEY));
829
830  tmp->name = strdup(c1);
831  if (!tmp->name) goto error;
832  tmp->owner = key;
833  tmp->type = REG_SUB_KEY;
834  /*
835   * Next, pull security from the parent, but override with
836   * anything passed in on the command line
837   */
838  tmp->security = nt_inherit_security(key);
839
840  list->keys[list->key_count - 1] = tmp;
841
842  if (c2) {
843    ret = nt_add_reg_key_list(regf, key, c2, True);
844  }
845
846  if (lname) free(lname);
847
848  return ret;
849
850 error:
851  if (tmp) free(tmp);
852  if (lname) free(lname);
853  return NULL;
854}
855
856
857/*
858 * Load and unload a registry file.
859 *
860 * Load, loads it into memory as a tree, while unload sealizes/flattens it
861 */
862
863/*
864 * Get the starting record for NT Registry file
865 */
866
867/*
868 * Where we keep all the regf stuff for one registry.
869 * This is the structure that we use to tie the in memory tree etc
870 * together. By keeping separate structs, we can operate on different
871 * registries at the same time.
872 * Currently, the SK_MAP is an array of mapping structure.
873 * Since we only need this on input and output, we fill in the structure
874 * as we go on input. On output, we know how many SK items we have, so
875 * we can allocate the structure as we need to.
876 * If you add stuff here that is dynamically allocated, add the
877 * appropriate free statements below.
878 */
879
880#define REGF_REGTYPE_NONE 0
881#define REGF_REGTYPE_NT   1
882#define REGF_REGTYPE_W9X  2
883
884#define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
885                              (r)->last_mod_time.high = (t2);
886
887#define REGF_HDR_BLKSIZ 0x1000
888
889#define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4)
890#define LOCN(base, f) ((base) + OFF(f))
891
892const VAL_STR reg_type_names[] = {
[7]893   { REG_TYPE_REGSZ,    "SZ" },
894   { REG_TYPE_EXPANDSZ, "EXPAND_SZ" },
895   { REG_TYPE_BIN,      "BIN" },
896   { REG_TYPE_DWORD,    "DWORD" },
897   { REG_TYPE_MULTISZ,  "MULTI_SZ" },
[8]898   { REG_TYPE_KEY,      "KEY" },
[1]899   { 0, NULL },
900};
901
[8]902
[1]903static
904const char *val_to_str(unsigned int val, const VAL_STR *val_array)
905{
[8]906  int i;
[1]907
[8]908  if (!val_array) 
909    return NULL;
[1]910
[8]911  for(i=0; val_array[i].val && val_array[i].str; i++) 
912    if (val_array[i].val == val) 
913      return val_array[i].str;
[1]914
[8]915  return NULL;
916}
[1]917
918
[8]919/* Returns 0 on error */
920static
921int str_to_val(const char* str, const VAL_STR *val_array)
922{
923  int i;
[1]924
[8]925  if (!val_array) 
926    return 0;
927
928  for(i=0; val_array[i].val && val_array[i].str; i++) 
929    if (strcmp(val_array[i].str, str) == 0) 
930      return val_array[i].val;
931
932  return 0;
[1]933}
934
[8]935
[1]936/*
937 * Convert from UniCode to Ascii ... Does not take into account other lang
938 * Restrict by ascii_max if > 0
939 */
940static
941int uni_to_ascii(unsigned char *uni, unsigned char *ascii, int ascii_max, 
942                 int uni_max)
943{
944  int i = 0; 
945
946  while (i < ascii_max && (uni[i*2] || uni[i*2+1]))
947  {
948    if (uni_max > 0 && (i*2) >= uni_max) break;
949    ascii[i] = uni[i*2];
950    i++;
951  }
952  ascii[i] = '\0';
953
954  return i;
955}
956
957/*
958 * Convert a data value to a string for display
959 */
960static
961unsigned char* data_to_ascii(unsigned char *datap, int len, int type)
962{
963  unsigned char *asciip;
964  unsigned int i;
965  unsigned short num_nulls;
966  unsigned char* ascii;
[4]967  unsigned char* cur_str;
[6]968  unsigned char* cur_ascii;
969  char* cur_quoted;
[4]970  unsigned int cur_str_len;
971  unsigned int ascii_max, cur_str_max;
972  unsigned int str_rem, cur_str_rem, alen;
[1]973
974  switch (type) 
975  {
976  case REG_TYPE_REGSZ:
977    if (verbose)
978      fprintf(stderr, "Len: %d\n", len);
[7]979   
[1]980    ascii_max = sizeof(char)*len;
981    ascii = malloc(ascii_max+4);
982    if(ascii == NULL)
983      return NULL;
[7]984   
[8]985    /* XXX: This has to be fixed. It has to be UNICODE */
[1]986    uni_to_ascii(datap, ascii, len, ascii_max);
987    return ascii;
988    break;
989
990  case REG_TYPE_EXPANDSZ:
991    ascii_max = sizeof(char)*len;
992    ascii = malloc(ascii_max+2);
993    if(ascii == NULL)
994      return NULL;
995
996    uni_to_ascii(datap, ascii, len, ascii_max);
997    return ascii;
998    break;
999
1000  case REG_TYPE_BIN:
[8]1001    ascii = (unsigned char*)quote_buffer(datap, "\\", len);
[1]1002    return ascii;
1003    break;
1004
1005  case REG_TYPE_DWORD:
1006    ascii_max = sizeof(char)*10;
1007    ascii = malloc(ascii_max+1);
1008    if(ascii == NULL)
1009      return NULL;
1010
1011    if (*(int *)datap == 0)
1012      snprintf((char*)ascii, ascii_max, "0");
1013    else
1014      snprintf((char*)ascii, ascii_max, "0x%x", *(int *)datap);
1015    return ascii;
1016    break;
1017
1018  case REG_TYPE_MULTISZ:
1019    ascii_max = sizeof(char)*len*4;
[4]1020    cur_str_max = sizeof(char)*len+1;
1021    cur_str = malloc(cur_str_max);
[6]1022    cur_ascii = malloc(cur_str_max);
[1]1023    ascii = malloc(ascii_max+4);
1024    if(ascii == NULL)
1025      return NULL;
1026
1027    /* Reads until it reaches 4 consecutive NULLs,
1028     * which is two nulls in unicode, or until it reaches len, or until we
1029     * run out of buffer.  The latter should never happen, but we shouldn't
1030     * trust our file to have the right lengths/delimiters.
1031     */
1032    asciip = ascii;
1033    num_nulls = 0;
1034    str_rem = ascii_max;
[4]1035    cur_str_rem = cur_str_max;
1036    cur_str_len = 0;
1037
1038    for(i=0; (i < len) && str_rem > 0; i++)
[1]1039    {
[4]1040      *(cur_str+cur_str_len) = *(datap+i);
1041      if(*(cur_str+cur_str_len) == 0)
[1]1042        num_nulls++;
1043      else
1044        num_nulls = 0;
[4]1045      cur_str_len++;
1046
1047      if(num_nulls == 2)
1048      {
[6]1049        uni_to_ascii(cur_str, cur_ascii, cur_str_max, 0);
[8]1050        /* XXX: Should backslashes be quoted as well? */
[6]1051        cur_quoted = quote_string((char*)cur_ascii, "|");
1052        alen = snprintf((char*)asciip, str_rem, "%s", cur_quoted);
[4]1053        asciip += alen;
1054        str_rem -= alen;
[6]1055        free(cur_quoted);
1056
[4]1057        if(*(datap+i+1) == 0 && *(datap+i+2) == 0)
1058          break;
1059        else
1060        {
[6]1061          alen = snprintf((char*)asciip, str_rem, "%c", '|');
[4]1062          asciip += alen;
1063          str_rem -= alen;
1064          memset(cur_str, 0, cur_str_max);
1065          cur_str_len = 0;
1066          num_nulls = 0;
1067          /* To eliminate leading nulls in subsequent strings. */
1068          i++;
1069        }
1070      }
[1]1071    }
[6]1072    *asciip = 0;
[1]1073    return ascii;
1074    break;
1075
1076  default:
1077    return NULL;
1078    break;
1079  } 
1080
1081  return NULL;
1082}
1083
1084static
1085REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent);
1086
1087static
1088int nt_set_regf_input_file(REGF *regf, char *filename)
1089{
1090  return ((regf->regfile_name = strdup(filename)) != NULL); 
1091}
1092
1093
1094/* Create a regf structure and init it */
1095
1096static
1097REGF *nt_create_regf(void)
1098{
1099  REGF *tmp = (REGF *)malloc(sizeof(REGF));
1100  if (!tmp) return tmp;
1101  memset(tmp, 0, sizeof(REGF));
1102  tmp->owner_sid_str = def_owner_sid_str;
1103  return tmp;
1104} 
1105
1106
1107/* Get the header of the registry. Return a pointer to the structure
1108 * If the mmap'd area has not been allocated, then mmap the input file
1109 */
1110static
1111REGF_HDR *nt_get_regf_hdr(REGF *regf)
1112{
1113  if (!regf)
1114    return NULL; /* What about errors */
1115
1116  if (!regf->regfile_name)
1117    return NULL; /* What about errors */
1118
1119  if (!regf->base) { /* Try to mmap etc the file */
1120
1121    if ((regf->fd = open(regf->regfile_name, O_RDONLY, 0000)) <0) {
1122      return NULL; /* What about errors? */
1123    }
1124
1125    if (fstat(regf->fd, &regf->sbuf) < 0) {
1126      return NULL;
1127    }
1128
1129    regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
1130
1131    if ((int)regf->base == 1) {
1132      fprintf(stderr, "Could not mmap file: %s, %s\n", regf->regfile_name,
1133              strerror(errno));
1134      return NULL;
1135    }
1136  }
1137
1138  /*
1139   * At this point, regf->base != NULL, and we should be able to read the
1140   * header
1141   */
1142
1143  assert(regf->base != NULL);
1144
1145  return (REGF_HDR *)regf->base;
1146}
1147
1148/*
1149 * Validate a regf header
1150 * For now, do nothing, but we should check the checksum
1151 */
1152static
1153int valid_regf_hdr(REGF_HDR *regf_hdr)
1154{
1155  if (!regf_hdr) return 0;
1156
1157  return 1;
1158}
1159
1160/*
1161 * Process an SK header ...
1162 * Every time we see a new one, add it to the map. Otherwise, just look it up.
1163 * We will do a simple linear search for the moment, since many KEYs have the
1164 * same security descriptor.
1165 * We allocate the map in increments of 10 entries.
1166 */
1167
1168/*
1169 * Create a new entry in the map, and increase the size of the map if needed
1170 */
1171static
1172SK_MAP *alloc_sk_map_entry(REGF *regf, KEY_SEC_DESC *tmp, int sk_off)
1173{
1174 if (!regf->sk_map) { /* Allocate a block of 10 */
1175    regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
1176    if (!regf->sk_map) {
1177      free(tmp);
1178      return NULL;
1179    }
1180    regf->sk_map_size = 10;
1181    regf->sk_count = 1;
1182    (regf->sk_map)[0].sk_off = sk_off;
1183    (regf->sk_map)[0].key_sec_desc = tmp;
1184  }
1185  else { /* Simply allocate a new slot, unless we have to expand the list */ 
1186    int ndx = regf->sk_count;
1187    if (regf->sk_count >= regf->sk_map_size) {
1188      regf->sk_map = (SK_MAP *)realloc(regf->sk_map, 
1189                                       (regf->sk_map_size + 10)*sizeof(SK_MAP));
1190      if (!regf->sk_map) {
1191        free(tmp);
1192        return NULL;
1193      }
1194      /*
1195       * ndx already points at the first entry of the new block
1196       */
1197      regf->sk_map_size += 10;
1198    }
1199    (regf->sk_map)[ndx].sk_off = sk_off;
1200    (regf->sk_map)[ndx].key_sec_desc = tmp;
1201    regf->sk_count++;
1202  }
1203 return regf->sk_map;
1204}
1205
1206/*
1207 * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
1208 * found
1209 */
1210static
1211KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
1212{
1213  int i;
1214
1215  if (!sk_map) return NULL;
1216
1217  for (i = 0; i < count; i++) {
1218
1219    if (sk_map[i].sk_off == sk_off)
1220      return sk_map[i].key_sec_desc;
1221
1222  }
1223
1224  return NULL;
1225
1226}
1227
1228/*
1229 * Allocate a KEY_SEC_DESC if we can't find one in the map
1230 */
1231static
1232KEY_SEC_DESC *lookup_create_sec_key(REGF *regf, SK_MAP *sk_map, int sk_off)
1233{
1234  KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
1235
1236  if (tmp) {
1237    return tmp;
1238  }
1239  else { /* Allocate a new one */
1240    tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
1241    if (!tmp) {
1242      return NULL;
1243    }
1244    memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */
1245    tmp->state = SEC_DESC_RES;
1246    if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
1247      return NULL;
1248    }
1249    return tmp;
1250  }
1251}
1252
1253/*
1254 * Allocate storage and duplicate a SID
1255 * We could allocate the SID to be only the size needed, but I am too lazy.
1256 */
1257static
1258sid_t *dup_sid(sid_t *sid)
1259{
1260  sid_t *tmp = (sid_t *)malloc(sizeof(sid_t));
1261  int i;
1262 
1263  if (!tmp) return NULL;
1264  tmp->ver = sid->ver;
1265  tmp->auths = sid->auths;
1266  for (i=0; i<6; i++) {
1267    tmp->auth[i] = sid->auth[i];
1268  }
1269  for (i=0; i<tmp->auths&&i<MAXSUBAUTHS; i++) {
1270    tmp->sub_auths[i] = sid->sub_auths[i];
1271  }
1272  return tmp;
1273}
1274
1275/*
1276 * Allocate space for an ACE and duplicate the registry encoded one passed in
1277 */
1278static
1279ACE *dup_ace(REG_ACE *ace)
1280{
1281  ACE *tmp = NULL; 
1282
1283  tmp = (ACE *)malloc(sizeof(ACE));
1284
[8]1285  if (!tmp) 
1286    return NULL;
[1]1287
1288  tmp->type = CVAL(&ace->type);
1289  tmp->flags = CVAL(&ace->flags);
1290  tmp->perms = IVAL(&ace->perms);
1291  tmp->trustee = dup_sid(&ace->trustee);
1292  return tmp;
1293}
1294
1295/*
1296 * Allocate space for an ACL and duplicate the registry encoded one passed in
1297 */
1298static
1299ACL *dup_acl(REG_ACL *acl)
1300{
1301  ACL *tmp = NULL;
1302  REG_ACE* ace;
1303  int i, num_aces;
1304
1305  num_aces = IVAL(&acl->num_aces);
1306
1307  tmp = (ACL *)malloc(sizeof(ACL) + (num_aces - 1)*sizeof(ACE *));
1308  if (!tmp) return NULL;
1309
1310  tmp->num_aces = num_aces;
1311  tmp->refcnt = 1;
1312  tmp->rev = SVAL(&acl->rev);
1313  if (verbose) fprintf(stdout, "ACL: refcnt: %u, rev: %u\n", tmp->refcnt, 
1314                       tmp->rev);
1315  ace = (REG_ACE *)&acl->aces;
1316  for (i=0; i<num_aces; i++) {
1317    tmp->aces[i] = dup_ace(ace);
1318    ace = (REG_ACE *)((char *)ace + SVAL(&ace->length));
[8]1319    /* XXX: should handle NULLs returned from dup_ace() */
[1]1320  }
1321
1322  return tmp;
1323}
1324
1325static
1326SEC_DESC *process_sec_desc(REGF *regf, REG_SEC_DESC *sec_desc)
1327{
1328  SEC_DESC *tmp = NULL;
1329 
1330  tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
1331
1332  if (!tmp) {
1333    return NULL;
1334  }
1335 
1336  tmp->rev = SVAL(&sec_desc->rev);
1337  tmp->type = SVAL(&sec_desc->type);
1338  if (verbose) fprintf(stdout, "SEC_DESC Rev: %0X, Type: %0X\n", 
1339                       tmp->rev, tmp->type);
1340  if (verbose) fprintf(stdout, "SEC_DESC Owner Off: %0X\n",
1341                       IVAL(&sec_desc->owner_off));
1342  if (verbose) fprintf(stdout, "SEC_DESC Group Off: %0X\n",
1343                       IVAL(&sec_desc->group_off));
1344  if (verbose) fprintf(stdout, "SEC_DESC DACL Off: %0X\n",
1345                       IVAL(&sec_desc->dacl_off));
1346  tmp->owner = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->owner_off)));
1347  if (!tmp->owner) {
1348    free(tmp);
1349    return NULL;
1350  }
1351  tmp->group = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->group_off)));
1352  if (!tmp->group) {
1353    free(tmp);
1354    return NULL;
1355  }
1356
1357  /* Now pick up the SACL and DACL */
1358
1359  if (sec_desc->sacl_off)
1360    tmp->sacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->sacl_off)));
1361  else
1362    tmp->sacl = NULL;
1363
1364  if (sec_desc->dacl_off)
1365    tmp->dacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->dacl_off)));
1366  else
1367    tmp->dacl = NULL;
1368
1369  return tmp;
1370}
1371
1372static
1373KEY_SEC_DESC *process_sk(REGF *regf, SK_HDR *sk_hdr, int sk_off, int size)
1374{
1375  KEY_SEC_DESC *tmp = NULL;
1376  int sk_next_off, sk_prev_off, sk_size;
1377  REG_SEC_DESC *sec_desc;
1378
1379  if (!sk_hdr) return NULL;
1380
1381  if (SVAL(&sk_hdr->SK_ID) != REG_SK_ID) {
1382    fprintf(stderr, "Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
1383            regf->regfile_name);
1384    return NULL;
1385  }
1386
1387  if (-size < (sk_size = IVAL(&sk_hdr->rec_size))) {
1388    fprintf(stderr, "Incorrect SK record size: %d vs %d. %s\n",
1389            -size, sk_size, regf->regfile_name);
1390    return NULL;
1391  }
1392
1393  /*
1394   * Now, we need to look up the SK Record in the map, and return it
1395   * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
1396   * use that
1397   */
1398
1399  if (regf->sk_map &&
1400      ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
1401      && (tmp->state == SEC_DESC_OCU)) {
1402    tmp->ref_cnt++;
1403    return tmp;
1404  }
1405
1406  /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
1407
1408  assert(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
1409
1410  /*
1411   * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
1412   * new KEY_SEC_DESC to the mapping structure, since the offset supplied is
1413   * the actual offset of structure. The same offset will be used by
1414   * all future references to this structure
1415   * We could put all this unpleasantness in a function.
1416   */
1417
1418  if (!tmp) {
1419    tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
1420    if (!tmp) return NULL;
1421    memset(tmp, 0, sizeof(KEY_SEC_DESC));
1422   
1423    /*
1424     * Allocate an entry in the SK_MAP ...
1425     * We don't need to free tmp, because that is done for us if the
1426     * sm_map entry can't be expanded when we need more space in the map.
1427     */
1428   
1429    if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
1430      return NULL;
1431    }
1432  }
1433
1434  tmp->ref_cnt++;
1435  tmp->state = SEC_DESC_OCU;
1436
1437  /*
1438   * Now, process the actual sec desc and plug the values in
1439   */
1440
1441  sec_desc = (REG_SEC_DESC *)&sk_hdr->sec_desc[0];
1442  tmp->sec_desc = process_sec_desc(regf, sec_desc);
1443
1444  /*
1445   * Now forward and back links. Here we allocate an entry in the sk_map
1446   * if it does not exist, and mark it reserved
1447   */
1448
1449  sk_prev_off = IVAL(&sk_hdr->prev_off);
1450  tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
1451  assert(tmp->prev != NULL);
1452  sk_next_off = IVAL(&sk_hdr->next_off);
1453  tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
1454  assert(tmp->next != NULL);
1455
1456  return tmp;
1457}
1458
1459/*
1460 * Process a VK header and return a value
1461 */
1462static
1463VAL_KEY *process_vk(REGF *regf, VK_HDR *vk_hdr, int size)
1464{
1465  char val_name[1024];
1466  int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
1467  const char *val_type;
1468  VAL_KEY *tmp = NULL; 
1469
1470  if (!vk_hdr) return NULL;
1471
1472  if ((vk_id = SVAL(&vk_hdr->VK_ID)) != REG_VK_ID) {
1473    fprintf(stderr, "Unrecognized VK header ID: %0X, block: %0X, %s\n",
1474            vk_id, (int)vk_hdr, regf->regfile_name);
1475    return NULL;
1476  }
1477
1478  nam_len = SVAL(&vk_hdr->nam_len);
1479  val_name[nam_len] = '\0';
1480  flag = SVAL(&vk_hdr->flag);
1481  dat_type = IVAL(&vk_hdr->dat_type);
1482  dat_len = IVAL(&vk_hdr->dat_len);  /* If top bit, offset contains data */
1483  dat_off = IVAL(&vk_hdr->dat_off);
1484
1485  tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
1486  if (!tmp) {
1487    goto error;
1488  }
1489  memset(tmp, 0, sizeof(VAL_KEY));
1490  tmp->has_name = flag;
1491  tmp->data_type = dat_type;
1492
1493  if (flag & 0x01) {
1494    strncpy(val_name, vk_hdr->dat_name, nam_len);
1495    tmp->name = strdup(val_name);
1496    if (!tmp->name) {
1497      goto error;
1498    }
1499  }
1500  else
1501    strncpy(val_name, "<No Name>", 10);
1502
1503  /*
1504   * Allocate space and copy the data as a BLOB
1505   */
1506
1507  if (dat_len) {
1508   
1509    char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
1510   
1511    if (!dtmp) {
1512      goto error;
1513    }
1514
1515    tmp->data_blk = dtmp;
1516
1517    if ((dat_len&0x80000000) == 0) 
1518    { /* The data is pointed to by the offset */
1519      char *dat_ptr = LOCN(regf->base, dat_off);
[8]1520      memcpy(dtmp, dat_ptr, dat_len);
[1]1521    }
1522    else { /* The data is in the offset or type */
1523      /*
[8]1524       * XXX:
[1]1525       * Some registry files seem to have wierd fields. If top bit is set,
1526       * but len is 0, the type seems to be the value ...
1527       * Not sure how to handle this last type for the moment ...
1528       */
1529      dat_len = dat_len & 0x7FFFFFFF;
[8]1530      memcpy(dtmp, &dat_off, dat_len);
[1]1531    }
1532
1533    tmp->data_len = dat_len;
1534  }
1535
1536  val_type = val_to_str(dat_type, reg_type_names);
1537
1538  /*
1539   * We need to save the data area as well
1540   */
1541  if (verbose) 
1542    fprintf(stdout, "  %s : %s : \n", val_name, val_type);
1543
1544  return tmp;
1545
1546 error:
1547  if (tmp) nt_delete_val_key(tmp);
1548  return NULL;
1549
1550}
1551
1552/*
1553 * Process a VL Header and return a list of values
1554 */
1555static
1556VAL_LIST *process_vl(REGF *regf, VL_TYPE vl, int count, int size)
1557{
1558  int i, vk_off;
1559  VK_HDR *vk_hdr;
1560  VAL_LIST *tmp = NULL;
1561
1562  if (!vl) return NULL;
1563
1564  if (-size < (count+1)*sizeof(int)){
1565    fprintf(stderr, "Error in VL header format. Size less than space required. %d\n", -size);
1566    return NULL;
1567  }
1568
1569  tmp = (VAL_LIST *)malloc(sizeof(VAL_LIST) + (count - 1) * sizeof(VAL_KEY *));
1570  if (!tmp) {
1571    goto error;
1572  }
1573
1574  for (i=0; i<count; i++) {
1575    vk_off = IVAL(&vl[i]);
1576    vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
1577    tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr));
1578    if (!tmp->vals[i]){
1579      goto error;
1580    }
1581  }
1582
1583  tmp->val_count = count;
1584  tmp->max_vals = count;
1585
1586  return tmp;
1587
1588 error:
[8]1589  /* XXX: free the partially allocated structure */
[1]1590  return NULL;
1591} 
1592
1593/*
1594 * Process an LF Header and return a list of sub-keys
1595 */
1596static
1597KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size, REG_KEY *parent)
1598{
1599  int count, i, nk_off;
1600  unsigned int lf_id;
1601  KEY_LIST *tmp;
1602
1603  if (!lf_hdr) return NULL;
1604
1605  if ((lf_id = SVAL(&lf_hdr->LF_ID)) != REG_LF_ID) {
1606    fprintf(stderr, "Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
1607            lf_id, (int)lf_hdr, regf->regfile_name);
1608    return NULL;
1609  }
1610
1611  assert(size < 0);
1612
1613  count = SVAL(&lf_hdr->key_count);
1614  if (verbose) 
1615    fprintf(stdout, "Key Count: %u\n", count);
1616  if (count <= 0) return NULL;
1617
1618  /* Now, we should allocate a KEY_LIST struct and fill it in ... */
1619
1620  tmp = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (count - 1) * sizeof(REG_KEY *));
1621  if (!tmp) {
1622    goto error;
1623  }
1624
1625  tmp->key_count = count;
1626  tmp->max_keys = count;
1627
1628  for (i=0; i<count; i++) {
1629    NK_HDR *nk_hdr;
1630
1631    nk_off = IVAL(&lf_hdr->hr[i].nk_off);
1632    if (verbose) 
1633      fprintf(stdout, "NK Offset: %0X\n", nk_off);
1634    nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
1635    tmp->keys[i] = nt_get_key_tree(regf, nk_hdr, BLK_SIZE(nk_hdr), parent);
1636    if (!tmp->keys[i]) {
1637      goto error;
1638    }
1639  }
1640
1641  return tmp;
1642
1643 error:
1644  /*if (tmp) nt_delete_key_list(tmp, False);*/
1645  return NULL;
1646}
1647
1648
1649/*
1650 * This routine is passed an NK_HDR pointer and retrieves the entire tree
1651 * from there down. It returns a REG_KEY *.
1652 */
1653static
1654REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent)
1655{
1656  REG_KEY *tmp = NULL, *own;
1657  int name_len, clsname_len, lf_off, val_off, val_count, sk_off, own_off;
1658  unsigned int nk_id;
1659  LF_HDR *lf_hdr;
1660  VL_TYPE *vl;
1661  SK_HDR *sk_hdr;
1662  char key_name[1024];
1663  unsigned char cls_name[1024];
1664
1665  if (!nk_hdr) return NULL;
1666
1667  if ((nk_id = SVAL(&nk_hdr->NK_ID)) != REG_NK_ID) {
1668    fprintf(stderr, "Unrecognized NK Header format: %08X, Block: %0X. %s\n", 
1669            nk_id, (int)nk_hdr, regf->regfile_name);
1670    return NULL;
1671  }
1672
1673  assert(size < 0);
1674
1675  name_len = SVAL(&nk_hdr->nam_len);
1676  clsname_len = SVAL(&nk_hdr->clsnam_len);
1677
1678  /*
1679   * The value of -size should be ge
1680   * (sizeof(NK_HDR) - 1 + name_len)
1681   * The -1 accounts for the fact that we included the first byte of
1682   * the name in the structure. clsname_len is the length of the thing
1683   * pointed to by clsnam_off
1684   */
1685
1686  if (-size < (sizeof(NK_HDR) - 1 + name_len)) {
1687    fprintf(stderr, "Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr);
1688    fprintf(stderr, "Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
1689            sizeof(NK_HDR), name_len, clsname_len);
1690    /*return NULL;*/
1691  }
1692
1693  if (verbose) fprintf(stdout, "NK HDR: Name len: %d, class name len: %d\n", 
1694                       name_len, clsname_len);
1695
1696  /* Fish out the key name and process the LF list */
1697
1698  assert(name_len < sizeof(key_name));
1699
1700  /* Allocate the key struct now */
1701  tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
1702  if (!tmp) return tmp;
1703  memset(tmp, 0, sizeof(REG_KEY));
1704
1705  tmp->type = (SVAL(&nk_hdr->type)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
1706 
1707  strncpy(key_name, nk_hdr->key_nam, name_len);
1708  key_name[name_len] = '\0';
1709
1710  if (verbose) fprintf(stdout, "Key name: %s\n", key_name);
1711
1712  tmp->name = strdup(key_name);
1713  if (!tmp->name) {
1714    goto error;
1715  }
1716
1717  /*
1718   * Fish out the class name, it is in UNICODE, while the key name is
1719   * ASCII :-)
1720   */
1721
1722  if (clsname_len) 
1723  { /* Just print in Ascii for now */
1724    unsigned char *clsnamep;
1725    unsigned int clsnam_off;
1726
1727    clsnam_off = IVAL(&nk_hdr->clsnam_off);
1728    clsnamep = (unsigned char*)LOCN(regf->base, clsnam_off);
1729    if (verbose) fprintf(stdout, "Class Name Offset: %0X\n", clsnam_off);
1730 
1731    memset(cls_name, 0, clsname_len);
1732    uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len);
1733   
1734    /*
[8]1735     * XXX:
[1]1736     * I am keeping class name as an ascii string for the moment.
1737     * That means it needs to be converted on output.
1738     * It will also piss off people who need Unicode/UTF-8 strings. Sorry.
1739     */
1740    tmp->class_name = strdup((char*)cls_name);
1741    if (!tmp->class_name) {
1742      goto error;
1743    }
1744
1745    if (verbose) fprintf(stdout, "  Class Name: %s\n", cls_name);
1746
1747  }
1748
1749  /*
1750   * Process the owner offset ...
1751   */
1752  own_off = IVAL(&nk_hdr->own_off);
1753  own = (REG_KEY *)LOCN(regf->base, own_off);
1754  if (verbose) 
1755    fprintf(stdout, "Owner Offset: %0X\n", own_off);
1756
1757  if (verbose) 
1758    fprintf(stdout, "  Owner locn: %0X, Our locn: %0X\n", 
1759                       (unsigned int)own, (unsigned int)nk_hdr);
1760
1761  /*
1762   * We should verify that the owner field is correct ...
1763   * for now, we don't worry ...
1764   */
1765  tmp->owner = parent;
1766
1767  /*
1768   * If there are any values, process them here
1769   */
1770
1771  val_count = IVAL(&nk_hdr->val_cnt);
1772  if (verbose) 
1773    fprintf(stdout, "Val Count: %d\n", val_count);
1774  if (val_count) 
1775  {
1776    val_off = IVAL(&nk_hdr->val_off);
1777    vl = (VL_TYPE *)LOCN(regf->base, val_off);
1778    if (verbose) 
1779      fprintf(stdout, "Val List Offset: %0X\n", val_off);
1780
1781    tmp->values = process_vl(regf, *vl, val_count, BLK_SIZE(vl));
1782    if (!tmp->values) {
1783      goto error;
1784    }
1785
1786  }
1787
1788  /*
1789   * Also handle the SK header ...
1790   */
1791
1792  sk_off = IVAL(&nk_hdr->sk_off);
1793  sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
1794  if (verbose) 
1795    fprintf(stdout, "SK Offset: %0X\n", sk_off);
1796
1797  if (sk_off != -1) {
1798
1799    tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
1800
1801  } 
1802
1803  lf_off = IVAL(&nk_hdr->lf_off);
1804  if (verbose) 
1805    fprintf(stdout, "SubKey list offset: %0X\n", lf_off);
1806
1807  /*
1808   * No more subkeys if lf_off == -1
1809   */
1810  if (lf_off != -1) 
1811  {
1812    lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
1813   
1814    tmp->sub_keys = process_lf(regf, lf_hdr, BLK_SIZE(lf_hdr), tmp);
1815    if (!tmp->sub_keys)
1816      goto error;
1817  }
1818
1819  return tmp;
1820
1821 error:
1822  /*if (tmp) nt_delete_reg_key(tmp, False);*/
1823  return NULL;
1824}
1825
1826static
1827int nt_load_registry(REGF *regf)
1828{
1829  REGF_HDR *regf_hdr;
1830  unsigned int regf_id, hbin_id;
1831  HBIN_HDR *hbin_hdr;
1832  NK_HDR *first_key;
1833
1834  /* Get the header */
1835
1836  if ((regf_hdr = nt_get_regf_hdr(regf)) == NULL) {
1837    return -1;
1838  }
1839
1840  /* Now process that header and start to read the rest in */
1841
1842  if ((regf_id = IVAL(&regf_hdr->REGF_ID)) != REG_REGF_ID) {
1843    fprintf(stderr, "Unrecognized NT registry header id: %0X, %s\n",
1844            regf_id, regf->regfile_name);
1845    return -1;
1846  }
1847
1848  /*
1849   * Validate the header ...
1850   */
1851  if (!valid_regf_hdr(regf_hdr)) {
1852    fprintf(stderr, "Registry file header does not validate: %s\n",
1853            regf->regfile_name);
1854    return -1;
1855  }
1856
1857  /* Update the last mod date, and then go get the first NK record and on */
1858
1859  TTTONTTIME(regf, IVAL(&regf_hdr->tim1), IVAL(&regf_hdr->tim2));
1860
1861  /*
1862   * The hbin hdr seems to be just uninteresting garbage. Check that
1863   * it is there, but that is all.
1864   */
1865
1866  hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
1867
1868  if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID)) != REG_HBIN_ID) {
1869    fprintf(stderr, "Unrecognized registry hbin hdr ID: %0X, %s\n", 
1870            hbin_id, regf->regfile_name);
1871    return -1;
1872  } 
1873
1874  /*
1875   * Get a pointer to the first key from the hreg_hdr
1876   */
1877
1878  if (verbose) 
1879    fprintf(stdout, "First Key: %0X\n", IVAL(&regf_hdr->first_key));
1880
1881  first_key = (NK_HDR *)LOCN(regf->base, IVAL(&regf_hdr->first_key));
1882  if (verbose) fprintf(stdout, "First Key Offset: %0X\n", 
1883                       IVAL(&regf_hdr->first_key));
1884
1885  if (verbose) fprintf(stdout, "Data Block Size: %d\n",
1886                       IVAL(&regf_hdr->dblk_size));
1887
1888  if (verbose) fprintf(stdout, "Offset to next hbin block: %0X\n",
1889                       IVAL(&hbin_hdr->off_to_next));
1890
1891  if (verbose) fprintf(stdout, "HBIN block size: %0X\n",
1892                       IVAL(&hbin_hdr->blk_size));
1893
1894  /*
1895   * Now, get the registry tree by processing that NK recursively
1896   */
1897
1898  regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key), NULL);
1899
1900  assert(regf->root != NULL);
1901
1902  /*
1903   * Unmap the registry file, as we might want to read in another
1904   * tree etc.
1905   */
1906
1907  if (regf->base) munmap(regf->base, regf->sbuf.st_size);
1908  regf->base = NULL;
1909  close(regf->fd);    /* Ignore the error :-) */
1910
1911  return 1;
1912}
1913
1914
1915/*
1916 * Routines to parse a REGEDIT4 file
1917 *
1918 * The file consists of:
1919 *
1920 * REGEDIT4
1921 * \[[-]key-path\]\n
1922 * <value-spec>*
1923 *
1924 * Format:
1925 * [cmd:]name=type:value
1926 *
1927 * cmd = a|d|c|add|delete|change|as|ds|cs
1928 *
1929 * There can be more than one key-path and value-spec.
1930 *
1931 * Since we want to support more than one type of file format, we
1932 * construct a command-file structure that keeps info about the command file
1933 */
1934
1935#define FMT_UNREC -1
1936#define FMT_REGEDIT4 0
1937#define FMT_EDITREG1_1 1
1938
1939#define FMT_STRING_REGEDIT4 "REGEDIT4"
1940#define FMT_STRING_EDITREG1_0 "EDITREG1.0"
1941
1942#define CMD_NONE     0
1943#define CMD_ADD_KEY  1
1944#define CMD_DEL_KEY  2
1945
1946#define CMD_KEY 1
1947#define CMD_VAL 2
1948
1949typedef struct val_spec_list {
1950  struct val_spec_list *next;
1951  char *name;
1952  int type;
1953  char *val;    /* Kept as a char string, really? */
1954} VAL_SPEC_LIST;
1955
1956typedef struct command_s {
1957  int cmd;
1958  char *key;
1959  int val_count;
1960  VAL_SPEC_LIST *val_spec_list, *val_spec_last;
1961} CMD;
1962
1963typedef struct cmd_line {
1964  int len, line_len;
1965  char *line;
1966} CMD_LINE;
1967
1968
1969
1970#define INIT_ALLOC 10
1971
1972
1973/* prints a key */
1974static
1975int print_key(const char *path, char *name, char *class_name, int root, 
1976              int terminal, int vals, char* newline)
1977{
1978  if (full_print)
[7]1979    fprintf(stdout, "%s%s/%s", path, name, newline);
[1]1980
1981  return 1;
1982}
1983
1984/*
1985 * Sec Desc print functions
1986 */
1987static
1988void print_type(unsigned char type)
1989{
1990  switch (type) {
1991  case 0x00:
1992    fprintf(stdout, "    ALLOW");
1993    break;
1994  case 0x01:
1995    fprintf(stdout, "     DENY");
1996    break;
1997  case 0x02:
1998    fprintf(stdout, "    AUDIT");
1999    break;
2000  case 0x03:
2001    fprintf(stdout, "    ALARM");
2002    break;
2003  case 0x04:
2004    fprintf(stdout, "ALLOW CPD");
2005    break;
2006  case 0x05:
2007    fprintf(stdout, "OBJ ALLOW");
2008    break;
2009  case 0x06:
2010    fprintf(stdout, " OBJ DENY");
2011    break;
2012  default:
2013    fprintf(stdout, "  UNKNOWN");
2014    break;
2015  }
2016}
2017
2018static
2019void print_flags(unsigned char flags)
2020{
2021  char flg_output[21];
2022  int some = 0;
2023
2024  flg_output[0] = 0;
2025  if (!flags) {
2026    fprintf(stdout, "         ");
2027    return;
2028  }
2029  if (flags & 0x01) {
2030    if (some) strcat(flg_output, ",");
2031    some = 1;
2032    strcat(flg_output, "OI");
2033  }
2034  if (flags & 0x02) {
2035    if (some) strcat(flg_output, ",");
2036    some = 1;
2037    strcat(flg_output, "CI");
2038  }
2039  if (flags & 0x04) {
2040    if (some) strcat(flg_output, ",");
2041    some = 1;
2042    strcat(flg_output, "NP");
2043  }
2044  if (flags & 0x08) {
2045    if (some) strcat(flg_output, ",");
2046    some = 1;
2047    strcat(flg_output, "IO");
2048  }
2049  if (flags & 0x10) {
2050    if (some) strcat(flg_output, ",");
2051    some = 1;
2052    strcat(flg_output, "IA");
2053  }
2054  if (flags == 0xF) {
2055    if (some) strcat(flg_output, ",");
2056    some = 1;
2057    strcat(flg_output, "VI");
2058  }
2059  fprintf(stdout, " %s", flg_output);
2060}
2061
2062static
2063void print_perms(int perms)
2064{
2065  fprintf(stdout, " %8X", perms);
2066}
2067
2068static
2069void print_sid(sid_t *sid)
2070{
2071  int i, comps = sid->auths;
2072  fprintf(stdout, "S-%u-%u", sid->ver, sid->auth[5]);
2073
2074  for (i = 0; i < comps; i++) 
2075    fprintf(stdout, "-%u", sid->sub_auths[i]);
2076
2077  /*fprintf(stdout, "\n");*/
2078}
2079
2080static
2081void print_acl(ACL *acl, const char *prefix)
2082{
2083  int i;
2084
2085  for (i = 0; i < acl->num_aces; i++) {
2086    fprintf(stdout, ";;%s", prefix);
2087    print_type(acl->aces[i]->type);
2088    print_flags(acl->aces[i]->flags);
2089    print_perms(acl->aces[i]->perms);
2090    fprintf(stdout, " ");
2091    print_sid(acl->aces[i]->trustee);
2092  }
2093}
2094
2095static
2096int print_sec(SEC_DESC *sec_desc)
2097{
2098  if (!print_security) return 1;
2099  fprintf(stdout, ";;  SECURITY\n");
2100  fprintf(stdout, ";;   Owner: ");
2101  print_sid(sec_desc->owner);
2102  fprintf(stdout, ";;   Group: ");
2103  print_sid(sec_desc->group);
2104  if (sec_desc->sacl) {
2105    fprintf(stdout, ";;    SACL:\n");
2106    print_acl(sec_desc->sacl, " ");
2107  }
2108  if (sec_desc->dacl) {
2109    fprintf(stdout, ";;    DACL:\n");
2110    print_acl(sec_desc->dacl, " ");
2111  }
2112  return 1;
2113}
2114
2115/*
2116 * Value print function here ...
2117 */
2118static
2119int print_val(const char *path, char *val_name, int val_type, int data_len, 
2120              void *data_blk, int terminal, int first, int last)
2121{
2122  unsigned char* data_asc;
[7]2123  char* new_path;
[8]2124  const char* str_type;
[7]2125
[1]2126  if(!val_name)
[7]2127    val_name = "";
2128  if(!path)
2129    path = "";
[1]2130
[7]2131  new_path = (char *)malloc(strlen(path)+ strlen(val_name) + 1);
2132  if (!new_path)
2133    return 0; /* Errors? */
2134  new_path[0] = '\0';
2135  strcat(new_path, path);
2136  strcat(new_path, val_name);
2137
2138  if (str_is_prefix(prefix_filter, new_path))
2139  {
[8]2140    if (!type_filter_enabled || (type_filter == val_type))
[7]2141    {
2142      if(!val_name)
2143        val_name = "<No Name>";
[8]2144
2145      str_type = val_to_str(val_type,reg_type_names);
2146      if(!str_type)
2147        str_type = "";
[7]2148     
2149      data_asc = data_to_ascii((unsigned char *)data_blk, data_len, val_type);
2150      fprintf(stdout, "%s:%s=%s\n", new_path, str_type, data_asc);
2151     
2152      free(data_asc);
2153    }
2154  }
2155
2156  free(new_path);
[1]2157  return 1;
2158}
2159
2160static
2161void usage(void)
2162{
[7]2163  fprintf(stderr, "Usage: readreg [-f<PREFIX_FILTER>] [-t<TYPE_FILTER>] "
[14]2164                  "[-v] [-s] <REGISTRY_FILE>\n");
[8]2165  /* XXX: replace version string with Subversion tag? */
[7]2166  fprintf(stderr, "Version: 0.1\n");
2167  fprintf(stderr, "\n\t-v\t sets verbose mode.");
[1]2168  fprintf(stderr, "\n\t-f\t a simple prefix filter.");
[7]2169  fprintf(stderr, "\n\t-t\t restrict results to a specific type.");
2170  fprintf(stderr, "\n\t-s\t prints security descriptors.");
[1]2171  fprintf(stderr, "\n");
2172}
2173
2174
2175int main(int argc, char *argv[])
2176{
2177  REGF *regf;
2178  extern char *optarg;
2179  extern int optind;
2180  int opt; 
2181  int regf_opt = 1;
2182
2183  if (argc < 2)
2184  {
2185    usage();
2186    exit(1);
2187  }
2188 
2189  /*
2190   * Now, process the arguments
2191   */
2192
[14]2193  while ((opt = getopt(argc, argv, "svf:t:o:c:")) != EOF)
[1]2194  {
2195    switch (opt)
2196    {
2197    case 'f':
2198      /*full_print = 1;*/
[7]2199      prefix_filter = strdup(optarg);
[1]2200      regf_opt++;
2201      break;
2202
[7]2203    case 't':
[8]2204      type_filter = str_to_val(optarg, reg_type_names);
[7]2205      type_filter_enabled = true;
2206      regf_opt++;
2207      break;
2208
[1]2209    case 's':
2210      print_security++;
[14]2211      /*full_print++;*/
[1]2212      regf_opt++;
2213      break;
2214
2215    case 'v':
2216      verbose++;
2217      regf_opt++;
2218      break;
2219
2220    default:
2221      usage();
2222      exit(1);
2223      break;
2224    }
2225  }
2226
2227  /*
2228   * We only want to complain about the lack of a default owner SID if
2229   * we need one. This approximates that need
2230   */
2231  if (!def_owner_sid_str) {
2232    def_owner_sid_str = "S-1-5-21-1-2-3-4";
2233    if (verbose)
2234      fprintf(stderr, "Warning, default owner SID not set. Setting to %s\n",
2235              def_owner_sid_str);
2236  }
2237
2238  if ((regf = nt_create_regf()) == NULL) 
2239  {
2240    fprintf(stderr, "Could not create registry object: %s\n", strerror(errno));
2241    exit(2);
2242  }
2243
2244  if (regf_opt < argc) 
2245  { /* We have a registry file */
2246    if (!nt_set_regf_input_file(regf, argv[regf_opt])) 
2247    {
2248      fprintf(stderr, "Could not set name of registry file: %s, %s\n", 
2249              argv[regf_opt], strerror(errno));
2250      exit(3);
2251    }
2252
2253    /* Now, open it, and bring it into memory :-) */
2254    if (nt_load_registry(regf) < 0) 
2255    {
2256      fprintf(stderr, "Could not load registry: %s\n", argv[1]);
2257      exit(4);
2258    }
2259  }
2260
2261  /*
2262   * At this point, we should have a registry in memory and should be able
2263   * to iterate over it.
2264   */
[7]2265  nt_key_iterator(regf, regf->root, 0, "");
[1]2266
2267  return 0;
2268}
Note: See TracBrowser for help on using the repository browser.