We now read in the whole of the registry, including security bits.
[gd/samba/.git] / source3 / utils / editreg.c
1 /* 
2    Samba Unix/Linux SMB client utility editreg.c 
3    Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14    
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
18  
19 /*************************************************************************
20                                                        
21  A utility to edit a Windows NT/2K etc registry file.
22                                      
23  Many of the ideas in here come from other people and software. 
24  I first looked in Wine in misc/registry.c and was also influenced by
25  http://www.wednesday.demon.co.uk/dosreg.html
26
27  Which seems to contain comments from someone else. I reproduce them here
28  incase the site above disappears. It actually comes from 
29  http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt. 
30
31  The goal here is to read the registry into memory, manipulate it, and then
32  write it out if it was changed by any actions of the user.
33
34 The windows NT registry has 2 different blocks, where one can occur many
35 times...
36
37 the "regf"-Block
38 ================
39  
40 "regf" is obviosly the abbreviation for "Registry file". "regf" is the
41 signature of the header-block which is always 4kb in size, although only
42 the first 64 bytes seem to be used and a checksum is calculated over
43 the first 0x200 bytes only!
44
45 Offset            Size      Contents
46 0x00000000      D-Word      ID: ASCII-"regf" = 0x66676572
47 0x00000004      D-Word      ???? //see struct REGF
48 0x00000008      D-Word      ???? Always the same value as at 0x00000004
49 0x0000000C      Q-Word      last modify date in WinNT date-format
50 0x00000014      D-Word      1
51 0x00000018      D-Word      3
52 0x0000001C      D-Word      0
53 0x00000020      D-Word      1
54 0x00000024      D-Word      Offset of 1st key record
55 0x00000028      D-Word      Size of the data-blocks (Filesize-4kb)
56 0x0000002C      D-Word      1
57 0x000001FC      D-Word      Sum of all D-Words from 0x00000000 to
58 0x000001FB  //XOR of all words. Nigel
59
60 I have analyzed more registry files (from multiple machines running
61 NT 4.0 german version) and could not find an explanation for the values
62 marked with ???? the rest of the first 4kb page is not important...
63
64 the "hbin"-Block
65 ================
66 I don't know what "hbin" stands for, but this block is always a multiple
67 of 4kb in size.
68
69 Inside these hbin-blocks the different records are placed. The memory-
70 management looks like a C-compiler heap management to me...
71
72 hbin-Header
73 ===========
74 Offset      Size      Contents
75 0x0000      D-Word      ID: ASCII-"hbin" = 0x6E696268
76 0x0004      D-Word      Offset from the 1st hbin-Block
77 0x0008      D-Word      Offset to the next hbin-Block
78 0x001C      D-Word      Block-size
79
80 The values in 0x0008 and 0x001C should be the same, so I don't know
81 if they are correct or swapped...
82
83 From offset 0x0020 inside a hbin-block data is stored with the following
84 format:
85
86 Offset      Size      Contents
87 0x0000      D-Word      Data-block size    //this size must be a
88 multiple of 8. Nigel
89 0x0004      ????      Data
90  
91 If the size field is negative (bit 31 set), the corresponding block
92 is free and has a size of -blocksize!
93
94 That does not seem to be true. All block lengths seem to be negative! (Richard Sharpe) 
95
96 The data is stored as one record per block. Block size is a multiple
97 of 4 and the last block reaches the next hbin-block, leaving no room.
98
99 Records in the hbin-blocks
100 ==========================
101
102 nk-Record
103
104       The nk-record can be treated as a kombination of tree-record and
105       key-record of the win 95 registry.
106
107 lf-Record
108
109       The lf-record is the counterpart to the RGKN-record (the
110       hash-function)
111
112 vk-Record
113
114       The vk-record consists information to a single value.
115
116 sk-Record
117
118       sk (? Security Key ?) is the ACL of the registry.
119
120 Value-Lists
121
122       The value-lists contain information about which values are inside a
123       sub-key and don't have a header.
124
125 Datas
126
127       The datas of the registry are (like the value-list) stored without a
128       header.
129
130 All offset-values are relative to the first hbin-block and point to the
131 block-size field of the record-entry. to get the file offset, you have to add
132 the header size (4kb) and the size field (4 bytes)...
133
134 the nk-Record
135 =============
136 Offset      Size      Contents
137 0x0000      Word      ID: ASCII-"nk" = 0x6B6E
138 0x0002      Word      for the root-key: 0x2C, otherwise 0x20  //key symbolic links 0x10. Nigel
139 0x0004      Q-Word      write-date/time in windows nt notation
140 0x0010      D-Word      Offset of Owner/Parent key
141 0x0014      D-Word      number of sub-Keys
142 0x001C      D-Word      Offset of the sub-key lf-Records
143 0x0024      D-Word      number of values
144 0x0028      D-Word      Offset of the Value-List
145 0x002C      D-Word      Offset of the sk-Record
146
147 0x0030      D-Word      Offset of the Class-Name //see NK structure for the use of these fields. Nigel
148 0x0044      D-Word      Unused (data-trash)  //some kind of run time index. Does not appear to be important. Nigel
149 0x0048      Word      name-length
150 0x004A      Word      class-name length
151 0x004C      ????      key-name
152
153 the Value-List
154 ==============
155 Offset      Size      Contents
156 0x0000      D-Word      Offset 1st Value
157 0x0004      D-Word      Offset 2nd Value
158 0x????      D-Word      Offset nth Value
159
160 To determine the number of values, you have to look at the owner-nk-record!
161
162 Der vk-Record
163 =============
164 Offset      Size      Contents
165 0x0000      Word      ID: ASCII-"vk" = 0x6B76
166 0x0002      Word      name length
167 0x0004      D-Word      length of the data   //if top bit is set when offset contains data. Nigel
168 0x0008      D-Word      Offset of Data
169 0x000C      D-Word      Type of value
170 0x0010      Word      Flag
171 0x0012      Word      Unused (data-trash)
172 0x0014      ????      Name
173
174 If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
175
176 If the data-size is lower 5, the data-offset value is used to store the data itself!
177
178 The data-types
179 ==============
180 Wert      Beteutung
181 0x0001      RegSZ:             character string (in UNICODE!)
182 0x0002      ExpandSZ:   string with "%var%" expanding (UNICODE!)
183 0x0003      RegBin:           raw-binary value
184 0x0004      RegDWord:   Dword
185 0x0007      RegMultiSZ:      multiple strings, seperated with 0
186                   (UNICODE!)
187
188 The "lf"-record
189 ===============
190 Offset      Size      Contents
191 0x0000      Word      ID: ASCII-"lf" = 0x666C
192 0x0002      Word      number of keys
193 0x0004      ????      Hash-Records
194
195 Hash-Record
196 ===========
197 Offset      Size      Contents
198 0x0000      D-Word      Offset of corresponding "nk"-Record
199 0x0004      D-Word      ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
200
201 Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the 
202 key-name you have to change the hash-value too!
203
204 //These hashrecords must be sorted low to high within the lf record. Nigel.
205
206 The "sk"-block
207 ==============
208 (due to the complexity of the SAM-info, not clear jet)
209 (This is just a security descriptor in the data. R Sharpe.) 
210
211
212 Offset      Size      Contents
213 0x0000      Word      ID: ASCII-"sk" = 0x6B73
214 0x0002      Word      Unused
215 0x0004      D-Word      Offset of previous "sk"-Record
216 0x0008      D-Word      Offset of next "sk"-Record
217 0x000C      D-Word      usage-counter
218 0x0010      D-Word      Size of "sk"-record in bytes
219 ????                                             //standard self
220 relative security desciptor. Nigel
221 ????  ????      Security and auditing settings...
222 ????
223
224 The usage counter counts the number of references to this
225 "sk"-record. You can use one "sk"-record for the entire registry!
226
227 Windows nt date/time format
228 ===========================
229 The time-format is a 64-bit integer which is incremented every
230 0,0000001 seconds by 1 (I don't know how accurate it realy is!)
231 It starts with 0 at the 1st of january 1601 0:00! All values are
232 stored in GMT time! The time-zone is important to get the real
233 time!
234
235 Common values for win95 and win-nt
236 ==================================
237 Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
238 If a value has no name (length=0, flag(bit 0)=0), it is treated as the
239 "Default" entry...
240 If a value has no data (length=0), it is displayed as empty.
241
242 simplyfied win-3.?? registry:
243 =============================
244
245 +-----------+
246 | next rec. |---+                      +----->+------------+
247 | first sub |   |                      |      | Usage cnt. |
248 | name      |   |  +-->+------------+  |      | length     |
249 | value     |   |  |   | next rec.  |  |      | text       |------->+-------+
250 +-----------+   |  |   | name rec.  |--+      +------------+        | xxxxx |
251    +------------+  |   | value rec. |-------->+------------+        +-------+
252    v               |   +------------+         | Usage cnt. |
253 +-----------+      |                          | length     |
254 | next rec. |      |                          | text       |------->+-------+
255 | first sub |------+                          +------------+        | xxxxx |
256 | name      |                                                       +-------+
257 | value     |
258 +-----------+    
259
260 Greatly simplyfied structure of the nt-registry:
261 ================================================
262    
263 +---------------------------------------------------------------+
264 |                                                               |
265 v                                                               |
266 +---------+     +---------->+-----------+  +----->+---------+   |
267 | "nk"    |     |           | lf-rec.   |  |      | nk-rec. |   |
268 | ID      |     |           | # of keys |  |      | parent  |---+
269 | Date    |     |           | 1st key   |--+      | ....    |
270 | parent  |     |           +-----------+         +---------+
271 | suk-keys|-----+
272 | values  |--------------------->+----------+
273 | SK-rec. |---------------+      | 1. value |--> +----------+
274 | class   |--+            |      +----------+    | vk-rec.  |
275 +---------+  |            |                      | ....     |
276              v            |                      | data     |--> +-------+
277       +------------+      |                      +----------+    | xxxxx |
278       | Class name |      |                                      +-------+
279       +------------+      |
280                           v
281           +---------+    +---------+
282    +----->| next sk |--->| Next sk |--+
283    |  +---| prev sk |<---| prev sk |  |
284    |  |   | ....    |    | ...     |  |
285    |  |   +---------+    +---------+  |
286    |  |                    ^          |
287    |  |                    |          |
288    |  +--------------------+          |
289    +----------------------------------+
290
291 ---------------------------------------------------------------------------
292
293 Hope this helps....  (Although it was "fun" for me to uncover this things,
294                   it took me several sleepless nights ;)
295
296             B.D.
297
298 *************************************************************************/
299
300 #include <stdio.h>
301 #include <stdlib.h>
302 #include <errno.h>
303 #include <assert.h>
304 #include <sys/types.h>
305 #include <sys/stat.h>
306 #include <unistd.h>
307 #include <sys/mman.h>
308 #include <string.h>
309 #include <fcntl.h>
310
311 static int verbose = 0;
312
313 /* 
314  * These definitions are for the in-memory registry structure.
315  * It is a tree structure that mimics what you see with tools like regedit
316  */
317
318 /*
319  * DateTime struct for Windows
320  */
321
322 typedef struct date_time_s {
323   unsigned int low, high;
324 } NTTIME;
325
326 /*
327  * Definition of a Key. It has a name, classname, date/time last modified,
328  * sub-keys, values, and a security descriptor
329  */
330
331 #define REG_ROOT_KEY 1
332 #define REG_SUB_KEY  2
333 #define REG_SYM_LINK 3
334
335 typedef struct reg_key_s {
336   char *name;         /* Name of the key                    */
337   char *class_name;
338   int type;           /* One of REG_ROOT_KEY or REG_SUB_KEY */
339   NTTIME last_mod; /* Time last modified                 */
340   struct reg_key_s *owner;
341   struct key_list_s *sub_keys;
342   struct val_list_s *values;
343   struct key_sec_desc_s *security;
344 } REG_KEY;
345
346 /*
347  * The KEY_LIST struct lists sub-keys.
348  */
349
350 typedef struct key_list_s {
351   int key_count;
352   REG_KEY *keys[1];
353 } KEY_LIST;
354
355 typedef struct val_key_s {
356   char *name;
357   int has_name;
358   int data_type;
359   int data_len;
360   void *data_blk;    /* Might want a separate block */
361 } VAL_KEY;
362
363 typedef struct val_list_s {
364   int val_count;
365   VAL_KEY *vals[1];
366 } VAL_LIST;
367
368 #ifndef MAXSUBAUTHS
369 #define MAXSUBAUTHS 15
370 #endif
371
372 typedef struct dom_sid_s {
373   unsigned char ver, auths;
374   unsigned char auth[6];
375   unsigned int sub_auths[MAXSUBAUTHS];
376 } DOM_SID;
377
378 typedef struct ace_struct_s {
379   unsigned char type, flags;
380   unsigned int perms;   /* Perhaps a better def is in order */
381   DOM_SID *trustee;
382 } ACE; 
383
384 typedef struct acl_struct_s {
385   unsigned short rev, refcnt;
386   unsigned short num_aces;
387   ACE *aces[1];
388 } ACL;
389
390 typedef struct sec_desc_s {
391   unsigned int rev, type;
392   DOM_SID *owner, *group;
393   ACL *sacl, *dacl;
394 } SEC_DESC;
395
396 #define SEC_DESC_NON 0
397 #define SEC_DESC_RES 1
398 #define SEC_DESC_OCU 2
399
400 typedef struct key_sec_desc_s {
401   struct key_sec_desc_s *prev, *next;
402   int ref_cnt;
403   int state;
404   SEC_DESC *sec_desc;
405 } KEY_SEC_DESC; 
406
407
408 /*
409  * An API for accessing/creating/destroying items above
410  */
411
412 /*
413  * Iterate over the keys, depth first, calling a function for each key
414  * and indicating if it is terminal or non-terminal and if it has values.
415  *
416  * In addition, for each value in the list, call a value list function
417  */
418
419 /*
420  * There should eventually be one to deal with security keys as well
421  */
422
423 typedef int (*key_print_f)(char *path, char *key_name, char *class_name, 
424                            int root, int terminal, int values);
425
426 typedef int (*val_print_f)(char *path, char *val_name, int val_type, 
427                            int data_len, void *data_blk, int terminal,
428                            int first, int last);
429
430 typedef int (*sec_print_f)(SEC_DESC *sec_desc);
431
432 typedef struct regf_struct_s REGF;
433
434 int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, char *path, 
435                     key_print_f key_print, sec_print_f sec_print,
436                     val_print_f val_print);
437
438 int nt_val_list_iterator(REGF *regf, VAL_LIST *val_list, int bf, char *path,
439                          int terminal, val_print_f val_print)
440 {
441   int i;
442
443   if (!val_list) return 1;
444
445   if (!val_print) return 1;
446
447   for (i=0; i<val_list->val_count; i++) {
448     if (!val_print(path, val_list->vals[i]->name, val_list->vals[i]->data_type,
449                    val_list->vals[i]->data_len, val_list->vals[i]->data_blk,
450                    terminal,
451                    (i == 0),
452                    (i == val_list->val_count))) {
453
454       return 0;
455
456     }
457   }
458
459   return 1;
460 }
461
462 int nt_key_list_iterator(REGF *regf, KEY_LIST *key_list, int bf, char *path,
463                          key_print_f key_print, sec_print_f sec_print, 
464                          val_print_f val_print)
465 {
466   int i;
467
468   if (!key_list) return 1;
469
470   for (i=0; i< key_list->key_count; i++) {
471     if (!nt_key_iterator(regf, key_list->keys[i], bf, path, key_print, 
472                          sec_print, val_print)) {
473       return 0;
474     }
475   }
476   return 1;
477 }
478
479 int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, char *path,
480                     key_print_f key_print, sec_print_f sec_print,
481                     val_print_f val_print)
482 {
483   int path_len = strlen(path);
484   char *new_path;
485
486   if (!regf || !key_tree)
487     return -1;
488
489   /* List the key first, then the values, then the sub-keys */
490
491   if (key_print) {
492
493     if (!(*key_print)(path, key_tree->name, 
494                       key_tree->class_name, 
495                       (key_tree->type == REG_ROOT_KEY),
496                       (key_tree->sub_keys == NULL),
497                       (key_tree->values?(key_tree->values->val_count):0)))
498       return 0;
499   }
500
501   /*
502    * If we have a security print routine, call it
503    */
504   if (sec_print) {
505     if (key_tree->security && !(*sec_print)(key_tree->security->sec_desc))
506       return 0;
507   }
508
509   new_path = (char *)malloc(path_len + 1 + strlen(key_tree->name) + 1);
510   if (!new_path) return 0; /* Errors? */
511   new_path[0] = '\0';
512   strcat(new_path, path);
513   strcat(new_path, "\\");
514   strcat(new_path, key_tree->name);
515
516   /*
517    * Now, iterate through the values in the val_list 
518    */
519
520   if (key_tree->values &&
521       !nt_val_list_iterator(regf, key_tree->values, bf, new_path, 
522                             (key_tree->values!=NULL),
523                             val_print)) {
524
525     free(new_path);
526     return 0;
527   } 
528
529   /* 
530    * Now, iterate through the keys in the key list
531    */
532
533   if (key_tree->sub_keys && 
534       !nt_key_list_iterator(regf, key_tree->sub_keys, bf, new_path, key_print, 
535                             sec_print, val_print)) {
536     free(new_path);
537     return 0;
538   } 
539
540   free(new_path);
541   return 1;
542 }
543
544 /* Make, delete keys */
545
546 int nt_delete_val_list(VAL_LIST *vl)
547 {
548
549   return 1;
550 }
551
552 int nt_delete_reg_key(REG_KEY *key)
553 {
554
555   return 1;
556 }
557
558 /* 
559  * Create/delete key lists and add delete keys to/from a list, count the keys 
560  */
561
562
563 /*
564  * Create/delete value lists, add/delete values, count them
565  */
566
567
568 /*
569  * Create/delete security descriptors, add/delete SIDS, count SIDS, etc.
570  * We reference count the security descriptors. Any new reference increments 
571  * the ref count. If we modify an SD, we copy the old one, dec the ref count
572  * and make the change. We also want to be able to check for equality so
573  * we can reduce the number of SDs in use.
574  */
575
576
577 /*
578  * Load and unload a registry file.
579  *
580  * Load, loads it into memory as a tree, while unload sealizes/flattens it
581  */
582
583 /*
584  * Get the starting record for NT Registry file 
585  */
586
587 /* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
588 typedef struct sk_map_s {
589   int sk_off;
590   KEY_SEC_DESC *key_sec_desc;
591 } SK_MAP;
592
593 /* 
594  * Where we keep all the regf stuff for one registry.
595  * This is the structure that we use to tie the in memory tree etc 
596  * together. By keeping separate structs, we can operate on different
597  * registries at the same time.
598  * Currently, the SK_MAP is an array of mapping structure.
599  * Since we only need this on input and output, we fill in the structure
600  * as we go on input. On output, we know how many SK items we have, so
601  * we can allocate the structure as we need to.
602  * If you add stuff here that is dynamically allocated, add the 
603  * appropriate free statements below.
604  */
605
606 #define REGF_REGTYPE_NONE 0
607 #define REGF_REGTYPE_NT   1
608 #define REGF_REGTYPE_W9X  2
609
610 #define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
611                               (r)->last_mod_time.high = (t2);
612
613 #define REGF_HDR_BLKSIZ 0x1000 
614
615 struct regf_struct_s {
616   int reg_type;
617   char *regfile_name, *outfile_name;
618   int fd;
619   struct stat sbuf;
620   char *base;
621   int modified;
622   NTTIME last_mod_time;
623   REG_KEY *root;  /* Root of the tree for this file */
624   int sk_count, sk_map_size;
625   SK_MAP *sk_map;
626 };
627
628 /*
629  * Structures for dealing with the on-disk format of the registry
630  */
631
632 #define IVAL(buf) ((unsigned int) \
633                    (unsigned int)*((unsigned char *)(buf)+3)<<24| \
634                    (unsigned int)*((unsigned char *)(buf)+2)<<16| \
635                    (unsigned int)*((unsigned char *)(buf)+1)<<8| \
636                    (unsigned int)*((unsigned char *)(buf)+0)) 
637
638 #define SVAL(buf) ((unsigned short) \
639                    (unsigned short)*((unsigned char *)(buf)+1)<<8| \
640                    (unsigned short)*((unsigned char *)(buf)+0)) 
641
642 #define CVAL(buf) ((unsigned char)*((unsigned char *)(buf)))
643
644 #define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4) 
645 #define LOCN(base, f) ((base) + OFF(f))
646
647 /* 
648  * All of the structures below actually have a four-byte lenght before them
649  * which always seems to be negative. The following macro retrieves that
650  * size as an integer
651  */
652
653 #define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
654
655 typedef unsigned int DWORD;
656 typedef unsigned short WORD;
657
658 #define REG_REGF_ID 0x66676572
659
660 typedef struct regf_block {
661   DWORD REGF_ID;     /* regf */
662   DWORD uk1;
663   DWORD uk2;
664   DWORD tim1, tim2;
665   DWORD uk3;             /* 1 */
666   DWORD uk4;             /* 3 */
667   DWORD uk5;             /* 0 */
668   DWORD uk6;             /* 1 */
669   DWORD first_key;       /* offset */
670   unsigned int dblk_size;
671   DWORD uk7[116];        /* 1 */
672   DWORD chksum;
673 } REGF_HDR;
674
675 typedef struct hbin_sub_struct {
676   DWORD dblocksize;
677   char data[1];
678 } HBIN_SUB_HDR;
679
680 #define REG_HBIN_ID 0x6E696268
681
682 typedef struct hbin_struct {
683   DWORD HBIN_ID; /* hbin */
684   DWORD next_off;
685   DWORD prev_off;
686   DWORD uk1;
687   DWORD uk2;
688   DWORD uk3;
689   DWORD uk4;
690   DWORD blk_size;
691   HBIN_SUB_HDR hbin_sub_hdr;
692 } HBIN_HDR;
693
694 #define REG_NK_ID 0x6B6E
695
696 typedef struct nk_struct {
697   WORD NK_ID;
698   WORD type;
699   DWORD t1, t2;
700   DWORD uk1;
701   DWORD own_off;
702   DWORD subk_num;
703   DWORD uk2;
704   DWORD lf_off;
705   DWORD uk3;
706   DWORD val_cnt;
707   DWORD val_off;
708   DWORD sk_off;
709   DWORD clsnam_off;
710   DWORD unk4[4];
711   DWORD unk5;
712   WORD nam_len;
713   WORD clsnam_len;
714   char key_nam[1];  /* Actual length determined by nam_len */
715 } NK_HDR;
716
717 #define REG_SK_ID 0x6B73
718
719 typedef struct sk_struct {
720   WORD SK_ID;
721   WORD uk1;
722   DWORD prev_off;
723   DWORD next_off;
724   DWORD ref_cnt;
725   DWORD rec_size;
726   char sec_desc[1];
727 } SK_HDR;
728
729 typedef struct ace_struct {
730     unsigned char type;
731     unsigned char flags;
732     unsigned short length;
733     unsigned int perms;
734     DOM_SID trustee;
735 } REG_ACE;
736
737 typedef struct acl_struct {
738   WORD rev;
739   WORD size;
740   DWORD num_aces;
741   REG_ACE *aces;   /* One or more ACEs */
742 } REG_ACL;
743
744 typedef struct sec_desc_rec {
745   WORD rev;
746   WORD type;
747   DWORD owner_off;
748   DWORD group_off;
749   DWORD sacl_off;
750   DWORD dacl_off;
751 } REG_SEC_DESC;
752
753 typedef struct hash_struct {
754   DWORD nk_off;
755   char hash[4];
756 } HASH_REC;
757
758 #define REG_LF_ID 0x666C
759
760 typedef struct lf_struct {
761   WORD LF_ID;
762   WORD key_count;
763   struct hash_struct hr[1];  /* Array of hash records, depending on key_count */
764 } LF_HDR;
765
766 typedef DWORD VL_TYPE[1];  /* Value list is an array of vk rec offsets */
767
768 #define REG_VK_ID 0x6B76
769
770 typedef struct vk_struct {
771   WORD VK_ID;
772   WORD nam_len;
773   DWORD dat_len;    /* If top-bit set, offset contains the data */
774   DWORD dat_off;   
775   DWORD dat_type;
776   WORD flag;        /* =1, has name, else no name (=Default). */
777   WORD unk1;
778   char dat_name[1]; /* Name starts here ... */
779 } VK_HDR;
780
781 #define REG_TYPE_REGSZ     1
782 #define REG_TYPE_EXPANDSZ  2
783 #define REG_TYPE_BIN       3  
784 #define REG_TYPE_DWORD     4
785 #define REG_TYPE_MULTISZ   7
786
787 typedef struct _val_str { 
788   unsigned int val;
789   char * str;
790 } VAL_STR;
791
792 VAL_STR reg_type_names[] = {
793    { 1, "REG_SZ" },
794    { 2, "REG_EXPAND_SZ" },
795    { 3, "REG_BIN" },
796    { 4, "REG_DWORD" },
797    { 7, "REG_MULTI_SZ" },
798    { 0, NULL },
799 };
800
801 char *val_to_str(unsigned int val, VAL_STR *val_array)
802 {
803   int i = 0;
804
805   if (!val_array) return NULL;
806
807   while (val_array[i].val && val_array[i].str) {
808
809     if (val_array[i].val == val) return val_array[i].str;
810     i++;
811
812   }
813
814   return NULL;
815
816 }
817
818 REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size);
819
820 int nt_set_regf_input_file(REGF *regf, char *filename)
821 {
822   return ((regf->regfile_name = strdup(filename)) != NULL); 
823 }
824
825 int nt_set_regf_output_file(REGF *regf, char *filename)
826 {
827   return ((regf->outfile_name = strdup(filename)) != NULL); 
828 }
829
830 /* Create a regf structure and init it */
831
832 REGF *nt_create_regf(void)
833 {
834   REGF *tmp = (REGF *)malloc(sizeof(REGF));
835   if (!tmp) return tmp;
836   bzero(tmp, sizeof(REGF));
837   return tmp;
838
839
840 /* Free all the bits and pieces ... Assumes regf was malloc'd */
841 /* If you add stuff to REGF, add the relevant free bits here  */
842 int nt_free_regf(REGF *regf)
843 {
844   if (!regf) return 0;
845
846   if (regf->regfile_name) free(regf->regfile_name);
847   if (regf->outfile_name) free(regf->outfile_name);
848
849   /* Free the mmap'd area */
850
851   if (regf->base) munmap(regf->base, regf->sbuf.st_size);
852   regf->base = NULL;
853   close(regf->fd);    /* Ignore the error :-) */
854
855   nt_delete_reg_key(regf->root); /* Free the tree */
856   free(regf->sk_map);
857   regf->sk_count = regf->sk_map_size = 0;
858
859   free(regf);
860
861   return 1;
862 }
863
864 /*
865  * Convert from UniCode to Ascii ... Does not take into account other lang
866  * Restrict by ascii_max if > 0
867  */
868 int uni_to_ascii(unsigned char *uni, unsigned char *ascii, int ascii_max, 
869                  int uni_max)
870 {
871   int i = 0; 
872
873   while (i < ascii_max && !(!uni[i*2] && !uni[i*2+1])) {
874     if (uni_max > 0 && (i*2) >= uni_max) break;
875     ascii[i] = uni[i*2];
876     i++;
877
878   }
879
880   ascii[i] = '\0';
881
882   return i;
883 }
884
885 /* Get the header of the registry. Return a pointer to the structure 
886  * If the mmap'd area has not been allocated, then mmap the input file
887  */
888 REGF_HDR *nt_get_regf_hdr(REGF *regf)
889 {
890   if (!regf)
891     return NULL; /* What about errors */
892
893   if (!regf->regfile_name)
894     return NULL; /* What about errors */
895
896   if (!regf->base) { /* Try to mmap etc the file */
897
898     if ((regf->fd = open(regf->regfile_name, O_RDONLY, 0000)) <0) {
899       return NULL; /* What about errors? */
900     }
901
902     if (fstat(regf->fd, &regf->sbuf) < 0) {
903       return NULL;
904     }
905
906     regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
907
908     if ((int)regf->base == 1) {
909       fprintf(stderr, "Could not mmap file: %s, %s\n", regf->regfile_name,
910               strerror(errno));
911       return NULL;
912     }
913   }
914
915   /* 
916    * At this point, regf->base != NULL, and we should be able to read the 
917    * header 
918    */
919
920   assert(regf->base != NULL);
921
922   return (REGF_HDR *)regf->base;
923 }
924
925 /*
926  * Validate a regf header
927  * For now, do nothing, but we should check the checksum
928  */
929 int valid_regf_hdr(REGF_HDR *regf_hdr)
930 {
931   if (!regf_hdr) return 0;
932
933   return 1;
934 }
935
936 /*
937  * Process an SK header ...
938  * Every time we see a new one, add it to the map. Otherwise, just look it up.
939  * We will do a simple linear search for the moment, since many KEYs have the 
940  * same security descriptor. 
941  * We allocate the map in increments of 10 entries.
942  */
943
944 /*
945  * Create a new entry in the map, and increase the size of the map if needed
946  */
947
948 SK_MAP *alloc_sk_map_entry(REGF *regf, KEY_SEC_DESC *tmp, int sk_off)
949 {
950  if (!regf->sk_map) { /* Allocate a block of 10 */
951     regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
952     if (!regf->sk_map) {
953       free(tmp);
954       return NULL;
955     }
956     regf->sk_map_size = 10;
957     regf->sk_count = 1;
958     (regf->sk_map)[0].sk_off = sk_off;
959     (regf->sk_map)[0].key_sec_desc = tmp;
960   }
961   else { /* Simply allocate a new slot, unless we have to expand the list */ 
962     int index = regf->sk_count;
963     if (regf->sk_count >= regf->sk_map_size) {
964       regf->sk_map = (SK_MAP *)realloc(regf->sk_map, 
965                                        (regf->sk_map_size + 10)*sizeof(SK_MAP));
966       if (!regf->sk_map) {
967         free(tmp);
968         return NULL;
969       }
970       /*
971        * Index already points at the first entry of the new block
972        */
973       regf->sk_map_size += 10;
974     }
975     (regf->sk_map)[index].sk_off = sk_off;
976     (regf->sk_map)[index].key_sec_desc = tmp;
977     regf->sk_count++;
978     if (regf->sk_map[2].key_sec_desc == 0x19) { /* Take us over */
979       fprintf(stderr, "%0x\n", regf->sk_map[7].key_sec_desc->sec_desc);
980     }
981   }
982  return regf->sk_map;
983 }
984
985 /*
986  * Search for a KEY_SEC_DESC in the sk_map, but dont create one if not
987  * found
988  */
989
990 KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
991 {
992   int i;
993
994   if (!sk_map) return NULL;
995
996   for (i = 0; i < count; i++) {
997
998     if (sk_map[i].sk_off == sk_off)
999       return sk_map[i].key_sec_desc;
1000
1001   }
1002
1003   return NULL;
1004
1005 }
1006
1007 /*
1008  * Allocate a KEY_SEC_DESC if we can't find one in the map
1009  */
1010
1011 KEY_SEC_DESC *lookup_create_sec_key(REGF *regf, SK_MAP *sk_map, int sk_off)
1012 {
1013   KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
1014
1015   if (tmp) {
1016     return tmp;
1017   }
1018   else { /* Allocate a new one */
1019     tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
1020     if (!tmp) {
1021       return NULL;
1022     }
1023     tmp->state = SEC_DESC_RES;
1024     if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
1025       return NULL;
1026     }
1027     return tmp;
1028   }
1029 }
1030
1031 /*
1032  * Allocate storage and duplicate a SID 
1033  * We could allocate the SID to be only the size needed, but I am too lazy. 
1034  */
1035 DOM_SID *dup_sid(DOM_SID *sid)
1036 {
1037   DOM_SID *tmp = (DOM_SID *)malloc(sizeof(DOM_SID));
1038   int i;
1039   
1040   if (!tmp) return NULL;
1041   tmp->ver = sid->ver;
1042   tmp->auths = sid->auths;
1043   for (i=0; i<6; i++) {
1044     tmp->auth[i] = sid->auth[i];
1045   }
1046   for (i=0; i<tmp->auths&&i<MAXSUBAUTHS; i++) {
1047     tmp->sub_auths[i] = sid->sub_auths[i];
1048   }
1049   return tmp;
1050 }
1051
1052 /*
1053  * Allocate space for an ACE and duplicate the registry encoded one passed in
1054  */
1055 ACE *dup_ace(REG_ACE *ace)
1056 {
1057   ACE *tmp = NULL; 
1058
1059   tmp = (ACE *)malloc(sizeof(ACE));
1060
1061   if (!tmp) return NULL;
1062
1063   tmp->type = CVAL(&ace->type);
1064   tmp->flags = CVAL(&ace->flags);
1065   tmp->perms = IVAL(&ace->perms);
1066   tmp->trustee = dup_sid(&ace->trustee);
1067   return tmp;
1068 }
1069
1070 /*
1071  * Allocate space for an ACL and duplicate the registry encoded one passed in 
1072  */
1073 ACL *dup_acl(REG_ACL *acl)
1074 {
1075   ACL *tmp = NULL;
1076   REG_ACE* ace;
1077   int i, num_aces;
1078
1079   num_aces = IVAL(&acl->num_aces);
1080
1081   tmp = (ACL *)malloc(sizeof(ACL) + (num_aces - 1)*sizeof(ACE *));
1082   if (!tmp) return NULL;
1083
1084   tmp->num_aces = num_aces;
1085   tmp->refcnt = 1;
1086   tmp->rev = SVAL(&acl->rev);
1087   ace = (REG_ACE *)&acl->aces;
1088   for (i=0; i<num_aces; i++) {
1089     tmp->aces[i] = dup_ace(ace);
1090     ace = (REG_ACE *)((char *)ace + SVAL(&ace->length));
1091     /* XXX: FIXME, should handle malloc errors */
1092   }
1093
1094   return tmp;
1095 }
1096
1097 SEC_DESC *process_sec_desc(REGF *regf, REG_SEC_DESC *sec_desc)
1098 {
1099   SEC_DESC *tmp = NULL;
1100   
1101   tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
1102
1103   if (!tmp) {
1104     return NULL;
1105   }
1106   
1107   tmp->rev = SVAL(&sec_desc->rev);
1108   tmp->type = SVAL(&sec_desc->type);
1109   tmp->owner = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->owner_off)));
1110   if (!tmp->owner) {
1111     free(tmp);
1112     return NULL;
1113   }
1114   tmp->group = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->group_off)));
1115   if (!tmp->group) {
1116     free(tmp);
1117     return NULL;
1118   }
1119
1120   /* Now pick up the SACL and DACL */
1121
1122   if (sec_desc->sacl_off)
1123     tmp->sacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->sacl_off)));
1124   else
1125     tmp->sacl = NULL;
1126
1127   if (sec_desc->dacl_off)
1128     tmp->dacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->dacl_off)));
1129   else
1130     tmp->dacl = NULL;
1131
1132   return tmp;
1133 }
1134
1135 KEY_SEC_DESC *process_sk(REGF *regf, SK_HDR *sk_hdr, int sk_off, int size)
1136 {
1137   KEY_SEC_DESC *tmp = NULL;
1138   int sk_next_off, sk_prev_off, sk_size;
1139   REG_SEC_DESC *sec_desc;
1140
1141   if (!sk_hdr) return NULL;
1142
1143   if (SVAL(&sk_hdr->SK_ID) != REG_SK_ID) {
1144     fprintf(stderr, "Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
1145             regf->regfile_name);
1146     return NULL;
1147   }
1148
1149   if (-size < (sk_size = IVAL(&sk_hdr->rec_size))) {
1150     fprintf(stderr, "Incorrect SK record size: %d vs %d. %s\n",
1151             -size, sk_size, regf->regfile_name);
1152     return NULL;
1153   }
1154
1155   /* 
1156    * Now, we need to look up the SK Record in the map, and return it
1157    * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
1158    * use that
1159    */
1160
1161   if (regf->sk_map &&
1162       ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
1163       && (tmp->state == SEC_DESC_OCU)) {
1164     tmp->ref_cnt++;
1165     return tmp;
1166   }
1167
1168   /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
1169
1170   assert(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
1171
1172   /*
1173    * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
1174    * new KEY_SEC_DESC to the mapping structure, since the offset supplied is 
1175    * the actual offset of structure. The same offset will be used by all
1176    * all future references to this structure
1177    * We chould put all this unpleasantness in a function.
1178    */
1179
1180   if (!tmp) {
1181     tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
1182     if (!tmp) return NULL;
1183     bzero(tmp, sizeof(KEY_SEC_DESC));
1184     
1185     /*
1186      * Allocate an entry in the SK_MAP ...
1187      * We don't need to free tmp, because that is done for us if the
1188      * sm_map entry can't be expanded when we need more space in the map.
1189      */
1190     
1191     if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
1192       return NULL;
1193     }
1194   }
1195
1196   tmp->ref_cnt++;
1197   tmp->state = SEC_DESC_OCU;
1198
1199   /*
1200    * Now, process the actual sec desc and plug the values in
1201    */
1202
1203   sec_desc = (REG_SEC_DESC *)&sk_hdr->sec_desc[0];
1204   tmp->sec_desc = process_sec_desc(regf, sec_desc);
1205
1206   /*
1207    * Now forward and back links. Here we allocate an entry in the sk_map
1208    * if it does not exist, and mark it reserved
1209    */
1210
1211   sk_prev_off = IVAL(&sk_hdr->prev_off);
1212   tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
1213   assert(tmp->prev != NULL);
1214   sk_next_off = IVAL(&sk_hdr->next_off);
1215   tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
1216   assert(tmp->next != NULL);
1217
1218   return tmp;
1219 }
1220
1221 /*
1222  * Process a VK header and return a value
1223  */
1224 VAL_KEY *process_vk(REGF *regf, VK_HDR *vk_hdr, int size)
1225 {
1226   char val_name[1024];
1227   int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
1228   char *val_type;
1229   VAL_KEY *tmp = NULL; 
1230
1231   if (!vk_hdr) return NULL;
1232
1233   if ((vk_id = SVAL(&vk_hdr->VK_ID)) != REG_VK_ID) {
1234     fprintf(stderr, "Unrecognized VK header ID: %0X, block: %0X, %s\n",
1235             vk_id, (int)vk_hdr, regf->regfile_name);
1236     return NULL;
1237   }
1238
1239   nam_len = SVAL(&vk_hdr->nam_len);
1240   val_name[nam_len] = '\0';
1241   flag = SVAL(&vk_hdr->flag);
1242   dat_type = IVAL(&vk_hdr->dat_type);
1243   dat_len = IVAL(&vk_hdr->dat_len);  /* If top bit, offset contains data */
1244   dat_off = IVAL(&vk_hdr->dat_off);
1245
1246   tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
1247   if (!tmp) {
1248     goto error;
1249   }
1250   bzero(tmp, sizeof(VAL_KEY));
1251   tmp->has_name = flag;
1252   tmp->data_type = dat_type;
1253
1254   if (flag & 0x01) {
1255     strncpy(val_name, vk_hdr->dat_name, nam_len);
1256     tmp->name = strdup(val_name);
1257     if (!tmp->name) {
1258       goto error;
1259     }
1260   }
1261   else
1262     strncpy(val_name, "<No Name>", 10);
1263
1264   /*
1265    * Allocate space and copy the data as a BLOB
1266    */
1267
1268   if (dat_len) {
1269     
1270     char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
1271     
1272     if (!dtmp) {
1273       goto error;
1274     }
1275
1276     tmp->data_blk = dtmp;
1277
1278     if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
1279       char *dat_ptr = LOCN(regf->base, dat_off);
1280       bcopy(dat_ptr, dtmp, dat_len);
1281     }
1282     else { /* The data is in the offset */
1283       dat_len = dat_len & 0x7FFFFFFF;
1284       bcopy(&dat_off, dtmp, dat_len);
1285     }
1286
1287   }
1288
1289   val_type = val_to_str(dat_type, reg_type_names);
1290
1291   /*
1292    * We need to save the data area as well
1293    */
1294
1295   if (verbose) fprintf(stdout, "  %s : %s : \n", val_name, val_type);
1296
1297   return tmp;
1298
1299  error:
1300   /* XXX: FIXME, free the partially allocated struct */
1301   return NULL;
1302
1303 }
1304
1305 /*
1306  * Process a VL Header and return a list of values
1307  */
1308 VAL_LIST *process_vl(REGF *regf, VL_TYPE vl, int count, int size)
1309 {
1310   int i, vk_off;
1311   VK_HDR *vk_hdr;
1312   VAL_LIST *tmp = NULL;
1313
1314   if (!vl) return NULL;
1315
1316   if (-size < (count+1)*sizeof(int)){
1317     fprintf(stderr, "Error in VL header format. Size less than space required. %d\n", -size);
1318     return NULL;
1319   }
1320
1321   tmp = (VAL_LIST *)malloc(sizeof(VAL_LIST) + (count - 1) * sizeof(VAL_KEY *));
1322   if (!tmp) {
1323     goto error;
1324   }
1325
1326   for (i=0; i<count; i++) {
1327     vk_off = IVAL(&vl[i]);
1328     vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
1329     tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr));
1330     if (!tmp->vals[i]){
1331       goto error;
1332     }
1333   }
1334
1335   tmp->val_count = count;
1336
1337   return tmp;
1338
1339  error:
1340   /* XXX: FIXME, free the partially allocated structure */
1341   return NULL;
1342
1343
1344 /*
1345  * Process an LF Header and return a list of sub-keys
1346  */
1347 KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size)
1348 {
1349   int count, i, nk_off;
1350   unsigned int lf_id;
1351   KEY_LIST *tmp;
1352
1353   if (!lf_hdr) return NULL;
1354
1355   if ((lf_id = SVAL(&lf_hdr->LF_ID)) != REG_LF_ID) {
1356     fprintf(stderr, "Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
1357             lf_id, (int)lf_hdr, regf->regfile_name);
1358     return NULL;
1359   }
1360
1361   assert(size < 0);
1362
1363   count = SVAL(&lf_hdr->key_count);
1364
1365   if (count <= 0) return NULL;
1366
1367   /* Now, we should allocate a KEY_LIST struct and fill it in ... */
1368
1369   tmp = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (count - 1) * sizeof(REG_KEY *));
1370   if (!tmp) {
1371     goto error;
1372   }
1373
1374   tmp->key_count = count;
1375
1376   for (i=0; i<count; i++) {
1377     NK_HDR *nk_hdr;
1378
1379     nk_off = IVAL(&lf_hdr->hr[i].nk_off);
1380     nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
1381     tmp->keys[i] = nt_get_key_tree(regf, nk_hdr, BLK_SIZE(nk_hdr));
1382     if (!tmp->keys[i]) {
1383       goto error;
1384     }
1385   }
1386
1387   return tmp;
1388
1389  error:
1390   /* XXX: FIXME, free the partially allocated structure */
1391   return NULL;
1392 }
1393
1394 /*
1395  * This routine is passed a NK_HDR pointer and retrieves the entire tree
1396  * from there down. It return a REG_KEY *.
1397  */
1398 REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size)
1399 {
1400   REG_KEY *tmp = NULL;
1401   int name_len, clsname_len, lf_off, val_off, val_count, sk_off;
1402   unsigned int nk_id;
1403   LF_HDR *lf_hdr;
1404   VL_TYPE *vl;
1405   SK_HDR *sk_hdr;
1406   char key_name[1024], cls_name[1024];
1407
1408   if (!nk_hdr) return NULL;
1409
1410   if ((nk_id = SVAL(&nk_hdr->NK_ID)) != REG_NK_ID) {
1411     fprintf(stderr, "Unrecognized NK Header format: %08X, Block: %0X. %s\n", 
1412             nk_id, (int)nk_hdr, regf->regfile_name);
1413     return NULL;
1414   }
1415
1416   assert(size < 0);
1417
1418   name_len = SVAL(&nk_hdr->nam_len);
1419   clsname_len = SVAL(&nk_hdr->clsnam_len);
1420
1421   /*
1422    * The value of -size should be ge 
1423    * (sizeof(NK_HDR) - 1 + name_len)
1424    * The -1 accounts for the fact that we included the first byte of 
1425    * the name in the structure. clsname_len is the length of the thing 
1426    * pointed to by clsnam_off
1427    */
1428
1429   if (-size < (sizeof(NK_HDR) - 1 + name_len)) {
1430     fprintf(stderr, "Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr);
1431     fprintf(stderr, "Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
1432             sizeof(NK_HDR), name_len, clsname_len);
1433     /*return NULL;*/
1434   }
1435
1436   if (verbose) fprintf(stdout, "NK HDR: Name len: %d, class name len: %d\n", 
1437                        name_len, clsname_len);
1438
1439   /* Fish out the key name and process the LF list */
1440
1441   assert(name_len < sizeof(key_name));
1442
1443   /* Allocate the key struct now */
1444   tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
1445   if (!tmp) return tmp;
1446   bzero(tmp, sizeof(REG_KEY));
1447
1448   tmp->type = (SVAL(&nk_hdr->type)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
1449   
1450   strncpy(key_name, nk_hdr->key_nam, name_len);
1451   key_name[name_len] = '\0';
1452
1453   if (verbose) fprintf(stdout, "Key name: %s\n", key_name);
1454
1455   tmp->name = strdup(key_name);
1456   if (!tmp->name) {
1457     goto error;
1458   }
1459
1460   /*
1461    * Fish out the class name, it is in UNICODE, while the key name is 
1462    * ASCII :-)
1463    */
1464
1465   if (clsname_len) { /* Just print in Ascii for now */
1466     char *clsnamep;
1467     int clsnam_off;
1468
1469     clsnam_off = IVAL(&nk_hdr->clsnam_off);
1470     clsnamep = LOCN(regf->base, clsnam_off);
1471  
1472     bzero(cls_name, clsname_len);
1473     uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len);
1474     
1475     /*
1476      * I am keeping class name as an ascii string for the moment.
1477      * That means it needs to be converted on output.
1478      * XXX: FIXME
1479      */
1480
1481     tmp->class_name = strdup(cls_name);
1482     if (!tmp->class_name) {
1483       goto error;
1484     }
1485
1486     if (verbose) fprintf(stdout, "  Class Name: %s\n", cls_name);
1487
1488   }
1489
1490   /*
1491    * If there are any values, process them here
1492    */
1493
1494   val_count = IVAL(&nk_hdr->val_cnt);
1495
1496   if (val_count) {
1497
1498     val_off = IVAL(&nk_hdr->val_off);
1499     vl = (VL_TYPE *)LOCN(regf->base, val_off);
1500
1501     tmp->values = process_vl(regf, *vl, val_count, BLK_SIZE(vl));
1502     if (!tmp->values) {
1503       goto error;
1504     }
1505
1506   }
1507
1508   /* 
1509    * Also handle the SK header ...
1510    */
1511
1512   sk_off = IVAL(&nk_hdr->sk_off);
1513   sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
1514
1515   if (sk_off != -1) {
1516
1517     tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
1518
1519   } 
1520
1521   lf_off = IVAL(&nk_hdr->lf_off);
1522
1523   /*
1524    * No more subkeys if lf_off == -1
1525    */
1526
1527   if (lf_off != -1) {
1528
1529     lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
1530     
1531     tmp->sub_keys = process_lf(regf, lf_hdr, BLK_SIZE(lf_hdr));
1532     if (!tmp->sub_keys){
1533       goto error;
1534     }
1535
1536   }
1537
1538   return tmp;
1539
1540  error:
1541   if (tmp) nt_delete_reg_key(tmp);
1542   return NULL;
1543 }
1544
1545 int nt_load_registry(REGF *regf)
1546 {
1547   REGF_HDR *regf_hdr;
1548   unsigned int regf_id, hbin_id;
1549   HBIN_HDR *hbin_hdr;
1550   NK_HDR *first_key;
1551
1552   /* Get the header */
1553
1554   if ((regf_hdr = nt_get_regf_hdr(regf)) == NULL) {
1555     return -1;
1556   }
1557
1558   /* Now process that header and start to read the rest in */
1559
1560   if ((regf_id = IVAL(&regf_hdr->REGF_ID)) != REG_REGF_ID) {
1561     fprintf(stderr, "Unrecognized NT registry header id: %0X, %s\n",
1562             regf_id, regf->regfile_name);
1563     return -1;
1564   }
1565
1566   /*
1567    * Validate the header ...
1568    */
1569   if (!valid_regf_hdr(regf_hdr)) {
1570     fprintf(stderr, "Registry file header does not validate: %s\n",
1571             regf->regfile_name);
1572     return -1;
1573   }
1574
1575   /* Update the last mod date, and then go get the first NK record and on */
1576
1577   TTTONTTIME(regf, IVAL(&regf_hdr->tim1), IVAL(&regf_hdr->tim2));
1578
1579   /* 
1580    * The hbin hdr seems to be just uninteresting garbage. Check that
1581    * it is there, but that is all.
1582    */
1583
1584   hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
1585
1586   if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID)) != REG_HBIN_ID) {
1587     fprintf(stderr, "Unrecognized registry hbin hdr ID: %0X, %s\n", 
1588             hbin_id, regf->regfile_name);
1589     return -1;
1590   } 
1591
1592   /*
1593    * Get a pointer to the first key from the hreg_hdr
1594    */
1595
1596   first_key = (NK_HDR *)LOCN(regf->base, IVAL(&regf_hdr->first_key));
1597
1598   /*
1599    * Now, get the registry tree by processing that NK recursively
1600    */
1601
1602   regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key));
1603
1604   assert(regf->root != NULL);
1605
1606   return 1;
1607 }
1608
1609 /*
1610  * Main code from here on ...
1611  */
1612
1613 /*
1614  * key print function here ...
1615  */
1616
1617 int print_key(char *path, char *name, char *class_name, int root, 
1618               int terminal, int vals)
1619 {
1620
1621   if (terminal) fprintf(stdout, "%s\\%s\n", path, name);
1622
1623   return 1;
1624 }
1625
1626 /*
1627  * Sec Desc print functions 
1628  */
1629
1630 void print_sid(DOM_SID *sid)
1631 {
1632   int i, comps = sid->auths;
1633   fprintf(stdout, "S-%u-%u", sid->ver, sid->auth[5]);
1634
1635   for (i = 0; i < comps; i++) {
1636
1637     fprintf(stdout, "-%u", sid->sub_auths[i]);
1638
1639   }
1640   fprintf(stdout, "\n");
1641 }
1642
1643 int print_sec(SEC_DESC *sec_desc)
1644 {
1645
1646   fprintf(stdout, "  SECURITY\n");
1647   fprintf(stdout, "    Owner: ");
1648   print_sid(sec_desc->owner);
1649   fprintf(stdout, "    Group: ");
1650   print_sid(sec_desc->group);
1651   return 1;
1652 }
1653
1654 /*
1655  * Value print function here ...
1656  */
1657 int print_val(char *path, char *val_name, int val_type, int data_len, 
1658               void *data_blk, int terminal, int first, int last)
1659 {
1660   if (!terminal && first)
1661     fprintf(stdout, "%s\n", path);
1662   fprintf(stdout, "  %s : %s : \n", (val_name?val_name:"<No Name>"), 
1663                    val_to_str(val_type, reg_type_names));
1664   return 1;
1665 }
1666
1667 void usage(void)
1668 {
1669   fprintf(stderr, "Usage: editreg [-v] [-k] <registryfile>\n");
1670   fprintf(stderr, "Version: 0.1\n\n");
1671   fprintf(stderr, "\n\t-v\t sets verbose mode");
1672 }
1673
1674 int main(int argc, char *argv[])
1675 {
1676   REGF *regf;
1677   extern char *optarg;
1678   extern int optind;
1679   int opt;
1680
1681   if (argc < 2) {
1682     usage();
1683     exit(1);
1684   }
1685   
1686   /* 
1687    * Now, process the arguments
1688    */
1689
1690   while ((opt = getopt(argc, argv, "vk")) != EOF) {
1691     switch (opt) {
1692     case 'v':
1693       verbose++;
1694       break;
1695
1696     case 'k':
1697       break;
1698
1699     default:
1700       usage();
1701       exit(1);
1702       break;
1703     }
1704   }
1705
1706   if ((regf = nt_create_regf()) == NULL) {
1707     fprintf(stderr, "Could not create registry object: %s\n", strerror(errno));
1708     exit(2);
1709   }
1710
1711   if (!nt_set_regf_input_file(regf, argv[optind])) {
1712     fprintf(stderr, "Could not set name of registry file: %s, %s\n", 
1713             argv[1], strerror(errno));
1714     exit(3);
1715   }
1716
1717   /* Now, open it, and bring it into memory :-) */
1718
1719   if (nt_load_registry(regf) < 0) {
1720     fprintf(stderr, "Could not load registry: %s\n", argv[1]);
1721     exit(4);
1722   }
1723
1724   /*
1725    * At this point, we should have a registry in memory and should be able
1726    * to iterate over it.
1727    */
1728
1729   nt_key_iterator(regf, regf->root, 0, "", print_key, print_sec, print_val);
1730   return 0;
1731 }
1732