def _connect():
    global _currentLdapConnection

    if not user.isInAD() and not (user.isRoot() or not computer.isInAD()):
        logging.warning("Cannot perform LDAP search: User is not in AD!")
        _currentLdapConnection = None
        return False

    if not _currentLdapConnection == None:
        return True

    try:
        sasl_auth = ldap.sasl.sasl({}, 'GSSAPI')
        _currentLdapConnection = ldap.initialize(serverUrl(), trace_level=0)
        # TODO:
        # conn.set_option(ldap.OPT_X_TLS_CACERTFILE, '/path/to/ca.pem')
        # conn.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
        # conn.start_tls_s()
        _currentLdapConnection.set_option(ldap.OPT_REFERRALS, 0)
        _currentLdapConnection.protocol_version = ldap.VERSION3

        _currentLdapConnection.sasl_interactive_bind_s("", sasl_auth)
    except Exception as e:
        _currentLdapConnection = None
        logging.error("Cloud not bind to ldap!")
        logging.exception(e)
        return False

    return True
예제 #2
0
def unmountAllSharesOfUser(username):
    logging.info(
        "=== Trying to unmount all shares of user {0} ===".format(username))
    for basedir in [
            constants.shareMountBasepath, constants.hiddenShareMountBasepath
    ]:
        shareMountBasedir = basedir.format(username)

        try:
            mountedShares = os.listdir(shareMountBasedir)
        except FileNotFoundError:
            logging.info(
                "Mount basedir {} does not exist -> nothing to unmount".format(
                    shareMountBasedir))
            continue

        for share in mountedShares:
            _unmountShare("{0}/{1}".format(shareMountBasedir, share))

        if len(os.listdir(shareMountBasedir)) > 0:
            logging.warning(
                "* Mount basedir {} is not empty so not removed!".format(
                    shareMountBasedir))
            return
        else:
            # Delete the directory
            logging.info("Deleting {0}...".format(shareMountBasedir))
            try:
                shutil.rmtree(shareMountBasedir)
            except Exception as e:
                logging.error("FAILED!")
                logging.exception(e)

    logging.info(
        "===> Finished unmounting all shares of user {0} ===".format(username))
예제 #3
0
def cleanTemplateUserGtkBookmarks():
    logging.info("Cleaning {} gtk bookmarks".format(constants.templateUser))
    gtkBookmarksFile = "/home/{0}/.config/gtk-3.0/bookmarks".format(user.username())

    if not os.path.isfile(gtkBookmarksFile):
        logging.warning("Gtk bookmarks file not found, skipping!")
        return

    fileHelper.removeLinesInFileContainingString(gtkBookmarksFile, constants.templateUser)
예제 #4
0
def _processDrivesPolicy(policyBasepath):
    logging.info("== Parsing a drive policy! ==")
    policyFile = "{}/User/Preferences/Drives/Drives.xml".format(policyBasepath)
    shareList = []

    rc, tree = _parseXmlPolicy(policyFile)

    if not rc:
        logging.error(
            "==> Error while reading Drives policy file, skipping! ==")
        return

    xmlDrives = tree.getroot()

    if not xmlDrives.tag == "Drives":
        logging.warning(
            "==> Drive policy xml File is of invalid format, skipping! ==")
        return

    for xmlDrive in xmlDrives:

        if xmlDrive.tag != "Drive" or ("disabled" in xmlDrive.attrib
                                       and xmlDrive.attrib["disabled"] == "1"):
            continue

        drive = {}
        drive["filters"] = []
        for xmlDriveProperty in xmlDrive:
            if xmlDriveProperty.tag == "Properties":
                try:
                    drive["label"] = xmlDriveProperty.attrib["label"]
                    drive["letter"] = xmlDriveProperty.attrib["letter"]
                    drive["path"] = xmlDriveProperty.attrib["path"]
                except Exception as e:
                    logging.warning(
                        "Exception when parsing a drive policy XML file")
                    logging.exception(e)
                    continue

            if xmlDriveProperty.tag == "Filters":
                drive["filters"] = _parseXmlFilters(xmlDriveProperty)

        shareList.append(drive)

    shareList = _processFilters(shareList)

    logging.info("Found shares:")
    for drive in shareList:
        logging.info("* {0}\t\t| {1}\t| {2}\t| {3}".format(
            drive["label"], drive["letter"], drive["path"], drive["filters"]))

        shares.mountShare(drive["path"],
                          shareName="{0} ({1}:)".format(
                              drive["label"], drive["letter"]))

    logging.info("==> Successfully parsed a drive policy! ==")
def getGroupsOfLocalUser(username):
    try:
        groups = subprocess.check_output(["id", "-Gnz", username])
        stringList = [x.decode('utf-8') for x in groups.split(b"\x00")]
        return True, stringList
    except Exception as e:
        logging.warning(
            "Exception when querying groups of user {}, it probaply does not exist"
            .format(username))
        return False, None
예제 #6
0
def _checkNetworkConfigVersion(rawNetworkConfig):
    try:
        networkConfigVersion = int(rawNetworkConfig["version"])
    except KeyError as e:
        logging.warning("The network.conf version could not be identified, assuming 0")
        networkConfigVersion = 0

    if networkConfigVersion != _networkConfigVersion():
        logging.warning("The network.conf Version is a mismatch!")
        return False, networkConfigVersion

    return True, networkConfigVersion
예제 #7
0
def _parseXmlPolicy(policyFile):
    if not os.path.isfile(policyFile):
        logging.warning("==> XML policy file not found! ==")
        return False, None

    try:
        tree = ElementTree.parse(policyFile)
        return True, tree
    except Exception as e:
        logging.exception(e)
        logging.error("==> Error while reading XML policy file! ==")
        return False, None
예제 #8
0
def _unmountShare(mountpoint):
    # check if mountpoint exists
    if (not os.path.exists(mountpoint)) or (not os.path.isdir(mountpoint)):
        logging.warning(
            f"* Could not unmount {mountpoint}, it does not exist.")

    # Try to unmount share
    logging.info("* Trying to unmount {0}...".format(mountpoint))
    if not subprocess.call(["umount", mountpoint]) == 0:
        logging.warning("* Failed!")
        if _directoryIsMountpoint(mountpoint):
            logging.warning("* It is still mounted! Exiting!")
            # Do not delete in this case! We might delete userdata!
            return
        logging.info("* It is not mounted! Continuing!")

    # check if the mountpoint is empty
    if len(os.listdir(mountpoint)) > 0:
        logging.warning(
            "* mountpoint {} is not empty so not removed!".format(mountpoint))
        return

    # Delete the directory
    logging.info("* Deleting {0}...".format(mountpoint))
    try:
        shutil.rmtree(mountpoint)
    except Exception as e:
        logging.error("* FAILED!")
        logging.exception(e)
예제 #9
0
def _runHookScript(filePath):
    if not os.path.isfile(filePath):
        logging.warning(
            "* File {0} should be executed as hook but does not exist!".format(
                filePath))
        return
    if not os.access(filePath, os.X_OK):
        logging.warning(
            "* File {0} is in hook dir but not executable!".format(filePath))
        return

    logging.info("== Executing script {0} ==".format(filePath))

    result = subprocess.call([filePath])

    logging.info("==> Script {0} finished with exit code {1} ==".format(
        filePath, result))
예제 #10
0
def _parseXmlFilters(filtersXmlNode):
    if not filtersXmlNode.tag == "Filters":
        logging.warning("Tried to parse a non-filter node as a filter!")
        return []

    filters = []

    for xmlFilter in filtersXmlNode:
        if xmlFilter.tag == "FilterGroup":
            filters.append({
                "name": xmlFilter.attrib["name"].split("\\")[1],
                "bool": xmlFilter.attrib["bool"],
                "userContext": xmlFilter.attrib["userContext"],
                # userContext defines if the filter applies in user or computer context
                "type": xmlFilter.tag
            })

    return filters
def getGroupsOfLocalUser(username):
    """
    Get all groups of a local user

    :param username: The username of the user
    :type username: str
    :return: Tuple (success, list of groups)
    :rtype: tuple
    """
    try:
        groups = subprocess.check_output(["id", "-Gnz", username])
        stringList = [x.decode('utf-8') for x in groups.split(b"\x00")]
        return True, stringList
    except Exception as e:
        logging.warning(
            "Exception when querying groups of user {}, it probaply does not exist"
            .format(username))
        return False, None
def _apply(templatePath):
    try:
        # read template file
        rc, fileData = _readTextfile(templatePath)

        if not rc:
            logging.error('Failed!')
            return False

        fileData = _resolveVariables(fileData)

        # get target path
        firstLine = fileData.split('\n')[0]
        targetFilePath = firstLine.partition(' ')[2]

        # remove first line (the target file path)
        fileData = fileData[fileData.find('\n'):]

        # never ever overwrite sssd.conf, this will lead to issues!
        # sssd.conf is written by `realm join`!
        if targetFilePath in constants.notTemplatableFiles:
            logging.warning(
                "Skipping forbidden file {}".format(targetFilePath))
            return True

        # create target directory
        Path(Path(targetFilePath).parent.absolute()).mkdir(parents=True,
                                                           exist_ok=True)

        # remove comment lines beginning with # from .xml files
        if targetFilePath.endswith('.xml'):
            fileData = _stripComment(fileData)

        # write config file
        logging.debug("-> to {}".format(targetFilePath))
        with open(targetFilePath, 'w') as targetFile:
            targetFile.write(fileData)

        return True

    except Exception as e:
        logging.error('Failed!')
        logging.exception(e)
        return False
예제 #13
0
def removeLinesInFileContainingString(filePath, forbiddenStrings):
    """
    Remove all lines containing a given string form a file.

    :param filePath: The path to the file
    :type filePath: str
    :param forbiddenStrings: The string to search for
    :type forbiddenStrings: str
    :return: True on success, False otherwise
    :rtype: bool
    """
    if not isinstance(forbiddenStrings, list):
        forbiddenStrings = [forbiddenStrings]

    try:
        with open(filePath, "r") as originalFile:
            originalContents = originalFile.read()
    except Exception as e:
        logging.exception(e)
        logging.warning("Could not read contents of original file")
        return False

    newContents = ""
    for line in originalContents.split("\n"):
        lineIsClean = True
        for forbiddenString in forbiddenStrings:
            lineIsClean = lineIsClean and not forbiddenString in line

        if lineIsClean:
            newContents += line + "\n"

    try:
        with open(filePath, "w") as originalFile:
            originalFile.write(newContents)
    except Exception as e:
        logging.exception(e)
        logging.warning("Could not write new contents to original file")
        return False

    return True
예제 #14
0
def _processFilters(policies):
    filteredPolicies = []

    for policy in policies:
        if not len(policy["filters"]) > 0:
            filteredPolicies.append(policy)
        else:
            filtersPassed = True
            for filter in policy["filters"]:
                logging.debug("Testing filter: {}".format(filter))
                # TODO: check for AND and OR
                if filter["bool"] == "AND":
                    filtersPassed = filtersPassed and _processFilter(filter)
                elif filter["bool"] == "OR":
                    filtersPassed = filtersPassed or _processFilter(filter)
                else:
                    logging.warning(
                        "Unknown boolean operation: {}! Cannot process filter!"
                        .format(filter["bool"]))

            if filtersPassed:
                filteredPolicies.append(policy)

    return filteredPolicies
예제 #15
0
def _processPrintersPolicy(policyBasepath):
    logging.info("== Parsing a printer policy! ==")
    policyFile = "{}/User/Preferences/Printers/Printers.xml".format(
        policyBasepath)
    printerList = []
    # test
    rc, tree = _parseXmlPolicy(policyFile)

    if not rc:
        logging.error(
            "==> Error while reading Printer policy file, skipping! ==")
        return

    xmlPrinters = tree.getroot()

    if not xmlPrinters.tag == "Printers":
        logging.warning(
            "==> Printer policy xml File is of invalid format, skipping! ==")
        return

    for xmlPrinter in xmlPrinters:

        if xmlPrinter.tag != "SharedPrinter" or (
                "disabled" in xmlPrinter.attrib
                and xmlPrinter.attrib["disabled"] == "1"):
            continue

        printer = {}
        printer["filters"] = []

        try:
            printer["name"] = xmlPrinter.attrib["name"]
        except Exception as e:
            logging.warning(
                "Exception when reading a printer name from a printer policy XML file"
            )
            logging.exception(e)

        for xmlPrinterProperty in xmlPrinter:
            if xmlPrinterProperty.tag == "Properties":
                try:
                    rc, printerUrl = printers.translateSambaToIpp(
                        xmlPrinterProperty.attrib["path"])
                    if rc:
                        printer["path"] = printerUrl
                except Exception as e:
                    logging.warning(
                        "Exception when parsing a printer policy XML file")
                    logging.exception(e)
                    continue

            if xmlPrinterProperty.tag == "Filters":
                printer["filters"] = _parseXmlFilters(xmlPrinterProperty)

        printerList.append(printer)

    printerList = _processFilters(printerList)

    logging.info("Found printers:")
    for printer in printerList:
        logging.info("* {0}\t\t| {1}\t| {2}".format(printer["name"],
                                                    printer["path"],
                                                    printer["filters"]))
        printers.installPrinter(printer["path"], printer["name"])

    logging.info("==> Successfully parsed a printer policy! ==")
예제 #16
0
def _mountShare(username,
                networkPath,
                shareName,
                hiddenShare,
                useCruidOfExecutingUser=False):

    mountpoint = _getShareMountpoint(networkPath, username, hiddenShare,
                                     shareName)

    mountCommandOptions = f"file_mode=0700,dir_mode=0700,sec=krb5,nodev,nosuid,mfsymlinks,nobrl,vers=3.0,user={username}"
    rc, networkConfig = config.network()
    domain = None

    if rc:
        domain = networkConfig["domain"]
        mountCommandOptions += f",domain={domain.upper()}"

    try:
        pwdInfo = pwd.getpwnam(username)
        uid = pwdInfo.pw_uid
        gid = pwdInfo.pw_gid
        mountCommandOptions += f",gid={gid},uid={uid}"

        if not useCruidOfExecutingUser:
            mountCommandOptions += f",cruid={uid}"

    except KeyError:
        uid = -1
        gid = -1
        logging.warning("Uid could not be found! Continuing anyway!")

    mountCommand = [
        "/usr/sbin/mount.cifs", "-o", mountCommandOptions, networkPath,
        mountpoint
    ]

    logging.debug(f"Trying to mount '{networkPath}' to '{mountpoint}'")
    logging.debug("* Creating directory...")

    try:
        Path(mountpoint).mkdir(parents=True, exist_ok=False)
    except FileExistsError:
        # Test if a share is already mounted there
        if _directoryIsMountpoint(mountpoint):
            logging.debug("* The mountpoint is already mounted.")
            return True, mountpoint
        else:
            logging.warning(
                "* The target directory already exists, proceeding anyway!")

    logging.debug("* Executing '{}' ".format(" ".join(mountCommand)))
    logging.debug("* Trying to mount...")
    if not subprocess.call(
            mountCommand, stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0:
        logging.fatal(
            f"* Error mounting share {networkPath} to {mountpoint}!\n")
        return False, None

    logging.debug("* Success!")

    # hide the shares parent dir (/home/%user/media) in case it is not a hidden share
    if not hiddenShare:
        try:
            hiddenFilePath = f"{mountpoint}/../../.hidden"
            logging.debug(f"* hiding parent dir {hiddenFilePath}")
            hiddenFile = open(hiddenFilePath, "w+")
            hiddenFile.write(mountpoint.split("/")[-2])
            hiddenFile.close()
        except:
            logging.warning(f"Could not hide parent dir of share {mountpoint}")

    return True, mountpoint