1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-01-03 01:17:56 +03:00

Moved "custom" parameters for osManager inside the "custom" dict

Also, kept "old" parematers for a while (a couple of versions), so old clients are compatible with UDS actor 4.0 at least (so we can upgrade server, keep running and eventually upgrade actors).
Compatibility mast be kept for at lest a couple of minor releases,
Al least, until UDS 4.5 will be kept this way
This commit is contained in:
Adolfo Gómez García 2023-05-18 15:04:47 +02:00
parent 880aa24dbb
commit c264ea9c13
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
14 changed files with 179 additions and 218 deletions

View File

@ -187,13 +187,16 @@ def renameComputer(newName: str) -> bool:
rename(newName)
return True # Always reboot right now. Not much slower but much more convenient
def joinDomain(domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False, custom: typing.Optional[typing.Mapping[str, typing.Any]] = None):
def joinDomain(name: str, custom: typing.Optional[typing.Mapping[str, typing.Any]] = None):
if not custom:
logger.error('Error joining domain: no custom data provided')
return
# Read parameters from custom data
name: str = custom.get('name', '')
domain: str = custom.get('domain', '')
ou: str = custom.get('ou', '')
account: str = custom.get('account', '')
password: str = custom.get('password', '')
client_software: str = custom.get('client_software', '')
server_software: str = custom.get('server_software', '')
membership_software: str = custom.get('membership_software', '')

View File

@ -64,20 +64,15 @@ class UDSActorSvc(daemon.Daemon, CommonService):
return self._sensibleDataCleanable
def joinDomain( # pylint: disable=unused-argument, too-many-arguments
self, name: str, domain: str, ou: str, account: str, password: str, custom: typing.Optional[typing.Mapping[str, typing.Any]] = None
self, name: str, custom: typing.Mapping[str, typing.Any]
) -> None:
# Add name to custom data, needed by joinDomain on Linux
# First, Copy mapping to mutable dict so we can add name to it
localCustom = {k: v for k, v in (custom.items() if custom is not None else [])}
localCustom['name'] = name
# If isPersistent is False, we need to keep sensible data, so it's not cleaned
self._sensibleDataCleanable = localCustom.get('isPersistent', False)
self._sensibleDataCleanable = custom.get('isPersistent', False)
self.rename(name)
logger.debug(f'Starting joining domain {domain} with name {name}')
operations.joinDomain(domain, ou, account, password, custom=localCustom)
logger.debug('Starting joining domain %s with name %s', custom.get('domain', ''), name)
operations.joinDomain(name, custom)
def finish(self) -> None:
try:
@ -86,11 +81,11 @@ class UDSActorSvc(daemon.Daemon, CommonService):
custom = self._cfg.config.os.custom
if osData.action == 'rename_ad' and custom.get('isPersistent', False):
operations.leaveDomain(
osData.ad or '',
osData.username or '',
osData.password or '',
custom['clientSoftware'] or '',
custom['serverSoftware'] or '',
custom.get('ad', ''),
custom.get('username', ''),
custom.get('password', ''),
custom.get('clientSoftware', ''),
custom.get('serverSoftware', ''),
)
except Exception as e:
logger.error(f'Got exception operating machine: {e}')

View File

@ -289,11 +289,6 @@ class UDSServerApi(UDSApi):
os=types.ActorOsConfigurationType(
action=os['action'],
name=os['name'],
username=os.get('username'),
password=os.get('password'),
new_password=os.get('new_password'),
ad=os.get('ad'),
ou=os.get('ou'),
custom=os.get('custom'),
)
if r['os']

View File

@ -75,9 +75,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
logger.debug('Executing command on {}: {}'.format(section, cmdLine))
res = subprocess.check_call(cmdLine, shell=True)
except Exception as e:
logger.error(
'Got exception executing: {} - {} - {}'.format(section, cmdLine, e)
)
logger.error('Got exception executing: {} - {} - {}'.format(section, cmdLine, e))
return False
logger.debug('Result of executing cmd for {} was {}'.format(section, res))
return True
@ -131,9 +129,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
) # Emty interfaces is like "no ip change" because cannot be notified
if self._cfg.config and interfaces:
try:
return next(
x for x in interfaces if x.mac.lower() == self._cfg.config.unique_id
)
return next(x for x in interfaces if x.mac.lower() == self._cfg.config.unique_id)
except StopIteration:
return interfaces[0]
@ -188,9 +184,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
# Success or any error that is not recoverable (retunerd by UDS). if Error, service will be cleaned in a while.
break
else:
logger.error(
'Could not locate IP address!!!. (Not registered with UDS)'
)
logger.error('Could not locate IP address!!!. (Not registered with UDS)')
# Do not continue if not alive...
if not self._isAlive:
@ -199,9 +193,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
# Cleans sensible data
if self._cfg.config:
if self.canCleanSensibleData():
self._cfg = self._cfg._replace(
config=self._cfg.config._replace(os=None), data=None
)
self._cfg = self._cfg._replace(config=self._cfg.config._replace(os=None), data=None)
platform.store.writeConfig(self._cfg)
logger.info('Service ready')
@ -232,22 +224,17 @@ class CommonService: # pylint: disable=too-many-instance-attributes
try:
if self._cfg.config and self._cfg.config.os:
osData = self._cfg.config.os
custom: typing.Mapping[str, typing.Any] = osData.custom or {}
# Needs UDS Server >= 4.0 to work
if osData.action == 'rename':
self.rename(
osData.name,
osData.username,
osData.password,
osData.new_password,
custom.get('username'),
custom.get('password'),
custom.get('new_password'),
)
elif osData.action == 'rename_ad':
self.joinDomain(
osData.name,
osData.ad or '',
osData.ou or '',
osData.username or '',
osData.password or '',
osData.custom
)
self.joinDomain(osData.name, custom)
if self._rebootRequested:
try:
@ -295,9 +282,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
self.doWait(5000)
def initialize(self) -> bool:
if (
self._initialized or not self._cfg.host or not self._isAlive
): # Not configured or not running
if self._initialized or not self._cfg.host or not self._isAlive: # Not configured or not running
return False
self._initialized = True
@ -319,18 +304,14 @@ class CommonService: # pylint: disable=too-many-instance-attributes
)
if not initResult.own_token: # Not managed
logger.debug(
'This host is not managed by UDS Broker (ids: {})'.format(
self._interfaces
)
'This host is not managed by UDS Broker (ids: {})'.format(self._interfaces)
)
return False
# Only removes master token for managed machines (will need it on next client execution)
# For unmanaged, if alias is present, replace master token with it
master_token = (
None
if self.isManaged()
else (initResult.alias_token or self._cfg.master_token)
None if self.isManaged() else (initResult.alias_token or self._cfg.master_token)
)
# Replace master token with alias token if present
self._cfg = self._cfg._replace(
@ -352,16 +333,10 @@ class CommonService: # pylint: disable=too-many-instance-attributes
break # Initial configuration done..
except rest.RESTConnectionError as e:
logger.info(
'Trying to inititialize connection with broker (last error: {})'.format(
e
)
)
logger.info('Trying to inititialize connection with broker (last error: {})'.format(e))
self.doWait(5000) # Wait a bit and retry
except rest.RESTError as e: # Invalid key?
logger.error(
'Error validating with broker. (Invalid token?): {}'.format(e)
)
logger.error('Error validating with broker. (Invalid token?): {}'.format(e))
return False
except Exception:
logger.exception()
@ -371,9 +346,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
def uninitialize(self):
self._initialized = False
self._cfg = self._cfg._replace(
own_token=None
) # Ensures assigned token is cleared
self._cfg = self._cfg._replace(own_token=None) # Ensures assigned token is cleared
def finish(self) -> None:
if self._http:
@ -389,8 +362,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
self._cfg.actorType,
self._cfg.own_token,
'',
client.session_id
or 'stop', # If no session id, pass "stop"
client.session_id or 'stop', # If no session id, pass "stop"
'',
self._interfaces,
self._secret,
@ -405,11 +377,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
return # Unamanaged hosts does not changes ips. (The full initialize-login-logout process is done in a row, so at login the IP is correct)
try:
if (
not self._cfg.own_token
or not self._cfg.config
or not self._cfg.config.unique_id
):
if not self._cfg.own_token or not self._cfg.config or not self._cfg.config.unique_id:
# Not enouth data do check
return
currentInterfaces = tools.validNetworkCards(
@ -418,59 +386,20 @@ class CommonService: # pylint: disable=too-many-instance-attributes
old = self.serviceInterfaceInfo()
new = self.serviceInterfaceInfo(currentInterfaces)
if not new or not old:
raise Exception(
'No ip currently available for {}'.format(
self._cfg.config.unique_id
)
)
raise Exception('No ip currently available for {}'.format(self._cfg.config.unique_id))
if old.ip != new.ip:
self._certificate = self._api.notifyIpChange(
self._cfg.own_token, self._secret, new.ip, rest.LISTEN_PORT
)
# Now store new addresses & interfaces...
self._interfaces = currentInterfaces
logger.info(
'Ip changed from {} to {}. Notified to UDS'.format(old.ip, new.ip)
)
logger.info('Ip changed from {} to {}. Notified to UDS'.format(old.ip, new.ip))
# Stop the running HTTP Thread and start a new one, with new generated cert
self.startHttpServer()
except Exception as e:
# No ip changed, log exception for info
logger.warn('Checking ips failed: {}'.format(e))
def rename(
self,
name: str,
userName: typing.Optional[str] = None,
oldPassword: typing.Optional[str] = None,
newPassword: typing.Optional[str] = None,
) -> None:
'''
Invoked when broker requests a rename action
default does nothing
'''
hostName = platform.operations.getComputerName()
# Check for password change request for an user
if userName and newPassword:
logger.info('Setting password for configured user')
try:
platform.operations.changeUserPassword(
userName, oldPassword or '', newPassword
)
except Exception as e:
# Logs error, but continue renaming computer
logger.error(
'Could not change password for user {}: {}'.format(userName, e)
)
if hostName.lower() == name.lower():
logger.info('Computer name is already {}'.format(hostName))
return
if platform.operations.renameComputer(name):
self.reboot()
def loop(self):
# Main common loop
try:
@ -487,22 +416,44 @@ class CommonService: # pylint: disable=too-many-instance-attributes
# ******************************************************
# Methods that can be overriden by linux & windows Actor
# ******************************************************
def joinDomain( # pylint: disable=unused-argument, too-many-arguments
self, name: str, domain: str, ou: str, account: str, password: str, custom: typing.Optional[typing.Mapping[str, typing.Any]] = None
def rename(
self,
name: str,
userName: typing.Optional[str] = None,
oldPassword: typing.Optional[str] = None,
newPassword: typing.Optional[str] = None,
) -> None:
'''
Invoked when broker requests a rename action
'''
hostName = platform.operations.getComputerName()
# Check for password change request for an user
if userName and newPassword:
logger.info('Setting password for configured user')
try:
platform.operations.changeUserPassword(userName, oldPassword or '', newPassword)
except Exception as e:
# Logs error, but continue renaming computer
logger.error('Could not change password for user {}: {}'.format(userName, e))
if hostName.lower() == name.lower():
logger.info('Computer name is already {}'.format(hostName))
return
if platform.operations.renameComputer(name):
self.reboot()
def joinDomain(self, name: str, custom: typing.Mapping[str, typing.Any]) -> None:
'''
Invoked when broker requests a "domain" action
default does nothing
'''
logger.debug('Base join invoked: {} on {}, {}'.format(name, domain, ou))
logger.debug('Base join invoked: %s on %s, %s', name, custom)
# Client notifications
def login(
self, username: str, sessionType: typing.Optional[str] = None
) -> types.LoginResultInfoType:
result = types.LoginResultInfoType(
ip='', hostname='', dead_line=None, max_idle=None, session_id=None
)
def login(self, username: str, sessionType: typing.Optional[str] = None) -> types.LoginResultInfoType:
result = types.LoginResultInfoType(ip='', hostname='', dead_line=None, max_idle=None, session_id=None)
master_token = None
secret = None
# If unmanaged, do initialization now, because we don't know before this
@ -564,9 +515,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
)
!= 'ok' # Can return also "notified", that means the logout has not been processed by UDS
):
logger.info(
'Logout from %s ignored as required by uds broker', username
)
logger.info('Logout from %s ignored as required by uds broker', username)
return
self.onLogout(username, session_id or '')
@ -595,9 +544,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
'''
logger.info('Service stopped')
def preConnect(
self, userName: str, protocol: str, ip: str, hostname: str, udsUserName: str
) -> str:
def preConnect(self, userName: str, protocol: str, ip: str, hostname: str, udsUserName: str) -> str:
'''
Invoked when received a PRE Connection request via REST
Base preconnect executes the preconnect command

View File

@ -19,11 +19,6 @@ class AuthenticatorType(typing.NamedTuple):
class ActorOsConfigurationType(typing.NamedTuple):
action: str
name: str
username: typing.Optional[str] = None
password: typing.Optional[str] = None
new_password: typing.Optional[str] = None
ad: typing.Optional[str] = None
ou: typing.Optional[str] = None
custom: typing.Optional[typing.Mapping[str, typing.Any]] = None
class ActorDataConfigurationType(typing.NamedTuple):

View File

@ -63,24 +63,17 @@ def getComputerName() -> str:
def getNetworkInfo() -> typing.Iterator[types.InterfaceInfoType]:
obj = win32com.client.Dispatch("WbemScripting.SWbemLocator")
wmobj = obj.ConnectServer("localhost", "root\\cimv2")
adapters = wmobj.ExecQuery(
"Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True"
)
adapters = wmobj.ExecQuery("Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True")
try:
for obj in adapters:
for ip in obj.IPAddress:
if ':' in ip: # Is IPV6, skip this
continue
if (
ip is None
or ip == ''
or ip.startswith('169.254')
or ip.startswith('0.')
ip is None or ip == '' or ip.startswith('169.254') or ip.startswith('0.')
): # If single link ip, or no ip
continue
yield types.InterfaceInfoType(
name=obj.Caption, mac=obj.MACAddress, ip=ip
)
yield types.InterfaceInfoType(name=obj.Caption, mac=obj.MACAddress, ip=ip)
except Exception:
return
@ -97,7 +90,7 @@ def getDomainName() -> str:
# 3 = Domain
domain, status = win32net.NetGetJoinInformation()
if status != 3:
domain = None
domain = ''
return domain
@ -109,9 +102,7 @@ def getWindowsVersion() -> typing.Tuple[int, int, int, int, str]:
def getVersion() -> str:
verinfo = getWindowsVersion()
# Remove platform id i
return 'Windows-{}.{} Build {} ({})'.format(
verinfo[0], verinfo[1], verinfo[2], verinfo[4]
)
return 'Windows-{}.{} Build {} ({})'.format(verinfo[0], verinfo[1], verinfo[2], verinfo[4])
EWX_LOGOFF = 0x00000000
@ -129,11 +120,11 @@ def reboot(flags: int = EWX_FORCEIFHUNG | EWX_REBOOT) -> None:
)
privs = (
(
win32security.LookupPrivilegeValue(None, win32security.SE_SHUTDOWN_NAME),
win32security.LookupPrivilegeValue(None, win32security.SE_SHUTDOWN_NAME), # type: ignore
win32security.SE_PRIVILEGE_ENABLED,
),
)
win32security.AdjustTokenPrivileges(htok, 0, privs)
win32security.AdjustTokenPrivileges(htok, 0, privs) # type: ignore
win32api.ExitWindowsEx(flags, 0)
@ -148,7 +139,7 @@ def renameComputer(newName: str) -> bool:
'''
# Needs admin privileges to work
if (
ctypes.windll.kernel32.SetComputerNameExW(
ctypes.windll.kernel32.SetComputerNameExW( # type: ignore
DWORD(win32con.ComputerNamePhysicalDnsHostname), LPCWSTR(newName)
)
== 0
@ -157,14 +148,8 @@ def renameComputer(newName: str) -> bool:
# win32api.GetLastError -> returns error code
# (just put this comment here to remember to log this when logger is available)
error = getErrorMessage()
computerName = win32api.GetComputerNameEx(
win32con.ComputerNamePhysicalDnsHostname
)
raise Exception(
'Error renaming computer from {} to {}: {}'.format(
computerName, newName, error
)
)
computerName = win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname)
raise Exception('Error renaming computer from {} to {}: {}'.format(computerName, newName, error))
return True
@ -179,9 +164,7 @@ NETSETUP_JOIN_WITH_NEW_NAME = 0x00000400
NETSETUP_DEFER_SPN_SET = 0x1000000
def joinDomain(
domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False
) -> None:
def joinDomain(domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False) -> None:
'''
Joins machine to a windows domain
:param domain: Domain to join to
@ -198,9 +181,7 @@ def joinDomain(
account = domain + '\\' + account
# Do log
flags: typing.Any = (
NETSETUP_ACCT_CREATE | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN
)
flags: typing.Any = NETSETUP_ACCT_CREATE | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN
if executeInOneStep:
flags |= NETSETUP_JOIN_WITH_NEW_NAME
@ -214,13 +195,13 @@ def joinDomain(
lpAccount = LPCWSTR(account)
lpPassword = LPCWSTR(password)
res = ctypes.windll.netapi32.NetJoinDomain(
res = ctypes.windll.netapi32.NetJoinDomain( # type: ignore
None, lpDomain, lpOu, lpAccount, lpPassword, flags
)
# Machine found in another ou, use it and warn this on log
if res == 2224:
flags = DWORD(NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN)
res = ctypes.windll.netapi32.NetJoinDomain(
res = ctypes.windll.netapi32.NetJoinDomain( # type: ignore
None, lpDomain, None, lpAccount, lpPassword, flags
)
if res:
@ -247,14 +228,12 @@ def changeUserPassword(user: str, oldPassword: str, newPassword: str) -> None:
# res = ctypes.windll.netapi32.NetUserChangePassword(None, lpUser, lpOldPassword, lpNewPassword)
# Try to set new password "a las bravas", ignoring old one. This will not work with domain users
res = win32net.NetUserSetInfo(None, user, 1003, {'password': newPassword})
res = win32net.NetUserSetInfo(None, user, 1003, {'password': newPassword}) # type: ignore
if res:
# Log the error, and raise exception to parent
error = getErrorMessage(res)
raise Exception(
'Error changing password for user {}: {} {}'.format(user, res, error)
)
raise Exception('Error changing password for user {}: {} {}'.format(user, res, error))
class LASTINPUTINFO(ctypes.Structure): # pylint: disable=too-few-public-methods
@ -274,14 +253,14 @@ def initIdleDuration(atLeastSeconds: int): # pylint: disable=unused-argument
def getIdleDuration() -> float:
try:
lastInputInfo = LASTINPUTINFO()
lastInputInfo.cbSize = ctypes.sizeof(
lastInputInfo
) # pylint: disable=attribute-defined-outside-init
if ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lastInputInfo)) == 0:
lastInputInfo.cbSize = ctypes.sizeof(lastInputInfo) # pylint: disable=attribute-defined-outside-init
if ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lastInputInfo)) == 0: # type: ignore
return 0
current = ctypes.c_uint(ctypes.windll.kernel32.GetTickCount()).value
current = ctypes.c_uint(ctypes.windll.kernel32.GetTickCount()).value # type: ignore
if current < lastInputInfo.dwTime:
current += 4294967296 # If current has "rolled" to zero, adjust it so it is greater than lastInputInfo
current += (
4294967296 # If current has "rolled" to zero, adjust it so it is greater than lastInputInfo
)
millis = current - lastInputInfo.dwTime # @UndefinedVariable
return millis / 1000.0
except Exception as e:
@ -306,9 +285,7 @@ def getSessionType() -> str:
return os.environ.get('SESSIONNAME', 'unknown')
def writeToPipe(
pipeName: str, bytesPayload: bytes, waitForResponse: bool
) -> typing.Optional[bytes]:
def writeToPipe(pipeName: str, bytesPayload: bytes, waitForResponse: bool) -> typing.Optional[bytes]:
# (str, bytes, bool) -> Optional[bytes]
try:
with open(pipeName, 'r+b', 0) as f:
@ -323,8 +300,6 @@ def writeToPipe(
def forceTimeSync() -> None:
try:
subprocess.call(
[r'c:\WINDOWS\System32\w32tm.exe', ' /resync']
) # , '/rediscover'])
subprocess.call([r'c:\WINDOWS\System32\w32tm.exe', ' /resync']) # , '/rediscover'])
except Exception as e:
logger.error('Error invoking time sync command: %s', e)

View File

@ -49,11 +49,13 @@ from ..log import logger
REMOTE_USERS_SID = 'S-1-5-32-555' # Well nown sid for remote desktop users
class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
'''
This class represents a Windows Service for managing actor interactions
with UDS Broker and Machine
'''
# ServiceeFramework related
_svc_name_ = "UDSActorNG"
_svc_display_name_ = "UDS Actor Service"
@ -78,7 +80,9 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
SvcShutdown = SvcStop
def notifyStop(self) -> None:
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED, (self._svc_name_, ''))
servicemanager.LogMsg(
servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED, (self._svc_name_, '')
)
super().notifyStop()
def doWait(self, miliseconds: int) -> None:
@ -86,7 +90,9 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
# On windows, and while on tasks, ensure that our app processes waiting messages on "wait times"
pythoncom.PumpWaitingMessages() # pylint: disable=no-member
def oneStepJoin(self, name: str, domain: str, ou: str, account: str, password: str) -> None: # pylint: disable=too-many-arguments
def oneStepJoin(
self, name: str, domain: str, ou: str, account: str, password: str
) -> None: # pylint: disable=too-many-arguments
'''
Ejecutes the join domain in exactly one step
'''
@ -103,7 +109,9 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
logger.debug('Requested join domain {} without errors'.format(domain))
self.reboot()
def multiStepJoin(self, name: str, domain: str, ou: str, account: str, password: str) -> None: # pylint: disable=too-many-arguments
def multiStepJoin(
self, name: str, domain: str, ou: str, account: str, password: str
) -> None: # pylint: disable=too-many-arguments
currName = operations.getComputerName()
if currName.lower() == name.lower():
currDomain = operations.getDomainName()
@ -119,17 +127,21 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
logger.info('Rebooting computer for activating new name {}'.format(name))
self.reboot()
def joinDomain( # pylint: disable=unused-argument, too-many-arguments
self,
name: str,
domain: str,
ou: str,
account: str,
password: str
) -> None:
def joinDomain(self, name: str, custom: typing.Mapping[str, typing.Any]) -> None:
versionData = operations.getWindowsVersion()
versionInt = versionData[0] * 10 + versionData[1]
logger.debug('Starting joining domain {} with name {} (detected operating version: {})'.format(domain, name, versionData))
# Extract custom data
domain = custom.get('domain', '')
ou = custom.get('ou', '')
account = custom.get('account', '')
password = custom.get('password', '')
logger.debug(
'Starting joining domain {} with name {} (detected operating version: {})'.format(
domain, name, versionData
)
)
# Accepts one step joinDomain, also remember XP is no more supported by
# microsoft, but this also must works with it because will do a "multi
# step" join
@ -144,12 +156,14 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
if protocol == 'rdp': # If connection is not using rdp, skip adding user
# Well known SSID for Remote Desktop Users
groupName = win32security.LookupAccountSid(None, win32security.GetBinarySid(REMOTE_USERS_SID))[0]
groupName = win32security.LookupAccountSid(None, win32security.GetBinarySid(REMOTE_USERS_SID))[0] # type: ignore
useraAlreadyInGroup = False
resumeHandle = 0
while True:
users, _, resumeHandle = win32net.NetLocalGroupGetMembers(None, groupName, 1, resumeHandle, 32768)
users, _, resumeHandle = win32net.NetLocalGroupGetMembers(
None, groupName, 1, resumeHandle, 32768 # type: ignore
)[:3]
if userName.lower() in [u['name'].lower() for u in users]:
useraAlreadyInGroup = True
break
@ -161,7 +175,7 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
self._user = userName
try:
userSSID = win32security.LookupAccountName(None, userName)[0]
win32net.NetLocalGroupAddMembers(None, groupName, 0, [{'sid': userSSID}])
win32net.NetLocalGroupAddMembers(None, groupName, 0, [{'sid': userSSID}]) # type: ignore
except Exception as e:
logger.error('Exception adding user to Remote Desktop Users: {}'.format(e))
else:
@ -178,7 +192,12 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
# Compose packet for ov
usernameBytes = username.encode()
passwordBytes = password.encode()
packet = struct.pack('!I', len(usernameBytes)) + usernameBytes + struct.pack('!I', len(passwordBytes)) + passwordBytes
packet = (
struct.pack('!I', len(usernameBytes))
+ usernameBytes
+ struct.pack('!I', len(passwordBytes))
+ passwordBytes
)
# Send packet with username/password to ov pipe
operations.writeToPipe("\\\\.\\pipe\\VDSMDPipe", packet, True)
return 'done'
@ -187,14 +206,14 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
logger.debug('Windows onLogout invoked: {}, {}'.format(userName, self._user))
try:
p = win32security.GetBinarySid(REMOTE_USERS_SID)
groupName = win32security.LookupAccountSid(None, p)[0]
groupName = win32security.LookupAccountSid(None, p)[0] # type: ignore
except Exception:
logger.error('Exception getting Windows Group')
return
if self._user:
try:
win32net.NetLocalGroupDelMembers(None, groupName, [self._user])
win32net.NetLocalGroupDelMembers(None, groupName, [self._user]) # type: ignore
except Exception as e:
logger.error('Exception removing user from Remote Desktop Users: {}'.format(e))
@ -203,10 +222,12 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
Detect if windows is installing anything, so we can delay the execution of Service
'''
try:
key = wreg.OpenKey(wreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\State')
data, _ = wreg.QueryValueEx(key, 'ImageState')
key = wreg.OpenKey(wreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\State') # type: ignore
data, _ = wreg.QueryValueEx(key, 'ImageState') # type: ignore
logger.debug('State: %s', data)
return data != 'IMAGE_STATE_COMPLETE' # If ImageState is different of ImageStateComplete, there is something running on installation
return (
data != 'IMAGE_STATE_COMPLETE'
) # If ImageState is different of ImageStateComplete, there is something running on installation
except Exception: # If not found, means that no installation is running
return False
@ -214,7 +235,9 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
'''
Main service loop
'''
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, ''))
servicemanager.LogMsg(
servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '')
)
# call the CoInitialize to allow the registration to run in an other
# thread

View File

@ -185,12 +185,12 @@ class LinuxOsADManager(LinuxOsManager):
return {
'action': 'rename_ad',
'name': userService.getName(),
'ad': self._domain,
'custom': {
'domain': self._domain,
'username': self._account,
'password': self._password,
'ou': self._ou,
'isPersistent': self.isPersistent(),
'custom': {
'clientSoftware': self._clientSoftware,
'serverSoftware': self._serverSoftware,
'membershipSoftware': self._membershipSoftware,

View File

@ -174,11 +174,11 @@ class LinuxOsFreeIPAManager(LinuxOsManager):
return {
'action': 'rename_ad',
'name': userService.getName(),
'ad': self._domain,
'custom': {
'domain': self._domain,
'username': self._account,
'password': self._password,
'isPersistent': self.isPersistent(),
'custom': {
'clientSoftware': self._clientSoftware,
'serverSoftware': self._serverSoftware,
'membershipSoftware': self._membershipSoftware,

View File

@ -151,7 +151,7 @@ class LinuxOsManager(osmanagers.OSManager):
def actorData(
self, userService: 'UserService'
) -> typing.MutableMapping[str, typing.Any]:
return {'action': 'rename', 'name': userService.getName()}
return {'action': 'rename', 'name': userService.getName()} # No custom data
def processUnused(self, userService: 'UserService') -> None:
"""

View File

@ -114,9 +114,18 @@ class LinuxRandomPassManager(LinuxOsManager):
return {
'action': 'rename',
'name': userService.getName(),
# Repeat data, to keep compat with old versions of Actor
# Will be removed in a couple of versions
'username': self._userAccount,
'password': '', # On linux, user password is not needed so we provide an empty one
'new_password': self.genPassword(userService),
'custom': {
'username': self._userAccount,
'password': '', # On linux, user password is not needed so we provide an empty one
'new_password': self.genPassword(userService),
},
}
def marshal(self) -> bytes:

View File

@ -160,7 +160,7 @@ class WindowsOsManager(osmanagers.OSManager):
def actorData(
self, userService: 'UserService'
) -> typing.MutableMapping[str, typing.Any]:
return {'action': 'rename', 'name': userService.getName()}
return {'action': 'rename', 'name': userService.getName()} # No custom data
def processUserPassword(
self, userService: 'UserService', username: str, password: str

View File

@ -440,10 +440,20 @@ class WinDomainOsManager(WindowsOsManager):
return {
'action': 'rename_ad',
'name': userService.getName(),
# Repeat data, to keep compat with old versions of Actor
# Will be removed in a couple of versions
'ad': self._domain,
'ou': self._ou,
'username': self._account,
'password': self._password,
'custom': {
'domain': self._domain,
'ou': self._ou,
'username': self._account,
'password': self._password,
},
}
def marshal(self) -> bytes:

View File

@ -127,9 +127,18 @@ class WinRandomPassManager(WindowsOsManager):
return {
'action': 'rename',
'name': userService.getName(),
# Repeat data, to keep compat with old versions of Actor
# Will be removed in a couple of versions
'username': self._userAccount,
'password': self._password,
'new_password': self.genPassword(userService),
'custom': {
'username': self._userAccount,
'password': self._password,
'new_password': self.genPassword(userService),
},
}
def marshal(self) -> bytes: