Commit a3c550f9 authored by Nikos Skalkotos's avatar Nikos Skalkotos

windows: Use RIDs instead of Usernames

Relative IDs are safer. The Administrator's RID is always 500. The
name may change.
parent 35d47a23
......@@ -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(
......
......@@ -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()
......
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment