diff --git a/image_creator/os_type/windows/__init__.py b/image_creator/os_type/windows/__init__.py index c4b464bb72aad7e0a7c3922acb506dcbdc75aa42..28007439f711629f98d68bf4f63bf9d59bd7a101 100644 --- a/image_creator/os_type/windows/__init__.py +++ b/image_creator/os_type/windows/__init__.py @@ -97,6 +97,9 @@ VIRTIO = ( # id Name "vioscsi", # 1004 Virtio SCSI "viorng") # 1005 Virtio RNG +# The Administrator's Relative ID +ADMIN_RID = 500 + def parse_inf(inf): """Parse the content of a Windows INF file and fetch all information found @@ -218,7 +221,6 @@ DESCR = { "it has booted, before giving up.", "smp": "Number of CPUs to use for the Windows customization VM.", "mem": "Virtual RAM size in MiB for the Windows customization VM.", - "admin": "Name of the Administration user.", "virtio": "Directory hosting the Windows virtio drivers.", "virtio_timeout": "Time in seconds to wait for the installation of the VirtIO drivers."} @@ -226,7 +228,6 @@ DESCR = { class Windows(OSBase): """OS class for Windows""" - @add_sysprep_param('admin', "string", 'Administrator', DESCR['admin']) @add_sysprep_param('mem', "posint", 1024, DESCR['mem']) @add_sysprep_param('smp', "posint", 1, DESCR['smp']) @add_sysprep_param( @@ -265,14 +266,22 @@ class Windows(OSBase): self.systemroot = self.image.g.inspect_get_windows_systemroot( self.root) - self.vm = VM(self.image.device, self.sysprep_params) self.registry = Registry(self.image) - # If the image is already sysprepped we cannot further customize it with self.mount(readonly=True, silent=True): self.out.output("Checking media state ...", False) + + # Enumerate the windows users + (self.usernames, + self.active_users, + self.admins) = self.registry.enum_users() + assert ADMIN_RID in self.usernames, "Administrator account missing" + + # If the image is already sysprepped we cannot further customize it self.sysprepped = self.registry.get_setup_state() > 0 + self.virtio_state = self.compute_virtio_state() + arch = self.image.g.inspect_get_arch(self.root) if arch == 'x86_64': arch = 'amd64' @@ -291,6 +300,9 @@ class Windows(OSBase): if root == self.root: self.systemdrive = drive + admin = self.usernames[ADMIN_RID] + self.vm = VM(self.image.device, self.sysprep_params, admin) + @sysprep('Disabling IPv6 privacy extensions') def disable_ipv6_privacy_extensions(self): """Disable IPv6 privacy extensions""" @@ -458,15 +470,14 @@ class Windows(OSBase): "The media has no VirtIO Ethernet Adapter driver installed. " "Further image customization is not possible.") - admin = self.sysprep_params['admin'].value timeout = self.sysprep_params['boot_timeout'].value shutdown_timeout = self.sysprep_params['shutdown_timeout'].value 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) + activated = self.registry.reset_account(ADMIN_RID) + v_val = self.registry.reset_passwd(ADMIN_RID) disabled_uac = self.registry.update_uac_remote_setting(1) self._add_boot_scripts() @@ -534,11 +545,11 @@ class Windows(OSBase): self.registry.update_uac_remote_setting(0) if not activated: - self.registry.reset_account(admin, False) + self.registry.reset_account(ADMIN_RID, False) if not self.sysprepped: # Reset the old password - self.registry.reset_passwd(admin, v_val) + self.registry.reset_passwd(ADMIN_RID, v_val) self.registry.update_firewalls(*firewall_states) self.out.success("done") @@ -595,6 +606,7 @@ class Windows(OSBase): next boot. """ + admin = self.usernames[ADMIN_RID] commands = {} # Disable hibernation. This is not needed for a VM @@ -605,7 +617,7 @@ class Windows(OSBase): # This will update the password of the admin user to self.vm.password commands["UpdatePassword"] = "net user %s %s" % \ - (self.sysprep_params['admin'].value, self.vm.password) + (admin, self.vm.password) # This is previously done with hivex when we executed # self.registry.update_uac_remote_setting(1). @@ -636,16 +648,15 @@ class Windows(OSBase): # defined on the server either by a Group Policy object (GPO) or by a # local policy. - self.registry.enable_autologon(self.sysprep_params['admin'].value) + self.registry.enable_autologon(admin) def _do_collect_metadata(self): """Collect metadata about the OS""" super(Windows, self)._do_collect_metadata() # We only care for active users - users, active, _ = self.registry.enum_users() - - self.meta["USERS"] = " ".join([users[a] for a in active]) + self.meta["USERS"] = \ + " ".join([self.usernames[a] for a in self.active_users]) def _check_connectivity(self): """Check if winexe works on the Windows VM""" @@ -793,18 +804,16 @@ class Windows(OSBase): """Upload the VirtIO drivers and installation scripts to the media. """ with self.mount(readonly=False, silent=True): - admin = self.sysprep_params['admin'].value - - v_val = self.registry.reset_passwd(admin) + v_val = self.registry.reset_passwd(ADMIN_RID) self._add_cleanup('virtio', - self.registry.reset_passwd, admin, v_val) + self.registry.reset_passwd, ADMIN_RID, v_val) - active = self.registry.reset_account(admin) + active = self.registry.reset_account(ADMIN_RID) self._add_cleanup('virtio', - self.registry.reset_account, admin, active) + self.registry.reset_account, ADMIN_RID, active) # We disable this with powershell scripts - self.registry.enable_autologon(admin) + self.registry.enable_autologon(self.usernames[ADMIN_RID]) active = self.registry.reset_first_logon_animation(False) self._add_cleanup( diff --git a/image_creator/os_type/windows/registry.py b/image_creator/os_type/windows/registry.py index bdd807af78b6a893cc343c7bfbf90ef908503f36..25eb9394476cc07cd2ce8ec03a2dc4f600271be2 100644 --- a/image_creator/os_type/windows/registry.py +++ b/image_creator/os_type/windows/registry.py @@ -24,6 +24,9 @@ import tempfile import os import struct +# The Administrators group RID +ADMINS = 0x00000220 + # http://technet.microsoft.com/en-us/library/hh824815.aspx WINDOWS_SETUP_STATES = ( "IMAGE_STATE_COMPLETE", @@ -181,7 +184,6 @@ class Registry(object): runonce = hive.node_add_child(key, "RunOnce") for desc, cmd in commands.items(): - assert type(desc) is str and type(cmd) is str hive.node_set_value(runonce, REG_SZ(desc, cmd)) hive.commit(None) @@ -189,8 +191,6 @@ class Registry(object): def enable_autologon(self, username, password=""): """Enable automatic logon for a specific user""" - assert type(username) is str and type(password) is str - with self.open_hive('SOFTWARE', write=True) as hive: winlogon = hive.root() @@ -333,11 +333,11 @@ class Registry(object): self._foreach_account( userlist=[], useraction=collect_users, - grouplist=['Administrators'], groupaction=collect_group_members) + grouplist=[ADMINS], groupaction=collect_group_members) return (users, active, members) - def reset_passwd(self, user, v_field=None): + def reset_passwd(self, rid, v_field=None): r"""Reset the password for user 'user'. If v_field is not None, the value of key \HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\%RID%\V is replaced with this one, otherwise the LM and NT passwords of the @@ -350,6 +350,7 @@ class Registry(object): parent = {} def update_v_field(hive, username, rid_node): + """Updates the user's V field to reset the password""" assert 'old' not in parent, "Multiple users with same username" field = hive.node_get_value(rid_node, 'V') @@ -373,12 +374,12 @@ class Registry(object): hive.commit(None) parent['old'] = v_val - self._foreach_account(True, userlist=[user], useraction=update_v_field) + self._foreach_account(True, userlist=[rid], useraction=update_v_field) - assert 'old' in parent, "user: `%s' does not exist" % user + assert 'old' in parent, "user whith RID: `%s' does not exist" % rid return parent['old'] - def reset_account(self, user, activate=True): + def reset_account(self, rid, activate=True): # This is a hack. I cannot assign a new value to nonlocal variable. # This is why I'm using a dict @@ -398,7 +399,7 @@ class Registry(object): isactive = lambda f: (to_int(f[56]) & 0x01) == 0 def update_f_field(hive, username, rid_node): - + """Updates the user's F field to reset the account""" 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 @@ -415,7 +416,7 @@ class Registry(object): hive.node_set_value(rid_node, REG_BINARY('F', new)) hive.commit(None) - self._foreach_account(True, userlist=[user], useraction=update_f_field) + self._foreach_account(True, userlist=[rid], useraction=update_f_field) return state['old'] @@ -456,12 +457,9 @@ class Registry(object): writing. """ - def parse_sam(namelist, action, path): + def parse_sam(ridlist, action, path): """Parse the registry users and groups nodes""" - # In Windows account names are case-insensitive - namelist = [name.lower() for name in namelist] - accounts = traverse(hive, 'SAM/Domains/' + path) names = hive.node_get_child(accounts, 'Names') @@ -471,11 +469,11 @@ class Registry(object): for node in hive.node_children(names): name = hive.node_name(node) + rid = hive.value_type(hive.node_get_value(node, ""))[0] - if len(namelist) != 0 and name.lower() not in namelist: + if len(ridlist) != 0 and rid not in ridlist: continue - rid = hive.value_type(hive.node_get_value(node, ""))[0] # if RID is 500 (=0x1f4), the corresponding node name is # '000001F4' key = ("%8.x" % rid).replace(' ', '0').upper() diff --git a/image_creator/os_type/windows/vm.py b/image_creator/os_type/windows/vm.py index b030d7ec2d81b88aeffa699fd3499ee0be6c9d56..0b049ebe254d3be5287486f658617f900a02e9f1 100644 --- a/image_creator/os_type/windows/vm.py +++ b/image_creator/os_type/windows/vm.py @@ -34,11 +34,12 @@ RANDOM_TOKEN = "".join(random.choice(lowercase + uppercase) for _ in range(16)) class VM(object): """Windows Virtual Machine""" - def __init__(self, disk, params): + def __init__(self, disk, params, admin): """Create VM instance""" self.disk = disk self.params = params + self.admin = admin self.interface = 'virtio' # expected number of token occurrences in serial port @@ -222,9 +223,8 @@ class VM(object): debug = kwargs['debug'] if 'debug' in kwargs else False uninstall = kwargs['uninstall'] if 'uninstall' in kwargs else False - user = self.params['admin'].value - winexe = WinEXE(user, 'localhost', password=self.password) - winexe.runas(user, self.password).no_pass() + winexe = WinEXE(self.admin, 'localhost', password=self.password) + winexe.runas(self.admin, self.password).no_pass() if debug: winexe.debug(9)