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"
307 #include "lib/registry/common/registry.h"
309 #define REG_KEY_LIST_SIZE 10
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 unsigned int 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 */
361 unsigned int dblk_size;
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 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;
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;
491 DWORD str_to_dword(const char *a) {
493 unsigned long ret = 0;
494 for(i = strlen(a)-1; i >= 0; i--) {
495 ret = ret * 0x100 + a[i];
505 static BOOL nt_create_ace(SEC_ACE *ace, int type, int flags, uint32 perms, const char *sid)
510 if(!string_to_sid(&s, sid))return False;
511 init_sec_ace(ace, &s, type, access, flags);
516 * Create a default ACL
518 static SEC_ACL *nt_create_default_acl(REG_HANDLE *regf)
522 if(!nt_create_ace(&aces[0], 0x00, 0x0, 0xF003F, regf->owner_sid_str)) return NULL;
523 if(!nt_create_ace(&aces[1], 0x00, 0x0, 0xF003F, "S-1-5-18")) return NULL;
524 if(!nt_create_ace(&aces[2], 0x00, 0x0, 0xF003F, "S-1-5-32-544")) return NULL;
525 if(!nt_create_ace(&aces[3], 0x00, 0x0, 0x20019, "S-1-5-12")) return NULL;
526 if(!nt_create_ace(&aces[4], 0x00, 0x0B, GENERIC_RIGHT_ALL_ACCESS, regf->owner_sid_str)) return NULL;
527 if(!nt_create_ace(&aces[5], 0x00, 0x0B, 0x10000000, "S-1-5-18")) return NULL;
528 if(!nt_create_ace(&aces[6], 0x00, 0x0B, 0x10000000, "S-1-5-32-544")) return NULL;
529 if(!nt_create_ace(&aces[7], 0x00, 0x0B, 0x80000000, "S-1-5-12")) return NULL;
531 return make_sec_acl(regf->mem_ctx, 2, 8, aces);
535 * Create a default security descriptor. We pull in things from env
538 static SEC_DESC *nt_create_def_sec_desc(REG_HANDLE *regf)
542 tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
545 tmp->type = SEC_DESC_SELF_RELATIVE | SEC_DESC_DACL_PRESENT;
546 if (!string_to_sid(tmp->owner_sid, "S-1-5-32-544")) goto error;
547 if (!string_to_sid(tmp->grp_sid, "S-1-5-18")) goto error;
549 tmp->dacl = nt_create_default_acl(regf);
554 if (tmp) nt_delete_sec_desc(tmp);
559 * We will implement inheritence that is based on what the parent's SEC_DESC
560 * says, but the Owner and Group SIDs can be overwridden from the command line
561 * and additional ACEs can be applied from the command line etc.
563 static KEY_SEC_DESC *nt_inherit_security(REG_KEY *key)
566 if (!key) return NULL;
567 return key->security;
571 * Create an initial security descriptor and init other structures, if needed
572 * We assume that the initial security stuff is empty ...
574 static KEY_SEC_DESC *nt_create_init_sec(REG_HANDLE *h)
576 REGF *regf = h->backend_data;
577 KEY_SEC_DESC *tsec = NULL;
579 tsec = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
582 tsec->state = SEC_DESC_NBK;
585 tsec->sec_desc = regf->def_sec_desc;
592 * Get the starting record for NT Registry file
596 * Where we keep all the regf stuff for one registry.
597 * This is the structure that we use to tie the in memory tree etc
598 * together. By keeping separate structs, we can operate on different
599 * registries at the same time.
600 * Currently, the SK_MAP is an array of mapping structure.
601 * Since we only need this on input and output, we fill in the structure
602 * as we go on input. On output, we know how many SK items we have, so
603 * we can allocate the structure as we need to.
604 * If you add stuff here that is dynamically allocated, add the
605 * appropriate free statements below.
608 #define REG_HANDLE_REGTYPE_NONE 0
609 #define REG_HANDLE_REGTYPE_NT 1
610 #define REG_HANDLE_REGTYPE_W9X 2
612 #define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
613 (r)->last_mod_time.high = (t2);
615 #define REGF_HDR_BLKSIZ 0x1000
617 #define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4)
618 #define LOCN(base, f) ((base) + OFF(f))
620 /* Get the header of the registry. Return a pointer to the structure
621 * If the mmap'd area has not been allocated, then mmap the input file
623 static REGF_HDR *nt_get_regf_hdr(REG_HANDLE *h)
625 REGF *regf = h->backend_data;
626 SMB_REG_ASSERT(regf);
628 if (!regf->base) { /* Try to mmap etc the file */
630 if ((regf->fd = open(h->location, O_RDONLY, 0000)) <0) {
631 return NULL; /* What about errors? */
634 if (fstat(regf->fd, ®f->sbuf) < 0) {
638 regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
640 if ((int)regf->base == 1) {
641 DEBUG(0,("Could not mmap file: %s, %s\n", h->location,
648 * At this point, regf->base != NULL, and we should be able to read the
652 SMB_REG_ASSERT(regf->base != NULL);
654 return (REGF_HDR *)regf->base;
658 * Validate a regf header
659 * For now, do nothing, but we should check the checksum
661 static int valid_regf_hdr(REGF_HDR *regf_hdr)
663 if (!regf_hdr) return 0;
671 * Process an SK header ...
672 * Every time we see a new one, add it to the map. Otherwise, just look it up.
673 * We will do a simple linear search for the moment, since many KEYs have the
674 * same security descriptor.
675 * We allocate the map in increments of 10 entries.
679 * Create a new entry in the map, and increase the size of the map if needed
681 static SK_MAP *alloc_sk_map_entry(REG_HANDLE *h, KEY_SEC_DESC *tmp, int sk_off)
683 REGF *regf = h->backend_data;
684 if (!regf->sk_map) { /* Allocate a block of 10 */
685 regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
686 regf->sk_map_size = 10;
688 (regf->sk_map)[0].sk_off = sk_off;
689 (regf->sk_map)[0].key_sec_desc = tmp;
691 else { /* Simply allocate a new slot, unless we have to expand the list */
692 int ndx = regf->sk_count;
693 if (regf->sk_count >= regf->sk_map_size) {
694 regf->sk_map = (SK_MAP *)realloc(regf->sk_map,
695 (regf->sk_map_size + 10)*sizeof(SK_MAP));
701 * ndx already points at the first entry of the new block
703 regf->sk_map_size += 10;
705 (regf->sk_map)[ndx].sk_off = sk_off;
706 (regf->sk_map)[ndx].key_sec_desc = tmp;
713 * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
716 KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
720 if (!sk_map) return NULL;
722 for (i = 0; i < count; i++) {
724 if (sk_map[i].sk_off == sk_off)
725 return sk_map[i].key_sec_desc;
734 * Allocate a KEY_SEC_DESC if we can't find one in the map
736 static KEY_SEC_DESC *lookup_create_sec_key(REG_HANDLE *h, SK_MAP *sk_map, int sk_off)
738 REGF *regf = h->backend_data;
739 KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
744 else { /* Allocate a new one */
745 tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
746 memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */
747 tmp->state = SEC_DESC_RES;
748 if (!alloc_sk_map_entry(h, tmp, sk_off)) {
755 static SEC_DESC *process_sec_desc(REG_HANDLE *regf, SEC_DESC *sec_desc)
757 SEC_DESC *tmp = NULL;
759 tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
761 tmp->revision = SVAL(&sec_desc->revision,0);
762 tmp->type = SVAL(&sec_desc->type,0);
763 DEBUG(2, ("SEC_DESC Rev: %0X, Type: %0X\n", tmp->revision, tmp->type));
764 DEBUGADD(2, ("SEC_DESC Owner Off: %0X\n", IVAL(&sec_desc->off_owner_sid,0)));
765 DEBUGADD(2, ("SEC_DESC Group Off: %0X\n", IVAL(&sec_desc->off_grp_sid,0)));
766 DEBUGADD(2, ("SEC_DESC DACL Off: %0X\n", IVAL(&sec_desc->off_dacl,0)));
767 tmp->owner_sid = sid_dup_talloc(regf->mem_ctx, (DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->off_owner_sid,0)));
768 if (!tmp->owner_sid) {
772 tmp->grp_sid = sid_dup_talloc(regf->mem_ctx, (DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->off_grp_sid,0)));
778 /* Now pick up the SACL and DACL */
780 DEBUG(0, ("%d, %d\n", IVAL(&sec_desc->off_sacl,0), IVAL(&sec_desc->off_dacl,0)));
782 if (sec_desc->off_sacl)
783 tmp->sacl = dup_sec_acl(regf->mem_ctx, (SEC_ACL *)((char *)sec_desc + IVAL(&sec_desc->off_sacl,0)));
787 if (sec_desc->off_dacl)
788 tmp->dacl = dup_sec_acl(regf->mem_ctx, (SEC_ACL *)((char *)sec_desc + IVAL(&sec_desc->off_dacl,0)));
795 static KEY_SEC_DESC *process_sk(REG_HANDLE *regf, SK_HDR *sk_hdr, int sk_off, int size)
797 KEY_SEC_DESC *tmp = NULL;
798 int sk_next_off, sk_prev_off, sk_size;
801 if (!sk_hdr) return NULL;
803 if (SVAL(&sk_hdr->SK_ID,0) != str_to_dword("sk")) {
804 DEBUG(0, ("Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
805 regf->regfile_name));
809 if (-size < (sk_size = IVAL(&sk_hdr->rec_size,0))) {
810 DEBUG(0, ("Incorrect SK record size: %d vs %d. %s\n",
811 -size, sk_size, regf->regfile_name));
816 * Now, we need to look up the SK Record in the map, and return it
817 * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
822 ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
823 && (tmp->state == SEC_DESC_OCU)) {
828 /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
830 SMB_REG_ASSERT(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
833 * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
834 * new KEY_SEC_DESC to the mapping structure, since the offset supplied is
835 * the actual offset of structure. The same offset will be used by
836 * all future references to this structure
837 * We could put all this unpleasantness in a function.
841 tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
842 memset(tmp, 0, sizeof(KEY_SEC_DESC));
845 * Allocate an entry in the SK_MAP ...
846 * We don't need to free tmp, because that is done for us if the
847 * sm_map entry can't be expanded when we need more space in the map.
850 if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
856 tmp->state = SEC_DESC_OCU;
859 * Now, process the actual sec desc and plug the values in
862 sec_desc = (SEC_DESC *)&sk_hdr->sec_desc[0];
863 tmp->sec_desc = process_sec_desc(regf, sec_desc);
866 * Now forward and back links. Here we allocate an entry in the sk_map
867 * if it does not exist, and mark it reserved
870 sk_prev_off = IVAL(&sk_hdr->prev_off,0);
871 tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
872 SMB_REG_ASSERT(tmp->prev != NULL);
873 sk_next_off = IVAL(&sk_hdr->next_off,0);
874 tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
875 SMB_REG_ASSERT(tmp->next != NULL);
882 * Process a VK header and return a value
884 static WERROR vk_to_val(REG_KEY *parent, VK_HDR *vk_hdr, int size, REG_VAL **value)
887 REGF *regf = parent->handle->backend_data;
888 int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
889 const char *val_type;
892 if (!vk_hdr) return WERR_INVALID_PARAM;
894 if ((vk_id = SVAL(&vk_hdr->VK_ID,0)) != str_to_dword("vk")) {
895 DEBUG(0, ("Unrecognized VK header ID: %0X, block: %0X, %s\n",
896 vk_id, (int)vk_hdr, parent->handle->location));
897 return WERR_GENERAL_FAILURE;
900 nam_len = SVAL(&vk_hdr->nam_len,0);
901 val_name[nam_len] = '\0';
902 flag = SVAL(&vk_hdr->flag,0);
903 dat_type = IVAL(&vk_hdr->dat_type,0);
904 dat_len = IVAL(&vk_hdr->dat_len,0); /* If top bit, offset contains data */
905 dat_off = IVAL(&vk_hdr->dat_off,0);
907 tmp = reg_val_new(parent, NULL);
908 tmp->has_name = flag;
909 tmp->data_type = dat_type;
912 strncpy(val_name, vk_hdr->dat_name, nam_len);
913 tmp->name = strdup(val_name);
916 strncpy(val_name, "<No Name>", 10);
919 * Allocate space and copy the data as a BLOB
922 if (dat_len&0x7FFFFFFF) {
924 char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
926 tmp->data_blk = dtmp;
928 if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
929 char *dat_ptr = LOCN(regf->base, dat_off);
930 memcpy(dtmp, dat_ptr, dat_len);
932 else { /* The data is in the offset or type */
935 * Some registry files seem to have weird fields. If top bit is set,
936 * but len is 0, the type seems to be the value ...
937 * Not sure how to handle this last type for the moment ...
939 dat_len = dat_len & 0x7FFFFFFF;
940 memcpy(dtmp, &dat_off, dat_len);
943 tmp->data_len = dat_len;
950 static BOOL vl_verify(VL_TYPE vl, int count, int size)
952 if(!vl) return False;
953 if (-size < (count+1)*sizeof(int)){
954 DEBUG(0, ("Error in VL header format. Size less than space required. %d\n", -size));
960 static WERROR lf_verify(REG_HANDLE *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(REG_HANDLE *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(REG_HANDLE *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent, REG_KEY **);
993 * Process an LF Header and return a list of sub-keys
995 static WERROR lf_get_entry(REG_KEY *parent, LF_HDR *lf_hdr, int size, int n, REG_KEY **key)
997 REGF *regf = parent->handle->backend_data;
1002 if (!lf_hdr) return WERR_INVALID_PARAM;
1004 error = lf_verify(parent->handle, 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(parent->handle, nk_hdr, BLK_SIZE(nk_hdr), parent, key);
1020 static WERROR nk_to_key(REG_HANDLE *h, NK_HDR *nk_hdr, int size, REG_KEY *parent, REG_KEY **key)
1022 REGF *regf = h->backend_data;
1023 REG_KEY *tmp = NULL, *own;
1024 int name_len, clsname_len, sk_off, own_off;
1028 char key_name[1024], cls_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->handle->location));
1035 return WERR_INVALID_PARAM;
1038 SMB_REG_ASSERT(size < 0);
1040 name_len = 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 + name_len)
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 + name_len)) {
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 sizeof(NK_HDR), name_len, clsname_len));
1055 return WERR_GENERAL_FAILURE;
1058 DEBUG(2, ("NK HDR: Name len: %d, class name len: %d\n", name_len, clsname_len));
1060 /* Fish out the key name and process the LF list */
1062 SMB_REG_ASSERT(name_len < sizeof(key_name));
1064 strncpy(key_name, nk_hdr->key_nam, name_len);
1065 key_name[name_len] = '\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 if(type == REG_ROOT_KEY) tmp = reg_key_new_abs(key_name, h, nk_hdr);
1074 else tmp = reg_key_new_rel(key_name, parent, nk_hdr);
1076 DEBUG(2, ("Key name: %s\n", key_name));
1079 * Fish out the class name, it is in UNICODE, while the key name is
1083 if (clsname_len) { /* Just print in Ascii for now */
1084 smb_ucs2_t *clsnamep;
1087 clsnam_off = IVAL(&nk_hdr->clsnam_off,0);
1088 clsnamep = (smb_ucs2_t *)LOCN(regf->base, clsnam_off);
1089 DEBUG(2, ("Class Name Offset: %0X\n", clsnam_off));
1091 tmp->class_name = talloc_strdup_w(regf->mem_ctx, clsnamep);
1093 DEBUGADD(2,(" Class Name: %s\n", cls_name));
1098 * Process the owner offset ...
1101 own_off = IVAL(&nk_hdr->own_off,0);
1102 own = (REG_KEY *)LOCN(regf->base, own_off);
1103 DEBUG(2, ("Owner Offset: %0X\n", own_off));
1105 DEBUGADD(2, (" Owner locn: %0X, Our locn: %0X\n",
1106 (unsigned int)own, (unsigned int)nk_hdr));
1109 * We should verify that the owner field is correct ...
1110 * for now, we don't worry ...
1114 * Also handle the SK header ...
1117 sk_off = IVAL(&nk_hdr->sk_off,0);
1118 sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
1119 DEBUG(2, ("SK Offset: %0X\n", sk_off));
1124 tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
1134 * Allocate a new hbin block, set up the header for the block etc
1136 static HBIN_BLK *nt_create_hbin_blk(REG_HANDLE *h, int size)
1138 REGF *regf = h->backend_data;
1142 if (!regf || !size) return NULL;
1144 /* Round size up to multiple of REGF_HDR_BLKSIZ */
1146 size = (size + (REGF_HDR_BLKSIZ - 1)) & ~(REGF_HDR_BLKSIZ - 1);
1148 tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
1149 memset(tmp, 0, sizeof(HBIN_BLK));
1151 tmp->data = malloc(size);
1153 memset(tmp->data, 0, size); /* Make it pristine */
1156 /*FIXMEtmp->file_offset = regf->blk_tail->file_offset + regf->blk_tail->size;*/
1158 tmp->free_space = size - (sizeof(HBIN_HDR) - sizeof(HBIN_SUB_HDR));
1159 tmp->fsp_off = size - tmp->free_space;
1162 * Now, build the header in the data block
1164 hdr = (HBIN_HDR *)tmp->data;
1165 hdr->HBIN_ID = str_to_dword("hbin");
1166 hdr->off_from_first = tmp->file_offset - REGF_HDR_BLKSIZ;
1167 hdr->off_to_next = tmp->size;
1168 hdr->blk_size = tmp->size;
1174 regf->blk_tail->next = tmp;
1175 regf->blk_tail = tmp;
1176 if (!regf->free_space) regf->free_space = tmp;
1182 * Allocate a unit of space ... and return a pointer as function param
1183 * and the block's offset as a side effect
1185 static void *nt_alloc_regf_space(REG_HANDLE *h, int size, unsigned int *off)
1187 REGF *regf = h->backend_data;
1192 if (!regf || !size || !off) return NULL;
1194 SMB_REG_ASSERT(regf->blk_head != NULL);
1197 * round up size to include header and then to 8-byte boundary
1199 size = (size + 4 + 7) & ~7;
1202 * Check if there is space, if none, grab a block
1204 if (!regf->free_space) {
1205 if (!nt_create_hbin_blk(h, REGF_HDR_BLKSIZ))
1210 * Now, chain down the list of blocks looking for free space
1213 for (blk = regf->free_space; blk != NULL; blk = blk->next) {
1214 if (blk->free_space <= size) {
1215 tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
1216 ret = blk->data + blk->fsp_off;
1217 blk->free_space -= size;
1218 blk->fsp_off += size;
1220 /* Insert the header */
1221 ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
1224 * Fix up the free space ptr
1225 * If it is NULL, we fix it up next time
1228 if (!blk->free_space)
1229 regf->free_space = blk->next;
1232 return (((char *)ret)+4);/* The pointer needs to be to the data struct */
1237 * If we got here, we need to add another block, which might be
1238 * larger than one block -- deal with that later
1240 if (nt_create_hbin_blk(h, REGF_HDR_BLKSIZ)) {
1241 blk = regf->free_space;
1242 tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
1243 ret = blk->data + blk->fsp_off;
1244 blk->free_space -= size;
1245 blk->fsp_off += size;
1247 /* Insert the header */
1248 ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
1251 * Fix up the free space ptr
1252 * If it is NULL, we fix it up next time
1255 if (!blk->free_space)
1256 regf->free_space = blk->next;
1259 return (((char *)ret) + 4);/* The pointer needs to be to the data struct */
1266 * Store a SID at the location provided
1268 static int nt_store_SID(REG_HANDLE *regf, DOM_SID *sid, unsigned char *locn)
1271 unsigned char *p = locn;
1273 if (!regf || !sid || !locn) return 0;
1275 *p = sid->sid_rev_num; p++;
1276 *p = sid->num_auths; p++;
1278 for (i=0; i < 6; i++) {
1279 *p = sid->id_auth[i]; p++;
1282 for (i=0; i < sid->num_auths; i++) {
1283 SIVAL(p, 0, sid->sub_auths[i]); p+=4;
1290 static int nt_store_ace(REG_HANDLE *regf, SEC_ACE *ace, unsigned char *locn)
1293 SEC_ACE *reg_ace = (SEC_ACE *)locn;
1296 if (!regf || !ace || !locn) return 0;
1298 reg_ace->type = ace->type;
1299 reg_ace->flags = ace->flags;
1301 /* Deal with the length when we have stored the SID */
1303 p = (unsigned char *)®_ace->info.mask;
1305 SIVAL(p, 0, ace->info.mask); p += 4;
1307 size = nt_store_SID(regf, &ace->trustee, p);
1309 size += 8; /* Size of the fixed header */
1311 p = (unsigned char *)®_ace->size;
1319 * Store an ACL at the location provided
1321 static int nt_store_acl(REG_HANDLE *regf, SEC_ACL *acl, unsigned char *locn) {
1323 unsigned char *p = locn, *s;
1325 if (!regf || !acl || !locn) return 0;
1328 * Now store the header and then the ACEs ...
1331 SSVAL(p, 0, acl->revision);
1333 p += 2; s = p; /* Save this for the size field */
1337 SIVAL(p, 0, acl->num_aces);
1341 for (i = 0; i < acl->num_aces; i++) {
1342 size = nt_store_ace(regf, &acl->ace[i], p);
1352 * Flatten and store the Sec Desc
1353 * Windows lays out the DACL first, but since there is no SACL, it might be
1354 * that first, then the owner, then the group SID. So, we do it that way
1357 static unsigned int nt_store_sec_desc(REG_HANDLE *regf, SEC_DESC *sd, char *locn)
1359 SEC_DESC *rsd = (SEC_DESC *)locn;
1360 unsigned int size = 0, off = 0;
1362 if (!regf || !sd || !locn) return 0;
1365 * Now, fill in the first two fields, then lay out the various fields
1369 rsd->revision = SEC_DESC_REVISION;
1370 rsd->type = SEC_DESC_DACL_PRESENT | SEC_DESC_SELF_RELATIVE;
1372 off = 4 * sizeof(DWORD) + 4;
1375 size = nt_store_acl(regf, sd->sacl, (char *)(locn + off));
1376 rsd->off_sacl = off;
1384 rsd->off_dacl = off;
1385 size = nt_store_acl(regf, sd->dacl, (char *)(locn + off));
1393 /* Now the owner and group SIDs */
1395 if (sd->owner_sid) {
1396 rsd->off_owner_sid = off;
1397 size = nt_store_SID(regf, sd->owner_sid, (char *)(locn + off));
1400 rsd->off_owner_sid = 0;
1406 rsd->off_grp_sid = off;
1407 size = nt_store_SID(regf, sd->grp_sid, (char *)(locn + off));
1410 rsd->off_grp_sid = 0;
1419 * Store the security information
1421 * If it has already been stored, just get its offset from record
1422 * otherwise, store it and record its offset
1424 static unsigned int nt_store_security(REG_HANDLE *regf, KEY_SEC_DESC *sec)
1427 unsigned int sk_off;
1430 if (sec->offset) return sec->offset;
1433 * OK, we don't have this one in the file yet. We must compute the
1434 * size taken by the security descriptor as a self-relative SD, which
1435 * means making one pass over each structure and figuring it out
1438 //FIXME size = sec_desc_size(sec->sec_desc);
1440 /* Allocate that much space */
1442 sk_hdr = nt_alloc_regf_space(regf, size, &sk_off);
1443 sec->sk_hdr = sk_hdr;
1445 if (!sk_hdr) return 0;
1447 /* Now, lay out the sec_desc in the space provided */
1449 sk_hdr->SK_ID = str_to_dword("sk");
1452 * We can't deal with the next and prev offset in the SK_HDRs until the
1453 * whole tree has been stored, then we can go and deal with them
1456 sk_hdr->ref_cnt = sec->ref_cnt;
1457 sk_hdr->rec_size = size; /* Is this correct */
1459 /* Now, lay out the sec_desc */
1461 if (!nt_store_sec_desc(regf, sec->sec_desc, (char *)&sk_hdr->sec_desc))
1471 * Store a KEY in the file ...
1473 * We store this depth first, and defer storing the lf struct until
1474 * all the sub-keys have been stored.
1476 * We store the NK hdr, any SK header, class name, and VK structure, then
1477 * recurse down the LF structures ...
1479 * We return the offset of the NK struct
1480 * FIXME, FIXME, FIXME: Convert to using SIVAL and SSVAL ...
1482 static int nt_store_reg_key(REG_HANDLE *regf, REG_KEY *key)
1485 unsigned int nk_off, sk_off, size;
1487 if (!regf || !key) return 0;
1489 size = sizeof(NK_HDR) + strlen(key->name) - 1;
1490 nk_hdr = nt_alloc_regf_space(regf, size, &nk_off);
1491 if (!nk_hdr) goto error;
1493 key->offset = nk_off; /* We will need this later */
1496 * Now fill in each field etc ...
1499 nk_hdr->NK_ID = str_to_dword("nk");
1500 if (key->type == REG_ROOT_KEY)
1501 nk_hdr->type = 0x2C;
1503 nk_hdr->type = 0x20;
1505 /* FIXME: Fill in the time of last update */
1507 if (key->type != REG_ROOT_KEY)
1508 nk_hdr->own_off = key->owner->offset;
1511 nk_hdr->subk_num = key->sub_keys->key_count;
1514 * Now, process the Sec Desc and then store its offset
1517 sk_off = nt_store_security(regf, key->security);
1518 nk_hdr->sk_off = sk_off;
1521 * Then, store the val list and store its offset
1524 nk_hdr->val_cnt = key->values->val_count;
1525 nk_hdr->val_off = nt_store_val_list(regf, key->values);
1528 nk_hdr->val_off = -1;
1529 nk_hdr->val_cnt = 0;
1533 * 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(REG_HANDLE *h) {
1547 REGF *regf = h->backend_data;
1548 HBIN_BLK *tmp = NULL;
1550 tmp = (HBIN_BLK *)malloc(sizeof(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;
1568 static WERROR nt_close_registry (REG_HANDLE *h)
1570 REGF *regf = h->backend_data;
1571 if (regf->base) munmap(regf->base, regf->sbuf.st_size);
1573 close(regf->fd); /* Ignore the error :-) */
1576 regf->sk_count = regf->sk_map_size = 0;
1582 static WERROR nt_open_registry (REG_HANDLE *h, const char *location, const char *credentials)
1584 REGF *regf = (REGF *)malloc(sizeof(REGF));
1586 unsigned int regf_id, hbin_id;
1589 memset(regf, 0, sizeof(REGF));
1590 regf->mem_ctx = talloc_init("regf");
1591 regf->owner_sid_str = credentials;
1592 h->backend_data = regf;
1594 DEBUG(5, ("Attempting to load registry file\n"));
1596 /* Get the header */
1598 if ((regf_hdr = nt_get_regf_hdr(h)) == NULL) {
1599 DEBUG(0, ("Unable to get header\n"));
1600 return WERR_GENERAL_FAILURE;
1603 /* Now process that header and start to read the rest in */
1605 if ((regf_id = IVAL(®f_hdr->REGF_ID,0)) != str_to_dword("regf")) {
1606 DEBUG(0, ("Unrecognized NT registry header id: %0X, %s\n",
1607 regf_id, h->location));
1608 return WERR_GENERAL_FAILURE;
1612 * Validate the header ...
1614 if (!valid_regf_hdr(regf_hdr)) {
1615 DEBUG(0, ("Registry file header does not validate: %s\n",
1617 return WERR_GENERAL_FAILURE;
1620 /* Update the last mod date, and then go get the first NK record and on */
1622 TTTONTTIME(regf, IVAL(®f_hdr->tim1,0), IVAL(®f_hdr->tim2,0));
1625 * The hbin hdr seems to be just uninteresting garbage. Check that
1626 * it is there, but that is all.
1629 hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
1631 if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID,0)) != str_to_dword("hbin")) {
1632 DEBUG(0, ("Unrecognized registry hbin hdr ID: %0X, %s\n",
1633 hbin_id, h->location));
1634 return WERR_GENERAL_FAILURE;
1638 * Get a pointer to the first key from the hreg_hdr
1641 DEBUG(2, ("First Key: %0X\n",
1642 IVAL(®f_hdr->first_key, 0)));
1644 regf->first_key = (NK_HDR *)LOCN(regf->base, IVAL(®f_hdr->first_key,0));
1645 DEBUGADD(2, ("First Key Offset: %0X\n",
1646 IVAL(®f_hdr->first_key, 0)));
1648 DEBUGADD(2, ("Data Block Size: %d\n",
1649 IVAL(®f_hdr->dblk_size, 0)));
1651 DEBUGADD(2, ("Offset to next hbin block: %0X\n",
1652 IVAL(&hbin_hdr->off_to_next, 0)));
1654 DEBUGADD(2, ("HBIN block size: %0X\n",
1655 IVAL(&hbin_hdr->blk_size, 0)));
1658 * Unmap the registry file, as we might want to read in another
1662 h->backend_data = regf;
1667 static WERROR nt_get_root_key(REG_HANDLE *h, REG_KEY **key)
1669 return nk_to_key(h, ((REGF *)h->backend_data)->first_key, BLK_SIZE(((REGF *)h->backend_data)->first_key), NULL, key);
1672 static WERROR nt_num_subkeys(REG_KEY *k, int *num)
1674 REGF *regf = k->handle->backend_data;
1677 NK_HDR *nk_hdr = k->backend_data;
1678 lf_off = IVAL(&nk_hdr->lf_off,0);
1679 DEBUG(2, ("SubKey list offset: %0X\n", lf_off));
1684 lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
1686 return lf_num_entries(k->handle, lf_hdr, BLK_SIZE(lf_hdr), num);
1689 static WERROR nt_num_values(REG_KEY *k, int *count)
1691 NK_HDR *nk_hdr = k->backend_data;
1692 *count = IVAL(&nk_hdr->val_cnt,0);
1696 static WERROR nt_value_by_index(REG_KEY *k, int n, REG_VAL **value)
1699 int val_off, vk_off;
1701 REGF *regf = k->handle->backend_data;
1702 NK_HDR *nk_hdr = k->backend_data;
1703 val_off = IVAL(&nk_hdr->val_off,0);
1704 vl = (VL_TYPE *)LOCN(regf->base, val_off);
1705 DEBUG(2, ("Val List Offset: %0X\n", val_off));
1707 vk_off = IVAL(&vl[n],0);
1708 vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
1709 return vk_to_val(k, vk_hdr, BLK_SIZE(vk_hdr), value);
1712 static WERROR nt_key_by_index(REG_KEY *k, int n, REG_KEY **subkey)
1714 REGF *regf = k->handle->backend_data;
1716 NK_HDR *nk_hdr = k->backend_data;
1718 lf_off = IVAL(&nk_hdr->lf_off,0);
1719 DEBUG(2, ("SubKey list offset: %0X\n", lf_off));
1722 * No more subkeys if lf_off == -1
1726 lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
1727 return lf_get_entry(k, lf_hdr, BLK_SIZE(lf_hdr), n, subkey);
1730 return WERR_NO_MORE_ITEMS;
1733 static struct registry_ops reg_backend_nt4 = {
1735 .open_registry = nt_open_registry,
1736 .close_registry = nt_close_registry,
1737 .open_root_key = nt_get_root_key,
1738 .num_subkeys = nt_num_subkeys,
1739 .num_values = nt_num_values,
1740 .get_subkey_by_index = nt_key_by_index,
1741 .get_value_by_index = nt_value_by_index,
1752 NTSTATUS reg_nt4_init(void)
1754 return register_backend("registry", ®_backend_nt4);