/* * $Id: reglookup.c 14 2005-06-04 20:54:26Z tim $ * * A utility to read a Windows NT/2K etc registry file. * * This code was taken from Richard Sharpe''s editreg utility, in the * Samba CVS tree. It has since been simplified and turned into a * strictly read-only utility. * * Copyright (C) 2005 Timothy D. Morgan * 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; version 2 of the License. * * 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 note from Richard Sharpe: 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. NOTE: the comments he refers to have been moved to doc/winntreg.txt **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #define False 0 #define True 1 #define REG_KEY_LIST_SIZE 10 /* * 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 SIVAL(buf, val) \ ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\ (((unsigned char *)(buf))[1])=(unsigned char)(((val)>>8)&0xFF),\ (((unsigned char *)(buf))[2])=(unsigned char)(((val)>>16)&0xFF),\ (((unsigned char *)(buf))[3])=(unsigned char)((val)>>24)) #define SSVAL(buf, val) \ ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\ (((unsigned char *)(buf))[1])=(unsigned char)((val)>>8)) static int verbose = 0; static int print_security = 0; static int full_print = 0; static const char *def_owner_sid_str = NULL; /* * 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 key_sec_desc_s KEY_SEC_DESC; 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; KEY_SEC_DESC *security; unsigned int offset; /* Offset of the record in the file */ } 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; int max_vals; VAL_KEY *vals[1]; } VAL_LIST; #ifndef MAXSUBAUTHS #define MAXSUBAUTHS 15 #endif typedef struct sid_s { unsigned char ver, auths; unsigned char auth[6]; unsigned int sub_auths[MAXSUBAUTHS]; } sid_t; typedef struct ace_struct_s { unsigned char type, flags; unsigned int perms; /* Perhaps a better def is in order */ sid_t *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; sid_t *owner, *group; ACL *sacl, *dacl; } SEC_DESC; #define SEC_DESC_NON 0 #define SEC_DESC_RES 1 #define SEC_DESC_OCU 2 #define SEC_DESC_NBK 3 typedef struct sk_struct SK_HDR; struct key_sec_desc_s { struct key_sec_desc_s *prev, *next; int ref_cnt; int state; int offset; SK_HDR *sk_hdr; /* This means we must keep the registry in memory */ SEC_DESC *sec_desc; }; /* * All of the structures below actually have a four-byte length 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 off_from_first; DWORD off_to_next; 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 struct sk_struct { WORD SK_ID; WORD uk1; DWORD prev_off; DWORD next_off; DWORD ref_cnt; DWORD rec_size; char sec_desc[1]; }; typedef struct ace_struct { unsigned char type; unsigned char flags; unsigned short length; unsigned int perms; sid_t 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_DELETE -1 #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 /* Not a real type in the registry */ #define REG_TYPE_KEY 255 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; /* * This structure keeps track of the output format of the registry */ #define REG_OUTBLK_HDR 1 #define REG_OUTBLK_HBIN 2 typedef struct hbin_blk_s { int type, size; struct hbin_blk_s *next; char *data; /* The data block */ unsigned int file_offset; /* Offset in file */ unsigned int free_space; /* Amount of free space in block */ unsigned int fsp_off; /* Start of free space in block */ int complete, stored; } HBIN_BLK; /* * This structure keeps all the registry stuff in one place */ typedef 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; const char *owner_sid_str; SEC_DESC *def_sec_desc; /* * These next pointers point to the blocks used to contain the * keys when we are preparing to write them to a file */ HBIN_BLK *blk_head, *blk_tail, *free_space; } REGF; /* Function prototypes */ static int nt_val_list_iterator(REGF *regf, REG_KEY *key_tree, int bf, char *path, int terminal); static int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path); static REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key); static int print_key(const char *path, char *name, char *class_name, int root, int terminal, int vals, char* newline); static int print_val(const char *path, char *val_name, int val_type, int data_len, void *data_blk, int terminal, int first, int last); static int print_sec(SEC_DESC *sec_desc); /* Globals */ char* prefix_filter = ""; int type_filter = 0; bool type_filter_enabled = false; unsigned int str_is_prefix(const char* p, const char* s) { const char* cp; const char* cs; cs = s; for(cp=p; (*cp) != '\0'; cp++) { if((*cp)!=(*cs)) return 0; cs++; } return 1; } /* Returns a newly malloc()ed string which contains original buffer, * except for non-printable or special characters are quoted in hex * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted * character. A null terminator is added, as only ascii, not binary, * is returned. */ static char* quote_buffer(const unsigned char* str, char* special, unsigned int len) { unsigned int i; unsigned int num_written=0; unsigned int out_len = sizeof(char)*len+1; char* ret_val = malloc(out_len); if(ret_val == NULL) return NULL; for(i=0; i 126 || strchr(special, str[i]) != NULL) { out_len += 3; /* XXX: may not be the most efficient way of getting enough memory. */ ret_val = realloc(ret_val, out_len); if(ret_val == NULL) break; num_written += snprintf(ret_val+num_written, (out_len)-num_written, "\\x%.2X", str[i]); } else ret_val[num_written++] = str[i]; } ret_val[num_written] = '\0'; return ret_val; } /* Returns a newly malloc()ed string which contains original string, * except for non-printable or special characters are quoted in hex * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted * character. */ static char* quote_string(const char* str, char* special) { unsigned int len = strlen(str); char* ret_val = quote_buffer((const unsigned char*)str, special, len); return ret_val; } /* * 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 */ static int nt_val_list_iterator(REGF *regf, REG_KEY *key_tree, int bf, char *path, int terminal) { int i; VAL_LIST* val_list = key_tree->values; for (i=0; ival_count; i++) { /*XXX: print_key() is doing nothing right now, can probably be removed. */ if (!print_key(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), "\n") || !print_val(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; } static int nt_key_list_iterator(REGF *regf, KEY_LIST *key_list, int bf, const char *path) { 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)) return 0; } return 1; } static int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path) { int path_len = strlen(path); char *new_path; if (!regf || !key_tree) return -1; 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, "/"); /* List the key first, then the values, then the sub-keys */ /*printf("prefix_filter: %s, path: %s\n", prefix_filter, path);*/ if (str_is_prefix(prefix_filter, new_path)) { if (!type_filter_enabled || (type_filter == REG_TYPE_KEY)) printf("%s%s:KEY\n", path, key_tree->name); /*XXX: print_key() is doing nothing right now, can probably be removed. */ if (!print_key(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), "\n")) { return 0; } /* * If we have a security print routine, call it * If the security print routine returns false, stop. */ if (key_tree->security && !print_sec(key_tree->security->sec_desc)) return 0; } /* * Now, iterate through the values in the val_list */ if (key_tree->values && !nt_val_list_iterator(regf, key_tree, bf, new_path, (key_tree->values!=NULL))) { 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)) { free(new_path); return 0; } free(new_path); return 1; } /* * Find key by name in a list ... * Take the first component and search for that in the list */ static 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 ... */ static REG_KEY* nt_find_key_by_name(REG_KEY* tree, char* key) { char* lname = NULL; char* c1; char* 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) { if (lname) free(lname); return NULL; } 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; } return NULL; } /* Make, delete keys */ static int nt_delete_val_key(VAL_KEY *val_key) { if (val_key) { if (val_key->name) free(val_key->name); if (val_key->data_blk) free(val_key->data_blk); free(val_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 ... */ /* * Convert a string of the form S-1-5-x[-y-z-r] to a SID */ /* MIGHT COME IN HANDY LATER. static int sid_string_to_sid(sid_t **sid, const char *sid_str) { int i = 0; unsigned int auth; const char *lstr; *sid = (sid_t *)malloc(sizeof(sid_t)); if (!*sid) return 0; memset(*sid, 0, sizeof(sid_t)); if (strncmp(sid_str, "S-1-5", 5)) { fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str); return 0; } //We only allow strings of form S-1-5... (*sid)->ver = 1; (*sid)->auth[5] = 5; lstr = sid_str + 5; while (1) { if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) { if (i < 1) { fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i); return 0; } (*sid)->auths=i; return 1; } (*sid)->sub_auths[i] = auth; i++; lstr = strchr(lstr + 1, '-'); } return 1; } */ /* * We will implement inheritence that is based on what the parent's SEC_DESC * says, but the Owner and Group SIDs can be overwridden from the command line * and additional ACEs can be applied from the command line etc. */ static KEY_SEC_DESC *nt_inherit_security(REG_KEY *key) { if (!key) return NULL; return key->security; } /* * Add a sub-key */ static REG_KEY *nt_add_reg_key_list(REGF *regf, REG_KEY *key, char * name, int create) { int i; REG_KEY *ret = NULL, *tmp = NULL; KEY_LIST *list; char *lname, *c1, *c2; if (!key || !name || !*name) return NULL; list = key->sub_keys; if (!list) { /* Create an empty list */ list = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (REG_KEY_LIST_SIZE - 1) * sizeof(REG_KEY *)); list->key_count = 0; list->max_keys = REG_KEY_LIST_SIZE; } lname = strdup(name); if (!lname) return NULL; c1 = lname; c2 = strchr(c1, '/'); if (c2) { /* Split here ... */ *c2 = 0; c2++; } for (i = 0; i < list->key_count; i++) { if (strcmp(list->keys[i]->name, c1) == 0) { ret = nt_add_reg_key_list(regf, list->keys[i], c2, create); free(lname); return ret; } } /* * If we reach here we could not find the the first component * so create it ... */ if (list->key_count < list->max_keys){ list->key_count++; } else { /* Create more space in the list ... */ if (!(list = (KEY_LIST *)realloc(list, sizeof(KEY_LIST) + (list->max_keys + REG_KEY_LIST_SIZE - 1) * sizeof(REG_KEY *)))) goto error; list->max_keys += REG_KEY_LIST_SIZE; list->key_count++; } /* * add the new key at the new slot * XXX: Sort the list someday */ /* * We want to create the key, and then do the rest */ tmp = (REG_KEY *)malloc(sizeof(REG_KEY)); memset(tmp, 0, sizeof(REG_KEY)); tmp->name = strdup(c1); if (!tmp->name) goto error; tmp->owner = key; tmp->type = REG_SUB_KEY; /* * Next, pull security from the parent, but override with * anything passed in on the command line */ tmp->security = nt_inherit_security(key); list->keys[list->key_count - 1] = tmp; if (c2) { ret = nt_add_reg_key_list(regf, key, c2, True); } if (lname) free(lname); return ret; error: if (tmp) free(tmp); if (lname) free(lname); return NULL; } /* * 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 #define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4) #define LOCN(base, f) ((base) + OFF(f)) const VAL_STR reg_type_names[] = { { REG_TYPE_REGSZ, "SZ" }, { REG_TYPE_EXPANDSZ, "EXPAND_SZ" }, { REG_TYPE_BIN, "BIN" }, { REG_TYPE_DWORD, "DWORD" }, { REG_TYPE_MULTISZ, "MULTI_SZ" }, { REG_TYPE_KEY, "KEY" }, { 0, NULL }, }; static const char *val_to_str(unsigned int val, const VAL_STR *val_array) { int i; if (!val_array) return NULL; for(i=0; val_array[i].val && val_array[i].str; i++) if (val_array[i].val == val) return val_array[i].str; return NULL; } /* Returns 0 on error */ static int str_to_val(const char* str, const VAL_STR *val_array) { int i; if (!val_array) return 0; for(i=0; val_array[i].val && val_array[i].str; i++) if (strcmp(val_array[i].str, str) == 0) return val_array[i].val; return 0; } /* * Convert from UniCode to Ascii ... Does not take into account other lang * Restrict by ascii_max if > 0 */ static 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 */ static unsigned char* data_to_ascii(unsigned char *datap, int len, int type) { unsigned char *asciip; unsigned int i; unsigned short num_nulls; unsigned char* ascii; unsigned char* cur_str; unsigned char* cur_ascii; char* cur_quoted; unsigned int cur_str_len; unsigned int ascii_max, cur_str_max; unsigned int str_rem, cur_str_rem, alen; switch (type) { case REG_TYPE_REGSZ: if (verbose) fprintf(stderr, "Len: %d\n", len); ascii_max = sizeof(char)*len; ascii = malloc(ascii_max+4); if(ascii == NULL) return NULL; /* XXX: This has to be fixed. It has to be UNICODE */ uni_to_ascii(datap, ascii, len, ascii_max); return ascii; break; case REG_TYPE_EXPANDSZ: ascii_max = sizeof(char)*len; ascii = malloc(ascii_max+2); if(ascii == NULL) return NULL; uni_to_ascii(datap, ascii, len, ascii_max); return ascii; break; case REG_TYPE_BIN: ascii = (unsigned char*)quote_buffer(datap, "\\", len); return ascii; break; case REG_TYPE_DWORD: ascii_max = sizeof(char)*10; ascii = malloc(ascii_max+1); if(ascii == NULL) return NULL; if (*(int *)datap == 0) snprintf((char*)ascii, ascii_max, "0"); else snprintf((char*)ascii, ascii_max, "0x%x", *(int *)datap); return ascii; break; case REG_TYPE_MULTISZ: ascii_max = sizeof(char)*len*4; cur_str_max = sizeof(char)*len+1; cur_str = malloc(cur_str_max); cur_ascii = malloc(cur_str_max); ascii = malloc(ascii_max+4); if(ascii == NULL) return NULL; /* Reads until it reaches 4 consecutive NULLs, * which is two nulls in unicode, or until it reaches len, or until we * run out of buffer. The latter should never happen, but we shouldn't * trust our file to have the right lengths/delimiters. */ asciip = ascii; num_nulls = 0; str_rem = ascii_max; cur_str_rem = cur_str_max; cur_str_len = 0; for(i=0; (i < len) && str_rem > 0; i++) { *(cur_str+cur_str_len) = *(datap+i); if(*(cur_str+cur_str_len) == 0) num_nulls++; else num_nulls = 0; cur_str_len++; if(num_nulls == 2) { uni_to_ascii(cur_str, cur_ascii, cur_str_max, 0); /* XXX: Should backslashes be quoted as well? */ cur_quoted = quote_string((char*)cur_ascii, "|"); alen = snprintf((char*)asciip, str_rem, "%s", cur_quoted); asciip += alen; str_rem -= alen; free(cur_quoted); if(*(datap+i+1) == 0 && *(datap+i+2) == 0) break; else { alen = snprintf((char*)asciip, str_rem, "%c", '|'); asciip += alen; str_rem -= alen; memset(cur_str, 0, cur_str_max); cur_str_len = 0; num_nulls = 0; /* To eliminate leading nulls in subsequent strings. */ i++; } } } *asciip = 0; return ascii; break; default: return NULL; break; } return NULL; } static REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent); static int nt_set_regf_input_file(REGF *regf, char *filename) { return ((regf->regfile_name = strdup(filename)) != NULL); } /* Create a regf structure and init it */ static REGF *nt_create_regf(void) { REGF *tmp = (REGF *)malloc(sizeof(REGF)); if (!tmp) return tmp; memset(tmp, 0, sizeof(REGF)); tmp->owner_sid_str = def_owner_sid_str; return tmp; } /* 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 */ static 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, ®f->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 */ static 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 */ static 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 */ static 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 */ static 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; } memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */ 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. */ static sid_t *dup_sid(sid_t *sid) { sid_t *tmp = (sid_t *)malloc(sizeof(sid_t)); 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; iauths&&isub_auths[i] = sid->sub_auths[i]; } return tmp; } /* * Allocate space for an ACE and duplicate the registry encoded one passed in */ static 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 */ static 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); if (verbose) fprintf(stdout, "ACL: refcnt: %u, rev: %u\n", tmp->refcnt, tmp->rev); ace = (REG_ACE *)&acl->aces; for (i=0; iaces[i] = dup_ace(ace); ace = (REG_ACE *)((char *)ace + SVAL(&ace->length)); /* XXX: should handle NULLs returned from dup_ace() */ } return tmp; } static 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); if (verbose) fprintf(stdout, "SEC_DESC Rev: %0X, Type: %0X\n", tmp->rev, tmp->type); if (verbose) fprintf(stdout, "SEC_DESC Owner Off: %0X\n", IVAL(&sec_desc->owner_off)); if (verbose) fprintf(stdout, "SEC_DESC Group Off: %0X\n", IVAL(&sec_desc->group_off)); if (verbose) fprintf(stdout, "SEC_DESC DACL Off: %0X\n", IVAL(&sec_desc->dacl_off)); tmp->owner = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->owner_off))); if (!tmp->owner) { free(tmp); return NULL; } tmp->group = dup_sid((sid_t *)((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; } static 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; memset(tmp, 0, 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 */ static 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; } memset(tmp, 0, 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, "", 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); memcpy(dtmp, dat_ptr, dat_len); } else { /* The data is in the offset or type */ /* * XXX: * Some registry files seem to have wierd fields. If top bit is set, * but len is 0, the type seems to be the value ... * Not sure how to handle this last type for the moment ... */ dat_len = dat_len & 0x7FFFFFFF; memcpy(dtmp, &dat_off, 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: if (tmp) nt_delete_val_key(tmp); return NULL; } /* * Process a VL Header and return a list of values */ static 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; ibase, vk_off); tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr)); if (!tmp->vals[i]){ goto error; } } tmp->val_count = count; tmp->max_vals = count; return tmp; error: /* XXX: free the partially allocated structure */ return NULL; } /* * Process an LF Header and return a list of sub-keys */ static 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 (verbose) fprintf(stdout, "Key Count: %u\n", 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; ihr[i].nk_off); if (verbose) fprintf(stdout, "NK Offset: %0X\n", 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 an NK_HDR pointer and retrieves the entire tree * from there down. It returns a REG_KEY *. */ static 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]; unsigned char 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; memset(tmp, 0, 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 */ unsigned char *clsnamep; unsigned int clsnam_off; clsnam_off = IVAL(&nk_hdr->clsnam_off); clsnamep = (unsigned char*)LOCN(regf->base, clsnam_off); if (verbose) fprintf(stdout, "Class Name Offset: %0X\n", clsnam_off); memset(cls_name, 0, clsname_len); uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len); /* * XXX: * 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. */ tmp->class_name = strdup((char*)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\n", own_off); if (verbose) fprintf(stdout, " Owner locn: %0X, Our locn: %0X\n", (unsigned int)own, (unsigned int)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 (verbose) fprintf(stdout, "Val Count: %d\n", val_count); if (val_count) { val_off = IVAL(&nk_hdr->val_off); vl = (VL_TYPE *)LOCN(regf->base, val_off); if (verbose) fprintf(stdout, "Val List Offset: %0X\n", 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 (verbose) fprintf(stdout, "SK Offset: %0X\n", 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); if (verbose) fprintf(stdout, "SubKey list offset: %0X\n", 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; } static 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(®f_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(®f_hdr->tim1), IVAL(®f_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 */ if (verbose) fprintf(stdout, "First Key: %0X\n", IVAL(®f_hdr->first_key)); first_key = (NK_HDR *)LOCN(regf->base, IVAL(®f_hdr->first_key)); if (verbose) fprintf(stdout, "First Key Offset: %0X\n", IVAL(®f_hdr->first_key)); if (verbose) fprintf(stdout, "Data Block Size: %d\n", IVAL(®f_hdr->dblk_size)); if (verbose) fprintf(stdout, "Offset to next hbin block: %0X\n", IVAL(&hbin_hdr->off_to_next)); if (verbose) fprintf(stdout, "HBIN block size: %0X\n", IVAL(&hbin_hdr->blk_size)); /* * 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); /* * Unmap the registry file, as we might want to read in another * tree etc. */ if (regf->base) munmap(regf->base, regf->sbuf.st_size); regf->base = NULL; close(regf->fd); /* Ignore the error :-) */ return 1; } /* * Routines to parse a REGEDIT4 file * * The file consists of: * * REGEDIT4 * \[[-]key-path\]\n * * * * 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; #define INIT_ALLOC 10 /* prints a key */ static int print_key(const char *path, char *name, char *class_name, int root, int terminal, int vals, char* newline) { if (full_print) fprintf(stdout, "%s%s/%s", path, name, newline); return 1; } /* * Sec Desc print functions */ static 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"); break; default: fprintf(stdout, " UNKNOWN"); break; } } static 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); } static void print_perms(int perms) { fprintf(stdout, " %8X", perms); } static void print_sid(sid_t *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");*/ } static void print_acl(ACL *acl, const 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); } } static 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 ... */ static int print_val(const char *path, char *val_name, int val_type, int data_len, void *data_blk, int terminal, int first, int last) { unsigned char* data_asc; char* new_path; const char* str_type; if(!val_name) val_name = ""; if(!path) path = ""; new_path = (char *)malloc(strlen(path)+ strlen(val_name) + 1); if (!new_path) return 0; /* Errors? */ new_path[0] = '\0'; strcat(new_path, path); strcat(new_path, val_name); if (str_is_prefix(prefix_filter, new_path)) { if (!type_filter_enabled || (type_filter == val_type)) { if(!val_name) val_name = ""; str_type = val_to_str(val_type,reg_type_names); if(!str_type) str_type = ""; data_asc = data_to_ascii((unsigned char *)data_blk, data_len, val_type); fprintf(stdout, "%s:%s=%s\n", new_path, str_type, data_asc); free(data_asc); } } free(new_path); return 1; } static void usage(void) { fprintf(stderr, "Usage: readreg [-f] [-t] " "[-v] [-s] \n"); /* XXX: replace version string with Subversion tag? */ fprintf(stderr, "Version: 0.1\n"); fprintf(stderr, "\n\t-v\t sets verbose mode."); fprintf(stderr, "\n\t-f\t a simple prefix filter."); fprintf(stderr, "\n\t-t\t restrict results to a specific type."); fprintf(stderr, "\n\t-s\t prints security descriptors."); fprintf(stderr, "\n"); } int main(int argc, char *argv[]) { REGF *regf; extern char *optarg; extern int optind; int opt; int regf_opt = 1; if (argc < 2) { usage(); exit(1); } /* * Now, process the arguments */ while ((opt = getopt(argc, argv, "svf:t:o:c:")) != EOF) { switch (opt) { case 'f': /*full_print = 1;*/ prefix_filter = strdup(optarg); regf_opt++; break; case 't': type_filter = str_to_val(optarg, reg_type_names); type_filter_enabled = true; regf_opt++; break; case 's': print_security++; /*full_print++;*/ regf_opt++; break; case 'v': verbose++; regf_opt++; break; default: usage(); exit(1); break; } } /* * We only want to complain about the lack of a default owner SID if * we need one. This approximates that need */ if (!def_owner_sid_str) { def_owner_sid_str = "S-1-5-21-1-2-3-4"; if (verbose) fprintf(stderr, "Warning, default owner SID not set. Setting to %s\n", def_owner_sid_str); } 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); } } /* * At this point, we should have a registry in memory and should be able * to iterate over it. */ nt_key_iterator(regf, regf->root, 0, ""); return 0; }