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
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 I don't know what "hbin" stands for, but this block is always a multiple
70 Inside these hbin-blocks the different records are placed. The memory-
71 management looks like a C-compiler heap management to me...
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
81 The values in 0x0008 and 0x001C should be the same, so I don't know
82 if they are correct or swapped...
84 From offset 0x0020 inside a hbin-block data is stored with the following
88 0x0000 D-Word Data-block size //this size must be a
92 If the size field is negative (bit 31 set), the corresponding block
93 is free and has a size of -blocksize!
95 That does not seem to be true. All block lengths seem to be negative!
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.
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.)
105 Records in the hbin-blocks
106 ==========================
110 The nk-record can be treated as a combination of tree-record and
111 key-record of the win 95 registry.
115 The lf-record is the counterpart to the RGKN-record (the
120 The vk-record consists information to a single value (value key).
124 sk (? Security Key ?) is the ACL of the registry.
128 The value-lists contain information about which values are inside a
129 sub-key and don't have a header.
133 The datas of the registry are (like the value-list) stored without a
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)...
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
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
162 0x0000 D-Word Offset 1st Value
163 0x0004 D-Word Offset 2nd Value
164 0x???? D-Word Offset nth Value
166 To determine the number of values, you have to look at the owner-nk-record!
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
177 0x0012 Word Unused (data-trash)
180 If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
182 If the data-size is lower 5, the data-offset value is used to store the data itself!
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
197 0x0000 Word ID: ASCII-"lf" = 0x666C
198 0x0002 Word number of keys
199 0x0004 ???? Hash-Records
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!
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!
210 //These hashrecords must be sorted low to high within the lf record. Nigel.
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.)
219 0x0000 Word ID: ASCII-"sk" = 0x6B73
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
226 relative security desciptor. Nigel
227 ???? ???? Security and auditing settings...
230 The usage counter counts the number of references to this
231 "sk"-record. You can use one "sk"-record for the entire registry!
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
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
246 If a value has no data (length=0), it is displayed as empty.
248 simplyfied win-3.?? registry:
249 =============================
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 |
266 Greatly simplyfied structure of the nt-registry:
267 ================================================
269 +---------------------------------------------------------------+
272 +---------+ +---------->+-----------+ +----->+---------+ |
273 | "nk" | | | lf-rec. | | | nk-rec. | |
274 | ID | | | # of keys | | | parent |---+
275 | Date | | | 1st key |--+ | .... |
276 | parent | | +-----------+ +---------+
278 | values |--------------------->+----------+
279 | SK-rec. |---------------+ | 1. value |--> +----------+
280 | class |--+ | +----------+ | vk-rec. |
281 +---------+ | | | .... |
282 v | | data |--> +-------+
283 +------------+ | +----------+ | xxxxx |
284 | Class name | | +-------+
287 +---------+ +---------+
288 +----->| next sk |--->| Next sk |--+
289 | +---| prev sk |<---| prev sk | |
290 | | | .... | | ... | |
291 | | +---------+ +---------+ |
294 | +--------------------+ |
295 +----------------------------------+
297 ---------------------------------------------------------------------------
299 Hope this helps.... (Although it was "fun" for me to uncover this things,
300 it took me several sleepless nights ;)
304 *************************************************************************/
306 #include "includes.h"
308 #define REG_KEY_LIST_SIZE 10
309 #define FLAG_HAS_NAME 0x01
313 * Structures for dealing with the on-disk format of the registry
316 const char *def_owner_sid_str = NULL;
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
325 * Definition of a Key. It has a name, classname, date/time last modified,
326 * sub-keys, values, and a security descriptor
329 #define REG_ROOT_KEY 1
330 #define REG_SUB_KEY 2
331 #define REG_SYM_LINK 3
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
339 #define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
341 typedef uint_t DWORD;
342 typedef unsigned short WORD;
344 typedef struct sk_struct SK_HDR;
346 * This structure keeps track of the output format of the registry
348 #define REG_OUTBLK_HDR 1
349 #define REG_OUTBLK_HBIN 2
351 typedef struct regf_block {
352 DWORD REGF_ID; /* regf */
360 DWORD first_key; /* offset */
362 DWORD uk7[116]; /* 1 */
366 typedef struct hbin_sub_struct {
371 typedef struct hbin_struct {
372 DWORD HBIN_ID; /* hbin */
373 DWORD off_from_first;
380 HBIN_SUB_HDR hbin_sub_hdr;
383 typedef struct nk_struct {
401 char key_nam[1]; /* Actual length determined by nam_len */
414 typedef struct key_sec_desc_s {
415 struct key_sec_desc_s *prev, *next;
419 SK_HDR *sk_hdr; /* This means we must keep the registry in memory */
423 /* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
424 typedef struct sk_map_s {
426 KEY_SEC_DESC *key_sec_desc;
429 typedef struct vk_struct {
432 DWORD dat_len; /* If top-bit set, offset contains the data */
435 WORD flag; /* =1, has name, else no name (=Default). */
437 char dat_name[1]; /* Name starts here ... */
440 typedef DWORD VL_TYPE[1]; /* Value list is an array of vk rec offsets */
442 typedef struct hash_struct {
448 typedef struct lf_struct {
451 struct hash_struct hr[1]; /* Array of hash records, depending on key_count */} LF_HDR;
456 * This structure keeps track of the output format of the registry
458 #define REG_OUTBLK_HDR 1
459 #define REG_OUTBLK_HBIN 2
461 typedef struct hbin_blk_s {
463 struct hbin_blk_s *next;
464 char *data; /* The data block */
465 uint_t file_offset; /* Offset in file */
466 uint_t free_space; /* Amount of free space in block */
467 uint_t fsp_off; /* Start of free space in block */
468 int complete, stored;
471 typedef struct regf_struct_s {
477 NTTIME last_mod_time;
479 int sk_count, sk_map_size;
481 const char *owner_sid_str;
482 SEC_DESC *def_sec_desc;
484 * These next pointers point to the blocks used to contain the
485 * keys when we are preparing to write them to a file
487 HBIN_BLK *blk_head, *blk_tail, *free_space;
490 static DWORD str_to_dword(const char *a) {
492 unsigned long ret = 0;
493 for(i = strlen(a)-1; i >= 0; i--) {
494 ret = ret * 0x100 + a[i];
504 static BOOL nt_create_ace(SEC_ACE *ace, int type, int flags, uint32_t perms, const char *sid)
509 if(!string_to_sid(&s, sid))return False;
510 init_sec_ace(ace, &s, type, access, flags);
515 * Create a default ACL
517 static SEC_ACL *nt_create_default_acl(struct registry_hive *regf)
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;
530 return make_sec_acl(regf->mem_ctx, 2, 8, aces);
534 * Create a default security descriptor. We pull in things from env
537 static SEC_DESC *nt_create_def_sec_desc(struct registry_hive *regf)
541 tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
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;
548 tmp->dacl = nt_create_default_acl(regf);
553 if (tmp) nt_delete_sec_desc(tmp);
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.
562 static KEY_SEC_DESC *nt_inherit_security(struct registry_key *key)
565 if (!key) return NULL;
566 return key->security;
570 * Create an initial security descriptor and init other structures, if needed
571 * We assume that the initial security stuff is empty ...
573 static KEY_SEC_DESC *nt_create_init_sec(struct registry_hive *h)
575 REGF *regf = h->backend_data;
576 KEY_SEC_DESC *tsec = NULL;
578 tsec = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
581 tsec->state = SEC_DESC_NBK;
584 tsec->sec_desc = regf->def_sec_desc;
591 * Get the starting record for NT Registry file
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.
607 #define REG_HANDLE_REGTYPE_NONE 0
608 #define REG_HANDLE_REGTYPE_NT 1
609 #define REG_HANDLE_REGTYPE_W9X 2
611 #define TTTONTTIME(r, t1, t2) (r)->last_mod_time = (t1) | (((uint64_t)(t2)) << 32)
613 #define REGF_HDR_BLKSIZ 0x1000
615 #define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4)
616 #define LOCN(base, f) ((base) + OFF(f))
618 /* Get the header of the registry. Return a pointer to the structure
619 * If the mmap'd area has not been allocated, then mmap the input file
621 static REGF_HDR *nt_get_regf_hdr(struct registry_hive *h)
623 REGF *regf = h->backend_data;
624 SMB_REG_ASSERT(regf);
626 if (!regf->base) { /* Try to mmap etc the file */
628 if ((regf->fd = open(h->location, O_RDONLY, 0000)) <0) {
629 return NULL; /* What about errors? */
632 if (fstat(regf->fd, ®f->sbuf) < 0) {
636 regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
638 if ((int)regf->base == 1) {
639 DEBUG(0,("Could not mmap file: %s, %s\n", h->location,
646 * At this point, regf->base != NULL, and we should be able to read the
650 SMB_REG_ASSERT(regf->base != NULL);
652 return (REGF_HDR *)regf->base;
656 * Validate a regf header
657 * For now, do nothing, but we should check the checksum
659 static int valid_regf_hdr(REGF_HDR *regf_hdr)
661 if (!regf_hdr) return 0;
669 * Process an SK header ...
670 * Every time we see a new one, add it to the map. Otherwise, just look it up.
671 * We will do a simple linear search for the moment, since many KEYs have the
672 * same security descriptor.
673 * We allocate the map in increments of 10 entries.
677 * Create a new entry in the map, and increase the size of the map if needed
679 static SK_MAP *alloc_sk_map_entry(struct registry_hive *h, KEY_SEC_DESC *tmp, int sk_off)
681 REGF *regf = h->backend_data;
682 if (!regf->sk_map) { /* Allocate a block of 10 */
683 regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
684 regf->sk_map_size = 10;
686 (regf->sk_map)[0].sk_off = sk_off;
687 (regf->sk_map)[0].key_sec_desc = tmp;
689 else { /* Simply allocate a new slot, unless we have to expand the list */
690 int ndx = regf->sk_count;
691 if (regf->sk_count >= regf->sk_map_size) {
692 regf->sk_map = (SK_MAP *)realloc(regf->sk_map,
693 (regf->sk_map_size + 10)*sizeof(SK_MAP));
699 * ndx already points at the first entry of the new block
701 regf->sk_map_size += 10;
703 (regf->sk_map)[ndx].sk_off = sk_off;
704 (regf->sk_map)[ndx].key_sec_desc = tmp;
711 * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
714 KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
718 if (!sk_map) return NULL;
720 for (i = 0; i < count; i++) {
722 if (sk_map[i].sk_off == sk_off)
723 return sk_map[i].key_sec_desc;
732 * Allocate a KEY_SEC_DESC if we can't find one in the map
734 static KEY_SEC_DESC *lookup_create_sec_key(struct registry_hive *h, SK_MAP *sk_map, int sk_off)
736 REGF *regf = h->backend_data;
737 KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
742 else { /* Allocate a new one */
743 tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
744 memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */
745 tmp->state = SEC_DESC_RES;
746 if (!alloc_sk_map_entry(h, tmp, sk_off)) {
753 static SEC_DESC *process_sec_desc(struct registry_hive *regf, SEC_DESC *sec_desc)
755 SEC_DESC *tmp = NULL;
757 tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
759 tmp->revision = SVAL(&sec_desc->revision,0);
760 tmp->type = SVAL(&sec_desc->type,0);
761 DEBUG(2, ("SEC_DESC Rev: %0X, Type: %0X\n", tmp->revision, tmp->type));
762 DEBUGADD(2, ("SEC_DESC Owner Off: %0X\n", IVAL(&sec_desc->off_owner_sid,0)));
763 DEBUGADD(2, ("SEC_DESC Group Off: %0X\n", IVAL(&sec_desc->off_grp_sid,0)));
764 DEBUGADD(2, ("SEC_DESC DACL Off: %0X\n", IVAL(&sec_desc->off_dacl,0)));
765 tmp->owner_sid = sid_dup_talloc(regf->mem_ctx, (DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->off_owner_sid,0)));
766 if (!tmp->owner_sid) {
770 tmp->grp_sid = sid_dup_talloc(regf->mem_ctx, (DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->off_grp_sid,0)));
776 /* Now pick up the SACL and DACL */
778 DEBUG(0, ("%d, %d\n", IVAL(&sec_desc->off_sacl,0), IVAL(&sec_desc->off_dacl,0)));
780 if (sec_desc->off_sacl)
781 tmp->sacl = dup_sec_acl(regf->mem_ctx, (SEC_ACL *)((char *)sec_desc + IVAL(&sec_desc->off_sacl,0)));
785 if (sec_desc->off_dacl)
786 tmp->dacl = dup_sec_acl(regf->mem_ctx, (SEC_ACL *)((char *)sec_desc + IVAL(&sec_desc->off_dacl,0)));
793 static KEY_SEC_DESC *process_sk(struct registry_hive *regf, SK_HDR *sk_hdr, int sk_off, int size)
795 KEY_SEC_DESC *tmp = NULL;
796 int sk_next_off, sk_prev_off, sk_size;
799 if (!sk_hdr) return NULL;
801 if (SVAL(&sk_hdr->SK_ID,0) != str_to_dword("sk")) {
802 DEBUG(0, ("Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
803 regf->regfile_name));
807 if (-size < (sk_size = IVAL(&sk_hdr->rec_size,0))) {
808 DEBUG(0, ("Incorrect SK record size: %d vs %d. %s\n",
809 -size, sk_size, regf->regfile_name));
814 * Now, we need to look up the SK Record in the map, and return it
815 * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
820 ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
821 && (tmp->state == SEC_DESC_OCU)) {
826 /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
828 SMB_REG_ASSERT(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
831 * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
832 * new KEY_SEC_DESC to the mapping structure, since the offset supplied is
833 * the actual offset of structure. The same offset will be used by
834 * all future references to this structure
835 * We could put all this unpleasantness in a function.
839 tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
840 memset(tmp, 0, sizeof(KEY_SEC_DESC));
843 * Allocate an entry in the SK_MAP ...
844 * We don't need to free tmp, because that is done for us if the
845 * sm_map entry can't be expanded when we need more space in the map.
848 if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
854 tmp->state = SEC_DESC_OCU;
857 * Now, process the actual sec desc and plug the values in
860 sec_desc = (SEC_DESC *)&sk_hdr->sec_desc[0];
861 tmp->sec_desc = process_sec_desc(regf, sec_desc);
864 * Now forward and back links. Here we allocate an entry in the sk_map
865 * if it does not exist, and mark it reserved
868 sk_prev_off = IVAL(&sk_hdr->prev_off,0);
869 tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
870 SMB_REG_ASSERT(tmp->prev != NULL);
871 sk_next_off = IVAL(&sk_hdr->next_off,0);
872 tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
873 SMB_REG_ASSERT(tmp->next != NULL);
880 * Process a VK header and return a value
882 static WERROR vk_to_val(TALLOC_CTX *mem_ctx, struct registry_key *parent, VK_HDR *vk_hdr, int size, struct registry_value **value)
884 REGF *regf = parent->hive->backend_data;
885 int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
886 struct registry_value *tmp = NULL;
888 if (!vk_hdr) return WERR_INVALID_PARAM;
890 if ((vk_id = SVAL(&vk_hdr->VK_ID,0)) != str_to_dword("vk")) {
891 DEBUG(0, ("Unrecognized VK header ID: %0X, block: %0X, %s\n",
892 vk_id, (int)vk_hdr, parent->hive->location));
893 return WERR_GENERAL_FAILURE;
896 nam_len = SVAL(&vk_hdr->nam_len,0);
897 flag = SVAL(&vk_hdr->flag,0);
898 dat_type = IVAL(&vk_hdr->dat_type,0);
899 dat_len = IVAL(&vk_hdr->dat_len,0); /* If top bit, offset contains data */
900 dat_off = IVAL(&vk_hdr->dat_off,0);
902 tmp = talloc_p(mem_ctx, struct registry_value);
903 tmp->data_type = dat_type;
905 if (flag & FLAG_HAS_NAME) {
906 tmp->name = talloc_strndup(mem_ctx, vk_hdr->dat_name, nam_len);
912 * Allocate space and copy the data as a BLOB
915 if (dat_len&0x7FFFFFFF) {
917 char *dtmp = (char *)talloc(mem_ctx, dat_len&0x7FFFFFFF);
919 if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
920 char *dat_ptr = LOCN(regf->base, dat_off);
921 memcpy(dtmp, dat_ptr, dat_len);
923 else { /* The data is in the offset or type */
926 * Some registry files seem to have weird fields. If top bit is set,
927 * but len is 0, the type seems to be the value ...
928 * Not sure how to handle this last type for the moment ...
930 dat_len = dat_len & 0x7FFFFFFF;
931 memcpy(dtmp, &dat_off, dat_len);
935 if(tmp->data_type == REG_SZ) {
937 dat_len = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, dtmp, dat_len, (void **)&ret);
942 tmp->data_blk = dtmp;
943 tmp->data_len = dat_len;
952 static BOOL vl_verify(VL_TYPE vl, int count, int size)
954 if(!vl) return False;
955 if (-size < (count+1)*sizeof(int)){
956 DEBUG(0, ("Error in VL header format. Size less than space required. %d\n", -size));
964 static WERROR lf_verify(struct registry_hive *h, LF_HDR *lf_hdr, int size)
967 if ((lf_id = SVAL(&lf_hdr->LF_ID,0)) != str_to_dword("lf")) {
968 DEBUG(0, ("Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
969 lf_id, (int)lf_hdr, h->location));
970 return WERR_INVALID_PARAM;
975 static WERROR lf_num_entries(struct registry_hive *h, LF_HDR *lf_hdr, int size, int *count)
979 error = lf_verify(h, lf_hdr, size);
980 if(!W_ERROR_IS_OK(error)) return error;
982 SMB_REG_ASSERT(size < 0);
984 *count = SVAL(&lf_hdr->key_count,0);
985 DEBUG(2, ("Key Count: %u\n", *count));
986 if (*count <= 0) return WERR_INVALID_PARAM;
992 static WERROR nk_to_key(TALLOC_CTX *, struct registry_hive *regf, NK_HDR *nk_hdr, int size, struct registry_key *parent, struct registry_key **);
997 * Process an LF Header and return a list of sub-keys
999 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)
1001 REGF *regf = parent->hive->backend_data;
1006 if (!lf_hdr) return WERR_INVALID_PARAM;
1008 error = lf_verify(parent->hive, lf_hdr, size);
1009 if(!W_ERROR_IS_OK(error)) return error;
1011 SMB_REG_ASSERT(size < 0);
1013 count = SVAL(&lf_hdr->key_count,0);
1014 DEBUG(2, ("Key Count: %u\n", count));
1015 if (count <= 0) return WERR_GENERAL_FAILURE;
1016 if (n >= count) return WERR_NO_MORE_ITEMS;
1018 nk_off = IVAL(&lf_hdr->hr[n].nk_off,0);
1019 DEBUG(2, ("NK Offset: %0X\n", nk_off));
1020 nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
1021 return nk_to_key(mem_ctx, parent->hive, nk_hdr, BLK_SIZE(nk_hdr), parent, key);
1024 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)
1026 REGF *regf = h->backend_data;
1027 struct registry_key *tmp = NULL, *own;
1028 int namlen, clsname_len, sk_off, own_off;
1032 char key_name[1024];
1034 if (!nk_hdr) return WERR_INVALID_PARAM;
1036 if ((nk_id = SVAL(&nk_hdr->NK_ID,0)) != str_to_dword("nk")) {
1037 DEBUG(0, ("Unrecognized NK Header format: %08X, Block: %0X. %s\n",
1038 nk_id, (int)nk_hdr, parent->hive->location));
1039 return WERR_INVALID_PARAM;
1042 SMB_REG_ASSERT(size < 0);
1044 namlen = SVAL(&nk_hdr->nam_len,0);
1045 clsname_len = SVAL(&nk_hdr->clsnam_len,0);
1048 * The value of -size should be ge
1049 * (sizeof(NK_HDR) - 1 + namlen)
1050 * The -1 accounts for the fact that we included the first byte of
1051 * the name in the structure. clsname_len is the length of the thing
1052 * pointed to by clsnam_off
1055 if (-size < (sizeof(NK_HDR) - 1 + namlen)) {
1056 DEBUG(0, ("Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr));
1057 DEBUG(0, ("Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
1058 sizeof(NK_HDR), namlen, clsname_len));
1059 return WERR_GENERAL_FAILURE;
1062 DEBUG(2, ("NK HDR: Name len: %d, class name len: %d\n", namlen, clsname_len));
1064 /* Fish out the key name and process the LF list */
1066 SMB_REG_ASSERT(namlen < sizeof(key_name));
1068 strncpy(key_name, nk_hdr->key_nam, namlen);
1069 key_name[namlen] = '\0';
1071 type = (SVAL(&nk_hdr->type,0)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
1072 if(type == REG_ROOT_KEY && parent) {
1073 DEBUG(0,("Root key encountered below root level!\n"));
1074 return WERR_GENERAL_FAILURE;
1077 tmp = talloc_p(mem_ctx, struct registry_key);
1078 tmp->name = talloc_strdup(mem_ctx, key_name);
1079 tmp->backend_data = nk_hdr;
1081 DEBUG(2, ("Key name: %s\n", key_name));
1084 * Fish out the class name, it is in UNICODE, while the key name is
1088 if (clsname_len) { /* Just print in Ascii for now */
1092 clsnam_off = IVAL(&nk_hdr->clsnam_off,0);
1093 clsnamep = LOCN(regf->base, clsnam_off);
1094 DEBUG(2, ("Class Name Offset: %0X\n", clsnam_off));
1096 pull_ucs2_talloc(mem_ctx, &tmp->class_name, clsnamep);
1098 DEBUGADD(2,(" Class Name: %s\n", tmp->class_name));
1103 * Process the owner offset ...
1106 own_off = IVAL(&nk_hdr->own_off,0);
1107 own = (struct registry_key *)LOCN(regf->base, own_off);
1108 DEBUG(2, ("Owner Offset: %0X\n", own_off));
1110 DEBUGADD(2, (" Owner locn: %0X, Our locn: %0X\n",
1111 (uint_t)own, (uint_t)nk_hdr));
1114 * We should verify that the owner field is correct ...
1115 * for now, we don't worry ...
1119 * Also handle the SK header ...
1122 sk_off = IVAL(&nk_hdr->sk_off,0);
1123 sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
1124 DEBUG(2, ("SK Offset: %0X\n", sk_off));
1129 tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
1141 * Allocate a new hbin block, set up the header for the block etc
1143 static HBIN_BLK *nt_create_hbin_blk(struct registry_hive *h, int size)
1145 REGF *regf = h->backend_data;
1149 if (!regf || !size) return NULL;
1151 /* Round size up to multiple of REGF_HDR_BLKSIZ */
1153 size = (size + (REGF_HDR_BLKSIZ - 1)) & ~(REGF_HDR_BLKSIZ - 1);
1155 tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
1156 memset(tmp, 0, sizeof(HBIN_BLK));
1158 tmp->data = malloc(size);
1160 memset(tmp->data, 0, size); /* Make it pristine */
1163 /*FIXMEtmp->file_offset = regf->blk_tail->file_offset + regf->blk_tail->size;*/
1165 tmp->free_space = size - (sizeof(HBIN_HDR) - sizeof(HBIN_SUB_HDR));
1166 tmp->fsp_off = size - tmp->free_space;
1169 * Now, build the header in the data block
1171 hdr = (HBIN_HDR *)tmp->data;
1172 hdr->HBIN_ID = str_to_dword("hbin");
1173 hdr->off_from_first = tmp->file_offset - REGF_HDR_BLKSIZ;
1174 hdr->off_to_next = tmp->size;
1175 hdr->blk_size = tmp->size;
1181 regf->blk_tail->next = tmp;
1182 regf->blk_tail = tmp;
1183 if (!regf->free_space) regf->free_space = tmp;
1189 * Allocate a unit of space ... and return a pointer as function param
1190 * and the block's offset as a side effect
1192 static void *nt_alloc_regf_space(struct registry_hive *h, int size, uint_t *off)
1194 REGF *regf = h->backend_data;
1199 if (!regf || !size || !off) return NULL;
1201 SMB_REG_ASSERT(regf->blk_head != NULL);
1204 * round up size to include header and then to 8-byte boundary
1206 size = (size + 4 + 7) & ~7;
1209 * Check if there is space, if none, grab a block
1211 if (!regf->free_space) {
1212 if (!nt_create_hbin_blk(h, REGF_HDR_BLKSIZ))
1217 * Now, chain down the list of blocks looking for free space
1220 for (blk = regf->free_space; blk != NULL; blk = blk->next) {
1221 if (blk->free_space <= size) {
1222 tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
1223 ret = blk->data + blk->fsp_off;
1224 blk->free_space -= size;
1225 blk->fsp_off += size;
1227 /* Insert the header */
1228 ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
1231 * Fix up the free space ptr
1232 * If it is NULL, we fix it up next time
1235 if (!blk->free_space)
1236 regf->free_space = blk->next;
1239 return (((char *)ret)+4);/* The pointer needs to be to the data struct */
1244 * If we got here, we need to add another block, which might be
1245 * larger than one block -- deal with that later
1247 if (nt_create_hbin_blk(h, REGF_HDR_BLKSIZ)) {
1248 blk = regf->free_space;
1249 tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
1250 ret = blk->data + blk->fsp_off;
1251 blk->free_space -= size;
1252 blk->fsp_off += size;
1254 /* Insert the header */
1255 ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
1258 * Fix up the free space ptr
1259 * If it is NULL, we fix it up next time
1262 if (!blk->free_space)
1263 regf->free_space = blk->next;
1266 return (((char *)ret) + 4);/* The pointer needs to be to the data struct */
1273 * Store a SID at the location provided
1275 static int nt_store_SID(struct registry_hive *regf, DOM_SID *sid, uint8_t *locn)
1280 if (!regf || !sid || !locn) return 0;
1282 *p = sid->sid_rev_num; p++;
1283 *p = sid->num_auths; p++;
1285 for (i=0; i < 6; i++) {
1286 *p = sid->id_auth[i]; p++;
1289 for (i=0; i < sid->num_auths; i++) {
1290 SIVAL(p, 0, sid->sub_auths[i]); p+=4;
1297 static int nt_store_ace(struct registry_hive *regf, SEC_ACE *ace, uint8_t *locn)
1300 SEC_ACE *reg_ace = (SEC_ACE *)locn;
1303 if (!regf || !ace || !locn) return 0;
1305 reg_ace->type = ace->type;
1306 reg_ace->flags = ace->flags;
1308 /* Deal with the length when we have stored the SID */
1310 p = (uint8_t *)®_ace->info.mask;
1312 SIVAL(p, 0, ace->info.mask); p += 4;
1314 size = nt_store_SID(regf, &ace->trustee, p);
1316 size += 8; /* Size of the fixed header */
1318 p = (uint8_t *)®_ace->size;
1326 * Store an ACL at the location provided
1328 static int nt_store_acl(struct registry_hive *regf, SEC_ACL *acl, uint8_t *locn) {
1330 uint8_t *p = locn, *s;
1332 if (!regf || !acl || !locn) return 0;
1335 * Now store the header and then the ACEs ...
1338 SSVAL(p, 0, acl->revision);
1340 p += 2; s = p; /* Save this for the size field */
1344 SIVAL(p, 0, acl->num_aces);
1348 for (i = 0; i < acl->num_aces; i++) {
1349 size = nt_store_ace(regf, &acl->ace[i], p);
1359 * Flatten and store the Sec Desc
1360 * Windows lays out the DACL first, but since there is no SACL, it might be
1361 * that first, then the owner, then the group SID. So, we do it that way
1364 static uint_t nt_store_sec_desc(struct registry_hive *regf, SEC_DESC *sd, char *locn)
1366 SEC_DESC *rsd = (SEC_DESC *)locn;
1367 uint_t size = 0, off = 0;
1369 if (!regf || !sd || !locn) return 0;
1372 * Now, fill in the first two fields, then lay out the various fields
1376 rsd->revision = SEC_DESC_REVISION;
1377 rsd->type = SEC_DESC_DACL_PRESENT | SEC_DESC_SELF_RELATIVE;
1379 off = 4 * sizeof(DWORD) + 4;
1382 size = nt_store_acl(regf, sd->sacl, (char *)(locn + off));
1383 rsd->off_sacl = off;
1391 rsd->off_dacl = off;
1392 size = nt_store_acl(regf, sd->dacl, (char *)(locn + off));
1400 /* Now the owner and group SIDs */
1402 if (sd->owner_sid) {
1403 rsd->off_owner_sid = off;
1404 size = nt_store_SID(regf, sd->owner_sid, (char *)(locn + off));
1407 rsd->off_owner_sid = 0;
1413 rsd->off_grp_sid = off;
1414 size = nt_store_SID(regf, sd->grp_sid, (char *)(locn + off));
1417 rsd->off_grp_sid = 0;
1426 * Store the security information
1428 * If it has already been stored, just get its offset from record
1429 * otherwise, store it and record its offset
1431 static uint_t nt_store_security(struct registry_hive *regf, KEY_SEC_DESC *sec)
1437 if (sec->offset) return sec->offset;
1440 * OK, we don't have this one in the file yet. We must compute the
1441 * size taken by the security descriptor as a self-relative SD, which
1442 * means making one pass over each structure and figuring it out
1445 /* FIXME size = sec_desc_size(sec->sec_desc); */
1447 /* Allocate that much space */
1449 sk_hdr = nt_alloc_regf_space(regf, size, &sk_off);
1450 sec->sk_hdr = sk_hdr;
1452 if (!sk_hdr) return 0;
1454 /* Now, lay out the sec_desc in the space provided */
1456 sk_hdr->SK_ID = str_to_dword("sk");
1459 * We can't deal with the next and prev offset in the SK_HDRs until the
1460 * whole tree has been stored, then we can go and deal with them
1463 sk_hdr->ref_cnt = sec->ref_cnt;
1464 sk_hdr->rec_size = size; /* Is this correct */
1466 /* Now, lay out the sec_desc */
1468 if (!nt_store_sec_desc(regf, sec->sec_desc, (char *)&sk_hdr->sec_desc))
1476 * Store a KEY in the file ...
1478 * We store this depth first, and defer storing the lf struct until
1479 * all the sub-keys have been stored.
1481 * We store the NK hdr, any SK header, class name, and VK structure, then
1482 * recurse down the LF structures ...
1484 * We return the offset of the NK struct
1485 * FIXME, FIXME, FIXME: Convert to using SIVAL and SSVAL ...
1487 static int nt_store_reg_key(struct registry_hive *regf, struct registry_key *key)
1490 uint_t nk_off, sk_off, size;
1492 if (!regf || !key) return 0;
1494 size = sizeof(NK_HDR) + strlen(key->name) - 1;
1495 nk_hdr = nt_alloc_regf_space(regf, size, &nk_off);
1496 if (!nk_hdr) goto error;
1498 key->offset = nk_off; /* We will need this later */
1501 * Now fill in each field etc ...
1504 nk_hdr->NK_ID = str_to_dword("nk");
1505 if (key->type == REG_ROOT_KEY)
1506 nk_hdr->type = 0x2C;
1508 nk_hdr->type = 0x20;
1510 /* FIXME: Fill in the time of last update */
1512 if (key->type != REG_ROOT_KEY)
1513 nk_hdr->own_off = key->owner->offset;
1516 nk_hdr->subk_num = key->sub_keys->key_count;
1519 * Now, process the Sec Desc and then store its offset
1522 sk_off = nt_store_security(regf, key->security);
1523 nk_hdr->sk_off = sk_off;
1526 * Then, store the val list and store its offset
1529 nk_hdr->val_cnt = key->values->val_count;
1530 nk_hdr->val_off = nt_store_val_list(regf, key->values);
1533 nk_hdr->val_off = -1;
1534 nk_hdr->val_cnt = 0;
1538 * Finally, store the subkeys, and their offsets
1546 * Store the registry header ...
1547 * We actually create the registry header block and link it to the chain
1550 static REGF_HDR *nt_get_reg_header(struct registry_hive *h) {
1551 REGF *regf = h->backend_data;
1552 HBIN_BLK *tmp = NULL;
1554 tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
1556 memset(tmp, 0, sizeof(HBIN_BLK));
1557 tmp->type = REG_OUTBLK_HDR;
1558 tmp->size = REGF_HDR_BLKSIZ;
1559 tmp->data = malloc(REGF_HDR_BLKSIZ);
1560 if (!tmp->data) goto error;
1562 memset(tmp->data, 0, REGF_HDR_BLKSIZ); /* Make it pristine, unlike Windows */
1563 regf->blk_head = regf->blk_tail = tmp;
1565 return (REGF_HDR *)tmp->data;
1574 static WERROR nt_open_hive (TALLOC_CTX *mem_ctx, struct registry_hive *h, struct registry_key **key)
1578 uint_t regf_id, hbin_id;
1581 regf = (REGF *)talloc_p(mem_ctx, REGF);
1582 memset(regf, 0, sizeof(REGF));
1583 regf->owner_sid_str = h->credentials;
1584 h->backend_data = regf;
1586 DEBUG(5, ("Attempting to load registry file\n"));
1588 /* Get the header */
1590 if ((regf_hdr = nt_get_regf_hdr(h)) == NULL) {
1591 DEBUG(0, ("Unable to get header\n"));
1592 return WERR_GENERAL_FAILURE;
1595 /* Now process that header and start to read the rest in */
1597 if ((regf_id = IVAL(®f_hdr->REGF_ID,0)) != str_to_dword("regf")) {
1598 DEBUG(0, ("Unrecognized NT registry header id: %0X, %s\n",
1599 regf_id, h->location));
1600 return WERR_GENERAL_FAILURE;
1604 * Validate the header ...
1606 if (!valid_regf_hdr(regf_hdr)) {
1607 DEBUG(0, ("Registry file header does not validate: %s\n",
1609 return WERR_GENERAL_FAILURE;
1612 /* Update the last mod date, and then go get the first NK record and on */
1614 TTTONTTIME(regf, IVAL(®f_hdr->tim1,0), IVAL(®f_hdr->tim2,0));
1617 * The hbin hdr seems to be just uninteresting garbage. Check that
1618 * it is there, but that is all.
1621 hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
1623 if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID,0)) != str_to_dword("hbin")) {
1624 DEBUG(0, ("Unrecognized registry hbin hdr ID: %0X, %s\n",
1625 hbin_id, h->location));
1626 return WERR_GENERAL_FAILURE;
1630 * Get a pointer to the first key from the hreg_hdr
1633 DEBUG(2, ("First Key: %0X\n",
1634 IVAL(®f_hdr->first_key, 0)));
1636 regf->first_key = (NK_HDR *)LOCN(regf->base, IVAL(®f_hdr->first_key,0));
1637 DEBUGADD(2, ("First Key Offset: %0X\n",
1638 IVAL(®f_hdr->first_key, 0)));
1640 DEBUGADD(2, ("Data Block Size: %d\n",
1641 IVAL(®f_hdr->dblk_size, 0)));
1643 DEBUGADD(2, ("Offset to next hbin block: %0X\n",
1644 IVAL(&hbin_hdr->off_to_next, 0)));
1646 DEBUGADD(2, ("HBIN block size: %0X\n",
1647 IVAL(&hbin_hdr->blk_size, 0)));
1650 * Unmap the registry file, as we might want to read in another
1654 h->backend_data = regf;
1656 return nk_to_key(mem_ctx, h, ((REGF *)h->backend_data)->first_key, BLK_SIZE(((REGF *)h->backend_data)->first_key), NULL, key);
1660 static WERROR nt_num_subkeys(struct registry_key *k, int *num)
1662 REGF *regf = k->hive->backend_data;
1665 NK_HDR *nk_hdr = k->backend_data;
1666 lf_off = IVAL(&nk_hdr->lf_off,0);
1667 DEBUG(2, ("SubKey list offset: %0X\n", lf_off));
1672 lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
1674 return lf_num_entries(k->hive, lf_hdr, BLK_SIZE(lf_hdr), num);
1677 static WERROR nt_num_values(struct registry_key *k, int *count)
1679 NK_HDR *nk_hdr = k->backend_data;
1680 *count = IVAL(&nk_hdr->val_cnt,0);
1684 static WERROR nt_value_by_index(TALLOC_CTX *mem_ctx, struct registry_key *k, int n, struct registry_value **value)
1687 int val_off, vk_off;
1690 REGF *regf = k->hive->backend_data;
1691 NK_HDR *nk_hdr = k->backend_data;
1692 val_count = IVAL(&nk_hdr->val_cnt,0);
1693 val_off = IVAL(&nk_hdr->val_off,0);
1694 vl = (VL_TYPE *)LOCN(regf->base, val_off);
1695 DEBUG(2, ("Val List Offset: %0X\n", val_off));
1696 if(n < 0) return WERR_INVALID_PARAM;
1697 if(n >= val_count) return WERR_NO_MORE_ITEMS;
1699 vk_off = IVAL(&vl[n],0);
1700 vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
1701 return vk_to_val(mem_ctx, k, vk_hdr, BLK_SIZE(vk_hdr), value);
1704 static WERROR nt_key_by_index(TALLOC_CTX *mem_ctx, struct registry_key *k, int n, struct registry_key **subkey)
1706 REGF *regf = k->hive->backend_data;
1708 NK_HDR *nk_hdr = k->backend_data;
1710 lf_off = IVAL(&nk_hdr->lf_off,0);
1711 DEBUG(2, ("SubKey list offset: %0X\n", lf_off));
1714 * No more subkeys if lf_off == -1
1718 lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
1719 return lf_get_entry(mem_ctx, k, lf_hdr, BLK_SIZE(lf_hdr), n, subkey);
1722 return WERR_NO_MORE_ITEMS;
1725 static struct registry_operations reg_backend_nt4 = {
1727 .open_hive = nt_open_hive,
1728 .num_subkeys = nt_num_subkeys,
1729 .num_values = nt_num_values,
1730 .get_subkey_by_index = nt_key_by_index,
1731 .get_value_by_index = nt_value_by_index,
1742 NTSTATUS registry_nt4_init(void)
1744 return register_backend("registry", ®_backend_nt4);