From d2536b31200106ad0bbea60abd3e654c23540e6d Mon Sep 17 00:00:00 2001 From: Amitay Isaacs Date: Thu, 25 Aug 2011 17:10:23 +1000 Subject: py-samba3: Use passdb/param wrapper for samba3 module Instead of parsing samba3 database files (password, group mapping, account policy, secrets), use passdb python wrapper. Similarly for parsing configuration, use samba3 param python wrapper. Other databases (idmap, registry, wins) are still parsed in python. Signed-off-by: Andrew Bartlett --- source4/scripting/python/samba/samba3/__init__.py | 454 ++-------------------- 1 file changed, 23 insertions(+), 431 deletions(-) (limited to 'source4/scripting/python/samba/samba3') diff --git a/source4/scripting/python/samba/samba3/__init__.py b/source4/scripting/python/samba/samba3/__init__.py index 385d9331ec..dd2f927aa4 100644 --- a/source4/scripting/python/samba/samba3/__init__.py +++ b/source4/scripting/python/samba/samba3/__init__.py @@ -26,6 +26,9 @@ import os import struct import tdb +import passdb +import param as s3param + def fetch_uint32(tdb, key): try: @@ -125,74 +128,6 @@ class Registry(TdbDatabase): return ret -class PolicyDatabase(TdbDatabase): - """Samba 3 Account Policy database reader.""" - def __init__(self, file): - """Open a policy database - - :param file: Path to the file to open. - """ - super(PolicyDatabase, self).__init__(file) - self.min_password_length = fetch_uint32(self.tdb, "min password length\x00") - self.password_history = fetch_uint32(self.tdb, "password history\x00") - self.user_must_logon_to_change_password = fetch_uint32(self.tdb, "user must logon to change pasword\x00") - self.maximum_password_age = fetch_uint32(self.tdb, "maximum password age\x00") - self.minimum_password_age = fetch_uint32(self.tdb, "minimum password age\x00") - self.lockout_duration = fetch_uint32(self.tdb, "lockout duration\x00") - self.reset_count_minutes = fetch_uint32(self.tdb, "reset count minutes\x00") - self.bad_lockout_minutes = fetch_uint32(self.tdb, "bad lockout minutes\x00") - self.disconnect_time = fetch_int32(self.tdb, "disconnect time\x00") - self.refuse_machine_password_change = fetch_uint32(self.tdb, "refuse machine password change\x00") - - # FIXME: Read privileges as well - - -GROUPDB_DATABASE_VERSION_V1 = 1 # native byte format. -GROUPDB_DATABASE_VERSION_V2 = 2 # le format. - -GROUP_PREFIX = "UNIXGROUP/" - -# Alias memberships are stored reverse, as memberships. The performance -# critical operation is to determine the aliases a SID is member of, not -# listing alias members. So we store a list of alias SIDs a SID is member of -# hanging of the member as key. -MEMBEROF_PREFIX = "MEMBEROF/" - -class GroupMappingDatabase(TdbDatabase): - """Samba 3 group mapping database reader.""" - def _check_version(self): - assert fetch_int32(self.tdb, "INFO/version\x00") in (GROUPDB_DATABASE_VERSION_V1, GROUPDB_DATABASE_VERSION_V2) - - def groupsids(self): - """Retrieve the SIDs for the groups in this database. - - :return: List with sids as strings. - """ - for k in self.tdb.iterkeys(): - if k.startswith(GROUP_PREFIX): - yield k[len(GROUP_PREFIX):].rstrip("\0") - - def get_group(self, sid): - """Retrieve the group mapping information for a particular group. - - :param sid: SID of the group - :return: None if the group can not be found, otherwise - a tuple with gid, sid_name_use, the NT name and comment. - """ - data = self.tdb.get("%s%s\0" % (GROUP_PREFIX, sid)) - if data is None: - return data - (gid, sid_name_use) = struct.unpack(" 0: - (bad_password_time, data) = unpack_int32(data) - if bad_password_time != 0: - user.bad_password_time = bad_password_time - (pass_last_set_time, data) = unpack_int32(data) - (pass_can_change_time, data) = unpack_int32(data) - (pass_must_change_time, data) = unpack_int32(data) - - if logon_time != 0: - user.logon_time = logon_time - user.logoff_time = logoff_time - user.kickoff_time = kickoff_time - if pass_last_set_time != 0: - user.pass_last_set_time = pass_last_set_time - user.pass_can_change_time = pass_can_change_time - - (user.username, data) = unpack_string(data) - (user.domain, data) = unpack_string(data) - (user.nt_username, data) = unpack_string(data) - (user.fullname, data) = unpack_string(data) - (user.homedir, data) = unpack_string(data) - (user.dir_drive, data) = unpack_string(data) - (user.logon_script, data) = unpack_string(data) - (user.profile_path, data) = unpack_string(data) - (user.acct_desc, data) = unpack_string(data) - (user.workstations, data) = unpack_string(data) - (user.unknown_str, data) = unpack_string(data) - (user.munged_dial, data) = unpack_string(data) - - (user.user_rid, data) = unpack_int32(data) - (user.group_rid, data) = unpack_int32(data) - - (user.lm_password, data) = unpack_string(data) - (user.nt_password, data) = unpack_string(data) - - if self.version > 1: - (user.nt_password_history, data) = unpack_string(data) - - (user.acct_ctrl, data) = unpack_uint16(data) - (_, data) = unpack_uint32(data) # remove_me field - (user.logon_divs, data) = unpack_uint16(data) - (hours, data) = unpack_string(data) - user.hours = [] - for entry in hours: - for i in range(8): - user.hours.append(ord(entry) & (2 ** i) == (2 ** i)) - (user.bad_password_count, data) = unpack_uint16(data) - (user.logon_count, data) = unpack_uint16(data) - (user.unknown_6, data) = unpack_uint32(data) - assert len(data) == 0 - return user - - def shellsplit(text): """Very simple shell-like line splitting. @@ -675,139 +355,51 @@ class WinsDatabase(object): pass -class ParamFile(object): - """Simple smb.conf-compatible file parser - - Does not use a parameter table, unlike the "normal". - """ - - def __init__(self, sections=None): - self._sections = sections or {} - - def _sanitize_name(self, name): - return name.strip().lower().replace(" ","") - - def __repr__(self): - return "ParamFile(%r)" % self._sections - - def read(self, filename): - """Read a file. - - :param filename: Path to the file - """ - section = None - for i, l in enumerate(open(filename, 'r').xreadlines()): - l = l.strip() - if not l or l[0] == '#' or l[0] == ';': - continue - if l[0] == "[" and l[-1] == "]": - section = self._sanitize_name(l[1:-1]) - self._sections.setdefault(section, {}) - elif "=" in l: - (k, v) = l.split("=", 1) - self._sections[section][self._sanitize_name(k)] = v - else: - raise Exception("Unable to parser line %d: %r" % (i+1,l)) - - def get(self, param, section=None): - """Return the value of a parameter. - - :param param: Parameter name - :param section: Section name, defaults to "global" - :return: parameter value as string if found, None otherwise. - """ - if section is None: - section = "global" - section = self._sanitize_name(section) - if not section in self._sections: - return None - param = self._sanitize_name(param) - if not param in self._sections[section]: - return None - return self._sections[section][param].strip() - - def __getitem__(self, section): - return self._sections[section] - - def get_section(self, section): - return self._sections.get(section) - - def add_section(self, section): - self._sections[self._sanitize_name(section)] = {} - - def set_string(self, name, value): - self._sections["global"][name] = value - - def get_string(self, name): - return self._sections["global"].get(name) - - class Samba3(object): """Samba 3 configuration and state data reader.""" - def __init__(self, libdir, smbconfpath): + def __init__(self, smbconfpath, s3_lp_ctx=None): """Open the configuration and data for a Samba 3 installation. - :param libdir: Library directory :param smbconfpath: Path to the smb.conf file. + :param s3_lp_ctx: Samba3 Loadparm context """ self.smbconfpath = smbconfpath - self.libdir = libdir - self.lp = ParamFile() - self.lp.read(self.smbconfpath) - self.privatedir = self.lp.get("private dir") or libdir + if s3_lp_ctx: + self.lp = s3_lp_ctx + else: + self.lp = s3param.get_context() + self.lp.load(smbconfpath) - def libdir_path(self, path): + def statedir_path(self, path): if path[0] == "/" or path[0] == ".": return path - return os.path.join(self.libdir, path) + return os.path.join(self.lp.get("state directory"), path) def privatedir_path(self, path): if path[0] == "/" or path[0] == ".": return path - return os.path.join(self.privatedir, path) + return os.path.join(self.lp.get("private dir"), path) def get_conf(self): return self.lp def get_sam_db(self): - lp = self.get_conf() - backends = (lp.get("passdb backend") or "").split(" ") - if ":" in backends[0]: - (name, location) = backends[0].split(":", 2) - else: - name = backends[0] - location = None - if name == "smbpasswd": - return SmbpasswdFile(self.libdir_path(location or "smbpasswd")) - elif name == "tdbsam": - return TdbSam(self.libdir_path(location or "passdb.tdb")) - elif name == "ldapsam": - if location is not None: - return LdapSam("ldap:%s" % location) - return LdapSam(lp.get("ldap server")) - else: - raise NotImplementedError("unsupported passdb backend %s" % backends[0]) - - def get_policy_db(self): - return PolicyDatabase(self.libdir_path("account_policy.tdb")) + return passdb.PDB(self.lp.get('passdb backend')) def get_registry(self): - return Registry(self.libdir_path("registry.tdb")) + return Registry(self.statedir_path("registry.tdb")) def get_secrets_db(self): return SecretsDatabase(self.privatedir_path("secrets.tdb")) def get_shareinfo_db(self): - return ShareInfoDatabase(self.libdir_path("share_info.tdb")) + return ShareInfoDatabase(self.statedir_path("share_info.tdb")) def get_idmap_db(self): - return IdmapDatabase(self.libdir_path("winbindd_idmap.tdb")) + return IdmapDatabase(self.statedir_path("winbindd_idmap.tdb")) def get_wins_db(self): - return WinsDatabase(self.libdir_path("wins.dat")) + return WinsDatabase(self.statedir_path("wins.dat")) def get_shares(self): return Shares(self.get_conf(), self.get_shareinfo_db()) - - def get_groupmapping_db(self): - return GroupMappingDatabase(self.libdir_path("group_mapping.tdb")) -- cgit