• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src/router/samba-3.5.8/source4/scripting/python/samba/
1#!/usr/bin/python
2
3# Unix SMB/CIFS implementation.
4# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
5#
6# Based on the original in EJS:
7# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program.  If not, see <http://www.gnu.org/licenses/>.
21#
22
23"""Samba 4."""
24
25__docformat__ = "restructuredText"
26
27import os
28
29def _in_source_tree():
30    """Check whether the script is being run from the source dir. """
31    return os.path.exists("%s/../../../selftest/skip" % os.path.dirname(__file__))
32
33
34# When running, in-tree, make sure bin/python is in the PYTHONPATH
35if _in_source_tree():
36    import sys
37    srcdir = "%s/../../.." % os.path.dirname(__file__)
38    sys.path.append("%s/bin/python" % srcdir)
39    default_ldb_modules_dir = "%s/bin/modules/ldb" % srcdir
40else:
41    default_ldb_modules_dir = None
42
43
44import ldb
45import glue
46
47class Ldb(ldb.Ldb):
48    """Simple Samba-specific LDB subclass that takes care
49    of setting up the modules dir, credentials pointers, etc.
50
51    Please note that this is intended to be for all Samba LDB files,
52    not necessarily the Sam database. For Sam-specific helper
53    functions see samdb.py.
54    """
55    def __init__(self, url=None, lp=None, modules_dir=None, session_info=None,
56                 credentials=None, flags=0, options=None):
57        """Opens a Samba Ldb file.
58
59        :param url: Optional LDB URL to open
60        :param lp: Optional loadparm object
61        :param modules_dir: Optional modules directory
62        :param session_info: Optional session information
63        :param credentials: Optional credentials, defaults to anonymous.
64        :param flags: Optional LDB flags
65        :param options: Additional options (optional)
66
67        This is different from a regular Ldb file in that the Samba-specific
68        modules-dir is used by default and that credentials and session_info
69        can be passed through (required by some modules).
70        """
71
72        if modules_dir is not None:
73            self.set_modules_dir(modules_dir)
74        elif default_ldb_modules_dir is not None:
75            self.set_modules_dir(default_ldb_modules_dir)
76        elif lp is not None:
77            self.set_modules_dir(os.path.join(lp.get("modules dir"), "ldb"))
78
79        if session_info is not None:
80            self.set_session_info(session_info)
81
82        if credentials is not None:
83            self.set_credentials(credentials)
84
85        if lp is not None:
86            self.set_loadparm(lp)
87
88        # This must be done before we load the schema, as these handlers for
89        # objectSid and objectGUID etc must take precedence over the 'binary
90        # attribute' declaration in the schema
91        glue.ldb_register_samba_handlers(self)
92
93        # TODO set debug
94        def msg(l,text):
95            print text
96        #self.set_debug(msg)
97
98        glue.ldb_set_utf8_casefold(self)
99
100        # Allow admins to force non-sync ldb for all databases
101        if lp is not None:
102            nosync_p = lp.get("nosync", "ldb")
103            if nosync_p is not None and nosync_p == true:
104                flags |= FLG_NOSYNC
105
106        if url is not None:
107            self.connect(url, flags, options)
108
109    def set_credentials(self, credentials):
110        glue.ldb_set_credentials(self, credentials)
111
112    def set_session_info(self, session_info):
113        glue.ldb_set_session_info(self, session_info)
114
115    def set_loadparm(self, lp_ctx):
116        glue.ldb_set_loadparm(self, lp_ctx)
117
118    def searchone(self, attribute, basedn=None, expression=None,
119                  scope=ldb.SCOPE_BASE):
120        """Search for one attribute as a string.
121
122        :param basedn: BaseDN for the search.
123        :param attribute: Name of the attribute
124        :param expression: Optional search expression.
125        :param scope: Search scope (defaults to base).
126        :return: Value of attribute as a string or None if it wasn't found.
127        """
128        res = self.search(basedn, scope, expression, [attribute])
129        if len(res) != 1 or res[0][attribute] is None:
130            return None
131        values = set(res[0][attribute])
132        assert len(values) == 1
133        return self.schema_format_value(attribute, values.pop())
134
135    def erase_users_computers(self, dn):
136        """Erases user and computer objects from our AD. This is needed since the 'samldb' module denies the deletion of primary groups. Therefore all groups shouldn't be primary somewhere anymore."""
137
138        try:
139            res = self.search(base=dn, scope=ldb.SCOPE_SUBTREE, attrs=[],
140                      expression="(|(objectclass=user)(objectclass=computer))")
141        except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _):
142            # Ignore no such object errors
143            return
144            pass
145
146        try:
147            for msg in res:
148                self.delete(msg.dn)
149        except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _):
150            # Ignore no such object errors
151            return
152
153    def erase_except_schema_controlled(self):
154        """Erase this ldb, removing all records, except those that are controlled by Samba4's schema."""
155
156        basedn = ""
157
158        # Try to delete user/computer accounts to allow deletion of groups
159        self.erase_users_computers(basedn)
160
161        # Delete the 'visible' records, and the invisble 'deleted' records (if this DB supports it)
162        for msg in self.search(basedn, ldb.SCOPE_SUBTREE,
163                               "(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))",
164                               [], controls=["show_deleted:0"]):
165            try:
166                self.delete(msg.dn)
167            except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _):
168                # Ignore no such object errors
169                pass
170
171        res = self.search(basedn, ldb.SCOPE_SUBTREE,
172                          "(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))",
173                          [], controls=["show_deleted:0"])
174        assert len(res) == 0
175
176        # delete the specials
177        for attr in ["@SUBCLASSES", "@MODULES",
178                     "@OPTIONS", "@PARTITION", "@KLUDGEACL"]:
179            try:
180                self.delete(attr)
181            except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _):
182                # Ignore missing dn errors
183                pass
184
185    def erase(self):
186        """Erase this ldb, removing all records."""
187
188        self.erase_except_schema_controlled()
189
190        # delete the specials
191        for attr in ["@INDEXLIST", "@ATTRIBUTES"]:
192            try:
193                self.delete(attr)
194            except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _):
195                # Ignore missing dn errors
196                pass
197
198    def erase_partitions(self):
199        """Erase an ldb, removing all records."""
200
201        def erase_recursive(self, dn):
202            try:
203                res = self.search(base=dn, scope=ldb.SCOPE_ONELEVEL, attrs=[],
204                                  controls=["show_deleted:0"])
205            except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _):
206                # Ignore no such object errors
207                return
208                pass
209
210            for msg in res:
211                erase_recursive(self, msg.dn)
212
213            try:
214                self.delete(dn)
215            except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _):
216                # Ignore no such object errors
217                pass
218
219        res = self.search("", ldb.SCOPE_BASE, "(objectClass=*)",
220                         ["namingContexts"])
221        assert len(res) == 1
222        if not "namingContexts" in res[0]:
223            return
224        for basedn in res[0]["namingContexts"]:
225            # Try to delete user/computer accounts to allow deletion of groups
226            self.erase_users_computers(basedn)
227            # Try and erase from the bottom-up in the tree
228            erase_recursive(self, basedn)
229
230    def load_ldif_file_add(self, ldif_path):
231        """Load a LDIF file.
232
233        :param ldif_path: Path to LDIF file.
234        """
235        self.add_ldif(open(ldif_path, 'r').read())
236
237    def add_ldif(self, ldif):
238        """Add data based on a LDIF string.
239
240        :param ldif: LDIF text.
241        """
242        for changetype, msg in self.parse_ldif(ldif):
243            assert changetype == ldb.CHANGETYPE_NONE
244            self.add(msg)
245
246    def modify_ldif(self, ldif):
247        """Modify database based on a LDIF string.
248
249        :param ldif: LDIF text.
250        """
251        for changetype, msg in self.parse_ldif(ldif):
252            self.modify(msg)
253
254    def set_domain_sid(self, sid):
255        """Change the domain SID used by this LDB.
256
257        :param sid: The new domain sid to use.
258        """
259        glue.samdb_set_domain_sid(self, sid)
260
261    def domain_sid(self):
262        """Read the domain SID used by this LDB.
263
264        """
265        glue.samdb_get_domain_sid(self)
266
267    def set_schema_from_ldif(self, pf, df):
268        glue.dsdb_set_schema_from_ldif(self, pf, df)
269
270    def set_schema_from_ldb(self, ldb):
271        glue.dsdb_set_schema_from_ldb(self, ldb)
272
273    def write_prefixes_from_schema(self):
274        glue.dsdb_write_prefixes_from_schema_to_ldb(self)
275
276    def convert_schema_to_openldap(self, target, mapping):
277        return glue.dsdb_convert_schema_to_openldap(self, target, mapping)
278
279    def set_invocation_id(self, invocation_id):
280        """Set the invocation id for this SamDB handle.
281
282        :param invocation_id: GUID of the invocation id.
283        """
284        glue.dsdb_set_ntds_invocation_id(self, invocation_id)
285
286    def set_opaque_integer(self, name, value):
287        """Set an integer as an opaque (a flag or other value) value on the database
288
289        :param name: The name for the opaque value
290        :param value: The integer value
291        """
292        glue.dsdb_set_opaque_integer(self, name, value)
293
294
295def substitute_var(text, values):
296    """substitute strings of the form ${NAME} in str, replacing
297    with substitutions from subobj.
298
299    :param text: Text in which to subsitute.
300    :param values: Dictionary with keys and values.
301    """
302
303    for (name, value) in values.items():
304        assert isinstance(name, str), "%r is not a string" % name
305        assert isinstance(value, str), "Value %r for %s is not a string" % (value, name)
306        text = text.replace("${%s}" % name, value)
307
308    return text
309
310
311def check_all_substituted(text):
312    """Make sure that all substitution variables in a string have been replaced.
313    If not, raise an exception.
314
315    :param text: The text to search for substitution variables
316    """
317    if not "${" in text:
318        return
319
320    var_start = text.find("${")
321    var_end = text.find("}", var_start)
322
323    raise Exception("Not all variables substituted: %s" % text[var_start:var_end+1])
324
325
326def valid_netbios_name(name):
327    """Check whether a name is valid as a NetBIOS name. """
328    # See crh's book (1.4.1.1)
329    if len(name) > 15:
330        return False
331    for x in name:
332        if not x.isalnum() and not x in " !#$%&'()-.@^_{}~":
333            return False
334    return True
335
336
337def dom_sid_to_rid(sid_str):
338    """Converts a domain SID to the relative RID.
339
340    :param sid_str: The domain SID formatted as string
341    """
342
343    return glue.dom_sid_to_rid(sid_str)
344
345
346version = glue.version
347
348# "userAccountControl" flags
349UF_NORMAL_ACCOUNT = glue.UF_NORMAL_ACCOUNT
350UF_TEMP_DUPLICATE_ACCOUNT = glue.UF_TEMP_DUPLICATE_ACCOUNT
351UF_SERVER_TRUST_ACCOUNT = glue.UF_SERVER_TRUST_ACCOUNT
352UF_WORKSTATION_TRUST_ACCOUNT = glue.UF_WORKSTATION_TRUST_ACCOUNT
353UF_INTERDOMAIN_TRUST_ACCOUNT = glue.UF_INTERDOMAIN_TRUST_ACCOUNT
354UF_PASSWD_NOTREQD = glue.UF_PASSWD_NOTREQD
355UF_ACCOUNTDISABLE = glue.UF_ACCOUNTDISABLE
356
357# "groupType" flags
358GTYPE_SECURITY_BUILTIN_LOCAL_GROUP = glue.GTYPE_SECURITY_BUILTIN_LOCAL_GROUP
359GTYPE_SECURITY_GLOBAL_GROUP = glue.GTYPE_SECURITY_GLOBAL_GROUP
360GTYPE_SECURITY_DOMAIN_LOCAL_GROUP = glue.GTYPE_SECURITY_DOMAIN_LOCAL_GROUP
361GTYPE_SECURITY_UNIVERSAL_GROUP = glue.GTYPE_SECURITY_UNIVERSAL_GROUP
362GTYPE_DISTRIBUTION_GLOBAL_GROUP = glue.GTYPE_DISTRIBUTION_GLOBAL_GROUP
363GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP = glue.GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP
364GTYPE_DISTRIBUTION_UNIVERSAL_GROUP = glue.GTYPE_DISTRIBUTION_UNIVERSAL_GROUP
365
366# "sAMAccountType" flags
367ATYPE_NORMAL_ACCOUNT = glue.ATYPE_NORMAL_ACCOUNT
368ATYPE_WORKSTATION_TRUST = glue.ATYPE_WORKSTATION_TRUST
369ATYPE_INTERDOMAIN_TRUST = glue.ATYPE_INTERDOMAIN_TRUST
370ATYPE_SECURITY_GLOBAL_GROUP = glue.ATYPE_SECURITY_GLOBAL_GROUP
371ATYPE_SECURITY_LOCAL_GROUP = glue.ATYPE_SECURITY_LOCAL_GROUP
372ATYPE_SECURITY_UNIVERSAL_GROUP = glue.ATYPE_SECURITY_UNIVERSAL_GROUP
373ATYPE_DISTRIBUTION_GLOBAL_GROUP = glue.ATYPE_DISTRIBUTION_GLOBAL_GROUP
374ATYPE_DISTRIBUTION_LOCAL_GROUP = glue.ATYPE_DISTRIBUTION_LOCAL_GROUP
375ATYPE_DISTRIBUTION_UNIVERSAL_GROUP = glue.ATYPE_DISTRIBUTION_UNIVERSAL_GROUP
376
377# "domainFunctionality", "forestFunctionality" flags in the rootDSE */
378DS_DOMAIN_FUNCTION_2000 = glue.DS_DOMAIN_FUNCTION_2000
379DS_DOMAIN_FUNCTION_2003_MIXED = glue.DS_DOMAIN_FUNCTION_2003_MIXED
380DS_DOMAIN_FUNCTION_2003 = glue.DS_DOMAIN_FUNCTION_2003
381DS_DOMAIN_FUNCTION_2008 = glue.DS_DOMAIN_FUNCTION_2008
382DS_DOMAIN_FUNCTION_2008_R2 = glue.DS_DOMAIN_FUNCTION_2008_R2
383
384# "domainControllerFunctionality" flags in the rootDSE */
385DS_DC_FUNCTION_2000 = glue.DS_DC_FUNCTION_2000
386DS_DC_FUNCTION_2003 = glue.DS_DC_FUNCTION_2003
387DS_DC_FUNCTION_2008 = glue.DS_DC_FUNCTION_2008
388DS_DC_FUNCTION_2008_R2 = glue.DS_DC_FUNCTION_2008_R2
389
390