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
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!
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
54 0x00000024 D-Word Offset of 1st key record
55 0x00000028 D-Word Size of the data-blocks (Filesize-4kb)
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...
66 I don't know what "hbin" stands for, but this block is always a multiple
69 Inside these hbin-blocks the different records are placed. The memory-
70 management looks like a C-compiler heap management to me...
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
87 0x0000 D-Word Data-block size //this size must be a
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 ==========================
104 The nk-record can be treated as a kombination of tree-record and
105 key-record of the win 95 registry.
109 The lf-record is the counterpart to the RGKN-record (the
114 The vk-record consists information to a single value.
118 sk (? Security Key ?) is the ACL of the registry.
122 The value-lists contain information about which values are inside a
123 sub-key and don't have a header.
127 The datas of the registry are (like the value-list) stored without a
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)...
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
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!
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
171 0x0012 Word Unused (data-trash)
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!
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
191 0x0000 Word ID: ASCII-"lf" = 0x666C
192 0x0002 Word number of keys
193 0x0004 ???? Hash-Records
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.
208 (due to the complexity of the SAM-info, not clear jet)
209 (This is just a security descriptor in the data. R Sharpe.)
213 0x0000 Word ID: ASCII-"sk" = 0x6B73
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
220 relative security desciptor. Nigel
221 ???? ???? Security and auditing settings...
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
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
240 If a value has no data (length=0), it is displayed as empty.
242 simplyfied win-3.?? registry:
243 =============================
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 |
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 | | +-----------+ +---------+
272 | values |--------------------->+----------+
273 | SK-rec. |---------------+ | 1. value |--> +----------+
274 | class |--+ | +----------+ | vk-rec. |
275 +---------+ | | | .... |
276 v | | data |--> +-------+
277 +------------+ | +----------+ | xxxxx |
278 | Class name | | +-------+
281 +---------+ +---------+
282 +----->| next sk |--->| Next sk |--+
283 | +---| prev sk |<---| prev sk | |
284 | | | .... | | ... | |
285 | | +---------+ +---------+ |
288 | +--------------------+ |
289 +----------------------------------+
291 ---------------------------------------------------------------------------
293 Hope this helps.... (Although it was "fun" for me to uncover this things,
294 it took me several sleepless nights ;)
298 *************************************************************************/
304 #include <sys/types.h>
305 #include <sys/stat.h>
307 #include <sys/mman.h>
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
;
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 */
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
;
370 * The KEY_LIST struct lists sub-keys.
373 typedef struct key_list_s
{
379 typedef struct val_key_s
{
384 void *data_blk
; /* Might want a separate block */
387 typedef struct val_list_s
{
393 #define MAXSUBAUTHS 15
396 typedef struct dom_sid_s
{
397 unsigned char ver
, auths
;
398 unsigned char auth
[6];
399 unsigned int sub_auths
[MAXSUBAUTHS
];
402 typedef struct ace_struct_s
{
403 unsigned char type
, flags
;
404 unsigned int perms
; /* Perhaps a better def is in order */
408 typedef struct acl_struct_s
{
409 unsigned short rev
, refcnt
;
410 unsigned short num_aces
;
414 typedef struct sec_desc_s
{
415 unsigned int rev
, type
;
416 DOM_SID
*owner
, *group
;
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
;
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
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 */
453 DWORD first_key
; /* offset */
454 unsigned int dblk_size
;
455 DWORD uk7
[116]; /* 1 */
459 typedef struct hbin_sub_struct
{
464 #define REG_HBIN_ID 0x6E696268
466 typedef struct hbin_struct
{
467 DWORD HBIN_ID
; /* hbin */
475 HBIN_SUB_HDR hbin_sub_hdr
;
478 #define REG_NK_ID 0x6B6E
480 typedef struct nk_struct
{
498 char key_nam
[1]; /* Actual length determined by nam_len */
501 #define REG_SK_ID 0x6B73
503 typedef struct sk_struct
{
513 typedef struct ace_struct
{
516 unsigned short length
;
521 typedef struct acl_struct
{
525 REG_ACE
*aces
; /* One or more ACEs */
528 typedef struct sec_desc_rec
{
537 typedef struct hash_struct
{
542 #define REG_LF_ID 0x666C
544 typedef struct lf_struct
{
547 struct hash_struct hr
[1]; /* Array of hash records, depending on key_count */
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
{
557 DWORD dat_len
; /* If top-bit set, offset contains the data */
560 WORD flag
; /* =1, has name, else no name (=Default). */
562 char dat_name
[1]; /* Name starts here ... */
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
{
577 /* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
578 typedef struct sk_map_s
{
580 KEY_SEC_DESC
*key_sec_desc
;
583 struct regf_struct_s
{
585 char *regfile_name
, *outfile_name
;
590 NTTIME last_mod_time
;
591 REG_KEY
*root
; /* Root of the tree for this file */
592 int sk_count
, sk_map_size
;
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
)
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
,
636 (i
== val_list
->val_count
))) {
646 int nt_key_list_iterator(REGF
*regf
, KEY_LIST
*key_list
, int bf
,
648 key_print_f key_print
, sec_print_f sec_print
,
649 val_print_f val_print
)
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
)) {
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
);
671 if (!regf
|| !key_tree
)
674 /* List the key first, then the values, then the sub-keys */
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)))
687 * If we have a security print routine, call it
688 * If the security print routine returns false, stop.
691 if (key_tree
->security
&& !(*sec_print
)(key_tree
->security
->sec_desc
))
695 new_path
= (char *)malloc(path_len
+ 1 + strlen(key_tree
->name
) + 1);
696 if (!new_path
) return 0; /* Errors? */
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
),
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
)) {
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
)
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
)))
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
;
759 if (!tree
|| !key
|| !*key
) return NULL
;
762 if (!lname
) return NULL
;
765 * Make sure that the first component is correct ...
768 c2
= strchr(c1
, '\\');
769 if (c2
) { /* Split here ... */
773 if (strcmp(c1
, tree
->name
) != 0) goto error
;
776 tmp
= nt_find_key_in_list_by_name(tree
->sub_keys
, c2
);
781 if (lname
) free(lname
);
785 if (lname
) free(lname
);
789 /* Make, delete keys */
791 int nt_delete_val_key(VAL_KEY
*val_key
)
795 if (val_key
->data_blk
) free(val_key
->data_blk
);
801 int nt_delete_val_list(VAL_LIST
*vl
)
806 for (i
=0; i
<vl
->val_count
; i
++)
807 nt_delete_val_key(vl
->vals
[i
]);
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
)
819 for (i
=0; i
<key_list
->key_count
; i
++)
820 nt_delete_reg_key(key_list
->keys
[i
], False
);
827 * Find the key, and if it exists, delete it ...
829 int nt_delete_key_by_name(REGF
*regf
, char *name
)
833 if (!name
|| !*name
) return 0;
835 key
= nt_find_key_by_name(regf
->root
, name
);
838 if (key
== regf
->root
) regf
->root
= NULL
;
839 return nt_delete_reg_key(key
, True
);
846 int nt_delete_sid(DOM_SID
*sid
)
854 int nt_delete_ace(ACE
*ace
)
858 nt_delete_sid(ace
->trustee
);
865 int nt_delete_acl(ACL
*acl
)
871 for (i
=0; i
<acl
->num_aces
; i
++)
872 nt_delete_ace(acl
->aces
[i
]);
879 int nt_delete_sec_desc(SEC_DESC
*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
);
894 int nt_delete_key_sec_desc(KEY_SEC_DESC
*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
);
911 int nt_delete_reg_key(REG_KEY
*key
, int delete_name
)
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
) {
926 /* Find our owner, look in keylist for us and shuffle up */
927 /* Perhaps should be a function */
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");
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
];
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
);
962 * Add a value to the key specified ...
964 REG_KEY
*nt_add_reg_value(REG_KEY
*key
, char *name
, int type
, char *value
)
971 * Delete a value. Should perhaps return the value ...
973 REG_KEY
*nt_delete_reg_valye(REG_KEY
*key
, char *name
)
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
)
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
;
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
)
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
);
1027 /* We only allow strings of form S-1-5... */
1030 (*sid
)->auth
[5] = 5;
1035 if (!lstr
|| !lstr
[0] || sscanf(lstr
, "-%u", &auth
) == 0) {
1037 fprintf(stderr
, "Not of form -d-d...: %s, %u\n", lstr
, i
);
1044 (*sid
)->sub_auths
[i
] = auth
;
1046 lstr
= strchr(lstr
+ 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
;
1076 REG_KEY
*nt_add_reg_subkey(REGF
*regf
, REG_KEY
*key
, char *name
, int create
);
1081 REG_KEY
*nt_add_reg_key_list(REGF
*regf
, REG_KEY
*key
, char * name
, int create
)
1084 REG_KEY
*ret
= NULL
, *tmp
= NULL
;
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
)))
1105 * If we reach here we could not find the the first component
1109 lname
= strdup(name
);
1110 if (!lname
) return NULL
;
1113 c2
= strchr(c1
, '\\');
1114 if (c2
) { /* Split here ... */
1119 if (list
->key_count
< list
->max_keys
){
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
*))));
1128 list
->max_keys
+= REG_KEY_LIST_SIZE
;
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
;
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
;
1158 ret
= nt_add_reg_subkey(regf
, key
, c2
, True
);
1161 if (lname
) free(lname
);
1167 if (lname
) free(lname
);
1171 REG_KEY
*nt_add_reg_subkey(REGF
*regf
, REG_KEY
*key
, char *name
, int create
)
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
;
1197 c2
= strchr(c1
, '\\');
1198 if (c2
) { /* Split here ... */
1204 * If the root does not exist, create it and make it equal to the
1205 * first component ...
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
;
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
1228 if (strcmp(c1
, key
->name
) != 0)
1232 tmp
= nt_add_reg_key_list(regf
, key
, c2
, True
);
1238 if (lname
) free(lname
);
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" },
1286 const char *val_to_str(unsigned int val
, const VAL_STR
*val_array
)
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
;
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
,
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];
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
;
1333 case REG_TYPE_REGSZ
:
1334 if (verbose
) fprintf(stderr
, "Len: %d\n", len
);
1335 return uni_to_ascii(datap
, ascii
, len
, ascii_max
);
1338 case REG_TYPE_EXPANDSZ
:
1339 return uni_to_ascii(datap
, ascii
, len
, ascii_max
);
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
++;
1351 return ((int)asciip
- (int)ascii
);
1354 case REG_TYPE_DWORD
:
1355 if (*(int *)datap
== 0)
1356 return snprintf(ascii
, ascii_max
, "0");
1358 return snprintf(ascii
, ascii_max
, "0x%x", *(int *)datap
);
1361 case REG_TYPE_MULTISZ
:
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
));
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 */
1407 regf
->sk_count
= regf
->sk_map_size
= 0;
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
)
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
, ®f
->sbuf
) < 0) {
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
,
1445 * At this point, regf->base != NULL, and we should be able to read the
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;
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
) {
1485 regf
->sk_map_size
= 10;
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
) {
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
;
1508 return regf
->sk_map
;
1512 * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
1516 KEY_SEC_DESC
*lookup_sec_key(SK_MAP
*sk_map
, int count
, int sk_off
)
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
;
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
);
1544 else { /* Allocate a new one */
1545 tmp
= (KEY_SEC_DESC
*)malloc(sizeof(KEY_SEC_DESC
));
1549 tmp
->state
= SEC_DESC_RES
;
1550 if (!alloc_sk_map_entry(regf
, tmp
, sk_off
)) {
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
));
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
];
1579 * Allocate space for an ACE and duplicate the registry encoded one passed in
1581 ACE
*dup_ace(REG_ACE
*ace
)
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
);
1597 * Allocate space for an ACL and duplicate the registry encoded one passed in
1599 ACL
*dup_acl(REG_ACL
*acl
)
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
;
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 */
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
));
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
)));
1640 tmp
->group
= dup_sid((DOM_SID
*)((char *)sec_desc
+ IVAL(&sec_desc
->group_off
)));
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
)));
1653 if (sec_desc
->dacl_off
)
1654 tmp
->dacl
= dup_acl((REG_ACL
*)((char *)sec_desc
+ IVAL(&sec_desc
->dacl_off
)));
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
);
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
);
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
1688 ((tmp
= lookup_sec_key(regf
->sk_map
, regf
->sk_count
, sk_off
)) != NULL
)
1689 && (tmp
->state
== SEC_DESC_OCU
)) {
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.
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
)) {
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
);
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
);
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
));
1776 bzero(tmp
, sizeof(VAL_KEY
));
1777 tmp
->has_name
= flag
;
1778 tmp
->data_type
= dat_type
;
1781 strncpy(val_name
, vk_hdr
->dat_name
, nam_len
);
1782 tmp
->name
= strdup(val_name
);
1788 strncpy(val_name
, "<No Name>", 10);
1791 * Allocate space and copy the data as a BLOB
1796 char *dtmp
= (char *)malloc(dat_len
&0x7FFFFFFF);
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
);
1827 /* XXX: FIXME, free the partially allocated struct */
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
)
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
);
1848 tmp
= (VAL_LIST
*)malloc(sizeof(VAL_LIST
) + (count
- 1) * sizeof(VAL_KEY
*));
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
));
1862 tmp
->val_count
= count
;
1867 /* XXX: FIXME, free the partially allocated structure */
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
;
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
);
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
*));
1901 tmp
->key_count
= count
;
1902 tmp
->max_keys
= count
;
1904 for (i
=0; i
<count
; i
++) {
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
]) {
1918 if (tmp
) nt_delete_key_list(tmp
, False
);
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
;
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
);
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
);
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
);
1989 * Fish out the class name, it is in UNICODE, while the key name is
1993 if (clsname_len
) { /* Just print in Ascii for now */
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.
2010 tmp
->class_name
= strdup(cls_name
);
2011 if (!tmp
->class_name
) {
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
);
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
));
2055 * Also handle the SK header ...
2058 sk_off
= IVAL(&nk_hdr
->sk_off
);
2059 sk_hdr
= (SK_HDR
*)LOCN(regf
->base
, sk_off
);
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
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
){
2087 if (tmp
) nt_delete_reg_key(tmp
, False
);
2091 int nt_load_registry(REGF
*regf
)
2094 unsigned int regf_id
, hbin_id
;
2098 /* Get the header */
2100 if ((regf_hdr
= nt_get_regf_hdr(regf
)) == NULL
) {
2104 /* Now process that header and start to read the rest in */
2106 if ((regf_id
= IVAL(®f_hdr
->REGF_ID
)) != REG_REGF_ID
) {
2107 fprintf(stderr
, "Unrecognized NT registry header id: %0X, %s\n",
2108 regf_id
, regf
->regfile_name
);
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
);
2121 /* Update the last mod date, and then go get the first NK record and on */
2123 TTTONTTIME(regf
, IVAL(®f_hdr
->tim1
), IVAL(®f_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
);
2139 * Get a pointer to the first key from the hreg_hdr
2142 first_key
= (NK_HDR
*)LOCN(regf
->base
, IVAL(®f_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
2157 if (regf
->base
) munmap(regf
->base
, regf
->sbuf
.st_size
);
2159 close(regf
->fd
); /* Ignore the error :-) */
2165 * Story the registry in the output file
2167 int nt_store_registry(REGF
*regf
)
2174 * Routines to parse a REGEDIT4 file
2176 * The file consists of:
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"
2201 #define CMD_ADD_KEY 1
2202 #define CMD_DEL_KEY 2
2207 typedef struct val_spec_list
{
2208 struct val_spec_list
*next
;
2211 char *val
; /* Kept as a char string, really? */
2214 typedef struct command_s
{
2218 VAL_SPEC_LIST
*val_spec_list
, *val_spec_last
;
2221 typedef struct cmd_line
{
2227 * Some routines to handle lines of info in the command files
2229 void skip_to_eol(int fd
)
2234 while ((rc
= read(fd
, &ch
, 1)) == 1) {
2235 if (ch
== 0x0A) return;
2238 fprintf(stderr
, "Could not read file descriptor: %d, %s\n",
2239 fd
, strerror(errno
));
2244 void free_cmd(CMD
*cmd
)
2248 while (cmd
->val_spec_list
) {
2251 tmp
= cmd
->val_spec_list
;
2252 cmd
->val_spec_list
= tmp
->next
;
2260 void free_cmd_line(CMD_LINE
*cmd_line
)
2263 if (cmd_line
->line
) free(cmd_line
->line
);
2268 void print_line(struct cmd_line
*cl
)
2274 if ((pl
= malloc(cl
->line_len
+ 1)) == NULL
) {
2275 fprintf(stderr
, "Unable to allocate space to print line: %s\n",
2280 strncpy(pl
, cl
->line
, cl
->line_len
);
2281 pl
[cl
->line_len
] = 0;
2283 fprintf(stdout
, "%s\n", 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
));
2302 fprintf(stderr
, "Unable to allocate structure for command line: %s\n",
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",
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 */
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",
2335 cl
->len
+= INIT_ALLOC
;
2341 /* read 0 and we were at loc'n 0, return NULL */
2342 if (rc
== 0 && i
== 0) {
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
)
2370 nstr
= (char *)malloc(len
+ 1);
2372 memcpy(nstr
, s
, len
);
2378 char *parse_name(char *nstr
)
2380 int len
= 0, start
= 0;
2381 if (!nstr
) return NULL
;
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] == '"'))
2396 if (nstr
[0] == '"') {
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
--;
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
;
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 ... */
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
;
2481 if (nstr
) free(nstr
);
2482 if (vstr
) free(vstr
);
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
)
2498 if (cl
->line
[0] != '[' ||
2499 cl
->line
[cl
->line_len
- 1] != ']') return NULL
;
2500 if (cl
->line_len
== 2) return NULL
;
2502 if (cl
->line
[1] == '-') {
2503 if (cl
->line_len
== 3) return NULL
;
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;
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 */
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
)
2540 cur_ofs
= lseek(fd
, 0, SEEK_CUR
); /* Get current offset */
2542 fprintf(stderr
, "Unable to get current offset: %s\n", strerror(errno
));
2543 exit(1); /* FIXME */
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 */
2557 if (strcmp(desc
, FMT_STRING_REGEDIT4
) == 0) {
2559 lseek(fd
, cur_ofs
, SEEK_SET
);
2564 return FMT_REGEDIT4
;
2571 * Run though the data in the line and strip anything after a comment
2574 void strip_comment(struct cmd_line
*cl
)
2580 for (i
= 0; i
< cl
->line_len
; i
++) {
2581 if (cl
->line
[i
] == ';') {
2589 * trim leading space
2592 void trim_leading_spaces(struct cmd_line
*cl
)
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
);
2607 * trim trailing spaces
2609 void trim_trailing_spaces(struct cmd_line
*cl
)
2615 for (i
= cl
->line_len
; i
== 0; i
--) {
2616 if (cl
->line
[i
-1] != ' ' &&
2617 cl
->line
[i
-1] != '\t') {
2624 * Get a command ... This consists of possibly multiple lines:
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",
2645 cmd
->cmd
= CMD_NONE
;
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 */
2657 else { /* Else, non-empty ... */
2659 * Parse out the bits ...
2661 switch (parse_line(cl
)) {
2663 if ((cmd
->key
= parse_key(cl
, &cmd
->cmd
)) == NULL
) {
2664 fprintf(stderr
, "Error parsing key from line: ");
2666 fprintf(stderr
, "\n");
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
;
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
;
2685 cmd
->val_spec_last
->next
= vl
;
2686 cmd
->val_spec_last
= vl
;
2692 fprintf(stderr
, "Unrecognized line in command file: \n");
2699 if (!cmd
->cmd
) goto error
; /* End of file ... */
2705 if (cmd
) free_cmd(cmd
);
2709 int regedit4_exec_cmd(CMD
*cmd
)
2715 int editreg_1_0_file_type(int fd
)
2720 cur_ofs
= lseek(fd
, 0, SEEK_CUR
); /* Get current offset */
2722 fprintf(stderr
, "Unable to get current offset: %s\n", strerror(errno
));
2723 exit(1); /* FIXME */
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 */
2737 if (strcmp(desc
, FMT_STRING_EDITREG1_0
) == 0) {
2738 lseek(fd
, cur_ofs
, SEEK_SET
);
2739 return FMT_REGEDIT4
;
2745 CMD
*editreg_1_0_get_cmd(int fd
)
2750 int editreg_1_0_exec_cmd(CMD
*cmd
)
2756 typedef struct command_ops_s
{
2758 int (*file_type
)(int fd
);
2759 CMD
*(*get_cmd
)(int fd
);
2760 int (*exec_cmd
)(CMD
*cmd
);
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
{
2776 * Create a new command file structure
2779 CMD_FILE
*cmd_file_create(char *file
)
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 */
2795 tmp
= (CMD_FILE
*)malloc(sizeof(CMD_FILE
));
2801 * Let's fill in some of the fields;
2804 tmp
->name
= strdup(file
);
2806 if ((tmp
->fd
= open(file
, O_RDONLY
, 666)) < 0) {
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
];
2823 * If we got here, return NULL, as we could not figure out the type
2826 * What about errors?
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
);
2856 * Sec Desc print functions
2859 void print_type(unsigned char type
)
2863 fprintf(stdout
, " ALLOW");
2866 fprintf(stdout
, " DENY");
2869 fprintf(stdout
, " AUDIT");
2872 fprintf(stdout
, " ALARM");
2875 fprintf(stdout
, "ALLOW CPD");
2878 fprintf(stdout
, "OBJ ALLOW");
2881 fprintf(stdout
, " OBJ DENY");
2883 fprintf(stdout
, " UNKNOWN");
2888 void print_flags(unsigned char flags
)
2890 char flg_output
[21];
2895 fprintf(stdout
, " ");
2899 if (some
) strcat(flg_output
, ",");
2901 strcat(flg_output
, "OI");
2904 if (some
) strcat(flg_output
, ",");
2906 strcat(flg_output
, "CI");
2909 if (some
) strcat(flg_output
, ",");
2911 strcat(flg_output
, "NP");
2914 if (some
) strcat(flg_output
, ",");
2916 strcat(flg_output
, "IO");
2919 if (some
) strcat(flg_output
, ",");
2921 strcat(flg_output
, "IA");
2924 if (some
) strcat(flg_output
, ",");
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
)
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
, " ");
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
);
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
[])
3014 extern char *optarg
;
3016 int opt
, print_keys
= 0;
3017 int regf_opt
= 1; /* Command name */
3019 char *cmd_file_name
= NULL
;
3020 char *out_file_name
= NULL
;
3021 CMD_FILE
*cmd_file
= NULL
;
3029 * Now, process the arguments
3032 while ((opt
= getopt(argc
, argv
, "spvko:c:")) != EOF
) {
3036 cmd_file_name
= optarg
;
3041 out_file_name
= optarg
;
3071 if ((regf
= nt_create_regf()) == NULL
) {
3072 fprintf(stderr
, "Could not create registry object: %s\n", strerror(errno
));
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
));
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]);
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
));
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 ...
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 */
3119 tmp
= nt_add_reg_key(regf
, cmd
->key
, True
);
3125 while (cmd
->val_count
) {
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
);
3146 * At this point, we should have a registry in memory and should be able
3147 * to iterate over it.
3151 nt_key_iterator(regf
, regf
->root
, 0, "", print_key
, print_sec
, print_val
);