2 Samba Unix/Linux SMB client utility libeditreg.c
3 Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
4 Copyright (C) 2003-2005 Jelmer Vernooij, jelmer@samba.org
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.
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.
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. */
20 /*************************************************************************
22 A utility to edit a Windows NT/2K etc registry file.
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
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.
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.
35 The windows NT registry has 2 different blocks, where one can occur many
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!
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
55 0x00000024 D-Word Offset of 1st key record
56 0x00000028 D-Word Size of the data-blocks (Filesize-4kb)
58 0x000001FC D-Word Sum of all D-Words from 0x00000000 to
59 0x000001FB //XOR of all words. Nigel
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...
67 hbin probably means hive-bin (what bin stands for I don't know)
68 This block is always a multiple
71 Inside these hbin-blocks the different records are placed. The memory-
72 management looks like a C-compiler heap management to me...
77 0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268
78 0x0004 D-Word Offset from the 1st hbin-Block
79 0x0008 D-Word Offset to the next hbin-Block
80 0x001C D-Word Block-size
82 The values in 0x0008 and 0x001C should be the same, so I don't know
83 if they are correct or swapped...
85 From offset 0x0020 inside a hbin-block data is stored with the following
89 0x0000 D-Word Data-block size //this size must be a
93 If the size field is negative (bit 31 set), the corresponding block
94 is free and has a size of -blocksize!
96 That does not seem to be true. All block lengths seem to be negative!
99 The data is stored as one record per block. Block size is a multiple
100 of 4 and the last block reaches the next hbin-block, leaving no room.
102 (That also seems incorrect, in that the block size if a multiple of 8.
103 That is, the block, including the 4 byte header, is always a multiple of
104 8 bytes. Richard Sharpe.)
106 Records in the hbin-blocks
107 ==========================
111 The nk-record can be treated as a combination of tree-record and
112 key-record of the win 95 registry.
116 The lf-record is the counterpart to the RGKN-record (the
121 The vk-record consists information to a single value (value key).
125 sk (? Security Key ?) is the ACL of the registry.
129 The value-lists contain information about which values are inside a
130 sub-key and don't have a header.
134 The datas of the registry are (like the value-list) stored without a
137 All offset-values are relative to the first hbin-block and point to the
138 block-size field of the record-entry. to get the file offset, you have to add
139 the header size (4kb) and the size field (4 bytes)...
144 0x0000 Word ID: ASCII-"nk" = 0x6B6E
145 0x0002 Word for the root-key: 0x2C, otherwise 0x20 //key symbolic links 0x10. Nigel
146 0x0004 Q-Word write-date/time in windows nt notation
147 0x0010 D-Word Offset of Owner/Parent key
148 0x0014 D-Word number of sub-Keys
149 0x001C D-Word Offset of the sub-key lf-Records
150 0x0024 D-Word number of values
151 0x0028 D-Word Offset of the Value-List
152 0x002C D-Word Offset of the sk-Record
154 0x0030 D-Word Offset of the Class-Name //see NK structure for the use of these fields. Nigel
155 0x0044 D-Word Unused (data-trash) //some kind of run time index. Does not appear to be important. Nigel
156 0x0048 Word name-length
157 0x004A Word class-name length
163 0x0000 D-Word Offset 1st Value
164 0x0004 D-Word Offset 2nd Value
165 0x???? D-Word Offset nth Value
167 To determine the number of values, you have to look at the owner-nk-record!
172 0x0000 Word ID: ASCII-"vk" = 0x6B76
173 0x0002 Word name length
174 0x0004 D-Word length of the data //if top bit is set when offset contains data. Nigel
175 0x0008 D-Word Offset of Data
176 0x000C D-Word Type of value
178 0x0012 Word Unused (data-trash)
181 If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
183 If the data-size is lower 5, the data-offset value is used to store the data itself!
188 0x0001 RegSZ: character string (in UNICODE!)
189 0x0002 ExpandSZ: string with "%var%" expanding (UNICODE!)
190 0x0003 RegBin: raw-binary value
191 0x0004 RegDWord: Dword
192 0x0007 RegMultiSZ: multiple strings, seperated with 0
198 0x0000 Word ID: ASCII-"lf" = 0x666C
199 0x0002 Word number of keys
200 0x0004 ???? Hash-Records
205 0x0000 D-Word Offset of corresponding "nk"-Record
206 0x0004 D-Word ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
208 Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the
209 key-name you have to change the hash-value too!
211 //These hashrecords must be sorted low to high within the lf record. Nigel.
215 (due to the complexity of the SAM-info, not clear jet)
216 (This is just a self-relative security descriptor in the data. R Sharpe.)
220 0x0000 Word ID: ASCII-"sk" = 0x6B73
222 0x0004 D-Word Offset of previous "sk"-Record
223 0x0008 D-Word Offset of next "sk"-Record
224 0x000C D-Word usage-counter
225 0x0010 D-Word Size of "sk"-record in bytes
227 relative security desciptor. Nigel
228 ???? ???? Security and auditing settings...
231 The usage counter counts the number of references to this
232 "sk"-record. You can use one "sk"-record for the entire registry!
234 Windows nt date/time format
235 ===========================
236 The time-format is a 64-bit integer which is incremented every
237 0,0000001 seconds by 1 (I don't know how accurate it realy is!)
238 It starts with 0 at the 1st of january 1601 0:00! All values are
239 stored in GMT time! The time-zone is important to get the real
242 Common values for win95 and win-nt
243 ==================================
244 Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
245 If a value has no name (length=0, flag(bit 0)=0), it is treated as the
247 If a value has no data (length=0), it is displayed as empty.
249 simplyfied win-3.?? registry:
250 =============================
253 | next rec. |---+ +----->+------------+
254 | first sub | | | | Usage cnt. |
255 | name | | +-->+------------+ | | length |
256 | value | | | | next rec. | | | text |------->+-------+
257 +-----------+ | | | name rec. |--+ +------------+ | xxxxx |
258 +------------+ | | value rec. |-------->+------------+ +-------+
259 v | +------------+ | Usage cnt. |
260 +-----------+ | | length |
261 | next rec. | | | text |------->+-------+
262 | first sub |------+ +------------+ | xxxxx |
267 Greatly simplyfied structure of the nt-registry:
268 ================================================
270 +---------------------------------------------------------------+
273 +---------+ +---------->+-----------+ +----->+---------+ |
274 | "nk" | | | lf-rec. | | | nk-rec. | |
275 | ID | | | # of keys | | | parent |---+
276 | Date | | | 1st key |--+ | .... |
277 | parent | | +-----------+ +---------+
279 | values |--------------------->+----------+
280 | SK-rec. |---------------+ | 1. value |--> +----------+
281 | class |--+ | +----------+ | vk-rec. |
282 +---------+ | | | .... |
283 v | | data |--> +-------+
284 +------------+ | +----------+ | xxxxx |
285 | Class name | | +-------+
288 +---------+ +---------+
289 +----->| next sk |--->| Next sk |--+
290 | +---| prev sk |<---| prev sk | |
291 | | | .... | | ... | |
292 | | +---------+ +---------+ |
295 | +--------------------+ |
296 +----------------------------------+
298 ---------------------------------------------------------------------------
300 Hope this helps.... (Although it was "fun" for me to uncover this things,
301 it took me several sleepless nights ;)
305 *************************************************************************/
307 #include "includes.h"
308 #include "registry.h"
309 #include "system/filesys.h"
310 #include "system/shmem.h"
312 #define REG_KEY_LIST_SIZE 10
313 #define FLAG_HAS_NAME 0x01
317 * Structures for dealing with the on-disk format of the registry
320 const char *def_owner_sid_str = NULL;
323 * These definitions are for the in-memory registry structure.
324 * It is a tree structure that mimics what you see with tools like regedit
329 * Definition of a Key. It has a name, classname, date/time last modified,
330 * sub-keys, values, and a security descriptor
333 #define REG_ROOT_KEY 1
334 #define REG_SUB_KEY 2
335 #define REG_SYM_LINK 3
338 * All of the structures below actually have a four-byte length before them
339 * which always seems to be negative. The following macro retrieves that
343 #define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
345 typedef struct sk_struct SK_HDR;
347 * This structure keeps track of the output format of the registry
349 #define REG_OUTBLK_HDR 1
350 #define REG_OUTBLK_HBIN 2
352 typedef struct regf_block {
353 uint32_t REGF_ID; /* regf */
354 uint32_t update_counter1;
355 uint32_t update_counter2;
357 uint32_t uk3; /* 1 */
358 uint32_t uk4; /* 3 */
359 uint32_t uk5; /* 0 */
360 uint32_t uk6; /* 1 */
361 uint32_t first_key; /* offset */
363 uint32_t uk7; /* 1 */
364 wchar_t filename[64];
366 uint32_t chksum; /* Checksum of first 0x200 bytes */
369 typedef struct hbin_sub_struct {
374 typedef struct hbin_struct {
375 uint32_t HBIN_ID; /* hbin */
376 uint32_t off_from_first;
377 uint32_t off_to_next;
383 HBIN_SUB_HDR hbin_sub_hdr;
386 typedef struct nk_struct {
404 char key_nam[1]; /* Actual length determined by nam_len */
417 typedef struct key_sec_desc_s {
418 struct key_sec_desc_s *prev, *next;
422 SK_HDR *sk_hdr; /* This means we must keep the registry in memory */
423 struct security_descriptor *sec_desc;
426 /* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
427 typedef struct sk_map_s {
429 KEY_SEC_DESC *key_sec_desc;
432 typedef struct vk_struct {
435 uint32_t dat_len; /* If top-bit set, offset contains the data */
438 uint16_t flag; /* =1, has name, else no name (=Default). */
440 char dat_name[1]; /* Name starts here ... */
443 typedef uint32_t VL_TYPE[1]; /* Value list is an array of vk rec offsets */
445 typedef struct hash_struct {
451 typedef struct lf_struct {
454 struct hash_struct hr[1]; /* Array of hash records, depending on key_count */} LF_HDR;
459 * This structure keeps track of the output format of the registry
461 #define REG_OUTBLK_HDR 1
462 #define REG_OUTBLK_HBIN 2
464 typedef struct hbin_blk_s {
466 struct hbin_blk_s *next;
467 char *data; /* The data block */
468 uint_t file_offset; /* Offset in file */
469 uint_t free_space; /* Amount of free space in block */
470 uint_t fsp_off; /* Start of free space in block */
471 int complete, stored;
474 typedef struct regf_struct_s {
480 NTTIME last_mod_time;
482 int sk_count, sk_map_size;
484 const char *owner_sid_str;
485 struct security_descriptor *def_sec_desc;
487 * These next pointers point to the blocks used to contain the
488 * keys when we are preparing to write them to a file
490 HBIN_BLK *blk_head, *blk_tail, *free_space;
493 static uint32_t str_to_dword(const char *a) {
495 unsigned long ret = 0;
496 for(i = strlen(a)-1; i >= 0; i--) {
497 ret = ret * 0x100 + a[i];
507 static BOOL nt_create_ace(SEC_ACE *ace, int type, int flags, uint32_t perms, const char *sid)
512 if(!string_to_sid(&s, sid))return False;
513 init_sec_ace(ace, &s, type, access, flags);
518 * Create a default ACL
520 static SEC_ACL *nt_create_default_acl(struct registry_hive *regf)
524 if(!nt_create_ace(&aces[0], 0x00, 0x0, 0xF003F, regf->owner_sid_str)) return NULL;
525 if(!nt_create_ace(&aces[1], 0x00, 0x0, 0xF003F, "S-1-5-18")) return NULL;
526 if(!nt_create_ace(&aces[2], 0x00, 0x0, 0xF003F, "S-1-5-32-544")) return NULL;
527 if(!nt_create_ace(&aces[3], 0x00, 0x0, 0x20019, "S-1-5-12")) return NULL;
528 if(!nt_create_ace(&aces[4], 0x00, 0x0B, GENERIC_RIGHT_ALL_ACCESS, regf->owner_sid_str)) return NULL;
529 if(!nt_create_ace(&aces[5], 0x00, 0x0B, 0x10000000, "S-1-5-18")) return NULL;
530 if(!nt_create_ace(&aces[6], 0x00, 0x0B, 0x10000000, "S-1-5-32-544")) return NULL;
531 if(!nt_create_ace(&aces[7], 0x00, 0x0B, 0x80000000, "S-1-5-12")) return NULL;
533 return make_sec_acl(regf->mem_ctx, 2, 8, aces);
537 * Create a default security descriptor. We pull in things from env
540 static SEC_DESC *nt_create_def_sec_desc(struct registry_hive *regf)
544 tmp = malloc_p(SEC_DESC);
547 tmp->type = SEC_DESC_SELF_RELATIVE | SEC_DESC_DACL_PRESENT;
548 if (!string_to_sid(tmp->owner_sid, "S-1-5-32-544")) goto error;
549 if (!string_to_sid(tmp->grp_sid, "S-1-5-18")) goto error;
551 tmp->dacl = nt_create_default_acl(regf);
556 if (tmp) nt_delete_sec_desc(tmp);
561 * We will implement inheritence that is based on what the parent's SEC_DESC
562 * says, but the Owner and Group SIDs can be overwridden from the command line
563 * and additional ACEs can be applied from the command line etc.
565 static KEY_SEC_DESC *nt_inherit_security(struct registry_key *key)
568 if (!key) return NULL;
569 return key->security;
573 * Create an initial security descriptor and init other structures, if needed
574 * We assume that the initial security stuff is empty ...
576 static KEY_SEC_DESC *nt_create_init_sec(struct registry_hive *h)
578 REGF *regf = h->backend_data;
579 KEY_SEC_DESC *tsec = NULL;
581 tsec = malloc_p(KEY_SEC_DESC);
584 tsec->state = SEC_DESC_NBK;
587 tsec->sec_desc = regf->def_sec_desc;
594 * Get the starting record for NT Registry file
598 * Where we keep all the regf stuff for one registry.
599 * This is the structure that we use to tie the in memory tree etc
600 * together. By keeping separate structs, we can operate on different
601 * registries at the same time.
602 * Currently, the SK_MAP is an array of mapping structure.
603 * Since we only need this on input and output, we fill in the structure
604 * as we go on input. On output, we know how many SK items we have, so
605 * we can allocate the structure as we need to.
606 * If you add stuff here that is dynamically allocated, add the
607 * appropriate free statements below.
610 #define REG_HANDLE_REGTYPE_NONE 0
611 #define REG_HANDLE_REGTYPE_NT 1
612 #define REG_HANDLE_REGTYPE_W9X 2
614 #define TTTONTTIME(r, t1, t2) (r)->last_mod_time = (t1) | (((uint64_t)(t2)) << 32)
616 #define REGF_HDR_BLKSIZ 0x1000
618 #define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4)
619 #define LOCN(base, f) ((base) + OFF(f))
621 /* Get the header of the registry. Return a pointer to the structure
622 * If the mmap'd area has not been allocated, then mmap the input file
624 static REGF_HDR *nt_get_regf_hdr(struct registry_hive *h)
626 REGF *regf = h->backend_data;
627 SMB_REG_ASSERT(regf);
629 if (!regf->base) { /* Try to mmap etc the file */
631 if ((regf->fd = open(h->location, O_RDONLY, 0000)) <0) {
632 return NULL; /* What about errors? */
635 if (fstat(regf->fd, ®f->sbuf) < 0) {
639 regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
641 if ((int)regf->base == 1) {
642 DEBUG(0,("Could not mmap file: %s, %s\n", h->location,
649 * At this point, regf->base != NULL, and we should be able to read the
653 SMB_REG_ASSERT(regf->base != NULL);
655 return (REGF_HDR *)regf->base;
659 * Validate a regf header
660 * For now, do nothing, but we should check the checksum
662 static int valid_regf_hdr(REGF_HDR *regf_hdr)
664 if (!regf_hdr) return 0;
672 * Process an SK header ...
673 * Every time we see a new one, add it to the map. Otherwise, just look it up.
674 * We will do a simple linear search for the moment, since many KEYs have the
675 * same security descriptor.
676 * We allocate the map in increments of 10 entries.
680 * Create a new entry in the map, and increase the size of the map if needed
682 static SK_MAP *alloc_sk_map_entry(struct registry_hive *h, KEY_SEC_DESC *tmp, int sk_off)
684 REGF *regf = h->backend_data;
685 if (!regf->sk_map) { /* Allocate a block of 10 */
686 regf->sk_map = malloc_array_p(SK_MAP, 10);
687 regf->sk_map_size = 10;
689 (regf->sk_map)[0].sk_off = sk_off;
690 (regf->sk_map)[0].key_sec_desc = tmp;
692 else { /* Simply allocate a new slot, unless we have to expand the list */
693 int ndx = regf->sk_count;
694 if (regf->sk_count >= regf->sk_map_size) {
695 regf->sk_map = (SK_MAP *)realloc(regf->sk_map,
696 (regf->sk_map_size + 10)*sizeof(SK_MAP));
702 * ndx already points at the first entry of the new block
704 regf->sk_map_size += 10;
706 (regf->sk_map)[ndx].sk_off = sk_off;
707 (regf->sk_map)[ndx].key_sec_desc = tmp;
714 * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
717 KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
721 if (!sk_map) return NULL;
723 for (i = 0; i < count; i++) {
725 if (sk_map[i].sk_off == sk_off)
726 return sk_map[i].key_sec_desc;
735 * Allocate a KEY_SEC_DESC if we can't find one in the map
737 static KEY_SEC_DESC *lookup_create_sec_key(struct registry_hive *h, SK_MAP *sk_map, int sk_off)
739 REGF *regf = h->backend_data;
740 KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
745 else { /* Allocate a new one */
746 tmp = malloc_p(KEY_SEC_DESC);
747 memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */
748 tmp->state = SEC_DESC_RES;
749 if (!alloc_sk_map_entry(h, tmp, sk_off)) {
756 static SEC_DESC *process_sec_desc(struct registry_hive *regf, SEC_DESC *sec_desc)
758 SEC_DESC *tmp = NULL;
760 tmp = malloc_p(SEC_DESC);
762 tmp->revision = SVAL(&sec_desc->revision,0);
763 tmp->type = SVAL(&sec_desc->type,0);
764 DEBUG(2, ("SEC_DESC Rev: %0X, Type: %0X\n", tmp->revision, tmp->type));
765 DEBUGADD(2, ("SEC_DESC Owner Off: %0X\n", IVAL(&sec_desc->off_owner_sid,0)));
766 DEBUGADD(2, ("SEC_DESC Group Off: %0X\n", IVAL(&sec_desc->off_grp_sid,0)));
767 DEBUGADD(2, ("SEC_DESC DACL Off: %0X\n", IVAL(&sec_desc->off_dacl,0)));
768 tmp->owner_sid = sid_dup_talloc(regf->mem_ctx, (DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->off_owner_sid,0)));
769 if (!tmp->owner_sid) {
773 tmp->grp_sid = sid_dup_talloc(regf->mem_ctx, (DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->off_grp_sid,0)));
779 /* Now pick up the SACL and DACL */
781 DEBUG(0, ("%d, %d\n", IVAL(&sec_desc->off_sacl,0), IVAL(&sec_desc->off_dacl,0)));
783 if (sec_desc->off_sacl)
784 tmp->sacl = dup_sec_acl(regf->mem_ctx, (SEC_ACL *)((char *)sec_desc + IVAL(&sec_desc->off_sacl,0)));
788 if (sec_desc->off_dacl)
789 tmp->dacl = dup_sec_acl(regf->mem_ctx, (SEC_ACL *)((char *)sec_desc + IVAL(&sec_desc->off_dacl,0)));
796 static KEY_SEC_DESC *process_sk(struct registry_hive *regf, SK_HDR *sk_hdr, int sk_off, int size)
798 KEY_SEC_DESC *tmp = NULL;
799 int sk_next_off, sk_prev_off, sk_size;
802 if (!sk_hdr) return NULL;
804 if (SVAL(&sk_hdr->SK_ID,0) != str_to_dword("sk")) {
805 DEBUG(0, ("Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
806 regf->regfile_name));
810 if (-size < (sk_size = IVAL(&sk_hdr->rec_size,0))) {
811 DEBUG(0, ("Incorrect SK record size: %d vs %d. %s\n",
812 -size, sk_size, regf->regfile_name));
817 * Now, we need to look up the SK Record in the map, and return it
818 * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
823 ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
824 && (tmp->state == SEC_DESC_OCU)) {
829 /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
831 SMB_REG_ASSERT(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
834 * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
835 * new KEY_SEC_DESC to the mapping structure, since the offset supplied is
836 * the actual offset of structure. The same offset will be used by
837 * all future references to this structure
838 * We could put all this unpleasantness in a function.
842 tmp = malloc_p(KEY_SEC_DESC);
843 memset(tmp, 0, sizeof(KEY_SEC_DESC));
846 * Allocate an entry in the SK_MAP ...
847 * We don't need to free tmp, because that is done for us if the
848 * sm_map entry can't be expanded when we need more space in the map.
851 if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
857 tmp->state = SEC_DESC_OCU;
860 * Now, process the actual sec desc and plug the values in
863 sec_desc = (SEC_DESC *)&sk_hdr->sec_desc[0];
864 tmp->sec_desc = process_sec_desc(regf, sec_desc);
867 * Now forward and back links. Here we allocate an entry in the sk_map
868 * if it does not exist, and mark it reserved
871 sk_prev_off = IVAL(&sk_hdr->prev_off,0);
872 tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
873 SMB_REG_ASSERT(tmp->prev != NULL);
874 sk_next_off = IVAL(&sk_hdr->next_off,0);
875 tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
876 SMB_REG_ASSERT(tmp->next != NULL);
883 * Process a VK header and return a value
885 static WERROR vk_to_val(TALLOC_CTX *mem_ctx, struct registry_key *parent, VK_HDR *vk_hdr, int size, struct registry_value **value)
887 REGF *regf = parent->hive->backend_data;
888 int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
889 struct registry_value *tmp = NULL;
891 if (!vk_hdr) return WERR_INVALID_PARAM;
893 if ((vk_id = SVAL(&vk_hdr->VK_ID,0)) != str_to_dword("vk")) {
894 DEBUG(0, ("Unrecognized VK header ID: %0X, block: %0X, %s\n",
895 vk_id, (int)vk_hdr, parent->hive->location));
896 return WERR_GENERAL_FAILURE;
899 nam_len = SVAL(&vk_hdr->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);
905 tmp = talloc(mem_ctx, struct registry_value);
906 tmp->data_type = dat_type;
908 if (flag & FLAG_HAS_NAME) {
909 tmp->name = talloc_strndup(mem_ctx, vk_hdr->dat_name, nam_len);
915 * Allocate space and copy the data as a BLOB
918 if (dat_len&0x7FFFFFFF) {
920 char *dtmp = talloc_size(mem_ctx, dat_len&0x7FFFFFFF);
922 if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
923 char *dat_ptr = LOCN(regf->base, dat_off);
924 memcpy(dtmp, dat_ptr, dat_len);
926 else { /* The data is in the offset or type */
929 * Some registry files seem to have weird fields. If top bit is set,
930 * but len is 0, the type seems to be the value ...
931 * Not sure how to handle this last type for the moment ...
933 dat_len = dat_len & 0x7FFFFFFF;
934 memcpy(dtmp, &dat_off, dat_len);
938 tmp->data_blk = dtmp;
939 tmp->data_len = dat_len;
948 static BOOL vl_verify(VL_TYPE vl, int count, int size)
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));
960 static WERROR lf_verify(struct registry_hive *h, LF_HDR *lf_hdr, int size)
963 if ((lf_id = SVAL(&lf_hdr->LF_ID,0)) != str_to_dword("lf")) {
964 DEBUG(0, ("Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
965 lf_id, (int)lf_hdr, h->location));
966 return WERR_INVALID_PARAM;
971 static WERROR lf_num_entries(struct registry_hive *h, LF_HDR *lf_hdr, int size, int *count)
975 error = lf_verify(h, lf_hdr, size);
976 if(!W_ERROR_IS_OK(error)) return error;
978 SMB_REG_ASSERT(size < 0);
980 *count = SVAL(&lf_hdr->key_count,0);
981 DEBUG(2, ("Key Count: %u\n", *count));
982 if (*count <= 0) return WERR_INVALID_PARAM;
988 static WERROR nk_to_key(TALLOC_CTX *, struct registry_hive *regf, NK_HDR *nk_hdr, int size, struct registry_key *parent, struct registry_key **);
993 * Process an LF Header and return a list of sub-keys
995 static WERROR lf_get_entry(TALLOC_CTX *mem_ctx, struct registry_key *parent, LF_HDR *lf_hdr, int size, int n, struct registry_key **key)
997 REGF *regf = parent->hive->backend_data;
1002 if (!lf_hdr) return WERR_INVALID_PARAM;
1004 error = lf_verify(parent->hive, lf_hdr, size);
1005 if(!W_ERROR_IS_OK(error)) return error;
1007 SMB_REG_ASSERT(size < 0);
1009 count = SVAL(&lf_hdr->key_count,0);
1010 DEBUG(2, ("Key Count: %u\n", count));
1011 if (count <= 0) return WERR_GENERAL_FAILURE;
1012 if (n >= count) return WERR_NO_MORE_ITEMS;
1014 nk_off = IVAL(&lf_hdr->hr[n].nk_off,0);
1015 DEBUG(2, ("NK Offset: %0X\n", nk_off));
1016 nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
1017 return nk_to_key(mem_ctx, parent->hive, nk_hdr, BLK_SIZE(nk_hdr), parent, key);
1020 static WERROR nk_to_key(TALLOC_CTX *mem_ctx, struct registry_hive *h, NK_HDR *nk_hdr, int size, struct registry_key *parent, struct registry_key **key)
1022 REGF *regf = h->backend_data;
1023 struct registry_key *tmp = NULL, *own;
1024 int namlen, clsname_len, sk_off, own_off;
1028 char key_name[1024];
1030 if (!nk_hdr) return WERR_INVALID_PARAM;
1032 if ((nk_id = SVAL(&nk_hdr->NK_ID,0)) != str_to_dword("nk")) {
1033 DEBUG(0, ("Unrecognized NK Header format: %08X, Block: %0X. %s\n",
1034 nk_id, (int)nk_hdr, parent->hive->location));
1035 return WERR_INVALID_PARAM;
1038 SMB_REG_ASSERT(size < 0);
1040 namlen = SVAL(&nk_hdr->nam_len,0);
1041 clsname_len = SVAL(&nk_hdr->clsnam_len,0);
1044 * The value of -size should be ge
1045 * (sizeof(NK_HDR) - 1 + namlen)
1046 * The -1 accounts for the fact that we included the first byte of
1047 * the name in the structure. clsname_len is the length of the thing
1048 * pointed to by clsnam_off
1051 if (-size < (sizeof(NK_HDR) - 1 + namlen)) {
1052 DEBUG(0, ("Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr));
1053 DEBUG(0, ("Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
1054 (int)sizeof(NK_HDR), namlen, clsname_len));
1055 return WERR_GENERAL_FAILURE;
1058 DEBUG(2, ("NK HDR: Name len: %d, class name len: %d\n", namlen, clsname_len));
1060 /* Fish out the key name and process the LF list */
1062 SMB_REG_ASSERT(namlen < sizeof(key_name));
1064 strncpy(key_name, nk_hdr->key_nam, namlen);
1065 key_name[namlen] = '\0';
1067 type = (SVAL(&nk_hdr->type,0)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
1068 if(type == REG_ROOT_KEY && parent) {
1069 DEBUG(0,("Root key encountered below root level!\n"));
1070 return WERR_GENERAL_FAILURE;
1073 tmp = talloc(mem_ctx, struct registry_key);
1074 tmp->name = talloc_strdup(mem_ctx, key_name);
1075 tmp->backend_data = nk_hdr;
1077 DEBUG(2, ("Key name: %s\n", key_name));
1080 * Fish out the class name, it is in UNICODE, while the key name is
1084 if (clsname_len) { /* Just print in Ascii for now */
1088 clsnam_off = IVAL(&nk_hdr->clsnam_off,0);
1089 clsnamep = LOCN(regf->base, clsnam_off);
1090 DEBUG(2, ("Class Name Offset: %0X\n", clsnam_off));
1092 pull_ucs2_talloc(mem_ctx, &tmp->class_name, clsnamep);
1094 DEBUGADD(2,(" Class Name: %s\n", tmp->class_name));
1099 * Process the owner offset ...
1102 own_off = IVAL(&nk_hdr->own_off,0);
1103 own = (struct registry_key *)LOCN(regf->base, own_off);
1104 DEBUG(2, ("Owner Offset: %0X\n", own_off));
1106 DEBUGADD(2, (" Owner locn: %0X, Our locn: %0X\n",
1107 (uint_t)own, (uint_t)nk_hdr));
1110 * We should verify that the owner field is correct ...
1111 * for now, we don't worry ...
1115 * Also handle the SK header ...
1118 sk_off = IVAL(&nk_hdr->sk_off,0);
1119 sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
1120 DEBUG(2, ("SK Offset: %0X\n", sk_off));
1125 tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
1137 * Allocate a new hbin block, set up the header for the block etc
1139 static HBIN_BLK *nt_create_hbin_blk(struct registry_hive *h, int size)
1141 REGF *regf = h->backend_data;
1145 if (!regf || !size) return NULL;
1147 /* Round size up to multiple of REGF_HDR_BLKSIZ */
1149 size = (size + (REGF_HDR_BLKSIZ - 1)) & ~(REGF_HDR_BLKSIZ - 1);
1151 tmp = malloc_p(HBIN_BLK);
1152 memset(tmp, 0, sizeof(HBIN_BLK));
1154 tmp->data = malloc(size);
1156 memset(tmp->data, 0, size); /* Make it pristine */
1159 /*FIXMEtmp->file_offset = regf->blk_tail->file_offset + regf->blk_tail->size;*/
1161 tmp->free_space = size - (sizeof(HBIN_HDR) - sizeof(HBIN_SUB_HDR));
1162 tmp->fsp_off = size - tmp->free_space;
1165 * Now, build the header in the data block
1167 hdr = (HBIN_HDR *)tmp->data;
1168 hdr->HBIN_ID = str_to_dword("hbin");
1169 hdr->off_from_first = tmp->file_offset - REGF_HDR_BLKSIZ;
1170 hdr->off_to_next = tmp->size;
1171 hdr->blk_size = tmp->size;
1177 regf->blk_tail->next = tmp;
1178 regf->blk_tail = tmp;
1179 if (!regf->free_space) regf->free_space = tmp;
1185 * Allocate a unit of space ... and return a pointer as function param
1186 * and the block's offset as a side effect
1188 static void *nt_alloc_regf_space(struct registry_hive *h, int size, uint_t *off)
1190 REGF *regf = h->backend_data;
1195 if (!regf || !size || !off) return NULL;
1197 SMB_REG_ASSERT(regf->blk_head != NULL);
1200 * round up size to include header and then to 8-byte boundary
1202 size = (size + 4 + 7) & ~7;
1205 * Check if there is space, if none, grab a block
1207 if (!regf->free_space) {
1208 if (!nt_create_hbin_blk(h, REGF_HDR_BLKSIZ))
1213 * Now, chain down the list of blocks looking for free space
1216 for (blk = regf->free_space; blk != NULL; blk = blk->next) {
1217 if (blk->free_space <= size) {
1218 tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
1219 ret = blk->data + blk->fsp_off;
1220 blk->free_space -= size;
1221 blk->fsp_off += size;
1223 /* Insert the header */
1224 ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
1227 * Fix up the free space ptr
1228 * If it is NULL, we fix it up next time
1231 if (!blk->free_space)
1232 regf->free_space = blk->next;
1235 return (((char *)ret)+4);/* The pointer needs to be to the data struct */
1240 * If we got here, we need to add another block, which might be
1241 * larger than one block -- deal with that later
1243 if (nt_create_hbin_blk(h, REGF_HDR_BLKSIZ)) {
1244 blk = regf->free_space;
1245 tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
1246 ret = blk->data + blk->fsp_off;
1247 blk->free_space -= size;
1248 blk->fsp_off += size;
1250 /* Insert the header */
1251 ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
1254 * Fix up the free space ptr
1255 * If it is NULL, we fix it up next time
1258 if (!blk->free_space)
1259 regf->free_space = blk->next;
1262 return (((char *)ret) + 4);/* The pointer needs to be to the data struct */
1269 * Store a SID at the location provided
1271 static int nt_store_SID(struct registry_hive *regf, DOM_SID *sid, uint8_t *locn)
1276 if (!regf || !sid || !locn) return 0;
1278 *p = sid->sid_rev_num; p++;
1279 *p = sid->num_auths; p++;
1281 for (i=0; i < 6; i++) {
1282 *p = sid->id_auth[i]; p++;
1285 for (i=0; i < sid->num_auths; i++) {
1286 SIVAL(p, 0, sid->sub_auths[i]); p+=4;
1293 static int nt_store_ace(struct registry_hive *regf, SEC_ACE *ace, uint8_t *locn)
1296 SEC_ACE *reg_ace = (SEC_ACE *)locn;
1299 if (!regf || !ace || !locn) return 0;
1301 reg_ace->type = ace->type;
1302 reg_ace->flags = ace->flags;
1304 /* Deal with the length when we have stored the SID */
1306 p = (uint8_t *)®_ace->info.mask;
1308 SIVAL(p, 0, ace->info.mask); p += 4;
1310 size = nt_store_SID(regf, &ace->trustee, p);
1312 size += 8; /* Size of the fixed header */
1314 p = (uint8_t *)®_ace->size;
1322 * Store an ACL at the location provided
1324 static int nt_store_acl(struct registry_hive *regf, SEC_ACL *acl, uint8_t *locn) {
1326 uint8_t *p = locn, *s;
1328 if (!regf || !acl || !locn) return 0;
1331 * Now store the header and then the ACEs ...
1334 SSVAL(p, 0, acl->revision);
1336 p += 2; s = p; /* Save this for the size field */
1340 SIVAL(p, 0, acl->num_aces);
1344 for (i = 0; i < acl->num_aces; i++) {
1345 size = nt_store_ace(regf, &acl->ace[i], p);
1355 * Flatten and store the Sec Desc
1356 * Windows lays out the DACL first, but since there is no SACL, it might be
1357 * that first, then the owner, then the group SID. So, we do it that way
1360 static uint_t nt_store_sec_desc(struct registry_hive *regf, SEC_DESC *sd, char *locn)
1362 SEC_DESC *rsd = (SEC_DESC *)locn;
1363 uint_t size = 0, off = 0;
1365 if (!regf || !sd || !locn) return 0;
1368 * Now, fill in the first two fields, then lay out the various fields
1372 rsd->revision = SEC_DESC_REVISION;
1373 rsd->type = SEC_DESC_DACL_PRESENT | SEC_DESC_SELF_RELATIVE;
1375 off = 4 * sizeof(uint32_t) + 4;
1378 size = nt_store_acl(regf, sd->sacl, (char *)(locn + off));
1379 rsd->off_sacl = off;
1387 rsd->off_dacl = off;
1388 size = nt_store_acl(regf, sd->dacl, (char *)(locn + off));
1396 /* Now the owner and group SIDs */
1398 if (sd->owner_sid) {
1399 rsd->off_owner_sid = off;
1400 size = nt_store_SID(regf, sd->owner_sid, (char *)(locn + off));
1403 rsd->off_owner_sid = 0;
1409 rsd->off_grp_sid = off;
1410 size = nt_store_SID(regf, sd->grp_sid, (char *)(locn + off));
1413 rsd->off_grp_sid = 0;
1422 * Store the security information
1424 * If it has already been stored, just get its offset from record
1425 * otherwise, store it and record its offset
1427 static uint_t nt_store_security(struct registry_hive *regf, KEY_SEC_DESC *sec)
1433 if (sec->offset) return sec->offset;
1436 * OK, we don't have this one in the file yet. We must compute the
1437 * size taken by the security descriptor as a self-relative SD, which
1438 * means making one pass over each structure and figuring it out
1441 /* FIXME size = sec_desc_size(sec->sec_desc); */
1443 /* Allocate that much space */
1445 sk_hdr = nt_alloc_regf_space(regf, size, &sk_off);
1446 sec->sk_hdr = sk_hdr;
1448 if (!sk_hdr) return 0;
1450 /* Now, lay out the sec_desc in the space provided */
1452 sk_hdr->SK_ID = str_to_dword("sk");
1455 * We can't deal with the next and prev offset in the SK_HDRs until the
1456 * whole tree has been stored, then we can go and deal with them
1459 sk_hdr->ref_cnt = sec->ref_cnt;
1460 sk_hdr->rec_size = size; /* Is this correct */
1462 /* Now, lay out the sec_desc */
1464 if (!nt_store_sec_desc(regf, sec->sec_desc, (char *)&sk_hdr->sec_desc))
1472 * Store a KEY in the file ...
1474 * We store this depth first, and defer storing the lf struct until
1475 * all the sub-keys have been stored.
1477 * We store the NK hdr, any SK header, class name, and VK structure, then
1478 * recurse down the LF structures ...
1480 * We return the offset of the NK struct
1481 * FIXME, FIXME, FIXME: Convert to using SIVAL and SSVAL ...
1483 static int nt_store_reg_key(struct registry_hive *regf, struct registry_key *key)
1486 uint_t nk_off, sk_off, size;
1488 if (!regf || !key) return 0;
1490 size = sizeof(NK_HDR) + strlen(key->name) - 1;
1491 nk_hdr = nt_alloc_regf_space(regf, size, &nk_off);
1492 if (!nk_hdr) goto error;
1494 key->offset = nk_off; /* We will need this later */
1497 * Now fill in each field etc ...
1500 nk_hdr->NK_ID = str_to_dword("nk");
1501 if (key->type == REG_ROOT_KEY)
1502 nk_hdr->type = 0x2C;
1504 nk_hdr->type = 0x20;
1506 /* FIXME: Fill in the time of last update */
1508 if (key->type != REG_ROOT_KEY)
1509 nk_hdr->own_off = key->owner->offset;
1512 nk_hdr->subk_num = key->sub_keys->key_count;
1515 * Now, process the Sec Desc and then store its offset
1518 sk_off = nt_store_security(regf, key->security);
1519 nk_hdr->sk_off = sk_off;
1522 * Then, store the val list and store its offset
1525 nk_hdr->val_cnt = key->values->val_count;
1526 nk_hdr->val_off = nt_store_val_list(regf, key->values);
1529 nk_hdr->val_off = -1;
1530 nk_hdr->val_cnt = 0;
1534 * Finally, store the subkeys, and their offsets
1542 * Store the registry header ...
1543 * We actually create the registry header block and link it to the chain
1546 static REGF_HDR *nt_get_reg_header(struct registry_hive *h) {
1547 REGF *regf = h->backend_data;
1548 HBIN_BLK *tmp = NULL;
1550 tmp = malloc_p(HBIN_BLK);
1552 memset(tmp, 0, sizeof(HBIN_BLK));
1553 tmp->type = REG_OUTBLK_HDR;
1554 tmp->size = REGF_HDR_BLKSIZ;
1555 tmp->data = malloc(REGF_HDR_BLKSIZ);
1556 if (!tmp->data) goto error;
1558 memset(tmp->data, 0, REGF_HDR_BLKSIZ); /* Make it pristine, unlike Windows */
1559 regf->blk_head = regf->blk_tail = tmp;
1561 return (REGF_HDR *)tmp->data;
1570 static WERROR nt_open_hive (struct registry_hive *h, struct registry_key **key)
1574 uint_t regf_id, hbin_id;
1577 regf = (REGF *)talloc(h, REGF);
1578 memset(regf, 0, sizeof(REGF));
1579 regf->owner_sid_str = NULL; /* FIXME: Fill in */
1580 h->backend_data = regf;
1582 DEBUG(5, ("Attempting to load registry file\n"));
1584 /* Get the header */
1586 if ((regf_hdr = nt_get_regf_hdr(h)) == NULL) {
1587 DEBUG(0, ("Unable to get header\n"));
1588 return WERR_GENERAL_FAILURE;
1591 /* Now process that header and start to read the rest in */
1593 if ((regf_id = IVAL(®f_hdr->REGF_ID,0)) != str_to_dword("regf")) {
1594 DEBUG(0, ("Unrecognized NT registry header id: %0X, %s\n",
1595 regf_id, h->location));
1596 return WERR_GENERAL_FAILURE;
1600 * Validate the header ...
1602 if (!valid_regf_hdr(regf_hdr)) {
1603 DEBUG(0, ("Registry file header does not validate: %s\n",
1605 return WERR_GENERAL_FAILURE;
1608 /* Update the last mod date, and then go get the first NK record and on */
1610 TTTONTTIME(regf, IVAL(®f_hdr->tim1,0), IVAL(®f_hdr->tim2,0));
1613 * The hbin hdr seems to be just uninteresting garbage. Check that
1614 * it is there, but that is all.
1617 hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
1619 if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID,0)) != str_to_dword("hbin")) {
1620 DEBUG(0, ("Unrecognized registry hbin hdr ID: %0X, %s\n",
1621 hbin_id, h->location));
1622 return WERR_GENERAL_FAILURE;
1626 * Get a pointer to the first key from the hreg_hdr
1629 DEBUG(2, ("First Key: %0X\n",
1630 IVAL(®f_hdr->first_key, 0)));
1632 regf->first_key = (NK_HDR *)LOCN(regf->base, IVAL(®f_hdr->first_key,0));
1633 DEBUGADD(2, ("First Key Offset: %0X\n",
1634 IVAL(®f_hdr->first_key, 0)));
1636 DEBUGADD(2, ("Data Block Size: %d\n",
1637 IVAL(®f_hdr->dblk_size, 0)));
1639 DEBUGADD(2, ("Offset to next hbin block: %0X\n",
1640 IVAL(&hbin_hdr->off_to_next, 0)));
1642 DEBUGADD(2, ("HBIN block size: %0X\n",
1643 IVAL(&hbin_hdr->blk_size, 0)));
1646 * Unmap the registry file, as we might want to read in another
1650 h->backend_data = regf;
1652 return nk_to_key(h, h, ((REGF *)h->backend_data)->first_key, BLK_SIZE(((REGF *)h->backend_data)->first_key), NULL, key);
1656 static WERROR nt_num_subkeys(struct registry_key *k, int *num)
1658 REGF *regf = k->hive->backend_data;
1661 NK_HDR *nk_hdr = k->backend_data;
1662 lf_off = IVAL(&nk_hdr->lf_off,0);
1663 DEBUG(2, ("SubKey list offset: %0X\n", lf_off));
1668 lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
1670 return lf_num_entries(k->hive, lf_hdr, BLK_SIZE(lf_hdr), num);
1673 static WERROR nt_num_values(struct registry_key *k, int *count)
1675 NK_HDR *nk_hdr = k->backend_data;
1676 *count = IVAL(&nk_hdr->val_cnt,0);
1680 static WERROR nt_value_by_index(TALLOC_CTX *mem_ctx, struct registry_key *k, int n, struct registry_value **value)
1683 int val_off, vk_off;
1686 REGF *regf = k->hive->backend_data;
1687 NK_HDR *nk_hdr = k->backend_data;
1688 val_count = IVAL(&nk_hdr->val_cnt,0);
1689 val_off = IVAL(&nk_hdr->val_off,0);
1690 vl = (VL_TYPE *)LOCN(regf->base, val_off);
1691 DEBUG(2, ("Val List Offset: %0X\n", val_off));
1692 if(n < 0) return WERR_INVALID_PARAM;
1693 if(n >= val_count) return WERR_NO_MORE_ITEMS;
1695 vk_off = IVAL(&vl[n],0);
1696 vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
1697 return vk_to_val(mem_ctx, k, vk_hdr, BLK_SIZE(vk_hdr), value);
1700 static WERROR nt_key_by_index(TALLOC_CTX *mem_ctx, struct registry_key *k, int n, struct registry_key **subkey)
1702 REGF *regf = k->hive->backend_data;
1704 NK_HDR *nk_hdr = k->backend_data;
1706 lf_off = IVAL(&nk_hdr->lf_off,0);
1707 DEBUG(2, ("SubKey list offset: %0X\n", lf_off));
1710 * No more subkeys if lf_off == -1
1714 lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
1715 return lf_get_entry(mem_ctx, k, lf_hdr, BLK_SIZE(lf_hdr), n, subkey);
1718 return WERR_NO_MORE_ITEMS;
1721 static struct hive_operations reg_backend_nt4 = {
1723 .open_hive = nt_open_hive,
1724 .num_subkeys = nt_num_subkeys,
1725 .num_values = nt_num_values,
1726 .get_subkey_by_index = nt_key_by_index,
1727 .get_value_by_index = nt_value_by_index,
1738 NTSTATUS registry_nt4_init(void)
1740 return registry_register(®_backend_nt4);