gpo: Create base class gp_inf_ext
[nivanova/samba-autobuild/.git] / python / samba / __init__.py
1 # Unix SMB/CIFS implementation.
2 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
3 #
4 # Based on the original in EJS:
5 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20
21 """Samba 4."""
22
23 __docformat__ = "restructuredText"
24
25 import os
26 import sys
27 import time
28 import ldb
29 import samba.param
30 from samba import _glue
31 from samba._ldb import Ldb as _Ldb
32
33
34 def source_tree_topdir():
35     """Return the top level source directory."""
36     paths = ["../../..", "../../../.."]
37     for p in paths:
38         topdir = os.path.normpath(os.path.join(os.path.dirname(__file__), p))
39         if os.path.exists(os.path.join(topdir, 'source4')):
40             return topdir
41     raise RuntimeError("unable to find top level source directory")
42
43
44 def in_source_tree():
45     """Return True if we are running from within the samba source tree"""
46     try:
47         topdir = source_tree_topdir()
48     except RuntimeError:
49         return False
50     return True
51
52
53 class Ldb(_Ldb):
54     """Simple Samba-specific LDB subclass that takes care
55     of setting up the modules dir, credentials pointers, etc.
56
57     Please note that this is intended to be for all Samba LDB files,
58     not necessarily the Sam database. For Sam-specific helper
59     functions see samdb.py.
60     """
61
62     def __init__(self, url=None, lp=None, modules_dir=None, session_info=None,
63                  credentials=None, flags=0, options=None):
64         """Opens a Samba Ldb file.
65
66         :param url: Optional LDB URL to open
67         :param lp: Optional loadparm object
68         :param modules_dir: Optional modules directory
69         :param session_info: Optional session information
70         :param credentials: Optional credentials, defaults to anonymous.
71         :param flags: Optional LDB flags
72         :param options: Additional options (optional)
73
74         This is different from a regular Ldb file in that the Samba-specific
75         modules-dir is used by default and that credentials and session_info
76         can be passed through (required by some modules).
77         """
78
79         if modules_dir is not None:
80             self.set_modules_dir(modules_dir)
81         else:
82             self.set_modules_dir(os.path.join(samba.param.modules_dir(), "ldb"))
83
84         if session_info is not None:
85             self.set_session_info(session_info)
86
87         if credentials is not None:
88             self.set_credentials(credentials)
89
90         if lp is not None:
91             self.set_loadparm(lp)
92
93         # This must be done before we load the schema, as these handlers for
94         # objectSid and objectGUID etc must take precedence over the 'binary
95         # attribute' declaration in the schema
96         self.register_samba_handlers()
97
98         # TODO set debug
99         def msg(l, text):
100             print(text)
101         #self.set_debug(msg)
102
103         self.set_utf8_casefold()
104
105         # Allow admins to force non-sync ldb for all databases
106         if lp is not None:
107             nosync_p = lp.get("nosync", "ldb")
108             if nosync_p is not None and nosync_p:
109                 flags |= ldb.FLG_NOSYNC
110
111         self.set_create_perms(0o600)
112
113         if url is not None:
114             self.connect(url, flags, options)
115
116     def searchone(self, attribute, basedn=None, expression=None,
117                   scope=ldb.SCOPE_BASE):
118         """Search for one attribute as a string.
119
120         :param basedn: BaseDN for the search.
121         :param attribute: Name of the attribute
122         :param expression: Optional search expression.
123         :param scope: Search scope (defaults to base).
124         :return: Value of attribute as a string or None if it wasn't found.
125         """
126         res = self.search(basedn, scope, expression, [attribute])
127         if len(res) != 1 or res[0][attribute] is None:
128             return None
129         values = set(res[0][attribute])
130         assert len(values) == 1
131         return self.schema_format_value(attribute, values.pop())
132
133     def erase_users_computers(self, dn):
134         """Erases user and computer objects from our AD.
135
136         This is needed since the 'samldb' module denies the deletion of primary
137         groups. Therefore all groups shouldn't be primary somewhere anymore.
138         """
139
140         try:
141             res = self.search(base=dn, scope=ldb.SCOPE_SUBTREE, attrs=[],
142                       expression="(|(objectclass=user)(objectclass=computer))")
143         except ldb.LdbError as error:
144             (errno, estr) = error.args
145             if errno == ldb.ERR_NO_SUCH_OBJECT:
146                 # Ignore no such object errors
147                 return
148             else:
149                 raise
150
151         try:
152             for msg in res:
153                 self.delete(msg.dn, ["relax:0"])
154         except ldb.LdbError as error:
155             (errno, estr) = error.args
156             if errno != ldb.ERR_NO_SUCH_OBJECT:
157                 # Ignore no such object errors
158                 raise
159
160     def erase_except_schema_controlled(self):
161         """Erase this ldb.
162
163         :note: Removes all records, except those that are controlled by
164             Samba4's schema.
165         """
166
167         basedn = ""
168
169         # Try to delete user/computer accounts to allow deletion of groups
170         self.erase_users_computers(basedn)
171
172         # Delete the 'visible' records, and the invisble 'deleted' records (if
173         # this DB supports it)
174         for msg in self.search(basedn, ldb.SCOPE_SUBTREE,
175                        "(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))",
176                        [], controls=["show_deleted:0", "show_recycled:0"]):
177             try:
178                 self.delete(msg.dn, ["relax:0"])
179             except ldb.LdbError as error:
180                 (errno, estr) = error.args
181                 if errno != ldb.ERR_NO_SUCH_OBJECT:
182                     # Ignore no such object errors
183                     raise
184
185         res = self.search(basedn, ldb.SCOPE_SUBTREE,
186             "(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))",
187             [], controls=["show_deleted:0", "show_recycled:0"])
188         assert len(res) == 0
189
190         # delete the specials
191         for attr in ["@SUBCLASSES", "@MODULES",
192                      "@OPTIONS", "@PARTITION", "@KLUDGEACL"]:
193             try:
194                 self.delete(attr, ["relax:0"])
195             except ldb.LdbError as error:
196                 (errno, estr) = error.args
197                 if errno != ldb.ERR_NO_SUCH_OBJECT:
198                     # Ignore missing dn errors
199                     raise
200
201     def erase(self):
202         """Erase this ldb, removing all records."""
203         self.erase_except_schema_controlled()
204
205         # delete the specials
206         for attr in ["@INDEXLIST", "@ATTRIBUTES"]:
207             try:
208                 self.delete(attr, ["relax:0"])
209             except ldb.LdbError as error:
210                 (errno, estr) = error.args
211                 if errno != ldb.ERR_NO_SUCH_OBJECT:
212                     # Ignore missing dn errors
213                     raise
214
215     def load_ldif_file_add(self, ldif_path):
216         """Load a LDIF file.
217
218         :param ldif_path: Path to LDIF file.
219         """
220         self.add_ldif(open(ldif_path, 'r').read())
221
222     def add_ldif(self, ldif, controls=None):
223         """Add data based on a LDIF string.
224
225         :param ldif: LDIF text.
226         """
227         for changetype, msg in self.parse_ldif(ldif):
228             assert changetype == ldb.CHANGETYPE_NONE
229             self.add(msg, controls)
230
231     def modify_ldif(self, ldif, controls=None):
232         """Modify database based on a LDIF string.
233
234         :param ldif: LDIF text.
235         """
236         for changetype, msg in self.parse_ldif(ldif):
237             if changetype == ldb.CHANGETYPE_ADD:
238                 self.add(msg, controls)
239             else:
240                 self.modify(msg, controls)
241
242
243 def substitute_var(text, values):
244     """Substitute strings of the form ${NAME} in str, replacing
245     with substitutions from values.
246
247     :param text: Text in which to subsitute.
248     :param values: Dictionary with keys and values.
249     """
250
251     for (name, value) in values.items():
252         assert isinstance(name, str), "%r is not a string" % name
253         assert isinstance(value, str), "Value %r for %s is not a string" % (value, name)
254         text = text.replace("${%s}" % name, value)
255
256     return text
257
258
259 def check_all_substituted(text):
260     """Check that all substitution variables in a string have been replaced.
261
262     If not, raise an exception.
263
264     :param text: The text to search for substitution variables
265     """
266     if not "${" in text:
267         return
268
269     var_start = text.find("${")
270     var_end = text.find("}", var_start)
271
272     raise Exception("Not all variables substituted: %s" %
273         text[var_start:var_end+1])
274
275
276 def read_and_sub_file(file_name, subst_vars):
277     """Read a file and sub in variables found in it
278
279     :param file_name: File to be read (typically from setup directory)
280      param subst_vars: Optional variables to subsitute in the file.
281     """
282     data = open(file_name, 'r').read()
283     if subst_vars is not None:
284         data = substitute_var(data, subst_vars)
285         check_all_substituted(data)
286     return data
287
288
289 def setup_file(template, fname, subst_vars=None):
290     """Setup a file in the private dir.
291
292     :param template: Path of the template file.
293     :param fname: Path of the file to create.
294     :param subst_vars: Substitution variables.
295     """
296     if os.path.exists(fname):
297         os.unlink(fname)
298
299     data = read_and_sub_file(template, subst_vars)
300     f = open(fname, 'w')
301     try:
302         f.write(data)
303     finally:
304         f.close()
305
306 MAX_NETBIOS_NAME_LEN = 15
307 def is_valid_netbios_char(c):
308     return (c.isalnum() or c in " !#$%&'()-.@^_{}~")
309
310
311 def valid_netbios_name(name):
312     """Check whether a name is valid as a NetBIOS name. """
313     # See crh's book (1.4.1.1)
314     if len(name) > MAX_NETBIOS_NAME_LEN:
315         return False
316     for x in name:
317         if not is_valid_netbios_char(x):
318             return False
319     return True
320
321
322 def import_bundled_package(modulename, location, source_tree_container,
323                            namespace):
324     """Import the bundled version of a package.
325
326     :note: This should only be called if the system version of the package
327         is not adequate.
328
329     :param modulename: Module name to import
330     :param location: Location to add to sys.path (can be relative to
331         ${srcdir}/${source_tree_container})
332     :param source_tree_container: Directory under source root that
333         contains the bundled third party modules.
334     :param namespace: Namespace to import module from, when not in source tree
335     """
336     if in_source_tree():
337         extra_path = os.path.join(source_tree_topdir(), source_tree_container,
338             location)
339         if not extra_path in sys.path:
340             sys.path.insert(0, extra_path)
341         sys.modules[modulename] = __import__(modulename)
342     else:
343         sys.modules[modulename] = __import__(
344             "%s.%s" % (namespace, modulename), fromlist=[namespace])
345
346
347 def ensure_third_party_module(modulename, location):
348     """Add a location to sys.path if a third party dependency can't be found.
349
350     :param modulename: Module name to import
351     :param location: Location to add to sys.path (can be relative to
352         ${srcdir}/third_party)
353     """
354     try:
355         __import__(modulename)
356     except ImportError:
357         import_bundled_package(modulename, location,
358             source_tree_container="third_party",
359             namespace="samba.third_party")
360
361
362 def dn_from_dns_name(dnsdomain):
363     """return a DN from a DNS name domain/forest root"""
364     return "DC=" + ",DC=".join(dnsdomain.split("."))
365
366 def current_unix_time():
367     return int(time.time())
368
369 def string_to_byte_array(string):
370     blob = [0] * len(string)
371
372     for i in range(len(string)):
373         blob[i] = ord(string[i])
374
375     return blob
376
377 def arcfour_encrypt(key, data):
378     from samba.crypto import arcfour_crypt_blob
379     return arcfour_crypt_blob(data, key)
380
381 version = _glue.version
382 interface_ips = _glue.interface_ips
383 set_debug_level = _glue.set_debug_level
384 get_debug_level = _glue.get_debug_level
385 unix2nttime = _glue.unix2nttime
386 nttime2string = _glue.nttime2string
387 nttime2unix = _glue.nttime2unix
388 unix2nttime = _glue.unix2nttime
389 generate_random_password = _glue.generate_random_password
390 generate_random_machine_password = _glue.generate_random_machine_password
391 check_password_quality = _glue.check_password_quality
392 generate_random_bytes = _glue.generate_random_bytes
393 strcasecmp_m = _glue.strcasecmp_m
394 strstr_m = _glue.strstr_m
395 is_ntvfs_fileserver_built = _glue.is_ntvfs_fileserver_built
396 is_heimdal_built = _glue.is_heimdal_built
397
398 NTSTATUSError = _glue.NTSTATUSError
399 HRESULTError = _glue.HRESULTError
400 WERRORError = _glue.WERRORError
401 DsExtendedError = _glue.DsExtendedError