46057bcd635744fe4f6abc3b8a672a0634916a26
[bbaumbach/samba-autobuild/.git] / source4 / lib / registry / reg_backend_nt4.c
1 /*
2    Samba CIFS implementation
3    Registry backend for REGF files
4    Copyright (C) 2005 Jelmer Vernooij, jelmer@samba.org
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
19  
20 #include "includes.h"
21 #include "registry.h"
22 #include "system/filesys.h"
23 #include "system/time.h"
24 #include "lib/registry/tdr_regf.h"
25 #include "librpc/gen_ndr/ndr_security.h"
26
27 /*
28  * Read HBIN blocks into memory
29  */
30
31 struct regf_data {
32         DATA_BLOB data;
33         struct hbin_block **hbins;
34 };
35
36 static struct hbin_block *hbin_by_offset (const struct regf_data *data, uint32_t offset, uint32_t *rel_offset)
37 {
38         int i;
39
40         for (i = 0; data->hbins[i]; i++) {
41                 if (offset >= data->hbins[i]->offset_from_first && 
42                         offset < data->hbins[i]->offset_from_first+
43                                          data->hbins[i]->offset_to_next) {
44                         if (rel_offset)
45                                 *rel_offset = offset - data->hbins[i]->offset_from_first - 0x20;
46                         return data->hbins[i];
47                 }
48         }
49
50         return NULL;
51 }
52
53 /*
54  * Validate a regf header
55  * For now, do nothing, but we should check the checksum
56  */
57 static uint32_t regf_hdr_checksum(const uint8_t *buffer)
58 {
59         uint32_t checksum = 0, x;
60         int i;
61         
62         for (i = 0; i < 0x01FB; i+= 4) {
63                 x = IVAL(buffer, i);
64                 checksum ^= x;
65         }
66
67         return checksum;
68 }
69
70 static DATA_BLOB hbin_get_data(const struct regf_data *data, uint32_t offset)
71 {
72         DATA_BLOB ret;
73         struct hbin_block *hbin;
74         uint32_t rel_offset;
75         ret.data = NULL;
76         ret.length = 0;
77
78         hbin = hbin_by_offset(data, offset, &rel_offset);
79
80         if (hbin == NULL) {
81                 DEBUG(1, ("Can't find HBIN containing 0x%4x\n", offset));
82                 return ret;
83         }
84
85         ret.length = IVAL(hbin->data, rel_offset);
86         if (ret.length & 0x80000000) {
87                 /* absolute value */
88                 ret.length = (ret.length ^ 0xffffffff) + 1;
89         }
90         ret.length -= 4; /* 4 bytes for the length... */
91         ret.data = hbin->data + 
92                 (offset - hbin->offset_from_first - 0x20) + 4;
93         
94         return ret;
95 }
96
97 /* Allocate some new data */
98 static DATA_BLOB hbin_alloc (struct regf_data *data, uint32_t size, uint32_t *offset)
99 {
100         DATA_BLOB ret;
101         uint32_t rel_offset = -1; /* Relative offset ! */
102         struct hbin_block *hbin = NULL;
103         int i;
104
105         size += 4; /* Need to include uint32 for the length */
106
107         /* Allocate as a multiple of 8 */
108         size = (size + 7) & ~7;
109
110         ret.data = NULL;
111         ret.length = 0;
112
113         if (size == 0)
114                 return ret;
115
116         for (i = 0; (hbin = data->hbins[i]); i++) {
117                 int j;
118                 uint32_t my_size;
119                 for (j = 0; j < hbin->offset_to_next-0x20; j+= my_size) {
120                         my_size = IVAL(hbin->data, j);
121                         uint32_t header = IVAL(hbin->data, j + 4);
122
123                         if (my_size % 8 != 0) {
124                                 DEBUG(0, ("Encountered non-aligned block!\n"));
125                         }
126
127                         if (my_size & 0x80000000) { /* Used... */
128                                 my_size = (my_size ^ 0xffffffff) + 1;
129                         } else if (my_size == size) { /* exact match */
130                                 rel_offset = j;
131                                 break;
132                         } else if (my_size > size) { /* data will remain */
133                                 rel_offset = j;
134                                 SIVAL(hbin->data, rel_offset+size, my_size-size); 
135                                 break;
136                         }
137
138                         if (header == 0xffffffff &&
139                                 hbin->offset_to_next-rel_offset >= size)  {
140                                 rel_offset = j;
141                                 /* Mark new free block size */
142                                 SIVAL(hbin->data, rel_offset+size,hbin->offset_to_next - rel_offset - size - 0x20);
143                                 SIVAL(hbin->data, rel_offset+size+0x4, 0xffffffff);
144                                 break;
145                         }
146
147                         if (header == 0xffffffff)  {
148                                 break;
149                         }
150                 }
151
152                 if (rel_offset != -1)
153                         break;
154         }
155         
156         /* No space available in previous hbins, 
157          * allocate new one */
158         if (data->hbins[i] == NULL) { 
159                 data->hbins = talloc_realloc(data, data->hbins, struct hbin_block *, i+2);
160                 hbin = talloc(data->hbins, struct hbin_block);
161                 data->hbins[i] = hbin;
162                 data->hbins[i+1] = NULL;
163
164                 hbin->HBIN_ID = talloc_strdup(hbin, "hbin");
165                 hbin->offset_from_first = (i == 0?0:data->hbins[i-1]->offset_from_first+data->hbins[i-1]->offset_to_next);
166                 hbin->offset_to_next = 0x1000;
167                 hbin->unknown[0] = 0;
168                 hbin->unknown[0] = 0;
169                 unix_to_nt_time(&hbin->last_change, time(NULL));
170                 hbin->block_size = hbin->offset_to_next;
171                 hbin->data = talloc_zero_array(hbin, uint8_t, hbin->block_size - 0x20);
172
173                 rel_offset = 0x0;
174                 SIVAL(hbin->data, size, hbin->block_size - size - 0x20);
175                 SIVAL(hbin->data, size + 0x4, 0xffffffff);
176         }
177
178         /* Set size and mark as used */
179         SIVAL(hbin->data, rel_offset, size & 0x80000000);
180
181         ret.data = hbin->data + rel_offset + 0x4; /* Skip past length */
182         ret.length = size - 0x4;
183         if (offset)
184                 *offset = hbin->offset_from_first + rel_offset + 0x20;
185
186         return ret;
187 }
188
189 /* Store a data blob. Return the offset at which it was stored */
190 static uint32_t hbin_store (struct regf_data *data, DATA_BLOB blob)
191 {
192         uint32_t ret;
193         DATA_BLOB dest = hbin_alloc(data, blob.length, &ret);
194
195         memcpy(dest.data, blob.data, blob.length);
196
197         return ret;
198 }
199
200
201 /* Free existing data */
202 static void hbin_free (struct regf_data *data, uint32_t offset)
203 {
204         uint32_t size;
205         uint32_t rel_offset;
206         struct hbin_block *hbin = hbin_by_offset(data, offset, &rel_offset);
207
208         if (hbin == NULL)
209                 return;
210         
211         /* Get original size */
212         size = IVAL(hbin->data, rel_offset);
213
214         if (!(size & 0x80000000)) {
215                 DEBUG(1, ("Trying to free already freed block\n"));
216                 return;
217         }
218
219         /* Mark block as free */
220         SIVAL(hbin->data, rel_offset, (size ^ 0xffffffff) + 1);
221 }
222
223 /* Store a data blob data was already stored, but hsa changed in size
224  * Will try to save it at the current location if possible, otherwise 
225  * does a free + store */
226 static uint32_t hbin_store_resize (struct regf_data *data, uint32_t orig_offset, DATA_BLOB blob)
227 {
228         uint32_t rel_offset;
229         struct hbin_block *hbin = hbin_by_offset(data, orig_offset, &rel_offset);
230         uint32_t orig_size;
231         uint32_t needed_size;
232         uint32_t possible_size;
233         int i;
234
235         if (!hbin)
236                 return hbin_store(data, blob);
237
238         /* Get original size */
239         orig_size = IVAL(hbin->data, rel_offset);
240
241         /* Fits into current allocated block */
242         if (orig_size - 4 >= blob.length) {
243                 memcpy(hbin->data + rel_offset + 0x4, blob.data, blob.length);
244                 return orig_offset;
245         }
246
247         needed_size = blob.length + 4; /* Add uint32 containing length */
248         needed_size = (needed_size + 7) & ~7; /* Align */
249
250         possible_size = orig_size;
251
252         /* Check if it can be combined with the next few free records */
253         for (i = rel_offset; 
254                  i < hbin->offset_to_next - 0x20; 
255                  i = rel_offset + possible_size) {
256                 uint32_t header;
257                 if (IVAL(hbin->data, i) & 0x80000000) /* Used */
258                         break;
259
260                 header = IVAL(hbin->data, i + 4);
261                 if (header == 0xffffffff) {
262                         possible_size = hbin->offset_to_next - 0x20 - rel_offset;
263                 } else {
264                         possible_size += IVAL(hbin->data, i);
265                 }
266
267                 if (possible_size >= blob.length) {
268                         SIVAL(hbin->data, rel_offset, possible_size);
269                         memcpy(hbin->data + rel_offset + 0x4, blob.data, blob.length);
270                         return orig_offset;
271                 }
272
273                 if (header == 0xffffffff) 
274                         break;
275         }
276
277         hbin_free(data, orig_offset);
278         return hbin_store(data, blob);
279 }
280
281 static WERROR regf_num_subkeys (struct registry_key *key, uint32_t *count)
282 {
283         struct nk_block *nk = key->backend_data;
284
285         *count = nk->num_subkeys;
286         
287         return WERR_OK;
288 }
289
290 static WERROR regf_num_values (struct registry_key *key, uint32_t *count)
291 {
292         struct nk_block *nk = key->backend_data;
293
294         *count = nk->num_values;
295
296         return WERR_OK;
297 }
298
299 static struct registry_key *regf_get_key (TALLOC_CTX *ctx, struct regf_data *regf, uint32_t offset)
300 {
301         DATA_BLOB data = hbin_get_data(regf, offset);
302         struct tdr_pull *pull;
303         struct registry_key *ret;
304         struct nk_block *nk;
305
306         if (data.data == NULL) {
307                 DEBUG(0, ("Unable to find HBIN data for offset %d\n", offset));
308                 return NULL;
309         }
310
311         ret = talloc_zero(ctx, struct registry_key);
312         pull = talloc_zero(ret, struct tdr_pull);
313         pull->data = data;
314         nk = talloc(ret, struct nk_block);
315
316         if (NT_STATUS_IS_ERR(tdr_pull_nk_block(pull, nk))) {
317                 DEBUG(1, ("Error parsing 'nk' record\n"));
318                 talloc_free(ret);
319                 return NULL;
320         }
321
322         if (strcmp(nk->header, "nk") != 0) {
323                 DEBUG(0, ("Expected nk record, got %s\n", nk->header));
324                 talloc_free(ret);
325                 return NULL;
326         }
327
328         ret->name = talloc_steal(ret, nk->key_name);
329         ret->last_mod = nk->last_change;
330         ret->class_name = NULL; /* FIXME: get somehow using clsname_offset */
331         ret->backend_data = nk;
332
333         return ret;
334 }
335
336 static WERROR regf_get_value (TALLOC_CTX *ctx, struct registry_key *key, int idx, struct registry_value **ret)
337 {
338         struct nk_block *nk = key->backend_data;
339         struct vk_block *vk;
340         struct tdr_pull *pull;
341         uint32_t vk_offset;
342         DATA_BLOB data;
343
344         if (idx >= nk->num_values)
345                 return WERR_NO_MORE_ITEMS;
346
347         data = hbin_get_data(key->hive->backend_data, nk->values_offset);
348         if (!data.data) {
349                 DEBUG(0, ("Unable to find value list\n"));
350                 return WERR_GENERAL_FAILURE;
351         }
352
353         if (data.length < nk->num_values * 4) {
354                 DEBUG(1, ("Value counts mismatch\n"));
355         }
356
357         vk_offset = IVAL(data.data, idx * 4);
358
359         data = hbin_get_data(key->hive->backend_data, vk_offset);
360         if (!data.data) {
361                 DEBUG(0, ("Unable to find value\n"));
362                 return WERR_GENERAL_FAILURE;
363         }
364
365         *ret = talloc_zero(ctx, struct registry_value);
366         if (!(*ret)) 
367                 return WERR_NOMEM;
368
369         vk = talloc(*ret, struct vk_block);
370         if (!vk)
371                 return WERR_NOMEM;
372         
373         pull = talloc_zero(*ret, struct tdr_pull);
374         pull->data = data;
375
376         if (NT_STATUS_IS_ERR(tdr_pull_vk_block(pull, vk))) {
377                 DEBUG(0, ("Error parsing vk block\n"));
378                 return WERR_GENERAL_FAILURE;
379         }
380
381         (*ret)->name = talloc_steal(*ret, vk->data_name);
382         (*ret)->data_type = vk->data_type;
383         if (vk->data_length & 0x80000000) { 
384                 vk->data_length &= ~0x80000000;
385                 (*ret)->data.data = (uint8_t *)&vk->data_offset;
386                 (*ret)->data.length = vk->data_length;
387         } else {
388                 (*ret)->data = hbin_get_data(key->hive->backend_data, vk->data_offset);
389         }
390
391         if ((*ret)->data.length < vk->data_length) {
392                 DEBUG(1, ("Read data less then indicated data length!\n"));
393         }
394         
395         return WERR_OK;
396 }
397
398 static WERROR regf_get_subkey (TALLOC_CTX *ctx, struct registry_key *key, int idx, struct registry_key **ret)
399 {
400         DATA_BLOB data;
401         struct nk_block *nk = key->backend_data;
402         uint32_t key_off;
403
404         if (idx >= nk->num_subkeys)
405                 return WERR_NO_MORE_ITEMS;
406
407         data = hbin_get_data(key->hive->backend_data, nk->subkeys_offset);
408         if (!data.data) {
409                 DEBUG(0, ("Unable to find subkey list\n"));
410                 return WERR_GENERAL_FAILURE;
411         }
412
413         if (!strncmp((char *)data.data, "li", 2)) {
414                 DEBUG(4, ("Subkeys in LI list\n"));
415                 SMB_ASSERT(0);
416         } else if (!strncmp((char *)data.data, "lf", 2)) {
417                 struct lf_block lf;
418                 struct tdr_pull *pull = talloc_zero(ctx, struct tdr_pull);
419
420                 DEBUG(10, ("Subkeys in LF list\n"));
421                 pull->data = data;
422
423                 if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, &lf))) {
424                         DEBUG(0, ("Error parsing LF list\n"));
425                         return WERR_GENERAL_FAILURE;
426                 }
427
428                 if (lf.key_count != nk->num_subkeys) {
429                         DEBUG(0, ("Subkey counts don't match\n"));
430                         return WERR_GENERAL_FAILURE;
431                 }
432
433                 key_off = lf.hr[idx].nk_off;
434                 
435                 talloc_free(pull);
436         } else if (!strncmp((char *)data.data, "ri", 2)) {
437                 DEBUG(4, ("Subkeys in RI list\n"));
438                 SMB_ASSERT(0);
439         } else if (!strncmp((char *)data.data, "lh", 2)) {
440                 DEBUG(4, ("Subkeys in LH list\n"));
441                 SMB_ASSERT(0);
442         } else {
443                 DEBUG(0, ("Unknown type for subkey list (0x%04x): %c%c\n", nk->subkeys_offset, data.data[0], data.data[1]));
444                 return WERR_GENERAL_FAILURE;
445         }
446
447         *ret = regf_get_key (ctx, key->hive->backend_data, key_off);
448
449         return WERR_OK;
450 }
451
452 static WERROR regf_get_sec_desc(TALLOC_CTX *ctx, struct registry_key *key, struct security_descriptor **sd)
453 {
454         struct nk_block *nk = key->backend_data;
455         struct tdr_pull *tdr;
456         struct sk_block sk;
457         DATA_BLOB data;
458
459         data = hbin_get_data(key->hive->backend_data, nk->sk_offset);
460         if (!data.data) {
461                 DEBUG(0, ("Unable to find security descriptor\n"));
462                 return WERR_GENERAL_FAILURE;
463         }
464
465         tdr = talloc_zero(ctx, struct tdr_pull);
466         if (!tdr)
467                 return WERR_NOMEM;
468
469         tdr->data = data;
470
471         if (NT_STATUS_IS_ERR(tdr_pull_sk_block(tdr, &sk))) {
472                 DEBUG(0, ("Error parsing SK block\n"));
473                 return WERR_GENERAL_FAILURE;
474         }
475
476         if (strcmp(sk.header, "sk") != 0) {
477                 DEBUG(0, ("Expected 'sk', got '%s'\n", sk.header));
478                 return WERR_GENERAL_FAILURE;
479         }
480
481         *sd = talloc(ctx, struct security_descriptor);
482         if (!*sd)
483                 return WERR_NOMEM;
484
485         data.data = sk.sec_desc;
486         data.length = sk.rec_size;
487         if (NT_STATUS_IS_ERR(ndr_pull_struct_blob(&data, ctx, *sd, (ndr_pull_flags_fn_t)ndr_pull_security_descriptor))) {
488                 DEBUG(0, ("Error parsing security descriptor\n"));
489                 return WERR_GENERAL_FAILURE;
490         }
491
492         talloc_free(tdr);
493
494         return WERR_OK;
495 }
496
497 static uint32_t lf_add_entry (struct regf_data *regf, uint32_t list_offset, const char *name, uint32_t key_offset)
498 {
499         uint32_t ret;
500         struct lf_block lf;
501         struct tdr_pull *pull = NULL;
502         struct tdr_push *push;
503
504         /* Add to subkeys list */
505         if (list_offset == -1) { /* Need to create subkeys list */
506                 lf.header = "lf";
507                 lf.key_count = 0;
508                 lf.hr = NULL;
509         } else {
510                 DATA_BLOB data;
511                 pull = talloc(regf, struct tdr_pull);
512
513                 data = hbin_get_data(regf, list_offset);
514                 if (!data.data) {
515                         DEBUG(0, ("Can't get subkeys list\n"));
516                         talloc_free(pull);
517                         return -1;
518                 }
519
520                 if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, &lf))) {
521                         DEBUG(0, ("Unable to parse lf list\n"));
522                         talloc_free(pull);
523                         return -1;
524                 }
525         }
526
527         lf.hr = talloc_realloc(pull, lf.hr, struct hash_record, lf.key_count+1);
528         lf.hr[lf.key_count].nk_off = key_offset;
529         lf.hr[lf.key_count].hash = name;
530
531         push = talloc_zero(regf, struct tdr_push);
532
533         if (NT_STATUS_IS_ERR(tdr_push_lf_block(push, &lf))) {
534                 DEBUG(0, ("Error storing lf block\n"));
535                 return -1;
536         }
537
538         ret = hbin_store_resize (regf, list_offset, push->data);
539
540         talloc_free(push);
541         talloc_free(pull);
542         
543         return ret;
544 }
545
546 static WERROR regf_add_key (TALLOC_CTX *ctx, struct registry_key *parent, const char *name, uint32_t access_mask, struct security_descriptor *sec_desc, struct registry_key **ret)
547 {
548         struct nk_block *parent_nk = parent->backend_data, nk;
549         DATA_BLOB data;
550         struct tdr_push *push;
551         uint32_t offset;
552
553         nk.header = "nk";
554         nk.type = REG_SUB_KEY;
555         unix_to_nt_time(&nk.last_change, time(NULL));
556         nk.uk1 = 0;
557         nk.parent_offset = 0; /* FIXME */
558         nk.num_subkeys = 0;
559         nk.uk2 = 0;
560         nk.subkeys_offset = -1;
561         nk.unknown_offset = -1;
562         nk.num_values = 0;
563         nk.sk_offset = 0;
564         memset(nk.unk3, 0, 5);
565         nk.clsname_offset = -1;
566         nk.clsname_length = 0;
567         nk.key_name = name;
568         
569         push = talloc_zero(ctx, struct tdr_push);
570         if (NT_STATUS_IS_ERR(tdr_push_nk_block(push, &nk))) {
571                 DEBUG(0, ("Error storing 'nk' block\n"));
572                 return WERR_GENERAL_FAILURE;
573         }
574
575         offset = hbin_store(parent->hive->backend_data, push->data);
576
577         parent_nk->subkeys_offset = lf_add_entry(parent->hive->backend_data, parent_nk->subkeys_offset, name, nk.parent_offset);
578
579         parent_nk->num_subkeys++;
580
581         ZERO_STRUCTP(push);
582
583         if (NT_STATUS_IS_ERR(tdr_push_nk_block(push, parent_nk))) {
584                 DEBUG(0, ("Error storing parent 'nk' block\n"));
585                 return WERR_GENERAL_FAILURE;
586         }
587
588         data = hbin_get_data(parent->hive->backend_data, nk.parent_offset);
589         memcpy(data.data, push->data.data, push->data.length);
590
591         talloc_free(push);
592
593         /* FIXME: Set sec desc ! */
594
595         *ret = regf_get_key(ctx, parent->hive->backend_data, offset);
596         return WERR_OK;
597 }
598
599 static WERROR nt_open_hive (struct registry_hive *h, struct registry_key **key)
600 {
601         struct regf_data *regf;
602         struct regf_hdr *regf_hdr;
603         struct tdr_pull *pull;
604         int i;
605
606         regf = (struct regf_data *)talloc_zero(h, struct regf_data);
607         h->backend_data = regf;
608
609         DEBUG(5, ("Attempting to load registry file\n"));
610
611         /* Get the header */
612
613         regf->data.data = (uint8_t *)file_load(h->location, &regf->data.length, regf);
614         if (regf->data.data == NULL) {
615                 DEBUG(0,("Could not load file: %s, %s\n", h->location,
616                                  strerror(errno)));
617                 return WERR_GENERAL_FAILURE;
618         }
619
620         pull = talloc_zero(regf, struct tdr_pull);
621         if (!pull)
622                 return WERR_NOMEM;
623
624         pull->data = regf->data;
625
626         regf_hdr = talloc(regf, struct regf_hdr);
627         if (NT_STATUS_IS_ERR(tdr_pull_regf_hdr(pull, regf_hdr))) {
628                 return WERR_GENERAL_FAILURE;
629         }
630
631         if (strcmp(regf_hdr->REGF_ID, "regf") != 0) {
632                 DEBUG(0, ("Unrecognized NT registry header id: %s, %s\n",
633                                   regf_hdr->REGF_ID, h->location));
634         }
635
636         DEBUG(1, ("Registry '%s' read. Version %d.%d.%d.%d\n", 
637                           regf_hdr->description, regf_hdr->version.major,
638                           regf_hdr->version.minor, regf_hdr->version.release,
639                           regf_hdr->version.build));
640
641         /*
642          * Validate the header ...
643          */
644         if (regf_hdr_checksum(regf->data.data) != regf_hdr->chksum) {
645                 DEBUG(0, ("Registry file checksum error: %s: %d,%d\n",
646                                   h->location, regf_hdr->chksum, regf_hdr_checksum(regf->data.data)));
647                 return WERR_GENERAL_FAILURE;
648         }
649
650         pull->offset = 0x1000;
651
652         i = 0;
653         /* Read in all hbin blocks */
654         regf->hbins = talloc_array(regf, struct hbin_block *, 1);
655         regf->hbins[0] = NULL;
656
657         while (pull->offset < pull->data.length) {
658                 struct hbin_block *hbin = talloc(regf->hbins, struct hbin_block);
659
660                 if (NT_STATUS_IS_ERR(tdr_pull_hbin_block(pull, hbin))) {
661                         DEBUG(0, ("[%d] Error parsing HBIN block\n", i));
662                         return WERR_FOOBAR;
663                 }
664
665                 if (strcmp(hbin->HBIN_ID, "hbin") != 0) {
666                         DEBUG(0, ("[%d] Expected 'hbin', got '%s'\n", i, hbin->HBIN_ID));
667                         return WERR_FOOBAR;
668                 }
669
670                 regf->hbins[i] = hbin;
671                 i++;
672                 regf->hbins = talloc_realloc(regf, regf->hbins, struct hbin_block *, i+2);
673                 regf->hbins[i] = NULL;
674         } 
675
676         DEBUG(1, ("%d HBIN blocks read\n", i));
677
678         *key = regf_get_key(h, regf, 0x20);
679
680         return WERR_OK;
681 }
682
683 static struct hive_operations reg_backend_nt4 = {
684         .name = "nt4",
685         .open_hive = nt_open_hive,
686         .num_subkeys = regf_num_subkeys,
687         .num_values = regf_num_values,
688         .get_subkey_by_index = regf_get_subkey,
689         .get_value_by_index = regf_get_value,
690         .key_get_sec_desc = regf_get_sec_desc,
691         .add_key = regf_add_key,
692 };
693
694 NTSTATUS registry_nt4_init(void)
695 {
696         return registry_register(&reg_backend_nt4);
697 }