From 0f86bd4ec58bad24bb4b1f89fdf59c855d5b9e34 Mon Sep 17 00:00:00 2001
From: Nikos Skalkotos <skalkoto@grnet.gr>
Date: Thu, 10 Jul 2014 16:54:02 +0300
Subject: [PATCH] windows: Add code for enabling accounts

This is needed if the Administrator account is locked
---
 image_creator/os_type/windows/__init__.py |  6 +++-
 image_creator/os_type/windows/registry.py | 43 ++++++++++++++++++++++-
 2 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/image_creator/os_type/windows/__init__.py b/image_creator/os_type/windows/__init__.py
index 91a340a..35c313e 100644
--- a/image_creator/os_type/windows/__init__.py
+++ b/image_creator/os_type/windows/__init__.py
@@ -466,6 +466,7 @@ class Windows(OSBase):
         self.out.output("Preparing media for boot ...", False)
 
         with self.mount(readonly=False, silent=True):
+            activated = self.registry.reset_account(admin)
             v_val = self.registry.reset_passwd(admin)
             disabled_uac = self.registry.update_uac_remote_setting(1)
             self._add_boot_scripts()
@@ -533,9 +534,11 @@ class Windows(OSBase):
                     if disabled_uac:
                         self.registry.update_uac_remote_setting(0)
 
+                    if not activated:
+                        self.registry.reset_account(admin, False)
+
                     if not self.sysprepped:
                         # Reset the old password
-                        admin = self.sysprep_params['admin'].value
                         self.registry.reset_passwd(admin, v_val)
 
                     self.registry.update_firewalls(*firewall_states)
@@ -786,6 +789,7 @@ class Windows(OSBase):
         with self.mount(readonly=False, silent=True):
             admin = self.sysprep_params['admin'].value
             v_val = self.registry.reset_passwd(admin)
+            activated = self.registry.reset_account(admin)
             self.registry.enable_autologon(admin)
 
             tmp = uuid.uuid4().hex
diff --git a/image_creator/os_type/windows/registry.py b/image_creator/os_type/windows/registry.py
index 7715bc9..ff6ba3f 100644
--- a/image_creator/os_type/windows/registry.py
+++ b/image_creator/os_type/windows/registry.py
@@ -330,7 +330,7 @@ class Registry(object):
                 # http://www.beginningtoseethelight.org/ntsecurity/index.htm
                 #        #D3BC3F5643A17823
                 fmt = '%ds4x8s4x%ds' % (0xa0, len(v_val) - 0xb0)
-                new = ("\x00" * 4).join(struct.unpack(fmt,  v_val))
+                new = ("\x00" * 4).join(struct.unpack(fmt, v_val))
 
             hive.node_set_value(rid_node, REG_BINARY('V', new))
             hive.commit(None)
@@ -341,6 +341,47 @@ class Registry(object):
         assert 'old' in parent, "user: `%s' does not exist" % user
         return parent['old']
 
+    def reset_account(self, user, activate=True):
+
+        # This is a hack. I cannot assign a new value to nonlocal variable.
+        # This is why I'm using a dict
+        state = {}
+
+        # Convert byte to int
+        to_int = lambda b: int(b.encode('hex'), 16)
+
+        # Under HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\%RID% there is
+        # an F field that contains information about this user account. Bytes
+        # 56 & 57 are the account type and status flags. The first bit is the
+        # 'account disabled' bit:
+        #
+        # http://www.beginningtoseethelight.org/ntsecurity/index.htm
+        #        #8603CF0AFBB170DD
+        #
+        isactive = lambda f: (to_int(f[56]) & 0x01) == 0
+
+        def update_f_field(hive, username, rid_node):
+
+            field = hive.node_get_value(rid_node, 'F')
+            f_type, f_val = hive.value_value(field)
+            assert f_type == 3L, "F field type (=%d) isn't REG_BINARY" % f_type
+
+            state['old'] = isactive(f_val)
+            if activate is state['old']:
+                # nothing to do
+                return
+
+            mask = (lambda b: b & 0xfe) if activate else (lambda b: b | 0x01)
+            new = struct.pack("56sB23s", f_val[:56], mask(to_int(f_val[56])),
+                              f_val[57:])
+
+            hive.node_set_value(rid_node, REG_BINARY('F', new))
+            hive.commit(None)
+
+        self._foreach_user([user], update_f_field, write=True)
+
+        return state['old']
+
     def _foreach_user(self, userlist, action, write=False):
         """Performs an action on the RID node of a user in the registry, for
         every user found in the userlist. If userlist is empty, it performs the
-- 
GitLab