1/*
2   Samba Unix/Linux SMB client utility editreg.c
3   Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
18
19/*************************************************************************
20
21 A utility to edit a Windows NT/2K etc registry file.
22
23 Many of the ideas in here come from other people and software.
24 I first looked in Wine in misc/registry.c and was also influenced by
25 http://www.wednesday.demon.co.uk/dosreg.html
26
27 Which seems to contain comments from someone else. I reproduce them here
28 incase the site above disappears. It actually comes from
29 http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt.
30
31 The goal here is to read the registry into memory, manipulate it, and then
32 write it out if it was changed by any actions of the user.
33
34The windows NT registry has 2 different blocks, where one can occur many
35times...
36
37the "regf"-Block
38================
39
40"regf" is obviosly the abbreviation for "Registry file". "regf" is the
41signature of the header-block which is always 4kb in size, although only
42the first 64 bytes seem to be used and a checksum is calculated over
43the first 0x200 bytes only!
44
45Offset            Size      Contents
460x00000000      D-Word      ID: ASCII-"regf" = 0x66676572
470x00000004      D-Word      ???? //see struct REGF
480x00000008      D-Word      ???? Always the same value as at 0x00000004
490x0000000C      Q-Word      last modify date in WinNT date-format
500x00000014      D-Word      1
510x00000018      D-Word      3
520x0000001C      D-Word      0
530x00000020      D-Word      1
540x00000024      D-Word      Offset of 1st key record
550x00000028      D-Word      Size of the data-blocks (Filesize-4kb)
560x0000002C      D-Word      1
570x000001FC      D-Word      Sum of all D-Words from 0x00000000 to
580x000001FB  //XOR of all words. Nigel
59
60I have analyzed more registry files (from multiple machines running
61NT 4.0 german version) and could not find an explanation for the values
62marked with ???? the rest of the first 4kb page is not important...
63
64the "hbin"-Block
65================
66I don't know what "hbin" stands for, but this block is always a multiple
67of 4kb in size.
68
69Inside these hbin-blocks the different records are placed. The memory-
70management looks like a C-compiler heap management to me...
71
72hbin-Header
73===========
74Offset      Size      Contents
750x0000      D-Word      ID: ASCII-"hbin" = 0x6E696268
760x0004      D-Word      Offset from the 1st hbin-Block
770x0008      D-Word      Offset to the next hbin-Block
780x001C      D-Word      Block-size
79
80The values in 0x0008 and 0x001C should be the same, so I don't know
81if they are correct or swapped...
82
83From offset 0x0020 inside a hbin-block data is stored with the following
84format:
85
86Offset      Size      Contents
870x0000      D-Word      Data-block size    //this size must be a
88multiple of 8. Nigel
890x0004      ????      Data
90
91If the size field is negative (bit 31 set), the corresponding block
92is free and has a size of -blocksize!
93
94That does not seem to be true. All block lengths seem to be negative!
95(Richard Sharpe)
96
97The data is stored as one record per block. Block size is a multiple
98of 4 and the last block reaches the next hbin-block, leaving no room.
99
100(That also seems incorrect, in that the block size if a multiple of 8.
101That is, the block, including the 4 byte header, is always a multiple of
1028 bytes. Richard Sharpe.)
103
104Records in the hbin-blocks
105==========================
106
107nk-Record
108
109      The nk-record can be treated as a kombination of tree-record and
110      key-record of the win 95 registry.
111
112lf-Record
113
114      The lf-record is the counterpart to the RGKN-record (the
115      hash-function)
116
117vk-Record
118
119      The vk-record consists information to a single value.
120
121sk-Record
122
123      sk (? Security Key ?) is the ACL of the registry.
124
125Value-Lists
126
127      The value-lists contain information about which values are inside a
128      sub-key and don't have a header.
129
130Datas
131
132      The datas of the registry are (like the value-list) stored without a
133      header.
134
135All offset-values are relative to the first hbin-block and point to the
136block-size field of the record-entry. to get the file offset, you have to add
137the header size (4kb) and the size field (4 bytes)...
138
139the nk-Record
140=============
141Offset      Size      Contents
1420x0000      Word      ID: ASCII-"nk" = 0x6B6E
1430x0002      Word      for the root-key: 0x2C, otherwise 0x20  //key symbolic links 0x10. Nigel
1440x0004      Q-Word      write-date/time in windows nt notation
1450x0010      D-Word      Offset of Owner/Parent key
1460x0014      D-Word      number of sub-Keys
1470x001C      D-Word      Offset of the sub-key lf-Records
1480x0024      D-Word      number of values
1490x0028      D-Word      Offset of the Value-List
1500x002C      D-Word      Offset of the sk-Record
151
1520x0030      D-Word      Offset of the Class-Name //see NK structure for the use of these fields. Nigel
1530x0044      D-Word      Unused (data-trash)  //some kind of run time index. Does not appear to be important. Nigel
1540x0048      Word      name-length
1550x004A      Word      class-name length
1560x004C      ????      key-name
157
158the Value-List
159==============
160Offset      Size      Contents
1610x0000      D-Word      Offset 1st Value
1620x0004      D-Word      Offset 2nd Value
1630x????      D-Word      Offset nth Value
164
165To determine the number of values, you have to look at the owner-nk-record!
166
167Der vk-Record
168=============
169Offset      Size      Contents
1700x0000      Word      ID: ASCII-"vk" = 0x6B76
1710x0002      Word      name length
1720x0004      D-Word      length of the data   //if top bit is set when offset contains data. Nigel
1730x0008      D-Word      Offset of Data
1740x000C      D-Word      Type of value
1750x0010      Word      Flag
1760x0012      Word      Unused (data-trash)
1770x0014      ????      Name
178
179If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
180
181If the data-size is lower 5, the data-offset value is used to store the data itself!
182
183The data-types
184==============
185Wert      Beteutung
1860x0001      RegSZ:             character string (in UNICODE!)
1870x0002      ExpandSZ:   string with "%var%" expanding (UNICODE!)
1880x0003      RegBin:           raw-binary value
1890x0004      RegDWord:   Dword
1900x0007      RegMultiSZ:      multiple strings, seperated with 0
191                  (UNICODE!)
192
193The "lf"-record
194===============
195Offset      Size      Contents
1960x0000      Word      ID: ASCII-"lf" = 0x666C
1970x0002      Word      number of keys
1980x0004      ????      Hash-Records
199
200Hash-Record
201===========
202Offset      Size      Contents
2030x0000      D-Word      Offset of corresponding "nk"-Record
2040x0004      D-Word      ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
205
206Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the
207key-name you have to change the hash-value too!
208
209//These hashrecords must be sorted low to high within the lf record. Nigel.
210
211The "sk"-block
212==============
213(due to the complexity of the SAM-info, not clear jet)
214(This is just a self-relative security descriptor in the data. R Sharpe.)
215
216
217Offset      Size      Contents
2180x0000      Word      ID: ASCII-"sk" = 0x6B73
2190x0002      Word      Unused
2200x0004      D-Word      Offset of previous "sk"-Record
2210x0008      D-Word      Offset of next "sk"-Record
2220x000C      D-Word      usage-counter
2230x0010      D-Word      Size of "sk"-record in bytes
224????                                             //standard self
225relative security desciptor. Nigel
226????  ????      Security and auditing settings...
227????
228
229The usage counter counts the number of references to this
230"sk"-record. You can use one "sk"-record for the entire registry!
231
232Windows nt date/time format
233===========================
234The time-format is a 64-bit integer which is incremented every
2350,0000001 seconds by 1 (I don't know how accurate it realy is!)
236It starts with 0 at the 1st of january 1601 0:00! All values are
237stored in GMT time! The time-zone is important to get the real
238time!
239
240Common values for win95 and win-nt
241==================================
242Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
243If a value has no name (length=0, flag(bit 0)=0), it is treated as the
244"Default" entry...
245If a value has no data (length=0), it is displayed as empty.
246
247simplyfied win-3.?? registry:
248=============================
249
250+-----------+
251| next rec. |---+                      +----->+------------+
252| first sub |   |                      |      | Usage cnt. |
253| name      |   |  +-->+------------+  |      | length     |
254| value     |   |  |   | next rec.  |  |      | text       |------->+-------+
255+-----------+   |  |   | name rec.  |--+      +------------+        | xxxxx |
256   +------------+  |   | value rec. |-------->+------------+        +-------+
257   v               |   +------------+         | Usage cnt. |
258+-----------+      |                          | length     |
259| next rec. |      |                          | text       |------->+-------+
260| first sub |------+                          +------------+        | xxxxx |
261| name      |                                                       +-------+
262| value     |
263+-----------+
264
265Greatly simplyfied structure of the nt-registry:
266================================================
267
268+---------------------------------------------------------------+
269|                                                               |
270v                                                               |
271+---------+     +---------->+-----------+  +----->+---------+   |
272| "nk"    |     |           | lf-rec.   |  |      | nk-rec. |   |
273| ID      |     |           | # of keys |  |      | parent  |---+
274| Date    |     |           | 1st key   |--+      | ....    |
275| parent  |     |           +-----------+         +---------+
276| suk-keys|-----+
277| values  |--------------------->+----------+
278| SK-rec. |---------------+      | 1. value |--> +----------+
279| class   |--+            |      +----------+    | vk-rec.  |
280+---------+  |            |                      | ....     |
281             v            |                      | data     |--> +-------+
282      +------------+      |                      +----------+    | xxxxx |
283      | Class name |      |                                      +-------+
284      +------------+      |
285                          v
286          +---------+    +---------+
287   +----->| next sk |--->| Next sk |--+
288   |  +---| prev sk |<---| prev sk |  |
289   |  |   | ....    |    | ...     |  |
290   |  |   +---------+    +---------+  |
291   |  |                    ^          |
292   |  |                    |          |
293   |  +--------------------+          |
294   +----------------------------------+
295
296---------------------------------------------------------------------------
297
298Hope this helps....  (Although it was "fun" for me to uncover this things,
299                  it took me several sleepless nights ;)
300
301            B.D.
302
303*************************************************************************/
304
305#ifdef STANDALONE
306#include <stdio.h>
307#include <stdlib.h>
308#include <errno.h>
309#include <assert.h>
310#include <sys/types.h>
311#include <sys/stat.h>
312#include <unistd.h>
313#include <sys/mman.h>
314#include <string.h>
315#include <fcntl.h>
316
317#define False 0
318#define True 1
319#else /* STANDALAONE */
320#include "includes.h"
321#endif /* STANDALONE */
322
323#define REG_KEY_LIST_SIZE 10
324
325/*
326 * Structures for dealing with the on-disk format of the registry
327 */
328
329#define IVAL(buf) ((unsigned int) \
330                   (unsigned int)*((unsigned char *)(buf)+3)<<24| \
331                   (unsigned int)*((unsigned char *)(buf)+2)<<16| \
332                   (unsigned int)*((unsigned char *)(buf)+1)<<8| \
333                   (unsigned int)*((unsigned char *)(buf)+0))
334
335#define SVAL(buf) ((unsigned short) \
336                   (unsigned short)*((unsigned char *)(buf)+1)<<8| \
337                   (unsigned short)*((unsigned char *)(buf)+0))
338
339#define CVAL(buf) ((unsigned char)*((unsigned char *)(buf)))
340
341#define SIVAL(buf, val) \
342            ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\
343             (((unsigned char *)(buf))[1])=(unsigned char)(((val)>>8)&0xFF),\
344             (((unsigned char *)(buf))[2])=(unsigned char)(((val)>>16)&0xFF),\
345             (((unsigned char *)(buf))[3])=(unsigned char)((val)>>24))
346
347#define SSVAL(buf, val) \
348            ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\
349             (((unsigned char *)(buf))[1])=(unsigned char)((val)>>8))
350
351static int verbose = 0;
352static int print_security = 0;
353static int full_print = 0;
354static const char *def_owner_sid_str = NULL;
355
356/*
357 * These definitions are for the in-memory registry structure.
358 * It is a tree structure that mimics what you see with tools like regedit
359 */
360
361/*
362 * DateTime struct for Windows
363 */
364
365typedef struct date_time_s {
366  unsigned int low, high;
367} NTTIME;
368
369/*
370 * Definition of a Key. It has a name, classname, date/time last modified,
371 * sub-keys, values, and a security descriptor
372 */
373
374#define REG_ROOT_KEY 1
375#define REG_SUB_KEY  2
376#define REG_SYM_LINK 3
377
378typedef struct key_sec_desc_s KEY_SEC_DESC;
379
380typedef struct reg_key_s {
381  char *name;         /* Name of the key                    */
382  char *class_name;
383  int type;           /* One of REG_ROOT_KEY or REG_SUB_KEY */
384  NTTIME last_mod; /* Time last modified                 */
385  struct reg_key_s *owner;
386  struct key_list_s *sub_keys;
387  struct val_list_s *values;
388  KEY_SEC_DESC *security;
389  unsigned int offset;  /* Offset of the record in the file */
390} REG_KEY;
391
392/*
393 * The KEY_LIST struct lists sub-keys.
394 */
395
396typedef struct key_list_s {
397  int key_count;
398  int max_keys;
399  REG_KEY *keys[1];
400} KEY_LIST;
401
402typedef struct val_key_s {
403  char *name;
404  int has_name;
405  int data_type;
406  int data_len;
407  void *data_blk;    /* Might want a separate block */
408} VAL_KEY;
409
410typedef struct val_list_s {
411  int val_count;
412  int max_vals;
413  VAL_KEY *vals[1];
414} VAL_LIST;
415
416#ifndef MAXSUBAUTHS
417#define MAXSUBAUTHS 15
418#endif
419
420typedef struct sid_s {
421  unsigned char ver, auths;
422  unsigned char auth[6];
423  unsigned int sub_auths[MAXSUBAUTHS];
424} sid_t;
425
426typedef struct ace_struct_s {
427  unsigned char type, flags;
428  unsigned int perms;   /* Perhaps a better def is in order */
429  sid_t *trustee;
430} ACE;
431
432typedef struct acl_struct_s {
433  unsigned short rev, refcnt;
434  unsigned short num_aces;
435  ACE *aces[1];
436} ACL;
437
438typedef struct sec_desc_s {
439  unsigned int rev, type;
440  sid_t *owner, *group;
441  ACL *sacl, *dacl;
442} SEC_DESC;
443
444#define SEC_DESC_NON 0
445#define SEC_DESC_RES 1
446#define SEC_DESC_OCU 2
447#define SEC_DESC_NBK 3
448typedef struct sk_struct SK_HDR;
449struct key_sec_desc_s {
450  struct key_sec_desc_s *prev, *next;
451  int ref_cnt;
452  int state;
453  int offset;
454  SK_HDR *sk_hdr;     /* This means we must keep the registry in memory */
455  SEC_DESC *sec_desc;
456};
457
458/*
459 * All of the structures below actually have a four-byte length before them
460 * which always seems to be negative. The following macro retrieves that
461 * size as an integer
462 */
463
464#define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
465
466typedef unsigned int DWORD;
467typedef unsigned short WORD;
468
469#define REG_REGF_ID 0x66676572
470
471typedef struct regf_block {
472  DWORD REGF_ID;     /* regf */
473  DWORD uk1;
474  DWORD uk2;
475  DWORD tim1, tim2;
476  DWORD uk3;             /* 1 */
477  DWORD uk4;             /* 3 */
478  DWORD uk5;             /* 0 */
479  DWORD uk6;             /* 1 */
480  DWORD first_key;       /* offset */
481  unsigned int dblk_size;
482  DWORD uk7[116];        /* 1 */
483  DWORD chksum;
484} REGF_HDR;
485
486typedef struct hbin_sub_struct {
487  DWORD dblocksize;
488  char data[1];
489} HBIN_SUB_HDR;
490
491#define REG_HBIN_ID 0x6E696268
492
493typedef struct hbin_struct {
494  DWORD HBIN_ID; /* hbin */
495  DWORD off_from_first;
496  DWORD off_to_next;
497  DWORD uk1;
498  DWORD uk2;
499  DWORD uk3;
500  DWORD uk4;
501  DWORD blk_size;
502  HBIN_SUB_HDR hbin_sub_hdr;
503} HBIN_HDR;
504
505#define REG_NK_ID 0x6B6E
506
507typedef struct nk_struct {
508  WORD NK_ID;
509  WORD type;
510  DWORD t1, t2;
511  DWORD uk1;
512  DWORD own_off;
513  DWORD subk_num;
514  DWORD uk2;
515  DWORD lf_off;
516  DWORD uk3;
517  DWORD val_cnt;
518  DWORD val_off;
519  DWORD sk_off;
520  DWORD clsnam_off;
521  DWORD unk4[4];
522  DWORD unk5;
523  WORD nam_len;
524  WORD clsnam_len;
525  char key_nam[1];  /* Actual length determined by nam_len */
526} NK_HDR;
527
528#define REG_SK_ID 0x6B73
529
530struct sk_struct {
531  WORD SK_ID;
532  WORD uk1;
533  DWORD prev_off;
534  DWORD next_off;
535  DWORD ref_cnt;
536  DWORD rec_size;
537  char sec_desc[1];
538};
539
540typedef struct ace_struct {
541    unsigned char type;
542    unsigned char flags;
543    unsigned short length;
544    unsigned int perms;
545    sid_t trustee;
546} REG_ACE;
547
548typedef struct acl_struct {
549  WORD rev;
550  WORD size;
551  DWORD num_aces;
552  REG_ACE *aces;   /* One or more ACEs */
553} REG_ACL;
554
555typedef struct sec_desc_rec {
556  WORD rev;
557  WORD type;
558  DWORD owner_off;
559  DWORD group_off;
560  DWORD sacl_off;
561  DWORD dacl_off;
562} REG_SEC_DESC;
563
564typedef struct hash_struct {
565  DWORD nk_off;
566  char hash[4];
567} HASH_REC;
568
569#define REG_LF_ID 0x666C
570
571typedef struct lf_struct {
572  WORD LF_ID;
573  WORD key_count;
574  struct hash_struct hr[1];  /* Array of hash records, depending on key_count */
575} LF_HDR;
576
577typedef DWORD VL_TYPE[1];  /* Value list is an array of vk rec offsets */
578
579#define REG_VK_ID 0x6B76
580
581typedef struct vk_struct {
582  WORD VK_ID;
583  WORD nam_len;
584  DWORD dat_len;    /* If top-bit set, offset contains the data */
585  DWORD dat_off;
586  DWORD dat_type;
587  WORD flag;        /* =1, has name, else no name (=Default). */
588  WORD unk1;
589  char dat_name[1]; /* Name starts here ... */
590} VK_HDR;
591
592#define REG_TYPE_DELETE    -1
593#define REG_TYPE_NONE      0
594#define REG_TYPE_REGSZ     1
595#define REG_TYPE_EXPANDSZ  2
596#define REG_TYPE_BIN       3
597#define REG_TYPE_DWORD     4
598#define REG_TYPE_MULTISZ   7
599
600typedef struct _val_str {
601  unsigned int val;
602  const char * str;
603} VAL_STR;
604
605/* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
606typedef struct sk_map_s {
607  int sk_off;
608  KEY_SEC_DESC *key_sec_desc;
609} SK_MAP;
610
611/*
612 * This structure keeps track of the output format of the registry
613 */
614#define REG_OUTBLK_HDR 1
615#define REG_OUTBLK_HBIN 2
616
617typedef struct hbin_blk_s {
618  int type, size;
619  struct hbin_blk_s *next;
620  char *data;                /* The data block                */
621  unsigned int file_offset;  /* Offset in file                */
622  unsigned int free_space;   /* Amount of free space in block */
623  unsigned int fsp_off;      /* Start of free space in block  */
624  int complete, stored;
625} HBIN_BLK;
626
627/*
628 * This structure keeps all the registry stuff in one place
629 */
630typedef struct regf_struct_s {
631  int reg_type;
632  char *regfile_name, *outfile_name;
633  int fd;
634  struct stat sbuf;
635  char *base;
636  int modified;
637  NTTIME last_mod_time;
638  REG_KEY *root;  /* Root of the tree for this file */
639  int sk_count, sk_map_size;
640  SK_MAP *sk_map;
641  const char *owner_sid_str;
642  SEC_DESC *def_sec_desc;
643  /*
644   * These next pointers point to the blocks used to contain the
645   * keys when we are preparing to write them to a file
646   */
647  HBIN_BLK *blk_head, *blk_tail, *free_space;
648} REGF;
649
650/*
651 * An API for accessing/creating/destroying items above
652 */
653
654/*
655 * Iterate over the keys, depth first, calling a function for each key
656 * and indicating if it is terminal or non-terminal and if it has values.
657 *
658 * In addition, for each value in the list, call a value list function
659 */
660
661typedef int (*key_print_f)(const char *path, char *key_name, char *class_name,
662			   int root, int terminal, int values);
663
664typedef int (*val_print_f)(const char *path, char *val_name, int val_type,
665			   int data_len, void *data_blk, int terminal,
666			   int first, int last);
667
668typedef int (*sec_print_f)(SEC_DESC *sec_desc);
669
670static
671int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
672		    key_print_f key_print, sec_print_f sec_print,
673		    val_print_f val_print);
674
675static
676int nt_val_list_iterator(REGF *regf, VAL_LIST *val_list, int bf, char *path,
677			 int terminal, val_print_f val_print)
678{
679  int i;
680
681  if (!val_list) return 1;
682
683  if (!val_print) return 1;
684
685  for (i=0; i<val_list->val_count; i++) {
686    if (!val_print(path, val_list->vals[i]->name, val_list->vals[i]->data_type,
687		   val_list->vals[i]->data_len, val_list->vals[i]->data_blk,
688		   terminal,
689		   (i == 0),
690		   (i == val_list->val_count))) {
691
692      return 0;
693
694    }
695  }
696
697  return 1;
698}
699
700static
701int nt_key_list_iterator(REGF *regf, KEY_LIST *key_list, int bf,
702			 const char *path,
703			 key_print_f key_print, sec_print_f sec_print,
704			 val_print_f val_print)
705{
706  int i;
707
708  if (!key_list) return 1;
709
710  for (i=0; i< key_list->key_count; i++) {
711    if (!nt_key_iterator(regf, key_list->keys[i], bf, path, key_print,
712			 sec_print, val_print)) {
713      return 0;
714    }
715  }
716  return 1;
717}
718
719static
720int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
721		    key_print_f key_print, sec_print_f sec_print,
722		    val_print_f val_print)
723{
724  int path_len = strlen(path);
725  char *new_path;
726
727  if (!regf || !key_tree)
728    return -1;
729
730  /* List the key first, then the values, then the sub-keys */
731
732  if (key_print) {
733
734    if (!(*key_print)(path, key_tree->name,
735		      key_tree->class_name,
736		      (key_tree->type == REG_ROOT_KEY),
737		      (key_tree->sub_keys == NULL),
738		      (key_tree->values?(key_tree->values->val_count):0)))
739      return 0;
740  }
741
742  /*
743   * If we have a security print routine, call it
744   * If the security print routine returns false, stop.
745   */
746  if (sec_print) {
747    if (key_tree->security && !(*sec_print)(key_tree->security->sec_desc))
748      return 0;
749  }
750
751  new_path = (char *)SMB_MALLOC(path_len + 1 + strlen(key_tree->name) + 1);
752  if (!new_path) return 0; /* Errors? */
753  new_path[0] = '\0';
754  strcat(new_path, path);
755  strcat(new_path, key_tree->name);
756  strcat(new_path, "\\");
757
758  /*
759   * Now, iterate through the values in the val_list
760   */
761
762  if (key_tree->values &&
763      !nt_val_list_iterator(regf, key_tree->values, bf, new_path,
764			    (key_tree->values!=NULL),
765			    val_print)) {
766
767    free(new_path);
768    return 0;
769  }
770
771  /*
772   * Now, iterate through the keys in the key list
773   */
774
775  if (key_tree->sub_keys &&
776      !nt_key_list_iterator(regf, key_tree->sub_keys, bf, new_path, key_print,
777			    sec_print, val_print)) {
778    free(new_path);
779    return 0;
780  }
781
782  free(new_path);
783  return 1;
784}
785
786static
787REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key);
788
789/*
790 * Find key by name in a list ...
791 * Take the first component and search for that in the list
792 */
793static
794REG_KEY *nt_find_key_in_list_by_name(KEY_LIST *list, char *key)
795{
796  int i;
797  REG_KEY *res = NULL;
798
799  if (!list || !key || !*key) return NULL;
800
801  for (i = 0; i < list->key_count; i++)
802    if ((res = nt_find_key_by_name(list->keys[i], key)))
803      return res;
804
805  return NULL;
806}
807
808/*
809 * Find key by name in a tree ... We will assume absolute names here, but we
810 * need the root of the tree ...
811 */
812static
813REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key)
814{
815  char *lname = NULL, *c1, *c2;
816  REG_KEY *tmp;
817
818  if (!tree || !key || !*key) return NULL;
819
820  lname = SMB_STRDUP(key);
821  if (!lname) return NULL;
822
823  /*
824   * Make sure that the first component is correct ...
825   */
826  c1 = lname;
827  c2 = strchr(c1, '\\');
828  if (c2) { /* Split here ... */
829    *c2 = 0;
830    c2++;
831  }
832  if (strcmp(c1, tree->name) != 0) goto error;
833
834  if (c2) {
835    tmp = nt_find_key_in_list_by_name(tree->sub_keys, c2);
836    free(lname);
837    return tmp;
838  }
839  else {
840    if (lname) free(lname);
841    return tree;
842  }
843 error:
844  if (lname) free(lname);
845  return NULL;
846}
847
848/* Make, delete keys */
849static
850int nt_delete_val_key(VAL_KEY *val_key)
851{
852
853  if (val_key) {
854    if (val_key->name) free(val_key->name);
855    if (val_key->data_blk) free(val_key->data_blk);
856    free(val_key);
857  };
858  return 1;
859}
860
861static
862int nt_delete_val_list(VAL_LIST *vl)
863{
864  int i;
865
866  if (vl) {
867    for (i=0; i<vl->val_count; i++)
868      nt_delete_val_key(vl->vals[i]);
869    free(vl);
870  }
871  return 1;
872}
873
874static
875int nt_delete_reg_key(REG_KEY *key, int delete_name);
876
877static
878int nt_delete_key_list(KEY_LIST *key_list, int delete_name)
879{
880  int i;
881
882  if (key_list) {
883    for (i=0; i<key_list->key_count; i++)
884      nt_delete_reg_key(key_list->keys[i], False);
885    free(key_list);
886  }
887  return 1;
888}
889
890/*
891 * Find the key, and if it exists, delete it ...
892 */
893static
894int nt_delete_key_by_name(REGF *regf, char *name)
895{
896  REG_KEY *key;
897
898  if (!name || !*name) return 0;
899
900  key = nt_find_key_by_name(regf->root, name);
901
902  if (key) {
903    if (key == regf->root) regf->root = NULL;
904    return nt_delete_reg_key(key, True);
905  }
906
907  return 0;
908
909}
910
911static
912int nt_delete_sid(sid_t *sid)
913{
914
915  if (sid) free(sid);
916  return 1;
917
918}
919
920static
921int nt_delete_ace(ACE *ace)
922{
923
924  if (ace) {
925    nt_delete_sid(ace->trustee);
926    free(ace);
927  }
928  return 1;
929
930}
931
932static
933int nt_delete_acl(ACL *acl)
934{
935
936  if (acl) {
937    int i;
938
939    for (i=0; i<acl->num_aces; i++)
940      nt_delete_ace(acl->aces[i]);
941
942    free(acl);
943  }
944  return 1;
945}
946
947static
948int nt_delete_sec_desc(SEC_DESC *sec_desc)
949{
950
951  if (sec_desc) {
952
953    nt_delete_sid(sec_desc->owner);
954    nt_delete_sid(sec_desc->group);
955    nt_delete_acl(sec_desc->sacl);
956    nt_delete_acl(sec_desc->dacl);
957    free(sec_desc);
958
959  }
960  return 1;
961}
962
963static
964int nt_delete_key_sec_desc(KEY_SEC_DESC *key_sec_desc)
965{
966
967  if (key_sec_desc) {
968    key_sec_desc->ref_cnt--;
969    if (key_sec_desc->ref_cnt<=0) {
970      /*
971       * There should always be a next and prev, even if they point to us
972       */
973      key_sec_desc->next->prev = key_sec_desc->prev;
974      key_sec_desc->prev->next = key_sec_desc->next;
975      nt_delete_sec_desc(key_sec_desc->sec_desc);
976    }
977  }
978  return 1;
979}
980
981static
982int nt_delete_reg_key(REG_KEY *key, int delete_name)
983{
984
985  if (key) {
986    if (key->name) free(key->name);
987    if (key->class_name) free(key->class_name);
988
989    /*
990     * We will delete the owner if we are not the root and told to ...
991     */
992
993    if (key->owner && key->owner->sub_keys && delete_name) {
994      REG_KEY *own;
995      KEY_LIST *kl;
996      int i;
997      /* Find our owner, look in keylist for us and shuffle up */
998      /* Perhaps should be a function                          */
999
1000      own = key->owner;
1001      kl = own->sub_keys;
1002
1003      for (i=0; i < kl->key_count && kl->keys[i] != key ; i++) {
1004	/* Just find the entry ... */
1005      }
1006
1007      if (i == kl->key_count) {
1008	fprintf(stderr, "Bad data structure. Key not found in key list of owner\n");
1009      }
1010      else {
1011	int j;
1012
1013	/*
1014	 * Shuffle up. Works for the last one also
1015	 */
1016	for (j = i + 1; j < kl->key_count; j++) {
1017	  kl->keys[j - 1] = kl->keys[j];
1018	}
1019
1020	kl->key_count--;
1021      }
1022    }
1023
1024    if (key->sub_keys) nt_delete_key_list(key->sub_keys, False);
1025    if (key->values) nt_delete_val_list(key->values);
1026    if (key->security) nt_delete_key_sec_desc(key->security);
1027    free(key);
1028  }
1029  return 1;
1030}
1031
1032/*
1033 * Convert a string to a value ...
1034 * FIXME: Error handling and convert this at command parse time ...
1035 */
1036static
1037void *str_to_val(int type, char *val, int *len)
1038{
1039  unsigned int *dwordp = NULL;
1040
1041  if (!len || !val) return NULL;
1042
1043  switch (type) {
1044  case REG_TYPE_REGSZ:
1045    *len = strlen(val);
1046    return (void *)val;
1047
1048  case REG_TYPE_DWORD:
1049    dwordp = SMB_MALLOC_P(unsigned int);
1050    if (!dwordp) return NULL;
1051    /* Allow for ddddd and 0xhhhhh and 0ooooo */
1052    if (strncmp(val, "0x", 2) == 0 || strncmp(val, "0X", 2) == 0) {
1053      sscanf(&val[2], "%X", dwordp);
1054    }
1055    else if (*val == '0') {
1056      sscanf(&val[1], "%o", dwordp);
1057    }
1058    else {
1059      sscanf(val, "%d", dwordp);
1060    }
1061    *len = sizeof(unsigned int);
1062    return (void *)dwordp;
1063
1064    /* FIXME: Implement more of these */
1065
1066  default:
1067    return NULL;
1068  }
1069
1070  return NULL;
1071}
1072
1073/*
1074 * Add a value to the key specified ... We have to parse the value some more
1075 * based on the type to get it in the correct internal form
1076 * An empty name will be converted to "<No Name>" before here
1077 * Hmmm, maybe not. has_name is for that
1078 */
1079static
1080VAL_KEY *nt_add_reg_value(REG_KEY *key, char *name, int type, char *value)
1081{
1082  int i;
1083  VAL_KEY *tmp = NULL;
1084
1085  if (!key || !key->values || !name || !*name) return NULL;
1086
1087  assert(type != REG_TYPE_DELETE); /* We never process deletes here */
1088
1089  for (i = 0; i < key->values->val_count; i++) {
1090    if ((!key->values->vals[i]->has_name && !*name) ||
1091	(key->values->vals[i]->has_name &&
1092	 strcmp(name, key->values->vals[i]->name) == 0)){ /* Change the value */
1093      free(key->values->vals[i]->data_blk);
1094      key->values->vals[i]->data_blk = str_to_val(type, value, &
1095						  key->values->vals[i]->data_len);
1096      return key->values->vals[i];
1097    }
1098  }
1099
1100  /*
1101   * If we get here, the name was not found, so insert it
1102   */
1103
1104  tmp = SMB_MALLOC_P(VAL_KEY);
1105  if (!tmp) goto error;
1106
1107  memset(tmp, 0, sizeof(VAL_KEY));
1108  tmp->name = SMB_STRDUP(name);
1109  tmp->has_name = True;
1110  if (!tmp->name) goto error;
1111  tmp->data_type = type;
1112  tmp->data_blk = str_to_val(type, value, &tmp->data_len);
1113
1114  /* Now, add to val list */
1115
1116  if (key->values->val_count >= key->values->max_vals) {
1117    /*
1118     * Allocate some more space
1119     */
1120
1121    if ((key->values = (VAL_LIST *)SMB_REALLOC_ARRAY(key->values, sizeof(VAL_LIST) +
1122					   key->values->val_count - 1 +
1123					   REG_KEY_LIST_SIZE))) {
1124      key->values->max_vals += REG_KEY_LIST_SIZE;
1125    }
1126    else goto error;
1127  }
1128
1129  i = key->values->val_count;
1130  key->values->val_count++;
1131  key->values->vals[i] = tmp;
1132  return tmp;
1133
1134 error:
1135  if (tmp) nt_delete_val_key(tmp);
1136  return NULL;
1137}
1138
1139/*
1140 * Delete a value. We return the value and let the caller deal with it.
1141 */
1142static
1143VAL_KEY *nt_delete_reg_value(REG_KEY *key, char *name)
1144{
1145  int i, j;
1146
1147  if (!key || !key->values || !name || !*name) return NULL;
1148
1149  /* FIXME: Allow empty value name */
1150  for (i = 0; i< key->values->val_count; i++) {
1151    if ((!key->values->vals[i]->has_name && !*name) ||
1152	(key->values->vals[i]->has_name &&
1153	 strcmp(name, key->values->vals[i]->name) == 0)) {
1154      VAL_KEY *val;
1155
1156      val = key->values->vals[i];
1157
1158      /* Shuffle down */
1159      for (j = i + 1; j < key->values->val_count; j++)
1160	key->values->vals[j - 1] = key->values->vals[j];
1161
1162      key->values->val_count--;
1163
1164      return val;
1165    }
1166  }
1167  return NULL;
1168}
1169
1170/*
1171 * Add a key to the tree ... We walk down the components matching until
1172 * we don't find any. There must be a match on the first component ...
1173 * We return the key structure for the final component as that is
1174 * often where we want to add values ...
1175 */
1176
1177/*
1178 * Convert a string of the form S-1-5-x[-y-z-r] to a SID
1179 */
1180static
1181int sid_string_to_sid(sid_t **sid, const char *sid_str)
1182{
1183  int i = 0, auth;
1184  const char *lstr;
1185
1186  *sid = SMB_MALLOC_P(sid_t);
1187  if (!*sid) return 0;
1188
1189  memset(*sid, 0, sizeof(sid_t));
1190
1191  if (strncmp(sid_str, "S-1-5", 5)) {
1192    fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str);
1193    return 0;
1194  }
1195
1196  /* We only allow strings of form S-1-5... */
1197
1198  (*sid)->ver = 1;
1199  (*sid)->auth[5] = 5;
1200
1201  lstr = sid_str + 5;
1202
1203  while (1) {
1204    if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) {
1205      if (i < 1) {
1206	fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i);
1207	return 0;
1208      }
1209      (*sid)->auths=i;
1210      return 1;
1211    }
1212
1213    (*sid)->sub_auths[i] = auth;
1214    i++;
1215    lstr = strchr(lstr + 1, '-');
1216  }
1217
1218  /*return 1; */ /* Not Reached ... */
1219}
1220
1221/*
1222 * Create an ACE
1223 */
1224static
1225ACE *nt_create_ace(int type, int flags, unsigned int perms, const char *sid)
1226{
1227  ACE *ace;
1228
1229  ace = SMB_MALLOC_P(ACE);
1230  if (!ace) goto error;
1231  ace->type = type;
1232  ace->flags = flags;
1233  ace->perms = perms;
1234  if (!sid_string_to_sid(&ace->trustee, sid))
1235    goto error;
1236  return ace;
1237
1238 error:
1239  if (ace) nt_delete_ace(ace);
1240  return NULL;
1241}
1242
1243/*
1244 * Create a default ACL
1245 */
1246static
1247ACL *nt_create_default_acl(REGF *regf)
1248{
1249  ACL *acl;
1250
1251  acl = (ACL *)SMB_MALLOC(sizeof(ACL) + 7*sizeof(ACE *));
1252  if (!acl) goto error;
1253
1254  acl->rev = 2;
1255  acl->refcnt = 1;
1256  acl->num_aces = 8;
1257
1258  acl->aces[0] = nt_create_ace(0x00, 0x0, 0xF003F, regf->owner_sid_str);
1259  if (!acl->aces[0]) goto error;
1260  acl->aces[1] = nt_create_ace(0x00, 0x0, 0xF003F, "S-1-5-18");
1261  if (!acl->aces[1]) goto error;
1262  acl->aces[2] = nt_create_ace(0x00, 0x0, 0xF003F, "S-1-5-32-544");
1263  if (!acl->aces[2]) goto error;
1264  acl->aces[3] = nt_create_ace(0x00, 0x0, 0x20019, "S-1-5-12");
1265  if (!acl->aces[3]) goto error;
1266  acl->aces[4] = nt_create_ace(0x00, 0x0B, 0x10000000, regf->owner_sid_str);
1267  if (!acl->aces[4]) goto error;
1268  acl->aces[5] = nt_create_ace(0x00, 0x0B, 0x10000000, "S-1-5-18");
1269  if (!acl->aces[5]) goto error;
1270  acl->aces[6] = nt_create_ace(0x00, 0x0B, 0x10000000, "S-1-5-32-544");
1271  if (!acl->aces[6]) goto error;
1272  acl->aces[7] = nt_create_ace(0x00, 0x0B, 0x80000000, "S-1-5-12");
1273  if (!acl->aces[7]) goto error;
1274  return acl;
1275
1276 error:
1277  if (acl) nt_delete_acl(acl);
1278  return NULL;
1279}
1280
1281/*
1282 * Create a default security descriptor. We pull in things from env
1283 * if need be
1284 */
1285static
1286SEC_DESC *nt_create_def_sec_desc(REGF *regf)
1287{
1288  SEC_DESC *tmp;
1289
1290  tmp = SMB_MALLOC_P(SEC_DESC);
1291  if (!tmp) return NULL;
1292
1293  tmp->rev = 1;
1294  tmp->type = 0x8004;
1295  if (!sid_string_to_sid(&tmp->owner, "S-1-5-32-544")) goto error;
1296  if (!sid_string_to_sid(&tmp->group, "S-1-5-18")) goto error;
1297  tmp->sacl = NULL;
1298  tmp->dacl = nt_create_default_acl(regf);
1299
1300  return tmp;
1301
1302 error:
1303  if (tmp) nt_delete_sec_desc(tmp);
1304  return NULL;
1305}
1306
1307/*
1308 * We will implement inheritence that is based on what the parent's SEC_DESC
1309 * says, but the Owner and Group SIDs can be overwridden from the command line
1310 * and additional ACEs can be applied from the command line etc.
1311 */
1312static
1313KEY_SEC_DESC *nt_inherit_security(REG_KEY *key)
1314{
1315
1316  if (!key) return NULL;
1317  return key->security;
1318}
1319
1320/*
1321 * Create an initial security descriptor and init other structures, if needed
1322 * We assume that the initial security stuff is empty ...
1323 */
1324static
1325KEY_SEC_DESC *nt_create_init_sec(REGF *regf)
1326{
1327  KEY_SEC_DESC *tsec = NULL;
1328
1329  tsec = SMB_MALLOC_P(KEY_SEC_DESC);
1330  if (!tsec) return NULL;
1331
1332  tsec->ref_cnt = 1;
1333  tsec->state = SEC_DESC_NBK;
1334  tsec->offset = 0;
1335
1336  tsec->sec_desc = regf->def_sec_desc;
1337
1338  return tsec;
1339}
1340
1341/*
1342 * Add a sub-key
1343 */
1344static
1345REG_KEY *nt_add_reg_key_list(REGF *regf, REG_KEY *key, char * name, int create)
1346{
1347  int i;
1348  REG_KEY *ret = NULL, *tmp = NULL;
1349  KEY_LIST *list;
1350  char *lname, *c1, *c2;
1351
1352  if (!key || !name || !*name) return NULL;
1353
1354  list = key->sub_keys;
1355  if (!list) { /* Create an empty list */
1356
1357    list = (KEY_LIST *)SMB_MALLOC(sizeof(KEY_LIST) + (REG_KEY_LIST_SIZE - 1) * sizeof(REG_KEY *));
1358    list->key_count = 0;
1359    list->max_keys = REG_KEY_LIST_SIZE;
1360
1361  }
1362
1363  lname = SMB_STRDUP(name);
1364  if (!lname) return NULL;
1365
1366  c1 = lname;
1367  c2 = strchr(c1, '\\');
1368  if (c2) { /* Split here ... */
1369    *c2 = 0;
1370    c2++;
1371  }
1372
1373  for (i = 0; i < list->key_count; i++) {
1374    if (strcmp(list->keys[i]->name, c1) == 0) {
1375      ret = nt_add_reg_key_list(regf, list->keys[i], c2, create);
1376      free(lname);
1377      return ret;
1378    }
1379  }
1380
1381  /*
1382   * If we reach here we could not find the the first component
1383   * so create it ...
1384   */
1385
1386  if (list->key_count < list->max_keys){
1387    list->key_count++;
1388  }
1389  else { /* Create more space in the list ... */
1390    if (!(list = (KEY_LIST *)SMB_REALLOC(list, sizeof(KEY_LIST) +
1391				     (list->max_keys + REG_KEY_LIST_SIZE - 1)
1392				     * sizeof(REG_KEY *))))
1393      goto error;
1394
1395    list->max_keys += REG_KEY_LIST_SIZE;
1396    list->key_count++;
1397  }
1398
1399  /*
1400   * add the new key at the new slot
1401   * FIXME: Sort the list someday
1402   */
1403
1404  /*
1405   * We want to create the key, and then do the rest
1406   */
1407
1408  tmp = SMB_MALLOC_P(REG_KEY);
1409
1410  memset(tmp, 0, sizeof(REG_KEY));
1411
1412  tmp->name = SMB_STRDUP(c1);
1413  if (!tmp->name) goto error;
1414  tmp->owner = key;
1415  tmp->type = REG_SUB_KEY;
1416  /*
1417   * Next, pull security from the parent, but override with
1418   * anything passed in on the command line
1419   */
1420  tmp->security = nt_inherit_security(key);
1421
1422  list->keys[list->key_count - 1] = tmp;
1423
1424  if (c2) {
1425    ret = nt_add_reg_key_list(regf, key, c2, True);
1426  }
1427
1428  if (lname) free(lname);
1429
1430  return ret;
1431
1432 error:
1433  if (tmp) free(tmp);
1434  if (lname) free(lname);
1435  return NULL;
1436}
1437
1438/*
1439 * This routine only adds a key from the root down.
1440 * It calls helper functions to handle sub-key lists and sub-keys
1441 */
1442static
1443REG_KEY *nt_add_reg_key(REGF *regf, char *name, int create)
1444{
1445  char *lname = NULL, *c1, *c2;
1446  REG_KEY * tmp = NULL;
1447
1448  /*
1449   * Look until we hit the first component that does not exist, and
1450   * then add from there. However, if the first component does not
1451   * match and the path we are given is the root, then it must match
1452   */
1453  if (!regf || !name || !*name) return NULL;
1454
1455  lname = SMB_STRDUP(name);
1456  if (!lname) return NULL;
1457
1458  c1 = lname;
1459  c2 = strchr(c1, '\\');
1460  if (c2) { /* Split here ... */
1461    *c2 = 0;
1462    c2++;
1463  }
1464
1465  /*
1466   * If the root does not exist, create it and make it equal to the
1467   * first component ...
1468   */
1469
1470  if (!regf->root) {
1471
1472    tmp = SMB_MALLOC_P(REG_KEY);
1473    if (!tmp) goto error;
1474    memset(tmp, 0, sizeof(REG_KEY));
1475    tmp->name = SMB_STRDUP(c1);
1476    if (!tmp->name) goto error;
1477    tmp->security = nt_create_init_sec(regf);
1478    if (!tmp->security) goto error;
1479    regf->root = tmp;
1480
1481  }
1482  else {
1483    /*
1484     * If we don't match, then we have to return error ...
1485     * If we do match on this component, check the next one in the
1486     * list, and if not found, add it ... short circuit, add all the
1487     * way down
1488     */
1489
1490    if (strcmp(c1, regf->root->name) != 0)
1491      goto error;
1492  }
1493
1494  tmp = nt_add_reg_key_list(regf, regf->root, c2, True);
1495  free(lname);
1496  return tmp;
1497
1498 error:
1499  if (tmp) free(tmp);
1500  if (lname) free(lname);
1501  return NULL;
1502}
1503
1504/*
1505 * Load and unload a registry file.
1506 *
1507 * Load, loads it into memory as a tree, while unload sealizes/flattens it
1508 */
1509
1510/*
1511 * Get the starting record for NT Registry file
1512 */
1513
1514/*
1515 * Where we keep all the regf stuff for one registry.
1516 * This is the structure that we use to tie the in memory tree etc
1517 * together. By keeping separate structs, we can operate on different
1518 * registries at the same time.
1519 * Currently, the SK_MAP is an array of mapping structure.
1520 * Since we only need this on input and output, we fill in the structure
1521 * as we go on input. On output, we know how many SK items we have, so
1522 * we can allocate the structure as we need to.
1523 * If you add stuff here that is dynamically allocated, add the
1524 * appropriate free statements below.
1525 */
1526
1527#define REGF_REGTYPE_NONE 0
1528#define REGF_REGTYPE_NT   1
1529#define REGF_REGTYPE_W9X  2
1530
1531#define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
1532                              (r)->last_mod_time.high = (t2);
1533
1534#define REGF_HDR_BLKSIZ 0x1000
1535
1536#define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4)
1537#define LOCN(base, f) ((base) + OFF(f))
1538
1539const VAL_STR reg_type_names[] = {
1540   { REG_TYPE_REGSZ,    "REG_SZ" },
1541   { REG_TYPE_EXPANDSZ, "REG_EXPAND_SZ" },
1542   { REG_TYPE_BIN,      "REG_BIN" },
1543   { REG_TYPE_DWORD,    "REG_DWORD" },
1544   { REG_TYPE_MULTISZ,  "REG_MULTI_SZ" },
1545   { 0, NULL },
1546};
1547
1548static
1549const char *val_to_str(unsigned int val, const VAL_STR *val_array)
1550{
1551  int i = 0;
1552
1553  if (!val_array) return NULL;
1554
1555  while (val_array[i].val && val_array[i].str) {
1556
1557    if (val_array[i].val == val) return val_array[i].str;
1558    i++;
1559
1560  }
1561
1562  return NULL;
1563
1564}
1565
1566/*
1567 * Convert from UniCode to Ascii ... Does not take into account other lang
1568 * Restrict by ascii_max if > 0
1569 */
1570static
1571int uni_to_ascii(unsigned char *uni, unsigned char *ascii, int ascii_max,
1572		 int uni_max)
1573{
1574  int i = 0;
1575
1576  while (i < ascii_max && !(!uni[i*2] && !uni[i*2+1])) {
1577    if (uni_max > 0 && (i*2) >= uni_max) break;
1578    ascii[i] = uni[i*2];
1579    i++;
1580
1581  }
1582
1583  ascii[i] = '\0';
1584
1585  return i;
1586}
1587
1588/*
1589 * Convert a data value to a string for display
1590 */
1591static
1592int data_to_ascii(unsigned char *datap, int len, int type, char *ascii, int ascii_max)
1593{
1594  unsigned char *asciip;
1595  int i;
1596
1597  switch (type) {
1598  case REG_TYPE_REGSZ:
1599    if (verbose) fprintf(stderr, "Len: %d\n", len);
1600    /* FIXME. This has to be fixed. It has to be UNICODE */
1601    return uni_to_ascii(datap, ascii, len, ascii_max);
1602    break; /*NOTREACHED*/
1603
1604  case REG_TYPE_EXPANDSZ:
1605    return uni_to_ascii(datap, ascii, len, ascii_max);
1606    break;
1607
1608  case REG_TYPE_BIN:
1609    asciip = ascii;
1610    for (i=0; (i<len)&&(i+1)*3<ascii_max; i++) {
1611      int str_rem = ascii_max - ((int)asciip - (int)ascii);
1612      asciip += snprintf(asciip, str_rem, "%02x", *(unsigned char *)(datap+i));
1613      if (i < len && str_rem > 0)
1614	*asciip = ' '; asciip++;
1615    }
1616    *asciip = '\0';
1617    return ((int)asciip - (int)ascii);
1618    break;
1619
1620  case REG_TYPE_DWORD:
1621    if (*(int *)datap == 0)
1622      return snprintf(ascii, ascii_max, "0");
1623    else
1624      return snprintf(ascii, ascii_max, "0x%x", *(int *)datap);
1625    break;
1626
1627  case REG_TYPE_MULTISZ:
1628
1629    break;
1630
1631  default:
1632    return 0;
1633    break;
1634  }
1635
1636  return len;
1637
1638}
1639
1640static
1641REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent);
1642
1643static
1644int nt_set_regf_input_file(REGF *regf, char *filename)
1645{
1646  return ((regf->regfile_name = SMB_STRDUP(filename)) != NULL);
1647}
1648
1649static
1650int nt_set_regf_output_file(REGF *regf, char *filename)
1651{
1652  return ((regf->outfile_name = SMB_STRDUP(filename)) != NULL);
1653}
1654
1655/* Create a regf structure and init it */
1656
1657static
1658REGF *nt_create_regf(void)
1659{
1660  REGF *tmp = SMB_MALLOC_P(REGF);
1661  if (!tmp) return tmp;
1662  memset(tmp, 0, sizeof(REGF));
1663  tmp->owner_sid_str = def_owner_sid_str;
1664  return tmp;
1665}
1666
1667/* Free all the bits and pieces ... Assumes regf was malloc'd */
1668/* If you add stuff to REGF, add the relevant free bits here  */
1669static
1670int nt_free_regf(REGF *regf)
1671{
1672  if (!regf) return 0;
1673
1674  if (regf->regfile_name) free(regf->regfile_name);
1675  if (regf->outfile_name) free(regf->outfile_name);
1676
1677  nt_delete_reg_key(regf->root, False); /* Free the tree */
1678  free(regf->sk_map);
1679  regf->sk_count = regf->sk_map_size = 0;
1680
1681  free(regf);
1682
1683  return 1;
1684}
1685
1686/* Get the header of the registry. Return a pointer to the structure
1687 * If the mmap'd area has not been allocated, then mmap the input file
1688 */
1689static
1690REGF_HDR *nt_get_regf_hdr(REGF *regf)
1691{
1692  if (!regf)
1693    return NULL; /* What about errors */
1694
1695  if (!regf->regfile_name)
1696    return NULL; /* What about errors */
1697
1698  if (!regf->base) { /* Try to mmap etc the file */
1699
1700    if ((regf->fd = open(regf->regfile_name, O_RDONLY, 0000)) <0) {
1701      return NULL; /* What about errors? */
1702    }
1703
1704    if (fstat(regf->fd, &regf->sbuf) < 0) {
1705      return NULL;
1706    }
1707
1708    regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
1709
1710    if ((int)regf->base == 1) {
1711      fprintf(stderr, "Could not mmap file: %s, %s\n", regf->regfile_name,
1712	      strerror(errno));
1713      return NULL;
1714    }
1715  }
1716
1717  /*
1718   * At this point, regf->base != NULL, and we should be able to read the
1719   * header
1720   */
1721
1722  assert(regf->base != NULL);
1723
1724  return (REGF_HDR *)regf->base;
1725}
1726
1727/*
1728 * Validate a regf header
1729 * For now, do nothing, but we should check the checksum
1730 */
1731static
1732int valid_regf_hdr(REGF_HDR *regf_hdr)
1733{
1734  if (!regf_hdr) return 0;
1735
1736  return 1;
1737}
1738
1739/*
1740 * Process an SK header ...
1741 * Every time we see a new one, add it to the map. Otherwise, just look it up.
1742 * We will do a simple linear search for the moment, since many KEYs have the
1743 * same security descriptor.
1744 * We allocate the map in increments of 10 entries.
1745 */
1746
1747/*
1748 * Create a new entry in the map, and increase the size of the map if needed
1749 */
1750static
1751SK_MAP *alloc_sk_map_entry(REGF *regf, KEY_SEC_DESC *tmp, int sk_off)
1752{
1753 if (!regf->sk_map) { /* Allocate a block of 10 */
1754    regf->sk_map = SMB_MALLOC_ARRAY(SK_MAP, 10);
1755    if (!regf->sk_map) {
1756      free(tmp);
1757      return NULL;
1758    }
1759    regf->sk_map_size = 10;
1760    regf->sk_count = 1;
1761    (regf->sk_map)[0].sk_off = sk_off;
1762    (regf->sk_map)[0].key_sec_desc = tmp;
1763  }
1764  else { /* Simply allocate a new slot, unless we have to expand the list */
1765    int ndx = regf->sk_count;
1766    if (regf->sk_count >= regf->sk_map_size) {
1767      regf->sk_map = (SK_MAP *)SMB_REALLOC(regf->sk_map,
1768				       (regf->sk_map_size + 10)*sizeof(SK_MAP));
1769      if (!regf->sk_map) {
1770	free(tmp);
1771	return NULL;
1772      }
1773      /*
1774       * ndx already points at the first entry of the new block
1775       */
1776      regf->sk_map_size += 10;
1777    }
1778    (regf->sk_map)[ndx].sk_off = sk_off;
1779    (regf->sk_map)[ndx].key_sec_desc = tmp;
1780    regf->sk_count++;
1781  }
1782 return regf->sk_map;
1783}
1784
1785/*
1786 * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
1787 * found
1788 */
1789static
1790KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
1791{
1792  int i;
1793
1794  if (!sk_map) return NULL;
1795
1796  for (i = 0; i < count; i++) {
1797
1798    if (sk_map[i].sk_off == sk_off)
1799      return sk_map[i].key_sec_desc;
1800
1801  }
1802
1803  return NULL;
1804
1805}
1806
1807/*
1808 * Allocate a KEY_SEC_DESC if we can't find one in the map
1809 */
1810static
1811KEY_SEC_DESC *lookup_create_sec_key(REGF *regf, SK_MAP *sk_map, int sk_off)
1812{
1813  KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
1814
1815  if (tmp) {
1816    return tmp;
1817  }
1818  else { /* Allocate a new one */
1819    tmp = SMB_MALLOC_P(KEY_SEC_DESC);
1820    if (!tmp) {
1821      return NULL;
1822    }
1823    memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */
1824    tmp->state = SEC_DESC_RES;
1825    if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
1826      return NULL;
1827    }
1828    return tmp;
1829  }
1830}
1831
1832/*
1833 * Allocate storage and duplicate a SID
1834 * We could allocate the SID to be only the size needed, but I am too lazy.
1835 */
1836static
1837sid_t *dup_sid(sid_t *sid)
1838{
1839  sid_t *tmp = SMB_MALLOC_P(sid_t);
1840  int i;
1841
1842  if (!tmp) return NULL;
1843  tmp->ver = sid->ver;
1844  tmp->auths = sid->auths;
1845  for (i=0; i<6; i++) {
1846    tmp->auth[i] = sid->auth[i];
1847  }
1848  for (i=0; i<tmp->auths&&i<MAXSUBAUTHS; i++) {
1849    tmp->sub_auths[i] = sid->sub_auths[i];
1850  }
1851  return tmp;
1852}
1853
1854/*
1855 * Allocate space for an ACE and duplicate the registry encoded one passed in
1856 */
1857static
1858ACE *dup_ace(REG_ACE *ace)
1859{
1860  ACE *tmp = NULL;
1861
1862  tmp = SMB_MALLOC_P(ACE);
1863
1864  if (!tmp) return NULL;
1865
1866  tmp->type = CVAL(&ace->type);
1867  tmp->flags = CVAL(&ace->flags);
1868  tmp->perms = IVAL(&ace->perms);
1869  tmp->trustee = dup_sid(&ace->trustee);
1870  return tmp;
1871}
1872
1873/*
1874 * Allocate space for an ACL and duplicate the registry encoded one passed in
1875 */
1876static
1877ACL *dup_acl(REG_ACL *acl)
1878{
1879  ACL *tmp = NULL;
1880  REG_ACE* ace;
1881  int i, num_aces;
1882
1883  num_aces = IVAL(&acl->num_aces);
1884
1885  tmp = (ACL *)SMB_MALLOC(sizeof(ACL) + (num_aces - 1)*sizeof(ACE *));
1886  if (!tmp) return NULL;
1887
1888  tmp->num_aces = num_aces;
1889  tmp->refcnt = 1;
1890  tmp->rev = SVAL(&acl->rev);
1891  if (verbose) fprintf(stdout, "ACL: refcnt: %u, rev: %u\n", tmp->refcnt,
1892		       tmp->rev);
1893  ace = (REG_ACE *)&acl->aces;
1894  for (i=0; i<num_aces; i++) {
1895    tmp->aces[i] = dup_ace(ace);
1896    ace = (REG_ACE *)((char *)ace + SVAL(&ace->length));
1897    /* XXX: FIXME, should handle malloc errors */
1898  }
1899
1900  return tmp;
1901}
1902
1903static
1904SEC_DESC *process_sec_desc(REGF *regf, REG_SEC_DESC *sec_desc)
1905{
1906  SEC_DESC *tmp = NULL;
1907
1908  tmp = SMB_MALLOC_P(SEC_DESC);
1909
1910  if (!tmp) {
1911    return NULL;
1912  }
1913
1914  tmp->rev = SVAL(&sec_desc->rev);
1915  tmp->type = SVAL(&sec_desc->type);
1916  if (verbose) fprintf(stdout, "SEC_DESC Rev: %0X, Type: %0X\n",
1917		       tmp->rev, tmp->type);
1918  if (verbose) fprintf(stdout, "SEC_DESC Owner Off: %0X\n",
1919		       IVAL(&sec_desc->owner_off));
1920  if (verbose) fprintf(stdout, "SEC_DESC Group Off: %0X\n",
1921		       IVAL(&sec_desc->group_off));
1922  if (verbose) fprintf(stdout, "SEC_DESC DACL Off: %0X\n",
1923		       IVAL(&sec_desc->dacl_off));
1924  tmp->owner = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->owner_off)));
1925  if (!tmp->owner) {
1926    free(tmp);
1927    return NULL;
1928  }
1929  tmp->group = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->group_off)));
1930  if (!tmp->group) {
1931    free(tmp);
1932    return NULL;
1933  }
1934
1935  /* Now pick up the SACL and DACL */
1936
1937  if (sec_desc->sacl_off)
1938    tmp->sacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->sacl_off)));
1939  else
1940    tmp->sacl = NULL;
1941
1942  if (sec_desc->dacl_off)
1943    tmp->dacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->dacl_off)));
1944  else
1945    tmp->dacl = NULL;
1946
1947  return tmp;
1948}
1949
1950static
1951KEY_SEC_DESC *process_sk(REGF *regf, SK_HDR *sk_hdr, int sk_off, int size)
1952{
1953  KEY_SEC_DESC *tmp = NULL;
1954  int sk_next_off, sk_prev_off, sk_size;
1955  REG_SEC_DESC *sec_desc;
1956
1957  if (!sk_hdr) return NULL;
1958
1959  if (SVAL(&sk_hdr->SK_ID) != REG_SK_ID) {
1960    fprintf(stderr, "Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
1961	    regf->regfile_name);
1962    return NULL;
1963  }
1964
1965  if (-size < (sk_size = IVAL(&sk_hdr->rec_size))) {
1966    fprintf(stderr, "Incorrect SK record size: %d vs %d. %s\n",
1967	    -size, sk_size, regf->regfile_name);
1968    return NULL;
1969  }
1970
1971  /*
1972   * Now, we need to look up the SK Record in the map, and return it
1973   * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
1974   * use that
1975   */
1976
1977  if (regf->sk_map &&
1978      ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
1979      && (tmp->state == SEC_DESC_OCU)) {
1980    tmp->ref_cnt++;
1981    return tmp;
1982  }
1983
1984  /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
1985
1986  assert(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
1987
1988  /*
1989   * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
1990   * new KEY_SEC_DESC to the mapping structure, since the offset supplied is
1991   * the actual offset of structure. The same offset will be used by
1992   * all future references to this structure
1993   * We could put all this unpleasantness in a function.
1994   */
1995
1996  if (!tmp) {
1997    tmp = SMB_MALLOC_P(KEY_SEC_DESC);
1998    if (!tmp) return NULL;
1999    memset(tmp, 0, sizeof(KEY_SEC_DESC));
2000
2001    /*
2002     * Allocate an entry in the SK_MAP ...
2003     * We don't need to free tmp, because that is done for us if the
2004     * sm_map entry can't be expanded when we need more space in the map.
2005     */
2006
2007    if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
2008      return NULL;
2009    }
2010  }
2011
2012  tmp->ref_cnt++;
2013  tmp->state = SEC_DESC_OCU;
2014
2015  /*
2016   * Now, process the actual sec desc and plug the values in
2017   */
2018
2019  sec_desc = (REG_SEC_DESC *)&sk_hdr->sec_desc[0];
2020  tmp->sec_desc = process_sec_desc(regf, sec_desc);
2021
2022  /*
2023   * Now forward and back links. Here we allocate an entry in the sk_map
2024   * if it does not exist, and mark it reserved
2025   */
2026
2027  sk_prev_off = IVAL(&sk_hdr->prev_off);
2028  tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
2029  assert(tmp->prev != NULL);
2030  sk_next_off = IVAL(&sk_hdr->next_off);
2031  tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
2032  assert(tmp->next != NULL);
2033
2034  return tmp;
2035}
2036
2037/*
2038 * Process a VK header and return a value
2039 */
2040static
2041VAL_KEY *process_vk(REGF *regf, VK_HDR *vk_hdr, int size)
2042{
2043  char val_name[1024];
2044  int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
2045  const char *val_type;
2046  VAL_KEY *tmp = NULL;
2047
2048  if (!vk_hdr) return NULL;
2049
2050  if ((vk_id = SVAL(&vk_hdr->VK_ID)) != REG_VK_ID) {
2051    fprintf(stderr, "Unrecognized VK header ID: %0X, block: %0X, %s\n",
2052	    vk_id, (int)vk_hdr, regf->regfile_name);
2053    return NULL;
2054  }
2055
2056  nam_len = SVAL(&vk_hdr->nam_len);
2057  val_name[nam_len] = '\0';
2058  flag = SVAL(&vk_hdr->flag);
2059  dat_type = IVAL(&vk_hdr->dat_type);
2060  dat_len = IVAL(&vk_hdr->dat_len);  /* If top bit, offset contains data */
2061  dat_off = IVAL(&vk_hdr->dat_off);
2062
2063  tmp = SMB_MALLOC_P(VAL_KEY);
2064  if (!tmp) {
2065    goto error;
2066  }
2067  memset(tmp, 0, sizeof(VAL_KEY));
2068  tmp->has_name = flag;
2069  tmp->data_type = dat_type;
2070
2071  if (flag & 0x01) {
2072    strncpy(val_name, vk_hdr->dat_name, nam_len);
2073    tmp->name = SMB_STRDUP(val_name);
2074    if (!tmp->name) {
2075      goto error;
2076    }
2077  }
2078  else
2079    strncpy(val_name, "<No Name>", 10);
2080
2081  /*
2082   * Allocate space and copy the data as a BLOB
2083   */
2084
2085  if (dat_len) {
2086
2087    char *dtmp = (char *)SMB_MALLOC(dat_len&0x7FFFFFFF);
2088
2089    if (!dtmp) {
2090      goto error;
2091    }
2092
2093    tmp->data_blk = dtmp;
2094
2095    if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
2096      char *dat_ptr = LOCN(regf->base, dat_off);
2097      bcopy(dat_ptr, dtmp, dat_len);
2098    }
2099    else { /* The data is in the offset or type */
2100      /*
2101       * FIXME.
2102       * Some registry files seem to have wierd fields. If top bit is set,
2103       * but len is 0, the type seems to be the value ...
2104       * Not sure how to handle this last type for the moment ...
2105       */
2106      dat_len = dat_len & 0x7FFFFFFF;
2107      bcopy(&dat_off, dtmp, dat_len);
2108    }
2109
2110    tmp->data_len = dat_len;
2111  }
2112
2113  val_type = val_to_str(dat_type, reg_type_names);
2114
2115  /*
2116   * We need to save the data area as well
2117   */
2118
2119  if (verbose) fprintf(stdout, "  %s : %s : \n", val_name, val_type);
2120
2121  return tmp;
2122
2123 error:
2124  if (tmp) nt_delete_val_key(tmp);
2125  return NULL;
2126
2127}
2128
2129/*
2130 * Process a VL Header and return a list of values
2131 */
2132static
2133VAL_LIST *process_vl(REGF *regf, VL_TYPE vl, int count, int size)
2134{
2135  int i, vk_off;
2136  VK_HDR *vk_hdr;
2137  VAL_LIST *tmp = NULL;
2138
2139  if (!vl) return NULL;
2140
2141  if (-size < (count+1)*sizeof(int)){
2142    fprintf(stderr, "Error in VL header format. Size less than space required. %d\n", -size);
2143    return NULL;
2144  }
2145
2146  tmp = (VAL_LIST *)SMB_MALLOC(sizeof(VAL_LIST) + (count - 1) * sizeof(VAL_KEY *));
2147  if (!tmp) {
2148    goto error;
2149  }
2150
2151  for (i=0; i<count; i++) {
2152    vk_off = IVAL(&vl[i]);
2153    vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
2154    tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr));
2155    if (!tmp->vals[i]){
2156      goto error;
2157    }
2158  }
2159
2160  tmp->val_count = count;
2161  tmp->max_vals = count;
2162
2163  return tmp;
2164
2165 error:
2166  /* XXX: FIXME, free the partially allocated structure */
2167  return NULL;
2168}
2169
2170/*
2171 * Process an LF Header and return a list of sub-keys
2172 */
2173static
2174KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size, REG_KEY *parent)
2175{
2176  int count, i, nk_off;
2177  unsigned int lf_id;
2178  KEY_LIST *tmp;
2179
2180  if (!lf_hdr) return NULL;
2181
2182  if ((lf_id = SVAL(&lf_hdr->LF_ID)) != REG_LF_ID) {
2183    fprintf(stderr, "Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
2184	    lf_id, (int)lf_hdr, regf->regfile_name);
2185    return NULL;
2186  }
2187
2188  assert(size < 0);
2189
2190  count = SVAL(&lf_hdr->key_count);
2191  if (verbose) fprintf(stdout, "Key Count: %u\n", count);
2192  if (count <= 0) return NULL;
2193
2194  /* Now, we should allocate a KEY_LIST struct and fill it in ... */
2195
2196  tmp = (KEY_LIST *)SMB_MALLOC(sizeof(KEY_LIST) + (count - 1) * sizeof(REG_KEY *));
2197  if (!tmp) {
2198    goto error;
2199  }
2200
2201  tmp->key_count = count;
2202  tmp->max_keys = count;
2203
2204  for (i=0; i<count; i++) {
2205    NK_HDR *nk_hdr;
2206
2207    nk_off = IVAL(&lf_hdr->hr[i].nk_off);
2208    if (verbose) fprintf(stdout, "NK Offset: %0X\n", nk_off);
2209    nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
2210    tmp->keys[i] = nt_get_key_tree(regf, nk_hdr, BLK_SIZE(nk_hdr), parent);
2211    if (!tmp->keys[i]) {
2212      goto error;
2213    }
2214  }
2215
2216  return tmp;
2217
2218 error:
2219  if (tmp) nt_delete_key_list(tmp, False);
2220  return NULL;
2221}
2222
2223/*
2224 * This routine is passed an NK_HDR pointer and retrieves the entire tree
2225 * from there down. It returns a REG_KEY *.
2226 */
2227static
2228REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent)
2229{
2230  REG_KEY *tmp = NULL, *own;
2231  int name_len, clsname_len, lf_off, val_off, val_count, sk_off, own_off;
2232  unsigned int nk_id;
2233  LF_HDR *lf_hdr;
2234  VL_TYPE *vl;
2235  SK_HDR *sk_hdr;
2236  char key_name[1024], cls_name[1024];
2237
2238  if (!nk_hdr) return NULL;
2239
2240  if ((nk_id = SVAL(&nk_hdr->NK_ID)) != REG_NK_ID) {
2241    fprintf(stderr, "Unrecognized NK Header format: %08X, Block: %0X. %s\n",
2242	    nk_id, (int)nk_hdr, regf->regfile_name);
2243    return NULL;
2244  }
2245
2246  assert(size < 0);
2247
2248  name_len = SVAL(&nk_hdr->nam_len);
2249  clsname_len = SVAL(&nk_hdr->clsnam_len);
2250
2251  /*
2252   * The value of -size should be ge
2253   * (sizeof(NK_HDR) - 1 + name_len)
2254   * The -1 accounts for the fact that we included the first byte of
2255   * the name in the structure. clsname_len is the length of the thing
2256   * pointed to by clsnam_off
2257   */
2258
2259  if (-size < (sizeof(NK_HDR) - 1 + name_len)) {
2260    fprintf(stderr, "Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr);
2261    fprintf(stderr, "Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
2262	    sizeof(NK_HDR), name_len, clsname_len);
2263    /*return NULL;*/
2264  }
2265
2266  if (verbose) fprintf(stdout, "NK HDR: Name len: %d, class name len: %d\n",
2267		       name_len, clsname_len);
2268
2269  /* Fish out the key name and process the LF list */
2270
2271  assert(name_len < sizeof(key_name));
2272
2273  /* Allocate the key struct now */
2274  tmp = SMB_MALLOC_P(REG_KEY);
2275  if (!tmp) return tmp;
2276  memset(tmp, 0, sizeof(REG_KEY));
2277
2278  tmp->type = (SVAL(&nk_hdr->type)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
2279
2280  strncpy(key_name, nk_hdr->key_nam, name_len);
2281  key_name[name_len] = '\0';
2282
2283  if (verbose) fprintf(stdout, "Key name: %s\n", key_name);
2284
2285  tmp->name = SMB_STRDUP(key_name);
2286  if (!tmp->name) {
2287    goto error;
2288  }
2289
2290  /*
2291   * Fish out the class name, it is in UNICODE, while the key name is
2292   * ASCII :-)
2293   */
2294
2295  if (clsname_len) { /* Just print in Ascii for now */
2296    char *clsnamep;
2297    int clsnam_off;
2298
2299    clsnam_off = IVAL(&nk_hdr->clsnam_off);
2300    clsnamep = LOCN(regf->base, clsnam_off);
2301    if (verbose) fprintf(stdout, "Class Name Offset: %0X\n", clsnam_off);
2302
2303    memset(cls_name, 0, clsname_len);
2304    uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len);
2305
2306    /*
2307     * I am keeping class name as an ascii string for the moment.
2308     * That means it needs to be converted on output.
2309     * It will also piss off people who need Unicode/UTF-8 strings. Sorry.
2310     * XXX: FIXME
2311     */
2312
2313    tmp->class_name = SMB_STRDUP(cls_name);
2314    if (!tmp->class_name) {
2315      goto error;
2316    }
2317
2318    if (verbose) fprintf(stdout, "  Class Name: %s\n", cls_name);
2319
2320  }
2321
2322  /*
2323   * Process the owner offset ...
2324   */
2325
2326  own_off = IVAL(&nk_hdr->own_off);
2327  own = (REG_KEY *)LOCN(regf->base, own_off);
2328  if (verbose) fprintf(stdout, "Owner Offset: %0X\n", own_off);
2329
2330  if (verbose) fprintf(stdout, "  Owner locn: %0X, Our locn: %0X\n",
2331		       (unsigned int)own, (unsigned int)nk_hdr);
2332
2333  /*
2334   * We should verify that the owner field is correct ...
2335   * for now, we don't worry ...
2336   */
2337
2338  tmp->owner = parent;
2339
2340  /*
2341   * If there are any values, process them here
2342   */
2343
2344  val_count = IVAL(&nk_hdr->val_cnt);
2345  if (verbose) fprintf(stdout, "Val Count: %d\n", val_count);
2346  if (val_count) {
2347
2348    val_off = IVAL(&nk_hdr->val_off);
2349    vl = (VL_TYPE *)LOCN(regf->base, val_off);
2350    if (verbose) fprintf(stdout, "Val List Offset: %0X\n", val_off);
2351
2352    tmp->values = process_vl(regf, *vl, val_count, BLK_SIZE(vl));
2353    if (!tmp->values) {
2354      goto error;
2355    }
2356
2357  }
2358
2359  /*
2360   * Also handle the SK header ...
2361   */
2362
2363  sk_off = IVAL(&nk_hdr->sk_off);
2364  sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
2365  if (verbose) fprintf(stdout, "SK Offset: %0X\n", sk_off);
2366
2367  if (sk_off != -1) {
2368
2369    tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
2370
2371  }
2372
2373  lf_off = IVAL(&nk_hdr->lf_off);
2374  if (verbose) fprintf(stdout, "SubKey list offset: %0X\n", lf_off);
2375
2376  /*
2377   * No more subkeys if lf_off == -1
2378   */
2379
2380  if (lf_off != -1) {
2381
2382    lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
2383
2384    tmp->sub_keys = process_lf(regf, lf_hdr, BLK_SIZE(lf_hdr), tmp);
2385    if (!tmp->sub_keys){
2386      goto error;
2387    }
2388
2389  }
2390
2391  return tmp;
2392
2393 error:
2394  if (tmp) nt_delete_reg_key(tmp, False);
2395  return NULL;
2396}
2397
2398static
2399int nt_load_registry(REGF *regf)
2400{
2401  REGF_HDR *regf_hdr;
2402  unsigned int regf_id, hbin_id;
2403  HBIN_HDR *hbin_hdr;
2404  NK_HDR *first_key;
2405
2406  /* Get the header */
2407
2408  if ((regf_hdr = nt_get_regf_hdr(regf)) == NULL) {
2409    return -1;
2410  }
2411
2412  /* Now process that header and start to read the rest in */
2413
2414  if ((regf_id = IVAL(&regf_hdr->REGF_ID)) != REG_REGF_ID) {
2415    fprintf(stderr, "Unrecognized NT registry header id: %0X, %s\n",
2416	    regf_id, regf->regfile_name);
2417    return -1;
2418  }
2419
2420  /*
2421   * Validate the header ...
2422   */
2423  if (!valid_regf_hdr(regf_hdr)) {
2424    fprintf(stderr, "Registry file header does not validate: %s\n",
2425	    regf->regfile_name);
2426    return -1;
2427  }
2428
2429  /* Update the last mod date, and then go get the first NK record and on */
2430
2431  TTTONTTIME(regf, IVAL(&regf_hdr->tim1), IVAL(&regf_hdr->tim2));
2432
2433  /*
2434   * The hbin hdr seems to be just uninteresting garbage. Check that
2435   * it is there, but that is all.
2436   */
2437
2438  hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
2439
2440  if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID)) != REG_HBIN_ID) {
2441    fprintf(stderr, "Unrecognized registry hbin hdr ID: %0X, %s\n",
2442	    hbin_id, regf->regfile_name);
2443    return -1;
2444  }
2445
2446  /*
2447   * Get a pointer to the first key from the hreg_hdr
2448   */
2449
2450  if (verbose) fprintf(stdout, "First Key: %0X\n",
2451		       IVAL(&regf_hdr->first_key));
2452
2453  first_key = (NK_HDR *)LOCN(regf->base, IVAL(&regf_hdr->first_key));
2454  if (verbose) fprintf(stdout, "First Key Offset: %0X\n",
2455		       IVAL(&regf_hdr->first_key));
2456
2457  if (verbose) fprintf(stdout, "Data Block Size: %d\n",
2458		       IVAL(&regf_hdr->dblk_size));
2459
2460  if (verbose) fprintf(stdout, "Offset to next hbin block: %0X\n",
2461		       IVAL(&hbin_hdr->off_to_next));
2462
2463  if (verbose) fprintf(stdout, "HBIN block size: %0X\n",
2464		       IVAL(&hbin_hdr->blk_size));
2465
2466  /*
2467   * Now, get the registry tree by processing that NK recursively
2468   */
2469
2470  regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key), NULL);
2471
2472  assert(regf->root != NULL);
2473
2474  /*
2475   * Unmap the registry file, as we might want to read in another
2476   * tree etc.
2477   */
2478
2479  if (regf->base) munmap(regf->base, regf->sbuf.st_size);
2480  regf->base = NULL;
2481  close(regf->fd);    /* Ignore the error :-) */
2482
2483  return 1;
2484}
2485
2486/*
2487 * Allocate a new hbin block, set up the header for the block etc
2488 */
2489static
2490HBIN_BLK *nt_create_hbin_blk(REGF *regf, int size)
2491{
2492  HBIN_BLK *tmp;
2493  HBIN_HDR *hdr;
2494
2495  if (!regf || !size) return NULL;
2496
2497  /* Round size up to multiple of REGF_HDR_BLKSIZ */
2498
2499  size = (size + (REGF_HDR_BLKSIZ - 1)) & ~(REGF_HDR_BLKSIZ - 1);
2500
2501  tmp = (HBIN_BLK *)SMB_MALLOC_P(HBIN_BLK);
2502  memset(tmp, 0, sizeof(HBIN_BLK));
2503
2504  tmp->data = SMB_MALLOC(size);
2505  if (!tmp->data) goto error;
2506
2507  memset(tmp->data, 0, size);  /* Make it pristine */
2508
2509  tmp->size = size;
2510  tmp->file_offset = regf->blk_tail->file_offset + regf->blk_tail->size;
2511
2512  tmp->free_space = size - (sizeof(HBIN_HDR) - sizeof(HBIN_SUB_HDR));
2513  tmp->fsp_off = size - tmp->free_space;
2514
2515  /*
2516   * Now, build the header in the data block
2517   */
2518  hdr = (HBIN_HDR *)tmp->data;
2519  hdr->HBIN_ID = REG_HBIN_ID;
2520  hdr->off_from_first = tmp->file_offset - REGF_HDR_BLKSIZ;
2521  hdr->off_to_next = tmp->size;
2522  hdr->blk_size = tmp->size;
2523
2524  /*
2525   * Now link it in
2526   */
2527
2528  regf->blk_tail->next = tmp;
2529  regf->blk_tail = tmp;
2530  if (!regf->free_space) regf->free_space = tmp;
2531
2532  return tmp;
2533 error:
2534  if (tmp) free(tmp);
2535  return NULL;
2536}
2537
2538/*
2539 * Allocate a unit of space ... and return a pointer as function param
2540 * and the block's offset as a side effect
2541 */
2542static
2543void *nt_alloc_regf_space(REGF *regf, int size, unsigned int *off)
2544{
2545  int tmp = 0;
2546  void *ret = NULL;
2547  HBIN_BLK *blk;
2548
2549  if (!regf || !size || !off) return NULL;
2550
2551  assert(regf->blk_head != NULL);
2552
2553  /*
2554   * round up size to include header and then to 8-byte boundary
2555   */
2556  size = (size + 4 + 7) & ~7;
2557
2558  /*
2559   * Check if there is space, if none, grab a block
2560   */
2561  if (!regf->free_space) {
2562    if (!nt_create_hbin_blk(regf, REGF_HDR_BLKSIZ))
2563      return NULL;
2564  }
2565
2566  /*
2567   * Now, chain down the list of blocks looking for free space
2568   */
2569
2570  for (blk = regf->free_space; blk != NULL; blk = blk->next) {
2571    if (blk->free_space <= size) {
2572      tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
2573      ret = blk->data + blk->fsp_off;
2574      blk->free_space -= size;
2575      blk->fsp_off += size;
2576
2577      /* Insert the header */
2578      ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
2579
2580      /*
2581       * Fix up the free space ptr
2582       * If it is NULL, we fix it up next time
2583       */
2584
2585      if (!blk->free_space)
2586	regf->free_space = blk->next;
2587
2588      *off = tmp;
2589      return (((char *)ret)+4);/* The pointer needs to be to the data struct */
2590    }
2591  }
2592
2593  /*
2594   * If we got here, we need to add another block, which might be
2595   * larger than one block -- deal with that later
2596   */
2597  if (nt_create_hbin_blk(regf, REGF_HDR_BLKSIZ)) {
2598    blk = regf->free_space;
2599    tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
2600    ret = blk->data + blk->fsp_off;
2601    blk->free_space -= size;
2602    blk->fsp_off += size;
2603
2604    /* Insert the header */
2605    ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
2606
2607    /*
2608     * Fix up the free space ptr
2609     * If it is NULL, we fix it up next time
2610     */
2611
2612    if (!blk->free_space)
2613      regf->free_space = blk->next;
2614
2615    *off = tmp;
2616    return (((char *)ret) + 4);/* The pointer needs to be to the data struct */
2617  }
2618
2619  return NULL;
2620}
2621
2622/*
2623 * Compute the size of a SID stored ...
2624 */
2625static
2626unsigned int sid_size(sid_t *sid)
2627{
2628  unsigned int size;
2629
2630  if (!sid) return 0;
2631
2632  size = 8 + (sid->auths * sizeof(unsigned int));
2633
2634  return size;
2635}
2636
2637/*
2638 * Compute the size of an ACE on disk from its components
2639 */
2640static
2641unsigned int ace_size(ACE *ace)
2642{
2643  unsigned int size;
2644
2645  if (!ace) return 0;
2646
2647  size = 8 + sid_size(ace->trustee);
2648
2649  return size;
2650}
2651
2652/*
2653 * Compute the size of an ACL from its components ...
2654 */
2655static
2656unsigned int acl_size(ACL *acl)
2657{
2658  unsigned int size;
2659  int i;
2660
2661  if (!acl) return 0;
2662
2663  size = 8;
2664  for (i = 0; i < acl->num_aces; i++)
2665    size += ace_size(acl->aces[i]);
2666
2667  return size;
2668}
2669
2670/*
2671 * Compute the size of the sec desc as a self-relative SD
2672 */
2673static
2674unsigned int sec_desc_size(SEC_DESC *sd)
2675{
2676  unsigned int size;
2677
2678  if (!sd) return 0;
2679
2680  size = 20;
2681
2682  if (sd->owner) size += sid_size(sd->owner);
2683  if (sd->group) size += sid_size(sd->group);
2684  if (sd->sacl) size += acl_size(sd->sacl);
2685  if (sd->dacl) size += acl_size(sd->dacl);
2686
2687  return size;
2688}
2689
2690/*
2691 * Store a SID at the location provided
2692 */
2693static
2694int nt_store_SID(REGF *regf, sid_t *sid, unsigned char *locn)
2695{
2696  int i;
2697  unsigned char *p = locn;
2698
2699  if (!regf || !sid || !locn) return 0;
2700
2701  *p = sid->ver; p++;
2702  *p = sid->auths; p++;
2703
2704  for (i=0; i < 6; i++) {
2705    *p = sid->auth[i]; p++;
2706  }
2707
2708  for (i=0; i < sid->auths; i++) {
2709    SIVAL(p, sid->sub_auths[i]); p+=4;
2710  }
2711
2712  return p - locn;
2713
2714}
2715
2716static
2717int nt_store_ace(REGF *regf, ACE *ace, unsigned char *locn)
2718{
2719  int size = 0;
2720  REG_ACE *reg_ace = (REG_ACE *)locn;
2721  unsigned char *p;
2722
2723  if (!regf || !ace || !locn) return 0;
2724
2725  reg_ace->type = ace->type;
2726  reg_ace->flags = ace->flags;
2727
2728  /* Deal with the length when we have stored the SID */
2729
2730  p = (unsigned char *)&reg_ace->perms;
2731
2732  SIVAL(p, ace->perms); p += 4;
2733
2734  size = nt_store_SID(regf, ace->trustee, p);
2735
2736  size += 8; /* Size of the fixed header */
2737
2738  p = (unsigned char *)&reg_ace->length;
2739
2740  SSVAL(p, size);
2741
2742  return size;
2743}
2744
2745/*
2746 * Store an ACL at the location provided
2747 */
2748static
2749int nt_store_acl(REGF *regf, ACL *acl, unsigned char *locn)
2750{
2751  int size = 0, i;
2752  unsigned char *p = locn, *s;
2753
2754  if (!regf || !acl || !locn) return 0;
2755
2756  /*
2757   * Now store the header and then the ACEs ...
2758   */
2759
2760  SSVAL(p, acl->rev);
2761
2762  p += 2; s = p; /* Save this for the size field */
2763
2764  p += 2;
2765
2766  SIVAL(p, acl->num_aces);
2767
2768  p += 4;
2769
2770  for (i = 0; i < acl->num_aces; i++) {
2771    size = nt_store_ace(regf, acl->aces[i], p);
2772    p += size;
2773  }
2774
2775  size = s - locn;
2776  SSVAL(s, size);
2777  return size;
2778}
2779
2780/*
2781 * Flatten and store the Sec Desc
2782 * Windows lays out the DACL first, but since there is no SACL, it might be
2783 * that first, then the owner, then the group SID. So, we do it that way
2784 * too.
2785 */
2786static
2787unsigned int nt_store_sec_desc(REGF *regf, SEC_DESC *sd, char *locn)
2788{
2789  REG_SEC_DESC *rsd = (REG_SEC_DESC *)locn;
2790  unsigned int size = 0, off = 0;
2791
2792  if (!regf || !sd || !locn) return 0;
2793
2794  /*
2795   * Now, fill in the first two fields, then lay out the various fields
2796   * as needed
2797   */
2798
2799  rsd->rev = 0x01;
2800  /* Self relative, DACL pres, owner and group not defaulted */
2801  rsd->type = 0x8004;
2802
2803  off = 4 * sizeof(DWORD) + 4;
2804
2805  if (sd->sacl){
2806    size = nt_store_acl(regf, sd->sacl, (char *)(locn + off));
2807    rsd->sacl_off = off;
2808  }
2809  else
2810    rsd->sacl_off = 0;
2811
2812  off += size;
2813
2814  if (sd->dacl) {
2815    rsd->dacl_off = off;
2816    size = nt_store_acl(regf, sd->dacl, (char *)(locn + off));
2817  }
2818  else {
2819    rsd->dacl_off = 0;
2820  }
2821
2822  off += size;
2823
2824  /* Now the owner and group SIDs */
2825
2826  if (sd->owner) {
2827    rsd->owner_off = off;
2828    size = nt_store_SID(regf, sd->owner, (char *)(locn + off));
2829  }
2830  else {
2831    rsd->owner_off = 0;
2832  }
2833
2834  off += size;
2835
2836  if (sd->group) {
2837    rsd->group_off = off;
2838    size = nt_store_SID(regf, sd->group, (char *)(locn + off));
2839  }
2840  else {
2841    rsd->group_off = 0;
2842  }
2843
2844  off += size;
2845
2846  return size;
2847}
2848
2849/*
2850 * Store the security information
2851 *
2852 * If it has already been stored, just get its offset from record
2853 * otherwise, store it and record its offset
2854 */
2855static
2856unsigned int nt_store_security(REGF *regf, KEY_SEC_DESC *sec)
2857{
2858  int size = 0;
2859  unsigned int sk_off;
2860  SK_HDR *sk_hdr;
2861
2862  if (sec->offset) return sec->offset;
2863
2864  /*
2865   * OK, we don't have this one in the file yet. We must compute the
2866   * size taken by the security descriptor as a self-relative SD, which
2867   * means making one pass over each structure and figuring it out
2868   */
2869
2870  size = sec_desc_size(sec->sec_desc);
2871
2872  /* Allocate that much space */
2873
2874  sk_hdr = nt_alloc_regf_space(regf, size, &sk_off);
2875  sec->sk_hdr = sk_hdr;
2876
2877  if (!sk_hdr) return 0;
2878
2879  /* Now, lay out the sec_desc in the space provided */
2880
2881  sk_hdr->SK_ID = REG_SK_ID;
2882
2883  /*
2884   * We can't deal with the next and prev offset in the SK_HDRs until the
2885   * whole tree has been stored, then we can go and deal with them
2886   */
2887
2888  sk_hdr->ref_cnt = sec->ref_cnt;
2889  sk_hdr->rec_size = size;       /* Is this correct */
2890
2891  /* Now, lay out the sec_desc */
2892
2893  if (!nt_store_sec_desc(regf, sec->sec_desc, (char *)&sk_hdr->sec_desc))
2894    return 0;
2895
2896  return sk_off;
2897
2898}
2899
2900/*
2901 * Store a VAL LIST
2902 */
2903static
2904int nt_store_val_list(REGF *regf, VAL_LIST * values)
2905{
2906
2907  return 0;
2908}
2909
2910/*
2911 * Store a KEY in the file ...
2912 *
2913 * We store this depth first, and defer storing the lf struct until
2914 * all the sub-keys have been stored.
2915 *
2916 * We store the NK hdr, any SK header, class name, and VK structure, then
2917 * recurse down the LF structures ...
2918 *
2919 * We return the offset of the NK struct
2920 * FIXME, FIXME, FIXME: Convert to using SIVAL and SSVAL ...
2921 */
2922static
2923int nt_store_reg_key(REGF *regf, REG_KEY *key)
2924{
2925  NK_HDR *nk_hdr;
2926  unsigned int nk_off, sk_off, size;
2927
2928  if (!regf || !key) return 0;
2929
2930  size = sizeof(NK_HDR) + strlen(key->name) - 1;
2931  nk_hdr = nt_alloc_regf_space(regf, size, &nk_off);
2932  if (!nk_hdr) goto error;
2933
2934  key->offset = nk_off;  /* We will need this later */
2935
2936  /*
2937   * Now fill in each field etc ...
2938   */
2939
2940  nk_hdr->NK_ID = REG_NK_ID;
2941  if (key->type == REG_ROOT_KEY)
2942    nk_hdr->type = 0x2C;
2943  else
2944    nk_hdr->type = 0x20;
2945
2946  /* FIXME: Fill in the time of last update */
2947
2948  if (key->type != REG_ROOT_KEY)
2949    nk_hdr->own_off = key->owner->offset;
2950
2951  if (key->sub_keys)
2952    nk_hdr->subk_num = key->sub_keys->key_count;
2953
2954  /*
2955   * Now, process the Sec Desc and then store its offset
2956   */
2957
2958  sk_off = nt_store_security(regf, key->security);
2959  nk_hdr->sk_off = sk_off;
2960
2961  /*
2962   * Then, store the val list and store its offset
2963   */
2964  if (key->values) {
2965    nk_hdr->val_cnt = key->values->val_count;
2966    nk_hdr->val_off = nt_store_val_list(regf, key->values);
2967  }
2968  else {
2969    nk_hdr->val_off = -1;
2970    nk_hdr->val_cnt = 0;
2971  }
2972
2973  /*
2974   * Finally, store the subkeys, and their offsets
2975   */
2976
2977 error:
2978  return 0;
2979}
2980
2981/*
2982 * Store the registry header ...
2983 * We actually create the registry header block and link it to the chain
2984 * of output blocks.
2985 */
2986static
2987REGF_HDR *nt_get_reg_header(REGF *regf)
2988{
2989  HBIN_BLK *tmp = NULL;
2990
2991  tmp = SMB_MALLOC_P(HBIN_BLK);
2992  if (!tmp) return 0;
2993
2994  memset(tmp, 0, sizeof(HBIN_BLK));
2995  tmp->type = REG_OUTBLK_HDR;
2996  tmp->size = REGF_HDR_BLKSIZ;
2997  tmp->data = SMB_MALLOC(REGF_HDR_BLKSIZ);
2998  if (!tmp->data) goto error;
2999
3000  memset(tmp->data, 0, REGF_HDR_BLKSIZ);  /* Make it pristine, unlike Windows */
3001  regf->blk_head = regf->blk_tail = tmp;
3002
3003  return (REGF_HDR *)tmp->data;
3004
3005 error:
3006  if (tmp) free(tmp);
3007  return NULL;
3008}
3009
3010/*
3011 * Store the registry in the output file
3012 * We write out the header and then each of the keys etc into the file
3013 * We have to flatten the data structure ...
3014 *
3015 * The structures are stored in a depth-first fashion, with all records
3016 * aligned on 8-byte boundaries, with sub-keys and values layed down before
3017 * the lists that contain them. SK records are layed down first, however.
3018 * The lf fields are layed down after all sub-keys have been layed down, it
3019 * seems, including the whole tree associated with each sub-key.
3020 */
3021static
3022int nt_store_registry(REGF *regf)
3023{
3024  REGF_HDR *reg;
3025  int fkey, fd;
3026
3027  /*
3028   * Get a header ... and partially fill it in ...
3029   */
3030  reg = nt_get_reg_header(regf);
3031
3032  /*
3033   * Store the first key, which will store the whole thing
3034   */
3035  fkey = nt_store_reg_key(regf, regf->root);
3036
3037  /*
3038   * At this point we have the registry as a series of blocks, so
3039   * run down that series of blocks and save them ...
3040   */
3041
3042  if (!regf->outfile_name) {
3043    fprintf(stderr, "Cannot write file without a name!\n");
3044    return 0;
3045  }
3046
3047  if ((fd = open(regf->outfile_name, O_WRONLY, 0666)) < 0) {
3048    fprintf(stderr, "Unable to create file %s: %s\n", regf->outfile_name,
3049	    strerror(errno));
3050    return 0;
3051  }
3052
3053  return 1;
3054}
3055
3056/*
3057 * Routines to parse a REGEDIT4 file
3058 *
3059 * The file consists of:
3060 *
3061 * REGEDIT4
3062 * \[[-]key-path\]\n
3063 * <value-spec>*
3064 *
3065 * Format:
3066 * [cmd:]name=type:value
3067 *
3068 * cmd = a|d|c|add|delete|change|as|ds|cs
3069 *
3070 * There can be more than one key-path and value-spec.
3071 *
3072 * Since we want to support more than one type of file format, we
3073 * construct a command-file structure that keeps info about the command file
3074 */
3075
3076#define FMT_UNREC -1
3077#define FMT_REGEDIT4 0
3078#define FMT_EDITREG1_1 1
3079
3080#define FMT_STRING_REGEDIT4 "REGEDIT4"
3081#define FMT_STRING_EDITREG1_0 "EDITREG1.0"
3082
3083#define CMD_NONE     0
3084#define CMD_ADD_KEY  1
3085#define CMD_DEL_KEY  2
3086
3087#define CMD_KEY 1
3088#define CMD_VAL 2
3089
3090typedef struct val_spec_list {
3091  struct val_spec_list *next;
3092  char *name;
3093  int type;
3094  char *val;    /* Kept as a char string, really? */
3095} VAL_SPEC_LIST;
3096
3097typedef struct command_s {
3098  int cmd;
3099  char *key;
3100  int val_count;
3101  VAL_SPEC_LIST *val_spec_list, *val_spec_last;
3102} CMD;
3103
3104typedef struct cmd_line {
3105  int len, line_len;
3106  char *line;
3107} CMD_LINE;
3108
3109static
3110void free_val_spec_list(VAL_SPEC_LIST *vl)
3111{
3112  if (!vl) return;
3113  if (vl->name) free(vl->name);
3114  if (vl->val) free(vl->val);
3115  free(vl);
3116
3117}
3118
3119/*
3120 * Some routines to handle lines of info in the command files
3121 */
3122static
3123void skip_to_eol(int fd)
3124{
3125  int rc;
3126  char ch = 0;
3127
3128  while ((rc = read(fd, &ch, 1)) == 1) {
3129    if (ch == 0x0A) return;
3130  }
3131  if (rc < 0) {
3132    fprintf(stderr, "Could not read file descriptor: %d, %s\n",
3133	    fd, strerror(errno));
3134    exit(1);
3135  }
3136}
3137
3138static
3139void free_cmd(CMD *cmd)
3140{
3141  if (!cmd) return;
3142
3143  while (cmd->val_spec_list) {
3144    VAL_SPEC_LIST *tmp;
3145
3146    tmp = cmd->val_spec_list;
3147    cmd->val_spec_list = tmp->next;
3148    free(tmp);
3149  }
3150
3151  free(cmd);
3152
3153}
3154
3155static
3156void free_cmd_line(CMD_LINE *cmd_line)
3157{
3158  if (cmd_line) {
3159    if (cmd_line->line) free(cmd_line->line);
3160    free(cmd_line);
3161  }
3162}
3163
3164static
3165void print_line(struct cmd_line *cl)
3166{
3167  char *pl;
3168
3169  if (!cl) return;
3170
3171  if ((pl = SMB_MALLOC(cl->line_len + 1)) == NULL) {
3172    fprintf(stderr, "Unable to allocate space to print line: %s\n",
3173	    strerror(errno));
3174    exit(1);
3175  }
3176
3177  strncpy(pl, cl->line, cl->line_len);
3178  pl[cl->line_len] = 0;
3179
3180  fprintf(stdout, "%s\n", pl);
3181  free(pl);
3182}
3183
3184#define INIT_ALLOC 10
3185
3186/*
3187 * Read a line from the input file.
3188 * NULL returned when EOF and no chars read
3189 * Otherwise we return a cmd_line *
3190 * Exit if other errors
3191 */
3192static
3193struct cmd_line *get_cmd_line(int fd)
3194{
3195  struct cmd_line *cl = SMB_MALLOC_P(CMD_LINE);
3196  int i = 0, rc;
3197  unsigned char ch;
3198
3199  if (!cl) {
3200    fprintf(stderr, "Unable to allocate structure for command line: %s\n",
3201	    strerror(errno));
3202    exit(1);
3203  }
3204
3205  cl->len = INIT_ALLOC;
3206
3207  /*
3208   * Allocate some space for the line. We extend later if needed.
3209   */
3210
3211  if ((cl->line = (char *)SMB_MALLOC(INIT_ALLOC)) == NULL) {
3212    fprintf(stderr, "Unable to allocate initial space for line: %s\n",
3213	    strerror(errno));
3214    exit(1);
3215  }
3216
3217  /*
3218   * Now read in the chars to EOL. Don't store the EOL in the
3219   * line. What about CR?
3220   */
3221
3222  while ((rc = read(fd, &ch, 1)) == 1 && ch != '\n') {
3223    if (ch == '\r') continue; /* skip CR */
3224    if (i == cl->len) {
3225      /*
3226       * Allocate some more memory
3227       */
3228      if ((cl->line = SMB_REALLOC(cl->line, cl->len + INIT_ALLOC)) == NULL) {
3229	fprintf(stderr, "Unable to realloc space for line: %s\n",
3230		strerror(errno));
3231	exit(1);
3232      }
3233      cl->len += INIT_ALLOC;
3234    }
3235    cl->line[i] = ch;
3236    i++;
3237  }
3238
3239  /* read 0 and we were at loc'n 0, return NULL */
3240  if (rc == 0 && i == 0) {
3241    free_cmd_line(cl);
3242    return NULL;
3243  }
3244
3245  cl->line_len = i;
3246
3247  return cl;
3248
3249}
3250
3251/*
3252 * parse_value: parse out a value. We pull it apart as:
3253 *
3254 * <value> ::= <value-name>=<type>:<value-string>
3255 *
3256 * <value-name> ::= char-string-without-spaces | '"' char-string '"'
3257 *
3258 * If it parsed OK, return the <value-name> as a string, and the
3259 * value type and value-string in parameters.
3260 *
3261 * The value name can be empty. There can only be one empty name in
3262 * a list of values. A value of - removes the value entirely.
3263 */
3264static
3265char *dup_str(char *s, int len)
3266{
3267  char *nstr;
3268  nstr = (char *)SMB_MALLOC(len + 1);
3269  if (nstr) {
3270    memcpy(nstr, s, len);
3271    nstr[len] = 0;
3272  }
3273  return nstr;
3274}
3275
3276static
3277char *parse_name(char *nstr)
3278{
3279  int len = 0, start = 0;
3280  if (!nstr) return NULL;
3281
3282  len = strlen(nstr);
3283
3284  while (len && nstr[len - 1] == ' ') len--;
3285
3286  nstr[len] = 0; /* Trim any spaces ... if there were none, doesn't matter */
3287
3288  /*
3289   * Beginning and end should be '"' or neither should be so
3290   */
3291  if ((nstr[0] == '"' && nstr[len - 1] != '"') ||
3292      (nstr[0] != '"' && nstr[len - 1] == '"'))
3293    return NULL;
3294
3295  if (nstr[0] == '"') {
3296    start = 1;
3297    len -= 2;
3298  }
3299
3300  return dup_str(&nstr[start], len);
3301}
3302
3303static
3304int parse_value_type(char *tstr)
3305{
3306  int len = strlen(tstr);
3307
3308  while (len && tstr[len - 1] == ' ') len--;
3309  tstr[len] = 0;
3310
3311  if (strcmp(tstr, "REG_DWORD") == 0)
3312    return REG_TYPE_DWORD;
3313  else if (strcmp(tstr, "dword") == 0)
3314    return REG_TYPE_DWORD;
3315  else if (strcmp(tstr, "REG_EXPAND_SZ") == 0)
3316    return REG_TYPE_EXPANDSZ;
3317  else if (strcmp(tstr, "REG_BIN") == 0)
3318    return REG_TYPE_BIN;
3319  else if (strcmp(tstr, "REG_SZ") == 0)
3320    return REG_TYPE_REGSZ;
3321  else if (strcmp(tstr, "REG_MULTI_SZ") == 0)
3322    return REG_TYPE_MULTISZ;
3323  else if (strcmp(tstr, "-") == 0)
3324    return REG_TYPE_DELETE;
3325
3326  return 0;
3327}
3328
3329static
3330char *parse_val_str(char *vstr)
3331{
3332
3333  return dup_str(vstr, strlen(vstr));
3334
3335}
3336
3337static
3338char *parse_value(struct cmd_line *cl, int *vtype, char **val)
3339{
3340  char *p1 = NULL, *p2 = NULL, *nstr = NULL, *tstr = NULL, *vstr = NULL;
3341
3342  if (!cl || !vtype || !val) return NULL;
3343  if (!cl->line_len) return NULL;
3344
3345  p1 = dup_str(cl->line, cl->line_len);
3346  /* FIXME: Better return codes etc ... */
3347  if (!p1) return NULL;
3348  p2 = strchr(p1, '=');
3349  if (!p2) return NULL;
3350
3351  *p2 = 0; p2++; /* Split into two strings at p2 */
3352
3353  /* Now, parse the name ... */
3354
3355  nstr = parse_name(p1);
3356  if (!nstr) goto error;
3357
3358  /* Now, split the remainder and parse on type and val ... */
3359
3360  tstr = p2;
3361  while (*tstr == ' ') tstr++; /* Skip leading white space */
3362  p2 = strchr(p2, ':');
3363
3364  if (p2) {
3365    *p2 = 0; p2++; /* split on the : */
3366  }
3367
3368  *vtype = parse_value_type(tstr);
3369
3370  if (!vtype) goto error;
3371
3372  if (!p2 || !*p2) return nstr;
3373
3374  /* Now, parse the value string. It should return a newly malloc'd string */
3375
3376  while (*p2 == ' ') p2++; /* Skip leading space */
3377  vstr = parse_val_str(p2);
3378
3379  if (!vstr) goto error;
3380
3381  *val = vstr;
3382
3383  return nstr;
3384
3385 error:
3386  if (p1) free(p1);
3387  if (nstr) free(nstr);
3388  if (vstr) free(vstr);
3389  return NULL;
3390}
3391
3392/*
3393 * Parse out a key. Look for a correctly formatted key [...]
3394 * and whether it is a delete or add? A delete is signalled
3395 * by a - in front of the key.
3396 * Assumes that there are no leading and trailing spaces
3397 */
3398
3399static
3400char *parse_key(struct cmd_line *cl, int *cmd)
3401{
3402  int start = 1;
3403  char *tmp;
3404
3405  if (cl->line[0] != '[' ||
3406      cl->line[cl->line_len - 1] != ']') return NULL;
3407  if (cl->line_len == 2) return NULL;
3408  *cmd = CMD_ADD_KEY;
3409  if (cl->line[1] == '-') {
3410    if (cl->line_len == 3) return NULL;
3411    start = 2;
3412    *cmd = CMD_DEL_KEY;
3413  }
3414  tmp = SMB_MALLOC(cl->line_len - 1 - start + 1);
3415  if (!tmp) return tmp; /* Bail out on no mem ... FIXME */
3416  strncpy(tmp, &cl->line[start], cl->line_len - 1 - start);
3417  tmp[cl->line_len - 1 - start] = 0;
3418  return tmp;
3419}
3420
3421/*
3422 * Parse a line to determine if we have a key or a value
3423 * We only check for key or val ...
3424 */
3425
3426static
3427int parse_line(struct cmd_line *cl)
3428{
3429
3430  if (!cl || cl->len == 0) return 0;
3431
3432  if (cl->line[0] == '[')  /* No further checking for now */
3433    return CMD_KEY;
3434  else
3435    return CMD_VAL;
3436}
3437
3438/*
3439 * We seek to offset 0, read in the required number of bytes,
3440 * and compare to the correct value.
3441 * We then seek back to the original location
3442 */
3443static
3444int regedit4_file_type(int fd)
3445{
3446  int cur_ofs = 0;
3447  char desc[9];
3448
3449  cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
3450  if (cur_ofs < 0) {
3451    fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
3452    exit(1);  /* FIXME */
3453  }
3454
3455  if (cur_ofs) {
3456    lseek(fd, 0, SEEK_SET);
3457  }
3458
3459  if (read(fd, desc, 8) < 8) {
3460    fprintf(stderr, "Unable to read command file format\n");
3461    exit(2);  /* FIXME */
3462  }
3463
3464  desc[8] = 0;
3465
3466  if (strcmp(desc, FMT_STRING_REGEDIT4) == 0) {
3467    if (cur_ofs) {
3468      lseek(fd, cur_ofs, SEEK_SET);
3469    }
3470    else {
3471      skip_to_eol(fd);
3472    }
3473    return FMT_REGEDIT4;
3474  }
3475
3476  return FMT_UNREC;
3477}
3478
3479/*
3480 * Run though the data in the line and strip anything after a comment
3481 * char.
3482 */
3483static
3484void strip_comment(struct cmd_line *cl)
3485{
3486  int i;
3487
3488  if (!cl) return;
3489
3490  for (i = 0; i < cl->line_len; i++) {
3491    if (cl->line[i] == ';') {
3492      cl->line_len = i;
3493      return;
3494    }
3495  }
3496}
3497
3498/*
3499 * trim leading space
3500 */
3501
3502static
3503void trim_leading_spaces(struct cmd_line *cl)
3504{
3505  int i;
3506
3507  if (!cl) return;
3508
3509  for (i = 0; i < cl->line_len; i++) {
3510    if (cl->line[i] != ' '){
3511      if (i) memcpy(cl->line, &cl->line[i], cl->line_len - i);
3512      return;
3513    }
3514  }
3515}
3516
3517/*
3518 * trim trailing spaces
3519 */
3520static
3521void trim_trailing_spaces(struct cmd_line *cl)
3522{
3523  int i;
3524
3525  if (!cl) return;
3526
3527  for (i = cl->line_len; i == 0; i--) {
3528    if (cl->line[i-1] != ' ' &&
3529	cl->line[i-1] != '\t') {
3530      cl->line_len = i;
3531    }
3532  }
3533}
3534
3535/*
3536 * Get a command ... This consists of possibly multiple lines:
3537 * [key]
3538 * values*
3539 * possibly Empty line
3540 *
3541 * value ::= <value-name>=<value-type>':'<value-string>
3542 * <value-name> is some path, possibly enclosed in quotes ...
3543 * We alctually look for the next key to terminate a previous key
3544 * if <value-type> == '-', then it is a delete type.
3545 */
3546static
3547CMD *regedit4_get_cmd(int fd)
3548{
3549  struct command_s *cmd = NULL;
3550  struct cmd_line *cl = NULL;
3551  struct val_spec_list *vl = NULL;
3552
3553  if ((cmd = SMB_MALLOC_P(struct command_s)) == NULL) {
3554    fprintf(stderr, "Unable to malloc space for command: %s\n",
3555	    strerror(errno));
3556    exit(1);
3557  }
3558
3559  cmd->cmd = CMD_NONE;
3560  cmd->key = NULL;
3561  cmd->val_count = 0;
3562  cmd->val_spec_list = cmd->val_spec_last = NULL;
3563  while ((cl = get_cmd_line(fd))) {
3564
3565    /*
3566     * If it is an empty command line, and we already have a key
3567     * then exit from here ... FIXME: Clean up the parser
3568     */
3569
3570    if (cl->line_len == 0 && cmd->key) {
3571      free_cmd_line(cl);
3572      break;
3573    }
3574
3575    strip_comment(cl);     /* remove anything beyond a comment char */
3576    trim_trailing_spaces(cl);
3577    trim_leading_spaces(cl);
3578
3579    if (cl->line_len == 0) {    /* An empty line */
3580      free_cmd_line(cl);
3581    }
3582    else {                 /* Else, non-empty ... */
3583      /*
3584       * Parse out the bits ...
3585       */
3586      switch (parse_line(cl)) {
3587      case CMD_KEY:
3588	if ((cmd->key = parse_key(cl, &cmd->cmd)) == NULL) {
3589	  fprintf(stderr, "Error parsing key from line: ");
3590	  print_line(cl);
3591	  fprintf(stderr, "\n");
3592	}
3593	break;
3594
3595      case CMD_VAL:
3596	/*
3597	 * We need to add the value stuff to the list
3598	 * There could be a \ on the end which we need to
3599	 * handle at some time
3600	 */
3601	vl = SMB_MALLOC_P(struct val_spec_list);
3602	if (!vl) goto error;
3603	vl->next = NULL;
3604	vl->val = NULL;
3605	vl->name = parse_value(cl, &vl->type, &vl->val);
3606	if (!vl->name) goto error;
3607	if (cmd->val_spec_list == NULL) {
3608	  cmd->val_spec_list = cmd->val_spec_last = vl;
3609	}
3610	else {
3611	  cmd->val_spec_last->next = vl;
3612	  cmd->val_spec_last = vl;
3613	}
3614	cmd->val_count++;
3615	break;
3616
3617      default:
3618	fprintf(stderr, "Unrecognized line in command file: \n");
3619	print_line(cl);
3620	break;
3621      }
3622    }
3623
3624  }
3625  if (!cmd->cmd) goto error; /* End of file ... */
3626
3627  return cmd;
3628
3629 error:
3630  if (vl) free(vl);
3631  if (cmd) free_cmd(cmd);
3632  return NULL;
3633}
3634
3635static
3636int regedit4_exec_cmd(CMD *cmd)
3637{
3638
3639  return 0;
3640}
3641
3642static
3643int editreg_1_0_file_type(int fd)
3644{
3645  int cur_ofs = 0;
3646  char desc[11];
3647
3648  cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
3649  if (cur_ofs < 0) {
3650    fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
3651    exit(1);  /* FIXME */
3652  }
3653
3654  if (cur_ofs) {
3655    lseek(fd, 0, SEEK_SET);
3656  }
3657
3658  if (read(fd, desc, 10) < 10) {
3659    fprintf(stderr, "Unable to read command file format\n");
3660    exit(2);  /* FIXME */
3661  }
3662
3663  desc[10] = 0;
3664
3665  if (strcmp(desc, FMT_STRING_EDITREG1_0) == 0) {
3666    lseek(fd, cur_ofs, SEEK_SET);
3667    return FMT_REGEDIT4;
3668  }
3669
3670  return FMT_UNREC;
3671}
3672
3673static
3674CMD *editreg_1_0_get_cmd(int fd)
3675{
3676  return NULL;
3677}
3678
3679static
3680int editreg_1_0_exec_cmd(CMD *cmd)
3681{
3682
3683  return -1;
3684}
3685
3686typedef struct command_ops_s {
3687  int type;
3688  int (*file_type)(int fd);
3689  CMD *(*get_cmd)(int fd);
3690  int (*exec_cmd)(CMD *cmd);
3691} CMD_OPS;
3692
3693CMD_OPS default_cmd_ops[] = {
3694  {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd},
3695  {1, editreg_1_0_file_type, editreg_1_0_get_cmd, editreg_1_0_exec_cmd},
3696  {-1,  NULL, NULL, NULL}
3697};
3698
3699typedef struct command_file_s {
3700  char *name;
3701  int type, fd;
3702  CMD_OPS cmd_ops;
3703} CMD_FILE;
3704
3705/*
3706 * Create a new command file structure
3707 */
3708
3709static
3710CMD_FILE *cmd_file_create(char *file)
3711{
3712  CMD_FILE *tmp;
3713  struct stat sbuf;
3714  int i = 0;
3715
3716  /*
3717   * Let's check if the file exists ...
3718   * No use creating the cmd_file structure if the file does not exist
3719   */
3720
3721  if (stat(file, &sbuf) < 0) { /* Not able to access file */
3722
3723    return NULL;
3724  }
3725
3726  tmp = SMB_MALLOC_P(CMD_FILE);
3727  if (!tmp) {
3728    return NULL;
3729  }
3730
3731  /*
3732   * Let's fill in some of the fields;
3733   */
3734
3735  tmp->name = SMB_STRDUP(file);
3736
3737  if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) {
3738    free(tmp);
3739    return NULL;
3740  }
3741
3742  /*
3743   * Now, try to find the format by indexing through the table
3744   */
3745  while (default_cmd_ops[i].type != -1) {
3746    if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) {
3747      tmp->cmd_ops = default_cmd_ops[i];
3748      return tmp;
3749    }
3750    i++;
3751  }
3752
3753  /*
3754   * If we got here, return NULL, as we could not figure out the type
3755   * of command file.
3756   *
3757   * What about errors?
3758   */
3759
3760  free(tmp);
3761  return NULL;
3762}
3763
3764/*
3765 * Extract commands from the command file, and execute them.
3766 * We pass a table of command callbacks for that
3767 */
3768
3769/*
3770 * Main code from here on ...
3771 */
3772
3773/*
3774 * key print function here ...
3775 */
3776
3777static
3778int print_key(const char *path, char *name, char *class_name, int root,
3779	      int terminal, int vals)
3780{
3781
3782  if (full_print || terminal) fprintf(stdout, "[%s%s]\n", path, name);
3783
3784  return 1;
3785}
3786
3787/*
3788 * Sec Desc print functions
3789 */
3790
3791static
3792void print_type(unsigned char type)
3793{
3794  switch (type) {
3795  case 0x00:
3796    fprintf(stdout, "    ALLOW");
3797    break;
3798  case 0x01:
3799    fprintf(stdout, "     DENY");
3800    break;
3801  case 0x02:
3802    fprintf(stdout, "    AUDIT");
3803    break;
3804  case 0x03:
3805    fprintf(stdout, "    ALARM");
3806    break;
3807  case 0x04:
3808    fprintf(stdout, "ALLOW CPD");
3809    break;
3810  case 0x05:
3811    fprintf(stdout, "OBJ ALLOW");
3812    break;
3813  case 0x06:
3814    fprintf(stdout, " OBJ DENY");
3815  default:
3816    fprintf(stdout, "  UNKNOWN");
3817    break;
3818  }
3819}
3820
3821static
3822void print_flags(unsigned char flags)
3823{
3824  char flg_output[21];
3825  int some = 0;
3826
3827  flg_output[0] = 0;
3828  if (!flags) {
3829    fprintf(stdout, "         ");
3830    return;
3831  }
3832  if (flags & 0x01) {
3833    if (some) strcat(flg_output, ",");
3834    some = 1;
3835    strcat(flg_output, "OI");
3836  }
3837  if (flags & 0x02) {
3838    if (some) strcat(flg_output, ",");
3839    some = 1;
3840    strcat(flg_output, "CI");
3841  }
3842  if (flags & 0x04) {
3843    if (some) strcat(flg_output, ",");
3844    some = 1;
3845    strcat(flg_output, "NP");
3846  }
3847  if (flags & 0x08) {
3848    if (some) strcat(flg_output, ",");
3849    some = 1;
3850    strcat(flg_output, "IO");
3851  }
3852  if (flags & 0x10) {
3853    if (some) strcat(flg_output, ",");
3854    some = 1;
3855    strcat(flg_output, "IA");
3856  }
3857  if (flags == 0xF) {
3858    if (some) strcat(flg_output, ",");
3859    some = 1;
3860    strcat(flg_output, "VI");
3861  }
3862  fprintf(stdout, " %s", flg_output);
3863}
3864
3865static
3866void print_perms(int perms)
3867{
3868  fprintf(stdout, " %8X", perms);
3869}
3870
3871static
3872void print_sid(sid_t *sid)
3873{
3874  int i, comps = sid->auths;
3875  fprintf(stdout, "S-%u-%u", sid->ver, sid->auth[5]);
3876
3877  for (i = 0; i < comps; i++) {
3878
3879    fprintf(stdout, "-%u", sid->sub_auths[i]);
3880
3881  }
3882  fprintf(stdout, "\n");
3883}
3884
3885static
3886void print_acl(ACL *acl, const char *prefix)
3887{
3888  int i;
3889
3890  for (i = 0; i < acl->num_aces; i++) {
3891    fprintf(stdout, ";;%s", prefix);
3892    print_type(acl->aces[i]->type);
3893    print_flags(acl->aces[i]->flags);
3894    print_perms(acl->aces[i]->perms);
3895    fprintf(stdout, " ");
3896    print_sid(acl->aces[i]->trustee);
3897  }
3898}
3899
3900static
3901int print_sec(SEC_DESC *sec_desc)
3902{
3903  if (!print_security) return 1;
3904  fprintf(stdout, ";;  SECURITY\n");
3905  fprintf(stdout, ";;   Owner: ");
3906  print_sid(sec_desc->owner);
3907  fprintf(stdout, ";;   Group: ");
3908  print_sid(sec_desc->group);
3909  if (sec_desc->sacl) {
3910    fprintf(stdout, ";;    SACL:\n");
3911    print_acl(sec_desc->sacl, " ");
3912  }
3913  if (sec_desc->dacl) {
3914    fprintf(stdout, ";;    DACL:\n");
3915    print_acl(sec_desc->dacl, " ");
3916  }
3917  return 1;
3918}
3919
3920/*
3921 * Value print function here ...
3922 */
3923static
3924int print_val(const char *path, char *val_name, int val_type, int data_len,
3925	      void *data_blk, int terminal, int first, int last)
3926{
3927  char data_asc[1024];
3928
3929  memset(data_asc, 0, sizeof(data_asc));
3930  if (!terminal && first)
3931    fprintf(stdout, "%s\n", path);
3932  data_to_ascii((unsigned char *)data_blk, data_len, val_type, data_asc,
3933		sizeof(data_asc) - 1);
3934  fprintf(stdout, "  %s = %s : %s\n", (val_name?val_name:"<No Name>"),
3935		   val_to_str(val_type, reg_type_names), data_asc);
3936  return 1;
3937}
3938
3939static
3940void usage(void)
3941{
3942  fprintf(stderr, "Usage: editreg [-f] [-v] [-p] [-k] [-s] [-c <command-file>] <registryfile>\n");
3943  fprintf(stderr, "Version: 0.1\n\n");
3944  fprintf(stderr, "\n\t-v\t sets verbose mode");
3945  fprintf(stderr, "\n\t-f\t sets full print mode where non-terminals are printed");
3946  fprintf(stderr, "\n\t-p\t prints the registry");
3947  fprintf(stderr, "\n\t-s\t prints security descriptors");
3948  fprintf(stderr, "\n\t-c <command-file>\t specifies a command file");
3949  fprintf(stderr, "\n");
3950}
3951
3952int main(int argc, char *argv[])
3953{
3954  REGF *regf;
3955  extern char *optarg;
3956  extern int optind;
3957  int opt, print_keys = 0;
3958  int regf_opt = 1; /* Command name */
3959  int commands = 0, modified = 0;
3960  char *cmd_file_name = NULL;
3961  char *out_file_name = NULL;
3962  CMD_FILE *cmd_file = NULL;
3963  sid_t *lsid;
3964
3965  if (argc < 2) {
3966    usage();
3967    exit(1);
3968  }
3969
3970  /*
3971   * Now, process the arguments
3972   */
3973
3974  while ((opt = getopt(argc, argv, "fspvko:O:c:")) != EOF) {
3975    switch (opt) {
3976    case 'c':
3977      commands = 1;
3978      cmd_file_name = optarg;
3979      regf_opt += 2;
3980      break;
3981
3982    case 'f':
3983      full_print = 1;
3984      regf_opt++;
3985      break;
3986
3987    case 'o':
3988      out_file_name = optarg;
3989      regf_opt += 2;
3990      break;
3991
3992    case 'O':
3993      def_owner_sid_str = SMB_STRDUP(optarg);
3994      regf_opt += 2;
3995      if (!sid_string_to_sid(&lsid, def_owner_sid_str)) {
3996	fprintf(stderr, "Default Owner SID: %s is incorrectly formatted\n",
3997		def_owner_sid_str);
3998	free(&def_owner_sid_str[0]);
3999	def_owner_sid_str = NULL;
4000      }
4001      else
4002	nt_delete_sid(lsid);
4003      break;
4004
4005    case 'p':
4006      print_keys++;
4007      regf_opt++;
4008      break;
4009
4010    case 's':
4011      print_security++;
4012      full_print++;
4013      regf_opt++;
4014      break;
4015
4016    case 'v':
4017      verbose++;
4018      regf_opt++;
4019      break;
4020
4021    case 'k':
4022      regf_opt++;
4023      break;
4024
4025    default:
4026      usage();
4027      exit(1);
4028      break;
4029    }
4030  }
4031
4032  /*
4033   * We only want to complain about the lack of a default owner SID if
4034   * we need one. This approximates that need
4035   */
4036  if (!def_owner_sid_str) {
4037    def_owner_sid_str = "S-1-5-21-1-2-3-4";
4038    if (out_file_name || verbose)
4039      fprintf(stderr, "Warning, default owner SID not set. Setting to %s\n",
4040	      def_owner_sid_str);
4041  }
4042
4043  if ((regf = nt_create_regf()) == NULL) {
4044    fprintf(stderr, "Could not create registry object: %s\n", strerror(errno));
4045    exit(2);
4046  }
4047
4048  if (regf_opt < argc) { /* We have a registry file */
4049    if (!nt_set_regf_input_file(regf, argv[regf_opt])) {
4050      fprintf(stderr, "Could not set name of registry file: %s, %s\n",
4051	      argv[regf_opt], strerror(errno));
4052      exit(3);
4053    }
4054
4055    /* Now, open it, and bring it into memory :-) */
4056
4057    if (nt_load_registry(regf) < 0) {
4058      fprintf(stderr, "Could not load registry: %s\n", argv[1]);
4059      exit(4);
4060    }
4061  }
4062
4063  if (out_file_name) {
4064    if (!nt_set_regf_output_file(regf, out_file_name)) {
4065      fprintf(stderr, "Could not set name of output registry file: %s, %s\n",
4066	      out_file_name, strerror(errno));
4067      exit(3);
4068    }
4069
4070  }
4071
4072  if (commands) {
4073    CMD *cmd;
4074
4075    cmd_file = cmd_file_create(cmd_file_name);
4076
4077    while ((cmd = cmd_file->cmd_ops.get_cmd(cmd_file->fd)) != NULL) {
4078
4079      /*
4080       * Now, apply the requests to the tree ...
4081       */
4082      switch (cmd->cmd) {
4083      case CMD_ADD_KEY: {
4084	REG_KEY *tmp = NULL;
4085
4086	tmp = nt_find_key_by_name(regf->root, cmd->key);
4087
4088	/* If we found it, apply the other bits, else create such a key */
4089
4090	if (!tmp) {
4091	  tmp = nt_add_reg_key(regf, cmd->key, True);
4092	  modified = 1;
4093	}
4094
4095	while (cmd->val_count) {
4096	  VAL_SPEC_LIST *val = cmd->val_spec_list;
4097	  VAL_KEY *reg_val = NULL;
4098
4099	  if (val->type == REG_TYPE_DELETE) {
4100	    reg_val = nt_delete_reg_value(tmp, val -> name);
4101	    if (reg_val) nt_delete_val_key(reg_val);
4102	    modified = 1;
4103	  }
4104	  else {
4105	    reg_val = nt_add_reg_value(tmp, val->name, val->type,
4106				       val->val);
4107	    modified = 1;
4108	  }
4109
4110	  cmd->val_spec_list = val->next;
4111	  free_val_spec_list(val);
4112	  cmd->val_count--;
4113	}
4114
4115	break;
4116      }
4117
4118      case CMD_DEL_KEY:
4119	/*
4120	 * Any value does not matter ...
4121	 * Find the key if it exists, and delete it ...
4122	 */
4123
4124	nt_delete_key_by_name(regf, cmd->key);
4125	modified = 1;
4126	break;
4127      }
4128    }
4129    free_cmd(cmd);
4130  }
4131
4132  /*
4133   * At this point, we should have a registry in memory and should be able
4134   * to iterate over it.
4135   */
4136
4137  if (print_keys) {
4138    nt_key_iterator(regf, regf->root, 0, "", print_key, print_sec, print_val);
4139  }
4140
4141  /*
4142   * If there was an out_file_name and the tree was modified, print it
4143   */
4144  if (modified && out_file_name)
4145    if (!nt_store_registry(regf)) {
4146      fprintf(stdout, "Error storing registry\n");
4147    }
4148
4149  return 0;
4150}
4151