Replace all use of bzero with memset ...
[tprouty/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! 
95 (Richard Sharpe) 
96
97 The data is stored as one record per block. Block size is a multiple
98 of 4 and the last block reaches the next hbin-block, leaving no room.
99
100 (That also seems incorrect, in that the block size if a multiple of 8.
101 That is, the block, including the 4 byte header, is always a multiple of
102 8 bytes. Richard Sharpe.)
103
104 Records in the hbin-blocks
105 ==========================
106
107 nk-Record
108
109       The nk-record can be treated as a kombination of tree-record and
110       key-record of the win 95 registry.
111
112 lf-Record
113
114       The lf-record is the counterpart to the RGKN-record (the
115       hash-function)
116
117 vk-Record
118
119       The vk-record consists information to a single value.
120
121 sk-Record
122
123       sk (? Security Key ?) is the ACL of the registry.
124
125 Value-Lists
126
127       The value-lists contain information about which values are inside a
128       sub-key and don't have a header.
129
130 Datas
131
132       The datas of the registry are (like the value-list) stored without a
133       header.
134
135 All offset-values are relative to the first hbin-block and point to the
136 block-size field of the record-entry. to get the file offset, you have to add
137 the header size (4kb) and the size field (4 bytes)...
138
139 the nk-Record
140 =============
141 Offset      Size      Contents
142 0x0000      Word      ID: ASCII-"nk" = 0x6B6E
143 0x0002      Word      for the root-key: 0x2C, otherwise 0x20  //key symbolic links 0x10. Nigel
144 0x0004      Q-Word      write-date/time in windows nt notation
145 0x0010      D-Word      Offset of Owner/Parent key
146 0x0014      D-Word      number of sub-Keys
147 0x001C      D-Word      Offset of the sub-key lf-Records
148 0x0024      D-Word      number of values
149 0x0028      D-Word      Offset of the Value-List
150 0x002C      D-Word      Offset of the sk-Record
151
152 0x0030      D-Word      Offset of the Class-Name //see NK structure for the use of these fields. Nigel
153 0x0044      D-Word      Unused (data-trash)  //some kind of run time index. Does not appear to be important. Nigel
154 0x0048      Word      name-length
155 0x004A      Word      class-name length
156 0x004C      ????      key-name
157
158 the Value-List
159 ==============
160 Offset      Size      Contents
161 0x0000      D-Word      Offset 1st Value
162 0x0004      D-Word      Offset 2nd Value
163 0x????      D-Word      Offset nth Value
164
165 To determine the number of values, you have to look at the owner-nk-record!
166
167 Der vk-Record
168 =============
169 Offset      Size      Contents
170 0x0000      Word      ID: ASCII-"vk" = 0x6B76
171 0x0002      Word      name length
172 0x0004      D-Word      length of the data   //if top bit is set when offset contains data. Nigel
173 0x0008      D-Word      Offset of Data
174 0x000C      D-Word      Type of value
175 0x0010      Word      Flag
176 0x0012      Word      Unused (data-trash)
177 0x0014      ????      Name
178
179 If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
180
181 If the data-size is lower 5, the data-offset value is used to store the data itself!
182
183 The data-types
184 ==============
185 Wert      Beteutung
186 0x0001      RegSZ:             character string (in UNICODE!)
187 0x0002      ExpandSZ:   string with "%var%" expanding (UNICODE!)
188 0x0003      RegBin:           raw-binary value
189 0x0004      RegDWord:   Dword
190 0x0007      RegMultiSZ:      multiple strings, seperated with 0
191                   (UNICODE!)
192
193 The "lf"-record
194 ===============
195 Offset      Size      Contents
196 0x0000      Word      ID: ASCII-"lf" = 0x666C
197 0x0002      Word      number of keys
198 0x0004      ????      Hash-Records
199
200 Hash-Record
201 ===========
202 Offset      Size      Contents
203 0x0000      D-Word      Offset of corresponding "nk"-Record
204 0x0004      D-Word      ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
205
206 Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the 
207 key-name you have to change the hash-value too!
208
209 //These hashrecords must be sorted low to high within the lf record. Nigel.
210
211 The "sk"-block
212 ==============
213 (due to the complexity of the SAM-info, not clear jet)
214 (This is just a self-relative security descriptor in the data. R Sharpe.) 
215
216
217 Offset      Size      Contents
218 0x0000      Word      ID: ASCII-"sk" = 0x6B73
219 0x0002      Word      Unused
220 0x0004      D-Word      Offset of previous "sk"-Record
221 0x0008      D-Word      Offset of next "sk"-Record
222 0x000C      D-Word      usage-counter
223 0x0010      D-Word      Size of "sk"-record in bytes
224 ????                                             //standard self
225 relative security desciptor. Nigel
226 ????  ????      Security and auditing settings...
227 ????
228
229 The usage counter counts the number of references to this
230 "sk"-record. You can use one "sk"-record for the entire registry!
231
232 Windows nt date/time format
233 ===========================
234 The time-format is a 64-bit integer which is incremented every
235 0,0000001 seconds by 1 (I don't know how accurate it realy is!)
236 It starts with 0 at the 1st of january 1601 0:00! All values are
237 stored in GMT time! The time-zone is important to get the real
238 time!
239
240 Common values for win95 and win-nt
241 ==================================
242 Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
243 If a value has no name (length=0, flag(bit 0)=0), it is treated as the
244 "Default" entry...
245 If a value has no data (length=0), it is displayed as empty.
246
247 simplyfied win-3.?? registry:
248 =============================
249
250 +-----------+
251 | next rec. |---+                      +----->+------------+
252 | first sub |   |                      |      | Usage cnt. |
253 | name      |   |  +-->+------------+  |      | length     |
254 | value     |   |  |   | next rec.  |  |      | text       |------->+-------+
255 +-----------+   |  |   | name rec.  |--+      +------------+        | xxxxx |
256    +------------+  |   | value rec. |-------->+------------+        +-------+
257    v               |   +------------+         | Usage cnt. |
258 +-----------+      |                          | length     |
259 | next rec. |      |                          | text       |------->+-------+
260 | first sub |------+                          +------------+        | xxxxx |
261 | name      |                                                       +-------+
262 | value     |
263 +-----------+    
264
265 Greatly simplyfied structure of the nt-registry:
266 ================================================
267    
268 +---------------------------------------------------------------+
269 |                                                               |
270 v                                                               |
271 +---------+     +---------->+-----------+  +----->+---------+   |
272 | "nk"    |     |           | lf-rec.   |  |      | nk-rec. |   |
273 | ID      |     |           | # of keys |  |      | parent  |---+
274 | Date    |     |           | 1st key   |--+      | ....    |
275 | parent  |     |           +-----------+         +---------+
276 | suk-keys|-----+
277 | values  |--------------------->+----------+
278 | SK-rec. |---------------+      | 1. value |--> +----------+
279 | class   |--+            |      +----------+    | vk-rec.  |
280 +---------+  |            |                      | ....     |
281              v            |                      | data     |--> +-------+
282       +------------+      |                      +----------+    | xxxxx |
283       | Class name |      |                                      +-------+
284       +------------+      |
285                           v
286           +---------+    +---------+
287    +----->| next sk |--->| Next sk |--+
288    |  +---| prev sk |<---| prev sk |  |
289    |  |   | ....    |    | ...     |  |
290    |  |   +---------+    +---------+  |
291    |  |                    ^          |
292    |  |                    |          |
293    |  +--------------------+          |
294    +----------------------------------+
295
296 ---------------------------------------------------------------------------
297
298 Hope this helps....  (Although it was "fun" for me to uncover this things,
299                   it took me several sleepless nights ;)
300
301             B.D.
302
303 *************************************************************************/
304
305 #include <stdio.h>
306 #include <stdlib.h>
307 #include <errno.h>
308 #include <assert.h>
309 #include <sys/types.h>
310 #include <sys/stat.h>
311 #include <unistd.h>
312 #include <sys/mman.h>
313 #include <string.h>
314 #include <fcntl.h>
315
316 #define False 0
317 #define True 1
318 #define REG_KEY_LIST_SIZE 10
319
320 /*
321  * Structures for dealing with the on-disk format of the registry
322  */
323
324 #define IVAL(buf) ((unsigned int) \
325                    (unsigned int)*((unsigned char *)(buf)+3)<<24| \
326                    (unsigned int)*((unsigned char *)(buf)+2)<<16| \
327                    (unsigned int)*((unsigned char *)(buf)+1)<<8| \
328                    (unsigned int)*((unsigned char *)(buf)+0)) 
329
330 #define SVAL(buf) ((unsigned short) \
331                    (unsigned short)*((unsigned char *)(buf)+1)<<8| \
332                    (unsigned short)*((unsigned char *)(buf)+0)) 
333
334 #define CVAL(buf) ((unsigned char)*((unsigned char *)(buf)))
335
336 #define SIVAL(buf, val) \
337             ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\
338              (((unsigned char *)(buf))[1])=(unsigned char)(((val)>>8)&0xFF),\
339              (((unsigned char *)(buf))[2])=(unsigned char)(((val)>>16)&0xFF),\
340              (((unsigned char *)(buf))[3])=(unsigned char)((val)>>24))
341
342 #define SSVAL(buf, val) \
343             ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\
344              (((unsigned char *)(buf))[1])=(unsigned char)((val)>>8))
345
346 static int verbose = 0;
347 static int print_security = 0;
348 static int full_print = 0;
349 static const char *def_owner_sid_str = NULL;
350
351 /* 
352  * These definitions are for the in-memory registry structure.
353  * It is a tree structure that mimics what you see with tools like regedit
354  */
355
356 /*
357  * DateTime struct for Windows
358  */
359
360 typedef struct date_time_s {
361   unsigned int low, high;
362 } NTTIME;
363
364 /*
365  * Definition of a Key. It has a name, classname, date/time last modified,
366  * sub-keys, values, and a security descriptor
367  */
368
369 #define REG_ROOT_KEY 1
370 #define REG_SUB_KEY  2
371 #define REG_SYM_LINK 3
372
373 typedef struct key_sec_desc_s KEY_SEC_DESC;
374
375 typedef struct reg_key_s {
376   char *name;         /* Name of the key                    */
377   char *class_name;
378   int type;           /* One of REG_ROOT_KEY or REG_SUB_KEY */
379   NTTIME last_mod; /* Time last modified                 */
380   struct reg_key_s *owner;
381   struct key_list_s *sub_keys;
382   struct val_list_s *values;
383   KEY_SEC_DESC *security;
384   unsigned int offset;  /* Offset of the record in the file */
385 } REG_KEY;
386
387 /*
388  * The KEY_LIST struct lists sub-keys.
389  */
390
391 typedef struct key_list_s {
392   int key_count;
393   int max_keys;
394   REG_KEY *keys[1];
395 } KEY_LIST;
396
397 typedef struct val_key_s {
398   char *name;
399   int has_name;
400   int data_type;
401   int data_len;
402   void *data_blk;    /* Might want a separate block */
403 } VAL_KEY;
404
405 typedef struct val_list_s {
406   int val_count;
407   int max_vals;
408   VAL_KEY *vals[1];
409 } VAL_LIST;
410
411 #ifndef MAXSUBAUTHS
412 #define MAXSUBAUTHS 15
413 #endif
414
415 typedef struct sid_s {
416   unsigned char ver, auths;
417   unsigned char auth[6];
418   unsigned int sub_auths[MAXSUBAUTHS];
419 } sid_t;
420
421 typedef struct ace_struct_s {
422   unsigned char type, flags;
423   unsigned int perms;   /* Perhaps a better def is in order */
424   sid_t *trustee;
425 } ACE; 
426
427 typedef struct acl_struct_s {
428   unsigned short rev, refcnt;
429   unsigned short num_aces;
430   ACE *aces[1];
431 } ACL;
432
433 typedef struct sec_desc_s {
434   unsigned int rev, type;
435   sid_t *owner, *group;
436   ACL *sacl, *dacl;
437 } SEC_DESC;
438
439 #define SEC_DESC_NON 0
440 #define SEC_DESC_RES 1
441 #define SEC_DESC_OCU 2
442 #define SEC_DESC_NBK 3
443 typedef struct sk_struct SK_HDR;
444 struct key_sec_desc_s {
445   struct key_sec_desc_s *prev, *next;
446   int ref_cnt;
447   int state;
448   int offset;
449   SK_HDR *sk_hdr;     /* This means we must keep the registry in memory */
450   SEC_DESC *sec_desc;
451 }; 
452
453 /* 
454  * All of the structures below actually have a four-byte length before them
455  * which always seems to be negative. The following macro retrieves that
456  * size as an integer
457  */
458
459 #define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
460
461 typedef unsigned int DWORD;
462 typedef unsigned short WORD;
463
464 #define REG_REGF_ID 0x66676572
465
466 typedef struct regf_block {
467   DWORD REGF_ID;     /* regf */
468   DWORD uk1;
469   DWORD uk2;
470   DWORD tim1, tim2;
471   DWORD uk3;             /* 1 */
472   DWORD uk4;             /* 3 */
473   DWORD uk5;             /* 0 */
474   DWORD uk6;             /* 1 */
475   DWORD first_key;       /* offset */
476   unsigned int dblk_size;
477   DWORD uk7[116];        /* 1 */
478   DWORD chksum;
479 } REGF_HDR;
480
481 typedef struct hbin_sub_struct {
482   DWORD dblocksize;
483   char data[1];
484 } HBIN_SUB_HDR;
485
486 #define REG_HBIN_ID 0x6E696268
487
488 typedef struct hbin_struct {
489   DWORD HBIN_ID; /* hbin */
490   DWORD off_from_first;
491   DWORD off_to_next;
492   DWORD uk1;
493   DWORD uk2;
494   DWORD uk3;
495   DWORD uk4;
496   DWORD blk_size;
497   HBIN_SUB_HDR hbin_sub_hdr;
498 } HBIN_HDR;
499
500 #define REG_NK_ID 0x6B6E
501
502 typedef struct nk_struct {
503   WORD NK_ID;
504   WORD type;
505   DWORD t1, t2;
506   DWORD uk1;
507   DWORD own_off;
508   DWORD subk_num;
509   DWORD uk2;
510   DWORD lf_off;
511   DWORD uk3;
512   DWORD val_cnt;
513   DWORD val_off;
514   DWORD sk_off;
515   DWORD clsnam_off;
516   DWORD unk4[4];
517   DWORD unk5;
518   WORD nam_len;
519   WORD clsnam_len;
520   char key_nam[1];  /* Actual length determined by nam_len */
521 } NK_HDR;
522
523 #define REG_SK_ID 0x6B73
524
525 struct sk_struct {
526   WORD SK_ID;
527   WORD uk1;
528   DWORD prev_off;
529   DWORD next_off;
530   DWORD ref_cnt;
531   DWORD rec_size;
532   char sec_desc[1];
533 };
534
535 typedef struct ace_struct {
536     unsigned char type;
537     unsigned char flags;
538     unsigned short length;
539     unsigned int perms;
540     sid_t trustee;
541 } REG_ACE;
542
543 typedef struct acl_struct {
544   WORD rev;
545   WORD size;
546   DWORD num_aces;
547   REG_ACE *aces;   /* One or more ACEs */
548 } REG_ACL;
549
550 typedef struct sec_desc_rec {
551   WORD rev;
552   WORD type;
553   DWORD owner_off;
554   DWORD group_off;
555   DWORD sacl_off;
556   DWORD dacl_off;
557 } REG_SEC_DESC;
558
559 typedef struct hash_struct {
560   DWORD nk_off;
561   char hash[4];
562 } HASH_REC;
563
564 #define REG_LF_ID 0x666C
565
566 typedef struct lf_struct {
567   WORD LF_ID;
568   WORD key_count;
569   struct hash_struct hr[1];  /* Array of hash records, depending on key_count */
570 } LF_HDR;
571
572 typedef DWORD VL_TYPE[1];  /* Value list is an array of vk rec offsets */
573
574 #define REG_VK_ID 0x6B76
575
576 typedef struct vk_struct {
577   WORD VK_ID;
578   WORD nam_len;
579   DWORD dat_len;    /* If top-bit set, offset contains the data */
580   DWORD dat_off;   
581   DWORD dat_type;
582   WORD flag;        /* =1, has name, else no name (=Default). */
583   WORD unk1;
584   char dat_name[1]; /* Name starts here ... */
585 } VK_HDR;
586
587 #define REG_TYPE_DELETE    -1
588 #define REG_TYPE_NONE      0
589 #define REG_TYPE_REGSZ     1
590 #define REG_TYPE_EXPANDSZ  2
591 #define REG_TYPE_BIN       3  
592 #define REG_TYPE_DWORD     4
593 #define REG_TYPE_MULTISZ   7
594
595 typedef struct _val_str { 
596   unsigned int val;
597   const char * str;
598 } VAL_STR;
599
600 /* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
601 typedef struct sk_map_s {
602   int sk_off;
603   KEY_SEC_DESC *key_sec_desc;
604 } SK_MAP;
605
606 /*
607  * This structure keeps track of the output format of the registry
608  */
609 #define REG_OUTBLK_HDR 1
610 #define REG_OUTBLK_HBIN 2
611
612 typedef struct hbin_blk_s {
613   int type, size;
614   struct hbin_blk_s *next;
615   char *data;                /* The data block                */
616   unsigned int file_offset;  /* Offset in file                */
617   unsigned int free_space;   /* Amount of free space in block */
618   unsigned int fsp_off;      /* Start of free space in block  */
619   int complete, stored;
620 } HBIN_BLK;
621
622 /*
623  * This structure keeps all the registry stuff in one place
624  */
625 typedef struct regf_struct_s {
626   int reg_type;
627   char *regfile_name, *outfile_name;
628   int fd;
629   struct stat sbuf;
630   char *base;
631   int modified;
632   NTTIME last_mod_time;
633   REG_KEY *root;  /* Root of the tree for this file */
634   int sk_count, sk_map_size;
635   SK_MAP *sk_map;
636   const char *owner_sid_str;
637   SEC_DESC *def_sec_desc;
638   /*
639    * These next pointers point to the blocks used to contain the 
640    * keys when we are preparing to write them to a file
641    */
642   HBIN_BLK *blk_head, *blk_tail, *free_space;
643 } REGF;
644
645 /*
646  * An API for accessing/creating/destroying items above
647  */
648
649 /*
650  * Iterate over the keys, depth first, calling a function for each key
651  * and indicating if it is terminal or non-terminal and if it has values.
652  *
653  * In addition, for each value in the list, call a value list function
654  */
655
656 typedef int (*key_print_f)(const char *path, char *key_name, char *class_name, 
657                            int root, int terminal, int values);
658
659 typedef int (*val_print_f)(const char *path, char *val_name, int val_type, 
660                            int data_len, void *data_blk, int terminal,
661                            int first, int last);
662
663 typedef int (*sec_print_f)(SEC_DESC *sec_desc);
664
665 static
666 int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path, 
667                     key_print_f key_print, sec_print_f sec_print,
668                     val_print_f val_print);
669
670 static
671 int nt_val_list_iterator(REGF *regf, VAL_LIST *val_list, int bf, char *path,
672                          int terminal, val_print_f val_print)
673 {
674   int i;
675
676   if (!val_list) return 1;
677
678   if (!val_print) return 1;
679
680   for (i=0; i<val_list->val_count; i++) {
681     if (!val_print(path, val_list->vals[i]->name, val_list->vals[i]->data_type,
682                    val_list->vals[i]->data_len, val_list->vals[i]->data_blk,
683                    terminal,
684                    (i == 0),
685                    (i == val_list->val_count))) {
686
687       return 0;
688
689     }
690   }
691
692   return 1;
693 }
694
695 static
696 int nt_key_list_iterator(REGF *regf, KEY_LIST *key_list, int bf, 
697                          const char *path,
698                          key_print_f key_print, sec_print_f sec_print, 
699                          val_print_f val_print)
700 {
701   int i;
702
703   if (!key_list) return 1;
704
705   for (i=0; i< key_list->key_count; i++) {
706     if (!nt_key_iterator(regf, key_list->keys[i], bf, path, key_print, 
707                          sec_print, val_print)) {
708       return 0;
709     }
710   }
711   return 1;
712 }
713
714 static
715 int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
716                     key_print_f key_print, sec_print_f sec_print,
717                     val_print_f val_print)
718 {
719   int path_len = strlen(path);
720   char *new_path;
721
722   if (!regf || !key_tree)
723     return -1;
724
725   /* List the key first, then the values, then the sub-keys */
726
727   if (key_print) {
728
729     if (!(*key_print)(path, key_tree->name, 
730                       key_tree->class_name, 
731                       (key_tree->type == REG_ROOT_KEY),
732                       (key_tree->sub_keys == NULL),
733                       (key_tree->values?(key_tree->values->val_count):0)))
734       return 0;
735   }
736
737   /*
738    * If we have a security print routine, call it
739    * If the security print routine returns false, stop.
740    */
741   if (sec_print) {
742     if (key_tree->security && !(*sec_print)(key_tree->security->sec_desc))
743       return 0;
744   }
745
746   new_path = (char *)malloc(path_len + 1 + strlen(key_tree->name) + 1);
747   if (!new_path) return 0; /* Errors? */
748   new_path[0] = '\0';
749   strcat(new_path, path);
750   strcat(new_path, key_tree->name);
751   strcat(new_path, "\\");
752
753   /*
754    * Now, iterate through the values in the val_list 
755    */
756
757   if (key_tree->values &&
758       !nt_val_list_iterator(regf, key_tree->values, bf, new_path, 
759                             (key_tree->values!=NULL),
760                             val_print)) {
761
762     free(new_path);
763     return 0;
764   } 
765
766   /* 
767    * Now, iterate through the keys in the key list
768    */
769
770   if (key_tree->sub_keys && 
771       !nt_key_list_iterator(regf, key_tree->sub_keys, bf, new_path, key_print, 
772                             sec_print, val_print)) {
773     free(new_path);
774     return 0;
775   } 
776
777   free(new_path);
778   return 1;
779 }
780
781 static
782 REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key);
783
784 /*
785  * Find key by name in a list ...
786  * Take the first component and search for that in the list
787  */
788 static
789 REG_KEY *nt_find_key_in_list_by_name(KEY_LIST *list, char *key)
790 {
791   int i;
792   REG_KEY *res = NULL;
793
794   if (!list || !key || !*key) return NULL;
795
796   for (i = 0; i < list->key_count; i++)
797     if ((res = nt_find_key_by_name(list->keys[i], key)))
798       return res;
799   
800   return NULL;
801 }
802
803 /* 
804  * Find key by name in a tree ... We will assume absolute names here, but we
805  * need the root of the tree ...
806  */
807 static
808 REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key)
809 {
810   char *lname = NULL, *c1, *c2;
811   REG_KEY *tmp;
812
813   if (!tree || !key || !*key) return NULL;
814
815   lname = strdup(key);
816   if (!lname) return NULL;
817
818   /*
819    * Make sure that the first component is correct ...
820    */
821   c1 = lname;
822   c2 = strchr(c1, '\\');
823   if (c2) { /* Split here ... */
824     *c2 = 0;
825     c2++;
826   }
827   if (strcmp(c1, tree->name) != 0) goto error; 
828
829   if (c2) {
830     tmp = nt_find_key_in_list_by_name(tree->sub_keys, c2);
831     free(lname);
832     return tmp;
833   }
834   else {
835     if (lname) free(lname);
836     return tree;
837   }
838  error:
839   if (lname) free(lname);
840   return NULL;
841 }
842
843 /* Make, delete keys */
844 static
845 int nt_delete_val_key(VAL_KEY *val_key)
846 {
847
848   if (val_key) {
849     if (val_key->name) free(val_key->name);
850     if (val_key->data_blk) free(val_key->data_blk);
851     free(val_key);
852   };
853   return 1;
854 }
855
856 static
857 int nt_delete_val_list(VAL_LIST *vl)
858 {
859   int i;
860
861   if (vl) {
862     for (i=0; i<vl->val_count; i++)
863       nt_delete_val_key(vl->vals[i]);
864     free(vl);
865   }
866   return 1;
867 }
868
869 static
870 int nt_delete_reg_key(REG_KEY *key, int delete_name);
871
872 static
873 int nt_delete_key_list(KEY_LIST *key_list, int delete_name)
874 {
875   int i;
876
877   if (key_list) {
878     for (i=0; i<key_list->key_count; i++) 
879       nt_delete_reg_key(key_list->keys[i], False);
880     free(key_list);
881   }
882   return 1;
883 }
884
885 /*
886  * Find the key, and if it exists, delete it ...
887  */
888 static
889 int nt_delete_key_by_name(REGF *regf, char *name)
890 {
891   REG_KEY *key;
892
893   if (!name || !*name) return 0;
894
895   key = nt_find_key_by_name(regf->root, name);
896
897   if (key) {
898     if (key == regf->root) regf->root = NULL;
899     return nt_delete_reg_key(key, True);
900   }
901
902   return 0;
903
904 }
905
906 static
907 int nt_delete_sid(sid_t *sid)
908 {
909
910   if (sid) free(sid);
911   return 1;
912
913 }
914
915 static
916 int nt_delete_ace(ACE *ace)
917 {
918
919   if (ace) {
920     nt_delete_sid(ace->trustee);
921     free(ace);
922   }
923   return 1;
924
925 }
926
927 static
928 int nt_delete_acl(ACL *acl)
929 {
930
931   if (acl) {
932     int i;
933
934     for (i=0; i<acl->num_aces; i++)
935       nt_delete_ace(acl->aces[i]);
936
937     free(acl);
938   }
939   return 1;
940 }
941
942 static
943 int nt_delete_sec_desc(SEC_DESC *sec_desc)
944 {
945
946   if (sec_desc) {
947
948     nt_delete_sid(sec_desc->owner);
949     nt_delete_sid(sec_desc->group);
950     nt_delete_acl(sec_desc->sacl);
951     nt_delete_acl(sec_desc->dacl);
952     free(sec_desc);
953
954   }
955   return 1;
956 }
957
958 static
959 int nt_delete_key_sec_desc(KEY_SEC_DESC *key_sec_desc)
960 {
961
962   if (key_sec_desc) {
963     key_sec_desc->ref_cnt--;
964     if (key_sec_desc->ref_cnt<=0) {
965       /*
966        * There should always be a next and prev, even if they point to us 
967        */
968       key_sec_desc->next->prev = key_sec_desc->prev;
969       key_sec_desc->prev->next = key_sec_desc->next;
970       nt_delete_sec_desc(key_sec_desc->sec_desc);
971     }
972   }
973   return 1;
974 }
975
976 static
977 int nt_delete_reg_key(REG_KEY *key, int delete_name)
978 {
979
980   if (key) {
981     if (key->name) free(key->name);
982     if (key->class_name) free(key->class_name);
983
984     /*
985      * We will delete the owner if we are not the root and told to ...
986      */
987
988     if (key->owner && key->owner->sub_keys && delete_name) {
989       REG_KEY *own;
990       KEY_LIST *kl;
991       int i;
992       /* Find our owner, look in keylist for us and shuffle up */
993       /* Perhaps should be a function                          */
994
995       own = key->owner;
996       kl = own->sub_keys;
997
998       for (i=0; i < kl->key_count && kl->keys[i] != key ; i++) {
999         /* Just find the entry ... */
1000       }
1001
1002       if (i == kl->key_count) {
1003         fprintf(stderr, "Bad data structure. Key not found in key list of owner\n");
1004       }
1005       else {
1006         int j;
1007
1008         /*
1009          * Shuffle up. Works for the last one also 
1010          */
1011         for (j = i + 1; j < kl->key_count; j++) {
1012           kl->keys[j - 1] = kl->keys[j];
1013         }
1014
1015         kl->key_count--;
1016       }
1017     }
1018
1019     if (key->sub_keys) nt_delete_key_list(key->sub_keys, False);
1020     if (key->values) nt_delete_val_list(key->values);
1021     if (key->security) nt_delete_key_sec_desc(key->security);
1022     free(key);
1023   }
1024   return 1;
1025 }
1026
1027 /*
1028  * Convert a string to a value ...
1029  * FIXME: Error handling and convert this at command parse time ... 
1030  */
1031 static
1032 void *str_to_val(int type, char *val, int *len)
1033 {
1034   unsigned int *dwordp = NULL;
1035
1036   if (!len || !val) return NULL;
1037
1038   switch (type) {
1039   case REG_TYPE_REGSZ:
1040     *len = strlen(val);
1041     return (void *)val;
1042
1043   case REG_TYPE_DWORD:
1044     dwordp = (unsigned int *)malloc(sizeof(unsigned int));
1045     if (!dwordp) return NULL;
1046     /* Allow for ddddd and 0xhhhhh and 0ooooo */
1047     if (strncmp(val, "0x", 2) == 0 || strncmp(val, "0X", 2) == 0) {
1048       sscanf(&val[2], "%X", dwordp);
1049     }
1050     else if (*val == '0') {
1051       sscanf(&val[1], "%o", dwordp);
1052     }
1053     else { 
1054       sscanf(val, "%d", dwordp);
1055     }
1056     *len = sizeof(unsigned int);
1057     return (void *)dwordp;
1058
1059     /* FIXME: Implement more of these */
1060
1061   default:
1062     return NULL;
1063   }
1064
1065   return NULL;
1066 }
1067
1068 /*
1069  * Add a value to the key specified ... We have to parse the value some more
1070  * based on the type to get it in the correct internal form
1071  * An empty name will be converted to "<No Name>" before here
1072  * Hmmm, maybe not. has_name is for that
1073  */
1074 static
1075 VAL_KEY *nt_add_reg_value(REG_KEY *key, char *name, int type, char *value)
1076 {
1077   int i;
1078   VAL_KEY *tmp = NULL;
1079
1080   if (!key || !key->values || !name || !*name) return NULL;
1081
1082   assert(type != REG_TYPE_DELETE); /* We never process deletes here */
1083
1084   for (i = 0; i < key->values->val_count; i++) {
1085     if ((!key->values->vals[i]->has_name && !*name) || 
1086         (key->values->vals[i]->has_name &&
1087          strcmp(name, key->values->vals[i]->name) == 0)){ /* Change the value */
1088       free(key->values->vals[i]->data_blk);
1089       key->values->vals[i]->data_blk = str_to_val(type, value, &
1090                                                   key->values->vals[i]->data_len);
1091       return key->values->vals[i];
1092     }
1093   }
1094
1095   /* 
1096    * If we get here, the name was not found, so insert it 
1097    */
1098
1099   tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
1100   if (!tmp) goto error;
1101
1102   memset(tmp, 0, sizeof(VAL_KEY));
1103   tmp->name = strdup(name);
1104   tmp->has_name = True;
1105   if (!tmp->name) goto error;
1106   tmp->data_type = type;
1107   tmp->data_blk = str_to_val(type, value, &tmp->data_len);
1108
1109   /* Now, add to val list */
1110
1111   if (key->values->val_count >= key->values->max_vals) {
1112     /*
1113      * Allocate some more space 
1114      */
1115
1116     if ((key->values = (VAL_LIST *)realloc(key->values, sizeof(VAL_LIST) + 
1117                                            key->values->val_count - 1 +
1118                                            REG_KEY_LIST_SIZE))) {
1119       key->values->max_vals += REG_KEY_LIST_SIZE;
1120     }
1121     else goto error;
1122   }
1123
1124   i = key->values->val_count;
1125   key->values->val_count++;
1126   key->values->vals[i] = tmp;
1127   return tmp;
1128
1129  error:
1130   if (tmp) nt_delete_val_key(tmp);
1131   return NULL;
1132 }
1133
1134 /*
1135  * Delete a value. We return the value and let the caller deal with it. 
1136  */
1137 static
1138 VAL_KEY *nt_delete_reg_value(REG_KEY *key, char *name)
1139 {
1140   int i, j;
1141
1142   if (!key || !key->values || !name || !*name) return NULL;
1143
1144   /* FIXME: Allow empty value name */
1145   for (i = 0; i< key->values->val_count; i++) {
1146     if ((!key->values->vals[i]->has_name && !*name) || 
1147         (key->values->vals[i]->has_name &&
1148          strcmp(name, key->values->vals[i]->name) == 0)) {
1149       VAL_KEY *val;
1150
1151       val = key->values->vals[i];
1152
1153       /* Shuffle down */
1154       for (j = i + 1; j < key->values->val_count; j++)
1155         key->values->vals[j - 1] = key->values->vals[j];
1156
1157       key->values->val_count--;
1158
1159       return val;
1160     }
1161   }
1162   return NULL;
1163 }
1164
1165 /* 
1166  * Add a key to the tree ... We walk down the components matching until
1167  * we don't find any. There must be a match on the first component ...
1168  * We return the key structure for the final component as that is 
1169  * often where we want to add values ...
1170  */
1171
1172 /*
1173  * Convert a string of the form S-1-5-x[-y-z-r] to a SID
1174  */
1175 static
1176 int sid_string_to_sid(sid_t **sid, const char *sid_str)
1177 {
1178   int i = 0, auth;
1179   const char *lstr; 
1180
1181   *sid = (sid_t *)malloc(sizeof(sid_t));
1182   if (!*sid) return 0;
1183
1184   memset(*sid, 0, sizeof(sid_t));
1185
1186   if (strncmp(sid_str, "S-1-5", 5)) {
1187     fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str);
1188     return 0;
1189   }
1190
1191   /* We only allow strings of form S-1-5... */
1192
1193   (*sid)->ver = 1;
1194   (*sid)->auth[5] = 5;
1195
1196   lstr = sid_str + 5;
1197
1198   while (1) {
1199     if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) {
1200       if (i < 1) {
1201         fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i);
1202         return 0;
1203       }
1204       (*sid)->auths=i;
1205       return 1;
1206     }
1207
1208     (*sid)->sub_auths[i] = auth;
1209     i++;
1210     lstr = strchr(lstr + 1, '-'); 
1211   }
1212
1213   /*return 1; */ /* Not Reached ... */
1214 }
1215
1216 /*
1217  * Create an ACE
1218  */
1219 static
1220 ACE *nt_create_ace(int type, int flags, unsigned int perms, const char *sid)
1221 {
1222   ACE *ace;
1223
1224   ace = (ACE *)malloc(sizeof(ACE));
1225   if (!ace) goto error;
1226   ace->type = type;
1227   ace->flags = flags;
1228   ace->perms = perms;
1229   if (!sid_string_to_sid(&ace->trustee, sid))
1230     goto error;
1231   return ace;
1232
1233  error:
1234   if (ace) nt_delete_ace(ace);
1235   return NULL;
1236 }
1237
1238 /*
1239  * Create a default ACL
1240  */
1241 static
1242 ACL *nt_create_default_acl(REGF *regf)
1243 {
1244   ACL *acl;
1245
1246   acl = (ACL *)malloc(sizeof(ACL) + 7*sizeof(ACE *));
1247   if (!acl) goto error;
1248
1249   acl->rev = 2;
1250   acl->refcnt = 1;
1251   acl->num_aces = 8;
1252
1253   acl->aces[0] = nt_create_ace(0x00, 0x0, 0xF003F, regf->owner_sid_str);
1254   if (!acl->aces[0]) goto error;
1255   acl->aces[1] = nt_create_ace(0x00, 0x0, 0xF003F, "S-1-5-18");
1256   if (!acl->aces[1]) goto error;
1257   acl->aces[2] = nt_create_ace(0x00, 0x0, 0xF003F, "S-1-5-32-544");
1258   if (!acl->aces[2]) goto error;
1259   acl->aces[3] = nt_create_ace(0x00, 0x0, 0x20019, "S-1-5-12");
1260   if (!acl->aces[3]) goto error;
1261   acl->aces[4] = nt_create_ace(0x00, 0x0B, 0x10000000, regf->owner_sid_str);
1262   if (!acl->aces[4]) goto error;
1263   acl->aces[5] = nt_create_ace(0x00, 0x0B, 0x10000000, "S-1-5-18");
1264   if (!acl->aces[5]) goto error;
1265   acl->aces[6] = nt_create_ace(0x00, 0x0B, 0x10000000, "S-1-5-32-544");
1266   if (!acl->aces[6]) goto error;
1267   acl->aces[7] = nt_create_ace(0x00, 0x0B, 0x80000000, "S-1-5-12");
1268   if (!acl->aces[7]) goto error;
1269   return acl;
1270
1271  error:
1272   if (acl) nt_delete_acl(acl);
1273   return NULL;
1274 }
1275
1276 /*
1277  * Create a default security descriptor. We pull in things from env
1278  * if need be 
1279  */
1280 static
1281 SEC_DESC *nt_create_def_sec_desc(REGF *regf)
1282 {
1283   SEC_DESC *tmp;
1284
1285   tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
1286   if (!tmp) return NULL;
1287
1288   tmp->rev = 1;
1289   tmp->type = 0x8004;
1290   if (!sid_string_to_sid(&tmp->owner, "S-1-5-32-544")) goto error;
1291   if (!sid_string_to_sid(&tmp->group, "S-1-5-18")) goto error;
1292   tmp->sacl = NULL;
1293   tmp->dacl = nt_create_default_acl(regf);
1294
1295   return tmp;
1296
1297  error:
1298   if (tmp) nt_delete_sec_desc(tmp);
1299   return NULL;
1300 }
1301
1302 /*
1303  * We will implement inheritence that is based on what the parent's SEC_DESC
1304  * says, but the Owner and Group SIDs can be overwridden from the command line
1305  * and additional ACEs can be applied from the command line etc.
1306  */
1307 static
1308 KEY_SEC_DESC *nt_inherit_security(REG_KEY *key)
1309 {
1310
1311   if (!key) return NULL;
1312   return key->security;
1313 }
1314
1315 /*
1316  * Create an initial security descriptor and init other structures, if needed
1317  * We assume that the initial security stuff is empty ...
1318  */
1319 static
1320 KEY_SEC_DESC *nt_create_init_sec(REGF *regf)
1321 {
1322   KEY_SEC_DESC *tsec = NULL;
1323   
1324   tsec = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
1325   if (!tsec) return NULL;
1326
1327   tsec->ref_cnt = 1;
1328   tsec->state = SEC_DESC_NBK;
1329   tsec->offset = 0;
1330
1331   tsec->sec_desc = regf->def_sec_desc;
1332
1333   return tsec;
1334 }
1335
1336 /*
1337  * Add a sub-key 
1338  */
1339 static
1340 REG_KEY *nt_add_reg_key_list(REGF *regf, REG_KEY *key, char * name, int create)
1341 {
1342   int i;
1343   REG_KEY *ret = NULL, *tmp = NULL;
1344   KEY_LIST *list;
1345   char *lname, *c1, *c2;
1346
1347   if (!key || !name || !*name) return NULL;
1348   
1349   list = key->sub_keys;
1350   if (!list) { /* Create an empty list */
1351
1352     list = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (REG_KEY_LIST_SIZE - 1) * sizeof(REG_KEY *));
1353     list->key_count = 0;
1354     list->max_keys = REG_KEY_LIST_SIZE;
1355
1356   }
1357
1358   lname = strdup(name);
1359   if (!lname) return NULL;
1360
1361   c1 = lname;
1362   c2 = strchr(c1, '\\');
1363   if (c2) { /* Split here ... */
1364     *c2 = 0;
1365     c2++;
1366   }
1367
1368   for (i = 0; i < list->key_count; i++) {
1369     if (strcmp(list->keys[i]->name, c1) == 0) {
1370       ret = nt_add_reg_key_list(regf, list->keys[i], c2, create);
1371       free(lname);
1372       return ret;
1373     }
1374   }
1375
1376   /*
1377    * If we reach here we could not find the the first component
1378    * so create it ...
1379    */
1380
1381   if (list->key_count < list->max_keys){
1382     list->key_count++;
1383   }
1384   else { /* Create more space in the list ... */
1385     if (!(list = (KEY_LIST *)realloc(list, sizeof(KEY_LIST) + 
1386                                      (list->max_keys + REG_KEY_LIST_SIZE - 1) 
1387                                      * sizeof(REG_KEY *))))
1388       goto error;
1389
1390     list->max_keys += REG_KEY_LIST_SIZE;
1391     list->key_count++;
1392   }
1393
1394   /*
1395    * add the new key at the new slot 
1396    * FIXME: Sort the list someday
1397    */
1398
1399   /*
1400    * We want to create the key, and then do the rest
1401    */
1402
1403   tmp = (REG_KEY *)malloc(sizeof(REG_KEY)); 
1404
1405   memset(tmp, 0, sizeof(REG_KEY));
1406
1407   tmp->name = strdup(c1);
1408   if (!tmp->name) goto error;
1409   tmp->owner = key;
1410   tmp->type = REG_SUB_KEY;
1411   /*
1412    * Next, pull security from the parent, but override with
1413    * anything passed in on the command line
1414    */
1415   tmp->security = nt_inherit_security(key);
1416
1417   list->keys[list->key_count - 1] = tmp;
1418
1419   if (c2) {
1420     ret = nt_add_reg_key_list(regf, key, c2, True);
1421   }
1422
1423   if (lname) free(lname);
1424
1425   return ret;
1426
1427  error:
1428   if (tmp) free(tmp);
1429   if (lname) free(lname);
1430   return NULL;
1431 }
1432
1433 /*
1434  * This routine only adds a key from the root down.
1435  * It calls helper functions to handle sub-key lists and sub-keys
1436  */
1437 static
1438 REG_KEY *nt_add_reg_key(REGF *regf, char *name, int create)
1439 {
1440   char *lname = NULL, *c1, *c2;
1441   REG_KEY * tmp = NULL;
1442
1443   /*
1444    * Look until we hit the first component that does not exist, and
1445    * then add from there. However, if the first component does not 
1446    * match and the path we are given is the root, then it must match
1447    */
1448   if (!regf || !name || !*name) return NULL;
1449
1450   lname = strdup(name);
1451   if (!lname) return NULL;
1452
1453   c1 = lname;
1454   c2 = strchr(c1, '\\');
1455   if (c2) { /* Split here ... */
1456     *c2 = 0;
1457     c2++;
1458   }
1459
1460   /*
1461    * If the root does not exist, create it and make it equal to the
1462    * first component ...
1463    */
1464
1465   if (!regf->root) {
1466     
1467     tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
1468     if (!tmp) goto error;
1469     memset(tmp, 0, sizeof(REG_KEY));
1470     tmp->name = strdup(c1);
1471     if (!tmp->name) goto error;
1472     tmp->security = nt_create_init_sec(regf);
1473     if (!tmp->security) goto error;
1474     regf->root = tmp;
1475
1476   }
1477   else {
1478     /*
1479      * If we don't match, then we have to return error ...
1480      * If we do match on this component, check the next one in the
1481      * list, and if not found, add it ... short circuit, add all the
1482      * way down
1483      */
1484
1485     if (strcmp(c1, regf->root->name) != 0)
1486       goto error;
1487   }
1488
1489   tmp = nt_add_reg_key_list(regf, regf->root, c2, True);
1490   free(lname);
1491   return tmp;
1492   
1493  error:
1494   if (tmp) free(tmp);
1495   if (lname) free(lname);
1496   return NULL;
1497 }
1498
1499 /*
1500  * Load and unload a registry file.
1501  *
1502  * Load, loads it into memory as a tree, while unload sealizes/flattens it
1503  */
1504
1505 /*
1506  * Get the starting record for NT Registry file 
1507  */
1508
1509 /* 
1510  * Where we keep all the regf stuff for one registry.
1511  * This is the structure that we use to tie the in memory tree etc 
1512  * together. By keeping separate structs, we can operate on different
1513  * registries at the same time.
1514  * Currently, the SK_MAP is an array of mapping structure.
1515  * Since we only need this on input and output, we fill in the structure
1516  * as we go on input. On output, we know how many SK items we have, so
1517  * we can allocate the structure as we need to.
1518  * If you add stuff here that is dynamically allocated, add the 
1519  * appropriate free statements below.
1520  */
1521
1522 #define REGF_REGTYPE_NONE 0
1523 #define REGF_REGTYPE_NT   1
1524 #define REGF_REGTYPE_W9X  2
1525
1526 #define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
1527                               (r)->last_mod_time.high = (t2);
1528
1529 #define REGF_HDR_BLKSIZ 0x1000 
1530
1531 #define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4) 
1532 #define LOCN(base, f) ((base) + OFF(f))
1533
1534 const VAL_STR reg_type_names[] = {
1535    { REG_TYPE_REGSZ,    "REG_SZ" },
1536    { REG_TYPE_EXPANDSZ, "REG_EXPAND_SZ" },
1537    { REG_TYPE_BIN,      "REG_BIN" },
1538    { REG_TYPE_DWORD,    "REG_DWORD" },
1539    { REG_TYPE_MULTISZ,  "REG_MULTI_SZ" },
1540    { 0, NULL },
1541 };
1542
1543 static
1544 const char *val_to_str(unsigned int val, const VAL_STR *val_array)
1545 {
1546   int i = 0;
1547
1548   if (!val_array) return NULL;
1549
1550   while (val_array[i].val && val_array[i].str) {
1551
1552     if (val_array[i].val == val) return val_array[i].str;
1553     i++;
1554
1555   }
1556
1557   return NULL;
1558
1559 }
1560
1561 /*
1562  * Convert from UniCode to Ascii ... Does not take into account other lang
1563  * Restrict by ascii_max if > 0
1564  */
1565 static
1566 int uni_to_ascii(unsigned char *uni, unsigned char *ascii, int ascii_max, 
1567                  int uni_max)
1568 {
1569   int i = 0; 
1570
1571   while (i < ascii_max && !(!uni[i*2] && !uni[i*2+1])) {
1572     if (uni_max > 0 && (i*2) >= uni_max) break;
1573     ascii[i] = uni[i*2];
1574     i++;
1575
1576   }
1577
1578   ascii[i] = '\0';
1579
1580   return i;
1581 }
1582
1583 /*
1584  * Convert a data value to a string for display
1585  */
1586 static
1587 int data_to_ascii(unsigned char *datap, int len, int type, char *ascii, int ascii_max)
1588
1589   unsigned char *asciip;
1590   int i;
1591
1592   switch (type) {
1593   case REG_TYPE_REGSZ:
1594     if (verbose) fprintf(stderr, "Len: %d\n", len);
1595     /* FIXME. This has to be fixed. It has to be UNICODE */ 
1596     return uni_to_ascii(datap, ascii, len, ascii_max);
1597     break; /*NOTREACHED*/
1598
1599   case REG_TYPE_EXPANDSZ:
1600     return uni_to_ascii(datap, ascii, len, ascii_max);
1601     break;
1602
1603   case REG_TYPE_BIN:
1604     asciip = ascii;
1605     for (i=0; (i<len)&&(i+1)*3<ascii_max; i++) { 
1606       int str_rem = ascii_max - ((int)asciip - (int)ascii);
1607       asciip += snprintf(asciip, str_rem, "%02x", *(unsigned char *)(datap+i));
1608       if (i < len && str_rem > 0)
1609         *asciip = ' '; asciip++;        
1610     }
1611     *asciip = '\0';
1612     return ((int)asciip - (int)ascii);
1613     break;
1614
1615   case REG_TYPE_DWORD:
1616     if (*(int *)datap == 0)
1617       return snprintf(ascii, ascii_max, "0");
1618     else
1619       return snprintf(ascii, ascii_max, "0x%x", *(int *)datap);
1620     break;
1621
1622   case REG_TYPE_MULTISZ:
1623
1624     break;
1625
1626   default:
1627     return 0;
1628     break;
1629   } 
1630
1631   return len;
1632
1633 }
1634
1635 static
1636 REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent);
1637
1638 static
1639 int nt_set_regf_input_file(REGF *regf, char *filename)
1640 {
1641   return ((regf->regfile_name = strdup(filename)) != NULL); 
1642 }
1643
1644 static
1645 int nt_set_regf_output_file(REGF *regf, char *filename)
1646 {
1647   return ((regf->outfile_name = strdup(filename)) != NULL); 
1648 }
1649
1650 /* Create a regf structure and init it */
1651
1652 static
1653 REGF *nt_create_regf(void)
1654 {
1655   REGF *tmp = (REGF *)malloc(sizeof(REGF));
1656   if (!tmp) return tmp;
1657   memset(tmp, 0, sizeof(REGF));
1658   tmp->owner_sid_str = def_owner_sid_str;
1659   return tmp;
1660
1661
1662 /* Free all the bits and pieces ... Assumes regf was malloc'd */
1663 /* If you add stuff to REGF, add the relevant free bits here  */
1664 static
1665 int nt_free_regf(REGF *regf)
1666 {
1667   if (!regf) return 0;
1668
1669   if (regf->regfile_name) free(regf->regfile_name);
1670   if (regf->outfile_name) free(regf->outfile_name);
1671
1672   nt_delete_reg_key(regf->root, False); /* Free the tree */
1673   free(regf->sk_map);
1674   regf->sk_count = regf->sk_map_size = 0;
1675
1676   free(regf);
1677
1678   return 1;
1679 }
1680
1681 /* Get the header of the registry. Return a pointer to the structure 
1682  * If the mmap'd area has not been allocated, then mmap the input file
1683  */
1684 static
1685 REGF_HDR *nt_get_regf_hdr(REGF *regf)
1686 {
1687   if (!regf)
1688     return NULL; /* What about errors */
1689
1690   if (!regf->regfile_name)
1691     return NULL; /* What about errors */
1692
1693   if (!regf->base) { /* Try to mmap etc the file */
1694
1695     if ((regf->fd = open(regf->regfile_name, O_RDONLY, 0000)) <0) {
1696       return NULL; /* What about errors? */
1697     }
1698
1699     if (fstat(regf->fd, &regf->sbuf) < 0) {
1700       return NULL;
1701     }
1702
1703     regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
1704
1705     if ((int)regf->base == 1) {
1706       fprintf(stderr, "Could not mmap file: %s, %s\n", regf->regfile_name,
1707               strerror(errno));
1708       return NULL;
1709     }
1710   }
1711
1712   /* 
1713    * At this point, regf->base != NULL, and we should be able to read the 
1714    * header 
1715    */
1716
1717   assert(regf->base != NULL);
1718
1719   return (REGF_HDR *)regf->base;
1720 }
1721
1722 /*
1723  * Validate a regf header
1724  * For now, do nothing, but we should check the checksum
1725  */
1726 static
1727 int valid_regf_hdr(REGF_HDR *regf_hdr)
1728 {
1729   if (!regf_hdr) return 0;
1730
1731   return 1;
1732 }
1733
1734 /*
1735  * Process an SK header ...
1736  * Every time we see a new one, add it to the map. Otherwise, just look it up.
1737  * We will do a simple linear search for the moment, since many KEYs have the 
1738  * same security descriptor. 
1739  * We allocate the map in increments of 10 entries.
1740  */
1741
1742 /*
1743  * Create a new entry in the map, and increase the size of the map if needed
1744  */
1745 static
1746 SK_MAP *alloc_sk_map_entry(REGF *regf, KEY_SEC_DESC *tmp, int sk_off)
1747 {
1748  if (!regf->sk_map) { /* Allocate a block of 10 */
1749     regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
1750     if (!regf->sk_map) {
1751       free(tmp);
1752       return NULL;
1753     }
1754     regf->sk_map_size = 10;
1755     regf->sk_count = 1;
1756     (regf->sk_map)[0].sk_off = sk_off;
1757     (regf->sk_map)[0].key_sec_desc = tmp;
1758   }
1759   else { /* Simply allocate a new slot, unless we have to expand the list */ 
1760     int ndx = regf->sk_count;
1761     if (regf->sk_count >= regf->sk_map_size) {
1762       regf->sk_map = (SK_MAP *)realloc(regf->sk_map, 
1763                                        (regf->sk_map_size + 10)*sizeof(SK_MAP));
1764       if (!regf->sk_map) {
1765         free(tmp);
1766         return NULL;
1767       }
1768       /*
1769        * ndx already points at the first entry of the new block
1770        */
1771       regf->sk_map_size += 10;
1772     }
1773     (regf->sk_map)[ndx].sk_off = sk_off;
1774     (regf->sk_map)[ndx].key_sec_desc = tmp;
1775     regf->sk_count++;
1776   }
1777  return regf->sk_map;
1778 }
1779
1780 /*
1781  * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
1782  * found
1783  */
1784 static
1785 KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
1786 {
1787   int i;
1788
1789   if (!sk_map) return NULL;
1790
1791   for (i = 0; i < count; i++) {
1792
1793     if (sk_map[i].sk_off == sk_off)
1794       return sk_map[i].key_sec_desc;
1795
1796   }
1797
1798   return NULL;
1799
1800 }
1801
1802 /*
1803  * Allocate a KEY_SEC_DESC if we can't find one in the map
1804  */
1805 static
1806 KEY_SEC_DESC *lookup_create_sec_key(REGF *regf, SK_MAP *sk_map, int sk_off)
1807 {
1808   KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
1809
1810   if (tmp) {
1811     return tmp;
1812   }
1813   else { /* Allocate a new one */
1814     tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
1815     if (!tmp) {
1816       return NULL;
1817     }
1818     memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */
1819     tmp->state = SEC_DESC_RES;
1820     if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
1821       return NULL;
1822     }
1823     return tmp;
1824   }
1825 }
1826
1827 /*
1828  * Allocate storage and duplicate a SID 
1829  * We could allocate the SID to be only the size needed, but I am too lazy. 
1830  */
1831 static
1832 sid_t *dup_sid(sid_t *sid)
1833 {
1834   sid_t *tmp = (sid_t *)malloc(sizeof(sid_t));
1835   int i;
1836   
1837   if (!tmp) return NULL;
1838   tmp->ver = sid->ver;
1839   tmp->auths = sid->auths;
1840   for (i=0; i<6; i++) {
1841     tmp->auth[i] = sid->auth[i];
1842   }
1843   for (i=0; i<tmp->auths&&i<MAXSUBAUTHS; i++) {
1844     tmp->sub_auths[i] = sid->sub_auths[i];
1845   }
1846   return tmp;
1847 }
1848
1849 /*
1850  * Allocate space for an ACE and duplicate the registry encoded one passed in
1851  */
1852 static
1853 ACE *dup_ace(REG_ACE *ace)
1854 {
1855   ACE *tmp = NULL; 
1856
1857   tmp = (ACE *)malloc(sizeof(ACE));
1858
1859   if (!tmp) return NULL;
1860
1861   tmp->type = CVAL(&ace->type);
1862   tmp->flags = CVAL(&ace->flags);
1863   tmp->perms = IVAL(&ace->perms);
1864   tmp->trustee = dup_sid(&ace->trustee);
1865   return tmp;
1866 }
1867
1868 /*
1869  * Allocate space for an ACL and duplicate the registry encoded one passed in 
1870  */
1871 static
1872 ACL *dup_acl(REG_ACL *acl)
1873 {
1874   ACL *tmp = NULL;
1875   REG_ACE* ace;
1876   int i, num_aces;
1877
1878   num_aces = IVAL(&acl->num_aces);
1879
1880   tmp = (ACL *)malloc(sizeof(ACL) + (num_aces - 1)*sizeof(ACE *));
1881   if (!tmp) return NULL;
1882
1883   tmp->num_aces = num_aces;
1884   tmp->refcnt = 1;
1885   tmp->rev = SVAL(&acl->rev);
1886   if (verbose) fprintf(stdout, "ACL: refcnt: %u, rev: %u\n", tmp->refcnt, 
1887                        tmp->rev);
1888   ace = (REG_ACE *)&acl->aces;
1889   for (i=0; i<num_aces; i++) {
1890     tmp->aces[i] = dup_ace(ace);
1891     ace = (REG_ACE *)((char *)ace + SVAL(&ace->length));
1892     /* XXX: FIXME, should handle malloc errors */
1893   }
1894
1895   return tmp;
1896 }
1897
1898 static
1899 SEC_DESC *process_sec_desc(REGF *regf, REG_SEC_DESC *sec_desc)
1900 {
1901   SEC_DESC *tmp = NULL;
1902   
1903   tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
1904
1905   if (!tmp) {
1906     return NULL;
1907   }
1908   
1909   tmp->rev = SVAL(&sec_desc->rev);
1910   tmp->type = SVAL(&sec_desc->type);
1911   if (verbose) fprintf(stdout, "SEC_DESC Rev: %0X, Type: %0X\n", 
1912                        tmp->rev, tmp->type);
1913   if (verbose) fprintf(stdout, "SEC_DESC Owner Off: %0X\n",
1914                        IVAL(&sec_desc->owner_off));
1915   if (verbose) fprintf(stdout, "SEC_DESC Group Off: %0X\n",
1916                        IVAL(&sec_desc->group_off));
1917   if (verbose) fprintf(stdout, "SEC_DESC DACL Off: %0X\n",
1918                        IVAL(&sec_desc->dacl_off));
1919   tmp->owner = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->owner_off)));
1920   if (!tmp->owner) {
1921     free(tmp);
1922     return NULL;
1923   }
1924   tmp->group = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->group_off)));
1925   if (!tmp->group) {
1926     free(tmp);
1927     return NULL;
1928   }
1929
1930   /* Now pick up the SACL and DACL */
1931
1932   if (sec_desc->sacl_off)
1933     tmp->sacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->sacl_off)));
1934   else
1935     tmp->sacl = NULL;
1936
1937   if (sec_desc->dacl_off)
1938     tmp->dacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->dacl_off)));
1939   else
1940     tmp->dacl = NULL;
1941
1942   return tmp;
1943 }
1944
1945 static
1946 KEY_SEC_DESC *process_sk(REGF *regf, SK_HDR *sk_hdr, int sk_off, int size)
1947 {
1948   KEY_SEC_DESC *tmp = NULL;
1949   int sk_next_off, sk_prev_off, sk_size;
1950   REG_SEC_DESC *sec_desc;
1951
1952   if (!sk_hdr) return NULL;
1953
1954   if (SVAL(&sk_hdr->SK_ID) != REG_SK_ID) {
1955     fprintf(stderr, "Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
1956             regf->regfile_name);
1957     return NULL;
1958   }
1959
1960   if (-size < (sk_size = IVAL(&sk_hdr->rec_size))) {
1961     fprintf(stderr, "Incorrect SK record size: %d vs %d. %s\n",
1962             -size, sk_size, regf->regfile_name);
1963     return NULL;
1964   }
1965
1966   /* 
1967    * Now, we need to look up the SK Record in the map, and return it
1968    * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
1969    * use that
1970    */
1971
1972   if (regf->sk_map &&
1973       ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
1974       && (tmp->state == SEC_DESC_OCU)) {
1975     tmp->ref_cnt++;
1976     return tmp;
1977   }
1978
1979   /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
1980
1981   assert(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
1982
1983   /*
1984    * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
1985    * new KEY_SEC_DESC to the mapping structure, since the offset supplied is 
1986    * the actual offset of structure. The same offset will be used by
1987    * all future references to this structure
1988    * We could put all this unpleasantness in a function.
1989    */
1990
1991   if (!tmp) {
1992     tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
1993     if (!tmp) return NULL;
1994     memset(tmp, 0, sizeof(KEY_SEC_DESC));
1995     
1996     /*
1997      * Allocate an entry in the SK_MAP ...
1998      * We don't need to free tmp, because that is done for us if the
1999      * sm_map entry can't be expanded when we need more space in the map.
2000      */
2001     
2002     if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
2003       return NULL;
2004     }
2005   }
2006
2007   tmp->ref_cnt++;
2008   tmp->state = SEC_DESC_OCU;
2009
2010   /*
2011    * Now, process the actual sec desc and plug the values in
2012    */
2013
2014   sec_desc = (REG_SEC_DESC *)&sk_hdr->sec_desc[0];
2015   tmp->sec_desc = process_sec_desc(regf, sec_desc);
2016
2017   /*
2018    * Now forward and back links. Here we allocate an entry in the sk_map
2019    * if it does not exist, and mark it reserved
2020    */
2021
2022   sk_prev_off = IVAL(&sk_hdr->prev_off);
2023   tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
2024   assert(tmp->prev != NULL);
2025   sk_next_off = IVAL(&sk_hdr->next_off);
2026   tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
2027   assert(tmp->next != NULL);
2028
2029   return tmp;
2030 }
2031
2032 /*
2033  * Process a VK header and return a value
2034  */
2035 static
2036 VAL_KEY *process_vk(REGF *regf, VK_HDR *vk_hdr, int size)
2037 {
2038   char val_name[1024];
2039   int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
2040   const char *val_type;
2041   VAL_KEY *tmp = NULL; 
2042
2043   if (!vk_hdr) return NULL;
2044
2045   if ((vk_id = SVAL(&vk_hdr->VK_ID)) != REG_VK_ID) {
2046     fprintf(stderr, "Unrecognized VK header ID: %0X, block: %0X, %s\n",
2047             vk_id, (int)vk_hdr, regf->regfile_name);
2048     return NULL;
2049   }
2050
2051   nam_len = SVAL(&vk_hdr->nam_len);
2052   val_name[nam_len] = '\0';
2053   flag = SVAL(&vk_hdr->flag);
2054   dat_type = IVAL(&vk_hdr->dat_type);
2055   dat_len = IVAL(&vk_hdr->dat_len);  /* If top bit, offset contains data */
2056   dat_off = IVAL(&vk_hdr->dat_off);
2057
2058   tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
2059   if (!tmp) {
2060     goto error;
2061   }
2062   memset(tmp, 0, sizeof(VAL_KEY));
2063   tmp->has_name = flag;
2064   tmp->data_type = dat_type;
2065
2066   if (flag & 0x01) {
2067     strncpy(val_name, vk_hdr->dat_name, nam_len);
2068     tmp->name = strdup(val_name);
2069     if (!tmp->name) {
2070       goto error;
2071     }
2072   }
2073   else
2074     strncpy(val_name, "<No Name>", 10);
2075
2076   /*
2077    * Allocate space and copy the data as a BLOB
2078    */
2079
2080   if (dat_len) {
2081     
2082     char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
2083     
2084     if (!dtmp) {
2085       goto error;
2086     }
2087
2088     tmp->data_blk = dtmp;
2089
2090     if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
2091       char *dat_ptr = LOCN(regf->base, dat_off);
2092       bcopy(dat_ptr, dtmp, dat_len);
2093     }
2094     else { /* The data is in the offset or type */
2095       /*
2096        * FIXME.
2097        * Some registry files seem to have wierd fields. If top bit is set,
2098        * but len is 0, the type seems to be the value ...
2099        * Not sure how to handle this last type for the moment ...
2100        */
2101       dat_len = dat_len & 0x7FFFFFFF;
2102       bcopy(&dat_off, dtmp, dat_len);
2103     }
2104
2105     tmp->data_len = dat_len;
2106   }
2107
2108   val_type = val_to_str(dat_type, reg_type_names);
2109
2110   /*
2111    * We need to save the data area as well
2112    */
2113
2114   if (verbose) fprintf(stdout, "  %s : %s : \n", val_name, val_type);
2115
2116   return tmp;
2117
2118  error:
2119   if (tmp) nt_delete_val_key(tmp);
2120   return NULL;
2121
2122 }
2123
2124 /*
2125  * Process a VL Header and return a list of values
2126  */
2127 static
2128 VAL_LIST *process_vl(REGF *regf, VL_TYPE vl, int count, int size)
2129 {
2130   int i, vk_off;
2131   VK_HDR *vk_hdr;
2132   VAL_LIST *tmp = NULL;
2133
2134   if (!vl) return NULL;
2135
2136   if (-size < (count+1)*sizeof(int)){
2137     fprintf(stderr, "Error in VL header format. Size less than space required. %d\n", -size);
2138     return NULL;
2139   }
2140
2141   tmp = (VAL_LIST *)malloc(sizeof(VAL_LIST) + (count - 1) * sizeof(VAL_KEY *));
2142   if (!tmp) {
2143     goto error;
2144   }
2145
2146   for (i=0; i<count; i++) {
2147     vk_off = IVAL(&vl[i]);
2148     vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
2149     tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr));
2150     if (!tmp->vals[i]){
2151       goto error;
2152     }
2153   }
2154
2155   tmp->val_count = count;
2156   tmp->max_vals = count;
2157
2158   return tmp;
2159
2160  error:
2161   /* XXX: FIXME, free the partially allocated structure */
2162   return NULL;
2163
2164
2165 /*
2166  * Process an LF Header and return a list of sub-keys
2167  */
2168 static
2169 KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size, REG_KEY *parent)
2170 {
2171   int count, i, nk_off;
2172   unsigned int lf_id;
2173   KEY_LIST *tmp;
2174
2175   if (!lf_hdr) return NULL;
2176
2177   if ((lf_id = SVAL(&lf_hdr->LF_ID)) != REG_LF_ID) {
2178     fprintf(stderr, "Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
2179             lf_id, (int)lf_hdr, regf->regfile_name);
2180     return NULL;
2181   }
2182
2183   assert(size < 0);
2184
2185   count = SVAL(&lf_hdr->key_count);
2186   if (verbose) fprintf(stdout, "Key Count: %u\n", count);
2187   if (count <= 0) return NULL;
2188
2189   /* Now, we should allocate a KEY_LIST struct and fill it in ... */
2190
2191   tmp = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (count - 1) * sizeof(REG_KEY *));
2192   if (!tmp) {
2193     goto error;
2194   }
2195
2196   tmp->key_count = count;
2197   tmp->max_keys = count;
2198
2199   for (i=0; i<count; i++) {
2200     NK_HDR *nk_hdr;
2201
2202     nk_off = IVAL(&lf_hdr->hr[i].nk_off);
2203     if (verbose) fprintf(stdout, "NK Offset: %0X\n", nk_off);
2204     nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
2205     tmp->keys[i] = nt_get_key_tree(regf, nk_hdr, BLK_SIZE(nk_hdr), parent);
2206     if (!tmp->keys[i]) {
2207       goto error;
2208     }
2209   }
2210
2211   return tmp;
2212
2213  error:
2214   if (tmp) nt_delete_key_list(tmp, False);
2215   return NULL;
2216 }
2217
2218 /*
2219  * This routine is passed an NK_HDR pointer and retrieves the entire tree
2220  * from there down. It returns a REG_KEY *.
2221  */
2222 static
2223 REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent)
2224 {
2225   REG_KEY *tmp = NULL, *own;
2226   int name_len, clsname_len, lf_off, val_off, val_count, sk_off, own_off;
2227   unsigned int nk_id;
2228   LF_HDR *lf_hdr;
2229   VL_TYPE *vl;
2230   SK_HDR *sk_hdr;
2231   char key_name[1024], cls_name[1024];
2232
2233   if (!nk_hdr) return NULL;
2234
2235   if ((nk_id = SVAL(&nk_hdr->NK_ID)) != REG_NK_ID) {
2236     fprintf(stderr, "Unrecognized NK Header format: %08X, Block: %0X. %s\n", 
2237             nk_id, (int)nk_hdr, regf->regfile_name);
2238     return NULL;
2239   }
2240
2241   assert(size < 0);
2242
2243   name_len = SVAL(&nk_hdr->nam_len);
2244   clsname_len = SVAL(&nk_hdr->clsnam_len);
2245
2246   /*
2247    * The value of -size should be ge 
2248    * (sizeof(NK_HDR) - 1 + name_len)
2249    * The -1 accounts for the fact that we included the first byte of 
2250    * the name in the structure. clsname_len is the length of the thing 
2251    * pointed to by clsnam_off
2252    */
2253
2254   if (-size < (sizeof(NK_HDR) - 1 + name_len)) {
2255     fprintf(stderr, "Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr);
2256     fprintf(stderr, "Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
2257             sizeof(NK_HDR), name_len, clsname_len);
2258     /*return NULL;*/
2259   }
2260
2261   if (verbose) fprintf(stdout, "NK HDR: Name len: %d, class name len: %d\n", 
2262                        name_len, clsname_len);
2263
2264   /* Fish out the key name and process the LF list */
2265
2266   assert(name_len < sizeof(key_name));
2267
2268   /* Allocate the key struct now */
2269   tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
2270   if (!tmp) return tmp;
2271   memset(tmp, 0, sizeof(REG_KEY));
2272
2273   tmp->type = (SVAL(&nk_hdr->type)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
2274   
2275   strncpy(key_name, nk_hdr->key_nam, name_len);
2276   key_name[name_len] = '\0';
2277
2278   if (verbose) fprintf(stdout, "Key name: %s\n", key_name);
2279
2280   tmp->name = strdup(key_name);
2281   if (!tmp->name) {
2282     goto error;
2283   }
2284
2285   /*
2286    * Fish out the class name, it is in UNICODE, while the key name is 
2287    * ASCII :-)
2288    */
2289
2290   if (clsname_len) { /* Just print in Ascii for now */
2291     char *clsnamep;
2292     int clsnam_off;
2293
2294     clsnam_off = IVAL(&nk_hdr->clsnam_off);
2295     clsnamep = LOCN(regf->base, clsnam_off);
2296     if (verbose) fprintf(stdout, "Class Name Offset: %0X\n", clsnam_off);
2297  
2298     memset(cls_name, 0, clsname_len);
2299     uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len);
2300     
2301     /*
2302      * I am keeping class name as an ascii string for the moment.
2303      * That means it needs to be converted on output.
2304      * It will also piss off people who need Unicode/UTF-8 strings. Sorry. 
2305      * XXX: FIXME
2306      */
2307
2308     tmp->class_name = strdup(cls_name);
2309     if (!tmp->class_name) {
2310       goto error;
2311     }
2312
2313     if (verbose) fprintf(stdout, "  Class Name: %s\n", cls_name);
2314
2315   }
2316
2317   /*
2318    * Process the owner offset ...
2319    */
2320
2321   own_off = IVAL(&nk_hdr->own_off);
2322   own = (REG_KEY *)LOCN(regf->base, own_off);
2323   if (verbose) fprintf(stdout, "Owner Offset: %0X\n", own_off);
2324
2325   if (verbose) fprintf(stdout, "  Owner locn: %0X, Our locn: %0X\n", 
2326                        (unsigned int)own, (unsigned int)nk_hdr);
2327
2328   /* 
2329    * We should verify that the owner field is correct ...
2330    * for now, we don't worry ...
2331    */
2332
2333   tmp->owner = parent;
2334
2335   /*
2336    * If there are any values, process them here
2337    */
2338
2339   val_count = IVAL(&nk_hdr->val_cnt);
2340   if (verbose) fprintf(stdout, "Val Count: %d\n", val_count);
2341   if (val_count) {
2342
2343     val_off = IVAL(&nk_hdr->val_off);
2344     vl = (VL_TYPE *)LOCN(regf->base, val_off);
2345     if (verbose) fprintf(stdout, "Val List Offset: %0X\n", val_off);
2346
2347     tmp->values = process_vl(regf, *vl, val_count, BLK_SIZE(vl));
2348     if (!tmp->values) {
2349       goto error;
2350     }
2351
2352   }
2353
2354   /* 
2355    * Also handle the SK header ...
2356    */
2357
2358   sk_off = IVAL(&nk_hdr->sk_off);
2359   sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
2360   if (verbose) fprintf(stdout, "SK Offset: %0X\n", sk_off);
2361
2362   if (sk_off != -1) {
2363
2364     tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
2365
2366   } 
2367
2368   lf_off = IVAL(&nk_hdr->lf_off);
2369   if (verbose) fprintf(stdout, "SubKey list offset: %0X\n", lf_off);
2370
2371   /*
2372    * No more subkeys if lf_off == -1
2373    */
2374
2375   if (lf_off != -1) {
2376
2377     lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
2378     
2379     tmp->sub_keys = process_lf(regf, lf_hdr, BLK_SIZE(lf_hdr), tmp);
2380     if (!tmp->sub_keys){
2381       goto error;
2382     }
2383
2384   }
2385
2386   return tmp;
2387
2388  error:
2389   if (tmp) nt_delete_reg_key(tmp, False);
2390   return NULL;
2391 }
2392
2393 static
2394 int nt_load_registry(REGF *regf)
2395 {
2396   REGF_HDR *regf_hdr;
2397   unsigned int regf_id, hbin_id;
2398   HBIN_HDR *hbin_hdr;
2399   NK_HDR *first_key;
2400
2401   /* Get the header */
2402
2403   if ((regf_hdr = nt_get_regf_hdr(regf)) == NULL) {
2404     return -1;
2405   }
2406
2407   /* Now process that header and start to read the rest in */
2408
2409   if ((regf_id = IVAL(&regf_hdr->REGF_ID)) != REG_REGF_ID) {
2410     fprintf(stderr, "Unrecognized NT registry header id: %0X, %s\n",
2411             regf_id, regf->regfile_name);
2412     return -1;
2413   }
2414
2415   /*
2416    * Validate the header ...
2417    */
2418   if (!valid_regf_hdr(regf_hdr)) {
2419     fprintf(stderr, "Registry file header does not validate: %s\n",
2420             regf->regfile_name);
2421     return -1;
2422   }
2423
2424   /* Update the last mod date, and then go get the first NK record and on */
2425
2426   TTTONTTIME(regf, IVAL(&regf_hdr->tim1), IVAL(&regf_hdr->tim2));
2427
2428   /* 
2429    * The hbin hdr seems to be just uninteresting garbage. Check that
2430    * it is there, but that is all.
2431    */
2432
2433   hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
2434
2435   if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID)) != REG_HBIN_ID) {
2436     fprintf(stderr, "Unrecognized registry hbin hdr ID: %0X, %s\n", 
2437             hbin_id, regf->regfile_name);
2438     return -1;
2439   } 
2440
2441   /*
2442    * Get a pointer to the first key from the hreg_hdr
2443    */
2444
2445   if (verbose) fprintf(stdout, "First Key: %0X\n",
2446                        IVAL(&regf_hdr->first_key));
2447
2448   first_key = (NK_HDR *)LOCN(regf->base, IVAL(&regf_hdr->first_key));
2449   if (verbose) fprintf(stdout, "First Key Offset: %0X\n", 
2450                        IVAL(&regf_hdr->first_key));
2451
2452   if (verbose) fprintf(stdout, "Data Block Size: %d\n",
2453                        IVAL(&regf_hdr->dblk_size));
2454
2455   if (verbose) fprintf(stdout, "Offset to next hbin block: %0X\n",
2456                        IVAL(&hbin_hdr->off_to_next));
2457
2458   if (verbose) fprintf(stdout, "HBIN block size: %0X\n",
2459                        IVAL(&hbin_hdr->blk_size));
2460
2461   /*
2462    * Now, get the registry tree by processing that NK recursively
2463    */
2464
2465   regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key), NULL);
2466
2467   assert(regf->root != NULL);
2468
2469   /*
2470    * Unmap the registry file, as we might want to read in another
2471    * tree etc.
2472    */
2473
2474   if (regf->base) munmap(regf->base, regf->sbuf.st_size);
2475   regf->base = NULL;
2476   close(regf->fd);    /* Ignore the error :-) */
2477
2478   return 1;
2479 }
2480
2481 /*
2482  * Allocate a new hbin block, set up the header for the block etc 
2483  */
2484 static
2485 HBIN_BLK *nt_create_hbin_blk(REGF *regf, int size)
2486 {
2487   HBIN_BLK *tmp;
2488   HBIN_HDR *hdr;
2489
2490   if (!regf || !size) return NULL;
2491
2492   /* Round size up to multiple of REGF_HDR_BLKSIZ */
2493
2494   size = (size + (REGF_HDR_BLKSIZ - 1)) & ~(REGF_HDR_BLKSIZ - 1);
2495
2496   tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
2497   memset(tmp, 0, sizeof(HBIN_BLK));
2498
2499   tmp->data = malloc(size);
2500   if (!tmp->data) goto error;
2501
2502   memset(tmp->data, 0, size);  /* Make it pristine */
2503
2504   tmp->size = size;
2505   tmp->file_offset = regf->blk_tail->file_offset + regf->blk_tail->size;
2506
2507   tmp->free_space = size - (sizeof(HBIN_HDR) - sizeof(HBIN_SUB_HDR));
2508   tmp->fsp_off = size - tmp->free_space;
2509
2510   /* 
2511    * Now, build the header in the data block 
2512    */
2513   hdr = (HBIN_HDR *)tmp->data;
2514   hdr->HBIN_ID = REG_HBIN_ID;
2515   hdr->off_from_first = tmp->file_offset - REGF_HDR_BLKSIZ;
2516   hdr->off_to_next = tmp->size;
2517   hdr->blk_size = tmp->size;
2518
2519   /*
2520    * Now link it in
2521    */
2522
2523   regf->blk_tail->next = tmp;
2524   regf->blk_tail = tmp;
2525   if (!regf->free_space) regf->free_space = tmp;
2526
2527   return tmp;
2528  error:
2529   if (tmp) free(tmp);
2530   return NULL;
2531 }
2532
2533 /*
2534  * Allocate a unit of space ... and return a pointer as function param
2535  * and the block's offset as a side effect
2536  */
2537 static
2538 void *nt_alloc_regf_space(REGF *regf, int size, unsigned int *off)
2539 {
2540   int tmp = 0;
2541   void *ret = NULL;
2542   HBIN_BLK *blk;
2543   
2544   if (!regf || !size || !off) return NULL;
2545
2546   assert(regf->blk_head != NULL);
2547
2548   /*
2549    * round up size to include header and then to 8-byte boundary
2550    */
2551   size = (size + 4 + 7) & ~7;
2552
2553   /*
2554    * Check if there is space, if none, grab a block
2555    */
2556   if (!regf->free_space) {
2557     if (!nt_create_hbin_blk(regf, REGF_HDR_BLKSIZ))
2558       return NULL;
2559   }
2560
2561   /*
2562    * Now, chain down the list of blocks looking for free space
2563    */
2564
2565   for (blk = regf->free_space; blk != NULL; blk = blk->next) {
2566     if (blk->free_space <= size) {
2567       tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
2568       ret = blk->data + blk->fsp_off;
2569       blk->free_space -= size;
2570       blk->fsp_off += size;
2571
2572       /* Insert the header */
2573       ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
2574
2575       /*
2576        * Fix up the free space ptr
2577        * If it is NULL, we fix it up next time
2578        */
2579
2580       if (!blk->free_space) 
2581         regf->free_space = blk->next;
2582
2583       *off = tmp;
2584       return (((char *)ret)+4);/* The pointer needs to be to the data struct */
2585     }
2586   }
2587
2588   /*
2589    * If we got here, we need to add another block, which might be 
2590    * larger than one block -- deal with that later
2591    */
2592   if (nt_create_hbin_blk(regf, REGF_HDR_BLKSIZ)) {
2593     blk = regf->free_space;
2594     tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
2595     ret = blk->data + blk->fsp_off;
2596     blk->free_space -= size;
2597     blk->fsp_off += size;
2598
2599     /* Insert the header */
2600     ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
2601
2602     /*
2603      * Fix up the free space ptr
2604      * If it is NULL, we fix it up next time
2605      */
2606     
2607     if (!blk->free_space) 
2608       regf->free_space = blk->next;
2609
2610     *off = tmp;
2611     return (((char *)ret) + 4);/* The pointer needs to be to the data struct */
2612   }
2613
2614   return NULL;
2615 }
2616
2617 /*
2618  * Compute the size of a SID stored ...
2619  */
2620 static
2621 unsigned int sid_size(sid_t *sid)
2622 {
2623   unsigned int size;
2624
2625   if (!sid) return 0;
2626
2627   size = 8 + (sid->auths * sizeof(unsigned int));
2628
2629   return size;
2630 }
2631
2632 /*
2633  * Compute the size of an ACE on disk from its components
2634  */
2635 static
2636 unsigned int ace_size(ACE *ace)
2637 {
2638   unsigned int size;
2639
2640   if (!ace) return 0;
2641
2642   size = 8 + sid_size(ace->trustee);
2643
2644   return size;
2645 }     
2646
2647 /* 
2648  * Compute the size of an ACL from its components ...
2649  */
2650 static
2651 unsigned int acl_size(ACL *acl)
2652 {
2653   unsigned int size;
2654   int i;
2655
2656   if (!acl) return 0;
2657
2658   size = 8; 
2659   for (i = 0; i < acl->num_aces; i++)
2660     size += ace_size(acl->aces[i]);
2661
2662   return size;
2663 }
2664
2665 /*
2666  * Compute the size of the sec desc as a self-relative SD
2667  */
2668 static
2669 unsigned int sec_desc_size(SEC_DESC *sd)
2670 {
2671   unsigned int size;
2672   
2673   if (!sd) return 0;
2674
2675   size = 20;
2676
2677   if (sd->owner) size += sid_size(sd->owner);
2678   if (sd->group) size += sid_size(sd->group);
2679   if (sd->sacl) size += acl_size(sd->sacl);
2680   if (sd->dacl) size += acl_size(sd->dacl);
2681
2682   return size;
2683 }
2684
2685 /*
2686  * Store a SID at the location provided
2687  */
2688 static
2689 int nt_store_SID(REGF *regf, sid_t *sid, unsigned char *locn)
2690 {
2691   int i;
2692   unsigned char *p = locn;
2693
2694   if (!regf || !sid || !locn) return 0;
2695
2696   *p = sid->ver; p++;
2697   *p = sid->auths; p++;
2698   
2699   for (i=0; i < 6; i++) {
2700     *p = sid->auth[i]; p++;
2701   }
2702
2703   for (i=0; i < sid->auths; i++) {
2704     SIVAL(p, sid->sub_auths[i]); p+=4;
2705   }
2706
2707   return p - locn;
2708   
2709 }
2710
2711 static
2712 int nt_store_ace(REGF *regf, ACE *ace, unsigned char *locn)
2713 {
2714   int size = 0;
2715   REG_ACE *reg_ace = (REG_ACE *)locn;
2716   unsigned char *p;
2717
2718   if (!regf || !ace || !locn) return 0;
2719
2720   reg_ace->type = ace->type;
2721   reg_ace->flags = ace->flags;
2722
2723   /* Deal with the length when we have stored the SID */
2724
2725   p = (unsigned char *)&reg_ace->perms;
2726
2727   SIVAL(p, ace->perms); p += 4;
2728
2729   size = nt_store_SID(regf, ace->trustee, p);
2730
2731   size += 8; /* Size of the fixed header */
2732
2733   p = (unsigned char *)&reg_ace->length;
2734
2735   SSVAL(p, size);
2736
2737   return size;
2738 }
2739
2740 /*
2741  * Store an ACL at the location provided
2742  */
2743 static
2744 int nt_store_acl(REGF *regf, ACL *acl, unsigned char *locn)
2745 {
2746   int size = 0, i;
2747   unsigned char *p = locn, *s;
2748
2749   if (!regf || !acl || !locn) return 0;
2750
2751   /*
2752    * Now store the header and then the ACEs ...
2753    */
2754
2755   SSVAL(p, acl->rev);
2756
2757   p += 2; s = p; /* Save this for the size field */
2758
2759   p += 2;
2760
2761   SIVAL(p, acl->num_aces);
2762
2763   p += 4;
2764
2765   for (i = 0; i < acl->num_aces; i++) {
2766     size = nt_store_ace(regf, acl->aces[i], p);
2767     p += size;
2768   }
2769
2770   size = s - locn;
2771   SSVAL(s, size);
2772   return size;
2773 }
2774
2775 /*
2776  * Flatten and store the Sec Desc 
2777  * Windows lays out the DACL first, but since there is no SACL, it might be
2778  * that first, then the owner, then the group SID. So, we do it that way
2779  * too.
2780  */
2781 static
2782 unsigned int nt_store_sec_desc(REGF *regf, SEC_DESC *sd, char *locn)
2783 {
2784   REG_SEC_DESC *rsd = (REG_SEC_DESC *)locn;
2785   unsigned int size = 0, off = 0;
2786
2787   if (!regf || !sd || !locn) return 0;
2788
2789   /* 
2790    * Now, fill in the first two fields, then lay out the various fields
2791    * as needed
2792    */
2793
2794   rsd->rev = 0x01;
2795   /* Self relative, DACL pres, owner and group not defaulted */
2796   rsd->type = 0x8004;  
2797
2798   off = 4 * sizeof(DWORD) + 4;
2799
2800   if (sd->sacl){
2801     size = nt_store_acl(regf, sd->sacl, (char *)(locn + off));
2802     rsd->sacl_off = off;
2803   }
2804   else
2805     rsd->sacl_off = 0;
2806
2807   off += size;
2808
2809   if (sd->dacl) {
2810     rsd->dacl_off = off;
2811     size = nt_store_acl(regf, sd->dacl, (char *)(locn + off));
2812   }
2813   else {
2814     rsd->dacl_off = 0;
2815   }
2816
2817   off += size;
2818
2819   /* Now the owner and group SIDs */
2820
2821   if (sd->owner) {
2822     rsd->owner_off = off;
2823     size = nt_store_SID(regf, sd->owner, (char *)(locn + off));
2824   }
2825   else {
2826     rsd->owner_off = 0;
2827   }
2828
2829   off += size;
2830
2831   if (sd->group) {
2832     rsd->group_off = off;
2833     size = nt_store_SID(regf, sd->group, (char *)(locn + off));
2834   }
2835   else {
2836     rsd->group_off = 0;
2837   }
2838
2839   off += size;
2840
2841   return size;
2842 }
2843
2844 /*
2845  * Store the security information
2846  *
2847  * If it has already been stored, just get its offset from record
2848  * otherwise, store it and record its offset
2849  */
2850 static
2851 unsigned int nt_store_security(REGF *regf, KEY_SEC_DESC *sec)
2852 {
2853   int size = 0;
2854   unsigned int sk_off;
2855   SK_HDR *sk_hdr;
2856   
2857   if (sec->offset) return sec->offset;
2858
2859   /*
2860    * OK, we don't have this one in the file yet. We must compute the 
2861    * size taken by the security descriptor as a self-relative SD, which
2862    * means making one pass over each structure and figuring it out
2863    */
2864
2865   size = sec_desc_size(sec->sec_desc);
2866
2867   /* Allocate that much space */
2868
2869   sk_hdr = nt_alloc_regf_space(regf, size, &sk_off);
2870   sec->sk_hdr = sk_hdr;
2871
2872   if (!sk_hdr) return 0;
2873
2874   /* Now, lay out the sec_desc in the space provided */
2875
2876   sk_hdr->SK_ID = REG_SK_ID;
2877   
2878   /* 
2879    * We can't deal with the next and prev offset in the SK_HDRs until the
2880    * whole tree has been stored, then we can go and deal with them
2881    */
2882
2883   sk_hdr->ref_cnt = sec->ref_cnt;
2884   sk_hdr->rec_size = size;       /* Is this correct */
2885
2886   /* Now, lay out the sec_desc */
2887
2888   if (!nt_store_sec_desc(regf, sec->sec_desc, (char *)&sk_hdr->sec_desc))
2889     return 0;
2890
2891   return sk_off;
2892
2893 }
2894
2895 /*
2896  * Store a VAL LIST
2897  */
2898 static
2899 int nt_store_val_list(REGF *regf, VAL_LIST * values)
2900 {
2901
2902   return 0;
2903 }
2904
2905 /*
2906  * Store a KEY in the file ...
2907  *
2908  * We store this depth first, and defer storing the lf struct until
2909  * all the sub-keys have been stored.
2910  * 
2911  * We store the NK hdr, any SK header, class name, and VK structure, then
2912  * recurse down the LF structures ... 
2913  * 
2914  * We return the offset of the NK struct
2915  * FIXME, FIXME, FIXME: Convert to using SIVAL and SSVAL ...
2916  */
2917 static
2918 int nt_store_reg_key(REGF *regf, REG_KEY *key)
2919 {
2920   NK_HDR *nk_hdr; 
2921   unsigned int nk_off, sk_off, size;
2922
2923   if (!regf || !key) return 0;
2924
2925   size = sizeof(NK_HDR) + strlen(key->name) - 1;
2926   nk_hdr = nt_alloc_regf_space(regf, size, &nk_off);
2927   if (!nk_hdr) goto error;
2928
2929   key->offset = nk_off;  /* We will need this later */
2930
2931   /*
2932    * Now fill in each field etc ...
2933    */
2934
2935   nk_hdr->NK_ID = REG_NK_ID; 
2936   if (key->type == REG_ROOT_KEY)
2937     nk_hdr->type = 0x2C;
2938   else
2939     nk_hdr->type = 0x20;
2940
2941   /* FIXME: Fill in the time of last update */
2942
2943   if (key->type != REG_ROOT_KEY)
2944     nk_hdr->own_off = key->owner->offset;
2945
2946   if (key->sub_keys)
2947     nk_hdr->subk_num = key->sub_keys->key_count;
2948
2949   /*
2950    * Now, process the Sec Desc and then store its offset
2951    */
2952
2953   sk_off = nt_store_security(regf, key->security);
2954   nk_hdr->sk_off = sk_off;
2955
2956   /*
2957    * Then, store the val list and store its offset
2958    */
2959   if (key->values) {
2960     nk_hdr->val_cnt = key->values->val_count;
2961     nk_hdr->val_off = nt_store_val_list(regf, key->values);
2962   }
2963   else {
2964     nk_hdr->val_off = -1;
2965     nk_hdr->val_cnt = 0;
2966   }
2967
2968   /*
2969    * Finally, store the subkeys, and their offsets
2970    */
2971
2972  error:
2973   return 0;
2974 }
2975
2976 /*
2977  * Store the registry header ...
2978  * We actually create the registry header block and link it to the chain
2979  * of output blocks.
2980  */
2981 static
2982 REGF_HDR *nt_get_reg_header(REGF *regf)
2983 {
2984   HBIN_BLK *tmp = NULL;
2985   
2986   tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
2987   if (!tmp) return 0;
2988
2989   memset(tmp, 0, sizeof(HBIN_BLK));
2990   tmp->type = REG_OUTBLK_HDR;
2991   tmp->size = REGF_HDR_BLKSIZ;
2992   tmp->data = malloc(REGF_HDR_BLKSIZ);
2993   if (!tmp->data) goto error;
2994
2995   memset(tmp->data, 0, REGF_HDR_BLKSIZ);  /* Make it pristine, unlike Windows */
2996   regf->blk_head = regf->blk_tail = tmp;
2997
2998   return (REGF_HDR *)tmp->data;
2999
3000  error:
3001   if (tmp) free(tmp);
3002   return NULL;
3003 }
3004
3005 /*
3006  * Store the registry in the output file
3007  * We write out the header and then each of the keys etc into the file
3008  * We have to flatten the data structure ...
3009  *
3010  * The structures are stored in a depth-first fashion, with all records
3011  * aligned on 8-byte boundaries, with sub-keys and values layed down before
3012  * the lists that contain them. SK records are layed down first, however.
3013  * The lf fields are layed down after all sub-keys have been layed down, it
3014  * seems, including the whole tree associated with each sub-key.
3015  */
3016 static
3017 int nt_store_registry(REGF *regf)
3018 {
3019   REGF_HDR *reg;
3020   int fkey, fd;
3021
3022   /*
3023    * Get a header ... and partially fill it in ...
3024    */
3025   reg = nt_get_reg_header(regf);
3026
3027   /*
3028    * Store the first key, which will store the whole thing
3029    */
3030   fkey = nt_store_reg_key(regf, regf->root);
3031
3032   /*
3033    * At this point we have the registry as a series of blocks, so
3034    * run down that series of blocks and save them ...
3035    */
3036
3037   if (!regf->outfile_name) {
3038     fprintf(stderr, "Cannot write file without a name!\n");
3039     return 0;
3040   }
3041
3042   if ((fd = open(regf->outfile_name, O_WRONLY, 0666)) < 0) {
3043     fprintf(stderr, "Unable to create file %s: %s\n", regf->outfile_name,
3044             strerror(errno));
3045     return 0;
3046   }
3047
3048   return 1;
3049 }
3050
3051 /*
3052  * Routines to parse a REGEDIT4 file
3053  * 
3054  * The file consists of:
3055  * 
3056  * REGEDIT4
3057  * \[[-]key-path\]\n
3058  * <value-spec>*
3059  *
3060  * Format:
3061  * [cmd:]name=type:value
3062  *
3063  * cmd = a|d|c|add|delete|change|as|ds|cs
3064  *
3065  * There can be more than one key-path and value-spec.
3066  *
3067  * Since we want to support more than one type of file format, we
3068  * construct a command-file structure that keeps info about the command file
3069  */
3070
3071 #define FMT_UNREC -1
3072 #define FMT_REGEDIT4 0
3073 #define FMT_EDITREG1_1 1
3074
3075 #define FMT_STRING_REGEDIT4 "REGEDIT4"
3076 #define FMT_STRING_EDITREG1_0 "EDITREG1.0"
3077
3078 #define CMD_NONE     0
3079 #define CMD_ADD_KEY  1
3080 #define CMD_DEL_KEY  2
3081
3082 #define CMD_KEY 1
3083 #define CMD_VAL 2
3084
3085 typedef struct val_spec_list {
3086   struct val_spec_list *next;
3087   char *name;
3088   int type;
3089   char *val;    /* Kept as a char string, really? */
3090 } VAL_SPEC_LIST;
3091
3092 typedef struct command_s {
3093   int cmd;
3094   char *key;
3095   int val_count;
3096   VAL_SPEC_LIST *val_spec_list, *val_spec_last;
3097 } CMD;
3098
3099 typedef struct cmd_line {
3100   int len, line_len;
3101   char *line;
3102 } CMD_LINE;
3103
3104 static
3105 void free_val_spec_list(VAL_SPEC_LIST *vl)
3106 {
3107   if (!vl) return;
3108   if (vl->name) free(vl->name);
3109   if (vl->val) free(vl->val);
3110   free(vl);
3111
3112 }
3113
3114 /* 
3115  * Some routines to handle lines of info in the command files
3116  */
3117 static
3118 void skip_to_eol(int fd)
3119 {
3120   int rc;
3121   char ch = 0;
3122
3123   while ((rc = read(fd, &ch, 1)) == 1) {
3124     if (ch == 0x0A) return;
3125   }
3126   if (rc < 0) {
3127     fprintf(stderr, "Could not read file descriptor: %d, %s\n",
3128             fd, strerror(errno));
3129     exit(1);
3130   }
3131 }
3132
3133 static
3134 void free_cmd(CMD *cmd)
3135 {
3136   if (!cmd) return;
3137
3138   while (cmd->val_spec_list) {
3139     VAL_SPEC_LIST *tmp;
3140
3141     tmp = cmd->val_spec_list;
3142     cmd->val_spec_list = tmp->next;
3143     free(tmp);
3144   }
3145
3146   free(cmd);
3147
3148 }
3149
3150 static
3151 void free_cmd_line(CMD_LINE *cmd_line)
3152 {
3153   if (cmd_line) {
3154     if (cmd_line->line) free(cmd_line->line);
3155     free(cmd_line);
3156   }
3157 }
3158
3159 static
3160 void print_line(struct cmd_line *cl)
3161 {
3162   char *pl;
3163
3164   if (!cl) return;
3165
3166   if ((pl = malloc(cl->line_len + 1)) == NULL) {
3167     fprintf(stderr, "Unable to allocate space to print line: %s\n",
3168             strerror(errno));
3169     exit(1);
3170   }
3171
3172   strncpy(pl, cl->line, cl->line_len);
3173   pl[cl->line_len] = 0;
3174
3175   fprintf(stdout, "%s\n", pl);
3176   free(pl);
3177 }
3178
3179 #define INIT_ALLOC 10 
3180
3181 /*
3182  * Read a line from the input file.
3183  * NULL returned when EOF and no chars read
3184  * Otherwise we return a cmd_line *
3185  * Exit if other errors
3186  */
3187 static
3188 struct cmd_line *get_cmd_line(int fd)
3189 {
3190   struct cmd_line *cl = (CMD_LINE *)malloc(sizeof(CMD_LINE));
3191   int i = 0, rc;
3192   unsigned char ch;
3193
3194   if (!cl) {
3195     fprintf(stderr, "Unable to allocate structure for command line: %s\n",
3196             strerror(errno));
3197     exit(1);
3198   }
3199
3200   cl->len = INIT_ALLOC;
3201
3202   /*
3203    * Allocate some space for the line. We extend later if needed.
3204    */
3205
3206   if ((cl->line = (char *)malloc(INIT_ALLOC)) == NULL) {
3207     fprintf(stderr, "Unable to allocate initial space for line: %s\n",
3208             strerror(errno));
3209     exit(1);
3210   }
3211
3212   /*
3213    * Now read in the chars to EOL. Don't store the EOL in the 
3214    * line. What about CR?
3215    */
3216
3217   while ((rc = read(fd, &ch, 1)) == 1 && ch != '\n') {
3218     if (ch == '\r') continue; /* skip CR */
3219     if (i == cl->len) {
3220       /*
3221        * Allocate some more memory
3222        */
3223       if ((cl->line = realloc(cl->line, cl->len + INIT_ALLOC)) == NULL) {
3224         fprintf(stderr, "Unable to realloc space for line: %s\n",
3225                 strerror(errno));
3226         exit(1);
3227       }
3228       cl->len += INIT_ALLOC;
3229     }
3230     cl->line[i] = ch;
3231     i++;
3232   }
3233
3234   /* read 0 and we were at loc'n 0, return NULL */
3235   if (rc == 0 && i == 0) {
3236     free_cmd_line(cl);
3237     return NULL;
3238   }
3239
3240   cl->line_len = i;
3241
3242   return cl;
3243
3244 }
3245
3246 /*
3247  * parse_value: parse out a value. We pull it apart as:
3248  *
3249  * <value> ::= <value-name>=<type>:<value-string>
3250  *
3251  * <value-name> ::= char-string-without-spaces | '"' char-string '"'
3252  *
3253  * If it parsed OK, return the <value-name> as a string, and the
3254  * value type and value-string in parameters.
3255  *
3256  * The value name can be empty. There can only be one empty name in 
3257  * a list of values. A value of - removes the value entirely.  
3258  */
3259 static
3260 char *dup_str(char *s, int len) 
3261
3262   char *nstr; 
3263   nstr = (char *)malloc(len + 1);
3264   if (nstr) {
3265     memcpy(nstr, s, len);
3266     nstr[len] = 0;
3267   }
3268   return nstr;
3269 }
3270
3271 static
3272 char *parse_name(char *nstr)
3273 {
3274   int len = 0, start = 0;
3275   if (!nstr) return NULL;
3276
3277   len = strlen(nstr);
3278
3279   while (len && nstr[len - 1] == ' ') len--;
3280
3281   nstr[len] = 0; /* Trim any spaces ... if there were none, doesn't matter */
3282
3283   /*
3284    * Beginning and end should be '"' or neither should be so
3285    */
3286   if ((nstr[0] == '"' && nstr[len - 1] != '"') ||
3287       (nstr[0] != '"' && nstr[len - 1] == '"'))
3288     return NULL;
3289
3290   if (nstr[0] == '"') {
3291     start = 1;
3292     len -= 2;
3293   }
3294
3295   return dup_str(&nstr[start], len);
3296 }
3297
3298 static
3299 int parse_value_type(char *tstr)
3300 {
3301   int len = strlen(tstr);
3302   
3303   while (len && tstr[len - 1] == ' ') len--;
3304   tstr[len] = 0;
3305
3306   if (strcmp(tstr, "REG_DWORD") == 0)
3307     return REG_TYPE_DWORD;
3308   else if (strcmp(tstr, "dword") == 0)
3309     return REG_TYPE_DWORD;
3310   else if (strcmp(tstr, "REG_EXPAND_SZ") == 0)
3311     return REG_TYPE_EXPANDSZ;
3312   else if (strcmp(tstr, "REG_BIN") == 0)
3313     return REG_TYPE_BIN;
3314   else if (strcmp(tstr, "REG_SZ") == 0)
3315     return REG_TYPE_REGSZ;
3316   else if (strcmp(tstr, "REG_MULTI_SZ") == 0)
3317     return REG_TYPE_MULTISZ;
3318   else if (strcmp(tstr, "-") == 0)
3319     return REG_TYPE_DELETE;
3320
3321   return 0;
3322 }
3323
3324 static
3325 char *parse_val_str(char *vstr)
3326 {
3327   
3328   return dup_str(vstr, strlen(vstr));
3329
3330 }
3331
3332 static
3333 char *parse_value(struct cmd_line *cl, int *vtype, char **val)
3334 {
3335   char *p1 = NULL, *p2 = NULL, *nstr = NULL, *tstr = NULL, *vstr = NULL;
3336   
3337   if (!cl || !vtype || !val) return NULL;
3338   if (!cl->line_len) return NULL;
3339
3340   p1 = dup_str(cl->line, cl->line_len);
3341   /* FIXME: Better return codes etc ... */
3342   if (!p1) return NULL;
3343   p2 = strchr(p1, '=');
3344   if (!p2) return NULL;
3345
3346   *p2 = 0; p2++; /* Split into two strings at p2 */
3347
3348   /* Now, parse the name ... */
3349
3350   nstr = parse_name(p1);
3351   if (!nstr) goto error;
3352
3353   /* Now, split the remainder and parse on type and val ... */
3354
3355   tstr = p2;
3356   while (*tstr == ' ') tstr++; /* Skip leading white space */
3357   p2 = strchr(p2, ':');
3358
3359   if (p2) {
3360     *p2 = 0; p2++; /* split on the : */
3361   }
3362
3363   *vtype = parse_value_type(tstr);
3364
3365   if (!vtype) goto error;
3366
3367   if (!p2 || !*p2) return nstr;
3368
3369   /* Now, parse the value string. It should return a newly malloc'd string */
3370   
3371   while (*p2 == ' ') p2++; /* Skip leading space */
3372   vstr = parse_val_str(p2);
3373
3374   if (!vstr) goto error;
3375
3376   *val = vstr;
3377
3378   return nstr;
3379
3380  error:
3381   if (p1) free(p1);
3382   if (nstr) free(nstr);
3383   if (vstr) free(vstr);
3384   return NULL;
3385 }
3386
3387 /*
3388  * Parse out a key. Look for a correctly formatted key [...] 
3389  * and whether it is a delete or add? A delete is signalled 
3390  * by a - in front of the key.
3391  * Assumes that there are no leading and trailing spaces
3392  */
3393
3394 static
3395 char *parse_key(struct cmd_line *cl, int *cmd)
3396 {
3397   int start = 1;
3398   char *tmp;
3399
3400   if (cl->line[0] != '[' ||
3401       cl->line[cl->line_len - 1] != ']') return NULL;
3402   if (cl->line_len == 2) return NULL;
3403   *cmd = CMD_ADD_KEY;
3404   if (cl->line[1] == '-') {
3405     if (cl->line_len == 3) return NULL;
3406     start = 2;
3407     *cmd = CMD_DEL_KEY;
3408   }
3409   tmp = malloc(cl->line_len - 1 - start + 1);
3410   if (!tmp) return tmp; /* Bail out on no mem ... FIXME */
3411   strncpy(tmp, &cl->line[start], cl->line_len - 1 - start);
3412   tmp[cl->line_len - 1 - start] = 0;
3413   return tmp;
3414 }
3415
3416 /*
3417  * Parse a line to determine if we have a key or a value
3418  * We only check for key or val ...
3419  */
3420
3421 static
3422 int parse_line(struct cmd_line *cl)
3423 {
3424
3425   if (!cl || cl->len == 0) return 0;
3426
3427   if (cl->line[0] == '[')  /* No further checking for now */
3428     return CMD_KEY;
3429   else 
3430     return CMD_VAL;
3431 }
3432
3433 /*
3434  * We seek to offset 0, read in the required number of bytes, 
3435  * and compare to the correct value.
3436  * We then seek back to the original location
3437  */
3438 static
3439 int regedit4_file_type(int fd)
3440 {
3441   int cur_ofs = 0;
3442   char desc[9];
3443
3444   cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
3445   if (cur_ofs < 0) {
3446     fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
3447     exit(1);  /* FIXME */
3448   }
3449
3450   if (cur_ofs) {
3451     lseek(fd, 0, SEEK_SET);
3452   }
3453
3454   if (read(fd, desc, 8) < 8) {
3455     fprintf(stderr, "Unable to read command file format\n"); 
3456     exit(2);  /* FIXME */
3457   }
3458
3459   desc[8] = 0;
3460
3461   if (strcmp(desc, FMT_STRING_REGEDIT4) == 0) {
3462     if (cur_ofs) {
3463       lseek(fd, cur_ofs, SEEK_SET);
3464     }
3465     else {
3466       skip_to_eol(fd);
3467     }
3468     return FMT_REGEDIT4;
3469   }
3470
3471   return FMT_UNREC;
3472 }
3473
3474 /*
3475  * Run though the data in the line and strip anything after a comment
3476  * char.
3477  */
3478 static
3479 void strip_comment(struct cmd_line *cl)
3480 {
3481   int i;
3482
3483   if (!cl) return;
3484
3485   for (i = 0; i < cl->line_len; i++) {
3486     if (cl->line[i] == ';') {
3487       cl->line_len = i;
3488       return;
3489     }
3490   }
3491 }
3492
3493 /* 
3494  * trim leading space
3495  */
3496
3497 static
3498 void trim_leading_spaces(struct cmd_line *cl)
3499 {
3500   int i;
3501
3502   if (!cl) return;
3503
3504   for (i = 0; i < cl->line_len; i++) {
3505     if (cl->line[i] != ' '){
3506       if (i) memcpy(cl->line, &cl->line[i], cl->line_len - i);
3507       return;
3508     }
3509   }
3510 }
3511
3512 /* 
3513  * trim trailing spaces
3514  */
3515 static
3516 void trim_trailing_spaces(struct cmd_line *cl)
3517 {
3518   int i;
3519
3520   if (!cl) return;
3521
3522   for (i = cl->line_len; i == 0; i--) {
3523     if (cl->line[i-1] != ' ' &&
3524         cl->line[i-1] != '\t') {
3525       cl->line_len = i;
3526     }
3527   }
3528 }
3529
3530 /* 
3531  * Get a command ... This consists of possibly multiple lines:
3532  * [key]
3533  * values*
3534  * possibly Empty line
3535  *
3536  * value ::= <value-name>=<value-type>':'<value-string>
3537  * <value-name> is some path, possibly enclosed in quotes ...
3538  * We alctually look for the next key to terminate a previous key
3539  * if <value-type> == '-', then it is a delete type.
3540  */
3541 static
3542 CMD *regedit4_get_cmd(int fd)
3543 {
3544   struct command_s *cmd = NULL;
3545   struct cmd_line *cl = NULL;
3546   struct val_spec_list *vl = NULL;
3547
3548   if ((cmd = (struct command_s *)malloc(sizeof(struct command_s))) == NULL) {
3549     fprintf(stderr, "Unable to malloc space for command: %s\n",
3550             strerror(errno));
3551     exit(1);
3552   }
3553
3554   cmd->cmd = CMD_NONE;
3555   cmd->key = NULL;
3556   cmd->val_count = 0;
3557   cmd->val_spec_list = cmd->val_spec_last = NULL;
3558   while ((cl = get_cmd_line(fd))) {
3559
3560     /*
3561      * If it is an empty command line, and we already have a key
3562      * then exit from here ... FIXME: Clean up the parser
3563      */
3564
3565     if (cl->line_len == 0 && cmd->key) {
3566       free_cmd_line(cl);
3567       break;
3568     } 
3569
3570     strip_comment(cl);     /* remove anything beyond a comment char */
3571     trim_trailing_spaces(cl);
3572     trim_leading_spaces(cl);
3573
3574     if (cl->line_len == 0) {    /* An empty line */
3575       free_cmd_line(cl);
3576     }
3577     else {                 /* Else, non-empty ... */
3578       /* 
3579        * Parse out the bits ... 
3580        */
3581       switch (parse_line(cl)) {
3582       case CMD_KEY:
3583         if ((cmd->key = parse_key(cl, &cmd->cmd)) == NULL) {
3584           fprintf(stderr, "Error parsing key from line: ");
3585           print_line(cl);
3586           fprintf(stderr, "\n");
3587         }
3588         break;
3589
3590       case CMD_VAL:
3591         /*
3592          * We need to add the value stuff to the list
3593          * There could be a \ on the end which we need to 
3594          * handle at some time
3595          */
3596         vl = (struct val_spec_list *)malloc(sizeof(struct val_spec_list));
3597         if (!vl) goto error;
3598         vl->next = NULL;
3599         vl->val = NULL;
3600         vl->name = parse_value(cl, &vl->type, &vl->val);
3601         if (!vl->name) goto error;
3602         if (cmd->val_spec_list == NULL) {
3603           cmd->val_spec_list = cmd->val_spec_last = vl;
3604         }
3605         else {
3606           cmd->val_spec_last->next = vl;
3607           cmd->val_spec_last = vl;
3608         }
3609         cmd->val_count++;
3610         break;
3611
3612       default:
3613         fprintf(stderr, "Unrecognized line in command file: \n");
3614         print_line(cl);
3615         break;
3616       }
3617     }
3618
3619   }
3620   if (!cmd->cmd) goto error; /* End of file ... */
3621
3622   return cmd;
3623
3624  error:
3625   if (vl) free(vl);
3626   if (cmd) free_cmd(cmd);
3627   return NULL;
3628 }
3629
3630 static
3631 int regedit4_exec_cmd(CMD *cmd)
3632 {
3633
3634   return 0;
3635 }
3636
3637 static
3638 int editreg_1_0_file_type(int fd)
3639 {
3640   int cur_ofs = 0;
3641   char desc[11];
3642
3643   cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
3644   if (cur_ofs < 0) {
3645     fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
3646     exit(1);  /* FIXME */
3647   }
3648
3649   if (cur_ofs) {
3650     lseek(fd, 0, SEEK_SET);
3651   }
3652
3653   if (read(fd, desc, 10) < 10) {
3654     fprintf(stderr, "Unable to read command file format\n"); 
3655     exit(2);  /* FIXME */
3656   }
3657
3658   desc[10] = 0;
3659
3660   if (strcmp(desc, FMT_STRING_EDITREG1_0) == 0) {
3661     lseek(fd, cur_ofs, SEEK_SET);
3662     return FMT_REGEDIT4;
3663   }
3664
3665   return FMT_UNREC;
3666 }
3667
3668 static
3669 CMD *editreg_1_0_get_cmd(int fd)
3670 {
3671   return NULL;
3672 }
3673
3674 static
3675 int editreg_1_0_exec_cmd(CMD *cmd)
3676 {
3677
3678   return -1;
3679 }
3680
3681 typedef struct command_ops_s {
3682   int type;
3683   int (*file_type)(int fd);
3684   CMD *(*get_cmd)(int fd);
3685   int (*exec_cmd)(CMD *cmd);
3686 } CMD_OPS;
3687
3688 CMD_OPS default_cmd_ops[] = {
3689   {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd},
3690   {1, editreg_1_0_file_type, editreg_1_0_get_cmd, editreg_1_0_exec_cmd},
3691   {-1,  NULL, NULL, NULL}
3692 }; 
3693
3694 typedef struct command_file_s {
3695   char *name;
3696   int type, fd;
3697   CMD_OPS cmd_ops;
3698 } CMD_FILE;
3699
3700 /*
3701  * Create a new command file structure
3702  */
3703
3704 static
3705 CMD_FILE *cmd_file_create(char *file)
3706 {
3707   CMD_FILE *tmp;
3708   struct stat sbuf;
3709   int i = 0;
3710
3711   /*
3712    * Let's check if the file exists ...
3713    * No use creating the cmd_file structure if the file does not exist
3714    */
3715
3716   if (stat(file, &sbuf) < 0) { /* Not able to access file */
3717
3718     return NULL;
3719   }
3720
3721   tmp = (CMD_FILE *)malloc(sizeof(CMD_FILE)); 
3722   if (!tmp) {
3723     return NULL;
3724   }
3725
3726   /*
3727    * Let's fill in some of the fields;
3728    */
3729
3730   tmp->name = strdup(file);
3731
3732   if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) {
3733     free(tmp);
3734     return NULL;
3735   }
3736
3737   /*
3738    * Now, try to find the format by indexing through the table
3739    */
3740   while (default_cmd_ops[i].type != -1) {
3741     if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) {
3742       tmp->cmd_ops = default_cmd_ops[i];
3743       return tmp;
3744     }
3745     i++;
3746   }
3747
3748   /* 
3749    * If we got here, return NULL, as we could not figure out the type
3750    * of command file.
3751    *
3752    * What about errors? 
3753    */
3754
3755   free(tmp);
3756   return NULL;
3757 }
3758
3759 /*
3760  * Extract commands from the command file, and execute them.
3761  * We pass a table of command callbacks for that 
3762  */
3763
3764 /*
3765  * Main code from here on ...
3766  */
3767
3768 /*
3769  * key print function here ...
3770  */
3771
3772 static
3773 int print_key(const char *path, char *name, char *class_name, int root, 
3774               int terminal, int vals)
3775 {
3776
3777   if (full_print || terminal) fprintf(stdout, "[%s%s]\n", path, name);
3778
3779   return 1;
3780 }
3781
3782 /*
3783  * Sec Desc print functions 
3784  */
3785
3786 static
3787 void print_type(unsigned char type)
3788 {
3789   switch (type) {
3790   case 0x00:
3791     fprintf(stdout, "    ALLOW");
3792     break;
3793   case 0x01:
3794     fprintf(stdout, "     DENY");
3795     break;
3796   case 0x02:
3797     fprintf(stdout, "    AUDIT");
3798     break;
3799   case 0x03:
3800     fprintf(stdout, "    ALARM");
3801     break;
3802   case 0x04:
3803     fprintf(stdout, "ALLOW CPD");
3804     break;
3805   case 0x05:
3806     fprintf(stdout, "OBJ ALLOW");
3807     break;
3808   case 0x06:
3809     fprintf(stdout, " OBJ DENY");
3810   default:
3811     fprintf(stdout, "  UNKNOWN");
3812     break;
3813   }
3814 }
3815
3816 static
3817 void print_flags(unsigned char flags)
3818 {
3819   char flg_output[21];
3820   int some = 0;
3821
3822   flg_output[0] = 0;
3823   if (!flags) {
3824     fprintf(stdout, "         ");
3825     return;
3826   }
3827   if (flags & 0x01) {
3828     if (some) strcat(flg_output, ",");
3829     some = 1;
3830     strcat(flg_output, "OI");
3831   }
3832   if (flags & 0x02) {
3833     if (some) strcat(flg_output, ",");
3834     some = 1;
3835     strcat(flg_output, "CI");
3836   }
3837   if (flags & 0x04) {
3838     if (some) strcat(flg_output, ",");
3839     some = 1;
3840     strcat(flg_output, "NP");
3841   }
3842   if (flags & 0x08) {
3843     if (some) strcat(flg_output, ",");
3844     some = 1;
3845     strcat(flg_output, "IO");
3846   }
3847   if (flags & 0x10) {
3848     if (some) strcat(flg_output, ",");
3849     some = 1;
3850     strcat(flg_output, "IA");
3851   }
3852   if (flags == 0xF) {
3853     if (some) strcat(flg_output, ",");
3854     some = 1;
3855     strcat(flg_output, "VI");
3856   }
3857   fprintf(stdout, " %s", flg_output);
3858 }
3859
3860 static
3861 void print_perms(int perms)
3862 {
3863   fprintf(stdout, " %8X", perms);
3864 }
3865
3866 static
3867 void print_sid(sid_t *sid)
3868 {
3869   int i, comps = sid->auths;
3870   fprintf(stdout, "S-%u-%u", sid->ver, sid->auth[5]);
3871
3872   for (i = 0; i < comps; i++) {
3873
3874     fprintf(stdout, "-%u", sid->sub_auths[i]);
3875
3876   }
3877   fprintf(stdout, "\n");
3878 }
3879
3880 static
3881 void print_acl(ACL *acl, const char *prefix)
3882 {
3883   int i;
3884
3885   for (i = 0; i < acl->num_aces; i++) {
3886     fprintf(stdout, ";;%s", prefix);
3887     print_type(acl->aces[i]->type);
3888     print_flags(acl->aces[i]->flags);
3889     print_perms(acl->aces[i]->perms);
3890     fprintf(stdout, " ");
3891     print_sid(acl->aces[i]->trustee);
3892   }
3893 }
3894
3895 static
3896 int print_sec(SEC_DESC *sec_desc)
3897 {
3898   if (!print_security) return 1;
3899   fprintf(stdout, ";;  SECURITY\n");
3900   fprintf(stdout, ";;   Owner: ");
3901   print_sid(sec_desc->owner);
3902   fprintf(stdout, ";;   Group: ");
3903   print_sid(sec_desc->group);
3904   if (sec_desc->sacl) {
3905     fprintf(stdout, ";;    SACL:\n");
3906     print_acl(sec_desc->sacl, " ");
3907   }
3908   if (sec_desc->dacl) {
3909     fprintf(stdout, ";;    DACL:\n");
3910     print_acl(sec_desc->dacl, " ");
3911   }
3912   return 1;
3913 }
3914
3915 /*
3916  * Value print function here ...
3917  */
3918 static
3919 int print_val(const char *path, char *val_name, int val_type, int data_len, 
3920               void *data_blk, int terminal, int first, int last)
3921 {
3922   char data_asc[1024];
3923
3924   memset(data_asc, 0, sizeof(data_asc));
3925   if (!terminal && first)
3926     fprintf(stdout, "%s\n", path);
3927   data_to_ascii((unsigned char *)data_blk, data_len, val_type, data_asc, 
3928                 sizeof(data_asc) - 1);
3929   fprintf(stdout, "  %s = %s : %s\n", (val_name?val_name:"<No Name>"), 
3930                    val_to_str(val_type, reg_type_names), data_asc);
3931   return 1;
3932 }
3933
3934 static
3935 void usage(void)
3936 {
3937   fprintf(stderr, "Usage: editreg [-f] [-v] [-p] [-k] [-s] [-c <command-file>] <registryfile>\n");
3938   fprintf(stderr, "Version: 0.1\n\n");
3939   fprintf(stderr, "\n\t-v\t sets verbose mode");
3940   fprintf(stderr, "\n\t-f\t sets full print mode where non-terminals are printed");
3941   fprintf(stderr, "\n\t-p\t prints the registry");
3942   fprintf(stderr, "\n\t-s\t prints security descriptors");
3943   fprintf(stderr, "\n\t-c <command-file>\t specifies a command file");
3944   fprintf(stderr, "\n");
3945 }
3946
3947 int main(int argc, char *argv[])
3948 {
3949   REGF *regf;
3950   extern char *optarg;
3951   extern int optind;
3952   int opt, print_keys = 0;
3953   int regf_opt = 1; /* Command name */
3954   int commands = 0, modified = 0;
3955   char *cmd_file_name = NULL;
3956   char *out_file_name = NULL;
3957   CMD_FILE *cmd_file = NULL;
3958   sid_t *lsid;
3959
3960   if (argc < 2) {
3961     usage();
3962     exit(1);
3963   }
3964   
3965   /* 
3966    * Now, process the arguments
3967    */
3968
3969   while ((opt = getopt(argc, argv, "fspvko:O:c:")) != EOF) {
3970     switch (opt) {
3971     case 'c':
3972       commands = 1;
3973       cmd_file_name = optarg;
3974       regf_opt += 2;
3975       break;
3976
3977     case 'f':
3978       full_print = 1;
3979       regf_opt++;
3980       break;
3981
3982     case 'o':
3983       out_file_name = optarg;
3984       regf_opt += 2;
3985       break;
3986
3987     case 'O':
3988       def_owner_sid_str = strdup(optarg);
3989       regf_opt += 2;
3990       if (!sid_string_to_sid(&lsid, def_owner_sid_str)) {
3991         fprintf(stderr, "Default Owner SID: %s is incorrectly formatted\n",
3992                 def_owner_sid_str);
3993         free(&def_owner_sid_str[0]);
3994         def_owner_sid_str = NULL;
3995       }
3996       else 
3997         nt_delete_sid(lsid);
3998       break;
3999
4000     case 'p':
4001       print_keys++;
4002       regf_opt++;
4003       break;
4004
4005     case 's':
4006       print_security++;
4007       full_print++;
4008       regf_opt++;
4009       break;
4010
4011     case 'v':
4012       verbose++;
4013       regf_opt++;
4014       break;
4015
4016     case 'k':
4017       regf_opt++;
4018       break;
4019
4020     default:
4021       usage();
4022       exit(1);
4023       break;
4024     }
4025   }
4026
4027   /*
4028    * We only want to complain about the lack of a default owner SID if
4029    * we need one. This approximates that need 
4030    */
4031   if (!def_owner_sid_str) {
4032     def_owner_sid_str = "S-1-5-21-1-2-3-4";
4033     if (out_file_name || verbose)
4034       fprintf(stderr, "Warning, default owner SID not set. Setting to %s\n",
4035               def_owner_sid_str);
4036   }
4037
4038   if ((regf = nt_create_regf()) == NULL) {
4039     fprintf(stderr, "Could not create registry object: %s\n", strerror(errno));
4040     exit(2);
4041   }
4042
4043   if (regf_opt < argc) { /* We have a registry file */
4044     if (!nt_set_regf_input_file(regf, argv[regf_opt])) {
4045       fprintf(stderr, "Could not set name of registry file: %s, %s\n", 
4046               argv[regf_opt], strerror(errno));
4047       exit(3);
4048     }
4049
4050     /* Now, open it, and bring it into memory :-) */
4051
4052     if (nt_load_registry(regf) < 0) {
4053       fprintf(stderr, "Could not load registry: %s\n", argv[1]);
4054       exit(4);
4055     }
4056   }
4057
4058   if (out_file_name) {
4059     if (!nt_set_regf_output_file(regf, out_file_name)) {
4060       fprintf(stderr, "Could not set name of output registry file: %s, %s\n", 
4061               out_file_name, strerror(errno));
4062       exit(3);
4063     }
4064
4065   }
4066
4067   if (commands) {
4068     CMD *cmd;
4069
4070     cmd_file = cmd_file_create(cmd_file_name);
4071
4072     while ((cmd = cmd_file->cmd_ops.get_cmd(cmd_file->fd)) != NULL) {
4073
4074       /*
4075        * Now, apply the requests to the tree ...
4076        */
4077       switch (cmd->cmd) {
4078       case CMD_ADD_KEY: {
4079         REG_KEY *tmp = NULL;
4080
4081         tmp = nt_find_key_by_name(regf->root, cmd->key);
4082
4083         /* If we found it, apply the other bits, else create such a key */
4084
4085         if (!tmp) {
4086           tmp = nt_add_reg_key(regf, cmd->key, True);
4087           modified = 1;
4088         }
4089
4090         while (cmd->val_count) {
4091           VAL_SPEC_LIST *val = cmd->val_spec_list;
4092           VAL_KEY *reg_val = NULL;
4093           
4094           if (val->type == REG_TYPE_DELETE) {
4095             reg_val = nt_delete_reg_value(tmp, val -> name);
4096             if (reg_val) nt_delete_val_key(reg_val);
4097             modified = 1;
4098           }
4099           else {
4100             reg_val = nt_add_reg_value(tmp, val->name, val->type, 
4101                                        val->val);
4102             modified = 1;
4103           }
4104
4105           cmd->val_spec_list = val->next;
4106           free_val_spec_list(val);
4107           cmd->val_count--;
4108         }
4109
4110         break;
4111       }
4112       
4113       case CMD_DEL_KEY:
4114         /* 
4115          * Any value does not matter ...
4116          * Find the key if it exists, and delete it ...
4117          */
4118         
4119         nt_delete_key_by_name(regf, cmd->key);
4120         modified = 1;
4121         break;
4122       }
4123     }
4124     free_cmd(cmd);
4125   }
4126
4127   /*
4128    * At this point, we should have a registry in memory and should be able
4129    * to iterate over it.
4130    */
4131
4132   if (print_keys) {
4133     nt_key_iterator(regf, regf->root, 0, "", print_key, print_sec, print_val);
4134   }
4135
4136   /*
4137    * If there was an out_file_name and the tree was modified, print it
4138    */
4139   if (modified && out_file_name) 
4140     if (!nt_store_registry(regf)) {
4141       fprintf(stdout, "Error storing registry\n");
4142     }
4143
4144   return 0;
4145 }