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;
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 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(REG_HANDLE *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(REG_HANDLE *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(REG_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(REG_HANDLE *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.low = (t1); \
612 (r)->last_mod_time.high = (t2);
614 #define REGF_HDR_BLKSIZ 0x1000
616 #define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4)
617 #define LOCN(base, f) ((base) + OFF(f))
619 /* Get the header of the registry. Return a pointer to the structure
620 * If the mmap'd area has not been allocated, then mmap the input file
622 static REGF_HDR *nt_get_regf_hdr(REG_HANDLE *h)
624 REGF *regf = h->backend_data;
625 SMB_REG_ASSERT(regf);
627 if (!regf->base) { /* Try to mmap etc the file */
629 if ((regf->fd = open(h->location, O_RDONLY, 0000)) <0) {
630 return NULL; /* What about errors? */
633 if (fstat(regf->fd, ®f->sbuf) < 0) {
637 regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
639 if ((int)regf->base == 1) {
640 DEBUG(0,("Could not mmap file: %s, %s\n", h->location,
647 * At this point, regf->base != NULL, and we should be able to read the
651 SMB_REG_ASSERT(regf->base != NULL);
653 return (REGF_HDR *)regf->base;
657 * Validate a regf header
658 * For now, do nothing, but we should check the checksum
660 static int valid_regf_hdr(REGF_HDR *regf_hdr)
662 if (!regf_hdr) return 0;
670 * Process an SK header ...
671 * Every time we see a new one, add it to the map. Otherwise, just look it up.
672 * We will do a simple linear search for the moment, since many KEYs have the
673 * same security descriptor.
674 * We allocate the map in increments of 10 entries.
678 * Create a new entry in the map, and increase the size of the map if needed
680 static SK_MAP *alloc_sk_map_entry(REG_HANDLE *h, KEY_SEC_DESC *tmp, int sk_off)
682 REGF *regf = h->backend_data;
683 if (!regf->sk_map) { /* Allocate a block of 10 */
684 regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
685 regf->sk_map_size = 10;
687 (regf->sk_map)[0].sk_off = sk_off;
688 (regf->sk_map)[0].key_sec_desc = tmp;
690 else { /* Simply allocate a new slot, unless we have to expand the list */
691 int ndx = regf->sk_count;
692 if (regf->sk_count >= regf->sk_map_size) {
693 regf->sk_map = (SK_MAP *)realloc(regf->sk_map,
694 (regf->sk_map_size + 10)*sizeof(SK_MAP));
700 * ndx already points at the first entry of the new block
702 regf->sk_map_size += 10;
704 (regf->sk_map)[ndx].sk_off = sk_off;
705 (regf->sk_map)[ndx].key_sec_desc = tmp;
712 * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
715 KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
719 if (!sk_map) return NULL;
721 for (i = 0; i < count; i++) {
723 if (sk_map[i].sk_off == sk_off)
724 return sk_map[i].key_sec_desc;
733 * Allocate a KEY_SEC_DESC if we can't find one in the map
735 static KEY_SEC_DESC *lookup_create_sec_key(REG_HANDLE *h, SK_MAP *sk_map, int sk_off)
737 REGF *regf = h->backend_data;
738 KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
743 else { /* Allocate a new one */
744 tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
745 memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */
746 tmp->state = SEC_DESC_RES;
747 if (!alloc_sk_map_entry(h, tmp, sk_off)) {
754 static SEC_DESC *process_sec_desc(REG_HANDLE *regf, SEC_DESC *sec_desc)
756 SEC_DESC *tmp = NULL;
758 tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
760 tmp->revision = SVAL(&sec_desc->revision,0);
761 tmp->type = SVAL(&sec_desc->type,0);
762 DEBUG(2, ("SEC_DESC Rev: %0X, Type: %0X\n", tmp->revision, tmp->type));
763 DEBUGADD(2, ("SEC_DESC Owner Off: %0X\n", IVAL(&sec_desc->off_owner_sid,0)));
764 DEBUGADD(2, ("SEC_DESC Group Off: %0X\n", IVAL(&sec_desc->off_grp_sid,0)));
765 DEBUGADD(2, ("SEC_DESC DACL Off: %0X\n", IVAL(&sec_desc->off_dacl,0)));
766 tmp->owner_sid = sid_dup_talloc(regf->mem_ctx, (DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->off_owner_sid,0)));
767 if (!tmp->owner_sid) {
771 tmp->grp_sid = sid_dup_talloc(regf->mem_ctx, (DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->off_grp_sid,0)));
777 /* Now pick up the SACL and DACL */
779 DEBUG(0, ("%d, %d\n", IVAL(&sec_desc->off_sacl,0), IVAL(&sec_desc->off_dacl,0)));
781 if (sec_desc->off_sacl)
782 tmp->sacl = dup_sec_acl(regf->mem_ctx, (SEC_ACL *)((char *)sec_desc + IVAL(&sec_desc->off_sacl,0)));
786 if (sec_desc->off_dacl)
787 tmp->dacl = dup_sec_acl(regf->mem_ctx, (SEC_ACL *)((char *)sec_desc + IVAL(&sec_desc->off_dacl,0)));
794 static KEY_SEC_DESC *process_sk(REG_HANDLE *regf, SK_HDR *sk_hdr, int sk_off, int size)
796 KEY_SEC_DESC *tmp = NULL;
797 int sk_next_off, sk_prev_off, sk_size;
800 if (!sk_hdr) return NULL;
802 if (SVAL(&sk_hdr->SK_ID,0) != str_to_dword("sk")) {
803 DEBUG(0, ("Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
804 regf->regfile_name));
808 if (-size < (sk_size = IVAL(&sk_hdr->rec_size,0))) {
809 DEBUG(0, ("Incorrect SK record size: %d vs %d. %s\n",
810 -size, sk_size, regf->regfile_name));
815 * Now, we need to look up the SK Record in the map, and return it
816 * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
821 ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
822 && (tmp->state == SEC_DESC_OCU)) {
827 /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
829 SMB_REG_ASSERT(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
832 * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
833 * new KEY_SEC_DESC to the mapping structure, since the offset supplied is
834 * the actual offset of structure. The same offset will be used by
835 * all future references to this structure
836 * We could put all this unpleasantness in a function.
840 tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
841 memset(tmp, 0, sizeof(KEY_SEC_DESC));
844 * Allocate an entry in the SK_MAP ...
845 * We don't need to free tmp, because that is done for us if the
846 * sm_map entry can't be expanded when we need more space in the map.
849 if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
855 tmp->state = SEC_DESC_OCU;
858 * Now, process the actual sec desc and plug the values in
861 sec_desc = (SEC_DESC *)&sk_hdr->sec_desc[0];
862 tmp->sec_desc = process_sec_desc(regf, sec_desc);
865 * Now forward and back links. Here we allocate an entry in the sk_map
866 * if it does not exist, and mark it reserved
869 sk_prev_off = IVAL(&sk_hdr->prev_off,0);
870 tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
871 SMB_REG_ASSERT(tmp->prev != NULL);
872 sk_next_off = IVAL(&sk_hdr->next_off,0);
873 tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
874 SMB_REG_ASSERT(tmp->next != NULL);
881 * Process a VK header and return a value
883 static WERROR vk_to_val(REG_KEY *parent, VK_HDR *vk_hdr, int size, REG_VAL **value)
886 REGF *regf = parent->handle->backend_data;
887 int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
890 if (!vk_hdr) return WERR_INVALID_PARAM;
892 if ((vk_id = SVAL(&vk_hdr->VK_ID,0)) != str_to_dword("vk")) {
893 DEBUG(0, ("Unrecognized VK header ID: %0X, block: %0X, %s\n",
894 vk_id, (int)vk_hdr, parent->handle->location));
895 return WERR_GENERAL_FAILURE;
898 nam_len = SVAL(&vk_hdr->nam_len,0);
899 val_name[nam_len] = '\0';
900 flag = SVAL(&vk_hdr->flag,0);
901 dat_type = IVAL(&vk_hdr->dat_type,0);
902 dat_len = IVAL(&vk_hdr->dat_len,0); /* If top bit, offset contains data */
903 dat_off = IVAL(&vk_hdr->dat_off,0);
905 tmp = reg_val_new(parent, NULL);
906 tmp->has_name = flag;
907 tmp->data_type = dat_type;
910 strncpy(val_name, vk_hdr->dat_name, nam_len);
911 tmp->name = strdup(val_name);
914 strncpy(val_name, "<No Name>", 10);
917 * Allocate space and copy the data as a BLOB
920 if (dat_len&0x7FFFFFFF) {
922 char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
924 tmp->data_blk = dtmp;
926 if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
927 char *dat_ptr = LOCN(regf->base, dat_off);
928 memcpy(dtmp, dat_ptr, dat_len);
930 else { /* The data is in the offset or type */
933 * Some registry files seem to have weird fields. If top bit is set,
934 * but len is 0, the type seems to be the value ...
935 * Not sure how to handle this last type for the moment ...
937 dat_len = dat_len & 0x7FFFFFFF;
938 memcpy(dtmp, &dat_off, dat_len);
941 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));
958 static WERROR lf_verify(REG_HANDLE *h, LF_HDR *lf_hdr, int size)
961 if ((lf_id = SVAL(&lf_hdr->LF_ID,0)) != str_to_dword("lf")) {
962 DEBUG(0, ("Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
963 lf_id, (int)lf_hdr, h->location));
964 return WERR_INVALID_PARAM;
969 static WERROR lf_num_entries(REG_HANDLE *h, LF_HDR *lf_hdr, int size, int *count)
973 error = lf_verify(h, lf_hdr, size);
974 if(!W_ERROR_IS_OK(error)) return error;
976 SMB_REG_ASSERT(size < 0);
978 *count = SVAL(&lf_hdr->key_count,0);
979 DEBUG(2, ("Key Count: %u\n", *count));
980 if (*count <= 0) return WERR_INVALID_PARAM;
986 static WERROR nk_to_key(REG_HANDLE *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent, REG_KEY **);
991 * Process an LF Header and return a list of sub-keys
993 static WERROR lf_get_entry(REG_KEY *parent, LF_HDR *lf_hdr, int size, int n, REG_KEY **key)
995 REGF *regf = parent->handle->backend_data;
1000 if (!lf_hdr) return WERR_INVALID_PARAM;
1002 error = lf_verify(parent->handle, lf_hdr, size);
1003 if(!W_ERROR_IS_OK(error)) return error;
1005 SMB_REG_ASSERT(size < 0);
1007 count = SVAL(&lf_hdr->key_count,0);
1008 DEBUG(2, ("Key Count: %u\n", count));
1009 if (count <= 0) return WERR_GENERAL_FAILURE;
1010 if (n >= count) return WERR_NO_MORE_ITEMS;
1012 nk_off = IVAL(&lf_hdr->hr[n].nk_off,0);
1013 DEBUG(2, ("NK Offset: %0X\n", nk_off));
1014 nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
1015 return nk_to_key(parent->handle, nk_hdr, BLK_SIZE(nk_hdr), parent, key);
1018 static WERROR nk_to_key(REG_HANDLE *h, NK_HDR *nk_hdr, int size, REG_KEY *parent, REG_KEY **key)
1020 REGF *regf = h->backend_data;
1021 REG_KEY *tmp = NULL, *own;
1022 int namlen, clsname_len, sk_off, own_off;
1026 char key_name[1024], cls_name[1024];
1028 if (!nk_hdr) return WERR_INVALID_PARAM;
1030 if ((nk_id = SVAL(&nk_hdr->NK_ID,0)) != str_to_dword("nk")) {
1031 DEBUG(0, ("Unrecognized NK Header format: %08X, Block: %0X. %s\n",
1032 nk_id, (int)nk_hdr, parent->handle->location));
1033 return WERR_INVALID_PARAM;
1036 SMB_REG_ASSERT(size < 0);
1038 namlen = SVAL(&nk_hdr->nam_len,0);
1039 clsname_len = SVAL(&nk_hdr->clsnam_len,0);
1042 * The value of -size should be ge
1043 * (sizeof(NK_HDR) - 1 + namlen)
1044 * The -1 accounts for the fact that we included the first byte of
1045 * the name in the structure. clsname_len is the length of the thing
1046 * pointed to by clsnam_off
1049 if (-size < (sizeof(NK_HDR) - 1 + namlen)) {
1050 DEBUG(0, ("Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr));
1051 DEBUG(0, ("Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
1052 sizeof(NK_HDR), namlen, clsname_len));
1053 return WERR_GENERAL_FAILURE;
1056 DEBUG(2, ("NK HDR: Name len: %d, class name len: %d\n", namlen, clsname_len));
1058 /* Fish out the key name and process the LF list */
1060 SMB_REG_ASSERT(namlen < sizeof(key_name));
1062 strncpy(key_name, nk_hdr->key_nam, namlen);
1063 key_name[namlen] = '\0';
1065 type = (SVAL(&nk_hdr->type,0)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
1066 if(type == REG_ROOT_KEY && parent) {
1067 DEBUG(0,("Root key encountered below root level!\n"));
1068 return WERR_GENERAL_FAILURE;
1071 if(type == REG_ROOT_KEY) tmp = reg_key_new_abs(key_name, h, nk_hdr);
1072 else tmp = reg_key_new_rel(key_name, parent, nk_hdr);
1074 DEBUG(2, ("Key name: %s\n", key_name));
1077 * Fish out the class name, it is in UNICODE, while the key name is
1081 if (clsname_len) { /* Just print in Ascii for now */
1082 smb_ucs2_t *clsnamep;
1085 clsnam_off = IVAL(&nk_hdr->clsnam_off,0);
1086 clsnamep = (smb_ucs2_t *)LOCN(regf->base, clsnam_off);
1087 DEBUG(2, ("Class Name Offset: %0X\n", clsnam_off));
1089 tmp->class_name = talloc_strdup_w(h->mem_ctx, clsnamep);
1091 DEBUGADD(2,(" Class Name: %s\n", cls_name));
1096 * Process the owner offset ...
1099 own_off = IVAL(&nk_hdr->own_off,0);
1100 own = (REG_KEY *)LOCN(regf->base, own_off);
1101 DEBUG(2, ("Owner Offset: %0X\n", own_off));
1103 DEBUGADD(2, (" Owner locn: %0X, Our locn: %0X\n",
1104 (unsigned int)own, (unsigned int)nk_hdr));
1107 * We should verify that the owner field is correct ...
1108 * for now, we don't worry ...
1112 * Also handle the SK header ...
1115 sk_off = IVAL(&nk_hdr->sk_off,0);
1116 sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
1117 DEBUG(2, ("SK Offset: %0X\n", sk_off));
1122 tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
1132 * Allocate a new hbin block, set up the header for the block etc
1134 static HBIN_BLK *nt_create_hbin_blk(REG_HANDLE *h, int size)
1136 REGF *regf = h->backend_data;
1140 if (!regf || !size) return NULL;
1142 /* Round size up to multiple of REGF_HDR_BLKSIZ */
1144 size = (size + (REGF_HDR_BLKSIZ - 1)) & ~(REGF_HDR_BLKSIZ - 1);
1146 tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
1147 memset(tmp, 0, sizeof(HBIN_BLK));
1149 tmp->data = malloc(size);
1151 memset(tmp->data, 0, size); /* Make it pristine */
1154 /*FIXMEtmp->file_offset = regf->blk_tail->file_offset + regf->blk_tail->size;*/
1156 tmp->free_space = size - (sizeof(HBIN_HDR) - sizeof(HBIN_SUB_HDR));
1157 tmp->fsp_off = size - tmp->free_space;
1160 * Now, build the header in the data block
1162 hdr = (HBIN_HDR *)tmp->data;
1163 hdr->HBIN_ID = str_to_dword("hbin");
1164 hdr->off_from_first = tmp->file_offset - REGF_HDR_BLKSIZ;
1165 hdr->off_to_next = tmp->size;
1166 hdr->blk_size = tmp->size;
1172 regf->blk_tail->next = tmp;
1173 regf->blk_tail = tmp;
1174 if (!regf->free_space) regf->free_space = tmp;
1180 * Allocate a unit of space ... and return a pointer as function param
1181 * and the block's offset as a side effect
1183 static void *nt_alloc_regf_space(REG_HANDLE *h, int size, unsigned int *off)
1185 REGF *regf = h->backend_data;
1190 if (!regf || !size || !off) return NULL;
1192 SMB_REG_ASSERT(regf->blk_head != NULL);
1195 * round up size to include header and then to 8-byte boundary
1197 size = (size + 4 + 7) & ~7;
1200 * Check if there is space, if none, grab a block
1202 if (!regf->free_space) {
1203 if (!nt_create_hbin_blk(h, REGF_HDR_BLKSIZ))
1208 * Now, chain down the list of blocks looking for free space
1211 for (blk = regf->free_space; blk != NULL; blk = blk->next) {
1212 if (blk->free_space <= size) {
1213 tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
1214 ret = blk->data + blk->fsp_off;
1215 blk->free_space -= size;
1216 blk->fsp_off += size;
1218 /* Insert the header */
1219 ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
1222 * Fix up the free space ptr
1223 * If it is NULL, we fix it up next time
1226 if (!blk->free_space)
1227 regf->free_space = blk->next;
1230 return (((char *)ret)+4);/* The pointer needs to be to the data struct */
1235 * If we got here, we need to add another block, which might be
1236 * larger than one block -- deal with that later
1238 if (nt_create_hbin_blk(h, REGF_HDR_BLKSIZ)) {
1239 blk = regf->free_space;
1240 tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
1241 ret = blk->data + blk->fsp_off;
1242 blk->free_space -= size;
1243 blk->fsp_off += size;
1245 /* Insert the header */
1246 ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
1249 * Fix up the free space ptr
1250 * If it is NULL, we fix it up next time
1253 if (!blk->free_space)
1254 regf->free_space = blk->next;
1257 return (((char *)ret) + 4);/* The pointer needs to be to the data struct */
1264 * Store a SID at the location provided
1266 static int nt_store_SID(REG_HANDLE *regf, DOM_SID *sid, unsigned char *locn)
1269 unsigned char *p = locn;
1271 if (!regf || !sid || !locn) return 0;
1273 *p = sid->sid_rev_num; p++;
1274 *p = sid->num_auths; p++;
1276 for (i=0; i < 6; i++) {
1277 *p = sid->id_auth[i]; p++;
1280 for (i=0; i < sid->num_auths; i++) {
1281 SIVAL(p, 0, sid->sub_auths[i]); p+=4;
1288 static int nt_store_ace(REG_HANDLE *regf, SEC_ACE *ace, unsigned char *locn)
1291 SEC_ACE *reg_ace = (SEC_ACE *)locn;
1294 if (!regf || !ace || !locn) return 0;
1296 reg_ace->type = ace->type;
1297 reg_ace->flags = ace->flags;
1299 /* Deal with the length when we have stored the SID */
1301 p = (unsigned char *)®_ace->info.mask;
1303 SIVAL(p, 0, ace->info.mask); p += 4;
1305 size = nt_store_SID(regf, &ace->trustee, p);
1307 size += 8; /* Size of the fixed header */
1309 p = (unsigned char *)®_ace->size;
1317 * Store an ACL at the location provided
1319 static int nt_store_acl(REG_HANDLE *regf, SEC_ACL *acl, unsigned char *locn) {
1321 unsigned char *p = locn, *s;
1323 if (!regf || !acl || !locn) return 0;
1326 * Now store the header and then the ACEs ...
1329 SSVAL(p, 0, acl->revision);
1331 p += 2; s = p; /* Save this for the size field */
1335 SIVAL(p, 0, acl->num_aces);
1339 for (i = 0; i < acl->num_aces; i++) {
1340 size = nt_store_ace(regf, &acl->ace[i], p);
1350 * Flatten and store the Sec Desc
1351 * Windows lays out the DACL first, but since there is no SACL, it might be
1352 * that first, then the owner, then the group SID. So, we do it that way
1355 static unsigned int nt_store_sec_desc(REG_HANDLE *regf, SEC_DESC *sd, char *locn)
1357 SEC_DESC *rsd = (SEC_DESC *)locn;
1358 unsigned int size = 0, off = 0;
1360 if (!regf || !sd || !locn) return 0;
1363 * Now, fill in the first two fields, then lay out the various fields
1367 rsd->revision = SEC_DESC_REVISION;
1368 rsd->type = SEC_DESC_DACL_PRESENT | SEC_DESC_SELF_RELATIVE;
1370 off = 4 * sizeof(DWORD) + 4;
1373 size = nt_store_acl(regf, sd->sacl, (char *)(locn + off));
1374 rsd->off_sacl = off;
1382 rsd->off_dacl = off;
1383 size = nt_store_acl(regf, sd->dacl, (char *)(locn + off));
1391 /* Now the owner and group SIDs */
1393 if (sd->owner_sid) {
1394 rsd->off_owner_sid = off;
1395 size = nt_store_SID(regf, sd->owner_sid, (char *)(locn + off));
1398 rsd->off_owner_sid = 0;
1404 rsd->off_grp_sid = off;
1405 size = nt_store_SID(regf, sd->grp_sid, (char *)(locn + off));
1408 rsd->off_grp_sid = 0;
1417 * Store the security information
1419 * If it has already been stored, just get its offset from record
1420 * otherwise, store it and record its offset
1422 static unsigned int nt_store_security(REG_HANDLE *regf, KEY_SEC_DESC *sec)
1425 unsigned int sk_off;
1428 if (sec->offset) return sec->offset;
1431 * OK, we don't have this one in the file yet. We must compute the
1432 * size taken by the security descriptor as a self-relative SD, which
1433 * means making one pass over each structure and figuring it out
1436 //FIXME size = sec_desc_size(sec->sec_desc);
1438 /* Allocate that much space */
1440 sk_hdr = nt_alloc_regf_space(regf, size, &sk_off);
1441 sec->sk_hdr = sk_hdr;
1443 if (!sk_hdr) return 0;
1445 /* Now, lay out the sec_desc in the space provided */
1447 sk_hdr->SK_ID = str_to_dword("sk");
1450 * We can't deal with the next and prev offset in the SK_HDRs until the
1451 * whole tree has been stored, then we can go and deal with them
1454 sk_hdr->ref_cnt = sec->ref_cnt;
1455 sk_hdr->rec_size = size; /* Is this correct */
1457 /* Now, lay out the sec_desc */
1459 if (!nt_store_sec_desc(regf, sec->sec_desc, (char *)&sk_hdr->sec_desc))
1469 * Store a KEY in the file ...
1471 * We store this depth first, and defer storing the lf struct until
1472 * all the sub-keys have been stored.
1474 * We store the NK hdr, any SK header, class name, and VK structure, then
1475 * recurse down the LF structures ...
1477 * We return the offset of the NK struct
1478 * FIXME, FIXME, FIXME: Convert to using SIVAL and SSVAL ...
1480 static int nt_store_reg_key(REG_HANDLE *regf, REG_KEY *key)
1483 unsigned int nk_off, sk_off, size;
1485 if (!regf || !key) return 0;
1487 size = sizeof(NK_HDR) + strlen(key->name) - 1;
1488 nk_hdr = nt_alloc_regf_space(regf, size, &nk_off);
1489 if (!nk_hdr) goto error;
1491 key->offset = nk_off; /* We will need this later */
1494 * Now fill in each field etc ...
1497 nk_hdr->NK_ID = str_to_dword("nk");
1498 if (key->type == REG_ROOT_KEY)
1499 nk_hdr->type = 0x2C;
1501 nk_hdr->type = 0x20;
1503 /* FIXME: Fill in the time of last update */
1505 if (key->type != REG_ROOT_KEY)
1506 nk_hdr->own_off = key->owner->offset;
1509 nk_hdr->subk_num = key->sub_keys->key_count;
1512 * Now, process the Sec Desc and then store its offset
1515 sk_off = nt_store_security(regf, key->security);
1516 nk_hdr->sk_off = sk_off;
1519 * Then, store the val list and store its offset
1522 nk_hdr->val_cnt = key->values->val_count;
1523 nk_hdr->val_off = nt_store_val_list(regf, key->values);
1526 nk_hdr->val_off = -1;
1527 nk_hdr->val_cnt = 0;
1531 * Finally, store the subkeys, and their offsets
1540 * Store the registry header ...
1541 * We actually create the registry header block and link it to the chain
1544 static REGF_HDR *nt_get_reg_header(REG_HANDLE *h) {
1545 REGF *regf = h->backend_data;
1546 HBIN_BLK *tmp = NULL;
1548 tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
1550 memset(tmp, 0, sizeof(HBIN_BLK));
1551 tmp->type = REG_OUTBLK_HDR;
1552 tmp->size = REGF_HDR_BLKSIZ;
1553 tmp->data = malloc(REGF_HDR_BLKSIZ);
1554 if (!tmp->data) goto error;
1556 memset(tmp->data, 0, REGF_HDR_BLKSIZ); /* Make it pristine, unlike Windows */
1557 regf->blk_head = regf->blk_tail = tmp;
1559 return (REGF_HDR *)tmp->data;
1566 static WERROR nt_close_registry (REG_HANDLE *h)
1568 REGF *regf = h->backend_data;
1569 if (regf->base) munmap(regf->base, regf->sbuf.st_size);
1571 close(regf->fd); /* Ignore the error :-) */
1576 static WERROR nt_open_registry (REG_HANDLE *h, const char *location, const char *credentials)
1578 REGF *regf = (REGF *)talloc_p(h->mem_ctx, REGF);
1580 unsigned int regf_id, hbin_id;
1583 memset(regf, 0, sizeof(REGF));
1584 regf->owner_sid_str = credentials;
1585 h->backend_data = regf;
1587 DEBUG(5, ("Attempting to load registry file\n"));
1589 /* Get the header */
1591 if ((regf_hdr = nt_get_regf_hdr(h)) == NULL) {
1592 DEBUG(0, ("Unable to get header\n"));
1593 return WERR_GENERAL_FAILURE;
1596 /* Now process that header and start to read the rest in */
1598 if ((regf_id = IVAL(®f_hdr->REGF_ID,0)) != str_to_dword("regf")) {
1599 DEBUG(0, ("Unrecognized NT registry header id: %0X, %s\n",
1600 regf_id, h->location));
1601 return WERR_GENERAL_FAILURE;
1605 * Validate the header ...
1607 if (!valid_regf_hdr(regf_hdr)) {
1608 DEBUG(0, ("Registry file header does not validate: %s\n",
1610 return WERR_GENERAL_FAILURE;
1613 /* Update the last mod date, and then go get the first NK record and on */
1615 TTTONTTIME(regf, IVAL(®f_hdr->tim1,0), IVAL(®f_hdr->tim2,0));
1618 * The hbin hdr seems to be just uninteresting garbage. Check that
1619 * it is there, but that is all.
1622 hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
1624 if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID,0)) != str_to_dword("hbin")) {
1625 DEBUG(0, ("Unrecognized registry hbin hdr ID: %0X, %s\n",
1626 hbin_id, h->location));
1627 return WERR_GENERAL_FAILURE;
1631 * Get a pointer to the first key from the hreg_hdr
1634 DEBUG(2, ("First Key: %0X\n",
1635 IVAL(®f_hdr->first_key, 0)));
1637 regf->first_key = (NK_HDR *)LOCN(regf->base, IVAL(®f_hdr->first_key,0));
1638 DEBUGADD(2, ("First Key Offset: %0X\n",
1639 IVAL(®f_hdr->first_key, 0)));
1641 DEBUGADD(2, ("Data Block Size: %d\n",
1642 IVAL(®f_hdr->dblk_size, 0)));
1644 DEBUGADD(2, ("Offset to next hbin block: %0X\n",
1645 IVAL(&hbin_hdr->off_to_next, 0)));
1647 DEBUGADD(2, ("HBIN block size: %0X\n",
1648 IVAL(&hbin_hdr->blk_size, 0)));
1651 * Unmap the registry file, as we might want to read in another
1655 h->backend_data = regf;
1660 static WERROR nt_get_root_key(REG_HANDLE *h, REG_KEY **key)
1662 return nk_to_key(h, ((REGF *)h->backend_data)->first_key, BLK_SIZE(((REGF *)h->backend_data)->first_key), NULL, key);
1665 static WERROR nt_num_subkeys(REG_KEY *k, int *num)
1667 REGF *regf = k->handle->backend_data;
1670 NK_HDR *nk_hdr = k->backend_data;
1671 lf_off = IVAL(&nk_hdr->lf_off,0);
1672 DEBUG(2, ("SubKey list offset: %0X\n", lf_off));
1677 lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
1679 return lf_num_entries(k->handle, lf_hdr, BLK_SIZE(lf_hdr), num);
1682 static WERROR nt_num_values(REG_KEY *k, int *count)
1684 NK_HDR *nk_hdr = k->backend_data;
1685 *count = IVAL(&nk_hdr->val_cnt,0);
1689 static WERROR nt_value_by_index(REG_KEY *k, int n, REG_VAL **value)
1692 int val_off, vk_off;
1695 REGF *regf = k->handle->backend_data;
1696 NK_HDR *nk_hdr = k->backend_data;
1697 val_count = IVAL(&nk_hdr->val_cnt,0);
1698 val_off = IVAL(&nk_hdr->val_off,0);
1699 vl = (VL_TYPE *)LOCN(regf->base, val_off);
1700 DEBUG(2, ("Val List Offset: %0X\n", val_off));
1701 if(n < 0) return WERR_INVALID_PARAM;
1702 if(n >= val_count) return WERR_NO_MORE_ITEMS;
1704 vk_off = IVAL(&vl[n],0);
1705 vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
1706 return vk_to_val(k, vk_hdr, BLK_SIZE(vk_hdr), value);
1709 static WERROR nt_key_by_index(REG_KEY *k, int n, REG_KEY **subkey)
1711 REGF *regf = k->handle->backend_data;
1713 NK_HDR *nk_hdr = k->backend_data;
1715 lf_off = IVAL(&nk_hdr->lf_off,0);
1716 DEBUG(2, ("SubKey list offset: %0X\n", lf_off));
1719 * No more subkeys if lf_off == -1
1723 lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
1724 return lf_get_entry(k, lf_hdr, BLK_SIZE(lf_hdr), n, subkey);
1727 return WERR_NO_MORE_ITEMS;
1730 static struct registry_ops reg_backend_nt4 = {
1732 .open_registry = nt_open_registry,
1733 .close_registry = nt_close_registry,
1734 .open_root_key = nt_get_root_key,
1735 .num_subkeys = nt_num_subkeys,
1736 .num_values = nt_num_values,
1737 .get_subkey_by_index = nt_key_by_index,
1738 .get_value_by_index = nt_value_by_index,
1749 NTSTATUS reg_nt4_init(void)
1751 return register_backend("registry", ®_backend_nt4);