The code is a bit of a mess right now.
[Samba/bjacke.git] / source3 / utils / editreg.c
blob9d155a9a11a910725f9f322776f0e27d23174243
1 /*
2 Samba Unix/Linux SMB client utility editreg.c
3 Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19 /*************************************************************************
21 A utility to edit a Windows NT/2K etc registry file.
23 Many of the ideas in here come from other people and software.
24 I first looked in Wine in misc/registry.c and was also influenced by
25 http://www.wednesday.demon.co.uk/dosreg.html
27 Which seems to contain comments from someone else. I reproduce them here
28 incase the site above disappears. It actually comes from
29 http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt.
31 The goal here is to read the registry into memory, manipulate it, and then
32 write it out if it was changed by any actions of the user.
34 The windows NT registry has 2 different blocks, where one can occur many
35 times...
37 the "regf"-Block
38 ================
40 "regf" is obviosly the abbreviation for "Registry file". "regf" is the
41 signature of the header-block which is always 4kb in size, although only
42 the first 64 bytes seem to be used and a checksum is calculated over
43 the first 0x200 bytes only!
45 Offset Size Contents
46 0x00000000 D-Word ID: ASCII-"regf" = 0x66676572
47 0x00000004 D-Word ???? //see struct REGF
48 0x00000008 D-Word ???? Always the same value as at 0x00000004
49 0x0000000C Q-Word last modify date in WinNT date-format
50 0x00000014 D-Word 1
51 0x00000018 D-Word 3
52 0x0000001C D-Word 0
53 0x00000020 D-Word 1
54 0x00000024 D-Word Offset of 1st key record
55 0x00000028 D-Word Size of the data-blocks (Filesize-4kb)
56 0x0000002C D-Word 1
57 0x000001FC D-Word Sum of all D-Words from 0x00000000 to
58 0x000001FB //XOR of all words. Nigel
60 I have analyzed more registry files (from multiple machines running
61 NT 4.0 german version) and could not find an explanation for the values
62 marked with ???? the rest of the first 4kb page is not important...
64 the "hbin"-Block
65 ================
66 I don't know what "hbin" stands for, but this block is always a multiple
67 of 4kb in size.
69 Inside these hbin-blocks the different records are placed. The memory-
70 management looks like a C-compiler heap management to me...
72 hbin-Header
73 ===========
74 Offset Size Contents
75 0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268
76 0x0004 D-Word Offset from the 1st hbin-Block
77 0x0008 D-Word Offset to the next hbin-Block
78 0x001C D-Word Block-size
80 The values in 0x0008 and 0x001C should be the same, so I don't know
81 if they are correct or swapped...
83 From offset 0x0020 inside a hbin-block data is stored with the following
84 format:
86 Offset Size Contents
87 0x0000 D-Word Data-block size //this size must be a
88 multiple of 8. Nigel
89 0x0004 ???? Data
91 If the size field is negative (bit 31 set), the corresponding block
92 is free and has a size of -blocksize!
94 That does not seem to be true. All block lengths seem to be negative! (Richard Sharpe)
96 The data is stored as one record per block. Block size is a multiple
97 of 4 and the last block reaches the next hbin-block, leaving no room.
99 Records in the hbin-blocks
100 ==========================
102 nk-Record
104 The nk-record can be treated as a kombination of tree-record and
105 key-record of the win 95 registry.
107 lf-Record
109 The lf-record is the counterpart to the RGKN-record (the
110 hash-function)
112 vk-Record
114 The vk-record consists information to a single value.
116 sk-Record
118 sk (? Security Key ?) is the ACL of the registry.
120 Value-Lists
122 The value-lists contain information about which values are inside a
123 sub-key and don't have a header.
125 Datas
127 The datas of the registry are (like the value-list) stored without a
128 header.
130 All offset-values are relative to the first hbin-block and point to the
131 block-size field of the record-entry. to get the file offset, you have to add
132 the header size (4kb) and the size field (4 bytes)...
134 the nk-Record
135 =============
136 Offset Size Contents
137 0x0000 Word ID: ASCII-"nk" = 0x6B6E
138 0x0002 Word for the root-key: 0x2C, otherwise 0x20 //key symbolic links 0x10. Nigel
139 0x0004 Q-Word write-date/time in windows nt notation
140 0x0010 D-Word Offset of Owner/Parent key
141 0x0014 D-Word number of sub-Keys
142 0x001C D-Word Offset of the sub-key lf-Records
143 0x0024 D-Word number of values
144 0x0028 D-Word Offset of the Value-List
145 0x002C D-Word Offset of the sk-Record
147 0x0030 D-Word Offset of the Class-Name //see NK structure for the use of these fields. Nigel
148 0x0044 D-Word Unused (data-trash) //some kind of run time index. Does not appear to be important. Nigel
149 0x0048 Word name-length
150 0x004A Word class-name length
151 0x004C ???? key-name
153 the Value-List
154 ==============
155 Offset Size Contents
156 0x0000 D-Word Offset 1st Value
157 0x0004 D-Word Offset 2nd Value
158 0x???? D-Word Offset nth Value
160 To determine the number of values, you have to look at the owner-nk-record!
162 Der vk-Record
163 =============
164 Offset Size Contents
165 0x0000 Word ID: ASCII-"vk" = 0x6B76
166 0x0002 Word name length
167 0x0004 D-Word length of the data //if top bit is set when offset contains data. Nigel
168 0x0008 D-Word Offset of Data
169 0x000C D-Word Type of value
170 0x0010 Word Flag
171 0x0012 Word Unused (data-trash)
172 0x0014 ???? Name
174 If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
176 If the data-size is lower 5, the data-offset value is used to store the data itself!
178 The data-types
179 ==============
180 Wert Beteutung
181 0x0001 RegSZ: character string (in UNICODE!)
182 0x0002 ExpandSZ: string with "%var%" expanding (UNICODE!)
183 0x0003 RegBin: raw-binary value
184 0x0004 RegDWord: Dword
185 0x0007 RegMultiSZ: multiple strings, seperated with 0
186 (UNICODE!)
188 The "lf"-record
189 ===============
190 Offset Size Contents
191 0x0000 Word ID: ASCII-"lf" = 0x666C
192 0x0002 Word number of keys
193 0x0004 ???? Hash-Records
195 Hash-Record
196 ===========
197 Offset Size Contents
198 0x0000 D-Word Offset of corresponding "nk"-Record
199 0x0004 D-Word ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
201 Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the
202 key-name you have to change the hash-value too!
204 //These hashrecords must be sorted low to high within the lf record. Nigel.
206 The "sk"-block
207 ==============
208 (due to the complexity of the SAM-info, not clear jet)
209 (This is just a security descriptor in the data. R Sharpe.)
212 Offset Size Contents
213 0x0000 Word ID: ASCII-"sk" = 0x6B73
214 0x0002 Word Unused
215 0x0004 D-Word Offset of previous "sk"-Record
216 0x0008 D-Word Offset of next "sk"-Record
217 0x000C D-Word usage-counter
218 0x0010 D-Word Size of "sk"-record in bytes
219 ???? //standard self
220 relative security desciptor. Nigel
221 ???? ???? Security and auditing settings...
222 ????
224 The usage counter counts the number of references to this
225 "sk"-record. You can use one "sk"-record for the entire registry!
227 Windows nt date/time format
228 ===========================
229 The time-format is a 64-bit integer which is incremented every
230 0,0000001 seconds by 1 (I don't know how accurate it realy is!)
231 It starts with 0 at the 1st of january 1601 0:00! All values are
232 stored in GMT time! The time-zone is important to get the real
233 time!
235 Common values for win95 and win-nt
236 ==================================
237 Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
238 If a value has no name (length=0, flag(bit 0)=0), it is treated as the
239 "Default" entry...
240 If a value has no data (length=0), it is displayed as empty.
242 simplyfied win-3.?? registry:
243 =============================
245 +-----------+
246 | next rec. |---+ +----->+------------+
247 | first sub | | | | Usage cnt. |
248 | name | | +-->+------------+ | | length |
249 | value | | | | next rec. | | | text |------->+-------+
250 +-----------+ | | | name rec. |--+ +------------+ | xxxxx |
251 +------------+ | | value rec. |-------->+------------+ +-------+
252 v | +------------+ | Usage cnt. |
253 +-----------+ | | length |
254 | next rec. | | | text |------->+-------+
255 | first sub |------+ +------------+ | xxxxx |
256 | name | +-------+
257 | value |
258 +-----------+
260 Greatly simplyfied structure of the nt-registry:
261 ================================================
263 +---------------------------------------------------------------+
266 +---------+ +---------->+-----------+ +----->+---------+ |
267 | "nk" | | | lf-rec. | | | nk-rec. | |
268 | ID | | | # of keys | | | parent |---+
269 | Date | | | 1st key |--+ | .... |
270 | parent | | +-----------+ +---------+
271 | suk-keys|-----+
272 | values |--------------------->+----------+
273 | SK-rec. |---------------+ | 1. value |--> +----------+
274 | class |--+ | +----------+ | vk-rec. |
275 +---------+ | | | .... |
276 v | | data |--> +-------+
277 +------------+ | +----------+ | xxxxx |
278 | Class name | | +-------+
279 +------------+ |
281 +---------+ +---------+
282 +----->| next sk |--->| Next sk |--+
283 | +---| prev sk |<---| prev sk | |
284 | | | .... | | ... | |
285 | | +---------+ +---------+ |
286 | | ^ |
287 | | | |
288 | +--------------------+ |
289 +----------------------------------+
291 ---------------------------------------------------------------------------
293 Hope this helps.... (Although it was "fun" for me to uncover this things,
294 it took me several sleepless nights ;)
296 B.D.
298 *************************************************************************/
300 #include <stdio.h>
301 #include <stdlib.h>
302 #include <errno.h>
303 #include <assert.h>
304 #include <sys/types.h>
305 #include <sys/stat.h>
306 #include <unistd.h>
307 #include <sys/mman.h>
308 #include <string.h>
309 #include <fcntl.h>
311 #define False 0
312 #define True 1
313 #define REG_KEY_LIST_SIZE 10
316 * Structures for dealing with the on-disk format of the registry
319 #define IVAL(buf) ((unsigned int) \
320 (unsigned int)*((unsigned char *)(buf)+3)<<24| \
321 (unsigned int)*((unsigned char *)(buf)+2)<<16| \
322 (unsigned int)*((unsigned char *)(buf)+1)<<8| \
323 (unsigned int)*((unsigned char *)(buf)+0))
325 #define SVAL(buf) ((unsigned short) \
326 (unsigned short)*((unsigned char *)(buf)+1)<<8| \
327 (unsigned short)*((unsigned char *)(buf)+0))
329 #define CVAL(buf) ((unsigned char)*((unsigned char *)(buf)))
331 static int verbose = 0;
332 static int print_security = 0;
335 * These definitions are for the in-memory registry structure.
336 * It is a tree structure that mimics what you see with tools like regedit
340 * DateTime struct for Windows
343 typedef struct date_time_s {
344 unsigned int low, high;
345 } NTTIME;
348 * Definition of a Key. It has a name, classname, date/time last modified,
349 * sub-keys, values, and a security descriptor
352 #define REG_ROOT_KEY 1
353 #define REG_SUB_KEY 2
354 #define REG_SYM_LINK 3
356 typedef struct key_sec_desc_s KEY_SEC_DESC;
358 typedef struct reg_key_s {
359 char *name; /* Name of the key */
360 char *class_name;
361 int type; /* One of REG_ROOT_KEY or REG_SUB_KEY */
362 NTTIME last_mod; /* Time last modified */
363 struct reg_key_s *owner;
364 struct key_list_s *sub_keys;
365 struct val_list_s *values;
366 KEY_SEC_DESC *security;
367 } REG_KEY;
370 * The KEY_LIST struct lists sub-keys.
373 typedef struct key_list_s {
374 int key_count;
375 int max_keys;
376 REG_KEY *keys[1];
377 } KEY_LIST;
379 typedef struct val_key_s {
380 char *name;
381 int has_name;
382 int data_type;
383 int data_len;
384 void *data_blk; /* Might want a separate block */
385 } VAL_KEY;
387 typedef struct val_list_s {
388 int val_count;
389 VAL_KEY *vals[1];
390 } VAL_LIST;
392 #ifndef MAXSUBAUTHS
393 #define MAXSUBAUTHS 15
394 #endif
396 typedef struct dom_sid_s {
397 unsigned char ver, auths;
398 unsigned char auth[6];
399 unsigned int sub_auths[MAXSUBAUTHS];
400 } DOM_SID;
402 typedef struct ace_struct_s {
403 unsigned char type, flags;
404 unsigned int perms; /* Perhaps a better def is in order */
405 DOM_SID *trustee;
406 } ACE;
408 typedef struct acl_struct_s {
409 unsigned short rev, refcnt;
410 unsigned short num_aces;
411 ACE *aces[1];
412 } ACL;
414 typedef struct sec_desc_s {
415 unsigned int rev, type;
416 DOM_SID *owner, *group;
417 ACL *sacl, *dacl;
418 } SEC_DESC;
420 #define SEC_DESC_NON 0
421 #define SEC_DESC_RES 1
422 #define SEC_DESC_OCU 2
424 struct key_sec_desc_s {
425 struct key_sec_desc_s *prev, *next;
426 int ref_cnt;
427 int state;
428 SEC_DESC *sec_desc;
432 * All of the structures below actually have a four-byte lenght before them
433 * which always seems to be negative. The following macro retrieves that
434 * size as an integer
437 #define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
439 typedef unsigned int DWORD;
440 typedef unsigned short WORD;
442 #define REG_REGF_ID 0x66676572
444 typedef struct regf_block {
445 DWORD REGF_ID; /* regf */
446 DWORD uk1;
447 DWORD uk2;
448 DWORD tim1, tim2;
449 DWORD uk3; /* 1 */
450 DWORD uk4; /* 3 */
451 DWORD uk5; /* 0 */
452 DWORD uk6; /* 1 */
453 DWORD first_key; /* offset */
454 unsigned int dblk_size;
455 DWORD uk7[116]; /* 1 */
456 DWORD chksum;
457 } REGF_HDR;
459 typedef struct hbin_sub_struct {
460 DWORD dblocksize;
461 char data[1];
462 } HBIN_SUB_HDR;
464 #define REG_HBIN_ID 0x6E696268
466 typedef struct hbin_struct {
467 DWORD HBIN_ID; /* hbin */
468 DWORD next_off;
469 DWORD prev_off;
470 DWORD uk1;
471 DWORD uk2;
472 DWORD uk3;
473 DWORD uk4;
474 DWORD blk_size;
475 HBIN_SUB_HDR hbin_sub_hdr;
476 } HBIN_HDR;
478 #define REG_NK_ID 0x6B6E
480 typedef struct nk_struct {
481 WORD NK_ID;
482 WORD type;
483 DWORD t1, t2;
484 DWORD uk1;
485 DWORD own_off;
486 DWORD subk_num;
487 DWORD uk2;
488 DWORD lf_off;
489 DWORD uk3;
490 DWORD val_cnt;
491 DWORD val_off;
492 DWORD sk_off;
493 DWORD clsnam_off;
494 DWORD unk4[4];
495 DWORD unk5;
496 WORD nam_len;
497 WORD clsnam_len;
498 char key_nam[1]; /* Actual length determined by nam_len */
499 } NK_HDR;
501 #define REG_SK_ID 0x6B73
503 typedef struct sk_struct {
504 WORD SK_ID;
505 WORD uk1;
506 DWORD prev_off;
507 DWORD next_off;
508 DWORD ref_cnt;
509 DWORD rec_size;
510 char sec_desc[1];
511 } SK_HDR;
513 typedef struct ace_struct {
514 unsigned char type;
515 unsigned char flags;
516 unsigned short length;
517 unsigned int perms;
518 DOM_SID trustee;
519 } REG_ACE;
521 typedef struct acl_struct {
522 WORD rev;
523 WORD size;
524 DWORD num_aces;
525 REG_ACE *aces; /* One or more ACEs */
526 } REG_ACL;
528 typedef struct sec_desc_rec {
529 WORD rev;
530 WORD type;
531 DWORD owner_off;
532 DWORD group_off;
533 DWORD sacl_off;
534 DWORD dacl_off;
535 } REG_SEC_DESC;
537 typedef struct hash_struct {
538 DWORD nk_off;
539 char hash[4];
540 } HASH_REC;
542 #define REG_LF_ID 0x666C
544 typedef struct lf_struct {
545 WORD LF_ID;
546 WORD key_count;
547 struct hash_struct hr[1]; /* Array of hash records, depending on key_count */
548 } LF_HDR;
550 typedef DWORD VL_TYPE[1]; /* Value list is an array of vk rec offsets */
552 #define REG_VK_ID 0x6B76
554 typedef struct vk_struct {
555 WORD VK_ID;
556 WORD nam_len;
557 DWORD dat_len; /* If top-bit set, offset contains the data */
558 DWORD dat_off;
559 DWORD dat_type;
560 WORD flag; /* =1, has name, else no name (=Default). */
561 WORD unk1;
562 char dat_name[1]; /* Name starts here ... */
563 } VK_HDR;
565 #define REG_TYPE_NONE 0
566 #define REG_TYPE_REGSZ 1
567 #define REG_TYPE_EXPANDSZ 2
568 #define REG_TYPE_BIN 3
569 #define REG_TYPE_DWORD 4
570 #define REG_TYPE_MULTISZ 7
572 typedef struct _val_str {
573 unsigned int val;
574 const char * str;
575 } VAL_STR;
577 /* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
578 typedef struct sk_map_s {
579 int sk_off;
580 KEY_SEC_DESC *key_sec_desc;
581 } SK_MAP;
583 struct regf_struct_s {
584 int reg_type;
585 char *regfile_name, *outfile_name;
586 int fd;
587 struct stat sbuf;
588 char *base;
589 int modified;
590 NTTIME last_mod_time;
591 REG_KEY *root; /* Root of the tree for this file */
592 int sk_count, sk_map_size;
593 SK_MAP *sk_map;
596 typedef struct regf_struct_s REGF;
599 * An API for accessing/creating/destroying items above
603 * Iterate over the keys, depth first, calling a function for each key
604 * and indicating if it is terminal or non-terminal and if it has values.
606 * In addition, for each value in the list, call a value list function
609 typedef int (*key_print_f)(const char *path, char *key_name, char *class_name,
610 int root, int terminal, int values);
612 typedef int (*val_print_f)(const char *path, char *val_name, int val_type,
613 int data_len, void *data_blk, int terminal,
614 int first, int last);
616 typedef int (*sec_print_f)(SEC_DESC *sec_desc);
618 int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
619 key_print_f key_print, sec_print_f sec_print,
620 val_print_f val_print);
622 int nt_val_list_iterator(REGF *regf, VAL_LIST *val_list, int bf, char *path,
623 int terminal, val_print_f val_print)
625 int i;
627 if (!val_list) return 1;
629 if (!val_print) return 1;
631 for (i=0; i<val_list->val_count; i++) {
632 if (!val_print(path, val_list->vals[i]->name, val_list->vals[i]->data_type,
633 val_list->vals[i]->data_len, val_list->vals[i]->data_blk,
634 terminal,
635 (i == 0),
636 (i == val_list->val_count))) {
638 return 0;
643 return 1;
646 int nt_key_list_iterator(REGF *regf, KEY_LIST *key_list, int bf,
647 const char *path,
648 key_print_f key_print, sec_print_f sec_print,
649 val_print_f val_print)
651 int i;
653 if (!key_list) return 1;
655 for (i=0; i< key_list->key_count; i++) {
656 if (!nt_key_iterator(regf, key_list->keys[i], bf, path, key_print,
657 sec_print, val_print)) {
658 return 0;
661 return 1;
664 int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
665 key_print_f key_print, sec_print_f sec_print,
666 val_print_f val_print)
668 int path_len = strlen(path);
669 char *new_path;
671 if (!regf || !key_tree)
672 return -1;
674 /* List the key first, then the values, then the sub-keys */
676 if (key_print) {
678 if (!(*key_print)(path, key_tree->name,
679 key_tree->class_name,
680 (key_tree->type == REG_ROOT_KEY),
681 (key_tree->sub_keys == NULL),
682 (key_tree->values?(key_tree->values->val_count):0)))
683 return 0;
687 * If we have a security print routine, call it
688 * If the security print routine returns false, stop.
690 if (sec_print) {
691 if (key_tree->security && !(*sec_print)(key_tree->security->sec_desc))
692 return 0;
695 new_path = (char *)malloc(path_len + 1 + strlen(key_tree->name) + 1);
696 if (!new_path) return 0; /* Errors? */
697 new_path[0] = '\0';
698 strcat(new_path, path);
699 strcat(new_path, key_tree->name);
700 strcat(new_path, "\\");
703 * Now, iterate through the values in the val_list
706 if (key_tree->values &&
707 !nt_val_list_iterator(regf, key_tree->values, bf, new_path,
708 (key_tree->values!=NULL),
709 val_print)) {
711 free(new_path);
712 return 0;
716 * Now, iterate through the keys in the key list
719 if (key_tree->sub_keys &&
720 !nt_key_list_iterator(regf, key_tree->sub_keys, bf, new_path, key_print,
721 sec_print, val_print)) {
722 free(new_path);
723 return 0;
726 free(new_path);
727 return 1;
730 REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key);
733 * Find key by name in a list ...
734 * Take the first component and search for that in the list
736 REG_KEY *nt_find_key_in_list_by_name(KEY_LIST *list, char *key)
738 int i;
739 REG_KEY *res = NULL;
741 if (!list || !key || !*key) return NULL;
743 for (i = 0; i<= list->key_count; i++)
744 if ((res = nt_find_key_by_name(list->keys[i], key)))
745 return res;
747 return NULL;
751 * Find key by name in a tree ... We will assume absolute names here, but we
752 * need the root of the tree ...
754 REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key)
756 char *lname = NULL, *c1, *c2;
757 REG_KEY *tmp;
759 if (!tree || !key || !*key) return NULL;
761 lname = strdup(key);
762 if (!lname) return NULL;
765 * Make sure that the first component is correct ...
767 c1 = lname;
768 c2 = strchr(c1, '\\');
769 if (c2) { /* Split here ... */
770 *c2 = 0;
771 c2++;
773 if (strcmp(c1, tree->name) != 0) goto error;
775 if (c2) {
776 tmp = nt_find_key_in_list_by_name(tree->sub_keys, c2);
777 free(lname);
778 return tmp;
780 else {
781 if (lname) free(lname);
782 return tree;
784 error:
785 if (lname) free(lname);
786 return NULL;
789 /* Make, delete keys */
791 int nt_delete_val_key(VAL_KEY *val_key)
794 if (val_key) {
795 if (val_key->data_blk) free(val_key->data_blk);
796 free(val_key);
798 return 1;
801 int nt_delete_val_list(VAL_LIST *vl)
803 int i;
805 if (vl) {
806 for (i=0; i<vl->val_count; i++)
807 nt_delete_val_key(vl->vals[i]);
808 free(vl);
810 return 1;
813 int nt_delete_reg_key(REG_KEY *key, int delete_name);
814 int nt_delete_key_list(KEY_LIST *key_list, int delete_name)
816 int i;
818 if (key_list) {
819 for (i=0; i<key_list->key_count; i++)
820 nt_delete_reg_key(key_list->keys[i], False);
821 free(key_list);
823 return 1;
827 * Find the key, and if it exists, delete it ...
829 int nt_delete_key_by_name(REGF *regf, char *name)
831 REG_KEY *key;
833 if (!name || !*name) return 0;
835 key = nt_find_key_by_name(regf->root, name);
837 if (key) {
838 if (key == regf->root) regf->root = NULL;
839 return nt_delete_reg_key(key, True);
842 return 0;
846 int nt_delete_sid(DOM_SID *sid)
849 if (sid) free(sid);
850 return 1;
854 int nt_delete_ace(ACE *ace)
857 if (ace) {
858 nt_delete_sid(ace->trustee);
859 free(ace);
861 return 1;
865 int nt_delete_acl(ACL *acl)
868 if (acl) {
869 int i;
871 for (i=0; i<acl->num_aces; i++)
872 nt_delete_ace(acl->aces[i]);
874 free(acl);
876 return 1;
879 int nt_delete_sec_desc(SEC_DESC *sec_desc)
882 if (sec_desc) {
884 nt_delete_sid(sec_desc->owner);
885 nt_delete_sid(sec_desc->group);
886 nt_delete_acl(sec_desc->sacl);
887 nt_delete_acl(sec_desc->dacl);
888 free(sec_desc);
891 return 1;
894 int nt_delete_key_sec_desc(KEY_SEC_DESC *key_sec_desc)
897 if (key_sec_desc) {
898 key_sec_desc->ref_cnt--;
899 if (key_sec_desc->ref_cnt<=0) {
901 * There should always be a next and prev, even if they point to us
903 key_sec_desc->next->prev = key_sec_desc->prev;
904 key_sec_desc->prev->next = key_sec_desc->next;
905 nt_delete_sec_desc(key_sec_desc->sec_desc);
908 return 1;
911 int nt_delete_reg_key(REG_KEY *key, int delete_name)
914 if (key) {
915 if (key->name) free(key->name);
916 if (key->class_name) free(key->class_name);
919 * We will delete the owner if we are not the root and told to ...
922 if (key->owner && key->owner->sub_keys && delete_name) {
923 REG_KEY *own;
924 KEY_LIST *kl;
925 int i;
926 /* Find our owner, look in keylist for us and shuffle up */
927 /* Perhaps should be a function */
929 own = key->owner;
930 kl = own->sub_keys;
932 for (i=0; i < kl->key_count && kl->keys[i] != key ; i++) {
933 /* Just find the entry ... */
936 if (i == kl->key_count) {
937 fprintf(stderr, "Bad data structure. Key not found in key list of owner\n");
939 else {
940 int j;
943 * Shuffle up. Works for the last one also
945 for (j = i + 1; j < kl->key_count; j++) {
946 kl->keys[j - 1] = kl->keys[j];
949 kl->key_count--;
953 if (key->sub_keys) nt_delete_key_list(key->sub_keys, False);
954 if (key->values) nt_delete_val_list(key->values);
955 if (key->security) nt_delete_key_sec_desc(key->security);
956 free(key);
958 return 1;
962 * Add a value to the key specified ...
964 REG_KEY *nt_add_reg_value(REG_KEY *key, char *name, int type, char *value)
967 return NULL;
971 * Delete a value. Should perhaps return the value ...
973 REG_KEY *nt_delete_reg_valye(REG_KEY *key, char *name)
976 return NULL;
980 * Add a key to the tree ... We walk down the components matching until
981 * we don't find any. There must be a match on the first component ...
982 * We return the key structure for the final component as that is
983 * often where we want to add values ...
987 * Create a 1 component key name and set its parent to parent
989 REG_KEY *nt_create_reg_key1(char *name, REG_KEY *parent)
991 REG_KEY *tmp;
993 if (!name || !*name) return NULL; /* A key's name cannot be empty */
995 /* There should not be more than one component */
996 if (strchr(name, '\\')) return NULL;
998 if (!(tmp = (REG_KEY *)malloc(sizeof(REG_KEY)))) return NULL;
1000 bzero(tmp, sizeof(REG_KEY));
1002 if (!(tmp->name = strdup(name))) goto error;
1004 error:
1005 if (tmp) free(tmp);
1006 return NULL;
1010 * Convert a string of the form S-1-5-x[-y-z-r] to a SID
1012 int strng_to_sid(DOM_SID **sid, char *sid_str)
1014 int i = 0, auth;
1015 char *lstr;
1017 *sid = (DOM_SID *)malloc(sizeof(DOM_SID));
1018 if (!*sid) return 0;
1020 bzero(*sid, sizeof(DOM_SID));
1022 if (strncmp(sid_str, "S-1-5", 5)) {
1023 fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str);
1024 return 0;
1027 /* We only allow strings of form S-1-5... */
1029 (*sid)->ver = 1;
1030 (*sid)->auth[5] = 5;
1032 lstr = sid_str + 5;
1034 while (1) {
1035 if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) {
1036 if (i < 1) {
1037 fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i);
1038 return 0;
1040 (*sid)->auths=i;
1041 return 1;
1044 (*sid)->sub_auths[i] = auth;
1045 i++;
1046 lstr = strchr(lstr + 1, '-');
1049 return 1;
1053 * We will implement inheritence that is based on what the parent's SEC_DESC
1054 * says, but the Owner and Group SIDs can be overwridden from the command line
1055 * and additional ACEs can be applied from the command line etc.
1057 KEY_SEC_DESC *nt_inherit_security(REG_KEY *key)
1060 if (!key) return NULL;
1061 return key->security;
1065 * Create an initial security descriptor and init other structures, if needed
1066 * We assume that the initial security stuff is empty ...
1068 KEY_SEC_DESC *nt_create_init_sec(REGF *regf)
1070 KEY_SEC_DESC *tsec = NULL;
1073 return tsec;
1076 REG_KEY *nt_add_reg_subkey(REGF *regf, REG_KEY *key, char *name, int create);
1079 * Add a sub-key
1081 REG_KEY *nt_add_reg_key_list(REGF *regf, REG_KEY *key, char * name, int create)
1083 int i;
1084 REG_KEY *ret = NULL, *tmp = NULL;
1085 KEY_LIST *list;
1086 char *lname, *c1, *c2;
1088 if (!key || !name || !*name) return NULL;
1090 list = key->sub_keys;
1091 if (!list) { /* Create an empty list */
1093 list = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (REG_KEY_LIST_SIZE - 1) * sizeof(REG_KEY *));
1094 list->key_count = 0;
1095 list->max_keys = REG_KEY_LIST_SIZE;
1099 for (i = 0; i < list->key_count; i++) {
1100 if ((ret = nt_add_reg_subkey(regf, key, name, create)))
1101 return ret;
1105 * If we reach here we could not find the the first component
1106 * so create it ...
1109 lname = strdup(name);
1110 if (!lname) return NULL;
1112 c1 = lname;
1113 c2 = strchr(c1, '\\');
1114 if (c2) { /* Split here ... */
1115 *c2 = 0;
1116 c2++;
1119 if (list->key_count < list->max_keys){
1120 list->key_count++;
1122 else { /* Create more space in the list ... */
1123 if (!(list = (KEY_LIST *)realloc(list, sizeof(KEY_LIST) +
1124 (list->max_keys + REG_KEY_LIST_SIZE - 1)
1125 * sizeof(REG_KEY *))));
1126 goto error;
1128 list->max_keys += REG_KEY_LIST_SIZE;
1129 list->key_count++;
1133 * add the new key at the new slot
1134 * FIXME: Sort the list someday
1138 * We want to create the key, and then do the rest
1141 tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
1143 bzero(tmp, sizeof(REG_KEY));
1145 tmp->name = strdup(c1);
1146 if (!tmp->name) goto error;
1147 tmp->owner = key;
1148 tmp->type = REG_SUB_KEY;
1150 * Next, pull security from the parent, but override by with
1151 * anything passed in on the command line
1153 tmp->security = nt_inherit_security(key);
1155 list->keys[list->key_count - 1] = tmp;
1157 if (c2) {
1158 ret = nt_add_reg_subkey(regf, key, c2, True);
1161 if (lname) free(lname);
1163 return ret;
1165 error:
1166 if (tmp) free(tmp);
1167 if (lname) free(lname);
1168 return NULL;
1171 REG_KEY *nt_add_reg_subkey(REGF *regf, REG_KEY *key, char *name, int create)
1174 return NULL;
1178 * This routine only adds a key from the root down.
1179 * It calls helper functions to handle sub-key lists and sub-keys
1181 REG_KEY *nt_add_reg_key(REGF *regf, char *name, int create)
1183 char *lname = NULL, *c1, *c2;
1184 REG_KEY * tmp = NULL, *key = NULL;
1187 * Look until we hit the first component that does not exist, and
1188 * then add from there. However, if the first component does not
1189 * match and the path we are given is the root, then it must match
1191 if (!regf || !name || !*name) return NULL;
1193 lname = strdup(name);
1194 if (!lname) return NULL;
1196 c1 = lname;
1197 c2 = strchr(c1, '\\');
1198 if (c2) { /* Split here ... */
1199 *c2 = 0;
1200 c2++;
1204 * If the root does not exist, create it and make it equal to the
1205 * first component ...
1208 if (!regf->root) {
1210 tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
1211 if (!tmp) goto error;
1212 bzero(tmp, sizeof(REG_KEY));
1213 tmp->name = strdup(c1);
1214 if (!tmp->name) goto error;
1215 tmp->security = nt_create_init_sec(regf);
1216 if (!tmp->security) goto error;
1217 regf->root = tmp;
1220 else {
1222 * If we don't match, then we have to return error ...
1223 * If we do match on this component, check the next one in the
1224 * list, and if not found, add it ... short circuit, add all the
1225 * way down
1228 if (strcmp(c1, key->name) != 0)
1229 goto error;
1232 tmp = nt_add_reg_key_list(regf, key, c2, True);
1233 free(lname);
1234 return tmp;
1236 error:
1237 if (tmp) free(tmp);
1238 if (lname) free(lname);
1239 return NULL;
1243 * Load and unload a registry file.
1245 * Load, loads it into memory as a tree, while unload sealizes/flattens it
1249 * Get the starting record for NT Registry file
1253 * Where we keep all the regf stuff for one registry.
1254 * This is the structure that we use to tie the in memory tree etc
1255 * together. By keeping separate structs, we can operate on different
1256 * registries at the same time.
1257 * Currently, the SK_MAP is an array of mapping structure.
1258 * Since we only need this on input and output, we fill in the structure
1259 * as we go on input. On output, we know how many SK items we have, so
1260 * we can allocate the structure as we need to.
1261 * If you add stuff here that is dynamically allocated, add the
1262 * appropriate free statements below.
1265 #define REGF_REGTYPE_NONE 0
1266 #define REGF_REGTYPE_NT 1
1267 #define REGF_REGTYPE_W9X 2
1269 #define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
1270 (r)->last_mod_time.high = (t2);
1272 #define REGF_HDR_BLKSIZ 0x1000
1274 #define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4)
1275 #define LOCN(base, f) ((base) + OFF(f))
1277 const VAL_STR reg_type_names[] = {
1278 { REG_TYPE_REGSZ, "REG_SZ" },
1279 { REG_TYPE_EXPANDSZ, "REG_EXPAND_SZ" },
1280 { REG_TYPE_BIN, "REG_BIN" },
1281 { REG_TYPE_DWORD, "REG_DWORD" },
1282 { REG_TYPE_MULTISZ, "REG_MULTI_SZ" },
1283 { 0, NULL },
1286 const char *val_to_str(unsigned int val, const VAL_STR *val_array)
1288 int i = 0;
1290 if (!val_array) return NULL;
1292 while (val_array[i].val && val_array[i].str) {
1294 if (val_array[i].val == val) return val_array[i].str;
1295 i++;
1299 return NULL;
1304 * Convert from UniCode to Ascii ... Does not take into account other lang
1305 * Restrict by ascii_max if > 0
1307 int uni_to_ascii(unsigned char *uni, unsigned char *ascii, int ascii_max,
1308 int uni_max)
1310 int i = 0;
1312 while (i < ascii_max && !(!uni[i*2] && !uni[i*2+1])) {
1313 if (uni_max > 0 && (i*2) >= uni_max) break;
1314 ascii[i] = uni[i*2];
1315 i++;
1319 ascii[i] = '\0';
1321 return i;
1325 * Convert a data value to a string for display
1327 int data_to_ascii(unsigned char *datap, int len, int type, char *ascii, int ascii_max)
1329 unsigned char *asciip;
1330 int i;
1332 switch (type) {
1333 case REG_TYPE_REGSZ:
1334 if (verbose) fprintf(stderr, "Len: %d\n", len);
1335 return uni_to_ascii(datap, ascii, len, ascii_max);
1336 break;
1338 case REG_TYPE_EXPANDSZ:
1339 return uni_to_ascii(datap, ascii, len, ascii_max);
1340 break;
1342 case REG_TYPE_BIN:
1343 asciip = ascii;
1344 for (i=0; (i<len)&&(i+1)*3<ascii_max; i++) {
1345 int str_rem = ascii_max - ((int)asciip - (int)ascii);
1346 asciip += snprintf(asciip, str_rem, "%02x", *(unsigned char *)(datap+i));
1347 if (i < len && str_rem > 0)
1348 *asciip = ' '; asciip++;
1350 *asciip = '\0';
1351 return ((int)asciip - (int)ascii);
1352 break;
1354 case REG_TYPE_DWORD:
1355 if (*(int *)datap == 0)
1356 return snprintf(ascii, ascii_max, "0");
1357 else
1358 return snprintf(ascii, ascii_max, "0x%x", *(int *)datap);
1359 break;
1361 case REG_TYPE_MULTISZ:
1363 break;
1365 default:
1366 return 0;
1367 break;
1370 return len;
1374 REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent);
1376 int nt_set_regf_input_file(REGF *regf, char *filename)
1378 return ((regf->regfile_name = strdup(filename)) != NULL);
1381 int nt_set_regf_output_file(REGF *regf, char *filename)
1383 return ((regf->outfile_name = strdup(filename)) != NULL);
1386 /* Create a regf structure and init it */
1388 REGF *nt_create_regf(void)
1390 REGF *tmp = (REGF *)malloc(sizeof(REGF));
1391 if (!tmp) return tmp;
1392 bzero(tmp, sizeof(REGF));
1393 return tmp;
1396 /* Free all the bits and pieces ... Assumes regf was malloc'd */
1397 /* If you add stuff to REGF, add the relevant free bits here */
1398 int nt_free_regf(REGF *regf)
1400 if (!regf) return 0;
1402 if (regf->regfile_name) free(regf->regfile_name);
1403 if (regf->outfile_name) free(regf->outfile_name);
1405 nt_delete_reg_key(regf->root, False); /* Free the tree */
1406 free(regf->sk_map);
1407 regf->sk_count = regf->sk_map_size = 0;
1409 free(regf);
1411 return 1;
1414 /* Get the header of the registry. Return a pointer to the structure
1415 * If the mmap'd area has not been allocated, then mmap the input file
1417 REGF_HDR *nt_get_regf_hdr(REGF *regf)
1419 if (!regf)
1420 return NULL; /* What about errors */
1422 if (!regf->regfile_name)
1423 return NULL; /* What about errors */
1425 if (!regf->base) { /* Try to mmap etc the file */
1427 if ((regf->fd = open(regf->regfile_name, O_RDONLY, 0000)) <0) {
1428 return NULL; /* What about errors? */
1431 if (fstat(regf->fd, &regf->sbuf) < 0) {
1432 return NULL;
1435 regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
1437 if ((int)regf->base == 1) {
1438 fprintf(stderr, "Could not mmap file: %s, %s\n", regf->regfile_name,
1439 strerror(errno));
1440 return NULL;
1445 * At this point, regf->base != NULL, and we should be able to read the
1446 * header
1449 assert(regf->base != NULL);
1451 return (REGF_HDR *)regf->base;
1455 * Validate a regf header
1456 * For now, do nothing, but we should check the checksum
1458 int valid_regf_hdr(REGF_HDR *regf_hdr)
1460 if (!regf_hdr) return 0;
1462 return 1;
1466 * Process an SK header ...
1467 * Every time we see a new one, add it to the map. Otherwise, just look it up.
1468 * We will do a simple linear search for the moment, since many KEYs have the
1469 * same security descriptor.
1470 * We allocate the map in increments of 10 entries.
1474 * Create a new entry in the map, and increase the size of the map if needed
1477 SK_MAP *alloc_sk_map_entry(REGF *regf, KEY_SEC_DESC *tmp, int sk_off)
1479 if (!regf->sk_map) { /* Allocate a block of 10 */
1480 regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
1481 if (!regf->sk_map) {
1482 free(tmp);
1483 return NULL;
1485 regf->sk_map_size = 10;
1486 regf->sk_count = 1;
1487 (regf->sk_map)[0].sk_off = sk_off;
1488 (regf->sk_map)[0].key_sec_desc = tmp;
1490 else { /* Simply allocate a new slot, unless we have to expand the list */
1491 int ndx = regf->sk_count;
1492 if (regf->sk_count >= regf->sk_map_size) {
1493 regf->sk_map = (SK_MAP *)realloc(regf->sk_map,
1494 (regf->sk_map_size + 10)*sizeof(SK_MAP));
1495 if (!regf->sk_map) {
1496 free(tmp);
1497 return NULL;
1500 * ndx already points at the first entry of the new block
1502 regf->sk_map_size += 10;
1504 (regf->sk_map)[ndx].sk_off = sk_off;
1505 (regf->sk_map)[ndx].key_sec_desc = tmp;
1506 regf->sk_count++;
1508 return regf->sk_map;
1512 * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
1513 * found
1516 KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
1518 int i;
1520 if (!sk_map) return NULL;
1522 for (i = 0; i < count; i++) {
1524 if (sk_map[i].sk_off == sk_off)
1525 return sk_map[i].key_sec_desc;
1529 return NULL;
1534 * Allocate a KEY_SEC_DESC if we can't find one in the map
1537 KEY_SEC_DESC *lookup_create_sec_key(REGF *regf, SK_MAP *sk_map, int sk_off)
1539 KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
1541 if (tmp) {
1542 return tmp;
1544 else { /* Allocate a new one */
1545 tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
1546 if (!tmp) {
1547 return NULL;
1549 tmp->state = SEC_DESC_RES;
1550 if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
1551 return NULL;
1553 return tmp;
1558 * Allocate storage and duplicate a SID
1559 * We could allocate the SID to be only the size needed, but I am too lazy.
1561 DOM_SID *dup_sid(DOM_SID *sid)
1563 DOM_SID *tmp = (DOM_SID *)malloc(sizeof(DOM_SID));
1564 int i;
1566 if (!tmp) return NULL;
1567 tmp->ver = sid->ver;
1568 tmp->auths = sid->auths;
1569 for (i=0; i<6; i++) {
1570 tmp->auth[i] = sid->auth[i];
1572 for (i=0; i<tmp->auths&&i<MAXSUBAUTHS; i++) {
1573 tmp->sub_auths[i] = sid->sub_auths[i];
1575 return tmp;
1579 * Allocate space for an ACE and duplicate the registry encoded one passed in
1581 ACE *dup_ace(REG_ACE *ace)
1583 ACE *tmp = NULL;
1585 tmp = (ACE *)malloc(sizeof(ACE));
1587 if (!tmp) return NULL;
1589 tmp->type = CVAL(&ace->type);
1590 tmp->flags = CVAL(&ace->flags);
1591 tmp->perms = IVAL(&ace->perms);
1592 tmp->trustee = dup_sid(&ace->trustee);
1593 return tmp;
1597 * Allocate space for an ACL and duplicate the registry encoded one passed in
1599 ACL *dup_acl(REG_ACL *acl)
1601 ACL *tmp = NULL;
1602 REG_ACE* ace;
1603 int i, num_aces;
1605 num_aces = IVAL(&acl->num_aces);
1607 tmp = (ACL *)malloc(sizeof(ACL) + (num_aces - 1)*sizeof(ACE *));
1608 if (!tmp) return NULL;
1610 tmp->num_aces = num_aces;
1611 tmp->refcnt = 1;
1612 tmp->rev = SVAL(&acl->rev);
1613 ace = (REG_ACE *)&acl->aces;
1614 for (i=0; i<num_aces; i++) {
1615 tmp->aces[i] = dup_ace(ace);
1616 ace = (REG_ACE *)((char *)ace + SVAL(&ace->length));
1617 /* XXX: FIXME, should handle malloc errors */
1620 return tmp;
1623 SEC_DESC *process_sec_desc(REGF *regf, REG_SEC_DESC *sec_desc)
1625 SEC_DESC *tmp = NULL;
1627 tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
1629 if (!tmp) {
1630 return NULL;
1633 tmp->rev = SVAL(&sec_desc->rev);
1634 tmp->type = SVAL(&sec_desc->type);
1635 tmp->owner = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->owner_off)));
1636 if (!tmp->owner) {
1637 free(tmp);
1638 return NULL;
1640 tmp->group = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->group_off)));
1641 if (!tmp->group) {
1642 free(tmp);
1643 return NULL;
1646 /* Now pick up the SACL and DACL */
1648 if (sec_desc->sacl_off)
1649 tmp->sacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->sacl_off)));
1650 else
1651 tmp->sacl = NULL;
1653 if (sec_desc->dacl_off)
1654 tmp->dacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->dacl_off)));
1655 else
1656 tmp->dacl = NULL;
1658 return tmp;
1661 KEY_SEC_DESC *process_sk(REGF *regf, SK_HDR *sk_hdr, int sk_off, int size)
1663 KEY_SEC_DESC *tmp = NULL;
1664 int sk_next_off, sk_prev_off, sk_size;
1665 REG_SEC_DESC *sec_desc;
1667 if (!sk_hdr) return NULL;
1669 if (SVAL(&sk_hdr->SK_ID) != REG_SK_ID) {
1670 fprintf(stderr, "Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
1671 regf->regfile_name);
1672 return NULL;
1675 if (-size < (sk_size = IVAL(&sk_hdr->rec_size))) {
1676 fprintf(stderr, "Incorrect SK record size: %d vs %d. %s\n",
1677 -size, sk_size, regf->regfile_name);
1678 return NULL;
1682 * Now, we need to look up the SK Record in the map, and return it
1683 * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
1684 * use that
1687 if (regf->sk_map &&
1688 ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
1689 && (tmp->state == SEC_DESC_OCU)) {
1690 tmp->ref_cnt++;
1691 return tmp;
1694 /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
1696 assert(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
1699 * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
1700 * new KEY_SEC_DESC to the mapping structure, since the offset supplied is
1701 * the actual offset of structure. The same offset will be used by
1702 * all future references to this structure
1703 * We could put all this unpleasantness in a function.
1706 if (!tmp) {
1707 tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
1708 if (!tmp) return NULL;
1709 bzero(tmp, sizeof(KEY_SEC_DESC));
1712 * Allocate an entry in the SK_MAP ...
1713 * We don't need to free tmp, because that is done for us if the
1714 * sm_map entry can't be expanded when we need more space in the map.
1717 if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
1718 return NULL;
1722 tmp->ref_cnt++;
1723 tmp->state = SEC_DESC_OCU;
1726 * Now, process the actual sec desc and plug the values in
1729 sec_desc = (REG_SEC_DESC *)&sk_hdr->sec_desc[0];
1730 tmp->sec_desc = process_sec_desc(regf, sec_desc);
1733 * Now forward and back links. Here we allocate an entry in the sk_map
1734 * if it does not exist, and mark it reserved
1737 sk_prev_off = IVAL(&sk_hdr->prev_off);
1738 tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
1739 assert(tmp->prev != NULL);
1740 sk_next_off = IVAL(&sk_hdr->next_off);
1741 tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
1742 assert(tmp->next != NULL);
1744 return tmp;
1748 * Process a VK header and return a value
1750 VAL_KEY *process_vk(REGF *regf, VK_HDR *vk_hdr, int size)
1752 char val_name[1024];
1753 int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
1754 const char *val_type;
1755 VAL_KEY *tmp = NULL;
1757 if (!vk_hdr) return NULL;
1759 if ((vk_id = SVAL(&vk_hdr->VK_ID)) != REG_VK_ID) {
1760 fprintf(stderr, "Unrecognized VK header ID: %0X, block: %0X, %s\n",
1761 vk_id, (int)vk_hdr, regf->regfile_name);
1762 return NULL;
1765 nam_len = SVAL(&vk_hdr->nam_len);
1766 val_name[nam_len] = '\0';
1767 flag = SVAL(&vk_hdr->flag);
1768 dat_type = IVAL(&vk_hdr->dat_type);
1769 dat_len = IVAL(&vk_hdr->dat_len); /* If top bit, offset contains data */
1770 dat_off = IVAL(&vk_hdr->dat_off);
1772 tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
1773 if (!tmp) {
1774 goto error;
1776 bzero(tmp, sizeof(VAL_KEY));
1777 tmp->has_name = flag;
1778 tmp->data_type = dat_type;
1780 if (flag & 0x01) {
1781 strncpy(val_name, vk_hdr->dat_name, nam_len);
1782 tmp->name = strdup(val_name);
1783 if (!tmp->name) {
1784 goto error;
1787 else
1788 strncpy(val_name, "<No Name>", 10);
1791 * Allocate space and copy the data as a BLOB
1794 if (dat_len) {
1796 char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
1798 if (!dtmp) {
1799 goto error;
1802 tmp->data_blk = dtmp;
1804 if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
1805 char *dat_ptr = LOCN(regf->base, dat_off);
1806 bcopy(dat_ptr, dtmp, dat_len);
1808 else { /* The data is in the offset */
1809 dat_len = dat_len & 0x7FFFFFFF;
1810 bcopy(&dat_off, dtmp, dat_len);
1813 tmp->data_len = dat_len;
1816 val_type = val_to_str(dat_type, reg_type_names);
1819 * We need to save the data area as well
1822 if (verbose) fprintf(stdout, " %s : %s : \n", val_name, val_type);
1824 return tmp;
1826 error:
1827 /* XXX: FIXME, free the partially allocated struct */
1828 return NULL;
1833 * Process a VL Header and return a list of values
1835 VAL_LIST *process_vl(REGF *regf, VL_TYPE vl, int count, int size)
1837 int i, vk_off;
1838 VK_HDR *vk_hdr;
1839 VAL_LIST *tmp = NULL;
1841 if (!vl) return NULL;
1843 if (-size < (count+1)*sizeof(int)){
1844 fprintf(stderr, "Error in VL header format. Size less than space required. %d\n", -size);
1845 return NULL;
1848 tmp = (VAL_LIST *)malloc(sizeof(VAL_LIST) + (count - 1) * sizeof(VAL_KEY *));
1849 if (!tmp) {
1850 goto error;
1853 for (i=0; i<count; i++) {
1854 vk_off = IVAL(&vl[i]);
1855 vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
1856 tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr));
1857 if (!tmp->vals[i]){
1858 goto error;
1862 tmp->val_count = count;
1864 return tmp;
1866 error:
1867 /* XXX: FIXME, free the partially allocated structure */
1868 return NULL;
1872 * Process an LF Header and return a list of sub-keys
1874 KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size, REG_KEY *parent)
1876 int count, i, nk_off;
1877 unsigned int lf_id;
1878 KEY_LIST *tmp;
1880 if (!lf_hdr) return NULL;
1882 if ((lf_id = SVAL(&lf_hdr->LF_ID)) != REG_LF_ID) {
1883 fprintf(stderr, "Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
1884 lf_id, (int)lf_hdr, regf->regfile_name);
1885 return NULL;
1888 assert(size < 0);
1890 count = SVAL(&lf_hdr->key_count);
1892 if (count <= 0) return NULL;
1894 /* Now, we should allocate a KEY_LIST struct and fill it in ... */
1896 tmp = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (count - 1) * sizeof(REG_KEY *));
1897 if (!tmp) {
1898 goto error;
1901 tmp->key_count = count;
1902 tmp->max_keys = count;
1904 for (i=0; i<count; i++) {
1905 NK_HDR *nk_hdr;
1907 nk_off = IVAL(&lf_hdr->hr[i].nk_off);
1908 nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
1909 tmp->keys[i] = nt_get_key_tree(regf, nk_hdr, BLK_SIZE(nk_hdr), parent);
1910 if (!tmp->keys[i]) {
1911 goto error;
1915 return tmp;
1917 error:
1918 if (tmp) nt_delete_key_list(tmp, False);
1919 return NULL;
1923 * This routine is passed an NK_HDR pointer and retrieves the entire tree
1924 * from there down. It returns a REG_KEY *.
1926 REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent)
1928 REG_KEY *tmp = NULL, *own;
1929 int name_len, clsname_len, lf_off, val_off, val_count, sk_off, own_off;
1930 unsigned int nk_id;
1931 LF_HDR *lf_hdr;
1932 VL_TYPE *vl;
1933 SK_HDR *sk_hdr;
1934 char key_name[1024], cls_name[1024];
1936 if (!nk_hdr) return NULL;
1938 if ((nk_id = SVAL(&nk_hdr->NK_ID)) != REG_NK_ID) {
1939 fprintf(stderr, "Unrecognized NK Header format: %08X, Block: %0X. %s\n",
1940 nk_id, (int)nk_hdr, regf->regfile_name);
1941 return NULL;
1944 assert(size < 0);
1946 name_len = SVAL(&nk_hdr->nam_len);
1947 clsname_len = SVAL(&nk_hdr->clsnam_len);
1950 * The value of -size should be ge
1951 * (sizeof(NK_HDR) - 1 + name_len)
1952 * The -1 accounts for the fact that we included the first byte of
1953 * the name in the structure. clsname_len is the length of the thing
1954 * pointed to by clsnam_off
1957 if (-size < (sizeof(NK_HDR) - 1 + name_len)) {
1958 fprintf(stderr, "Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr);
1959 fprintf(stderr, "Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
1960 sizeof(NK_HDR), name_len, clsname_len);
1961 /*return NULL;*/
1964 if (verbose) fprintf(stdout, "NK HDR: Name len: %d, class name len: %d\n",
1965 name_len, clsname_len);
1967 /* Fish out the key name and process the LF list */
1969 assert(name_len < sizeof(key_name));
1971 /* Allocate the key struct now */
1972 tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
1973 if (!tmp) return tmp;
1974 bzero(tmp, sizeof(REG_KEY));
1976 tmp->type = (SVAL(&nk_hdr->type)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
1978 strncpy(key_name, nk_hdr->key_nam, name_len);
1979 key_name[name_len] = '\0';
1981 if (verbose) fprintf(stdout, "Key name: %s\n", key_name);
1983 tmp->name = strdup(key_name);
1984 if (!tmp->name) {
1985 goto error;
1989 * Fish out the class name, it is in UNICODE, while the key name is
1990 * ASCII :-)
1993 if (clsname_len) { /* Just print in Ascii for now */
1994 char *clsnamep;
1995 int clsnam_off;
1997 clsnam_off = IVAL(&nk_hdr->clsnam_off);
1998 clsnamep = LOCN(regf->base, clsnam_off);
2000 bzero(cls_name, clsname_len);
2001 uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len);
2004 * I am keeping class name as an ascii string for the moment.
2005 * That means it needs to be converted on output.
2006 * It will also piss off people who need Unicode/UTF-8 strings. Sorry.
2007 * XXX: FIXME
2010 tmp->class_name = strdup(cls_name);
2011 if (!tmp->class_name) {
2012 goto error;
2015 if (verbose) fprintf(stdout, " Class Name: %s\n", cls_name);
2020 * Process the owner offset ...
2023 own_off = IVAL(&nk_hdr->own_off);
2024 own = (REG_KEY *)LOCN(regf->base, own_off);
2026 if (verbose) fprintf(stdout, " Owner offset: %0X, Our Offset: %0X\n",
2027 (unsigned int)own, (unsigned int)nk_hdr);
2030 * We should verify that the owner field is correct ...
2031 * for now, we don't worry ...
2034 tmp->owner = parent;
2037 * If there are any values, process them here
2040 val_count = IVAL(&nk_hdr->val_cnt);
2042 if (val_count) {
2044 val_off = IVAL(&nk_hdr->val_off);
2045 vl = (VL_TYPE *)LOCN(regf->base, val_off);
2047 tmp->values = process_vl(regf, *vl, val_count, BLK_SIZE(vl));
2048 if (!tmp->values) {
2049 goto error;
2055 * Also handle the SK header ...
2058 sk_off = IVAL(&nk_hdr->sk_off);
2059 sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
2061 if (sk_off != -1) {
2063 tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
2067 lf_off = IVAL(&nk_hdr->lf_off);
2070 * No more subkeys if lf_off == -1
2073 if (lf_off != -1) {
2075 lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
2077 tmp->sub_keys = process_lf(regf, lf_hdr, BLK_SIZE(lf_hdr), tmp);
2078 if (!tmp->sub_keys){
2079 goto error;
2084 return tmp;
2086 error:
2087 if (tmp) nt_delete_reg_key(tmp, False);
2088 return NULL;
2091 int nt_load_registry(REGF *regf)
2093 REGF_HDR *regf_hdr;
2094 unsigned int regf_id, hbin_id;
2095 HBIN_HDR *hbin_hdr;
2096 NK_HDR *first_key;
2098 /* Get the header */
2100 if ((regf_hdr = nt_get_regf_hdr(regf)) == NULL) {
2101 return -1;
2104 /* Now process that header and start to read the rest in */
2106 if ((regf_id = IVAL(&regf_hdr->REGF_ID)) != REG_REGF_ID) {
2107 fprintf(stderr, "Unrecognized NT registry header id: %0X, %s\n",
2108 regf_id, regf->regfile_name);
2109 return -1;
2113 * Validate the header ...
2115 if (!valid_regf_hdr(regf_hdr)) {
2116 fprintf(stderr, "Registry file header does not validate: %s\n",
2117 regf->regfile_name);
2118 return -1;
2121 /* Update the last mod date, and then go get the first NK record and on */
2123 TTTONTTIME(regf, IVAL(&regf_hdr->tim1), IVAL(&regf_hdr->tim2));
2126 * The hbin hdr seems to be just uninteresting garbage. Check that
2127 * it is there, but that is all.
2130 hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
2132 if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID)) != REG_HBIN_ID) {
2133 fprintf(stderr, "Unrecognized registry hbin hdr ID: %0X, %s\n",
2134 hbin_id, regf->regfile_name);
2135 return -1;
2139 * Get a pointer to the first key from the hreg_hdr
2142 first_key = (NK_HDR *)LOCN(regf->base, IVAL(&regf_hdr->first_key));
2145 * Now, get the registry tree by processing that NK recursively
2148 regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key), NULL);
2150 assert(regf->root != NULL);
2153 * Unmap the registry file, as we might want to read in another
2154 * tree etc.
2157 if (regf->base) munmap(regf->base, regf->sbuf.st_size);
2158 regf->base = NULL;
2159 close(regf->fd); /* Ignore the error :-) */
2161 return 1;
2165 * Story the registry in the output file
2167 int nt_store_registry(REGF *regf)
2170 return 1;
2174 * Routines to parse a REGEDIT4 file
2176 * The file consists of:
2178 * REGEDIT4
2179 * \[[-]key-path\]\n
2180 * <value-spec>*
2182 * Format:
2183 * [cmd:]name=type:value
2185 * cmd = a|d|c|add|delete|change|as|ds|cs
2187 * There can be more than one key-path and value-spec.
2189 * Since we want to support more than one type of file format, we
2190 * construct a command-file structure that keeps info about the command file
2193 #define FMT_UNREC -1
2194 #define FMT_REGEDIT4 0
2195 #define FMT_EDITREG1_1 1
2197 #define FMT_STRING_REGEDIT4 "REGEDIT4"
2198 #define FMT_STRING_EDITREG1_0 "EDITREG1.0"
2200 #define CMD_NONE 0
2201 #define CMD_ADD_KEY 1
2202 #define CMD_DEL_KEY 2
2204 #define CMD_KEY 1
2205 #define CMD_VAL 2
2207 typedef struct val_spec_list {
2208 struct val_spec_list *next;
2209 char *name;
2210 int type;
2211 char *val; /* Kept as a char string, really? */
2212 } VAL_SPEC_LIST;
2214 typedef struct command_s {
2215 int cmd;
2216 char *key;
2217 int val_count;
2218 VAL_SPEC_LIST *val_spec_list, *val_spec_last;
2219 } CMD;
2221 typedef struct cmd_line {
2222 int len, line_len;
2223 char *line;
2224 } CMD_LINE;
2227 * Some routines to handle lines of info in the command files
2229 void skip_to_eol(int fd)
2231 int rc;
2232 char ch = 0;
2234 while ((rc = read(fd, &ch, 1)) == 1) {
2235 if (ch == 0x0A) return;
2237 if (rc < 0) {
2238 fprintf(stderr, "Could not read file descriptor: %d, %s\n",
2239 fd, strerror(errno));
2240 exit(1);
2244 void free_cmd(CMD *cmd)
2246 if (!cmd) return;
2248 while (cmd->val_spec_list) {
2249 VAL_SPEC_LIST *tmp;
2251 tmp = cmd->val_spec_list;
2252 cmd->val_spec_list = tmp->next;
2253 free(tmp);
2256 free(cmd);
2260 void free_cmd_line(CMD_LINE *cmd_line)
2262 if (cmd_line) {
2263 if (cmd_line->line) free(cmd_line->line);
2264 free(cmd_line);
2268 void print_line(struct cmd_line *cl)
2270 char *pl;
2272 if (!cl) return;
2274 if ((pl = malloc(cl->line_len + 1)) == NULL) {
2275 fprintf(stderr, "Unable to allocate space to print line: %s\n",
2276 strerror(errno));
2277 exit(1);
2280 strncpy(pl, cl->line, cl->line_len);
2281 pl[cl->line_len] = 0;
2283 fprintf(stdout, "%s\n", pl);
2284 free(pl);
2287 #define INIT_ALLOC 10
2290 * Read a line from the input file.
2291 * NULL returned when EOF and no chars read
2292 * Otherwise we return a cmd_line *
2293 * Exit if other errors
2295 struct cmd_line *get_cmd_line(int fd)
2297 struct cmd_line *cl = (CMD_LINE *)malloc(sizeof(CMD_LINE));
2298 int i = 0, rc;
2299 unsigned char ch;
2301 if (!cl) {
2302 fprintf(stderr, "Unable to allocate structure for command line: %s\n",
2303 strerror(errno));
2304 exit(1);
2307 cl->len = INIT_ALLOC;
2310 * Allocate some space for the line. We extend later if needed.
2313 if ((cl->line = (char *)malloc(INIT_ALLOC)) == NULL) {
2314 fprintf(stderr, "Unable to allocate initial space for line: %s\n",
2315 strerror(errno));
2316 exit(1);
2320 * Now read in the chars to EOL. Don't store the EOL in the
2321 * line. What about CR?
2324 while ((rc = read(fd, &ch, 1)) == 1 && ch != '\n') {
2325 if (ch == '\r') continue; /* skip CR */
2326 if (i == cl->len) {
2328 * Allocate some more memory
2330 if ((cl->line = realloc(cl->line, cl->len + INIT_ALLOC)) == NULL) {
2331 fprintf(stderr, "Unable to realloc space for line: %s\n",
2332 strerror(errno));
2333 exit(1);
2335 cl->len += INIT_ALLOC;
2337 cl->line[i] = ch;
2338 i++;
2341 /* read 0 and we were at loc'n 0, return NULL */
2342 if (rc == 0 && i == 0) {
2343 free_cmd_line(cl);
2344 return NULL;
2347 cl->line_len = i;
2349 return cl;
2354 * parse_value: parse out a value. We pull it apart as:
2356 * <value> ::= <value-name>=<type>:<value-string>
2358 * <value-name> ::= char-string-without-spaces | '"' char-string '"'
2360 * If it parsed OK, return the <value-name> as a string, and the
2361 * value type and value-string in parameters.
2363 * The value name can be empty. There can only be one empty name in
2364 * a list of values. A value of - removes the value entirely.
2367 char *dup_str(char *s, int len)
2369 char *nstr;
2370 nstr = (char *)malloc(len + 1);
2371 if (nstr) {
2372 memcpy(nstr, s, len);
2373 nstr[len] = 0;
2375 return nstr;
2378 char *parse_name(char *nstr)
2380 int len = 0, start = 0;
2381 if (!nstr) return NULL;
2383 len = strlen(nstr);
2385 while (len && nstr[len - 1] == ' ') len--;
2387 nstr[len] = 0; /* Trim any spaces ... if there were none, doesn't matter */
2390 * Beginning and end should be '"' or neither should be so
2392 if ((nstr[0] == '"' && nstr[len - 1] != '"') ||
2393 (nstr[0] != '"' && nstr[len - 1] == '"'))
2394 return NULL;
2396 if (nstr[0] == '"') {
2397 start = 1;
2398 len -= 2;
2401 return dup_str(&nstr[start], len);
2404 int parse_value_type(char *tstr)
2406 int len = strlen(tstr);
2408 while (len && tstr[len - 1] == ' ') len--;
2409 tstr[len] = 0;
2411 if (strcmp(tstr, "REG_DWORD") == 0)
2412 return REG_TYPE_DWORD;
2413 else if (strcmp(tstr, "dword") == 0)
2414 return REG_TYPE_DWORD;
2415 else if (strcmp(tstr, "REG_EXPAND_SZ") == 0)
2416 return REG_TYPE_EXPANDSZ;
2417 else if (strcmp(tstr, "REG_BIN") == 0)
2418 return REG_TYPE_BIN;
2419 else if (strcmp(tstr, "REG_SZ") == 0)
2420 return REG_TYPE_REGSZ;
2421 else if (strcmp(tstr, "REG_MULTI_SZ") == 0)
2422 return REG_TYPE_MULTISZ;
2424 return 0;
2427 char *parse_val_str(char *vstr)
2430 return dup_str(vstr, strlen(vstr));
2434 char *parse_value(struct cmd_line *cl, int *vtype, char **val)
2436 char *p1 = NULL, *p2 = NULL, *nstr = NULL, *tstr = NULL, *vstr = NULL;
2438 if (!cl || !vtype || !val) return NULL;
2439 if (!cl->line_len) return NULL;
2441 p1 = dup_str(cl->line, cl->line_len);
2442 /* FIXME: Better return codes etc ... */
2443 if (!p1) return NULL;
2444 p2 = strchr(p1, '=');
2445 if (!p2) return NULL;
2447 *p2 = 0; p2++; /* Split into two strings at p2 */
2449 /* Now, parse the name ... */
2451 nstr = parse_name(p1);
2452 if (!nstr) goto error;
2454 /* Now, split the remainder and parse on type and val ... */
2456 tstr = p2;
2457 while (*tstr == ' ') tstr++; /* Skip leading white space */
2458 p2 = strchr(p2, ':');
2460 if (!p2) goto error;
2462 *p2 = 0; p2++; /* split on the : */
2464 *vtype = parse_value_type(tstr);
2466 if (!vtype) goto error;
2468 /* Now, parse the value string. It should return a newly malloc'd string */
2470 while (*p2 == ' ') p2++; /* Skip leading space */
2471 vstr = parse_val_str(p2);
2473 if (!vstr) goto error;
2475 *val = vstr;
2477 return nstr;
2479 error:
2480 if (p1) free(p1);
2481 if (nstr) free(nstr);
2482 if (vstr) free(vstr);
2483 return NULL;
2487 * Parse out a key. Look for a correctly formatted key [...]
2488 * and whether it is a delete or add? A delete is signalled
2489 * by a - in front of the key.
2490 * Assumes that there are no leading and trailing spaces
2493 char *parse_key(struct cmd_line *cl, int *cmd)
2495 int start = 1;
2496 char *tmp;
2498 if (cl->line[0] != '[' ||
2499 cl->line[cl->line_len - 1] != ']') return NULL;
2500 if (cl->line_len == 2) return NULL;
2501 *cmd = CMD_ADD_KEY;
2502 if (cl->line[1] == '-') {
2503 if (cl->line_len == 3) return NULL;
2504 start = 2;
2505 *cmd = CMD_DEL_KEY;
2507 tmp = malloc(cl->line_len - 1 - start + 1);
2508 if (!tmp) return tmp; /* Bail out on no mem ... FIXME */
2509 strncpy(tmp, &cl->line[start], cl->line_len - 1 - start);
2510 tmp[cl->line_len - 1 - start] = 0;
2511 return tmp;
2515 * Parse a line to determine if we have a key or a value
2516 * We only check for key or val ...
2519 int parse_line(struct cmd_line *cl)
2522 if (!cl || cl->len == 0) return 0;
2524 if (cl->line[0] == '[') /* No further checking for now */
2525 return CMD_KEY;
2526 else
2527 return CMD_VAL;
2531 * We seek to offset 0, read in the required number of bytes,
2532 * and compare to the correct value.
2533 * We then seek back to the original location
2535 int regedit4_file_type(int fd)
2537 int cur_ofs = 0;
2538 char desc[9];
2540 cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
2541 if (cur_ofs < 0) {
2542 fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
2543 exit(1); /* FIXME */
2546 if (cur_ofs) {
2547 lseek(fd, 0, SEEK_SET);
2550 if (read(fd, desc, 8) < 8) {
2551 fprintf(stderr, "Unable to read command file format\n");
2552 exit(2); /* FIXME */
2555 desc[8] = 0;
2557 if (strcmp(desc, FMT_STRING_REGEDIT4) == 0) {
2558 if (cur_ofs) {
2559 lseek(fd, cur_ofs, SEEK_SET);
2561 else {
2562 skip_to_eol(fd);
2564 return FMT_REGEDIT4;
2567 return FMT_UNREC;
2571 * Run though the data in the line and strip anything after a comment
2572 * char.
2574 void strip_comment(struct cmd_line *cl)
2576 int i;
2578 if (!cl) return;
2580 for (i = 0; i < cl->line_len; i++) {
2581 if (cl->line[i] == ';') {
2582 cl->line_len = i;
2583 return;
2589 * trim leading space
2592 void trim_leading_spaces(struct cmd_line *cl)
2594 int i;
2596 if (!cl) return;
2598 for (i = 0; i < cl->line_len; i++) {
2599 if (cl->line[i] != ' '){
2600 if (i) memcpy(cl->line, &cl->line[i], cl->line_len - i);
2601 return;
2607 * trim trailing spaces
2609 void trim_trailing_spaces(struct cmd_line *cl)
2611 int i;
2613 if (!cl) return;
2615 for (i = cl->line_len; i == 0; i--) {
2616 if (cl->line[i-1] != ' ' &&
2617 cl->line[i-1] != '\t') {
2618 cl->line_len = i;
2624 * Get a command ... This consists of possibly multiple lines:
2625 * [key]
2626 * values*
2627 * possibly Empty line
2629 * value ::= <value-name>=<value-type>':'<value-string>
2630 * <value-name> is some path, possibly enclosed in quotes ...
2631 * We alctually look for the next key to terminate a previous key
2633 CMD *regedit4_get_cmd(int fd)
2635 struct command_s *cmd = NULL;
2636 struct cmd_line *cl = NULL;
2637 struct val_spec_list *vl = NULL;
2639 if ((cmd = (struct command_s *)malloc(sizeof(struct command_s))) == NULL) {
2640 fprintf(stderr, "Unable to malloc space for command: %s\n",
2641 strerror(errno));
2642 exit(1);
2645 cmd->cmd = CMD_NONE;
2646 cmd->key = NULL;
2647 cmd->val_spec_list = cmd->val_spec_last = NULL;
2648 while ((cl = get_cmd_line(fd))) {
2650 strip_comment(cl); /* remove anything beyond a comment char */
2651 trim_trailing_spaces(cl);
2652 trim_leading_spaces(cl);
2654 if (cl->line_len == 0) { /* An empty line */
2655 free_cmd_line(cl);
2657 else { /* Else, non-empty ... */
2659 * Parse out the bits ...
2661 switch (parse_line(cl)) {
2662 case CMD_KEY:
2663 if ((cmd->key = parse_key(cl, &cmd->cmd)) == NULL) {
2664 fprintf(stderr, "Error parsing key from line: ");
2665 print_line(cl);
2666 fprintf(stderr, "\n");
2668 break;
2670 case CMD_VAL:
2672 * We need to add the value stuff to the list
2673 * There could be a \ on the end which we need to
2674 * handle at some time
2676 vl = (struct val_spec_list *)malloc(sizeof(struct val_spec_list));
2677 if (!vl) goto error;
2678 vl->next = NULL;
2679 vl->name = parse_value(cl, &vl->type, &vl->val);
2680 if (!vl->name) goto error;
2681 if (cmd->val_spec_list == NULL) {
2682 cmd->val_spec_list = cmd->val_spec_last = vl;
2684 else {
2685 cmd->val_spec_last->next = vl;
2686 cmd->val_spec_last = vl;
2688 cmd->val_count++;
2689 break;
2691 default:
2692 fprintf(stderr, "Unrecognized line in command file: \n");
2693 print_line(cl);
2694 break;
2699 if (!cmd->cmd) goto error; /* End of file ... */
2701 return cmd;
2703 error:
2704 if (vl) free(vl);
2705 if (cmd) free_cmd(cmd);
2706 return NULL;
2709 int regedit4_exec_cmd(CMD *cmd)
2712 return 0;
2715 int editreg_1_0_file_type(int fd)
2717 int cur_ofs = 0;
2718 char desc[11];
2720 cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
2721 if (cur_ofs < 0) {
2722 fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
2723 exit(1); /* FIXME */
2726 if (cur_ofs) {
2727 lseek(fd, 0, SEEK_SET);
2730 if (read(fd, desc, 10) < 10) {
2731 fprintf(stderr, "Unable to read command file format\n");
2732 exit(2); /* FIXME */
2735 desc[10] = 0;
2737 if (strcmp(desc, FMT_STRING_EDITREG1_0) == 0) {
2738 lseek(fd, cur_ofs, SEEK_SET);
2739 return FMT_REGEDIT4;
2742 return FMT_UNREC;
2745 CMD *editreg_1_0_get_cmd(int fd)
2747 return NULL;
2750 int editreg_1_0_exec_cmd(CMD *cmd)
2753 return -1;
2756 typedef struct command_ops_s {
2757 int type;
2758 int (*file_type)(int fd);
2759 CMD *(*get_cmd)(int fd);
2760 int (*exec_cmd)(CMD *cmd);
2761 } CMD_OPS;
2763 CMD_OPS default_cmd_ops[] = {
2764 {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd},
2765 {1, editreg_1_0_file_type, editreg_1_0_get_cmd, editreg_1_0_exec_cmd},
2766 {-1, NULL, NULL, NULL}
2769 typedef struct command_file_s {
2770 char *name;
2771 int type, fd;
2772 CMD_OPS cmd_ops;
2773 } CMD_FILE;
2776 * Create a new command file structure
2779 CMD_FILE *cmd_file_create(char *file)
2781 CMD_FILE *tmp;
2782 struct stat sbuf;
2783 int i = 0;
2786 * Let's check if the file exists ...
2787 * No use creating the cmd_file structure if the file does not exist
2790 if (stat(file, &sbuf) < 0) { /* Not able to access file */
2792 return NULL;
2795 tmp = (CMD_FILE *)malloc(sizeof(CMD_FILE));
2796 if (!tmp) {
2797 return NULL;
2801 * Let's fill in some of the fields;
2804 tmp->name = strdup(file);
2806 if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) {
2807 free(tmp);
2808 return NULL;
2812 * Now, try to find the format by indexing through the table
2814 while (default_cmd_ops[i].type != -1) {
2815 if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) {
2816 tmp->cmd_ops = default_cmd_ops[i];
2817 return tmp;
2819 i++;
2823 * If we got here, return NULL, as we could not figure out the type
2824 * of command file.
2826 * What about errors?
2829 free(tmp);
2830 return NULL;
2834 * Extract commands from the command file, and execute them.
2835 * We pass a table of command callbacks for that
2839 * Main code from here on ...
2843 * key print function here ...
2846 int print_key(const char *path, char *name, char *class_name, int root,
2847 int terminal, int vals)
2850 /*if (terminal)*/ fprintf(stdout, "[%s%s]\n", path, name);
2852 return 1;
2856 * Sec Desc print functions
2859 void print_type(unsigned char type)
2861 switch (type) {
2862 case 0x00:
2863 fprintf(stdout, " ALLOW");
2864 break;
2865 case 0x01:
2866 fprintf(stdout, " DENY");
2867 break;
2868 case 0x02:
2869 fprintf(stdout, " AUDIT");
2870 break;
2871 case 0x03:
2872 fprintf(stdout, " ALARM");
2873 break;
2874 case 0x04:
2875 fprintf(stdout, "ALLOW CPD");
2876 break;
2877 case 0x05:
2878 fprintf(stdout, "OBJ ALLOW");
2879 break;
2880 case 0x06:
2881 fprintf(stdout, " OBJ DENY");
2882 default:
2883 fprintf(stdout, " UNKNOWN");
2884 break;
2888 void print_flags(unsigned char flags)
2890 char flg_output[21];
2891 int some = 0;
2893 flg_output[0] = 0;
2894 if (!flags) {
2895 fprintf(stdout, " ");
2896 return;
2898 if (flags & 0x01) {
2899 if (some) strcat(flg_output, ",");
2900 some = 1;
2901 strcat(flg_output, "OI");
2903 if (flags & 0x02) {
2904 if (some) strcat(flg_output, ",");
2905 some = 1;
2906 strcat(flg_output, "CI");
2908 if (flags & 0x04) {
2909 if (some) strcat(flg_output, ",");
2910 some = 1;
2911 strcat(flg_output, "NP");
2913 if (flags & 0x08) {
2914 if (some) strcat(flg_output, ",");
2915 some = 1;
2916 strcat(flg_output, "IO");
2918 if (flags & 0x10) {
2919 if (some) strcat(flg_output, ",");
2920 some = 1;
2921 strcat(flg_output, "IA");
2923 if (flags == 0xF) {
2924 if (some) strcat(flg_output, ",");
2925 some = 1;
2926 strcat(flg_output, "VI");
2928 fprintf(stdout, " %s", flg_output);
2931 void print_perms(int perms)
2933 fprintf(stdout, " %8X", perms);
2936 void print_sid(DOM_SID *sid)
2938 int i, comps = sid->auths;
2939 fprintf(stdout, "S-%u-%u", sid->ver, sid->auth[5]);
2941 for (i = 0; i < comps; i++) {
2943 fprintf(stdout, "-%u", sid->sub_auths[i]);
2946 fprintf(stdout, "\n");
2949 void print_acl(ACL *acl, char *prefix)
2951 int i;
2953 for (i = 0; i < acl->num_aces; i++) {
2954 fprintf(stdout, ";;%s", prefix);
2955 print_type(acl->aces[i]->type);
2956 print_flags(acl->aces[i]->flags);
2957 print_perms(acl->aces[i]->perms);
2958 fprintf(stdout, " ");
2959 print_sid(acl->aces[i]->trustee);
2963 int print_sec(SEC_DESC *sec_desc)
2965 if (!print_security) return 1;
2966 fprintf(stdout, ";; SECURITY\n");
2967 fprintf(stdout, ";; Owner: ");
2968 print_sid(sec_desc->owner);
2969 fprintf(stdout, ";; Group: ");
2970 print_sid(sec_desc->group);
2971 if (sec_desc->sacl) {
2972 fprintf(stdout, ";; SACL:\n");
2973 print_acl(sec_desc->sacl, " ");
2975 if (sec_desc->dacl) {
2976 fprintf(stdout, ";; DACL:\n");
2977 print_acl(sec_desc->dacl, " ");
2979 return 1;
2983 * Value print function here ...
2985 int print_val(const char *path, char *val_name, int val_type, int data_len,
2986 void *data_blk, int terminal, int first, int last)
2988 char data_asc[1024];
2990 bzero(data_asc, sizeof(data_asc));
2991 if (!terminal && first)
2992 fprintf(stdout, "%s\n", path);
2993 data_to_ascii((unsigned char *)data_blk, data_len, val_type, data_asc,
2994 sizeof(data_asc) - 1);
2995 fprintf(stdout, " %s = %s : %s\n", (val_name?val_name:"<No Name>"),
2996 val_to_str(val_type, reg_type_names), data_asc);
2997 return 1;
3000 void usage(void)
3002 fprintf(stderr, "Usage: editreg [-v] [-p] [-k] [-s] [-c <command-file>] <registryfile>\n");
3003 fprintf(stderr, "Version: 0.1\n\n");
3004 fprintf(stderr, "\n\t-v\t sets verbose mode");
3005 fprintf(stderr, "\n\t-p\t prints the registry");
3006 fprintf(stderr, "\n\t-s\t prints security descriptors");
3007 fprintf(stderr, "\n\t-c <command-file>\t specifies a command file");
3008 fprintf(stderr, "\n");
3011 int main(int argc, char *argv[])
3013 REGF *regf;
3014 extern char *optarg;
3015 extern int optind;
3016 int opt, print_keys = 0;
3017 int regf_opt = 1; /* Command name */
3018 int commands = 0;
3019 char *cmd_file_name = NULL;
3020 char *out_file_name = NULL;
3021 CMD_FILE *cmd_file = NULL;
3023 if (argc < 2) {
3024 usage();
3025 exit(1);
3029 * Now, process the arguments
3032 while ((opt = getopt(argc, argv, "spvko:c:")) != EOF) {
3033 switch (opt) {
3034 case 'c':
3035 commands = 1;
3036 cmd_file_name = optarg;
3037 regf_opt += 2;
3038 break;
3040 case 'o':
3041 out_file_name = optarg;
3042 regf_opt += 2;
3043 break;
3045 case 'p':
3046 print_keys++;
3047 regf_opt++;
3048 break;
3050 case 's':
3051 print_security++;
3052 regf_opt++;
3053 break;
3055 case 'v':
3056 verbose++;
3057 regf_opt++;
3058 break;
3060 case 'k':
3061 regf_opt++;
3062 break;
3064 default:
3065 usage();
3066 exit(1);
3067 break;
3071 if ((regf = nt_create_regf()) == NULL) {
3072 fprintf(stderr, "Could not create registry object: %s\n", strerror(errno));
3073 exit(2);
3076 if (regf_opt < argc) { /* We have a registry file */
3077 if (!nt_set_regf_input_file(regf, argv[regf_opt])) {
3078 fprintf(stderr, "Could not set name of registry file: %s, %s\n",
3079 argv[regf_opt], strerror(errno));
3080 exit(3);
3083 /* Now, open it, and bring it into memory :-) */
3085 if (nt_load_registry(regf) < 0) {
3086 fprintf(stderr, "Could not load registry: %s\n", argv[1]);
3087 exit(4);
3091 if (out_file_name) {
3092 if (!nt_set_regf_output_file(regf, out_file_name)) {
3093 fprintf(stderr, "Could not set name of output registry file: %s, %s\n",
3094 out_file_name, strerror(errno));
3095 exit(3);
3100 if (commands) {
3101 CMD *cmd;
3103 cmd_file = cmd_file_create(cmd_file_name);
3105 while ((cmd = cmd_file->cmd_ops.get_cmd(cmd_file->fd)) != NULL) {
3108 * Now, apply the requests to the tree ...
3110 switch (cmd->cmd) {
3111 case CMD_ADD_KEY: {
3112 REG_KEY *tmp = NULL;
3114 tmp = nt_find_key_by_name(regf->root, cmd->key);
3116 /* If we found it, apply the other bits, else create such a key */
3118 if (!tmp)
3119 tmp = nt_add_reg_key(regf, cmd->key, True);
3121 if (tmp) {
3125 while (cmd->val_count) {
3129 break;
3132 case CMD_DEL_KEY:
3134 * Any value does not matter ...
3135 * Find the key if it exists, and delete it ...
3138 nt_delete_key_by_name(regf, cmd->key);
3139 break;
3142 free_cmd(cmd);
3146 * At this point, we should have a registry in memory and should be able
3147 * to iterate over it.
3150 if (print_keys) {
3151 nt_key_iterator(regf, regf->root, 0, "", print_key, print_sec, print_val);
3154 return 0;