From c264ea9c136596eded450317b982d69c390daa4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Thu, 18 May 2023 15:04:47 +0200 Subject: [PATCH] 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 --- actor/src/udsactor/linux/operations.py | 7 +- actor/src/udsactor/linux/service.py | 25 ++- actor/src/udsactor/rest.py | 5 - actor/src/udsactor/service.py | 159 ++++++------------ actor/src/udsactor/types.py | 5 - actor/src/udsactor/windows/operations.py | 73 +++----- actor/src/udsactor/windows/service.py | 73 +++++--- .../LinuxOsManager/linux_ad_osmanager.py | 10 +- .../LinuxOsManager/linux_freeipa_osmanager.py | 8 +- .../LinuxOsManager/linux_osmanager.py | 2 +- .../linux_randompass_osmanager.py | 9 + .../osmanagers/WindowsOsManager/windows.py | 2 +- .../WindowsOsManager/windows_domain.py | 10 ++ .../WindowsOsManager/windows_random.py | 9 + 14 files changed, 179 insertions(+), 218 deletions(-) diff --git a/actor/src/udsactor/linux/operations.py b/actor/src/udsactor/linux/operations.py index bc9e80279..a93361431 100644 --- a/actor/src/udsactor/linux/operations.py +++ b/actor/src/udsactor/linux/operations.py @@ -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', '') diff --git a/actor/src/udsactor/linux/service.py b/actor/src/udsactor/linux/service.py index 34e2feb84..3a8698260 100644 --- a/actor/src/udsactor/linux/service.py +++ b/actor/src/udsactor/linux/service.py @@ -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}') diff --git a/actor/src/udsactor/rest.py b/actor/src/udsactor/rest.py index 8881512e2..b7fe21ec6 100644 --- a/actor/src/udsactor/rest.py +++ b/actor/src/udsactor/rest.py @@ -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'] diff --git a/actor/src/udsactor/service.py b/actor/src/udsactor/service.py index fb84fcbe4..15fe82e6c 100644 --- a/actor/src/udsactor/service.py +++ b/actor/src/udsactor/service.py @@ -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 diff --git a/actor/src/udsactor/types.py b/actor/src/udsactor/types.py index ca97b6e9d..beec0a8e6 100644 --- a/actor/src/udsactor/types.py +++ b/actor/src/udsactor/types.py @@ -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): diff --git a/actor/src/udsactor/windows/operations.py b/actor/src/udsactor/windows/operations.py index 4a9cdce65..677df5355 100644 --- a/actor/src/udsactor/windows/operations.py +++ b/actor/src/udsactor/windows/operations.py @@ -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) diff --git a/actor/src/udsactor/windows/service.py b/actor/src/udsactor/windows/service.py index 0568a8ada..ed08ca16f 100644 --- a/actor/src/udsactor/windows/service.py +++ b/actor/src/udsactor/windows/service.py @@ -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 @@ -139,17 +151,19 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService): logger.info('Using multiple step join because configuration requests to do so') self.multiStepJoin(name, domain, ou, account, password) - 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: logger.debug('Pre connect invoked') 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,18 +222,22 @@ 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 - except Exception: # If not found, means that no installation is running + 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 def SvcDoRun(self) -> None: # pylint: disable=too-many-statements, too-many-branches ''' 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() diff --git a/server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py b/server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py index 54a59d2aa..d6c067b06 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py +++ b/server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py @@ -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, diff --git a/server/src/uds/osmanagers/LinuxOsManager/linux_freeipa_osmanager.py b/server/src/uds/osmanagers/LinuxOsManager/linux_freeipa_osmanager.py index df8a50e80..c0034d018 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/linux_freeipa_osmanager.py +++ b/server/src/uds/osmanagers/LinuxOsManager/linux_freeipa_osmanager.py @@ -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, diff --git a/server/src/uds/osmanagers/LinuxOsManager/linux_osmanager.py b/server/src/uds/osmanagers/LinuxOsManager/linux_osmanager.py index 971a2f20a..7bc82248f 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/linux_osmanager.py +++ b/server/src/uds/osmanagers/LinuxOsManager/linux_osmanager.py @@ -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: """ diff --git a/server/src/uds/osmanagers/LinuxOsManager/linux_randompass_osmanager.py b/server/src/uds/osmanagers/LinuxOsManager/linux_randompass_osmanager.py index e4e64c9ec..443aa4400 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/linux_randompass_osmanager.py +++ b/server/src/uds/osmanagers/LinuxOsManager/linux_randompass_osmanager.py @@ -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: diff --git a/server/src/uds/osmanagers/WindowsOsManager/windows.py b/server/src/uds/osmanagers/WindowsOsManager/windows.py index a77ac074a..e8d277355 100644 --- a/server/src/uds/osmanagers/WindowsOsManager/windows.py +++ b/server/src/uds/osmanagers/WindowsOsManager/windows.py @@ -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 diff --git a/server/src/uds/osmanagers/WindowsOsManager/windows_domain.py b/server/src/uds/osmanagers/WindowsOsManager/windows_domain.py index d5c228726..b3c4443c6 100644 --- a/server/src/uds/osmanagers/WindowsOsManager/windows_domain.py +++ b/server/src/uds/osmanagers/WindowsOsManager/windows_domain.py @@ -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: diff --git a/server/src/uds/osmanagers/WindowsOsManager/windows_random.py b/server/src/uds/osmanagers/WindowsOsManager/windows_random.py index b77a7a62e..fce60c83c 100644 --- a/server/src/uds/osmanagers/WindowsOsManager/windows_random.py +++ b/server/src/uds/osmanagers/WindowsOsManager/windows_random.py @@ -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: