summaryrefslogtreecommitdiff
path: root/source3/utils
diff options
context:
space:
mode:
Diffstat (limited to 'source3/utils')
-rw-r--r--source3/utils/editreg.c2976
1 files changed, 2976 insertions, 0 deletions
diff --git a/source3/utils/editreg.c b/source3/utils/editreg.c
new file mode 100644
index 0000000000..ad4417921c
--- /dev/null
+++ b/source3/utils/editreg.c
@@ -0,0 +1,2976 @@
+/*
+ Samba Unix/Linux SMB client utility editreg.c
+ Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*************************************************************************
+
+ A utility to edit a Windows NT/2K etc registry file.
+
+ Many of the ideas in here come from other people and software.
+ I first looked in Wine in misc/registry.c and was also influenced by
+ http://www.wednesday.demon.co.uk/dosreg.html
+
+ Which seems to contain comments from someone else. I reproduce them here
+ incase the site above disappears. It actually comes from
+ http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt.
+
+ The goal here is to read the registry into memory, manipulate it, and then
+ write it out if it was changed by any actions of the user.
+
+The windows NT registry has 2 different blocks, where one can occur many
+times...
+
+the "regf"-Block
+================
+
+"regf" is obviosly the abbreviation for "Registry file". "regf" is the
+signature of the header-block which is always 4kb in size, although only
+the first 64 bytes seem to be used and a checksum is calculated over
+the first 0x200 bytes only!
+
+Offset Size Contents
+0x00000000 D-Word ID: ASCII-"regf" = 0x66676572
+0x00000004 D-Word ???? //see struct REGF
+0x00000008 D-Word ???? Always the same value as at 0x00000004
+0x0000000C Q-Word last modify date in WinNT date-format
+0x00000014 D-Word 1
+0x00000018 D-Word 3
+0x0000001C D-Word 0
+0x00000020 D-Word 1
+0x00000024 D-Word Offset of 1st key record
+0x00000028 D-Word Size of the data-blocks (Filesize-4kb)
+0x0000002C D-Word 1
+0x000001FC D-Word Sum of all D-Words from 0x00000000 to
+0x000001FB //XOR of all words. Nigel
+
+I have analyzed more registry files (from multiple machines running
+NT 4.0 german version) and could not find an explanation for the values
+marked with ???? the rest of the first 4kb page is not important...
+
+the "hbin"-Block
+================
+I don't know what "hbin" stands for, but this block is always a multiple
+of 4kb in size.
+
+Inside these hbin-blocks the different records are placed. The memory-
+management looks like a C-compiler heap management to me...
+
+hbin-Header
+===========
+Offset Size Contents
+0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268
+0x0004 D-Word Offset from the 1st hbin-Block
+0x0008 D-Word Offset to the next hbin-Block
+0x001C D-Word Block-size
+
+The values in 0x0008 and 0x001C should be the same, so I don't know
+if they are correct or swapped...
+
+From offset 0x0020 inside a hbin-block data is stored with the following
+format:
+
+Offset Size Contents
+0x0000 D-Word Data-block size //this size must be a
+multiple of 8. Nigel
+0x0004 ???? Data
+
+If the size field is negative (bit 31 set), the corresponding block
+is free and has a size of -blocksize!
+
+That does not seem to be true. All block lengths seem to be negative! (Richard Sharpe)
+
+The data is stored as one record per block. Block size is a multiple
+of 4 and the last block reaches the next hbin-block, leaving no room.
+
+Records in the hbin-blocks
+==========================
+
+nk-Record
+
+ The nk-record can be treated as a kombination of tree-record and
+ key-record of the win 95 registry.
+
+lf-Record
+
+ The lf-record is the counterpart to the RGKN-record (the
+ hash-function)
+
+vk-Record
+
+ The vk-record consists information to a single value.
+
+sk-Record
+
+ sk (? Security Key ?) is the ACL of the registry.
+
+Value-Lists
+
+ The value-lists contain information about which values are inside a
+ sub-key and don't have a header.
+
+Datas
+
+ The datas of the registry are (like the value-list) stored without a
+ header.
+
+All offset-values are relative to the first hbin-block and point to the
+block-size field of the record-entry. to get the file offset, you have to add
+the header size (4kb) and the size field (4 bytes)...
+
+the nk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"nk" = 0x6B6E
+0x0002 Word for the root-key: 0x2C, otherwise 0x20 //key symbolic links 0x10. Nigel
+0x0004 Q-Word write-date/time in windows nt notation
+0x0010 D-Word Offset of Owner/Parent key
+0x0014 D-Word number of sub-Keys
+0x001C D-Word Offset of the sub-key lf-Records
+0x0024 D-Word number of values
+0x0028 D-Word Offset of the Value-List
+0x002C D-Word Offset of the sk-Record
+
+0x0030 D-Word Offset of the Class-Name //see NK structure for the use of these fields. Nigel
+0x0044 D-Word Unused (data-trash) //some kind of run time index. Does not appear to be important. Nigel
+0x0048 Word name-length
+0x004A Word class-name length
+0x004C ???? key-name
+
+the Value-List
+==============
+Offset Size Contents
+0x0000 D-Word Offset 1st Value
+0x0004 D-Word Offset 2nd Value
+0x???? D-Word Offset nth Value
+
+To determine the number of values, you have to look at the owner-nk-record!
+
+Der vk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"vk" = 0x6B76
+0x0002 Word name length
+0x0004 D-Word length of the data //if top bit is set when offset contains data. Nigel
+0x0008 D-Word Offset of Data
+0x000C D-Word Type of value
+0x0010 Word Flag
+0x0012 Word Unused (data-trash)
+0x0014 ???? Name
+
+If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
+
+If the data-size is lower 5, the data-offset value is used to store the data itself!
+
+The data-types
+==============
+Wert Beteutung
+0x0001 RegSZ: character string (in UNICODE!)
+0x0002 ExpandSZ: string with "%var%" expanding (UNICODE!)
+0x0003 RegBin: raw-binary value
+0x0004 RegDWord: Dword
+0x0007 RegMultiSZ: multiple strings, seperated with 0
+ (UNICODE!)
+
+The "lf"-record
+===============
+Offset Size Contents
+0x0000 Word ID: ASCII-"lf" = 0x666C
+0x0002 Word number of keys
+0x0004 ???? Hash-Records
+
+Hash-Record
+===========
+Offset Size Contents
+0x0000 D-Word Offset of corresponding "nk"-Record
+0x0004 D-Word ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
+
+Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the
+key-name you have to change the hash-value too!
+
+//These hashrecords must be sorted low to high within the lf record. Nigel.
+
+The "sk"-block
+==============
+(due to the complexity of the SAM-info, not clear jet)
+(This is just a security descriptor in the data. R Sharpe.)
+
+
+Offset Size Contents
+0x0000 Word ID: ASCII-"sk" = 0x6B73
+0x0002 Word Unused
+0x0004 D-Word Offset of previous "sk"-Record
+0x0008 D-Word Offset of next "sk"-Record
+0x000C D-Word usage-counter
+0x0010 D-Word Size of "sk"-record in bytes
+???? //standard self
+relative security desciptor. Nigel
+???? ???? Security and auditing settings...
+????
+
+The usage counter counts the number of references to this
+"sk"-record. You can use one "sk"-record for the entire registry!
+
+Windows nt date/time format
+===========================
+The time-format is a 64-bit integer which is incremented every
+0,0000001 seconds by 1 (I don't know how accurate it realy is!)
+It starts with 0 at the 1st of january 1601 0:00! All values are
+stored in GMT time! The time-zone is important to get the real
+time!
+
+Common values for win95 and win-nt
+==================================
+Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
+If a value has no name (length=0, flag(bit 0)=0), it is treated as the
+"Default" entry...
+If a value has no data (length=0), it is displayed as empty.
+
+simplyfied win-3.?? registry:
+=============================
+
++-----------+
+| next rec. |---+ +----->+------------+
+| first sub | | | | Usage cnt. |
+| name | | +-->+------------+ | | length |
+| value | | | | next rec. | | | text |------->+-------+
++-----------+ | | | name rec. |--+ +------------+ | xxxxx |
+ +------------+ | | value rec. |-------->+------------+ +-------+
+ v | +------------+ | Usage cnt. |
++-----------+ | | length |
+| next rec. | | | text |------->+-------+
+| first sub |------+ +------------+ | xxxxx |
+| name | +-------+
+| value |
++-----------+
+
+Greatly simplyfied structure of the nt-registry:
+================================================
+
++---------------------------------------------------------------+
+| |
+v |
++---------+ +---------->+-----------+ +----->+---------+ |
+| "nk" | | | lf-rec. | | | nk-rec. | |
+| ID | | | # of keys | | | parent |---+
+| Date | | | 1st key |--+ | .... |
+| parent | | +-----------+ +---------+
+| suk-keys|-----+
+| values |--------------------->+----------+
+| SK-rec. |---------------+ | 1. value |--> +----------+
+| class |--+ | +----------+ | vk-rec. |
++---------+ | | | .... |
+ v | | data |--> +-------+
+ +------------+ | +----------+ | xxxxx |
+ | Class name | | +-------+
+ +------------+ |
+ v
+ +---------+ +---------+
+ +----->| next sk |--->| Next sk |--+
+ | +---| prev sk |<---| prev sk | |
+ | | | .... | | ... | |
+ | | +---------+ +---------+ |
+ | | ^ |
+ | | | |
+ | +--------------------+ |
+ +----------------------------------+
+
+---------------------------------------------------------------------------
+
+Hope this helps.... (Although it was "fun" for me to uncover this things,
+ it took me several sleepless nights ;)
+
+ B.D.
+
+*************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <fcntl.h>
+
+#define False 0
+#define True 1
+#define REG_KEY_LIST_SIZE 10;
+
+static int verbose = 0;
+static int print_security = 0;
+
+/*
+ * These definitions are for the in-memory registry structure.
+ * It is a tree structure that mimics what you see with tools like regedit
+ */
+
+/*
+ * DateTime struct for Windows
+ */
+
+typedef struct date_time_s {
+ unsigned int low, high;
+} NTTIME;
+
+/*
+ * Definition of a Key. It has a name, classname, date/time last modified,
+ * sub-keys, values, and a security descriptor
+ */
+
+#define REG_ROOT_KEY 1
+#define REG_SUB_KEY 2
+#define REG_SYM_LINK 3
+
+typedef struct reg_key_s {
+ char *name; /* Name of the key */
+ char *class_name;
+ int type; /* One of REG_ROOT_KEY or REG_SUB_KEY */
+ NTTIME last_mod; /* Time last modified */
+ struct reg_key_s *owner;
+ struct key_list_s *sub_keys;
+ struct val_list_s *values;
+ struct key_sec_desc_s *security;
+} REG_KEY;
+
+/*
+ * The KEY_LIST struct lists sub-keys.
+ */
+
+typedef struct key_list_s {
+ int key_count;
+ int max_keys;
+ REG_KEY *keys[1];
+} KEY_LIST;
+
+typedef struct val_key_s {
+ char *name;
+ int has_name;
+ int data_type;
+ int data_len;
+ void *data_blk; /* Might want a separate block */
+} VAL_KEY;
+
+typedef struct val_list_s {
+ int val_count;
+ VAL_KEY *vals[1];
+} VAL_LIST;
+
+#ifndef MAXSUBAUTHS
+#define MAXSUBAUTHS 15
+#endif
+
+typedef struct dom_sid_s {
+ unsigned char ver, auths;
+ unsigned char auth[6];
+ unsigned int sub_auths[MAXSUBAUTHS];
+} DOM_SID;
+
+typedef struct ace_struct_s {
+ unsigned char type, flags;
+ unsigned int perms; /* Perhaps a better def is in order */
+ DOM_SID *trustee;
+} ACE;
+
+typedef struct acl_struct_s {
+ unsigned short rev, refcnt;
+ unsigned short num_aces;
+ ACE *aces[1];
+} ACL;
+
+typedef struct sec_desc_s {
+ unsigned int rev, type;
+ DOM_SID *owner, *group;
+ ACL *sacl, *dacl;
+} SEC_DESC;
+
+#define SEC_DESC_NON 0
+#define SEC_DESC_RES 1
+#define SEC_DESC_OCU 2
+
+typedef struct key_sec_desc_s {
+ struct key_sec_desc_s *prev, *next;
+ int ref_cnt;
+ int state;
+ SEC_DESC *sec_desc;
+} KEY_SEC_DESC;
+
+/*
+ * All of the structures below actually have a four-byte lenght before them
+ * which always seems to be negative. The following macro retrieves that
+ * size as an integer
+ */
+
+#define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
+
+typedef unsigned int DWORD;
+typedef unsigned short WORD;
+
+#define REG_REGF_ID 0x66676572
+
+typedef struct regf_block {
+ DWORD REGF_ID; /* regf */
+ DWORD uk1;
+ DWORD uk2;
+ DWORD tim1, tim2;
+ DWORD uk3; /* 1 */
+ DWORD uk4; /* 3 */
+ DWORD uk5; /* 0 */
+ DWORD uk6; /* 1 */
+ DWORD first_key; /* offset */
+ unsigned int dblk_size;
+ DWORD uk7[116]; /* 1 */
+ DWORD chksum;
+} REGF_HDR;
+
+typedef struct hbin_sub_struct {
+ DWORD dblocksize;
+ char data[1];
+} HBIN_SUB_HDR;
+
+#define REG_HBIN_ID 0x6E696268
+
+typedef struct hbin_struct {
+ DWORD HBIN_ID; /* hbin */
+ DWORD next_off;
+ DWORD prev_off;
+ DWORD uk1;
+ DWORD uk2;
+ DWORD uk3;
+ DWORD uk4;
+ DWORD blk_size;
+ HBIN_SUB_HDR hbin_sub_hdr;
+} HBIN_HDR;
+
+#define REG_NK_ID 0x6B6E
+
+typedef struct nk_struct {
+ WORD NK_ID;
+ WORD type;
+ DWORD t1, t2;
+ DWORD uk1;
+ DWORD own_off;
+ DWORD subk_num;
+ DWORD uk2;
+ DWORD lf_off;
+ DWORD uk3;
+ DWORD val_cnt;
+ DWORD val_off;
+ DWORD sk_off;
+ DWORD clsnam_off;
+ DWORD unk4[4];
+ DWORD unk5;
+ WORD nam_len;
+ WORD clsnam_len;
+ char key_nam[1]; /* Actual length determined by nam_len */
+} NK_HDR;
+
+#define REG_SK_ID 0x6B73
+
+typedef struct sk_struct {
+ WORD SK_ID;
+ WORD uk1;
+ DWORD prev_off;
+ DWORD next_off;
+ DWORD ref_cnt;
+ DWORD rec_size;
+ char sec_desc[1];
+} SK_HDR;
+
+typedef struct ace_struct {
+ unsigned char type;
+ unsigned char flags;
+ unsigned short length;
+ unsigned int perms;
+ DOM_SID trustee;
+} REG_ACE;
+
+typedef struct acl_struct {
+ WORD rev;
+ WORD size;
+ DWORD num_aces;
+ REG_ACE *aces; /* One or more ACEs */
+} REG_ACL;
+
+typedef struct sec_desc_rec {
+ WORD rev;
+ WORD type;
+ DWORD owner_off;
+ DWORD group_off;
+ DWORD sacl_off;
+ DWORD dacl_off;
+} REG_SEC_DESC;
+
+typedef struct hash_struct {
+ DWORD nk_off;
+ char hash[4];
+} HASH_REC;
+
+#define REG_LF_ID 0x666C
+
+typedef struct lf_struct {
+ WORD LF_ID;
+ WORD key_count;
+ struct hash_struct hr[1]; /* Array of hash records, depending on key_count */
+} LF_HDR;
+
+typedef DWORD VL_TYPE[1]; /* Value list is an array of vk rec offsets */
+
+#define REG_VK_ID 0x6B76
+
+typedef struct vk_struct {
+ WORD VK_ID;
+ WORD nam_len;
+ DWORD dat_len; /* If top-bit set, offset contains the data */
+ DWORD dat_off;
+ DWORD dat_type;
+ WORD flag; /* =1, has name, else no name (=Default). */
+ WORD unk1;
+ char dat_name[1]; /* Name starts here ... */
+} VK_HDR;
+
+#define REG_TYPE_NONE 0
+#define REG_TYPE_REGSZ 1
+#define REG_TYPE_EXPANDSZ 2
+#define REG_TYPE_BIN 3
+#define REG_TYPE_DWORD 4
+#define REG_TYPE_MULTISZ 7
+
+typedef struct _val_str {
+ unsigned int val;
+ const char * str;
+} VAL_STR;
+
+/* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
+typedef struct sk_map_s {
+ int sk_off;
+ KEY_SEC_DESC *key_sec_desc;
+} SK_MAP;
+
+struct regf_struct_s {
+ int reg_type;
+ char *regfile_name, *outfile_name;
+ int fd;
+ struct stat sbuf;
+ char *base;
+ int modified;
+ NTTIME last_mod_time;
+ REG_KEY *root; /* Root of the tree for this file */
+ int sk_count, sk_map_size;
+ SK_MAP *sk_map;
+};
+
+typedef struct regf_struct_s REGF;
+
+/*
+ * An API for accessing/creating/destroying items above
+ */
+
+/*
+ * Iterate over the keys, depth first, calling a function for each key
+ * and indicating if it is terminal or non-terminal and if it has values.
+ *
+ * In addition, for each value in the list, call a value list function
+ */
+
+typedef int (*key_print_f)(const char *path, char *key_name, char *class_name,
+ int root, int terminal, int values);
+
+typedef int (*val_print_f)(const char *path, char *val_name, int val_type,
+ int data_len, void *data_blk, int terminal,
+ int first, int last);
+
+typedef int (*sec_print_f)(SEC_DESC *sec_desc);
+
+int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
+ key_print_f key_print, sec_print_f sec_print,
+ val_print_f val_print);
+
+int nt_val_list_iterator(REGF *regf, VAL_LIST *val_list, int bf, char *path,
+ int terminal, val_print_f val_print)
+{
+ int i;
+
+ if (!val_list) return 1;
+
+ if (!val_print) return 1;
+
+ for (i=0; i<val_list->val_count; i++) {
+ if (!val_print(path, val_list->vals[i]->name, val_list->vals[i]->data_type,
+ val_list->vals[i]->data_len, val_list->vals[i]->data_blk,
+ terminal,
+ (i == 0),
+ (i == val_list->val_count))) {
+
+ return 0;
+
+ }
+ }
+
+ return 1;
+}
+
+int nt_key_list_iterator(REGF *regf, KEY_LIST *key_list, int bf,
+ const char *path,
+ key_print_f key_print, sec_print_f sec_print,
+ val_print_f val_print)
+{
+ int i;
+
+ if (!key_list) return 1;
+
+ for (i=0; i< key_list->key_count; i++) {
+ if (!nt_key_iterator(regf, key_list->keys[i], bf, path, key_print,
+ sec_print, val_print)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
+ key_print_f key_print, sec_print_f sec_print,
+ val_print_f val_print)
+{
+ int path_len = strlen(path);
+ char *new_path;
+
+ if (!regf || !key_tree)
+ return -1;
+
+ /* List the key first, then the values, then the sub-keys */
+
+ if (key_print) {
+
+ if (!(*key_print)(path, key_tree->name,
+ key_tree->class_name,
+ (key_tree->type == REG_ROOT_KEY),
+ (key_tree->sub_keys == NULL),
+ (key_tree->values?(key_tree->values->val_count):0)))
+ return 0;
+ }
+
+ /*
+ * If we have a security print routine, call it
+ * If the security print routine returns false, stop.
+ */
+ if (sec_print) {
+ if (key_tree->security && !(*sec_print)(key_tree->security->sec_desc))
+ return 0;
+ }
+
+ new_path = (char *)malloc(path_len + 1 + strlen(key_tree->name) + 1);
+ if (!new_path) return 0; /* Errors? */
+ new_path[0] = '\0';
+ strcat(new_path, path);
+ strcat(new_path, key_tree->name);
+ strcat(new_path, "\\");
+
+ /*
+ * Now, iterate through the values in the val_list
+ */
+
+ if (key_tree->values &&
+ !nt_val_list_iterator(regf, key_tree->values, bf, new_path,
+ (key_tree->values!=NULL),
+ val_print)) {
+
+ free(new_path);
+ return 0;
+ }
+
+ /*
+ * Now, iterate through the keys in the key list
+ */
+
+ if (key_tree->sub_keys &&
+ !nt_key_list_iterator(regf, key_tree->sub_keys, bf, new_path, key_print,
+ sec_print, val_print)) {
+ free(new_path);
+ return 0;
+ }
+
+ free(new_path);
+ return 1;
+}
+
+REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key);
+
+/*
+ * Find key by name in a list ...
+ * Take the first component and search for that in the list
+ */
+REG_KEY *nt_find_key_in_list_by_name(KEY_LIST *list, char *key)
+{
+ int i;
+ REG_KEY *res = NULL;
+
+ if (!list || !key || !*key) return NULL;
+
+ for (i = 0; i<= list->key_count; i++)
+ if ((res = nt_find_key_by_name(list->keys[i], key)))
+ return res;
+
+ return NULL;
+}
+
+/*
+ * Find key by name in a tree ... We will assume absolute names here, but we
+ * need the root of the tree ...
+ */
+REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key)
+{
+ char *lname = NULL, *c1, *c2;
+ REG_KEY *tmp;
+
+ if (!tree || !key || !*key) return NULL;
+
+ lname = strdup(key);
+ if (!lname) return NULL;
+
+ /*
+ * Make sure that the first component is correct ...
+ */
+ c1 = lname;
+ c2 = strchr(c1, '\\');
+ if (c2) { /* Split here ... */
+ *c2 = 0;
+ c2++;
+ }
+ if (strcmp(c1, tree->name) != 0) goto error;
+
+ if (c2) {
+ tmp = nt_find_key_in_list_by_name(tree->sub_keys, c2);
+ free(lname);
+ return tmp;
+ }
+ else {
+ if (lname) free(lname);
+ return tree;
+ }
+ error:
+ if (lname) free(lname);
+ return NULL;
+}
+
+/* Make, delete keys */
+
+int nt_delete_val_key(VAL_KEY *val_key)
+{
+
+ if (val_key) {
+ if (val_key->data_blk) free(val_key->data_blk);
+ free(val_key);
+ };
+ return 1;
+}
+
+int nt_delete_val_list(VAL_LIST *vl)
+{
+ int i;
+
+ if (vl) {
+ for (i=0; i<vl->val_count; i++)
+ nt_delete_val_key(vl->vals[i]);
+ free(vl);
+ }
+ return 1;
+}
+
+int nt_delete_reg_key(REG_KEY *key, int delete_name);
+int nt_delete_key_list(KEY_LIST *key_list, int delete_name)
+{
+ int i;
+
+ if (key_list) {
+ for (i=0; i<key_list->key_count; i++)
+ nt_delete_reg_key(key_list->keys[i], False);
+ free(key_list);
+ }
+ return 1;
+}
+
+/*
+ * Find the key, and if it exists, delete it ...
+ */
+int nt_delete_key_by_name(REGF *regf, char *name)
+{
+ REG_KEY *key;
+
+ if (!name || !*name) return 0;
+
+ key = nt_find_key_by_name(regf->root, name);
+
+ if (key) {
+ return nt_delete_reg_key(key, True);
+ }
+
+ return 0;
+
+}
+
+int nt_delete_sid(DOM_SID *sid)
+{
+
+ if (sid) free(sid);
+ return 1;
+
+}
+
+int nt_delete_ace(ACE *ace)
+{
+
+ if (ace) {
+ nt_delete_sid(ace->trustee);
+ free(ace);
+ }
+ return 1;
+
+}
+
+int nt_delete_acl(ACL *acl)
+{
+
+ if (acl) {
+ int i;
+
+ for (i=0; i<acl->num_aces; i++)
+ nt_delete_ace(acl->aces[i]);
+
+ free(acl);
+ }
+ return 1;
+}
+
+int nt_delete_sec_desc(SEC_DESC *sec_desc)
+{
+
+ if (sec_desc) {
+
+ nt_delete_sid(sec_desc->owner);
+ nt_delete_sid(sec_desc->group);
+ nt_delete_acl(sec_desc->sacl);
+ nt_delete_acl(sec_desc->dacl);
+ free(sec_desc);
+
+ }
+ return 1;
+}
+
+int nt_delete_key_sec_desc(KEY_SEC_DESC *key_sec_desc)
+{
+
+ if (key_sec_desc) {
+ key_sec_desc->ref_cnt--;
+ if (key_sec_desc->ref_cnt<=0) {
+ /*
+ * There should always be a next and prev, even if they point to us
+ */
+ key_sec_desc->next->prev = key_sec_desc->prev;
+ key_sec_desc->prev->next = key_sec_desc->next;
+ nt_delete_sec_desc(key_sec_desc->sec_desc);
+ }
+ }
+ return 1;
+}
+
+int nt_delete_reg_key(REG_KEY *key, int delete_name)
+{
+
+ if (key) {
+ if (key->name) free(key->name);
+ if (key->class_name) free(key->class_name);
+
+ /*
+ * We will delete the owner if we are not the root and told to ...
+ */
+
+ if (key->owner && key->owner->sub_keys && delete_name) {
+ REG_KEY *own;
+ KEY_LIST *kl;
+ int i;
+ /* Find our owner, look in keylist for us and shuffle up */
+ /* Perhaps should be a function */
+
+ own = key->owner;
+ kl = own->sub_keys;
+
+ for (i=0; i < kl->key_count && kl->keys[i] != key ; i++) {
+ /* Just find the entry ... */
+ }
+
+ if (i == kl->key_count) {
+ fprintf(stderr, "Bad data structure. Key not found in key list of owner\n");
+ }
+ else {
+ int j;
+
+ /*
+ * Shuffle up. Works for the last one also
+ */
+ for (j = i + 1; j < kl->key_count; j++) {
+ kl->keys[j - 1] = kl->keys[j];
+ }
+
+ kl->key_count--;
+ }
+ }
+
+ if (key->sub_keys) nt_delete_key_list(key->sub_keys, False);
+ if (key->values) nt_delete_val_list(key->values);
+ if (key->security) nt_delete_key_sec_desc(key->security);
+ free(key);
+ }
+ return 1;
+}
+
+/*
+ * Add a key to the tree ... We walk down the components matching until
+ * we don't find any. There must be a match on the first component ...
+ * We return the key structure for the final component as that is
+ * often where we want to add values ...
+ */
+
+/*
+ * Create a 1 component key name and set its parent to parent
+ */
+REG_KEY *nt_create_reg_key1(char *name, REG_KEY *parent)
+{
+ REG_KEY *tmp;
+
+ if (!name || !*name) return NULL; /* A key's name cannot be empty */
+
+ /* There should not be more than one component */
+ if (strchr(name, '\\')) return NULL;
+
+ if (!(tmp = (REG_KEY *)malloc(sizeof(REG_KEY)))) return NULL;
+
+ bzero(tmp, sizeof(REG_KEY));
+
+ if (!(tmp->name = strdup(name))) goto error;
+
+
+
+ error:
+ if (tmp) free(tmp);
+ return NULL;
+}
+
+REG_KEY *nt_add_reg_key(REG_KEY *key, char *name, int create);
+REG_KEY *nt_add_reg_key_list(KEY_LIST *list, char * name, REG_KEY *parent, int create)
+{
+ int i;
+ REG_KEY *ret;
+ char *lname, *c1, *c2;
+
+ if (!list || !name || !*name) return NULL;
+
+ for (i = 0; i < list->key_count; i++) {
+ if ((ret = nt_add_reg_key(list->keys[i], name, create)))
+ return ret;
+ }
+
+ /*
+ * If we reach here we could not find the the first component
+ * so create it ...
+ */
+
+ lname = strdup(name);
+ if (!lname) return NULL;
+
+ c1 = lname;
+ c2 = strchr(c1, '\\');
+ if (c2) { /* Split here ... */
+ *c2 = 0;
+ c2++;
+ }
+
+ if (list->key_count < list->max_keys){
+ list->key_count++;
+ }
+ else { /* Create more space in the list ... */
+
+ }
+
+ return NULL;
+ error:
+ if (lname) free(lname);
+ return NULL;
+}
+
+REG_KEY *nt_add_reg_key(REG_KEY *key, char *name, int create)
+{
+ char *lname = NULL, *c1, *c2;
+ REG_KEY * tmp;
+
+ /*
+ * Look until we hit the first component that does not exist, and
+ * then add from there. However, if the first component does not
+ * match and the path we are given is the root, then it must match
+ */
+ if (!key || !name || !*name) return NULL;
+
+ lname = strdup(name);
+ if (!lname) return NULL;
+
+ c1 = lname;
+ c2 = strchr(c1, '\\');
+ if (c2) { /* Split here ... */
+ *c2 = 0;
+ c2++;
+ }
+
+ /*
+ * If we don't match, then we have to return error ...
+ * If we do match on this component, check the next one in the
+ * list, and if not found, add it ... short circuit, add all the
+ * way down
+ */
+
+ if (strcmp(c1, key->name) != 0)
+ goto error;
+
+ tmp = nt_add_reg_key_list(key->sub_keys, c2, key, True);
+ free(lname);
+ return tmp;
+
+ error:
+ if (lname) free(lname);
+ return NULL;
+}
+
+/*
+ * Create/delete value lists, add/delete values, count them
+ */
+
+
+/*
+ * Create/delete security descriptors, add/delete SIDS, count SIDS, etc.
+ * We reference count the security descriptors. Any new reference increments
+ * the ref count. If we modify an SD, we copy the old one, dec the ref count
+ * and make the change. We also want to be able to check for equality so
+ * we can reduce the number of SDs in use.
+ */
+
+/*
+ * Load and unload a registry file.
+ *
+ * Load, loads it into memory as a tree, while unload sealizes/flattens it
+ */
+
+/*
+ * Get the starting record for NT Registry file
+ */
+
+/*
+ * Where we keep all the regf stuff for one registry.
+ * This is the structure that we use to tie the in memory tree etc
+ * together. By keeping separate structs, we can operate on different
+ * registries at the same time.
+ * Currently, the SK_MAP is an array of mapping structure.
+ * Since we only need this on input and output, we fill in the structure
+ * as we go on input. On output, we know how many SK items we have, so
+ * we can allocate the structure as we need to.
+ * If you add stuff here that is dynamically allocated, add the
+ * appropriate free statements below.
+ */
+
+#define REGF_REGTYPE_NONE 0
+#define REGF_REGTYPE_NT 1
+#define REGF_REGTYPE_W9X 2
+
+#define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
+ (r)->last_mod_time.high = (t2);
+
+#define REGF_HDR_BLKSIZ 0x1000
+
+/*
+ * Structures for dealing with the on-disk format of the registry
+ */
+
+#define IVAL(buf) ((unsigned int) \
+ (unsigned int)*((unsigned char *)(buf)+3)<<24| \
+ (unsigned int)*((unsigned char *)(buf)+2)<<16| \
+ (unsigned int)*((unsigned char *)(buf)+1)<<8| \
+ (unsigned int)*((unsigned char *)(buf)+0))
+
+#define SVAL(buf) ((unsigned short) \
+ (unsigned short)*((unsigned char *)(buf)+1)<<8| \
+ (unsigned short)*((unsigned char *)(buf)+0))
+
+#define CVAL(buf) ((unsigned char)*((unsigned char *)(buf)))
+
+#define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4)
+#define LOCN(base, f) ((base) + OFF(f))
+
+const VAL_STR reg_type_names[] = {
+ { REG_TYPE_REGSZ, "REG_SZ" },
+ { REG_TYPE_EXPANDSZ, "REG_EXPAND_SZ" },
+ { REG_TYPE_BIN, "REG_BIN" },
+ { REG_TYPE_DWORD, "REG_DWORD" },
+ { REG_TYPE_MULTISZ, "REG_MULTI_SZ" },
+ { 0, NULL },
+};
+
+const char *val_to_str(unsigned int val, const VAL_STR *val_array)
+{
+ int i = 0;
+
+ if (!val_array) return NULL;
+
+ while (val_array[i].val && val_array[i].str) {
+
+ if (val_array[i].val == val) return val_array[i].str;
+ i++;
+
+ }
+
+ return NULL;
+
+}
+
+/*
+ * Convert from UniCode to Ascii ... Does not take into account other lang
+ * Restrict by ascii_max if > 0
+ */
+int uni_to_ascii(unsigned char *uni, unsigned char *ascii, int ascii_max,
+ int uni_max)
+{
+ int i = 0;
+
+ while (i < ascii_max && !(!uni[i*2] && !uni[i*2+1])) {
+ if (uni_max > 0 && (i*2) >= uni_max) break;
+ ascii[i] = uni[i*2];
+ i++;
+
+ }
+
+ ascii[i] = '\0';
+
+ return i;
+}
+
+/*
+ * Convert a data value to a string for display
+ */
+int data_to_ascii(unsigned char *datap, int len, int type, char *ascii, int ascii_max)
+{
+ unsigned char *asciip;
+ int i;
+
+ switch (type) {
+ case REG_TYPE_REGSZ:
+ if (verbose) fprintf(stderr, "Len: %d\n", len);
+ return uni_to_ascii(datap, ascii, len, ascii_max);
+ break;
+
+ case REG_TYPE_EXPANDSZ:
+ return uni_to_ascii(datap, ascii, len, ascii_max);
+ break;
+
+ case REG_TYPE_BIN:
+ asciip = ascii;
+ for (i=0; (i<len)&&(i+1)*3<ascii_max; i++) {
+ int str_rem = ascii_max - ((int)asciip - (int)ascii);
+ asciip += snprintf(asciip, str_rem, "%02x", *(unsigned char *)(datap+i));
+ if (i < len && str_rem > 0)
+ *asciip = ' '; asciip++;
+ }
+ *asciip = '\0';
+ return ((int)asciip - (int)ascii);
+ break;
+
+ case REG_TYPE_DWORD:
+ if (*(int *)datap == 0)
+ return snprintf(ascii, ascii_max, "0");
+ else
+ return snprintf(ascii, ascii_max, "0x%x", *(int *)datap);
+ break;
+
+ case REG_TYPE_MULTISZ:
+
+ break;
+
+ default:
+ return 0;
+ break;
+ }
+
+ return len;
+
+}
+
+REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent);
+
+int nt_set_regf_input_file(REGF *regf, char *filename)
+{
+ return ((regf->regfile_name = strdup(filename)) != NULL);
+}
+
+int nt_set_regf_output_file(REGF *regf, char *filename)
+{
+ return ((regf->outfile_name = strdup(filename)) != NULL);
+}
+
+/* Create a regf structure and init it */
+
+REGF *nt_create_regf(void)
+{
+ REGF *tmp = (REGF *)malloc(sizeof(REGF));
+ if (!tmp) return tmp;
+ bzero(tmp, sizeof(REGF));
+ return tmp;
+}
+
+/* Free all the bits and pieces ... Assumes regf was malloc'd */
+/* If you add stuff to REGF, add the relevant free bits here */
+int nt_free_regf(REGF *regf)
+{
+ if (!regf) return 0;
+
+ if (regf->regfile_name) free(regf->regfile_name);
+ if (regf->outfile_name) free(regf->outfile_name);
+
+ /* Free the mmap'd area */
+
+ if (regf->base) munmap(regf->base, regf->sbuf.st_size);
+ regf->base = NULL;
+ close(regf->fd); /* Ignore the error :-) */
+
+ nt_delete_reg_key(regf->root, False); /* Free the tree */
+ free(regf->sk_map);
+ regf->sk_count = regf->sk_map_size = 0;
+
+ free(regf);
+
+ return 1;
+}
+
+/* Get the header of the registry. Return a pointer to the structure
+ * If the mmap'd area has not been allocated, then mmap the input file
+ */
+REGF_HDR *nt_get_regf_hdr(REGF *regf)
+{
+ if (!regf)
+ return NULL; /* What about errors */
+
+ if (!regf->regfile_name)
+ return NULL; /* What about errors */
+
+ if (!regf->base) { /* Try to mmap etc the file */
+
+ if ((regf->fd = open(regf->regfile_name, O_RDONLY, 0000)) <0) {
+ return NULL; /* What about errors? */
+ }
+
+ if (fstat(regf->fd, &regf->sbuf) < 0) {
+ return NULL;
+ }
+
+ regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
+
+ if ((int)regf->base == 1) {
+ fprintf(stderr, "Could not mmap file: %s, %s\n", regf->regfile_name,
+ strerror(errno));
+ return NULL;
+ }
+ }
+
+ /*
+ * At this point, regf->base != NULL, and we should be able to read the
+ * header
+ */
+
+ assert(regf->base != NULL);
+
+ return (REGF_HDR *)regf->base;
+}
+
+/*
+ * Validate a regf header
+ * For now, do nothing, but we should check the checksum
+ */
+int valid_regf_hdr(REGF_HDR *regf_hdr)
+{
+ if (!regf_hdr) return 0;
+
+ return 1;
+}
+
+/*
+ * Process an SK header ...
+ * Every time we see a new one, add it to the map. Otherwise, just look it up.
+ * We will do a simple linear search for the moment, since many KEYs have the
+ * same security descriptor.
+ * We allocate the map in increments of 10 entries.
+ */
+
+/*
+ * Create a new entry in the map, and increase the size of the map if needed
+ */
+
+SK_MAP *alloc_sk_map_entry(REGF *regf, KEY_SEC_DESC *tmp, int sk_off)
+{
+ if (!regf->sk_map) { /* Allocate a block of 10 */
+ regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
+ if (!regf->sk_map) {
+ free(tmp);
+ return NULL;
+ }
+ regf->sk_map_size = 10;
+ regf->sk_count = 1;
+ (regf->sk_map)[0].sk_off = sk_off;
+ (regf->sk_map)[0].key_sec_desc = tmp;
+ }
+ else { /* Simply allocate a new slot, unless we have to expand the list */
+ int ndx = regf->sk_count;
+ if (regf->sk_count >= regf->sk_map_size) {
+ regf->sk_map = (SK_MAP *)realloc(regf->sk_map,
+ (regf->sk_map_size + 10)*sizeof(SK_MAP));
+ if (!regf->sk_map) {
+ free(tmp);
+ return NULL;
+ }
+ /*
+ * ndx already points at the first entry of the new block
+ */
+ regf->sk_map_size += 10;
+ }
+ (regf->sk_map)[ndx].sk_off = sk_off;
+ (regf->sk_map)[ndx].key_sec_desc = tmp;
+ regf->sk_count++;
+ }
+ return regf->sk_map;
+}
+
+/*
+ * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
+ * found
+ */
+
+KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
+{
+ int i;
+
+ if (!sk_map) return NULL;
+
+ for (i = 0; i < count; i++) {
+
+ if (sk_map[i].sk_off == sk_off)
+ return sk_map[i].key_sec_desc;
+
+ }
+
+ return NULL;
+
+}
+
+/*
+ * Allocate a KEY_SEC_DESC if we can't find one in the map
+ */
+
+KEY_SEC_DESC *lookup_create_sec_key(REGF *regf, SK_MAP *sk_map, int sk_off)
+{
+ KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
+
+ if (tmp) {
+ return tmp;
+ }
+ else { /* Allocate a new one */
+ tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+ if (!tmp) {
+ return NULL;
+ }
+ tmp->state = SEC_DESC_RES;
+ if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
+ return NULL;
+ }
+ return tmp;
+ }
+}
+
+/*
+ * Allocate storage and duplicate a SID
+ * We could allocate the SID to be only the size needed, but I am too lazy.
+ */
+DOM_SID *dup_sid(DOM_SID *sid)
+{
+ DOM_SID *tmp = (DOM_SID *)malloc(sizeof(DOM_SID));
+ int i;
+
+ if (!tmp) return NULL;
+ tmp->ver = sid->ver;
+ tmp->auths = sid->auths;
+ for (i=0; i<6; i++) {
+ tmp->auth[i] = sid->auth[i];
+ }
+ for (i=0; i<tmp->auths&&i<MAXSUBAUTHS; i++) {
+ tmp->sub_auths[i] = sid->sub_auths[i];
+ }
+ return tmp;
+}
+
+/*
+ * Allocate space for an ACE and duplicate the registry encoded one passed in
+ */
+ACE *dup_ace(REG_ACE *ace)
+{
+ ACE *tmp = NULL;
+
+ tmp = (ACE *)malloc(sizeof(ACE));
+
+ if (!tmp) return NULL;
+
+ tmp->type = CVAL(&ace->type);
+ tmp->flags = CVAL(&ace->flags);
+ tmp->perms = IVAL(&ace->perms);
+ tmp->trustee = dup_sid(&ace->trustee);
+ return tmp;
+}
+
+/*
+ * Allocate space for an ACL and duplicate the registry encoded one passed in
+ */
+ACL *dup_acl(REG_ACL *acl)
+{
+ ACL *tmp = NULL;
+ REG_ACE* ace;
+ int i, num_aces;
+
+ num_aces = IVAL(&acl->num_aces);
+
+ tmp = (ACL *)malloc(sizeof(ACL) + (num_aces - 1)*sizeof(ACE *));
+ if (!tmp) return NULL;
+
+ tmp->num_aces = num_aces;
+ tmp->refcnt = 1;
+ tmp->rev = SVAL(&acl->rev);
+ ace = (REG_ACE *)&acl->aces;
+ for (i=0; i<num_aces; i++) {
+ tmp->aces[i] = dup_ace(ace);
+ ace = (REG_ACE *)((char *)ace + SVAL(&ace->length));
+ /* XXX: FIXME, should handle malloc errors */
+ }
+
+ return tmp;
+}
+
+SEC_DESC *process_sec_desc(REGF *regf, REG_SEC_DESC *sec_desc)
+{
+ SEC_DESC *tmp = NULL;
+
+ tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
+
+ if (!tmp) {
+ return NULL;
+ }
+
+ tmp->rev = SVAL(&sec_desc->rev);
+ tmp->type = SVAL(&sec_desc->type);
+ tmp->owner = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->owner_off)));
+ if (!tmp->owner) {
+ free(tmp);
+ return NULL;
+ }
+ tmp->group = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->group_off)));
+ if (!tmp->group) {
+ free(tmp);
+ return NULL;
+ }
+
+ /* Now pick up the SACL and DACL */
+
+ if (sec_desc->sacl_off)
+ tmp->sacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->sacl_off)));
+ else
+ tmp->sacl = NULL;
+
+ if (sec_desc->dacl_off)
+ tmp->dacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->dacl_off)));
+ else
+ tmp->dacl = NULL;
+
+ return tmp;
+}
+
+KEY_SEC_DESC *process_sk(REGF *regf, SK_HDR *sk_hdr, int sk_off, int size)
+{
+ KEY_SEC_DESC *tmp = NULL;
+ int sk_next_off, sk_prev_off, sk_size;
+ REG_SEC_DESC *sec_desc;
+
+ if (!sk_hdr) return NULL;
+
+ if (SVAL(&sk_hdr->SK_ID) != REG_SK_ID) {
+ fprintf(stderr, "Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
+ regf->regfile_name);
+ return NULL;
+ }
+
+ if (-size < (sk_size = IVAL(&sk_hdr->rec_size))) {
+ fprintf(stderr, "Incorrect SK record size: %d vs %d. %s\n",
+ -size, sk_size, regf->regfile_name);
+ return NULL;
+ }
+
+ /*
+ * Now, we need to look up the SK Record in the map, and return it
+ * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
+ * use that
+ */
+
+ if (regf->sk_map &&
+ ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
+ && (tmp->state == SEC_DESC_OCU)) {
+ tmp->ref_cnt++;
+ return tmp;
+ }
+
+ /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
+
+ assert(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
+
+ /*
+ * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
+ * new KEY_SEC_DESC to the mapping structure, since the offset supplied is
+ * the actual offset of structure. The same offset will be used by
+ * all future references to this structure
+ * We could put all this unpleasantness in a function.
+ */
+
+ if (!tmp) {
+ tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+ if (!tmp) return NULL;
+ bzero(tmp, sizeof(KEY_SEC_DESC));
+
+ /*
+ * Allocate an entry in the SK_MAP ...
+ * We don't need to free tmp, because that is done for us if the
+ * sm_map entry can't be expanded when we need more space in the map.
+ */
+
+ if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
+ return NULL;
+ }
+ }
+
+ tmp->ref_cnt++;
+ tmp->state = SEC_DESC_OCU;
+
+ /*
+ * Now, process the actual sec desc and plug the values in
+ */
+
+ sec_desc = (REG_SEC_DESC *)&sk_hdr->sec_desc[0];
+ tmp->sec_desc = process_sec_desc(regf, sec_desc);
+
+ /*
+ * Now forward and back links. Here we allocate an entry in the sk_map
+ * if it does not exist, and mark it reserved
+ */
+
+ sk_prev_off = IVAL(&sk_hdr->prev_off);
+ tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
+ assert(tmp->prev != NULL);
+ sk_next_off = IVAL(&sk_hdr->next_off);
+ tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
+ assert(tmp->next != NULL);
+
+ return tmp;
+}
+
+/*
+ * Process a VK header and return a value
+ */
+VAL_KEY *process_vk(REGF *regf, VK_HDR *vk_hdr, int size)
+{
+ char val_name[1024];
+ int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
+ const char *val_type;
+ VAL_KEY *tmp = NULL;
+
+ if (!vk_hdr) return NULL;
+
+ if ((vk_id = SVAL(&vk_hdr->VK_ID)) != REG_VK_ID) {
+ fprintf(stderr, "Unrecognized VK header ID: %0X, block: %0X, %s\n",
+ vk_id, (int)vk_hdr, regf->regfile_name);
+ return NULL;
+ }
+
+ nam_len = SVAL(&vk_hdr->nam_len);
+ val_name[nam_len] = '\0';
+ flag = SVAL(&vk_hdr->flag);
+ dat_type = IVAL(&vk_hdr->dat_type);
+ dat_len = IVAL(&vk_hdr->dat_len); /* If top bit, offset contains data */
+ dat_off = IVAL(&vk_hdr->dat_off);
+
+ tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
+ if (!tmp) {
+ goto error;
+ }
+ bzero(tmp, sizeof(VAL_KEY));
+ tmp->has_name = flag;
+ tmp->data_type = dat_type;
+
+ if (flag & 0x01) {
+ strncpy(val_name, vk_hdr->dat_name, nam_len);
+ tmp->name = strdup(val_name);
+ if (!tmp->name) {
+ goto error;
+ }
+ }
+ else
+ strncpy(val_name, "<No Name>", 10);
+
+ /*
+ * Allocate space and copy the data as a BLOB
+ */
+
+ if (dat_len) {
+
+ char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
+
+ if (!dtmp) {
+ goto error;
+ }
+
+ tmp->data_blk = dtmp;
+
+ if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
+ char *dat_ptr = LOCN(regf->base, dat_off);
+ bcopy(dat_ptr, dtmp, dat_len);
+ }
+ else { /* The data is in the offset */
+ dat_len = dat_len & 0x7FFFFFFF;
+ bcopy(&dat_off, dtmp, dat_len);
+ }
+
+ tmp->data_len = dat_len;
+ }
+
+ val_type = val_to_str(dat_type, reg_type_names);
+
+ /*
+ * We need to save the data area as well
+ */
+
+ if (verbose) fprintf(stdout, " %s : %s : \n", val_name, val_type);
+
+ return tmp;
+
+ error:
+ /* XXX: FIXME, free the partially allocated struct */
+ return NULL;
+
+}
+
+/*
+ * Process a VL Header and return a list of values
+ */
+VAL_LIST *process_vl(REGF *regf, VL_TYPE vl, int count, int size)
+{
+ int i, vk_off;
+ VK_HDR *vk_hdr;
+ VAL_LIST *tmp = NULL;
+
+ if (!vl) return NULL;
+
+ if (-size < (count+1)*sizeof(int)){
+ fprintf(stderr, "Error in VL header format. Size less than space required. %d\n", -size);
+ return NULL;
+ }
+
+ tmp = (VAL_LIST *)malloc(sizeof(VAL_LIST) + (count - 1) * sizeof(VAL_KEY *));
+ if (!tmp) {
+ goto error;
+ }
+
+ for (i=0; i<count; i++) {
+ vk_off = IVAL(&vl[i]);
+ vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
+ tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr));
+ if (!tmp->vals[i]){
+ goto error;
+ }
+ }
+
+ tmp->val_count = count;
+
+ return tmp;
+
+ error:
+ /* XXX: FIXME, free the partially allocated structure */
+ return NULL;
+}
+
+/*
+ * Process an LF Header and return a list of sub-keys
+ */
+KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size, REG_KEY *parent)
+{
+ int count, i, nk_off;
+ unsigned int lf_id;
+ KEY_LIST *tmp;
+
+ if (!lf_hdr) return NULL;
+
+ if ((lf_id = SVAL(&lf_hdr->LF_ID)) != REG_LF_ID) {
+ fprintf(stderr, "Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
+ lf_id, (int)lf_hdr, regf->regfile_name);
+ return NULL;
+ }
+
+ assert(size < 0);
+
+ count = SVAL(&lf_hdr->key_count);
+
+ if (count <= 0) return NULL;
+
+ /* Now, we should allocate a KEY_LIST struct and fill it in ... */
+
+ tmp = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (count - 1) * sizeof(REG_KEY *));
+ if (!tmp) {
+ goto error;
+ }
+
+ tmp->key_count = count;
+ tmp->max_keys = count;
+
+ for (i=0; i<count; i++) {
+ NK_HDR *nk_hdr;
+
+ nk_off = IVAL(&lf_hdr->hr[i].nk_off);
+ nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
+ tmp->keys[i] = nt_get_key_tree(regf, nk_hdr, BLK_SIZE(nk_hdr), parent);
+ if (!tmp->keys[i]) {
+ goto error;
+ }
+ }
+
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_key_list(tmp, False);
+ return NULL;
+}
+
+/*
+ * This routine is passed a NK_HDR pointer and retrieves the entire tree
+ * from there down. It returns a REG_KEY *.
+ */
+REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent)
+{
+ REG_KEY *tmp = NULL, *own;
+ int name_len, clsname_len, lf_off, val_off, val_count, sk_off, own_off;
+ unsigned int nk_id;
+ LF_HDR *lf_hdr;
+ VL_TYPE *vl;
+ SK_HDR *sk_hdr;
+ char key_name[1024], cls_name[1024];
+
+ if (!nk_hdr) return NULL;
+
+ if ((nk_id = SVAL(&nk_hdr->NK_ID)) != REG_NK_ID) {
+ fprintf(stderr, "Unrecognized NK Header format: %08X, Block: %0X. %s\n",
+ nk_id, (int)nk_hdr, regf->regfile_name);
+ return NULL;
+ }
+
+ assert(size < 0);
+
+ name_len = SVAL(&nk_hdr->nam_len);
+ clsname_len = SVAL(&nk_hdr->clsnam_len);
+
+ /*
+ * The value of -size should be ge
+ * (sizeof(NK_HDR) - 1 + name_len)
+ * The -1 accounts for the fact that we included the first byte of
+ * the name in the structure. clsname_len is the length of the thing
+ * pointed to by clsnam_off
+ */
+
+ if (-size < (sizeof(NK_HDR) - 1 + name_len)) {
+ fprintf(stderr, "Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr);
+ fprintf(stderr, "Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
+ sizeof(NK_HDR), name_len, clsname_len);
+ /*return NULL;*/
+ }
+
+ if (verbose) fprintf(stdout, "NK HDR: Name len: %d, class name len: %d\n",
+ name_len, clsname_len);
+
+ /* Fish out the key name and process the LF list */
+
+ assert(name_len < sizeof(key_name));
+
+ /* Allocate the key struct now */
+ tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
+ if (!tmp) return tmp;
+ bzero(tmp, sizeof(REG_KEY));
+
+ tmp->type = (SVAL(&nk_hdr->type)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
+
+ strncpy(key_name, nk_hdr->key_nam, name_len);
+ key_name[name_len] = '\0';
+
+ if (verbose) fprintf(stdout, "Key name: %s\n", key_name);
+
+ tmp->name = strdup(key_name);
+ if (!tmp->name) {
+ goto error;
+ }
+
+ /*
+ * Fish out the class name, it is in UNICODE, while the key name is
+ * ASCII :-)
+ */
+
+ if (clsname_len) { /* Just print in Ascii for now */
+ char *clsnamep;
+ int clsnam_off;
+
+ clsnam_off = IVAL(&nk_hdr->clsnam_off);
+ clsnamep = LOCN(regf->base, clsnam_off);
+
+ bzero(cls_name, clsname_len);
+ uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len);
+
+ /*
+ * I am keeping class name as an ascii string for the moment.
+ * That means it needs to be converted on output.
+ * It will also piss off people who need Unicode/UTF-8 strings. Sorry.
+ * XXX: FIXME
+ */
+
+ tmp->class_name = strdup(cls_name);
+ if (!tmp->class_name) {
+ goto error;
+ }
+
+ if (verbose) fprintf(stdout, " Class Name: %s\n", cls_name);
+
+ }
+
+ /*
+ * Process the owner offset ...
+ */
+
+ own_off = IVAL(&nk_hdr->own_off);
+ own = (REG_KEY *)LOCN(regf->base, own_off);
+
+ if (verbose) fprintf(stdout, " Owner offset: %0X, Our Offset: %0X\n",
+ own, nk_hdr);
+
+ /*
+ * We should verify that the owner field is correct ...
+ * for now, we don't worry ...
+ */
+
+ tmp->owner = parent;
+
+ /*
+ * If there are any values, process them here
+ */
+
+ val_count = IVAL(&nk_hdr->val_cnt);
+
+ if (val_count) {
+
+ val_off = IVAL(&nk_hdr->val_off);
+ vl = (VL_TYPE *)LOCN(regf->base, val_off);
+
+ tmp->values = process_vl(regf, *vl, val_count, BLK_SIZE(vl));
+ if (!tmp->values) {
+ goto error;
+ }
+
+ }
+
+ /*
+ * Also handle the SK header ...
+ */
+
+ sk_off = IVAL(&nk_hdr->sk_off);
+ sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
+
+ if (sk_off != -1) {
+
+ tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
+
+ }
+
+ lf_off = IVAL(&nk_hdr->lf_off);
+
+ /*
+ * No more subkeys if lf_off == -1
+ */
+
+ if (lf_off != -1) {
+
+ lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
+
+ tmp->sub_keys = process_lf(regf, lf_hdr, BLK_SIZE(lf_hdr), tmp);
+ if (!tmp->sub_keys){
+ goto error;
+ }
+
+ }
+
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_reg_key(tmp, False);
+ return NULL;
+}
+
+int nt_load_registry(REGF *regf)
+{
+ REGF_HDR *regf_hdr;
+ unsigned int regf_id, hbin_id;
+ HBIN_HDR *hbin_hdr;
+ NK_HDR *first_key;
+
+ /* Get the header */
+
+ if ((regf_hdr = nt_get_regf_hdr(regf)) == NULL) {
+ return -1;
+ }
+
+ /* Now process that header and start to read the rest in */
+
+ if ((regf_id = IVAL(&regf_hdr->REGF_ID)) != REG_REGF_ID) {
+ fprintf(stderr, "Unrecognized NT registry header id: %0X, %s\n",
+ regf_id, regf->regfile_name);
+ return -1;
+ }
+
+ /*
+ * Validate the header ...
+ */
+ if (!valid_regf_hdr(regf_hdr)) {
+ fprintf(stderr, "Registry file header does not validate: %s\n",
+ regf->regfile_name);
+ return -1;
+ }
+
+ /* Update the last mod date, and then go get the first NK record and on */
+
+ TTTONTTIME(regf, IVAL(&regf_hdr->tim1), IVAL(&regf_hdr->tim2));
+
+ /*
+ * The hbin hdr seems to be just uninteresting garbage. Check that
+ * it is there, but that is all.
+ */
+
+ hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
+
+ if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID)) != REG_HBIN_ID) {
+ fprintf(stderr, "Unrecognized registry hbin hdr ID: %0X, %s\n",
+ hbin_id, regf->regfile_name);
+ return -1;
+ }
+
+ /*
+ * Get a pointer to the first key from the hreg_hdr
+ */
+
+ first_key = (NK_HDR *)LOCN(regf->base, IVAL(&regf_hdr->first_key));
+
+ /*
+ * Now, get the registry tree by processing that NK recursively
+ */
+
+ regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key), NULL);
+
+ assert(regf->root != NULL);
+
+ return 1;
+}
+
+/*
+ * Story the registry in the output file
+ */
+int nt_store_registry(REGF *regf)
+{
+
+ return 1;
+}
+
+/*
+ * Routines to parse a REGEDIT4 file
+ *
+ * The file consists of:
+ *
+ * REGEDIT4
+ * \[[-]key-path\]\n
+ * <value-spec>*
+ *
+ * Format:
+ * [cmd:]name=type:value
+ *
+ * cmd = a|d|c|add|delete|change|as|ds|cs
+ *
+ * There can be more than one key-path and value-spec.
+ *
+ * Since we want to support more than one type of file format, we
+ * construct a command-file structure that keeps info about the command file
+ */
+
+#define FMT_UNREC -1
+#define FMT_REGEDIT4 0
+#define FMT_EDITREG1_1 1
+
+#define FMT_STRING_REGEDIT4 "REGEDIT4"
+#define FMT_STRING_EDITREG1_0 "EDITREG1.0"
+
+#define CMD_NONE 0
+#define CMD_ADD_KEY 1
+#define CMD_DEL_KEY 2
+
+#define CMD_KEY 1
+#define CMD_VAL 2
+
+typedef struct val_spec_list {
+ struct val_spec_list *next;
+ char *name;
+ int type;
+ char *val; /* Kept as a char string, really? */
+} VAL_SPEC_LIST;
+
+typedef struct command_s {
+ int cmd;
+ char *key;
+ int val_count;
+ VAL_SPEC_LIST *val_spec_list, *val_spec_last;
+} CMD;
+
+typedef struct cmd_line {
+ int len, line_len;
+ char *line;
+} CMD_LINE;
+
+/*
+ * Some routines to handle lines of info in the command files
+ */
+void skip_to_eol(int fd)
+{
+ int rc;
+ char ch = 0;
+
+ while ((rc = read(fd, &ch, 1)) == 1) {
+ if (ch == 0x0A) return;
+ }
+ if (rc < 0) {
+ fprintf(stderr, "Could not read file descriptor: %d, %s\n",
+ fd, strerror(errno));
+ exit(1);
+ }
+}
+
+void free_cmd(CMD *cmd)
+{
+ if (!cmd) return;
+
+ while (cmd->val_spec_list) {
+ VAL_SPEC_LIST *tmp;
+
+ tmp = cmd->val_spec_list;
+ cmd->val_spec_list = tmp->next;
+ free(tmp);
+ }
+
+ free(cmd);
+
+}
+
+void free_cmd_line(CMD_LINE *cmd_line)
+{
+ if (cmd_line) {
+ if (cmd_line->line) free(cmd_line->line);
+ free(cmd_line);
+ }
+}
+
+void print_line(struct cmd_line *cl)
+{
+ char *pl;
+
+ if (!cl) return;
+
+ if ((pl = malloc(cl->line_len + 1)) == NULL) {
+ fprintf(stderr, "Unable to allocate space to print line: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ strncpy(pl, cl->line, cl->line_len);
+ pl[cl->line_len] = 0;
+
+ fprintf(stdout, "%s\n", pl);
+ free(pl);
+}
+
+#define INIT_ALLOC 10
+
+/*
+ * Read a line from the input file.
+ * NULL returned when EOF and no chars read
+ * Otherwise we return a cmd_line *
+ * Exit if other errors
+ */
+struct cmd_line *get_cmd_line(int fd)
+{
+ struct cmd_line *cl = (CMD_LINE *)malloc(sizeof(CMD_LINE));
+ int i = 0, rc;
+ unsigned char ch;
+
+ if (!cl) {
+ fprintf(stderr, "Unable to allocate structure for command line: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ cl->len = INIT_ALLOC;
+
+ /*
+ * Allocate some space for the line. We extend later if needed.
+ */
+
+ if ((cl->line = (char *)malloc(INIT_ALLOC)) == NULL) {
+ fprintf(stderr, "Unable to allocate initial space for line: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * Now read in the chars to EOL. Don't store the EOL in the
+ * line. What about CR?
+ */
+
+ while ((rc = read(fd, &ch, 1)) == 1 && ch != '\n') {
+ if (ch == '\r') continue; /* skip CR */
+ if (i == cl->len) {
+ /*
+ * Allocate some more memory
+ */
+ if ((cl->line = realloc(cl->line, cl->len + INIT_ALLOC)) == NULL) {
+ fprintf(stderr, "Unable to realloc space for line: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+ cl->len += INIT_ALLOC;
+ }
+ cl->line[i] = ch;
+ i++;
+ }
+
+ /* read 0 and we were at loc'n 0, return NULL */
+ if (rc == 0 && i == 0) {
+ free_cmd_line(cl);
+ return NULL;
+ }
+
+ cl->line_len = i;
+
+ return cl;
+
+}
+
+/*
+ * parse_value: parse out a value. We pull it apart as:
+ *
+ * <value> ::= <value-name>=<type>:<value-string>
+ *
+ * <value-name> ::= char-string-without-spaces | '"' char-string '"'
+ *
+ * If it parsed OK, return the <value-name> as a string, and the
+ * value type and value-string in parameters.
+ */
+
+char *dup_str(char *s, int len)
+{
+ char *nstr;
+ nstr = (char *)malloc(len + 1);
+ if (nstr) {
+ memcpy(nstr, s, len);
+ nstr[len] = 0;
+ }
+ return nstr;
+}
+
+char *parse_name(char *nstr)
+{
+ int len = 0, start = 0;
+ if (!nstr) return NULL;
+
+ len = strlen(nstr);
+
+ while (len && nstr[len - 1] == ' ') len--;
+
+ nstr[len] = 0; /* Trim any spaces ... if there were none, doesn't matter */
+
+ /*
+ * Beginning and end should be '"' or neither should be so
+ */
+ if ((nstr[0] == '"' && nstr[len - 1] != '"') ||
+ (nstr[0] != '"' && nstr[len - 1] == '"'))
+ return NULL;
+
+ if (nstr[0] == '"') {
+ start = 1;
+ len -= 2;
+ }
+
+ return dup_str(&nstr[start], len);
+}
+
+int parse_value_type(char *tstr)
+{
+ int len = strlen(tstr);
+
+ while (len && tstr[len - 1] == ' ') len--;
+ tstr[len] = 0;
+
+ if (strcmp(tstr, "REG_DWORD") == 0)
+ return REG_TYPE_DWORD;
+ else if (strcmp(tstr, "dword") == 0)
+ return REG_TYPE_DWORD;
+ else if (strcmp(tstr, "REG_EXPAND_SZ") == 0)
+ return REG_TYPE_EXPANDSZ;
+ else if (strcmp(tstr, "REG_BIN") == 0)
+ return REG_TYPE_BIN;
+ else if (strcmp(tstr, "REG_SZ") == 0)
+ return REG_TYPE_REGSZ;
+ else if (strcmp(tstr, "REG_MULTI_SZ") == 0)
+ return REG_TYPE_MULTISZ;
+
+ return 0;
+}
+
+char *parse_val_str(char *vstr)
+{
+
+ return dup_str(vstr, strlen(vstr));
+
+}
+
+char *parse_value(struct cmd_line *cl, int *vtype, char **val)
+{
+ char *p1 = NULL, *p2 = NULL, *nstr = NULL, *tstr = NULL, *vstr = NULL;
+
+ if (!cl || !vtype || !val) return NULL;
+ if (!cl->line_len) return NULL;
+
+ p1 = dup_str(cl->line, cl->line_len);
+ /* FIXME: Better return codes etc ... */
+ if (!p1) return NULL;
+ p2 = strchr(p1, '=');
+ if (!p2) return NULL;
+
+ *p2 = 0; p2++; /* Split into two strings at p2 */
+
+ /* Now, parse the name ... */
+
+ nstr = parse_name(p1);
+ if (!nstr) goto error;
+
+ /* Now, split the remainder and parse on type and val ... */
+
+ tstr = p2;
+ while (*tstr == ' ') tstr++; /* Skip leading white space */
+ p2 = strchr(p2, ':');
+
+ if (!p2) goto error;
+
+ *p2 = 0; p2++; /* split on the : */
+
+ *vtype = parse_value_type(tstr);
+
+ if (!vtype) goto error;
+
+ /* Now, parse the value string. It should return a newly malloc'd string */
+
+ while (*p2 == ' ') p2++; /* Skip leading space */
+ vstr = parse_val_str(p2);
+
+ if (!vstr) goto error;
+
+ *val = vstr;
+
+ return nstr;
+
+ error:
+ if (p1) free(p1);
+ if (nstr) free(nstr);
+ if (vstr) free(vstr);
+ return NULL;
+}
+
+/*
+ * Parse out a key. Look for a correctly formatted key [...]
+ * and whether it is a delete or add? A delete is signalled
+ * by a - in front of the key.
+ * Assumes that there are no leading and trailing spaces
+ */
+
+char *parse_key(struct cmd_line *cl, int *cmd)
+{
+ int start = 1;
+ char *tmp;
+
+ if (cl->line[0] != '[' ||
+ cl->line[cl->line_len - 1] != ']') return NULL;
+ if (cl->line_len == 2) return NULL;
+ *cmd = CMD_ADD_KEY;
+ if (cl->line[1] == '-') {
+ if (cl->line_len == 3) return NULL;
+ start = 2;
+ *cmd = CMD_DEL_KEY;
+ }
+ tmp = malloc(cl->line_len - 1 - start + 1);
+ if (!tmp) return tmp; /* Bail out on no mem ... FIXME */
+ strncpy(tmp, &cl->line[start], cl->line_len - 1 - start);
+ tmp[cl->line_len - 1 - start] = 0;
+ return tmp;
+}
+
+/*
+ * Parse a line to determine if we have a key or a value
+ * We only check for key or val ...
+ */
+
+int parse_line(struct cmd_line *cl)
+{
+
+ if (!cl || cl->len == 0) return 0;
+
+ if (cl->line[0] == '[') /* No further checking for now */
+ return CMD_KEY;
+ else
+ return CMD_VAL;
+}
+
+/*
+ * We seek to offset 0, read in the required number of bytes,
+ * and compare to the correct value.
+ * We then seek back to the original location
+ */
+int regedit4_file_type(int fd)
+{
+ int cur_ofs = 0;
+ char desc[9];
+
+ cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
+ if (cur_ofs < 0) {
+ fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
+ exit(1); /* FIXME */
+ }
+
+ if (cur_ofs) {
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ if (read(fd, desc, 8) < 8) {
+ fprintf(stderr, "Unable to read command file format\n");
+ exit(2); /* FIXME */
+ }
+
+ desc[8] = 0;
+
+ if (strcmp(desc, FMT_STRING_REGEDIT4) == 0) {
+ if (cur_ofs) {
+ lseek(fd, cur_ofs, SEEK_SET);
+ }
+ else {
+ skip_to_eol(fd);
+ }
+ return FMT_REGEDIT4;
+ }
+
+ return FMT_UNREC;
+}
+
+/*
+ * Run though the data in the line and strip anything after a comment
+ * char.
+ */
+void strip_comment(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = 0; i < cl->line_len; i++) {
+ if (cl->line[i] == ';') {
+ cl->line_len = i;
+ return;
+ }
+ }
+}
+
+/*
+ * trim leading space
+ */
+
+void trim_leading_spaces(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = 0; i < cl->line_len; i++) {
+ if (cl->line[i] != ' '){
+ if (i) memcpy(cl->line, &cl->line[i], cl->line_len - i);
+ return;
+ }
+ }
+}
+
+/*
+ * trim trailing spaces
+ */
+void trim_trailing_spaces(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = cl->line_len; i == 0; i--) {
+ if (cl->line[i-1] != ' ' &&
+ cl->line[i-1] != '\t') {
+ cl->line_len = i;
+ }
+ }
+}
+
+/*
+ * Get a command ... This consists of possibly multiple lines:
+ * [key]
+ * values*
+ * possibly Empty line
+ *
+ * value ::= <value-name>=<value-type>':'<value-string>
+ * <value-name> is some path, possibly enclosed in quotes ...
+ * We alctually look for the next key to terminate a previous key
+ */
+CMD *regedit4_get_cmd(int fd)
+{
+ struct command_s *cmd = NULL;
+ struct cmd_line *cl = NULL;
+ struct val_spec_list *vl = NULL;
+
+ if ((cmd = (struct command_s *)malloc(sizeof(struct command_s))) == NULL) {
+ fprintf(stderr, "Unable to malloc space for command: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ cmd->cmd = CMD_NONE;
+ cmd->key = NULL;
+ cmd->val_spec_list = cmd->val_spec_last = NULL;
+ while (cl = get_cmd_line(fd)) {
+
+ strip_comment(cl); /* remove anything beyond a comment char */
+ trim_trailing_spaces(cl);
+ trim_leading_spaces(cl);
+
+ if (cl->line_len == 0) { /* An empty line */
+ free_cmd_line(cl);
+ }
+ else { /* Else, non-empty ... */
+ /*
+ * Parse out the bits ...
+ */
+ switch (parse_line(cl)) {
+ case CMD_KEY:
+ if ((cmd->key = parse_key(cl, &cmd->cmd)) == NULL) {
+ fprintf(stderr, "Error parsing key from line: ");
+ print_line(cl);
+ fprintf(stderr, "\n");
+ }
+ break;
+
+ case CMD_VAL:
+ /*
+ * We need to add the value stuff to the list
+ * There could be a \ on the end which we need to
+ * handle at some time
+ */
+ vl = (struct val_spec_list *)malloc(sizeof(struct val_spec_list));
+ if (!vl) goto error;
+ vl->next = NULL;
+ vl->name = parse_value(cl, &vl->type, &vl->val);
+ if (!vl->name) goto error;
+ if (cmd->val_spec_list == NULL) {
+ cmd->val_spec_list = cmd->val_spec_last = vl;
+ }
+ else {
+ cmd->val_spec_last->next = vl;
+ cmd->val_spec_last = vl;
+ }
+ cmd->val_count++;
+ break;
+
+ default:
+ fprintf(stderr, "Unrecognized line in command file: \n");
+ print_line(cl);
+ break;
+ }
+ }
+
+ }
+ if (!cmd->cmd) goto error; /* End of file ... */
+
+ return cmd;
+
+ error:
+ if (vl) free(vl);
+ if (cmd) free_cmd(cmd);
+ return NULL;
+}
+
+int regedit4_exec_cmd(CMD *cmd)
+{
+
+ return 0;
+}
+
+int editreg_1_0_file_type(int fd)
+{
+ int cur_ofs = 0;
+ char desc[11];
+
+ cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
+ if (cur_ofs < 0) {
+ fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
+ exit(1); /* FIXME */
+ }
+
+ if (cur_ofs) {
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ if (read(fd, desc, 10) < 10) {
+ fprintf(stderr, "Unable to read command file format\n");
+ exit(2); /* FIXME */
+ }
+
+ desc[10] = 0;
+
+ if (strcmp(desc, FMT_STRING_EDITREG1_0) == 0) {
+ lseek(fd, cur_ofs, SEEK_SET);
+ return FMT_REGEDIT4;
+ }
+
+ return FMT_UNREC;
+}
+
+CMD *editreg_1_0_get_cmd(int fd)
+{
+ return NULL;
+}
+
+int editreg_1_0_exec_cmd(CMD *cmd)
+{
+
+ return -1;
+}
+
+typedef struct command_ops_s {
+ int type;
+ int (*file_type)(int fd);
+ CMD *(*get_cmd)(int fd);
+ int (*exec_cmd)(CMD *cmd);
+} CMD_OPS;
+
+CMD_OPS default_cmd_ops[] = {
+ {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd},
+ {1, editreg_1_0_file_type, editreg_1_0_get_cmd, editreg_1_0_exec_cmd},
+ {-1, NULL, NULL, NULL}
+};
+
+typedef struct command_file_s {
+ char *name;
+ int type, fd;
+ CMD_OPS cmd_ops;
+} CMD_FILE;
+
+/*
+ * Create a new command file structure
+ */
+
+CMD_FILE *cmd_file_create(char *file)
+{
+ CMD_FILE *tmp;
+ struct stat sbuf;
+ int i = 0;
+
+ /*
+ * Let's check if the file exists ...
+ * No use creating the cmd_file structure if the file does not exist
+ */
+
+ if (stat(file, &sbuf) < 0) { /* Not able to access file */
+
+ return NULL;
+ }
+
+ tmp = (CMD_FILE *)malloc(sizeof(CMD_FILE));
+ if (!tmp) {
+ return NULL;
+ }
+
+ /*
+ * Let's fill in some of the fields;
+ */
+
+ tmp->name = strdup(file);
+
+ if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) {
+ free(tmp);
+ return NULL;
+ }
+
+ /*
+ * Now, try to find the format by indexing through the table
+ */
+ while (default_cmd_ops[i].type != -1) {
+ if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) {
+ tmp->cmd_ops = default_cmd_ops[i];
+ return tmp;
+ }
+ i++;
+ }
+
+ /*
+ * If we got here, return NULL, as we could not figure out the type
+ * of command file.
+ *
+ * What about errors?
+ */
+
+ free(tmp);
+ return NULL;
+}
+
+/*
+ * Extract commands from the command file, and execute them.
+ * We pass a table of command callbacks for that
+ */
+
+/*
+ * Main code from here on ...
+ */
+
+/*
+ * key print function here ...
+ */
+
+int print_key(const char *path, char *name, char *class_name, int root,
+ int terminal, int vals)
+{
+
+ /*if (terminal)*/ fprintf(stdout, "[%s%s]\n", path, name);
+
+ return 1;
+}
+
+/*
+ * Sec Desc print functions
+ */
+
+void print_type(unsigned char type)
+{
+ switch (type) {
+ case 0x00:
+ fprintf(stdout, " ALLOW");
+ break;
+ case 0x01:
+ fprintf(stdout, " DENY");
+ break;
+ case 0x02:
+ fprintf(stdout, " AUDIT");
+ break;
+ case 0x03:
+ fprintf(stdout, " ALARM");
+ break;
+ case 0x04:
+ fprintf(stdout, "ALLOW CPD");
+ break;
+ case 0x05:
+ fprintf(stdout, "OBJ ALLOW");
+ break;
+ case 0x06:
+ fprintf(stdout, " OBJ DENY");
+ default:
+ fprintf(stdout, " UNKNOWN");
+ break;
+ }
+}
+
+void print_flags(unsigned char flags)
+{
+ char flg_output[21];
+ int some = 0;
+
+ flg_output[0] = 0;
+ if (!flags) {
+ fprintf(stdout, " ");
+ return;
+ }
+ if (flags & 0x01) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "OI");
+ }
+ if (flags & 0x02) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "CI");
+ }
+ if (flags & 0x04) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "NP");
+ }
+ if (flags & 0x08) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "IO");
+ }
+ if (flags & 0x10) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "IA");
+ }
+ if (flags == 0xF) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "VI");
+ }
+ fprintf(stdout, " %s", flg_output);
+}
+
+void print_perms(int perms)
+{
+ fprintf(stdout, " %8X", perms);
+}
+
+void print_sid(DOM_SID *sid)
+{
+ int i, comps = sid->auths;
+ fprintf(stdout, "S-%u-%u", sid->ver, sid->auth[5]);
+
+ for (i = 0; i < comps; i++) {
+
+ fprintf(stdout, "-%u", sid->sub_auths[i]);
+
+ }
+ fprintf(stdout, "\n");
+}
+
+void print_acl(ACL *acl, char *prefix)
+{
+ int i;
+
+ for (i = 0; i < acl->num_aces; i++) {
+ fprintf(stdout, ";;%s", prefix);
+ print_type(acl->aces[i]->type);
+ print_flags(acl->aces[i]->flags);
+ print_perms(acl->aces[i]->perms);
+ fprintf(stdout, " ");
+ print_sid(acl->aces[i]->trustee);
+ }
+}
+
+int print_sec(SEC_DESC *sec_desc)
+{
+ if (!print_security) return 1;
+ fprintf(stdout, ";; SECURITY\n");
+ fprintf(stdout, ";; Owner: ");
+ print_sid(sec_desc->owner);
+ fprintf(stdout, ";; Group: ");
+ print_sid(sec_desc->group);
+ if (sec_desc->sacl) {
+ fprintf(stdout, ";; SACL:\n");
+ print_acl(sec_desc->sacl, " ");
+ }
+ if (sec_desc->dacl) {
+ fprintf(stdout, ";; DACL:\n");
+ print_acl(sec_desc->dacl, " ");
+ }
+ return 1;
+}
+
+/*
+ * Value print function here ...
+ */
+int print_val(const char *path, char *val_name, int val_type, int data_len,
+ void *data_blk, int terminal, int first, int last)
+{
+ char data_asc[1024];
+
+ bzero(data_asc, sizeof(data_asc));
+ if (!terminal && first)
+ fprintf(stdout, "%s\n", path);
+ data_to_ascii((unsigned char *)data_blk, data_len, val_type, data_asc,
+ sizeof(data_asc) - 1);
+ fprintf(stdout, " %s = %s : %s\n", (val_name?val_name:"<No Name>"),
+ val_to_str(val_type, reg_type_names), data_asc);
+ return 1;
+}
+
+void usage(void)
+{
+ fprintf(stderr, "Usage: editreg [-v] [-p] [-k] [-s] [-c <command-file>] <registryfile>\n");
+ fprintf(stderr, "Version: 0.1\n\n");
+ fprintf(stderr, "\n\t-v\t sets verbose mode");
+ fprintf(stderr, "\n\t-p\t prints the registry");
+ fprintf(stderr, "\n\t-s\t prints security descriptors");
+ fprintf(stderr, "\n\t-c <command-file>\t specifies a command file");
+ fprintf(stderr, "\n");
+}
+
+int main(int argc, char *argv[])
+{
+ REGF *regf;
+ extern char *optarg;
+ extern int optind;
+ int opt, print_keys = 0;
+ int regf_opt = 1; /* Command name */
+ int commands = 0;
+ char *cmd_file_name = NULL;
+ char *out_file_name = NULL;
+ CMD_FILE *cmd_file = NULL;
+
+ if (argc < 2) {
+ usage();
+ exit(1);
+ }
+
+ /*
+ * Now, process the arguments
+ */
+
+ while ((opt = getopt(argc, argv, "spvko:c:")) != EOF) {
+ switch (opt) {
+ case 'c':
+ commands = 1;
+ cmd_file_name = optarg;
+ regf_opt += 2;
+ break;
+
+ case 'o':
+ out_file_name = optarg;
+ regf_opt += 2;
+ break;
+
+ case 'p':
+ print_keys++;
+ regf_opt++;
+ break;
+
+ case 's':
+ print_security++;
+ regf_opt++;
+ break;
+
+ case 'v':
+ verbose++;
+ regf_opt++;
+ break;
+
+ case 'k':
+ regf_opt++;
+ break;
+
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ }
+
+ if ((regf = nt_create_regf()) == NULL) {
+ fprintf(stderr, "Could not create registry object: %s\n", strerror(errno));
+ exit(2);
+ }
+
+ if (regf_opt < argc) { /* We have a registry file */
+ if (!nt_set_regf_input_file(regf, argv[regf_opt])) {
+ fprintf(stderr, "Could not set name of registry file: %s, %s\n",
+ argv[regf_opt], strerror(errno));
+ exit(3);
+ }
+
+ /* Now, open it, and bring it into memory :-) */
+
+ if (nt_load_registry(regf) < 0) {
+ fprintf(stderr, "Could not load registry: %s\n", argv[1]);
+ exit(4);
+ }
+ }
+
+ if (out_file_name) {
+ if (!nt_set_regf_output_file(regf, out_file_name)) {
+ fprintf(stderr, "Could not set name of output registry file: %s, %s\n",
+ out_file_name, strerror(errno));
+ exit(3);
+ }
+
+ }
+
+ if (commands) {
+ CMD *cmd;
+
+ cmd_file = cmd_file_create(cmd_file_name);
+
+ while ((cmd = cmd_file->cmd_ops.get_cmd(cmd_file->fd)) != NULL) {
+
+ /*
+ * Now, apply the requests to the tree ...
+ */
+ switch (cmd->cmd) {
+ case CMD_ADD_KEY:
+
+ break;
+
+ case CMD_DEL_KEY:
+ /*
+ * Any value does not matter ...
+ * Find the key if it exists, and delete it ...
+ */
+
+ nt_delete_key_by_name(regf, cmd->key);
+ break;
+ }
+ }
+ free_cmd(cmd);
+ }
+
+ /*
+ * At this point, we should have a registry in memory and should be able
+ * to iterate over it.
+ */
+
+ if (print_keys) {
+ nt_key_iterator(regf, regf->root, 0, "", print_key, print_sec, print_val);
+ }
+
+ return 0;
+}