def createActionProcessorUser(self, recreate=True): if not config.get('action_processor', 'create_user'): return run_as_user = config.get('action_processor', 'run_as_user') if run_as_user.lower() == 'system': self._actionProcessorUserName = '' self._actionProcessorUserPassword = '' return if '\\' in run_as_user: logger.warning( "Ignoring domain part of user to run action processor '%s'", run_as_user) run_as_user = run_as_user.split('\\', -1) if not recreate and self._actionProcessorUserName and self._actionProcessorUserPassword and System.existsUser( username=run_as_user): return self._actionProcessorUserName = run_as_user logger.notice(f"Creating local user '{run_as_user}'") self._actionProcessorUserPassword = '******' + str( randomString(16)) + '!/%' secret_filter.add_secrets(self._actionProcessorUserPassword) if System.existsUser(username=run_as_user): System.deleteUser(username=run_as_user) System.createUser(username=run_as_user, password=self._actionProcessorUserPassword, groups=[System.getAdminGroupName()])
def setBlockLogin(self, blockLogin, handleNotifier=True): # pylint: disable=too-many-branches blockLogin = forceBool(blockLogin) changed = self._blockLogin != blockLogin self._blockLogin = blockLogin logger.notice("Block login now set to '%s'", self._blockLogin) if self._blockLogin: if not self._blockLoginEventId: self._blockLoginEventId = timeline.addEvent( title="Blocking login", description="User login blocked", category="block_login", durationEvent=True) if not self._blockLoginNotifierPid and config.get( 'global', 'block_login_notifier'): if handleNotifier and RUNNING_ON_WINDOWS: logger.info("Starting block login notifier app") # Start block login notifier on physical console sessionId = System.getActiveConsoleSessionId() while True: try: self._blockLoginNotifierPid = System.runCommandInSession( command=config.get('global', 'block_login_notifier'), sessionId=sessionId, desktop='winlogon', waitForProcessEnding=False)[2] break except Exception as err: # pylint: disable=broad-except logger.error( "Failed to start block login notifier app: %s", err) break else: if self._blockLoginEventId: timeline.setEventEnd(eventId=self._blockLoginEventId) self._blockLoginEventId = None if handleNotifier and self._blockLoginNotifierPid: try: logger.info( "Terminating block login notifier app (pid %s)", self._blockLoginNotifierPid) System.terminateProcess( processId=self._blockLoginNotifierPid) except Exception as err: # pylint: disable=broad-except logger.warning( "Failed to terminate block login notifier app: %s", err) self._blockLoginNotifierPid = None if changed and self._controlPipe: try: self._controlPipe.executeRpc("blockLogin", self._blockLogin) except Exception as rpc_error: # pylint: disable=broad-except logger.debug(rpc_error)
def rebootMachine(self, waitSeconds=3): if config.get('global', 'suspend_bitlocker_on_reboot'): windowsVersion = sys.getwindowsversion() # pylint: disable=no-member if (windowsVersion.major == 6 and windowsVersion.minor >= 4 ) or windowsVersion.major > 6: # Win10 and later self.suspendBitlocker() super().rebootMachine(waitSeconds)
def getDaemonLoopingContext(): with getEventGeneratorContext(): for event_generator in getEventGenerators( generatorClass=DaemonStartupEventGenerator): try: event_generator.createAndFireEvent() except ValueError as err: logger.error( "Unable to fire DaemonStartupEvent from %s: %s", event_generator, err, exc_info=True) if getEventGenerators(generatorClass=GUIStartupEventGenerator): # Wait until gui starts up logger.notice( "Waiting for gui startup (timeout: %d seconds)", config.get('global', 'wait_for_gui_timeout')) self.waitForGUI( timeout=config.get('global', 'wait_for_gui_timeout')) if not self.is_stopping(): logger.notice("Done waiting for GUI") # Wait some more seconds for events to fire time.sleep(5) try: yield finally: for event_generator in getEventGenerators( generatorClass=DaemonShutdownEventGenerator): logger.info( "Create and fire shutdown event generator %s", event_generator) try: event_generator.createAndFireEvent() except ValueError as err: logger.error( "Unable to fire DaemonStartupEvent from %s: %s", event_generator, err, exc_info=True)
def getControlServer(): logger.notice("Starting control server") self._controlServer = None try: self._controlServer = ControlServer( opsiclientd=self, httpsPort=config.get('control_server', 'port'), sslServerKeyFile=config.get('control_server', 'ssl_server_key_file'), sslServerCertFile=config.get('control_server', 'ssl_server_cert_file'), staticDir=config.get('control_server', 'static_dir')) logger.debug("Current control server: %s", self._controlServer) self._controlServer.start() logger.notice("Control server started") self._stopEvent.wait(1) if self._stopEvent.is_set(): # Probably a failure during binding to port. raise RuntimeError("Received stop signal.") yield except Exception as err: # pylint: disable=broad-except logger.error("Failed to start control server: %s", err, exc_info=True) raise err finally: if self._controlServer: logger.info("Stopping control server") try: self._controlServer.stop() self._controlServer.join(2) logger.info("Control server stopped") except (NameError, RuntimeError) as stopError: logger.debug("Stopping controlServer failed: %s", stopError)
def deleteActionProcessorUser(self): if not config.get('action_processor', 'delete_user'): return if not self._actionProcessorUserName: return if not System.existsUser(username=self._actionProcessorUserName): return logger.notice("Deleting local user '%s'", self._actionProcessorUserName) System.deleteUser(username=self._actionProcessorUserName) self._actionProcessorUserName = '' self._actionProcessorUserPassword = ''
def getCurrentActiveDesktopName(self, sessionId=None): if not RUNNING_ON_WINDOWS: return None if not ('opsiclientd_rpc' in config.getDict() and 'command' in config.getDict()['opsiclientd_rpc']): raise Exception("opsiclientd_rpc command not defined") if sessionId is None: sessionId = System.getActiveSessionId() if sessionId is None: sessionId = System.getActiveConsoleSessionId() rpc = f"setCurrentActiveDesktopName(\"{sessionId}\", System.getActiveDesktopName())" cmd = config.get('opsiclientd_rpc', 'command') + ' "' + rpc.replace( '"', '\\"') + '"' try: System.runCommandInSession(command=cmd, sessionId=sessionId, desktop="winlogon", waitForProcessEnding=True, timeoutSeconds=60, noWindow=True) except Exception as err: # pylint: disable=broad-except logger.error(err) desktop = self._currentActiveDesktopName.get(sessionId) if not desktop: logger.warning( "Failed to get current active desktop name for session %s, using 'default'", sessionId) desktop = "default" self._currentActiveDesktopName[sessionId] = desktop logger.debug( "Returning current active dektop name '%s' for session %s", desktop, sessionId) return desktop
def createOpsiSetupUser(self, admin=True, delete_existing=False): # pylint: disable=no-self-use,too-many-branches # https://bugs.python.org/file46988/issue.py user_info = { "name": OPSI_SETUP_USER_NAME, "full_name": "opsi setup user", "comment": "auto created by opsi", "password": f"/{''.join((random.choice(string.ascii_letters + string.digits) for i in range(8)))}?", "priv": win32netcon.USER_PRIV_USER, "flags": win32netcon.UF_NORMAL_ACCOUNT | win32netcon.UF_SCRIPT | win32netcon.UF_DONT_EXPIRE_PASSWD } # Test if user exists user_sid = None try: win32net.NetUserGetInfo(None, user_info["name"], 1) user_sid = win32security.ConvertSidToStringSid( win32security.LookupAccountName(None, user_info["name"])[0]) logger.info("User '%s' exists, sid is '%s'", user_info["name"], user_sid) except Exception as err: # pylint: disable=broad-except logger.info(err) self.cleanup_opsi_setup_user( keep_sid=None if delete_existing else user_sid) if delete_existing: user_sid = None # Hide user from login try: winreg.CreateKeyEx( winreg.HKEY_LOCAL_MACHINE, r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts', 0, winreg.KEY_WOW64_64KEY | winreg.KEY_ALL_ACCESS # sysnative ) except WindowsError: # pylint: disable=undefined-variable pass try: winreg.CreateKeyEx( winreg.HKEY_LOCAL_MACHINE, r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList', 0, winreg.KEY_WOW64_64KEY | winreg.KEY_ALL_ACCESS # sysnative ) except WindowsError: # pylint: disable=undefined-variable pass with winreg.OpenKey( winreg.HKEY_LOCAL_MACHINE, r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList', 0, winreg.KEY_SET_VALUE | winreg.KEY_WOW64_64KEY # sysnative ) as reg_key: winreg.SetValueEx(reg_key, user_info["name"], 0, winreg.REG_DWORD, 0) if user_sid: logger.info("Updating password of user '%s'", user_info["name"]) user_info_update = win32net.NetUserGetInfo(None, user_info["name"], 1) user_info_update["password"] = user_info["password"] win32net.NetUserSetInfo(None, user_info["name"], 1, user_info_update) else: logger.info("Creating user '%s'", user_info["name"]) win32net.NetUserAdd(None, 1, user_info) user_sid = win32security.ConvertSidToStringSid( win32security.LookupAccountName(None, user_info["name"])[0]) subprocess.run([ "icacls", os.path.dirname(sys.argv[0]), "/grant:r", f"*{user_sid}:(OI)(CI)RX" ], check=False) subprocess.run([ "icacls", os.path.dirname(config.get("global", "log_file")), "/grant:r", f"*{user_sid}:(OI)(CI)F" ], check=False) subprocess.run([ "icacls", os.path.dirname(config.get("global", "tmp_dir")), "/grant:r", f"*{user_sid}:(OI)(CI)F" ], check=False) local_admin_group_sid = win32security.ConvertStringSidToSid( "S-1-5-32-544") local_admin_group_name = win32security.LookupAccountSid( None, local_admin_group_sid)[0] try: if admin: logger.info("Adding user '%s' to admin group", user_info["name"]) win32net.NetLocalGroupAddMembers( None, local_admin_group_name, 3, [{ "domainandname": user_info["name"] }]) else: logger.info("Removing user '%s' from admin group", user_info["name"]) win32net.NetLocalGroupDelMembers(None, local_admin_group_name, [user_info["name"]]) except pywintypes.error as err: # 1377 - ERROR_MEMBER_NOT_IN_ALIAS # The specified account name is not a member of the group. # 1378 # ERROR_MEMBER_IN_ALIAS # The specified account name is already a member of the group. if err.winerror not in (1377, 1378): raise user_info_4 = win32net.NetUserGetInfo(None, user_info["name"], 4) user_info_4["password"] = user_info["password"] return user_info_4
def showPopup(self, message, mode='prepend', addTimestamp=True, displaySeconds=0): # pylint: disable=too-many-branches,too-many-statements, too-many-locals if mode not in ('prepend', 'append', 'replace'): mode = 'prepend' port = config.get('notification_server', 'popup_port') if not port: raise Exception('notification_server.popup_port not defined') notifierCommand = config.get('opsiclientd_notifier', 'command') if not notifierCommand: raise Exception('opsiclientd_notifier.command not defined') notifierCommand = f'{notifierCommand} -s {os.path.join("notifier", "popup.ini")}' if addTimestamp: message = "=== " + time.strftime( "%Y-%m-%d %H:%M:%S") + " ===\n" + message with self._popupNotificationLock: # pylint: disable=too-many-nested-blocks if (mode in ('prepend', 'append') and self._popupNotificationServer and self._popupNotificationServer.isListening()): # Already runnning try: for subject in self._popupNotificationServer.getSubjects(): if subject.getId() == 'message': if mode == 'prepend': message = message + "\n\n" + subject.getMessage( ) else: message = subject.getMessage( ) + "\n\n" + message break except Exception as err: # pylint: disable=broad-except logger.warning(err, exc_info=True) self.hidePopup() popupSubject = MessageSubject(id='message') choiceSubject = ChoiceSubject(id='choice') popupSubject.setMessage(message) logger.notice( "Starting popup message notification server on port %d", port) try: self._popupNotificationServer = NotificationServer( address="127.0.0.1", start_port=port, subjects=[popupSubject, choiceSubject]) self._popupNotificationServer.daemon = True with log_context({'instance': 'popup notification server'}): if not self._popupNotificationServer.start_and_wait( timeout=30): raise Exception( "Timed out while waiting for notification server") except Exception as err: # pylint: disable=broad-except logger.error("Failed to start notification server: %s", err) raise notifierCommand = notifierCommand.replace( '%port%', str(self._popupNotificationServer.port)).replace( '%id%', "popup") choiceSubject.setChoices([_('Close')]) choiceSubject.setCallbacks([self.popupCloseCallback]) sessionIds = System.getActiveSessionIds() if not sessionIds: sessionIds = [System.getActiveConsoleSessionId()] for sessionId in sessionIds: desktops = [None] if RUNNING_ON_WINDOWS: desktops = ["default", "winlogon"] for desktop in desktops: try: System.runCommandInSession(command=notifierCommand, sessionId=sessionId, desktop=desktop, waitForProcessEnding=False) except Exception as err: # pylint: disable=broad-except logger.error( "Failed to start popup message notifier app in session %s on desktop %s: %s", sessionId, desktop, err) class PopupClosingThread(threading.Thread): def __init__(self, opsiclientd, seconds): super().__init__() self.opsiclientd = opsiclientd self.seconds = seconds self.stopped = False def stop(self): self.stopped = True def run(self): while not self.stopped: time.sleep(1) if time.time() > self.seconds: break if not self.stopped: logger.debug("hiding popup window") self.opsiclientd.hidePopup() # last popup decides end time (even if unlimited) if self._popupClosingThread and self._popupClosingThread.is_alive( ): self._popupClosingThread.stop() if displaySeconds > 0: logger.debug("displaying popup for %s seconds", displaySeconds) self._popupClosingThread = PopupClosingThread( self, time.time() + displaySeconds) self._popupClosingThread.start()