diff options
Diffstat (limited to 'source4/scripting/python')
| -rw-r--r-- | source4/scripting/python/misc_wrap.c | 73 | ||||
| -rw-r--r-- | source4/scripting/python/samba/__init__.py | 20 | ||||
| -rw-r--r-- | source4/scripting/python/samba/provision.py | 4 | ||||
| -rw-r--r-- | source4/scripting/python/samba/samba3.py | 224 | ||||
| -rw-r--r-- | source4/scripting/python/samba/tests/__init__.py | 1 | ||||
| -rw-r--r-- | source4/scripting/python/samba/tests/samba3.py | 57 | ||||
| -rw-r--r-- | source4/scripting/python/samba/upgrade.py | 16 | 
7 files changed, 335 insertions, 60 deletions
diff --git a/source4/scripting/python/misc_wrap.c b/source4/scripting/python/misc_wrap.c index dc1203e2f0..0f36647487 100644 --- a/source4/scripting/python/misc_wrap.c +++ b/source4/scripting/python/misc_wrap.c @@ -2808,9 +2808,7 @@ SWIGINTERN PyObject *_wrap_random_password(PyObject *SWIGUNUSEDPARM(self), PyObj      (char *) "len", NULL     }; -  { -    arg1 = NULL; -  } +  arg1 = NULL;    if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"O:random_password",kwnames,&obj0)) SWIG_fail;    ecode2 = SWIG_AsVal_size_t(obj0, &val2);    if (!SWIG_IsOK(ecode2)) { @@ -2855,11 +2853,9 @@ SWIGINTERN PyObject *_wrap_ldb_set_credentials(PyObject *SWIGUNUSEDPARM(self), P      }      arg2 = (struct cli_credentials *)(argp2);    } -  { -    if (arg1 == NULL) -    SWIG_exception(SWIG_ValueError,  -      "ldb context must be non-NULL"); -  } +  if (arg1 == NULL) +  SWIG_exception(SWIG_ValueError,  +    "ldb context must be non-NULL");    ldb_set_credentials(arg1,arg2);    resultobj = SWIG_Py_Void();    return resultobj; @@ -2893,11 +2889,9 @@ SWIGINTERN PyObject *_wrap_ldb_set_session_info(PyObject *SWIGUNUSEDPARM(self),      SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "ldb_set_session_info" "', argument " "2"" of type '" "struct auth_session_info *""'");     }    arg2 = (struct auth_session_info *)(argp2); -  { -    if (arg1 == NULL) -    SWIG_exception(SWIG_ValueError,  -      "ldb context must be non-NULL"); -  } +  if (arg1 == NULL) +  SWIG_exception(SWIG_ValueError,  +    "ldb context must be non-NULL");    ldb_set_session_info(arg1,arg2);    resultobj = SWIG_Py_Void();    return resultobj; @@ -2936,11 +2930,9 @@ SWIGINTERN PyObject *_wrap_ldb_set_loadparm(PyObject *SWIGUNUSEDPARM(self), PyOb      }      arg2 = (struct loadparm_context *)(argp2);    } -  { -    if (arg1 == NULL) -    SWIG_exception(SWIG_ValueError,  -      "ldb context must be non-NULL"); -  } +  if (arg1 == NULL) +  SWIG_exception(SWIG_ValueError,  +    "ldb context must be non-NULL");    ldb_set_loadparm(arg1,arg2);    resultobj = SWIG_Py_Void();    return resultobj; @@ -2975,11 +2967,9 @@ SWIGINTERN PyObject *_wrap_samdb_set_domain_sid(PyObject *SWIGUNUSEDPARM(self),      SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "samdb_set_domain_sid" "', argument " "2"" of type '" "struct dom_sid const *""'");     }    arg2 = (struct dom_sid *)(argp2); -  { -    if (arg1 == NULL) -    SWIG_exception(SWIG_ValueError,  -      "ldb context must be non-NULL"); -  } +  if (arg1 == NULL) +  SWIG_exception(SWIG_ValueError,  +    "ldb context must be non-NULL");    result = (bool)samdb_set_domain_sid(arg1,(struct dom_sid const *)arg2);    resultobj = SWIG_From_bool((bool)(result));    return resultobj; @@ -3025,19 +3015,16 @@ SWIGINTERN PyObject *_wrap_dsdb_attach_schema_from_ldif_file(PyObject *SWIGUNUSE      SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "dsdb_attach_schema_from_ldif_file" "', argument " "3"" of type '" "char const *""'");    }    arg3 = (char *)(buf3); -  { -    if (arg1 == NULL) -    SWIG_exception(SWIG_ValueError,  -      "ldb context must be non-NULL"); -  } +  if (arg1 == NULL) +  SWIG_exception(SWIG_ValueError,  +    "ldb context must be non-NULL");    result = dsdb_attach_schema_from_ldif_file(arg1,(char const *)arg2,(char const *)arg3); -  { -    if (!W_ERROR_IS_OK(result)) { -      PyObject *obj = Py_BuildValue("(i,s)", (&result)->v, win_errstr(result)); -      PyErr_SetObject(PyExc_RuntimeError, obj); -    } else if (resultobj == NULL) { -      resultobj = Py_None; -    } +  if (!W_ERROR_IS_OK(result)) { +    PyObject *obj = Py_BuildValue("(i,s)", (&result)->v, win_errstr(result)); +    PyErr_SetObject(PyExc_RuntimeError, obj); +    SWIG_fail; +  } else if (resultobj == NULL) { +    resultobj = Py_None;    }    if (alloc2 == SWIG_NEWOBJ) free((char*)buf2);    if (alloc3 == SWIG_NEWOBJ) free((char*)buf3); @@ -3079,11 +3066,9 @@ SWIGINTERN PyObject *_wrap_dsdb_set_global_schema(PyObject *SWIGUNUSEDPARM(self)      SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "dsdb_set_global_schema" "', argument " "1"" of type '" "struct ldb_context *""'");     }    arg1 = (struct ldb_context *)(argp1); -  { -    if (arg1 == NULL) -    SWIG_exception(SWIG_ValueError,  -      "ldb context must be non-NULL"); -  } +  if (arg1 == NULL) +  SWIG_exception(SWIG_ValueError,  +    "ldb context must be non-NULL");    result = (int)dsdb_set_global_schema(arg1);    resultobj = SWIG_From_int((int)(result));    return resultobj; @@ -3109,11 +3094,9 @@ SWIGINTERN PyObject *_wrap_ldb_register_samba_handlers(PyObject *SWIGUNUSEDPARM(      SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ldb_register_samba_handlers" "', argument " "1"" of type '" "struct ldb_context *""'");     }    arg1 = (struct ldb_context *)(argp1); -  { -    if (arg1 == NULL) -    SWIG_exception(SWIG_ValueError,  -      "ldb context must be non-NULL"); -  } +  if (arg1 == NULL) +  SWIG_exception(SWIG_ValueError,  +    "ldb context must be non-NULL");    result = (int)ldb_register_samba_handlers(arg1);    resultobj = SWIG_From_int((int)(result));    return resultobj; diff --git a/source4/scripting/python/samba/__init__.py b/source4/scripting/python/samba/__init__.py index c735361353..5b34534133 100644 --- a/source4/scripting/python/samba/__init__.py +++ b/source4/scripting/python/samba/__init__.py @@ -91,7 +91,14 @@ class Ldb(ldb.Ldb):      def searchone(self, basedn, attribute, expression=None,                     scope=ldb.SCOPE_BASE): -        """Search for one attribute as a string.""" +        """Search for one attribute as a string. +         +        :param basedn: BaseDN for the search. +        :param attribute: Name of the attribute +        :param expression: Optional search expression. +        :param scope: Search scope (defaults to base). +        :return: Value of attribute as a string or None if it wasn't found. +        """          res = self.search(basedn, scope, expression, [attribute])          if len(res) != 1 or res[0][attribute] is None:              return None @@ -100,7 +107,7 @@ class Ldb(ldb.Ldb):          return values.pop()      def erase(self): -        """Erase an ldb, removing all records.""" +        """Erase this ldb, removing all records."""          # delete the specials          for attr in ["@INDEXLIST", "@ATTRIBUTES", "@SUBCLASSES", "@MODULES",                        "@OPTIONS", "@PARTITION", "@KLUDGEACL"]: @@ -149,13 +156,18 @@ class Ldb(ldb.Ldb):          :param ldif_path: Path to LDIF file.          """ -        self.load_ldif_add(open(ldif_path, 'r').read()) +        self.add_ldif(open(ldif_path, 'r').read()) -    def load_ldif_add(self, ldif): +    def add_ldif(self, ldif):          for changetype, msg in self.parse_ldif(ldif):              assert changetype == ldb.CHANGETYPE_NONE              self.add(msg) +    def modify_ldif(self, ldif): +        for (changetype, msg) in ldb.parse_ldif(data): +            assert changetype == CHANGETYPE_MODIFY +            ldb.modify(msg) +  def substitute_var(text, values):      """substitute strings of the form ${NAME} in str, replacing diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py index dcf567954a..a4e6c6a214 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -111,9 +111,7 @@ def setup_modify_ldif(ldb, ldif_path, substvars=None):      assert "${" not in data -    for (changetype, msg) in ldb.parse_ldif(data): -        assert changetype == CHANGETYPE_MODIFY -        ldb.modify(msg) +    ldb.modify_ldif(data)  def setup_ldb(ldb, ldif_path, subst_vars): diff --git a/source4/scripting/python/samba/samba3.py b/source4/scripting/python/samba/samba3.py new file mode 100644 index 0000000000..d8289ae756 --- /dev/null +++ b/source4/scripting/python/samba/samba3.py @@ -0,0 +1,224 @@ +#!/usr/bin/python + +# Unix SMB/CIFS implementation. +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 +#    +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +#    +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +#    +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# + +"""Support for reading Samba 3 data files.""" + +REGISTRY_VALUE_PREFIX = "SAMBA_REGVAL" +REGISTRY_DB_VERSION = 1 + +import tdb + +class Registry: +    """Simple read-only support for reading the Samba3 registry.""" +    def __init__(self, file): +        self.tdb = tdb.Tdb(file) + +    def __len__(self): +        """Return the number of keys.""" +        return len(self.keys()) + +    def keys(self): +        """Return list with all the keys.""" +        return [k.rstrip("\x00") for k in self.tdb.keys() if not k.startswith(REGISTRY_VALUE_PREFIX)] + +    def subkeys(self, key): +        data = self.tdb.get(key) +        if data is None: +            return [] +        # FIXME: Parse data +        return [] + +    def values(self, key): +        """Return a dictionary with the values set for a specific key.""" +        data = self.tdb.get("%s/%s" % (REGISTRY_VALUE_PREFIX, key)) +        if data is None: +            return {} +        # FIXME: Parse data +        return {} + + +class PolicyDatabase: +    def __init__(self, file): +        self.tdb = tdb.Tdb(file) +        self.min_password_length = tdb.fetch_uint32("min password length") +        self.user_must_logon_to_change_password = tdb.fetch_uint32("password history") +        self.user_must_logon_to_change_password = tdb.fetch_uint32("user must logon to change pasword") +        self.maximum_password_age = tdb.fetch_uint32("maximum password age") +        self.minimum_password_age = tdb.fetch_uint32("minimum password age") +        self.lockout_duration = tdb.fetch_uint32("lockout duration") +        self.reset_count_minutes = tdb.fetch_uint32("reset count minutes") +        self.bad_lockout_minutes = tdb.fetch_uint32("bad lockout minutes") +        self.disconnect_time = tdb.fetch_uint32("disconnect time") +        self.refuse_machine_password_change = tdb.fetch_uint32("refuse machine password change") + +        # 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: +    def __init__(self, file):  +        self.tdb = tdb.Tdb(file) + + +# High water mark keys +HWM_GROUP = "GROUP HWM" +HWM_USER = "USER HWM" + +# idmap version determines auto-conversion +IDMAP_VERSION = 2 + +class IdmapDatabase: +    def __init__(self, file): +        self.tdb = tdb.Tdb(file) +        assert self.tdb.fetch_int32("IDMAP_VERSION") == IDMAP_VERSION + + +class SecretsDatabase: +    def __init__(self, file): +        self.tdb = tdb.Tdb(file) +        self.domains = {} +        for k, v in self.tdb.items(): +            if k == "SECRETS/AUTH_PASSWORD": +                self.auth_password = v +            elif k == "SECRETS/AUTH_DOMAIN": +                self.auth_domain = v +            elif k == "SECRETS/AUTH_USER": +                self.auth_user = v +            elif k.startswith("SECRETS/SID/"): +                pass # FIXME +            elif k.startswith("SECRETS/DOMGUID/"): +                pass # FIXME +            elif k.startswith("SECRETS/LDAP_BIND_PW/"): +                pass # FIXME +            elif k.startswith("SECRETS/AFS_KEYFILE/"): +                pass # FIXME +            elif k.startswith("SECRETS/MACHINE_SEC_CHANNEL_TYPE/"): +                pass # FIXME +            elif k.startswith("SECRETS/MACHINE_LAST_CHANGE_TIME/"): +                pass # FIXME +            elif k.startswith("SECRETS/MACHINE_PASSWORD/"): +                pass # FIXME +            elif k.startswith("SECRETS/$MACHINE.ACC/"): +                pass # FIXME +            elif k.startswith("SECRETS/$DOMTRUST.ACC/"): +                pass # FIXME +            elif k == "INFO/random_seed": +                self.random_seed = v +            else: +                raise "Unknown key %s in secrets database" % k + +SHARE_DATABASE_VERSION_V1 = 1 +SHARE_DATABASE_VERSION_V2 = 2 + +class ShareInfoDatabase: +    def __init__(self, file): +        self.tdb = tdb.Tdb(file) +        assert self.tdb.fetch_int32("INFO/version") in (SHARE_DATABASE_VERSION_V1, SHARE_DATABASE_VERSION_V2) + +    def get_secdesc(self, name): +        secdesc = self.tdb.get("SECDESC/%s" % name) +        # FIXME: Run ndr_pull_security_descriptor + + +ACB_DISABLED = 0x00000001 +ACB_HOMDIRREQ = 0x00000002 +ACB_PWNOTREQ = 0x00000004 +ACB_TEMPDUP = 0x00000008 +ACB_NORMAL = 0x00000010 +ACB_MNS = 0x00000020 +ACB_DOMTRUST = 0x00000040 +ACB_WSTRUST = 0x00000080 +ACB_SVRTRUST = 0x00000100 +ACB_PWNOEXP = 0x00000200 +ACB_AUTOLOCK = 0x00000400 +ACB_ENC_TXT_PWD_ALLOWED = 0x00000800 +ACB_SMARTCARD_REQUIRED = 0x00001000 +ACB_TRUSTED_FOR_DELEGATION = 0x00002000 +ACB_NOT_DELEGATED = 0x00004000 +ACB_USE_DES_KEY_ONLY = 0x00008000 +ACB_DONT_REQUIRE_PREAUTH = 0x00010000 +ACB_PW_EXPIRED = 0x00020000 +ACB_NO_AUTH_DATA_REQD = 0x00080000 + +acb_info_mapping = { +        'N': ACB_PWNOTREQ,  # 'N'o password.  +        'D': ACB_DISABLED,  # 'D'isabled. +		'H': ACB_HOMDIRREQ, # 'H'omedir required. +		'T': ACB_TEMPDUP,   # 'T'emp account. +		'U': ACB_NORMAL,    # 'U'ser account (normal). +		'M': ACB_MNS,       # 'M'NS logon user account. What is this ? +		'W': ACB_WSTRUST,   # 'W'orkstation account. +		'S': ACB_SVRTRUST,  # 'S'erver account.  +		'L': ACB_AUTOLOCK,  # 'L'ocked account. +		'X': ACB_PWNOEXP,   # No 'X'piry on password +		'I': ACB_DOMTRUST,  # 'I'nterdomain trust account. +        ' ': 0 +        } + + +class Smbpasswd: +    def __init__(self, file): +        pass + + +class TdbSam: +    def __init__(self, file): +        self.tdb = tdb.Tdb(file) + + +class WinsDatabase: +    def __init__(self, file): +        pass + + +class Samba3: +    def __init__(self, smbconfpath, libdir): +        self.smbconfpath = smbconfpath +        self.libdir = libdir + +    def get_policy_db(self): +        return PolicyDatabase(os.path.join(libdir, "account_policy.tdb")) +     +    def get_registry(self): +        return Registry(os.path.join(libdir, "registry.tdb")) + +    def get_secrets_db(self): +        return SecretsDatabase(os.path.join(libdir, "secrets.tdb")) + +    def get_shares_db(self): +        return ShareInfoDatabase(os.path.join(libdir, "share_info.tdb")) + +    def get_idmap_db(self): +        return IdmapDatabase(os.path.join(libdir, "winbindd_idmap.tdb")) + +    def get_wins_db(self): +        return WinsDatabase(os.path.join(libdir, "wins.dat")) + +    def get_groupmapping_db(self): +        return GroupMappingDatabase(os.path.join(libdir, "group_mapping.tdb")) diff --git a/source4/scripting/python/samba/tests/__init__.py b/source4/scripting/python/samba/tests/__init__.py index e213c1cc1f..b01807c02f 100644 --- a/source4/scripting/python/samba/tests/__init__.py +++ b/source4/scripting/python/samba/tests/__init__.py @@ -43,6 +43,7 @@ class TestCaseInTempDir(unittest.TestCase):      def tearDown(self):          super(TestCaseInTempDir, self).tearDown() +        # FIXME: Remove all files in self.tempdir  class SubstituteVarTestCase(unittest.TestCase): diff --git a/source4/scripting/python/samba/tests/samba3.py b/source4/scripting/python/samba/tests/samba3.py new file mode 100644 index 0000000000..c6b6281c60 --- /dev/null +++ b/source4/scripting/python/samba/tests/samba3.py @@ -0,0 +1,57 @@ +#!/usr/bin/python + +# Unix SMB/CIFS implementation. +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 +#    +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +#    +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +#    +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# + +import unittest +from samba.samba3 import GroupMappingDatabase, Registry, PolicyDatabase +import os + +DATADIR=os.path.join(os.path.dirname(__file__), "../../../../../testdata/samba3") + +class RegistryTestCase(unittest.TestCase): +    def setUp(self): +        self.registry = Registry(os.path.join(DATADIR, "registry.tdb")) + +    def test_length(self): +        self.assertEquals(28, len(self.registry)) + +    def test_keys(self): +        self.assertEquals([], self.registry.keys()) + + +class PolicyTestCase(unittest.TestCase): +    def setUp(self): +        self.policy = PolicyDatabase(os.path.join(DATADIR, "account_policy.tdb")) + +    def test_policy(self): +        self.assertEquals(self.policy.min_password_length, 5) +        self.assertEquals(self.policy.minimum_password_age, 0) +        self.assertEquals(self.policy.maximum_password_age, 999999999) +        self.assertEquals(self.policy.refuse_machine_password_change, 0) +        self.assertEquals(self.policy.reset_count_minutes, 0) +        self.assertEquals(self.policy.disconnect_time, -1) +        self.assertEquals(self.policy.user_must_logon_to_change_password, 0) +        self.assertEquals(self.policy.password_history, 0) +        self.assertEquals(self.policy.lockout_duration, 0) +        self.assertEquals(self.policy.bad_lockout_minutes, 0) + + +class GroupsTestCase(unittest.TestCase): +    def setUp(self): +        self.groupdb = GroupMappingDatabase(os.path.join(DATADIR, "group_mapping.tdb")) + diff --git a/source4/scripting/python/samba/upgrade.py b/source4/scripting/python/samba/upgrade.py index 1c27f8ec25..3168fedf2d 100644 --- a/source4/scripting/python/samba/upgrade.py +++ b/source4/scripting/python/samba/upgrade.py @@ -7,7 +7,7 @@  """Support code for upgrading from Samba 3 to Samba 4.""" -from provision import findnss +from provision import findnss, provision  import provision  import grp  import pwd @@ -69,7 +69,7 @@ data:: %s""" % (keydn, rv.name, rv.name, rv.type, ldb.encode(rv.data))      return ldif -def upgrade_sam_policy(samba3,dn): +def upgrade_sam_policy(policy,dn):      ldif = """  dn: %s  changetype: modify @@ -84,11 +84,11 @@ samba3UserMustLogonToChangePassword: %d  samba3BadLockoutMinutes: %d  samba3DisconnectTime: %d -""" % (dn, samba3.policy.min_password_length,  -    samba3.policy.password_history, samba3.policy.minimum_password_age, -    samba3.policy.maximum_password_age, samba3.policy.lockout_duration, -    samba3.policy.reset_count_minutes, samba3.policy.user_must_logon_to_change_password, -    samba3.policy.bad_lockout_minutes, samba3.policy.disconnect_time) +""" % (dn, policy.min_password_length,  +    policy.password_history, policy.minimum_password_age, +    policy.maximum_password_age, policy.lockout_duration, +    policy.reset_count_minutes, policy.user_must_logon_to_change_password, +    policy.bad_lockout_minutes, policy.disconnect_time)      return ldif @@ -465,7 +465,7 @@ replace: type  type: 4  replace: data  data: %d -""" % samba3.policy.refuse_machine_password_change) +""" % policy.refuse_machine_password_change)      message("Importing users")      for account in samba3.samaccounts:  | 
