def createContainers(self): for container in list(self.containers): currentContainerPath = self.USER_ROOT_FOLDER + container.name if not PosixPath(currentContainerPath).is_file(): pwd = self.PASSWORD if not container.config["useUserPassword"]: pwd = self.getVolPasswordFromDB(container.name) if not container.data.createDevice( container.config["sizeInMB"], pwd, container.config["weak"]): self.containers.remove(container) continue try: chown(currentContainerPath, self.USER_NAME, self.USER_NAME) except Exception as e: logger.log( logging.ERROR, "Error changing '{}' owner to {}: {}".format( currentContainerPath, self.USER_NAME, e)) self.containers.remove(container) continue logger.log( logging.INFO, "Container {} created with size {}".format( container.name, container.config["sizeInMB"])) container.created = True
def isLuksypamEnabled(self): if not PosixPath(self.USER_ROOT_FOLDER[:-1]).is_dir() or not PosixPath( self.USER_CONFIG_FILE).is_file(): logger.log( logging.INFO, "Not activated, cant find {} neither {}".format( self.USER_ROOT_FOLDER[:-1], self.USER_CONFIG_FILE)) return False return True
def closeContainers(self): for container in list(self.containers): if container.data.isOpen(): if not container.data.close(): self.containers.remove(container) continue logger.log( logging.INFO, "Container {} closed successfully".format(container.name))
def parse(self): try: with open(self.path) as confFile: self.data = json.load(confFile) except Exception as e: logger.log(logging.ERROR, "Error parsing JSON file {}: {}".format(self.path, e)) return False return True
def disconnect(self): try: self.conn.commit() self.cursor.close() except Exception as e: logger.log(logging.ERROR, "Disconnecting from database error: {}".format(e)) return False return True
def generatePseudoRandomFileGarbage(path, size): try: with open(path, "wb") as f: f.write(os.urandom(size * 1024**2)) except Exception as e: logger.log(logging.ERROR, "Error generating garbage file {}: {}".format(path, e)) return False return True
def changePassword(self, old, new): try: self.c.addKeyByPassphrase(old, new) self.c.removePassphrase(old) except Exception as e: logger.log( logging.ERROR, "Can't change device {} password: {}".format(self.path, e)) return False return True
def initDB(self): self.db = SQLCipher(self.DB_PATH) if not self.db.connect(self.PASSWORD): self.db = None return cur = self.db.getCursor() try: cur.execute("SELECT count(*) FROM sqlite_master;") except Exception as e: logger.log(logging.INFO, "Can not access DB {}: {}".format(self.DB_PATH, e)) self.db = None
def connect(self, key): try: self.conn = sqlite.connect(self.path) self.conn.row_factory = sqlite.Row except Exception as e: logger.log( logging.ERROR, "Connecting to database {} error: {}".format(self.path, e)) self.cursor = None return False with self.conn: self.cursor = self.conn.cursor() self.cursor.execute("PRAGMA key='{}'".format(key)) return True
def loadConfs(self): configs = ParseConfig.ParseConfig(self.USER_CONFIG_FILE) if not configs.parse() or configs.isEmpty() or not configs.isValid(): return False configs = configs.getContent() logger.log( logging.INFO, "Config file for user '{}' found and valid".format(self.USER_NAME)) for name in configs: if configs[name]["enable"]: currentContainerPath = self.USER_ROOT_FOLDER + name self.containers.append( Container(name, configs[name], LUKSDevice.LUKSDevice(currentContainerPath))) return True
def umountContainers(self): for container in list(self.containers): currentMountPath = self.USER_HOME + "/" + container.config[ "mountDir"] if os.path.ismount(currentMountPath): ret = umount(currentMountPath) if not ret[0]: logger.log( logging.ERROR, "Error umounting {}: {} returned {}".format( currentMountPath, ret[1], ret[0])) self.containers.remove(container) continue logger.log(logging.INFO, "{} umount sucessfully".format(container.name))
def mountContainers(self): for container in list(self.containers): currentMountPath = self.USER_HOME + "/" + container.config[ "mountDir"] if not PosixPath(currentMountPath).is_dir(): try: PosixPath(currentMountPath).mkdir() except Exception as e: logger.log( logging.ERROR, "Error creating folder {}: {}".format( currentMountPath, e)) self.containers.remove(container) continue if PosixPath(currentMountPath).is_symlink(): logger.log( logging.ERROR, "Error folder {} must not be a symlink".format( currentMountPath)) self.containers.remove(container) continue if not os.path.ismount(currentMountPath): deviceInfos = container.data.c.info() currentDevicePath = deviceInfos["dir"] + "/" + deviceInfos[ "name"] ret = mount(currentDevicePath, currentMountPath, FORMAT_DRIVE_IN) if not ret[0]: logger.log( logging.ERROR, "Error mounting {} on {}: {} returned {}".format( currentDevicePath, currentMountPath, ret[1], ret[0])) self.containers.remove(container) continue try: chown(currentMountPath, self.USER_NAME, self.USER_NAME) except Exception as e: logger.log( logging.ERROR, "Error changing '{}' owner to {}: {}".format( currentMountPath, self.USER_NAME, e)) self.containers.remove(container) continue logger.log(logging.INFO, "Container {} mounted".format(container.name))
def init(self): self.USER_HOME = getUserHome(self.USER_NAME) if not self.USER_HOME: logger.log( logging.ERROR, "Can't get user {} home directory".format(self.USER_NAME)) return False self.USER_ROOT_FOLDER = self.USER_HOME + "/" + LUKSYPAM_FOLDER_NAME + "/" self.USER_CONFIG_FILE = self.USER_ROOT_FOLDER + CONFIG_FILE_NAME self.DB_PATH = self.USER_ROOT_FOLDER + LUKSYPAM_DB_NAME self.initDB() if self.db: cur = self.db.getCursor() cur.execute( "CREATE TABLE IF NOT EXISTS Containers(Name TEXT PRIMARY KEY, Password TEXT)" ) try: chown(self.DB_PATH, self.USER_NAME, self.USER_NAME) except Exception: pass return True
def openContainers(self): for container in list(self.containers): if not container.data.isOpen(): pwd = self.PASSWORD if not container.config["useUserPassword"]: pwd = self.getVolPasswordFromDB(container.name) if not container.data.open(pwd): self.containers.remove(container) continue deviceInfos = container.data.c.info() logger.log( logging.DEBUG, "Container {} infos: {}".format(container.name, deviceInfos)) currentDevicePath = deviceInfos["dir"] + "/" + deviceInfos["name"] if container.created: currentMountPath = self.USER_HOME + "/" + container.config[ "mountDir"] if os.path.ismount(currentMountPath): ret = umount(currentMountPath) if not ret[0]: logger.log( logging.ERROR, "Error umounting {}: {} returned {}".format( currentMountPath, ret[1], ret[0])) self.containers.remove(container) continue ret = execShellCmd("mkfs.{} {}".format(FORMAT_DRIVE_IN, currentDevicePath)) if ret[0] != 0: logger.log( logging.ERROR, "Error formating device {}: {}".format( currentDevicePath, ret[2])) self.containers.remove(container) continue logger.log( logging.INFO, "Container {} openned successfully".format(container.name))
def log_to_systemd(level, msg="<log message is not available>"): logger.log(logLevels.get(level, logging.NOTSET), "{}".format(msg)) return
def isValid(self): if not self.data: return False mountDirs = list() for container in self.data: if '../' in container or '/..' in container: logger.log( logging.ERROR, "Error: container name '{}' path must not contains '..'". format(container)) return False for key in MANDATORY_ENTRIES: if not key[0] in self.data[container]: logger.log( logging.ERROR, "Error: can't find key '{}' in {} config".format( key[0], container)) return False if not isinstance(self.data[container][key[0]], key[1]): logger.log( logging.ERROR, "Error in {} config, '{}' must be of type {}".format( container, key[0], key[1].__name__)) return False if key[0] == 'mountDir': if not self.data[container][key[0]]: logger.log( logging.ERROR, "Error in {} config, '{}' is empty".format( container, key[0])) return False if '../' in self.data[container][ key[0]] or '/..' in self.data[container][key[0]]: logger.log( logging.ERROR, "Error in {} config, '{}' path must not contains '..'" .format(container, key[0])) return False if self.data[container][key[0]] in mountDirs: logger.log( logging.ERROR, "Error in {} config, duplicate mount dir '{}'". format(container, self.data[container][key[0]])) return False mountDirs.append(self.data[container][key[0]]) if key[0] == 'sizeInMB': if self.data[container][ key[0]] < DEFAULT_MIN_CONTAINER_SIZE: logger.log( logging.ERROR, "Error in {} config, '{}' size must but at least {}" .format(container, key[0], DEFAULT_MIN_CONTAINER_SIZE)) return False return True
#!/usr/bin/env python3 import os import sys import logging from luksypam_log import logger import LuksyPam password = input()[:-1] MANDATORY_ENV_VARS = ['PAM_USER', 'PAM_TYPE'] for key in MANDATORY_ENV_VARS: if not key in os.environ: logger.log(logging.ERROR, "Env var {} is missing".format(key)) sys.exit(0) if not os.environ[key]: logger.log(logging.ERROR, "Env var '{}' is empty".format(key)) sys.exit(0) username = os.environ["PAM_USER"] action = os.environ["PAM_TYPE"] if action != "auth": sys.exit(0) inst = LuksyPam.LuksyPam(username, password) if not inst.init() or not inst.isLuksypamEnabled() or not inst.loadConfs(): sys.exit(0)
#!/usr/bin/env python3 import os import sys import psutil import logging from luksypam_log import logger import LuksyPam MANDATORY_ENV_VARS = ['PAM_USER', 'PAM_TYPE'] for key in MANDATORY_ENV_VARS: if not key in os.environ: logger.log(logging.ERROR, "Env var {} is missing".format(key)) sys.exit(0) if not os.environ[key]: logger.log(logging.ERROR, "Env var '{}' is empty".format(key)) sys.exit(0) username = os.environ["PAM_USER"] action = os.environ["PAM_TYPE"] if action != "close_session": sys.exit(0) users = list() for user in psutil.users(): users.append(user.name) logger.log(logging.DEBUG, "Logged users : {}".format(users)) if users.count(username) > 1: sys.exit(0)