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