Split "clobber" function and variables into its own file before it
[kai/samba.git] / source / 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)(const char *path, char *key_name, char *class_name, 
424                            int root, int terminal, int values);
425
426 typedef int (*val_print_f)(const 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, const 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, 
463                          const char *path,
464                          key_print_f key_print, sec_print_f sec_print, 
465                          val_print_f val_print)
466 {
467   int i;
468
469   if (!key_list) return 1;
470
471   for (i=0; i< key_list->key_count; i++) {
472     if (!nt_key_iterator(regf, key_list->keys[i], bf, path, key_print, 
473                          sec_print, val_print)) {
474       return 0;
475     }
476   }
477   return 1;
478 }
479
480 int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
481                     key_print_f key_print, sec_print_f sec_print,
482                     val_print_f val_print)
483 {
484   int path_len = strlen(path);
485   char *new_path;
486
487   if (!regf || !key_tree)
488     return -1;
489
490   /* List the key first, then the values, then the sub-keys */
491
492   if (key_print) {
493
494     if (!(*key_print)(path, key_tree->name, 
495                       key_tree->class_name, 
496                       (key_tree->type == REG_ROOT_KEY),
497                       (key_tree->sub_keys == NULL),
498                       (key_tree->values?(key_tree->values->val_count):0)))
499       return 0;
500   }
501
502   /*
503    * If we have a security print routine, call it
504    * If the security print routine returns false, stop.
505    */
506   if (sec_print) {
507     if (key_tree->security && !(*sec_print)(key_tree->security->sec_desc))
508       return 0;
509   }
510
511   new_path = (char *)malloc(path_len + 1 + strlen(key_tree->name) + 1);
512   if (!new_path) return 0; /* Errors? */
513   new_path[0] = '\0';
514   strcat(new_path, path);
515   strcat(new_path, "\\");
516   strcat(new_path, key_tree->name);
517
518   /*
519    * Now, iterate through the values in the val_list 
520    */
521
522   if (key_tree->values &&
523       !nt_val_list_iterator(regf, key_tree->values, bf, new_path, 
524                             (key_tree->values!=NULL),
525                             val_print)) {
526
527     free(new_path);
528     return 0;
529   } 
530
531   /* 
532    * Now, iterate through the keys in the key list
533    */
534
535   if (key_tree->sub_keys && 
536       !nt_key_list_iterator(regf, key_tree->sub_keys, bf, new_path, key_print, 
537                             sec_print, val_print)) {
538     free(new_path);
539     return 0;
540   } 
541
542   free(new_path);
543   return 1;
544 }
545
546 /* Make, delete keys */
547
548 int nt_delete_val_key(VAL_KEY *val_key)
549 {
550
551   if (val_key) {
552     if (val_key->data_blk) free(val_key->data_blk);
553     free(val_key);
554   };
555   return 1;
556 }
557
558 int nt_delete_val_list(VAL_LIST *vl)
559 {
560   int i;
561
562   if (vl) {
563     for (i=0; i<vl->val_count; i++)
564       nt_delete_val_key(vl->vals[i]);
565     free(vl);
566   }
567   return 1;
568 }
569
570 int nt_delete_reg_key(REG_KEY *key);
571 int nt_delete_key_list(KEY_LIST *key_list)
572 {
573   int i;
574
575   if (key_list) {
576     for (i=0; i<key_list->key_count; i++) 
577       nt_delete_reg_key(key_list->keys[i]);
578     free(key_list);
579   }
580   return 1;
581 }
582
583 int nt_delete_sid(DOM_SID *sid)
584 {
585
586   if (sid) free(sid);
587   return 1;
588
589 }
590
591 int nt_delete_ace(ACE *ace)
592 {
593
594   if (ace) {
595     nt_delete_sid(ace->trustee);
596     free(ace);
597   }
598   return 1;
599
600 }
601
602 int nt_delete_acl(ACL *acl)
603 {
604
605   if (acl) {
606     int i;
607
608     for (i=0; i<acl->num_aces; i++)
609       nt_delete_ace(acl->aces[i]);
610
611     free(acl);
612   }
613   return 1;
614 }
615
616 int nt_delete_sec_desc(SEC_DESC *sec_desc)
617 {
618
619   if (sec_desc) {
620
621     nt_delete_sid(sec_desc->owner);
622     nt_delete_sid(sec_desc->group);
623     nt_delete_acl(sec_desc->sacl);
624     nt_delete_acl(sec_desc->dacl);
625     free(sec_desc);
626
627   }
628   return 1;
629 }
630
631 int nt_delete_key_sec_desc(KEY_SEC_DESC *key_sec_desc)
632 {
633
634   if (key_sec_desc) {
635     key_sec_desc->ref_cnt--;
636     if (key_sec_desc->ref_cnt<=0) {
637       /*
638        * There should always be a next and prev, even if they point to us 
639        */
640       key_sec_desc->next->prev = key_sec_desc->prev;
641       key_sec_desc->prev->next = key_sec_desc->next;
642       nt_delete_sec_desc(key_sec_desc->sec_desc);
643     }
644   }
645   return 1;
646 }
647
648 int nt_delete_reg_key(REG_KEY *key)
649 {
650
651   if (key) {
652     if (key->name) free(key->name);
653     if (key->class_name) free(key->class_name);
654
655     /*
656      * Do not delete the owner ...
657      */
658
659     if (key->sub_keys) nt_delete_key_list(key->sub_keys);
660     if (key->values) nt_delete_val_list(key->values);
661     if (key->security) nt_delete_key_sec_desc(key->security);
662     free(key);
663   }
664   return 1;
665 }
666
667 /* 
668  * Create/delete key lists and add delete keys to/from a list, count the keys 
669  */
670
671
672 /*
673  * Create/delete value lists, add/delete values, count them
674  */
675
676
677 /*
678  * Create/delete security descriptors, add/delete SIDS, count SIDS, etc.
679  * We reference count the security descriptors. Any new reference increments 
680  * the ref count. If we modify an SD, we copy the old one, dec the ref count
681  * and make the change. We also want to be able to check for equality so
682  * we can reduce the number of SDs in use.
683  */
684
685 /*
686  * Code to parse registry specification from command line or files
687  *
688  * Format:
689  * [cmd:]key:type:value
690  *
691  * cmd = a|d|c|add|delete|change|as|ds|cs
692  *
693  */
694
695
696 /*
697  * Load and unload a registry file.
698  *
699  * Load, loads it into memory as a tree, while unload sealizes/flattens it
700  */
701
702 /*
703  * Get the starting record for NT Registry file 
704  */
705
706 /* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
707 typedef struct sk_map_s {
708   int sk_off;
709   KEY_SEC_DESC *key_sec_desc;
710 } SK_MAP;
711
712 /* 
713  * Where we keep all the regf stuff for one registry.
714  * This is the structure that we use to tie the in memory tree etc 
715  * together. By keeping separate structs, we can operate on different
716  * registries at the same time.
717  * Currently, the SK_MAP is an array of mapping structure.
718  * Since we only need this on input and output, we fill in the structure
719  * as we go on input. On output, we know how many SK items we have, so
720  * we can allocate the structure as we need to.
721  * If you add stuff here that is dynamically allocated, add the 
722  * appropriate free statements below.
723  */
724
725 #define REGF_REGTYPE_NONE 0
726 #define REGF_REGTYPE_NT   1
727 #define REGF_REGTYPE_W9X  2
728
729 #define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
730                               (r)->last_mod_time.high = (t2);
731
732 #define REGF_HDR_BLKSIZ 0x1000 
733
734 struct regf_struct_s {
735   int reg_type;
736   char *regfile_name, *outfile_name;
737   int fd;
738   struct stat sbuf;
739   char *base;
740   int modified;
741   NTTIME last_mod_time;
742   REG_KEY *root;  /* Root of the tree for this file */
743   int sk_count, sk_map_size;
744   SK_MAP *sk_map;
745 };
746
747 /*
748  * Structures for dealing with the on-disk format of the registry
749  */
750
751 #define IVAL(buf) ((unsigned int) \
752                    (unsigned int)*((unsigned char *)(buf)+3)<<24| \
753                    (unsigned int)*((unsigned char *)(buf)+2)<<16| \
754                    (unsigned int)*((unsigned char *)(buf)+1)<<8| \
755                    (unsigned int)*((unsigned char *)(buf)+0)) 
756
757 #define SVAL(buf) ((unsigned short) \
758                    (unsigned short)*((unsigned char *)(buf)+1)<<8| \
759                    (unsigned short)*((unsigned char *)(buf)+0)) 
760
761 #define CVAL(buf) ((unsigned char)*((unsigned char *)(buf)))
762
763 #define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4) 
764 #define LOCN(base, f) ((base) + OFF(f))
765
766 /* 
767  * All of the structures below actually have a four-byte lenght before them
768  * which always seems to be negative. The following macro retrieves that
769  * size as an integer
770  */
771
772 #define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
773
774 typedef unsigned int DWORD;
775 typedef unsigned short WORD;
776
777 #define REG_REGF_ID 0x66676572
778
779 typedef struct regf_block {
780   DWORD REGF_ID;     /* regf */
781   DWORD uk1;
782   DWORD uk2;
783   DWORD tim1, tim2;
784   DWORD uk3;             /* 1 */
785   DWORD uk4;             /* 3 */
786   DWORD uk5;             /* 0 */
787   DWORD uk6;             /* 1 */
788   DWORD first_key;       /* offset */
789   unsigned int dblk_size;
790   DWORD uk7[116];        /* 1 */
791   DWORD chksum;
792 } REGF_HDR;
793
794 typedef struct hbin_sub_struct {
795   DWORD dblocksize;
796   char data[1];
797 } HBIN_SUB_HDR;
798
799 #define REG_HBIN_ID 0x6E696268
800
801 typedef struct hbin_struct {
802   DWORD HBIN_ID; /* hbin */
803   DWORD next_off;
804   DWORD prev_off;
805   DWORD uk1;
806   DWORD uk2;
807   DWORD uk3;
808   DWORD uk4;
809   DWORD blk_size;
810   HBIN_SUB_HDR hbin_sub_hdr;
811 } HBIN_HDR;
812
813 #define REG_NK_ID 0x6B6E
814
815 typedef struct nk_struct {
816   WORD NK_ID;
817   WORD type;
818   DWORD t1, t2;
819   DWORD uk1;
820   DWORD own_off;
821   DWORD subk_num;
822   DWORD uk2;
823   DWORD lf_off;
824   DWORD uk3;
825   DWORD val_cnt;
826   DWORD val_off;
827   DWORD sk_off;
828   DWORD clsnam_off;
829   DWORD unk4[4];
830   DWORD unk5;
831   WORD nam_len;
832   WORD clsnam_len;
833   char key_nam[1];  /* Actual length determined by nam_len */
834 } NK_HDR;
835
836 #define REG_SK_ID 0x6B73
837
838 typedef struct sk_struct {
839   WORD SK_ID;
840   WORD uk1;
841   DWORD prev_off;
842   DWORD next_off;
843   DWORD ref_cnt;
844   DWORD rec_size;
845   char sec_desc[1];
846 } SK_HDR;
847
848 typedef struct ace_struct {
849     unsigned char type;
850     unsigned char flags;
851     unsigned short length;
852     unsigned int perms;
853     DOM_SID trustee;
854 } REG_ACE;
855
856 typedef struct acl_struct {
857   WORD rev;
858   WORD size;
859   DWORD num_aces;
860   REG_ACE *aces;   /* One or more ACEs */
861 } REG_ACL;
862
863 typedef struct sec_desc_rec {
864   WORD rev;
865   WORD type;
866   DWORD owner_off;
867   DWORD group_off;
868   DWORD sacl_off;
869   DWORD dacl_off;
870 } REG_SEC_DESC;
871
872 typedef struct hash_struct {
873   DWORD nk_off;
874   char hash[4];
875 } HASH_REC;
876
877 #define REG_LF_ID 0x666C
878
879 typedef struct lf_struct {
880   WORD LF_ID;
881   WORD key_count;
882   struct hash_struct hr[1];  /* Array of hash records, depending on key_count */
883 } LF_HDR;
884
885 typedef DWORD VL_TYPE[1];  /* Value list is an array of vk rec offsets */
886
887 #define REG_VK_ID 0x6B76
888
889 typedef struct vk_struct {
890   WORD VK_ID;
891   WORD nam_len;
892   DWORD dat_len;    /* If top-bit set, offset contains the data */
893   DWORD dat_off;   
894   DWORD dat_type;
895   WORD flag;        /* =1, has name, else no name (=Default). */
896   WORD unk1;
897   char dat_name[1]; /* Name starts here ... */
898 } VK_HDR;
899
900 #define REG_TYPE_REGSZ     1
901 #define REG_TYPE_EXPANDSZ  2
902 #define REG_TYPE_BIN       3  
903 #define REG_TYPE_DWORD     4
904 #define REG_TYPE_MULTISZ   7
905
906 typedef struct _val_str { 
907   unsigned int val;
908   const char * str;
909 } VAL_STR;
910
911 const VAL_STR reg_type_names[] = {
912    { 1, "REG_SZ" },
913    { 2, "REG_EXPAND_SZ" },
914    { 3, "REG_BIN" },
915    { 4, "REG_DWORD" },
916    { 7, "REG_MULTI_SZ" },
917    { 0, NULL },
918 };
919
920 const char *val_to_str(unsigned int val, const VAL_STR *val_array)
921 {
922   int i = 0;
923
924   if (!val_array) return NULL;
925
926   while (val_array[i].val && val_array[i].str) {
927
928     if (val_array[i].val == val) return val_array[i].str;
929     i++;
930
931   }
932
933   return NULL;
934
935 }
936
937 /*
938  * Convert from UniCode to Ascii ... Does not take into account other lang
939  * Restrict by ascii_max if > 0
940  */
941 int 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     if (uni_max > 0 && (i*2) >= uni_max) break;
948     ascii[i] = uni[i*2];
949     i++;
950
951   }
952
953   ascii[i] = '\0';
954
955   return i;
956 }
957
958 /*
959  * Convert a data value to a string for display
960  */
961 int data_to_ascii(unsigned char *datap, int len, int type, char *ascii, int ascii_max)
962
963   unsigned char *asciip;
964   int i;
965
966   switch (type) {
967   case REG_TYPE_REGSZ:
968     fprintf(stderr, "Len: %d\n", len);
969     return uni_to_ascii(datap, ascii, len, ascii_max);
970     break;
971
972   case REG_TYPE_EXPANDSZ:
973     return uni_to_ascii(datap, ascii, len, ascii_max);
974     break;
975
976   case REG_TYPE_BIN:
977     asciip = ascii;
978     for (i=0; (i<len)&&(i+1)*3<ascii_max; i++) { 
979       int str_rem = ascii_max - ((int)asciip - (int)ascii);
980       asciip += snprintf(asciip, str_rem, "%02x", *(unsigned char *)(datap+i));
981       if (i < len && str_rem > 0)
982         *asciip = ' '; asciip++;        
983     }
984     *asciip = '\0';
985     return ((int)asciip - (int)ascii);
986     break;
987
988   case REG_TYPE_DWORD:
989     if (*(int *)datap == 0)
990       return snprintf(ascii, ascii_max, "0");
991     else
992       return snprintf(ascii, ascii_max, "0x%x", *(int *)datap);
993     break;
994
995   case REG_TYPE_MULTISZ:
996
997     break;
998
999   default:
1000     return 0;
1001     break;
1002   } 
1003
1004   return len;
1005
1006 }
1007
1008 REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size);
1009
1010 int nt_set_regf_input_file(REGF *regf, char *filename)
1011 {
1012   return ((regf->regfile_name = strdup(filename)) != NULL); 
1013 }
1014
1015 int nt_set_regf_output_file(REGF *regf, char *filename)
1016 {
1017   return ((regf->outfile_name = strdup(filename)) != NULL); 
1018 }
1019
1020 /* Create a regf structure and init it */
1021
1022 REGF *nt_create_regf(void)
1023 {
1024   REGF *tmp = (REGF *)malloc(sizeof(REGF));
1025   if (!tmp) return tmp;
1026   bzero(tmp, sizeof(REGF));
1027   return tmp;
1028
1029
1030 /* Free all the bits and pieces ... Assumes regf was malloc'd */
1031 /* If you add stuff to REGF, add the relevant free bits here  */
1032 int nt_free_regf(REGF *regf)
1033 {
1034   if (!regf) return 0;
1035
1036   if (regf->regfile_name) free(regf->regfile_name);
1037   if (regf->outfile_name) free(regf->outfile_name);
1038
1039   /* Free the mmap'd area */
1040
1041   if (regf->base) munmap(regf->base, regf->sbuf.st_size);
1042   regf->base = NULL;
1043   close(regf->fd);    /* Ignore the error :-) */
1044
1045   nt_delete_reg_key(regf->root); /* Free the tree */
1046   free(regf->sk_map);
1047   regf->sk_count = regf->sk_map_size = 0;
1048
1049   free(regf);
1050
1051   return 1;
1052 }
1053
1054 /* Get the header of the registry. Return a pointer to the structure 
1055  * If the mmap'd area has not been allocated, then mmap the input file
1056  */
1057 REGF_HDR *nt_get_regf_hdr(REGF *regf)
1058 {
1059   if (!regf)
1060     return NULL; /* What about errors */
1061
1062   if (!regf->regfile_name)
1063     return NULL; /* What about errors */
1064
1065   if (!regf->base) { /* Try to mmap etc the file */
1066
1067     if ((regf->fd = open(regf->regfile_name, O_RDONLY, 0000)) <0) {
1068       return NULL; /* What about errors? */
1069     }
1070
1071     if (fstat(regf->fd, &regf->sbuf) < 0) {
1072       return NULL;
1073     }
1074
1075     regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
1076
1077     if ((int)regf->base == 1) {
1078       fprintf(stderr, "Could not mmap file: %s, %s\n", regf->regfile_name,
1079               strerror(errno));
1080       return NULL;
1081     }
1082   }
1083
1084   /* 
1085    * At this point, regf->base != NULL, and we should be able to read the 
1086    * header 
1087    */
1088
1089   assert(regf->base != NULL);
1090
1091   return (REGF_HDR *)regf->base;
1092 }
1093
1094 /*
1095  * Validate a regf header
1096  * For now, do nothing, but we should check the checksum
1097  */
1098 int valid_regf_hdr(REGF_HDR *regf_hdr)
1099 {
1100   if (!regf_hdr) return 0;
1101
1102   return 1;
1103 }
1104
1105 /*
1106  * Process an SK header ...
1107  * Every time we see a new one, add it to the map. Otherwise, just look it up.
1108  * We will do a simple linear search for the moment, since many KEYs have the 
1109  * same security descriptor. 
1110  * We allocate the map in increments of 10 entries.
1111  */
1112
1113 /*
1114  * Create a new entry in the map, and increase the size of the map if needed
1115  */
1116
1117 SK_MAP *alloc_sk_map_entry(REGF *regf, KEY_SEC_DESC *tmp, int sk_off)
1118 {
1119  if (!regf->sk_map) { /* Allocate a block of 10 */
1120     regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
1121     if (!regf->sk_map) {
1122       free(tmp);
1123       return NULL;
1124     }
1125     regf->sk_map_size = 10;
1126     regf->sk_count = 1;
1127     (regf->sk_map)[0].sk_off = sk_off;
1128     (regf->sk_map)[0].key_sec_desc = tmp;
1129   }
1130   else { /* Simply allocate a new slot, unless we have to expand the list */ 
1131     int ndx = regf->sk_count;
1132     if (regf->sk_count >= regf->sk_map_size) {
1133       regf->sk_map = (SK_MAP *)realloc(regf->sk_map, 
1134                                        (regf->sk_map_size + 10)*sizeof(SK_MAP));
1135       if (!regf->sk_map) {
1136         free(tmp);
1137         return NULL;
1138       }
1139       /*
1140        * ndx already points at the first entry of the new block
1141        */
1142       regf->sk_map_size += 10;
1143     }
1144     (regf->sk_map)[ndx].sk_off = sk_off;
1145     (regf->sk_map)[ndx].key_sec_desc = tmp;
1146     regf->sk_count++;
1147   }
1148  return regf->sk_map;
1149 }
1150
1151 /*
1152  * Search for a KEY_SEC_DESC in the sk_map, but dont create one if not
1153  * found
1154  */
1155
1156 KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
1157 {
1158   int i;
1159
1160   if (!sk_map) return NULL;
1161
1162   for (i = 0; i < count; i++) {
1163
1164     if (sk_map[i].sk_off == sk_off)
1165       return sk_map[i].key_sec_desc;
1166
1167   }
1168
1169   return NULL;
1170
1171 }
1172
1173 /*
1174  * Allocate a KEY_SEC_DESC if we can't find one in the map
1175  */
1176
1177 KEY_SEC_DESC *lookup_create_sec_key(REGF *regf, SK_MAP *sk_map, int sk_off)
1178 {
1179   KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
1180
1181   if (tmp) {
1182     return tmp;
1183   }
1184   else { /* Allocate a new one */
1185     tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
1186     if (!tmp) {
1187       return NULL;
1188     }
1189     tmp->state = SEC_DESC_RES;
1190     if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
1191       return NULL;
1192     }
1193     return tmp;
1194   }
1195 }
1196
1197 /*
1198  * Allocate storage and duplicate a SID 
1199  * We could allocate the SID to be only the size needed, but I am too lazy. 
1200  */
1201 DOM_SID *dup_sid(DOM_SID *sid)
1202 {
1203   DOM_SID *tmp = (DOM_SID *)malloc(sizeof(DOM_SID));
1204   int i;
1205   
1206   if (!tmp) return NULL;
1207   tmp->ver = sid->ver;
1208   tmp->auths = sid->auths;
1209   for (i=0; i<6; i++) {
1210     tmp->auth[i] = sid->auth[i];
1211   }
1212   for (i=0; i<tmp->auths&&i<MAXSUBAUTHS; i++) {
1213     tmp->sub_auths[i] = sid->sub_auths[i];
1214   }
1215   return tmp;
1216 }
1217
1218 /*
1219  * Allocate space for an ACE and duplicate the registry encoded one passed in
1220  */
1221 ACE *dup_ace(REG_ACE *ace)
1222 {
1223   ACE *tmp = NULL; 
1224
1225   tmp = (ACE *)malloc(sizeof(ACE));
1226
1227   if (!tmp) return NULL;
1228
1229   tmp->type = CVAL(&ace->type);
1230   tmp->flags = CVAL(&ace->flags);
1231   tmp->perms = IVAL(&ace->perms);
1232   tmp->trustee = dup_sid(&ace->trustee);
1233   return tmp;
1234 }
1235
1236 /*
1237  * Allocate space for an ACL and duplicate the registry encoded one passed in 
1238  */
1239 ACL *dup_acl(REG_ACL *acl)
1240 {
1241   ACL *tmp = NULL;
1242   REG_ACE* ace;
1243   int i, num_aces;
1244
1245   num_aces = IVAL(&acl->num_aces);
1246
1247   tmp = (ACL *)malloc(sizeof(ACL) + (num_aces - 1)*sizeof(ACE *));
1248   if (!tmp) return NULL;
1249
1250   tmp->num_aces = num_aces;
1251   tmp->refcnt = 1;
1252   tmp->rev = SVAL(&acl->rev);
1253   ace = (REG_ACE *)&acl->aces;
1254   for (i=0; i<num_aces; i++) {
1255     tmp->aces[i] = dup_ace(ace);
1256     ace = (REG_ACE *)((char *)ace + SVAL(&ace->length));
1257     /* XXX: FIXME, should handle malloc errors */
1258   }
1259
1260   return tmp;
1261 }
1262
1263 SEC_DESC *process_sec_desc(REGF *regf, REG_SEC_DESC *sec_desc)
1264 {
1265   SEC_DESC *tmp = NULL;
1266   
1267   tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
1268
1269   if (!tmp) {
1270     return NULL;
1271   }
1272   
1273   tmp->rev = SVAL(&sec_desc->rev);
1274   tmp->type = SVAL(&sec_desc->type);
1275   tmp->owner = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->owner_off)));
1276   if (!tmp->owner) {
1277     free(tmp);
1278     return NULL;
1279   }
1280   tmp->group = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->group_off)));
1281   if (!tmp->group) {
1282     free(tmp);
1283     return NULL;
1284   }
1285
1286   /* Now pick up the SACL and DACL */
1287
1288   if (sec_desc->sacl_off)
1289     tmp->sacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->sacl_off)));
1290   else
1291     tmp->sacl = NULL;
1292
1293   if (sec_desc->dacl_off)
1294     tmp->dacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->dacl_off)));
1295   else
1296     tmp->dacl = NULL;
1297
1298   return tmp;
1299 }
1300
1301 KEY_SEC_DESC *process_sk(REGF *regf, SK_HDR *sk_hdr, int sk_off, int size)
1302 {
1303   KEY_SEC_DESC *tmp = NULL;
1304   int sk_next_off, sk_prev_off, sk_size;
1305   REG_SEC_DESC *sec_desc;
1306
1307   if (!sk_hdr) return NULL;
1308
1309   if (SVAL(&sk_hdr->SK_ID) != REG_SK_ID) {
1310     fprintf(stderr, "Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
1311             regf->regfile_name);
1312     return NULL;
1313   }
1314
1315   if (-size < (sk_size = IVAL(&sk_hdr->rec_size))) {
1316     fprintf(stderr, "Incorrect SK record size: %d vs %d. %s\n",
1317             -size, sk_size, regf->regfile_name);
1318     return NULL;
1319   }
1320
1321   /* 
1322    * Now, we need to look up the SK Record in the map, and return it
1323    * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
1324    * use that
1325    */
1326
1327   if (regf->sk_map &&
1328       ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
1329       && (tmp->state == SEC_DESC_OCU)) {
1330     tmp->ref_cnt++;
1331     return tmp;
1332   }
1333
1334   /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
1335
1336   assert(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
1337
1338   /*
1339    * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
1340    * new KEY_SEC_DESC to the mapping structure, since the offset supplied is 
1341    * the actual offset of structure. The same offset will be used by all
1342    * all future references to this structure
1343    * We chould put all this unpleasantness in a function.
1344    */
1345
1346   if (!tmp) {
1347     tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
1348     if (!tmp) return NULL;
1349     bzero(tmp, sizeof(KEY_SEC_DESC));
1350     
1351     /*
1352      * Allocate an entry in the SK_MAP ...
1353      * We don't need to free tmp, because that is done for us if the
1354      * sm_map entry can't be expanded when we need more space in the map.
1355      */
1356     
1357     if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
1358       return NULL;
1359     }
1360   }
1361
1362   tmp->ref_cnt++;
1363   tmp->state = SEC_DESC_OCU;
1364
1365   /*
1366    * Now, process the actual sec desc and plug the values in
1367    */
1368
1369   sec_desc = (REG_SEC_DESC *)&sk_hdr->sec_desc[0];
1370   tmp->sec_desc = process_sec_desc(regf, sec_desc);
1371
1372   /*
1373    * Now forward and back links. Here we allocate an entry in the sk_map
1374    * if it does not exist, and mark it reserved
1375    */
1376
1377   sk_prev_off = IVAL(&sk_hdr->prev_off);
1378   tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
1379   assert(tmp->prev != NULL);
1380   sk_next_off = IVAL(&sk_hdr->next_off);
1381   tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
1382   assert(tmp->next != NULL);
1383
1384   return tmp;
1385 }
1386
1387 /*
1388  * Process a VK header and return a value
1389  */
1390 VAL_KEY *process_vk(REGF *regf, VK_HDR *vk_hdr, int size)
1391 {
1392   char val_name[1024];
1393   int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
1394   const char *val_type;
1395   VAL_KEY *tmp = NULL; 
1396
1397   if (!vk_hdr) return NULL;
1398
1399   if ((vk_id = SVAL(&vk_hdr->VK_ID)) != REG_VK_ID) {
1400     fprintf(stderr, "Unrecognized VK header ID: %0X, block: %0X, %s\n",
1401             vk_id, (int)vk_hdr, regf->regfile_name);
1402     return NULL;
1403   }
1404
1405   nam_len = SVAL(&vk_hdr->nam_len);
1406   val_name[nam_len] = '\0';
1407   flag = SVAL(&vk_hdr->flag);
1408   dat_type = IVAL(&vk_hdr->dat_type);
1409   dat_len = IVAL(&vk_hdr->dat_len);  /* If top bit, offset contains data */
1410   dat_off = IVAL(&vk_hdr->dat_off);
1411
1412   tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
1413   if (!tmp) {
1414     goto error;
1415   }
1416   bzero(tmp, sizeof(VAL_KEY));
1417   tmp->has_name = flag;
1418   tmp->data_type = dat_type;
1419
1420   if (flag & 0x01) {
1421     strncpy(val_name, vk_hdr->dat_name, nam_len);
1422     tmp->name = strdup(val_name);
1423     if (!tmp->name) {
1424       goto error;
1425     }
1426   }
1427   else
1428     strncpy(val_name, "<No Name>", 10);
1429
1430   /*
1431    * Allocate space and copy the data as a BLOB
1432    */
1433
1434   if (dat_len) {
1435     
1436     char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
1437     
1438     if (!dtmp) {
1439       goto error;
1440     }
1441
1442     tmp->data_blk = dtmp;
1443
1444     if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
1445       char *dat_ptr = LOCN(regf->base, dat_off);
1446       bcopy(dat_ptr, dtmp, dat_len);
1447     }
1448     else { /* The data is in the offset */
1449       dat_len = dat_len & 0x7FFFFFFF;
1450       bcopy(&dat_off, dtmp, dat_len);
1451     }
1452
1453     tmp->data_len = dat_len;
1454   }
1455
1456   val_type = val_to_str(dat_type, reg_type_names);
1457
1458   /*
1459    * We need to save the data area as well
1460    */
1461
1462   if (verbose) fprintf(stdout, "  %s : %s : \n", val_name, val_type);
1463
1464   return tmp;
1465
1466  error:
1467   /* XXX: FIXME, free the partially allocated struct */
1468   return NULL;
1469
1470 }
1471
1472 /*
1473  * Process a VL Header and return a list of values
1474  */
1475 VAL_LIST *process_vl(REGF *regf, VL_TYPE vl, int count, int size)
1476 {
1477   int i, vk_off;
1478   VK_HDR *vk_hdr;
1479   VAL_LIST *tmp = NULL;
1480
1481   if (!vl) return NULL;
1482
1483   if (-size < (count+1)*sizeof(int)){
1484     fprintf(stderr, "Error in VL header format. Size less than space required. %d\n", -size);
1485     return NULL;
1486   }
1487
1488   tmp = (VAL_LIST *)malloc(sizeof(VAL_LIST) + (count - 1) * sizeof(VAL_KEY *));
1489   if (!tmp) {
1490     goto error;
1491   }
1492
1493   for (i=0; i<count; i++) {
1494     vk_off = IVAL(&vl[i]);
1495     vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
1496     tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr));
1497     if (!tmp->vals[i]){
1498       goto error;
1499     }
1500   }
1501
1502   tmp->val_count = count;
1503
1504   return tmp;
1505
1506  error:
1507   /* XXX: FIXME, free the partially allocated structure */
1508   return NULL;
1509
1510
1511 /*
1512  * Process an LF Header and return a list of sub-keys
1513  */
1514 KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size)
1515 {
1516   int count, i, nk_off;
1517   unsigned int lf_id;
1518   KEY_LIST *tmp;
1519
1520   if (!lf_hdr) return NULL;
1521
1522   if ((lf_id = SVAL(&lf_hdr->LF_ID)) != REG_LF_ID) {
1523     fprintf(stderr, "Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
1524             lf_id, (int)lf_hdr, regf->regfile_name);
1525     return NULL;
1526   }
1527
1528   assert(size < 0);
1529
1530   count = SVAL(&lf_hdr->key_count);
1531
1532   if (count <= 0) return NULL;
1533
1534   /* Now, we should allocate a KEY_LIST struct and fill it in ... */
1535
1536   tmp = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (count - 1) * sizeof(REG_KEY *));
1537   if (!tmp) {
1538     goto error;
1539   }
1540
1541   tmp->key_count = count;
1542
1543   for (i=0; i<count; i++) {
1544     NK_HDR *nk_hdr;
1545
1546     nk_off = IVAL(&lf_hdr->hr[i].nk_off);
1547     nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
1548     tmp->keys[i] = nt_get_key_tree(regf, nk_hdr, BLK_SIZE(nk_hdr));
1549     if (!tmp->keys[i]) {
1550       goto error;
1551     }
1552   }
1553
1554   return tmp;
1555
1556  error:
1557   /* XXX: FIXME, free the partially allocated structure */
1558   return NULL;
1559 }
1560
1561 /*
1562  * This routine is passed a NK_HDR pointer and retrieves the entire tree
1563  * from there down. It return a REG_KEY *.
1564  */
1565 REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size)
1566 {
1567   REG_KEY *tmp = NULL;
1568   int name_len, clsname_len, lf_off, val_off, val_count, sk_off;
1569   unsigned int nk_id;
1570   LF_HDR *lf_hdr;
1571   VL_TYPE *vl;
1572   SK_HDR *sk_hdr;
1573   char key_name[1024], cls_name[1024];
1574
1575   if (!nk_hdr) return NULL;
1576
1577   if ((nk_id = SVAL(&nk_hdr->NK_ID)) != REG_NK_ID) {
1578     fprintf(stderr, "Unrecognized NK Header format: %08X, Block: %0X. %s\n", 
1579             nk_id, (int)nk_hdr, regf->regfile_name);
1580     return NULL;
1581   }
1582
1583   assert(size < 0);
1584
1585   name_len = SVAL(&nk_hdr->nam_len);
1586   clsname_len = SVAL(&nk_hdr->clsnam_len);
1587
1588   /*
1589    * The value of -size should be ge 
1590    * (sizeof(NK_HDR) - 1 + name_len)
1591    * The -1 accounts for the fact that we included the first byte of 
1592    * the name in the structure. clsname_len is the length of the thing 
1593    * pointed to by clsnam_off
1594    */
1595
1596   if (-size < (sizeof(NK_HDR) - 1 + name_len)) {
1597     fprintf(stderr, "Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr);
1598     fprintf(stderr, "Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
1599             sizeof(NK_HDR), name_len, clsname_len);
1600     /*return NULL;*/
1601   }
1602
1603   if (verbose) fprintf(stdout, "NK HDR: Name len: %d, class name len: %d\n", 
1604                        name_len, clsname_len);
1605
1606   /* Fish out the key name and process the LF list */
1607
1608   assert(name_len < sizeof(key_name));
1609
1610   /* Allocate the key struct now */
1611   tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
1612   if (!tmp) return tmp;
1613   bzero(tmp, sizeof(REG_KEY));
1614
1615   tmp->type = (SVAL(&nk_hdr->type)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
1616   
1617   strncpy(key_name, nk_hdr->key_nam, name_len);
1618   key_name[name_len] = '\0';
1619
1620   if (verbose) fprintf(stdout, "Key name: %s\n", key_name);
1621
1622   tmp->name = strdup(key_name);
1623   if (!tmp->name) {
1624     goto error;
1625   }
1626
1627   /*
1628    * Fish out the class name, it is in UNICODE, while the key name is 
1629    * ASCII :-)
1630    */
1631
1632   if (clsname_len) { /* Just print in Ascii for now */
1633     char *clsnamep;
1634     int clsnam_off;
1635
1636     clsnam_off = IVAL(&nk_hdr->clsnam_off);
1637     clsnamep = LOCN(regf->base, clsnam_off);
1638  
1639     bzero(cls_name, clsname_len);
1640     uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len);
1641     
1642     /*
1643      * I am keeping class name as an ascii string for the moment.
1644      * That means it needs to be converted on output.
1645      * XXX: FIXME
1646      */
1647
1648     tmp->class_name = strdup(cls_name);
1649     if (!tmp->class_name) {
1650       goto error;
1651     }
1652
1653     if (verbose) fprintf(stdout, "  Class Name: %s\n", cls_name);
1654
1655   }
1656
1657   /*
1658    * If there are any values, process them here
1659    */
1660
1661   val_count = IVAL(&nk_hdr->val_cnt);
1662
1663   if (val_count) {
1664
1665     val_off = IVAL(&nk_hdr->val_off);
1666     vl = (VL_TYPE *)LOCN(regf->base, val_off);
1667
1668     tmp->values = process_vl(regf, *vl, val_count, BLK_SIZE(vl));
1669     if (!tmp->values) {
1670       goto error;
1671     }
1672
1673   }
1674
1675   /* 
1676    * Also handle the SK header ...
1677    */
1678
1679   sk_off = IVAL(&nk_hdr->sk_off);
1680   sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
1681
1682   if (sk_off != -1) {
1683
1684     tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
1685
1686   } 
1687
1688   lf_off = IVAL(&nk_hdr->lf_off);
1689
1690   /*
1691    * No more subkeys if lf_off == -1
1692    */
1693
1694   if (lf_off != -1) {
1695
1696     lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
1697     
1698     tmp->sub_keys = process_lf(regf, lf_hdr, BLK_SIZE(lf_hdr));
1699     if (!tmp->sub_keys){
1700       goto error;
1701     }
1702
1703   }
1704
1705   return tmp;
1706
1707  error:
1708   if (tmp) nt_delete_reg_key(tmp);
1709   return NULL;
1710 }
1711
1712 int nt_load_registry(REGF *regf)
1713 {
1714   REGF_HDR *regf_hdr;
1715   unsigned int regf_id, hbin_id;
1716   HBIN_HDR *hbin_hdr;
1717   NK_HDR *first_key;
1718
1719   /* Get the header */
1720
1721   if ((regf_hdr = nt_get_regf_hdr(regf)) == NULL) {
1722     return -1;
1723   }
1724
1725   /* Now process that header and start to read the rest in */
1726
1727   if ((regf_id = IVAL(&regf_hdr->REGF_ID)) != REG_REGF_ID) {
1728     fprintf(stderr, "Unrecognized NT registry header id: %0X, %s\n",
1729             regf_id, regf->regfile_name);
1730     return -1;
1731   }
1732
1733   /*
1734    * Validate the header ...
1735    */
1736   if (!valid_regf_hdr(regf_hdr)) {
1737     fprintf(stderr, "Registry file header does not validate: %s\n",
1738             regf->regfile_name);
1739     return -1;
1740   }
1741
1742   /* Update the last mod date, and then go get the first NK record and on */
1743
1744   TTTONTTIME(regf, IVAL(&regf_hdr->tim1), IVAL(&regf_hdr->tim2));
1745
1746   /* 
1747    * The hbin hdr seems to be just uninteresting garbage. Check that
1748    * it is there, but that is all.
1749    */
1750
1751   hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
1752
1753   if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID)) != REG_HBIN_ID) {
1754     fprintf(stderr, "Unrecognized registry hbin hdr ID: %0X, %s\n", 
1755             hbin_id, regf->regfile_name);
1756     return -1;
1757   } 
1758
1759   /*
1760    * Get a pointer to the first key from the hreg_hdr
1761    */
1762
1763   first_key = (NK_HDR *)LOCN(regf->base, IVAL(&regf_hdr->first_key));
1764
1765   /*
1766    * Now, get the registry tree by processing that NK recursively
1767    */
1768
1769   regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key));
1770
1771   assert(regf->root != NULL);
1772
1773   return 1;
1774 }
1775
1776 /*
1777  * Routines to parse a REGEDIT4 file
1778  * 
1779  * The file consists of:
1780  * 
1781  * REGEDIT4
1782  * \[[-]key-path\]\n
1783  * <value-spec>*
1784  *
1785  * There can be more than one key-path and value-spec.
1786  *
1787  * Since we want to support more than one type of file format, we
1788  * construct a command-file structure that keeps info about the command file
1789  */
1790
1791 #define FMT_UNREC -1
1792 #define FMT_REGEDIT4 0
1793 #define FMT_EDITREG1_1 1
1794
1795 typedef struct command_s {
1796   int cmd;
1797   char *key;
1798   void *val_spec_list;
1799 } CMD;
1800
1801 /*
1802  * We seek to offset 0, read in the required number of bytes, 
1803  * and compare to the correct value.
1804  * We then seek back to the original location
1805  */
1806 int regedit4_file_type(int fd)
1807 {
1808   int cur_ofs = 0;
1809
1810   cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
1811   if (cur_ofs < 0) {
1812     fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
1813     exit(1);
1814   }
1815
1816   if (cur_ofs) {
1817     lseek(fd, 0, SEEK_SET);
1818   }
1819
1820   return FMT_UNREC;
1821 }
1822
1823 CMD *regedit4_get_cmd(int fd)
1824 {
1825   return NULL;
1826 }
1827
1828 int regedit4_exec_cmd(CMD *cmd)
1829 {
1830
1831   return 0;
1832 }
1833
1834 int editreg_1_1_file_type(int fd)
1835 {
1836
1837   return FMT_UNREC;
1838 }
1839
1840 CMD *editreg_1_1_get_cmd(int fd)
1841 {
1842   return NULL;
1843 }
1844
1845 int editreg_1_1_exec_cmd(CMD *cmd)
1846 {
1847
1848   return -1;
1849 }
1850
1851 typedef struct command_ops_s {
1852   int type;
1853   int (*file_type)(int fd);
1854   CMD *(*get_cmd)(int fd);
1855   int (*exec_cmd)(CMD *cmd);
1856 } CMD_OPS;
1857
1858 CMD_OPS default_cmd_ops[] = {
1859   {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd},
1860   {1, editreg_1_1_file_type, editreg_1_1_get_cmd, editreg_1_1_exec_cmd},
1861   {-1,  NULL, NULL, NULL}
1862 }; 
1863
1864 typedef struct command_file_s {
1865   char *name;
1866   int type, fd;
1867   CMD_OPS cmd_ops;
1868 } CMD_FILE;
1869
1870 /*
1871  * Create a new command file structure
1872  */
1873
1874 CMD_FILE *cmd_file_create(char *file)
1875 {
1876   CMD_FILE *tmp;
1877   struct stat sbuf;
1878   int i = 0;
1879
1880   /*
1881    * Let's check if the file exists ...
1882    * No use creating the cmd_file structure if the file does not exist
1883    */
1884
1885   if (stat(file, &sbuf) < 0) { /* Not able to access file */
1886
1887     return NULL;
1888   }
1889
1890   tmp = (CMD_FILE *)malloc(sizeof(CMD_FILE)); 
1891   if (!tmp) {
1892     return NULL;
1893   }
1894
1895   /*
1896    * Let's fill in some of the fields;
1897    */
1898
1899   tmp->name = strdup(file);
1900
1901   if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) {
1902     free(tmp);
1903     return NULL;
1904   }
1905
1906   /*
1907    * Now, try to find the format by indexing through the table
1908    */
1909   while (default_cmd_ops[i].type != -1) {
1910     if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) {
1911       tmp->cmd_ops = default_cmd_ops[i];
1912       return tmp;
1913     }
1914     i++;
1915   }
1916
1917   /* 
1918    * If we got here, return NULL, as we could not figure out the type
1919    * of command file.
1920    *
1921    * What about errors? 
1922    */
1923
1924   free(tmp);
1925   return NULL;
1926 }
1927
1928 /*
1929  * Extract commands from the command file, and execute them.
1930  * We pass a table of command callbacks for that 
1931  */
1932
1933 /*
1934  * Main code from here on ...
1935  */
1936
1937 /*
1938  * key print function here ...
1939  */
1940
1941 int print_key(const char *path, char *name, char *class_name, int root, 
1942               int terminal, int vals)
1943 {
1944
1945   if (terminal) fprintf(stdout, "%s\\%s\n", path, name);
1946
1947   return 1;
1948 }
1949
1950 /*
1951  * Sec Desc print functions 
1952  */
1953
1954 void print_sid(DOM_SID *sid)
1955 {
1956   int i, comps = sid->auths;
1957   fprintf(stdout, "S-%u-%u", sid->ver, sid->auth[5]);
1958
1959   for (i = 0; i < comps; i++) {
1960
1961     fprintf(stdout, "-%u", sid->sub_auths[i]);
1962
1963   }
1964   fprintf(stdout, "\n");
1965 }
1966
1967 int print_sec(SEC_DESC *sec_desc)
1968 {
1969
1970   fprintf(stdout, "  SECURITY\n");
1971   fprintf(stdout, "    Owner: ");
1972   print_sid(sec_desc->owner);
1973   fprintf(stdout, "    Group: ");
1974   print_sid(sec_desc->group);
1975   return 1;
1976 }
1977
1978 /*
1979  * Value print function here ...
1980  */
1981 int print_val(const char *path, char *val_name, int val_type, int data_len, 
1982               void *data_blk, int terminal, int first, int last)
1983 {
1984   char data_asc[1024];
1985
1986   bzero(data_asc, sizeof(data_asc));
1987   if (!terminal && first)
1988     fprintf(stdout, "%s\n", path);
1989   data_to_ascii((unsigned char *)data_blk, data_len, val_type, data_asc, 
1990                 sizeof(data_asc) - 1);
1991   fprintf(stdout, "  %s : %s : %s\n", (val_name?val_name:"<No Name>"), 
1992                    val_to_str(val_type, reg_type_names), data_asc);
1993   return 1;
1994 }
1995
1996 void usage(void)
1997 {
1998   fprintf(stderr, "Usage: editreg [-v] [-k] [-c <command-file>] <registryfile>\n");
1999   fprintf(stderr, "Version: 0.1\n\n");
2000   fprintf(stderr, "\n\t-v\t sets verbose mode");
2001   fprintf(stderr, "\n\t-c <command-file>\t specifies a command file");
2002   fprintf(stderr, "\n");
2003 }
2004
2005 int main(int argc, char *argv[])
2006 {
2007   REGF *regf;
2008   extern char *optarg;
2009   extern int optind;
2010   int opt;
2011   int commands = 0;
2012   char *cmd_file = NULL;
2013
2014   if (argc < 2) {
2015     usage();
2016     exit(1);
2017   }
2018   
2019   /* 
2020    * Now, process the arguments
2021    */
2022
2023   while ((opt = getopt(argc, argv, "vkc:")) != EOF) {
2024     switch (opt) {
2025     case 'c':
2026       commands = 1;
2027       cmd_file = optarg;
2028       break;
2029
2030     case 'v':
2031       verbose++;
2032       break;
2033
2034     case 'k':
2035       break;
2036
2037     default:
2038       usage();
2039       exit(1);
2040       break;
2041     }
2042   }
2043
2044   if ((regf = nt_create_regf()) == NULL) {
2045     fprintf(stderr, "Could not create registry object: %s\n", strerror(errno));
2046     exit(2);
2047   }
2048
2049   if (!nt_set_regf_input_file(regf, argv[optind])) {
2050     fprintf(stderr, "Could not set name of registry file: %s, %s\n", 
2051             argv[1], strerror(errno));
2052     exit(3);
2053   }
2054
2055   /* Now, open it, and bring it into memory :-) */
2056
2057   if (nt_load_registry(regf) < 0) {
2058     fprintf(stderr, "Could not load registry: %s\n", argv[1]);
2059     exit(4);
2060   }
2061
2062   /*
2063    * At this point, we should have a registry in memory and should be able
2064    * to iterate over it.
2065    */
2066
2067   nt_key_iterator(regf, regf->root, 0, "", print_key, print_sec, print_val);
2068   return 0;
2069 }