s3/profiles: improve copy_registry_tree() errors
[samba.git] / source3 / utils / profiles.c
index 9424233e1115ed1b073513271c13c1c7f871ee9a..0f274ad604a2aa94cf2cbcc29ee2f138ea58d9ae 100644 (file)
-/* 
-   Samba Unix/Linux SMB client utility profiles.c 
-   Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
+/*
+   Samba Unix/Linux SMB client utility profiles.c
+
+   Copyright (C) Richard Sharpe, <rsharpe@richardsharpe.com>   2002
+   Copyright (C) Jelmer Vernooij (conversion to popt)          2003
+   Copyright (C) Gerald (Jerry) Carter                         2005
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
-   
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
-   
+
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
-/*************************************************************************
-                                                       
- A utility to report and change SIDs in registry files 
-                                     
- Many of the ideas in here come from other people and software. 
- I first looked in Wine in misc/registry.c and was also influenced by
- http://www.wednesday.demon.co.uk/dosreg.html
-
- Which seems to contain comments from someone else. I reproduce them here
- incase the site above disappears. It actually comes from 
- http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt. 
-
-The windows NT registry has 2 different blocks, where one can occure many
-times...
-
-the "regf"-Block
-================
-"regf" is obviosly the abbreviation for "Registry file". "regf" is the
-signature of the header-block which is always 4kb in size, although only
-the first 64 bytes seem to be used and a checksum is calculated over
-the first 0x200 bytes only!
-
-Offset            Size      Contents
-0x00000000      D-Word      ID: ASCII-"regf" = 0x66676572
-0x00000004      D-Word      ???? //see struct REGF
-0x00000008      D-Word      ???? Always the same value as at 0x00000004
-0x0000000C      Q-Word      last modify date in WinNT date-format
-0x00000014      D-Word      1
-0x00000018      D-Word      3
-0x0000001C      D-Word      0
-0x00000020      D-Word      1
-0x00000024      D-Word      Offset of 1st key record
-0x00000028      D-Word      Size of the data-blocks (Filesize-4kb)
-0x0000002C      D-Word      1
-0x000001FC      D-Word      Sum of all D-Words from 0x00000000 to
-0x000001FB  //XOR of all words. Nigel
-
-I have analyzed more registry files (from multiple machines running
-NT 4.0 german version) and could not find an explanation for the values
-marked with ???? the rest of the first 4kb page is not important...
-
-the "hbin"-Block
-================
-I don't know what "hbin" stands for, but this block is always a multiple
-of 4kb in size.
-
-Inside these hbin-blocks the different records are placed. The memory-
-management looks like a C-compiler heap management to me...
-
-hbin-Header
-===========
-Offset      Size      Contents
-0x0000      D-Word      ID: ASCII-"hbin" = 0x6E696268
-0x0004      D-Word      Offset from the 1st hbin-Block
-0x0008      D-Word      Offset to the next hbin-Block
-0x001C      D-Word      Block-size
-
-The values in 0x0008 and 0x001C should be the same, so I don't know
-if they are correct or swapped...
-
-From offset 0x0020 inside a hbin-block data is stored with the following
-format:
-
-Offset      Size      Contents
-0x0000      D-Word      Data-block size    //this size must be a
-multiple of 8. Nigel
-0x0004      ????      Data
-If the size field is negative (bit 31 set), the corresponding block
-is free and has a size of -blocksize!
-
-The data is stored as one record per block. Block size is a multiple
-of 4 and the last block reaches the next hbin-block, leaving no room.
-
-Records in the hbin-blocks
-==========================
-
-nk-Record
-
-      The nk-record can be treated as a kombination of tree-record and
-      key-record of the win 95 registry.
-
-lf-Record
-
-      The lf-record is the counterpart to the RGKN-record (the
-      hash-function)
-
-vk-Record
-
-      The vk-record consists information to a single value.
-
-sk-Record
-
-      sk (? Security Key ?) is the ACL of the registry.
-
-Value-Lists
-
-      The value-lists contain information about which values are inside a
-      sub-key and don't have a header.
-
-Datas
-
-      The datas of the registry are (like the value-list) stored without a
-      header.
-
-All offset-values are relative to the first hbin-block and point to the
-block-size field of the record-entry. to get the file offset, you have to add
-the header size (4kb) and the size field (4 bytes)...
-
-the nk-Record
-=============
-Offset      Size      Contents
-0x0000      Word      ID: ASCII-"nk" = 0x6B6E
-0x0002      Word      for the root-key: 0x2C, otherwise 0x20  //key symbolic links 0x10. Nigel
-0x0004      Q-Word      write-date/time in windows nt notation
-0x0010      D-Word      Offset of Owner/Parent key
-0x0014      D-Word      number of sub-Keys
-0x001C      D-Word      Offset of the sub-key lf-Records
-0x0024      D-Word      number of values
-0x0028      D-Word      Offset of the Value-List
-0x002C      D-Word      Offset of the sk-Record
-
-0x0030      D-Word      Offset of the Class-Name //see NK structure for the use of these fields. Nigel
-0x0044      D-Word      Unused (data-trash)  //some kind of run time index. Does not appear to be important. Nigel
-0x0048      Word      name-length
-0x004A      Word      class-name length
-0x004C      ????      key-name
-
-the Value-List
-==============
-Offset      Size      Contents
-0x0000      D-Word      Offset 1st Value
-0x0004      D-Word      Offset 2nd Value
-0x????      D-Word      Offset nth Value
-
-To determine the number of values, you have to look at the owner-nk-record!
-
-Der vk-Record
-=============
-Offset      Size      Contents
-0x0000      Word      ID: ASCII-"vk" = 0x6B76
-0x0002      Word      name length
-0x0004      D-Word      length of the data   //if top bit is set when offset contains data. Nigel
-0x0008      D-Word      Offset of Data
-0x000C      D-Word      Type of value
-0x0010      Word      Flag
-0x0012      Word      Unused (data-trash)
-0x0014      ????      Name
-
-If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
-
-If the data-size is lower 5, the data-offset value is used to store the data itself!
-
-The data-types
-==============
-Wert      Beteutung
-0x0001      RegSZ:             character string (in UNICODE!)
-0x0002      ExpandSZ:   string with "%var%" expanding (UNICODE!)
-0x0003      RegBin:           raw-binary value
-0x0004      RegDWord:   Dword
-0x0007      RegMultiSZ:      multiple strings, separated with 0
-                  (UNICODE!)
-
-The "lf"-record
-===============
-Offset      Size      Contents
-0x0000      Word      ID: ASCII-"lf" = 0x666C
-0x0002      Word      number of keys
-0x0004      ????      Hash-Records
-
-Hash-Record
-===========
-Offset      Size      Contents
-0x0000      D-Word      Offset of corresponding "nk"-Record
-0x0004      D-Word      ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
-
-Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the 
-key-name you have to change the hash-value too!
-
-//These hashrecords must be sorted low to high within the lf record. Nigel.
-
-The "sk"-block
-==============
-(due to the complexity of the SAM-info, not clear jet)
-
-Offset      Size      Contents
-0x0000      Word      ID: ASCII-"sk" = 0x6B73
-0x0002      Word      Unused
-0x0004      D-Word      Offset of previous "sk"-Record
-0x0008      D-Word      Offset of next "sk"-Record
-0x000C      D-Word      usage-counter
-0x0010      D-Word      Size of "sk"-record in bytes
-????                                             //standard self
-relative security desciptor. Nigel
-????  ????      Security and auditing settings...
-????
-
-The usage counter counts the number of references to this
-"sk"-record. You can use one "sk"-record for the entire registry!
-
-Windows nt date/time format
-===========================
-The time-format is a 64-bit integer which is incremented every
-0,0000001 seconds by 1 (I don't know how accurate it realy is!)
-It starts with 0 at the 1st of january 1601 0:00! All values are
-stored in GMT time! The time-zone is important to get the real
-time!
-
-Common values for win95 and win-nt
-==================================
-Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
-If a value has no name (length=0, flag(bit 0)=0), it is treated as the
-"Default" entry...
-If a value has no data (length=0), it is displayed as empty.
-
-simplyfied win-3.?? registry:
-=============================
-
-+-----------+
-| next rec. |---+                      +----->+------------+
-| first sub |   |                      |      | Usage cnt. |
-| name      |   |  +-->+------------+  |      | length     |
-| value     |   |  |   | next rec.  |  |      | text       |------->+-------+
-+-----------+   |  |   | name rec.  |--+      +------------+        | xxxxx |
-   +------------+  |   | value rec. |-------->+------------+        +-------+
-   v               |   +------------+         | Usage cnt. |
-+-----------+      |                          | length     |
-| next rec. |      |                          | text       |------->+-------+
-| first sub |------+                          +------------+        | xxxxx |
-| name      |                                                       +-------+
-| value     |
-+-----------+    
-
-Greatly simplyfied structure of the nt-registry:
-================================================
-   
-+---------------------------------------------------------------+
-|                                                               |
-v                                                               |
-+---------+     +---------->+-----------+  +----->+---------+   |
-| "nk"    |     |           | lf-rec.   |  |      | nk-rec. |   |
-| ID      |     |           | # of keys |  |      | parent  |---+
-| Date    |     |           | 1st key   |--+      | ....    |
-| parent  |     |           +-----------+         +---------+
-| suk-keys|-----+
-| values  |--------------------->+----------+
-| SK-rec. |---------------+      | 1. value |--> +----------+
-| class   |--+            |      +----------+    | vk-rec.  |
-+---------+  |            |                      | ....     |
-             v            |                      | data     |--> +-------+
-      +------------+      |                      +----------+    | xxxxx |
-      | Class name |      |                                      +-------+
-      +------------+      |
-                          v
-          +---------+    +---------+
-   +----->| next sk |--->| Next sk |--+
-   |  +---| prev sk |<---| prev sk |  |
-   |  |   | ....    |    | ...     |  |
-   |  |   +---------+    +---------+  |
-   |  |         ^          |          |
-      |         +----------+          |
-      +-------------------------------+
-
----------------------------------------------------------------------------
-
-Hope this helps....  (Although it was "fun" for me to uncover this things,
-                  it took me several sleepless nights ;)
-
-            B.D.
-
-*************************************************************************/
-#include "includes.h"
-#include <stdio.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <sys/mman.h>
-
-typedef unsigned int DWORD;
-typedef unsigned short WORD;
-
-#define REG_REGF_ID 0x66676572
-
-typedef struct regf_block {
-  DWORD REGF_ID;     /* regf */
-  DWORD uk1;
-  DWORD uk2;
-  DWORD tim1, tim2;
-  DWORD uk3;             /* 1 */
-  DWORD uk4;             /* 3 */
-  DWORD uk5;             /* 0 */
-  DWORD uk6;             /* 1 */
-  DWORD first_key;       /* offset */
-  unsigned int dblk_size;
-  DWORD uk7[116];        /* 1 */
-  DWORD chksum;
-} REGF_HDR;
-
-typedef struct hbin_sub_struct {
-  DWORD dblocksize;
-  char data[1];
-} HBIN_SUB_HDR;
-
-#define REG_HBIN_ID 0x6E696268
-
-typedef struct hbin_struct {
-  DWORD HBIN_ID; /* hbin */
-  DWORD next_off;
-  DWORD prev_off;
-  DWORD uk1;
-  DWORD uk2;
-  DWORD uk3;
-  DWORD uk4;
-  DWORD blk_size;
-  HBIN_SUB_HDR hbin_sub_hdr;
-} HBIN_HDR;
-
-#define REG_NK_ID 0x6B6E
-
-typedef struct nk_struct {
-  WORD NK_ID;
-  WORD type;
-  DWORD t1, t2;
-  DWORD uk1;
-  DWORD own_off;
-  DWORD subk_num;
-  DWORD uk2;
-  DWORD lf_off;
-  DWORD uk3;
-  DWORD val_cnt;
-  DWORD val_off;
-  DWORD sk_off;
-  DWORD clsnam_off;
-} NK_HDR;
-
-#define REG_SK_ID 0x6B73
-
-typedef struct sk_struct {
-  WORD SK_ID;
-  WORD uk1;
-  DWORD prev_off;
-  DWORD next_off;
-  DWORD ref_cnt;
-  DWORD rec_size;
-  char sec_desc[1];
-} SK_HDR;
-
-typedef struct sec_desc_rec {
-  WORD rev;
-  WORD type;
-  DWORD owner_off;
-  DWORD group_off;
-  DWORD sacl_off;
-  DWORD dacl_off;
-} MY_SEC_DESC;
-
-typedef struct ace_struct {
-    unsigned char type;
-    unsigned char flags;
-    unsigned short length;
-    unsigned int perms;
-    DOM_SID trustee;
-} ACE;
-
-typedef struct acl_struct {
-  WORD rev;
-  WORD size;
-  DWORD num_aces;
-  ACE *aces;   /* One or more ACEs */
-} ACL;
-
-#define OFF(f) (0x1000 + (f) + 4) 
-
-void print_sid(DOM_SID *sid);
-
-int verbose = 1;
-DOM_SID old_sid, new_sid;
-int change = 0, new = 0;
-
-/* Compare two SIDs for equality */
-int my_sid_equal(DOM_SID *s1, DOM_SID *s2)
-{
-  int sa1, sa2;
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
 
-  if (s1->sid_rev_num != s2->sid_rev_num) return 0;
+#include "includes.h"
+#include "system/filesys.h"
+#include "popt_common.h"
+#include "registry/reg_objects.h"
+#include "registry/regfio.h"
+#include "../libcli/security/security.h"
 
-  sa1 = s1->num_auths; sa2 = s2->num_auths;
+/* GLOBAL VARIABLES */
 
-  if (sa1 != sa2) return 0;
+struct dom_sid old_sid, new_sid;
+int change = 0, new_val = 0;
+int opt_verbose = False;
 
-  return !bcmp((char *)&s1->id_auth, (char *)&s2->id_auth,
-               6 + sa1 * 4);
+/********************************************************************
+********************************************************************/
 
-}
-
-/*
- * Quick and dirty to read a SID in S-1-5-21-x-y-z-rid format and 
- * construct a DOM_SID
- */
-int get_sid(DOM_SID *sid, char *sid_str)
+static void verbose_output(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
+static void verbose_output(const char *format, ...)
 {
-  int i = 0, auth;
-  char *lstr; 
-
-  if (strncmp(sid_str, "S-1-5", 5)) {
-    fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str);
-    return 0;
-  }
-
-  /* We only allow strings of form S-1-5... */
-
-  sid->sid_rev_num = 1;
-  sid->id_auth[5] = 5;
-
-  lstr = sid_str + 5;
-
-  while (1) {
-    if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) {
-      if (i < 4) {
-       fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i);
-       return 0;
-      }
-      sid->num_auths=i;
-      print_sid(sid);
-      return 1;
-    }
-
-    SIVAL(&sid->sub_auths[i], 0, auth);
-    i++;
-    lstr = strchr(lstr + 1, '-'); 
-  }
-
-  return 1;
+       va_list args;
+       char *var = NULL;
+
+       if (!opt_verbose) {
+               return;
+       }
+
+       va_start(args, format);
+       if ((vasprintf(&var, format, args)) == -1) {
+               va_end(args);
+               return;
+       }
+
+       fprintf(stdout, "%s", var);
+       va_end(args);
+       SAFE_FREE(var);
 }
 
-/* 
- * Replace SID1, component by component with SID2
- * Assumes will never be called with unequal length SIDS
- * so only touches 21-x-y-z-rid portion
- * This routine does not need to deal with endianism as 
- * long as the incoming SIDs are both in the same (LE) format.
- */
-void change_sid(DOM_SID *s1, DOM_SID *s2)
-{
-  int i;
-  
-  for (i=0; i<s1->num_auths; i++) {
-    s1->sub_auths[i] = s2->sub_auths[i];
-  }
-}
+/********************************************************************
+********************************************************************/
 
-void print_sid(DOM_SID *sid)
+static bool swap_sid_in_acl( struct security_descriptor *sd, struct dom_sid *s1, struct dom_sid *s2 )
 {
-  int i, comps = sid->num_auths;
-  fprintf(stdout, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
-
-  for (i = 0; i < comps; i++) {
-
-    fprintf(stdout, "-%u", IVAL(&sid->sub_auths[i],0));
-
-  }
-  fprintf(stdout, "\n");
+       struct security_acl *theacl;
+       int i;
+       bool update = False;
+
+       verbose_output("  Owner SID: %s\n", sid_string_tos(sd->owner_sid));
+       if ( dom_sid_equal( sd->owner_sid, s1 ) ) {
+               sid_copy( sd->owner_sid, s2 );
+               update = True;
+               verbose_output("  New Owner SID: %s\n",
+                       sid_string_tos(sd->owner_sid));
+
+       }
+
+       verbose_output("  Group SID: %s\n", sid_string_tos(sd->group_sid));
+       if ( dom_sid_equal( sd->group_sid, s1 ) ) {
+               sid_copy( sd->group_sid, s2 );
+               update = True;
+               verbose_output("  New Group SID: %s\n",
+                       sid_string_tos(sd->group_sid));
+       }
+
+       theacl = sd->dacl;
+       verbose_output("  DACL: %d entries:\n", theacl->num_aces);
+       for ( i=0; i<theacl->num_aces; i++ ) {
+               verbose_output("    Trustee SID: %s\n",
+                       sid_string_tos(&theacl->aces[i].trustee));
+               if ( dom_sid_equal( &theacl->aces[i].trustee, s1 ) ) {
+                       sid_copy( &theacl->aces[i].trustee, s2 );
+                       update = True;
+                       verbose_output("    New Trustee SID: %s\n",
+                               sid_string_tos(&theacl->aces[i].trustee));
+               }
+       }
+
+#if 0
+       theacl = sd->sacl;
+       verbose_output("  SACL: %d entries: \n", theacl->num_aces);
+       for ( i=0; i<theacl->num_aces; i++ ) {
+               verbose_output("    Trustee SID: %s\n",
+                       sid_string_tos(&theacl->aces[i].trustee));
+               if ( dom_sid_equal( &theacl->aces[i].trustee, s1 ) ) {
+                       sid_copy( &theacl->aces[i].trustee, s2 );
+                       update = True;
+                       verbose_output("    New Trustee SID: %s\n",
+                               sid_string_tos(&theacl->aces[i].trustee));
+               }
+       }
+#endif
+       return update;
 }
 
-void process_sid(DOM_SID *sid, DOM_SID *o_sid, DOM_SID *n_sid) 
-{
-  int i;
-  if (my_sid_equal(sid, o_sid)) {
-
-    for (i=0; i<sid->num_auths; i++) {
-      sid->sub_auths[i] = n_sid->sub_auths[i];
+/********************************************************************
+********************************************************************/
 
-    }
-
-  }
-
-}
-
-void process_acl(ACL *acl, const char *prefix)
+static bool copy_registry_tree( REGF_FILE *infile, REGF_NK_REC *nk,
+                                REGF_NK_REC *parent, REGF_FILE *outfile,
+                                const char *parentpath  )
 {
-  int ace_cnt, i;
-  ACE *ace;
-
-  ace_cnt = IVAL(&acl->num_aces, 0);
-  ace = (ACE *)&acl->aces;
-  if (verbose) fprintf(stdout, "%sACEs: %u\n", prefix, ace_cnt);
-  for (i=0; i<ace_cnt; i++) {
-    if (verbose) fprintf(stdout, "%s  Perms: %08X, SID: ", prefix,
-                        IVAL(&ace->perms, 0));
-    if (change)
-      process_sid(&ace->trustee, &old_sid, &new_sid);
-    print_sid(&ace->trustee);
-    ace = (ACE *)((char *)ace + SVAL(&ace->length, 0));
-  }
-} 
-
-void usage(void)
-{
-  fprintf(stderr, "usage: profiles [-c <OLD-SID> -n <NEW-SID>] <profilefile>\n");
-  fprintf(stderr, "Version: %s\n", VERSION);
-  fprintf(stderr, "\n\t-v\t sets verbose mode");
-  fprintf(stderr, "\n\t-c S-1-5-21-z-y-x-oldrid - provides SID to change");
-  fprintf(stderr, "\n\t-n S-1-5-21-a-b-c-newrid - provides SID to change to");
-  fprintf(stderr, "\n\t\tBoth must be present if the other is.");
-  fprintf(stderr, "\n\t\tIf neither present, just report the SIDs found\n");
+       REGF_NK_REC *key, *subkey;
+       struct security_descriptor *new_sd;
+       struct regval_ctr *values;
+       struct regsubkey_ctr *subkeys;
+       int i;
+       char *path;
+       WERROR werr;
+
+       /* swap out the SIDs in the security descriptor */
+
+       if (nk->sec_desc->sec_desc == NULL) {
+               fprintf(stderr, "Invalid (NULL) security descriptor!\n");
+               return false;
+       }
+
+       new_sd = security_descriptor_copy(outfile->mem_ctx,
+                                         nk->sec_desc->sec_desc);
+       if (new_sd == NULL) {
+               fprintf(stderr, "Failed to copy security descriptor!\n");
+               return False;
+       }
+
+       verbose_output("ACL for %s%s%s\n", parentpath, parent ? "\\" : "", nk->keyname);
+       swap_sid_in_acl( new_sd, &old_sid, &new_sid );
+
+       werr = regsubkey_ctr_init(NULL, &subkeys);
+       if (!W_ERROR_IS_OK(werr)) {
+               DEBUG(0,("copy_registry_tree: talloc() failure!\n"));
+               return False;
+       }
+
+       werr = regval_ctr_init(subkeys, &values);
+       if (!W_ERROR_IS_OK(werr)) {
+               TALLOC_FREE( subkeys );
+               DEBUG(0,("copy_registry_tree: talloc() failure!\n"));
+               return False;
+       }
+
+       /* copy values into the struct regval_ctr */
+
+       for ( i=0; i<nk->num_values; i++ ) {
+               regval_ctr_addvalue( values, nk->values[i].valuename, nk->values[i].type,
+                       nk->values[i].data, (nk->values[i].data_size & ~VK_DATA_IN_OFFSET) );
+       }
+
+       /* copy subkeys into the struct regsubkey_ctr */
+
+       while ( (subkey = regfio_fetch_subkey( infile, nk )) ) {
+               regsubkey_ctr_addkey( subkeys, subkey->keyname );
+       }
+
+       key = regfio_write_key( outfile, nk->keyname, values, subkeys, new_sd, parent );
+
+       /* write each one of the subkeys out */
+
+       path = talloc_asprintf(subkeys, "%s%s%s",
+                       parentpath, parent ? "\\" : "",nk->keyname);
+       if (!path) {
+               TALLOC_FREE( subkeys );
+               return false;
+       }
+
+       nk->subkey_index = 0;
+       while ((subkey = regfio_fetch_subkey(infile, nk))) {
+               if (!copy_registry_tree( infile, subkey, key, outfile, path)) {
+                       TALLOC_FREE(subkeys);
+                       return false;
+               }
+       }
+
+       /* values is a talloc()'d child of subkeys here so just throw it all away */
+
+       TALLOC_FREE( subkeys );
+
+       verbose_output("[%s]\n", path);
+
+       return True;
 }
 
-int main(int argc, char *argv[])
+/*********************************************************************
+*********************************************************************/
+
+int main( int argc, const char *argv[] )
 {
-  extern char *optarg;
-  extern int optind;
-  int opt;
-  int fd, start = 0;
-  char *base;
-  struct stat sbuf;
-  REGF_HDR *regf_hdr;
-  HBIN_HDR *hbin_hdr;
-  NK_HDR *nk_hdr;
-  SK_HDR *sk_hdr;
-  WORD first_sk_off, sk_off;
-  MY_SEC_DESC *sec_desc;
-  int *ptr;
-
-  if (argc < 2) {
-    usage();
-    exit(1);
-  }
-
-  /*
-   * Now, process the arguments
-   */
-
-  while ((opt = getopt(argc, argv, "c:n:v")) != EOF) {
-    switch (opt) {
-    case 'c':
-      change = 1;
-      if (!get_sid(&old_sid, optarg)) {
-       fprintf(stderr, "Argument to -c should be a SID in form of S-1-5-...\n");
-       usage();
-       exit(254);
-      }
-      break;
-
-    case 'n':
-      new = 1;
-      if (!get_sid(&new_sid, optarg)) {
-       fprintf(stderr, "Argument to -n should be a SID in form of S-1-5-...\n");
-       usage();
-       exit(253);
-      }
-
-      break;
-
-    case 'v':
-      verbose++;
-      break;
-
-    default:
-      usage();
-      exit(255);
-    }
-  }
-
-  if ((!change & new) || (change & !new)) {
-    fprintf(stderr, "You must specify both -c and -n if one or the other is set!\n");
-    usage();
-    exit(252);
-  }
-
-  fd = open(argv[optind], O_RDWR, 0000);
-
-  if (fd < 0) {
-    fprintf(stderr, "Could not open %s: %s\n", argv[optind], 
-       strerror(errno));
-    exit(2);
-  }
-
-  if (fstat(fd, &sbuf) < 0) {
-    fprintf(stderr, "Could not stat file %s, %s\n", argv[optind],
-       strerror(errno));
-    exit(3);
-  }
-
-  /*
-   * Now, mmap the file into memory, check the header and start
-   * dealing with the records. We are interested in the sk record
-   */
-  start = 0;
-  base = mmap(&start, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-
-  if ((int)base == -1) {
-    fprintf(stderr, "Could not mmap file: %s, %s\n", argv[optind],
-       strerror(errno));
-    exit(4);
-  }
-
-  /*
-   * In what follows, and in places above, in order to work on both LE and
-   * BE platforms, we have to use the Samba macros to extract SHORT, LONG
-   * and associated UNSIGNED quantities from the data in the mmap'd file.
-   * NOTE, however, that we do not need to do anything with memory
-   * addresses that we construct from pointers in our address space.
-   * For example, 
-   *
-   *    sec_desc = (MY_SEC_DESC *)&(sk_hdr->sec_desc[0]);
-   *
-   * is simply taking the address of a structure we already have the address
-   * of in our address space, while, the fields within it, will have to 
-   * be accessed with the macros:
-   *
-   * owner_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] + 
-   *                         IVAL(&sec_desc->owner_off, 0));
-   *
-   * Which is pulling out an offset and adding it to an existing pointer.
-   *
-   */
-
-  regf_hdr = (REGF_HDR *)base;
-
-  if (verbose) fprintf(stdout, "Registry file size: %u\n", (unsigned int)sbuf.st_size);
-
-  if (IVAL(&regf_hdr->REGF_ID, 0) != REG_REGF_ID) {
-    fprintf(stderr, "Incorrect Registry file (doesn't have header ID): %s\n", argv[optind]);
-    exit(5);
-  }
-
-  if (verbose) fprintf(stdout, "First Key Off: %u, Data Block Size: %u\n",
-                      IVAL(&regf_hdr->first_key, 0), 
-                      IVAL(&regf_hdr->dblk_size, 0));
-
-  hbin_hdr = (HBIN_HDR *)(base + 0x1000); /* No need for Endian stuff */
-
-  /*
-   * This should be the hbin_hdr 
-   */
-
-  if (IVAL(&hbin_hdr->HBIN_ID, 0) != REG_HBIN_ID) {
-    fprintf(stderr, "Incorrect hbin hdr: %s\n", argv[optind]);
-    exit(6);
-  } 
-
-  if (verbose) fprintf(stdout, "Next Off: %u, Prev Off: %u\n", 
-                      IVAL(&hbin_hdr->next_off, 0), 
-                      IVAL(&hbin_hdr->prev_off, 0));
-
-  nk_hdr = (NK_HDR *)(base + 0x1000 + IVAL(&regf_hdr->first_key, 0) + 4);
-
-  if (SVAL(&nk_hdr->NK_ID, 0) != REG_NK_ID) {
-    fprintf(stderr, "Incorrect NK Header: %s\n", argv[optind]);
-    exit(7);
-  }
-
-  sk_off = first_sk_off = IVAL(&nk_hdr->sk_off, 0);
-  if (verbose) {
-    fprintf(stdout, "Type: %0x\n", SVAL(&nk_hdr->type, 0));
-    fprintf(stdout, "SK Off    : %o\n", (0x1000 + sk_off + 4));  
-  }
-
-  sk_hdr = (SK_HDR *)(base + 0x1000 + sk_off + 4);
-
-  do {
-    DOM_SID *owner_sid, *group_sid;
-    ACL *sacl, *dacl;
-    if (SVAL(&sk_hdr->SK_ID, 0) != REG_SK_ID) {
-      fprintf(stderr, "Incorrect SK Header format: %08X\n", 
-             (0x1000 + sk_off + 4));
-      exit(8);
-    }
-    ptr = (int *)sk_hdr;
-    if (verbose) fprintf(stdout, "Off: %08X, Refs: %u, Size: %u\n",
-                        sk_off, IVAL(&sk_hdr->ref_cnt, 0), 
-                        IVAL(&sk_hdr->rec_size, 0));
-
-    sec_desc = (MY_SEC_DESC *)&(sk_hdr->sec_desc[0]);
-    owner_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] +
-                           IVAL(&sec_desc->owner_off, 0));
-    group_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] + 
-                           IVAL(&sec_desc->group_off, 0));
-    sacl = (ACL *)(&sk_hdr->sec_desc[0] + 
-                  IVAL(&sec_desc->sacl_off, 0));
-    dacl = (ACL *)(&sk_hdr->sec_desc[0] + 
-                  IVAL(&sec_desc->dacl_off, 0));
-    if (verbose)fprintf(stdout, "  Owner SID: "); 
-    if (change) process_sid(owner_sid, &old_sid, &new_sid);
-    if (verbose) print_sid(owner_sid);
-    if (verbose) fprintf(stdout, "  Group SID: "); 
-    if (change) process_sid(group_sid, &old_sid, &new_sid);
-    if (verbose) print_sid(group_sid);
-    fprintf(stdout, "  SACL: ");
-    if (!sec_desc->sacl_off) { /* LE zero == BE zero */
-      if (verbose) fprintf(stdout, "NONE\n");
-    }
-    else 
-      process_acl(sacl, "    ");
-    if (verbose) fprintf(stdout, "  DACL: ");
-    if (!sec_desc->dacl_off) {
-      if (verbose) fprintf(stdout, "NONE\n");
-    }
-    else 
-      process_acl(dacl, "    ");
-    sk_off = IVAL(&sk_hdr->prev_off, 0);
-    sk_hdr = (SK_HDR *)(base + OFF(IVAL(&sk_hdr->prev_off, 0)));
-  } while (sk_off != first_sk_off);
-
-  munmap(base, sbuf.st_size); 
-
-  close(fd);
-  return 0;
+       TALLOC_CTX *frame = talloc_stackframe();
+       int opt;
+       REGF_FILE *infile, *outfile;
+       REGF_NK_REC *nk;
+       char *orig_filename, *new_filename;
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+               { "change-sid", 'c', POPT_ARG_STRING, NULL, 'c', "Provides SID to change" },
+               { "new-sid", 'n', POPT_ARG_STRING, NULL, 'n', "Provides SID to change to" },
+               { "verbose", 'v', POPT_ARG_NONE, &opt_verbose, 'v', "Verbose output" },
+               POPT_COMMON_SAMBA
+               POPT_COMMON_VERSION
+               POPT_TABLEEND
+       };
+       poptContext pc;
+
+       load_case_tables();
+
+       /* setup logging options */
+
+       setup_logging( "profiles", DEBUG_STDERR);
+
+       pc = poptGetContext("profiles", argc, argv, long_options,
+               POPT_CONTEXT_KEEP_FIRST);
+
+       poptSetOtherOptionHelp(pc, "<profilefile>");
+
+       /* Now, process the arguments */
+
+       while ((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+               case 'c':
+                       change = 1;
+                       if (!string_to_sid(&old_sid, poptGetOptArg(pc))) {
+                               fprintf(stderr, "Argument to -c should be a SID in form of S-1-5-...\n");
+                               poptPrintUsage(pc, stderr, 0);
+                               exit(254);
+                       }
+                       break;
+
+               case 'n':
+                       new_val = 1;
+                       if (!string_to_sid(&new_sid, poptGetOptArg(pc))) {
+                               fprintf(stderr, "Argument to -n should be a SID in form of S-1-5-...\n");
+                               poptPrintUsage(pc, stderr, 0);
+                               exit(253);
+                       }
+                       break;
+
+               }
+       }
+
+       poptGetArg(pc);
+
+       if (!poptPeekArg(pc)) {
+               poptPrintUsage(pc, stderr, 0);
+               exit(1);
+       }
+
+       if ((!change && new_val) || (change && !new_val)) {
+               fprintf(stderr, "You must specify both -c and -n if one or the other is set!\n");
+               poptPrintUsage(pc, stderr, 0);
+               exit(252);
+       }
+
+       orig_filename = talloc_strdup(frame, poptPeekArg(pc));
+       if (!orig_filename) {
+               exit(ENOMEM);
+       }
+       new_filename = talloc_asprintf(frame,
+                                       "%s.new",
+                                       orig_filename);
+       if (!new_filename) {
+               exit(ENOMEM);
+       }
+
+       if (!(infile = regfio_open( orig_filename, O_RDONLY, 0))) {
+               fprintf( stderr, "Failed to open %s!\n", orig_filename );
+               fprintf( stderr, "Error was (%s)\n", strerror(errno) );
+               exit (1);
+       }
+
+       if ( !(outfile = regfio_open( new_filename, (O_RDWR|O_CREAT|O_TRUNC),
+                                     (S_IRUSR|S_IWUSR) )) ) {
+               fprintf( stderr, "Failed to open new file %s!\n", new_filename );
+               fprintf( stderr, "Error was (%s)\n", strerror(errno) );
+               exit (1);
+       }
+
+       /* actually do the update now */
+
+       if ((nk = regfio_rootkey( infile )) == NULL) {
+               fprintf(stderr, "Could not get rootkey\n");
+               exit(3);
+       }
+
+       if (!copy_registry_tree( infile, nk, NULL, outfile, "")) {
+               fprintf(stderr, "Failed to write updated registry file!\n");
+               exit(2);
+       }
+
+       /* cleanup */
+
+       regfio_close(infile);
+       regfio_close(outfile);
+
+       poptFreeContext(pc);
+
+       TALLOC_FREE(frame);
+       return 0;
 }