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:
parent
880aa24dbb
commit
c264ea9c13
@ -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', '')
|
||||
|
@ -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}')
|
||||
|
@ -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']
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
@ -239,7 +262,7 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
|
||||
logger.info('Service stopped due to init')
|
||||
self.finish()
|
||||
win32event.WaitForSingleObject(self._hWaitStop, 5000)
|
||||
return # Stop daemon if initializes told to do so
|
||||
return # Stop daemon if initializes told to do so
|
||||
|
||||
# Initialization is done, set machine to ready for UDS, communicate urls, etc...
|
||||
self.setReady()
|
||||
|
@ -185,12 +185,12 @@ class LinuxOsADManager(LinuxOsManager):
|
||||
return {
|
||||
'action': 'rename_ad',
|
||||
'name': userService.getName(),
|
||||
'ad': self._domain,
|
||||
'username': self._account,
|
||||
'password': self._password,
|
||||
'ou': self._ou,
|
||||
'isPersistent': self.isPersistent(),
|
||||
'custom': {
|
||||
'domain': self._domain,
|
||||
'username': self._account,
|
||||
'password': self._password,
|
||||
'ou': self._ou,
|
||||
'isPersistent': self.isPersistent(),
|
||||
'clientSoftware': self._clientSoftware,
|
||||
'serverSoftware': self._serverSoftware,
|
||||
'membershipSoftware': self._membershipSoftware,
|
||||
|
@ -174,11 +174,11 @@ class LinuxOsFreeIPAManager(LinuxOsManager):
|
||||
return {
|
||||
'action': 'rename_ad',
|
||||
'name': userService.getName(),
|
||||
'ad': self._domain,
|
||||
'username': self._account,
|
||||
'password': self._password,
|
||||
'isPersistent': self.isPersistent(),
|
||||
'custom': {
|
||||
'domain': self._domain,
|
||||
'username': self._account,
|
||||
'password': self._password,
|
||||
'isPersistent': self.isPersistent(),
|
||||
'clientSoftware': self._clientSoftware,
|
||||
'serverSoftware': self._serverSoftware,
|
||||
'membershipSoftware': self._membershipSoftware,
|
||||
|
@ -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:
|
||||
"""
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user