Example #1
0
    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()])
Example #2
0
    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)
Example #3
0
 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)
Example #4
0
        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)
Example #5
0
        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)
Example #6
0
    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 = ''
Example #7
0
    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
Example #8
0
    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
Example #9
0
    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()