2007-11-21 15:07:16 +03:00
#!/usr/bin/python
# Unix SMB/CIFS implementation.
2008-01-25 03:02:13 +03:00
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
#
# Based on the original in EJS:
2007-11-21 15:07:16 +03:00
# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
#
# 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/>.
#
2008-05-24 06:01:57 +04:00
""" Samba 4. """
2008-05-22 19:42:18 +04:00
__docformat__ = " restructuredText "
2007-11-21 15:07:16 +03:00
import os
2007-12-16 17:33:58 +03:00
def _in_source_tree ( ) :
2007-12-16 19:17:37 +03:00
""" Check whether the script is being run from the source dir. """
2009-04-23 03:21:47 +04:00
return os . path . exists ( " %s /../../../selftest/skip " % os . path . dirname ( __file__ ) )
2007-12-16 17:33:58 +03:00
2007-12-16 19:17:37 +03:00
2007-12-16 17:33:58 +03:00
# When running, in-tree, make sure bin/python is in the PYTHONPATH
if _in_source_tree ( ) :
import sys
2007-12-16 19:17:37 +03:00
srcdir = " %s /../../.. " % os . path . dirname ( __file__ )
sys . path . append ( " %s /bin/python " % srcdir )
default_ldb_modules_dir = " %s /bin/modules/ldb " % srcdir
2008-05-22 03:50:38 +04:00
else :
default_ldb_modules_dir = None
2007-12-16 19:17:37 +03:00
2007-12-16 17:33:58 +03:00
2007-12-16 17:50:02 +03:00
import ldb
2008-12-21 05:08:14 +03:00
import glue
2007-12-17 10:20:20 +03:00
class Ldb ( ldb . Ldb ) :
""" Simple Samba-specific LDB subclass that takes care
of setting up the modules dir , credentials pointers , etc .
Please note that this is intended to be for all Samba LDB files ,
not necessarily the Sam database . For Sam - specific helper
functions see samdb . py .
2007-11-21 15:07:16 +03:00
"""
2009-08-15 17:20:09 +04:00
def __init__ ( self , url = None , lp = None , modules_dir = None , session_info = None ,
credentials = None , flags = 0 , options = None ) :
""" Opens a Samba Ldb file.
2007-12-17 10:20:20 +03:00
2007-12-17 13:12:36 +03:00
: param url : Optional LDB URL to open
2009-08-15 17:20:09 +04:00
: param lp : Optional loadparm object
: param modules_dir : Optional modules directory
2007-12-17 10:20:20 +03:00
: param session_info : Optional session information
: param credentials : Optional credentials , defaults to anonymous .
2009-08-15 17:20:09 +04:00
: param flags : Optional LDB flags
: param options : Additional options ( optional )
2007-12-17 10:20:20 +03:00
This is different from a regular Ldb file in that the Samba - specific
modules - dir is used by default and that credentials and session_info
can be passed through ( required by some modules ) .
"""
2007-12-17 13:12:36 +03:00
2007-12-17 10:20:20 +03:00
if modules_dir is not None :
2007-12-17 13:12:36 +03:00
self . set_modules_dir ( modules_dir )
elif default_ldb_modules_dir is not None :
self . set_modules_dir ( default_ldb_modules_dir )
2009-07-18 20:39:20 +04:00
elif lp is not None :
self . set_modules_dir ( os . path . join ( lp . get ( " modules dir " ) , " ldb " ) )
2007-12-17 13:12:36 +03:00
2007-12-17 10:20:20 +03:00
if session_info is not None :
2008-05-23 05:20:37 +04:00
self . set_session_info ( session_info )
2007-12-17 13:12:36 +03:00
2009-08-15 17:20:09 +04:00
if credentials is not None :
self . set_credentials ( credentials )
2007-12-20 17:53:56 +03:00
2007-12-17 10:20:20 +03:00
if lp is not None :
2008-05-23 05:20:37 +04:00
self . set_loadparm ( lp )
2007-12-17 13:12:36 +03:00
2009-08-15 17:20:09 +04:00
# This must be done before we load the schema, as these handlers for
# objectSid and objectGUID etc must take precedence over the 'binary
# attribute' declaration in the schema
glue . ldb_register_samba_handlers ( self )
# TODO set debug
2007-12-18 04:21:14 +03:00
def msg ( l , text ) :
print text
2007-12-18 19:21:13 +03:00
#self.set_debug(msg)
2007-12-18 04:21:14 +03:00
2009-08-15 17:20:09 +04:00
glue . ldb_set_utf8_casefold ( self )
# Allow admins to force non-sync ldb for all databases
2009-08-17 15:40:19 +04:00
if lp is not None :
nosync_p = lp . get ( " nosync " , " ldb " )
if nosync_p is not None and nosync_p == true :
2009-08-15 17:20:09 +04:00
flags | = FLG_NOSYNC
2009-08-17 15:40:19 +04:00
if url is not None :
self . connect ( url , flags , options )
2007-12-18 04:21:28 +03:00
2008-12-21 05:08:14 +03:00
def set_credentials ( self , credentials ) :
glue . ldb_set_credentials ( self , credentials )
2007-12-18 04:21:28 +03:00
2008-12-21 05:08:14 +03:00
def set_session_info ( self , session_info ) :
glue . ldb_set_session_info ( self , session_info )
def set_loadparm ( self , lp_ctx ) :
glue . ldb_set_loadparm ( self , lp_ctx )
2007-12-17 10:20:20 +03:00
2008-01-25 05:54:33 +03:00
def searchone ( self , attribute , basedn = None , expression = None ,
2007-12-20 01:27:38 +03:00
scope = ldb . SCOPE_BASE ) :
2007-12-24 04:19:41 +03:00
""" 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.
"""
2007-12-18 19:21:13 +03:00
res = self . search ( basedn , scope , expression , [ attribute ] )
2007-12-17 10:20:20 +03:00
if len ( res ) != 1 or res [ 0 ] [ attribute ] is None :
return None
2007-12-20 01:27:31 +03:00
values = set ( res [ 0 ] [ attribute ] )
assert len ( values ) == 1
2008-01-25 05:54:33 +03:00
return self . schema_format_value ( attribute , values . pop ( ) )
2007-12-17 10:20:20 +03:00
2009-09-06 23:08:08 +04:00
def erase_users_computers ( self , dn ) :
""" 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. """
try :
res = self . search ( base = dn , scope = ldb . SCOPE_SUBTREE , attrs = [ ] ,
expression = " (|(objectclass=user)(objectclass=computer)) " )
except ldb . LdbError , ( ldb . ERR_NO_SUCH_OBJECT , _ ) :
# Ignore no such object errors
return
pass
try :
for msg in res :
self . delete ( msg . dn )
except ldb . LdbError , ( ldb . ERR_NO_SUCH_OBJECT , _ ) :
# Ignore no such object errors
return
2009-08-17 05:33:25 +04:00
def erase_except_schema_controlled ( self ) :
""" Erase this ldb, removing all records, except those that are controlled by Samba4 ' s schema. """
2009-09-06 23:08:08 +04:00
2008-01-11 18:13:46 +03:00
basedn = " "
2009-09-06 23:08:08 +04:00
# Try to delete user/computer accounts to allow deletion of groups
self . erase_users_computers ( basedn )
2009-08-25 10:27:20 +04:00
# Delete the 'visible' records, and the invisble 'deleted' records (if this DB supports it)
2007-12-17 13:12:36 +03:00
for msg in self . search ( basedn , ldb . SCOPE_SUBTREE ,
2009-08-25 10:27:20 +04:00
" (&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO))) " ,
[ ] , controls = [ " show_deleted:0 " ] ) :
2007-12-20 01:27:38 +03:00
try :
self . delete ( msg . dn )
2009-07-02 08:52:25 +04:00
except ldb . LdbError , ( ldb . ERR_NO_SUCH_OBJECT , _ ) :
2008-03-03 05:03:19 +03:00
# Ignore no such object errors
2007-12-20 01:27:38 +03:00
pass
2009-08-25 10:27:20 +04:00
res = self . search ( basedn , ldb . SCOPE_SUBTREE ,
" (&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO))) " ,
[ ] , controls = [ " show_deleted:0 " ] )
2007-12-17 10:20:20 +03:00
assert len ( res ) == 0
2007-11-21 15:07:16 +03:00
2009-08-13 08:37:06 +04:00
# delete the specials
2009-08-17 05:33:25 +04:00
for attr in [ " @SUBCLASSES " , " @MODULES " ,
2009-08-13 08:37:06 +04:00
" @OPTIONS " , " @PARTITION " , " @KLUDGEACL " ] :
try :
self . delete ( attr )
except ldb . LdbError , ( ldb . ERR_NO_SUCH_OBJECT , _ ) :
# Ignore missing dn errors
pass
2009-08-17 05:33:25 +04:00
def erase ( self ) :
""" Erase this ldb, removing all records. """
self . erase_except_schema_controlled ( )
# delete the specials
for attr in [ " @INDEXLIST " , " @ATTRIBUTES " ] :
try :
self . delete ( attr )
except ldb . LdbError , ( ldb . ERR_NO_SUCH_OBJECT , _ ) :
# Ignore missing dn errors
pass
2007-12-20 01:27:38 +03:00
def erase_partitions ( self ) :
""" Erase an ldb, removing all records. """
2009-08-13 08:37:06 +04:00
def erase_recursive ( self , dn ) :
try :
2009-08-25 10:27:20 +04:00
res = self . search ( base = dn , scope = ldb . SCOPE_ONELEVEL , attrs = [ ] ,
controls = [ " show_deleted:0 " ] )
2009-08-13 08:37:06 +04:00
except ldb . LdbError , ( ldb . ERR_NO_SUCH_OBJECT , _ ) :
# Ignore no such object errors
return
pass
for msg in res :
erase_recursive ( self , msg . dn )
2009-08-14 02:46:13 +04:00
try :
self . delete ( dn )
except ldb . LdbError , ( ldb . ERR_NO_SUCH_OBJECT , _ ) :
# Ignore no such object errors
pass
2009-08-13 08:37:06 +04:00
2008-01-11 18:13:46 +03:00
res = self . search ( " " , ldb . SCOPE_BASE , " (objectClass=*) " ,
2007-12-20 01:27:38 +03:00
[ " namingContexts " ] )
assert len ( res ) == 1
if not " namingContexts " in res [ 0 ] :
return
for basedn in res [ 0 ] [ " namingContexts " ] :
2009-09-06 23:08:08 +04:00
# Try to delete user/computer accounts to allow deletion of groups
self . erase_users_computers ( basedn )
2009-08-13 08:37:06 +04:00
# Try and erase from the bottom-up in the tree
erase_recursive ( self , basedn )
2007-12-20 01:27:38 +03:00
def load_ldif_file_add ( self , ldif_path ) :
""" Load a LDIF file.
: param ldif_path : Path to LDIF file .
"""
2007-12-24 04:19:41 +03:00
self . add_ldif ( open ( ldif_path , ' r ' ) . read ( ) )
2007-12-20 01:27:38 +03:00
2009-09-23 01:49:22 +04:00
def add_ldif ( self , ldif , controls = None ) :
2007-12-30 03:14:15 +03:00
""" Add data based on a LDIF string.
: param ldif : LDIF text .
"""
2007-12-20 01:27:38 +03:00
for changetype , msg in self . parse_ldif ( ldif ) :
assert changetype == ldb . CHANGETYPE_NONE
2009-09-23 01:49:22 +04:00
self . add ( msg , controls )
2007-12-20 01:27:38 +03:00
2007-12-24 04:19:41 +03:00
def modify_ldif ( self , ldif ) :
2007-12-30 03:14:15 +03:00
""" Modify database based on a LDIF string.
: param ldif : LDIF text .
"""
2008-01-02 10:52:31 +03:00
for changetype , msg in self . parse_ldif ( ldif ) :
2007-12-26 01:36:44 +03:00
self . modify ( msg )
2007-12-24 04:19:41 +03:00
2009-08-13 08:37:06 +04:00
def set_domain_sid ( self , sid ) :
""" Change the domain SID used by this LDB.
: param sid : The new domain sid to use .
"""
glue . samdb_set_domain_sid ( self , sid )
2009-08-27 13:38:04 +04:00
def domain_sid ( self ) :
""" Read the domain SID used by this LDB.
"""
glue . samdb_get_domain_sid ( self )
2009-08-13 08:37:06 +04:00
def set_schema_from_ldif ( self , pf , df ) :
glue . dsdb_set_schema_from_ldif ( self , pf , df )
def set_schema_from_ldb ( self , ldb ) :
glue . dsdb_set_schema_from_ldb ( self , ldb )
2009-08-26 07:43:33 +04:00
def write_prefixes_from_schema ( self ) :
glue . dsdb_write_prefixes_from_schema_to_ldb ( self )
2009-08-13 08:37:06 +04:00
def convert_schema_to_openldap ( self , target , mapping ) :
return glue . dsdb_convert_schema_to_openldap ( self , target , mapping )
def set_invocation_id ( self , invocation_id ) :
""" Set the invocation id for this SamDB handle.
: param invocation_id : GUID of the invocation id .
"""
glue . dsdb_set_ntds_invocation_id ( self , invocation_id )
def set_opaque_integer ( self , name , value ) :
""" Set an integer as an opaque (a flag or other value) value on the database
: param name : The name for the opaque value
: param value : The integer value
"""
glue . dsdb_set_opaque_integer ( self , name , value )
2007-11-21 15:07:16 +03:00
def substitute_var ( text , values ) :
""" substitute strings of the form $ {NAME} in str, replacing
with substitutions from subobj .
: param text : Text in which to subsitute .
: param values : Dictionary with keys and values .
"""
for ( name , value ) in values . items ( ) :
2007-12-18 19:21:13 +03:00
assert isinstance ( name , str ) , " %r is not a string " % name
assert isinstance ( value , str ) , " Value %r for %s is not a string " % ( value , name )
2007-11-21 15:07:16 +03:00
text = text . replace ( " $ { %s } " % name , value )
return text
2007-12-10 12:29:45 +03:00
2008-01-25 00:18:27 +03:00
def check_all_substituted ( text ) :
""" Make sure that all substitution variables in a string have been replaced.
If not , raise an exception .
: param text : The text to search for substitution variables
"""
if not " $ { " in text :
2009-02-24 03:02:26 +03:00
return
2008-01-25 00:18:27 +03:00
var_start = text . find ( " $ { " )
var_end = text . find ( " } " , var_start )
2008-01-25 03:02:13 +03:00
raise Exception ( " Not all variables substituted: %s " % text [ var_start : var_end + 1 ] )
2008-01-25 00:18:27 +03:00
2009-10-30 06:31:25 +03:00
def read_and_sub_file ( file , subst_vars ) :
""" Read a file and sub in variables found in it
: param file : File to be read ( typically from setup directory )
param subst_vars : Optional variables to subsitute in the file .
"""
data = open ( file , ' r ' ) . read ( )
if subst_vars is not None :
data = substitute_var ( data , subst_vars )
check_all_substituted ( data )
return data
2009-10-30 07:18:42 +03:00
def setup_file ( template , fname , subst_vars = None ) :
""" Setup a file in the private dir.
: param template : Path of the template file .
: param fname : Path of the file to create .
: param subst_vars : Substitution variables .
"""
f = fname
if os . path . exists ( f ) :
os . unlink ( f )
data = read_and_sub_file ( template , subst_vars )
open ( f , ' w ' ) . write ( data )
2007-12-10 12:29:45 +03:00
def valid_netbios_name ( name ) :
""" Check whether a name is valid as a NetBIOS name. """
2009-04-06 01:17:43 +04:00
# See crh's book (1.4.1.1)
2008-03-28 00:30:18 +03:00
if len ( name ) > 15 :
2007-12-10 12:29:45 +03:00
return False
2009-04-06 01:17:43 +04:00
for x in name :
if not x . isalnum ( ) and not x in " !#$ % & ' ()-.@^_ {} ~ " :
return False
2007-12-10 12:29:45 +03:00
return True
2009-08-13 13:37:38 +04:00
def dom_sid_to_rid ( sid_str ) :
""" Converts a domain SID to the relative RID.
: param sid_str : The domain SID formatted as string
"""
return glue . dom_sid_to_rid ( sid_str )
2008-12-21 05:08:14 +03:00
version = glue . version
2009-07-14 02:15:50 +04:00
2009-09-09 23:24:34 +04:00
# "userAccountControl" flags
UF_NORMAL_ACCOUNT = glue . UF_NORMAL_ACCOUNT
UF_TEMP_DUPLICATE_ACCOUNT = glue . UF_TEMP_DUPLICATE_ACCOUNT
UF_SERVER_TRUST_ACCOUNT = glue . UF_SERVER_TRUST_ACCOUNT
UF_WORKSTATION_TRUST_ACCOUNT = glue . UF_WORKSTATION_TRUST_ACCOUNT
UF_INTERDOMAIN_TRUST_ACCOUNT = glue . UF_INTERDOMAIN_TRUST_ACCOUNT
UF_PASSWD_NOTREQD = glue . UF_PASSWD_NOTREQD
UF_ACCOUNTDISABLE = glue . UF_ACCOUNTDISABLE
# "groupType" flags
GTYPE_SECURITY_BUILTIN_LOCAL_GROUP = glue . GTYPE_SECURITY_BUILTIN_LOCAL_GROUP
GTYPE_SECURITY_GLOBAL_GROUP = glue . GTYPE_SECURITY_GLOBAL_GROUP
GTYPE_SECURITY_DOMAIN_LOCAL_GROUP = glue . GTYPE_SECURITY_DOMAIN_LOCAL_GROUP
GTYPE_SECURITY_UNIVERSAL_GROUP = glue . GTYPE_SECURITY_UNIVERSAL_GROUP
GTYPE_DISTRIBUTION_GLOBAL_GROUP = glue . GTYPE_DISTRIBUTION_GLOBAL_GROUP
GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP = glue . GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP
GTYPE_DISTRIBUTION_UNIVERSAL_GROUP = glue . GTYPE_DISTRIBUTION_UNIVERSAL_GROUP
# "sAMAccountType" flags
ATYPE_NORMAL_ACCOUNT = glue . ATYPE_NORMAL_ACCOUNT
ATYPE_WORKSTATION_TRUST = glue . ATYPE_WORKSTATION_TRUST
ATYPE_INTERDOMAIN_TRUST = glue . ATYPE_INTERDOMAIN_TRUST
ATYPE_SECURITY_GLOBAL_GROUP = glue . ATYPE_SECURITY_GLOBAL_GROUP
ATYPE_SECURITY_LOCAL_GROUP = glue . ATYPE_SECURITY_LOCAL_GROUP
ATYPE_SECURITY_UNIVERSAL_GROUP = glue . ATYPE_SECURITY_UNIVERSAL_GROUP
ATYPE_DISTRIBUTION_GLOBAL_GROUP = glue . ATYPE_DISTRIBUTION_GLOBAL_GROUP
ATYPE_DISTRIBUTION_LOCAL_GROUP = glue . ATYPE_DISTRIBUTION_LOCAL_GROUP
ATYPE_DISTRIBUTION_UNIVERSAL_GROUP = glue . ATYPE_DISTRIBUTION_UNIVERSAL_GROUP
# "domainFunctionality", "forestFunctionality" flags in the rootDSE */
DS_DOMAIN_FUNCTION_2000 = glue . DS_DOMAIN_FUNCTION_2000
DS_DOMAIN_FUNCTION_2003_MIXED = glue . DS_DOMAIN_FUNCTION_2003_MIXED
DS_DOMAIN_FUNCTION_2003 = glue . DS_DOMAIN_FUNCTION_2003
DS_DOMAIN_FUNCTION_2008 = glue . DS_DOMAIN_FUNCTION_2008
DS_DOMAIN_FUNCTION_2008_R2 = glue . DS_DOMAIN_FUNCTION_2008_R2
# "domainControllerFunctionality" flags in the rootDSE */
DS_DC_FUNCTION_2000 = glue . DS_DC_FUNCTION_2000
DS_DC_FUNCTION_2003 = glue . DS_DC_FUNCTION_2003
DS_DC_FUNCTION_2008 = glue . DS_DC_FUNCTION_2008
DS_DC_FUNCTION_2008_R2 = glue . DS_DC_FUNCTION_2008_R2