2007-11-21 15:07:16 +03:00
# Unix SMB/CIFS implementation.
2008-01-25 03:02:13 +03:00
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
2009-12-30 23:48:42 +03:00
#
2008-01-25 03:02:13 +03:00
# Based on the original in EJS:
2007-11-21 15:07:16 +03:00
# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
2009-12-30 23:48:42 +03:00
#
2007-11-21 15:07:16 +03:00
# 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.
2009-12-30 23:48:42 +03:00
#
2007-11-21 15:07:16 +03:00
# 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.
2009-12-30 23:48:42 +03:00
#
2007-11-21 15:07:16 +03:00
# 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
2010-03-31 00:06:45 +04:00
import sys
2015-01-22 14:23:09 +03:00
import time
2016-12-13 13:26:53 +03:00
import ldb
2011-06-06 08:39:19 +04:00
import samba . param
2016-12-13 13:26:53 +03:00
from samba import _glue
2017-01-02 10:51:19 +03:00
from samba . _ldb import Ldb as _Ldb
2007-11-21 15:07:16 +03:00
2012-02-19 02:59:48 +04:00
2011-02-03 05:49:29 +03:00
def source_tree_topdir ( ) :
2011-09-13 03:27:50 +04:00
""" Return the top level source directory. """
2012-02-19 02:59:48 +04:00
paths = [ " ../../.. " , " ../../../.. " ]
2011-02-03 05:49:29 +03:00
for p in paths :
topdir = os . path . normpath ( os . path . join ( os . path . dirname ( __file__ ) , p ) )
if os . path . exists ( os . path . join ( topdir , ' source4 ' ) ) :
return topdir
raise RuntimeError ( " unable to find top level source directory " )
2007-12-16 17:33:58 +03:00
2012-02-19 02:59:48 +04:00
2011-02-03 05:49:29 +03:00
def in_source_tree ( ) :
2011-09-13 03:27:50 +04:00
""" Return True if we are running from within the samba source tree """
2011-02-03 05:49:29 +03:00
try :
topdir = source_tree_topdir ( )
except RuntimeError :
return False
return True
2007-12-16 19:17:37 +03:00
2010-04-04 04:01:47 +04:00
class Ldb ( _Ldb ) :
2009-12-30 23:48:42 +03:00
""" Simple Samba-specific LDB subclass that takes care
2007-12-17 10:20:20 +03:00
of setting up the modules dir , credentials pointers , etc .
2009-12-30 23:48:42 +03:00
Please note that this is intended to be for all Samba LDB files ,
not necessarily the Sam database . For Sam - specific helper
2007-12-17 10:20:20 +03:00
functions see samdb . py .
2007-11-21 15:07:16 +03:00
"""
2010-09-22 08:33:30 +04: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
2009-12-30 23:48:42 +03:00
modules - dir is used by default and that credentials and session_info
2007-12-17 10:20:20 +03:00
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 )
2011-06-06 08:39:19 +04:00
else :
self . set_modules_dir ( os . path . join ( samba . param . 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
2010-04-04 04:07:46 +04:00
self . register_samba_handlers ( )
2009-08-15 17:20:09 +04:00
# TODO set debug
2010-09-29 02:09:09 +04:00
def msg ( l , text ) :
2016-12-13 13:26:53 +03:00
print ( text )
2018-07-30 09:19:49 +03:00
# self.set_debug(msg)
2007-12-18 04:21:14 +03:00
2010-04-04 04:01:47 +04:00
self . set_utf8_casefold ( )
2009-08-15 17:20:09 +04:00
# Allow admins to force non-sync ldb for all databases
2009-08-17 15:40:19 +04:00
if lp is not None :
2018-06-03 09:35:15 +03:00
nosync_p = lp . get ( " ldb:nosync " )
2012-09-27 20:30:47 +04:00
if nosync_p is not None and nosync_p :
2010-04-08 22:28:11 +04:00
flags | = ldb . FLG_NOSYNC
2009-08-15 17:20:09 +04:00
2016-12-13 13:26:53 +03:00
self . set_create_perms ( 0o600 )
2009-10-27 21:52:21 +03:00
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
2009-12-30 23:48:42 +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.
2009-12-30 23:48:42 +03:00
2007-12-24 04:19:41 +03:00
: 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 ) :
2010-06-20 04:32:23 +04:00
""" Erases user and computer objects from our AD.
2010-11-28 16:09:30 +03:00
2010-06-20 04:32:23 +04:00
This is needed since the ' samldb ' module denies the deletion of primary
groups . Therefore all groups shouldn ' t be primary somewhere anymore.
"""
2009-09-06 23:08:08 +04:00
try :
res = self . search ( base = dn , scope = ldb . SCOPE_SUBTREE , attrs = [ ] ,
2018-07-30 09:16:12 +03:00
expression = " (|(objectclass=user)(objectclass=computer)) " )
2016-12-13 13:26:53 +03:00
except ldb . LdbError as error :
( errno , estr ) = error . args
2010-03-01 07:04:23 +03:00
if errno == ldb . ERR_NO_SUCH_OBJECT :
# Ignore no such object errors
return
else :
raise
2009-09-06 23:08:08 +04:00
try :
for msg in res :
2010-06-19 00:20:22 +04:00
self . delete ( msg . dn , [ " relax:0 " ] )
2016-12-13 13:26:53 +03:00
except ldb . LdbError as error :
( errno , estr ) = error . args
2010-03-01 07:04:23 +03:00
if errno != ldb . ERR_NO_SUCH_OBJECT :
# Ignore no such object errors
raise
2009-09-06 23:08:08 +04:00
2009-08-17 05:33:25 +04:00
def erase_except_schema_controlled ( self ) :
2010-06-11 01:12:53 +04:00
""" Erase this ldb.
2010-11-28 16:09:30 +03:00
2010-06-11 01:12:53 +04:00
: note : Removes 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 )
2012-02-19 02:59:48 +04:00
# Delete the 'visible' records, and the invisble 'deleted' records (if
# this DB supports it)
2009-12-30 23:48:42 +03:00
for msg in self . search ( basedn , ldb . SCOPE_SUBTREE ,
2018-07-30 09:16:12 +03:00
" (&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO))) " ,
[ ] , controls = [ " show_deleted:0 " , " show_recycled:0 " ] ) :
2007-12-20 01:27:38 +03:00
try :
2010-06-19 00:20:22 +04:00
self . delete ( msg . dn , [ " relax:0 " ] )
2016-12-13 13:26:53 +03:00
except ldb . LdbError as error :
( errno , estr ) = error . args
2010-03-01 07:04:23 +03:00
if errno != ldb . ERR_NO_SUCH_OBJECT :
# Ignore no such object errors
raise
2009-12-30 23:48:42 +03:00
res = self . search ( basedn , ldb . SCOPE_SUBTREE ,
2018-07-30 09:16:12 +03:00
" (&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO))) " ,
[ ] , controls = [ " show_deleted:0 " , " show_recycled: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-12-30 23:48:42 +03:00
for attr in [ " @SUBCLASSES " , " @MODULES " ,
2009-08-13 08:37:06 +04:00
" @OPTIONS " , " @PARTITION " , " @KLUDGEACL " ] :
try :
2010-06-19 00:20:22 +04:00
self . delete ( attr , [ " relax:0 " ] )
2016-12-13 13:26:53 +03:00
except ldb . LdbError as error :
( errno , estr ) = error . args
2010-03-01 07:04:23 +03:00
if errno != ldb . ERR_NO_SUCH_OBJECT :
# Ignore missing dn errors
raise
2009-08-13 08:37:06 +04:00
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 :
2010-06-19 00:20:22 +04:00
self . delete ( attr , [ " relax:0 " ] )
2016-12-13 13:26:53 +03:00
except ldb . LdbError as error :
( errno , estr ) = error . args
2010-03-01 18:24:29 +03:00
if errno != ldb . ERR_NO_SUCH_OBJECT :
2010-03-01 07:04:23 +03:00
# Ignore missing dn errors
raise
2009-08-17 05:33:25 +04:00
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 .
"""
2021-09-01 06:42:28 +03:00
with open ( ldif_path , ' r ' ) as ldif_file :
self . add_ldif ( ldif_file . read ( ) )
2007-12-20 01:27:38 +03:00
2009-11-20 14:22:38 +03: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
2010-09-29 02:09:09 +04:00
self . add ( msg , controls )
2007-12-20 01:27:38 +03:00
2009-11-20 14:22:38 +03:00
def modify_ldif ( self , ldif , controls = None ) :
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 ) :
2023-03-13 16:42:29 +03:00
if changetype == ldb . CHANGETYPE_NONE :
changetype = ldb . CHANGETYPE_MODIFY
2010-06-20 04:32:23 +04:00
if changetype == ldb . CHANGETYPE_ADD :
2010-01-06 01:15:35 +03:00
self . add ( msg , controls )
2023-03-13 16:42:29 +03:00
elif changetype == ldb . CHANGETYPE_MODIFY :
2010-01-06 01:15:35 +03:00
self . modify ( msg , controls )
2023-03-13 16:56:55 +03:00
elif changetype == ldb . CHANGETYPE_DELETE :
deldn = msg
self . delete ( deldn , controls )
2023-03-13 17:03:39 +03:00
elif changetype == ldb . CHANGETYPE_MODRDN :
olddn = msg [ " olddn " ]
deleteoldrdn = msg [ " deleteoldrdn " ]
newdn = msg [ " newdn " ]
if deleteoldrdn is False :
raise ValueError ( " Invalid ldb.CHANGETYPE_MODRDN with deleteoldrdn=False " )
self . rename ( olddn , newdn , controls )
2023-03-13 16:42:29 +03:00
else :
raise ValueError ( " Invalid ldb.CHANGETYPE_ %u : %s " % ( changetype , msg ) )
2007-12-24 04:19:41 +03:00
2007-11-21 15:07:16 +03:00
def substitute_var ( text , values ) :
2010-06-11 01:12:53 +04:00
""" Substitute strings of the form $ {NAME} in str, replacing
2010-06-20 17:22:49 +04:00
with substitutions from values .
2009-12-30 23:48:42 +03:00
2007-11-21 15:07:16 +03:00
: param text : Text in which to subsitute .
: param values : Dictionary with keys and values .
"""
for ( name , value ) in values . items ( ) :
2020-07-04 04:47:44 +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 ) :
2010-11-28 16:09:30 +03:00
""" Check that all substitution variables in a string have been replaced.
2008-01-25 00:18:27 +03:00
If not , raise an exception .
2009-12-30 23:48:42 +03:00
2008-01-25 00:18:27 +03:00
: param text : The text to search for substitution variables
"""
2018-07-30 05:56:46 +03:00
if " $ { " not in text :
2009-02-24 03:02:26 +03:00
return
2009-12-30 23:48:42 +03:00
2008-01-25 00:18:27 +03:00
var_start = text . find ( " $ { " )
var_end = text . find ( " } " , var_start )
2009-12-30 23:48:42 +03:00
2010-11-28 16:09:30 +03:00
raise Exception ( " Not all variables substituted: %s " %
2018-07-30 09:18:25 +03:00
text [ var_start : var_end + 1 ] )
2008-01-25 00:18:27 +03:00
2010-09-29 02:53:22 +04:00
def read_and_sub_file ( file_name , subst_vars ) :
2009-10-30 06:31:25 +03:00
""" Read a file and sub in variables found in it
2009-12-30 23:48:42 +03:00
2010-09-29 02:53:22 +04:00
: param file_name : File to be read ( typically from setup directory )
2009-10-30 06:31:25 +03:00
param subst_vars : Optional variables to subsitute in the file .
"""
2021-09-01 06:42:28 +03:00
with open ( file_name , ' r ' , encoding = " utf-8 " ) as data_file :
data = data_file . read ( )
if subst_vars is not None :
data = substitute_var ( data , subst_vars )
check_all_substituted ( data )
2009-10-30 06:31:25 +03:00
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 .
"""
2010-06-11 01:12:53 +04:00
if os . path . exists ( fname ) :
os . unlink ( fname )
2009-10-30 07:18:42 +03:00
data = read_and_sub_file ( template , subst_vars )
2010-06-11 01:12:53 +04:00
f = open ( fname , ' w ' )
try :
f . write ( data )
finally :
f . close ( )
2009-10-30 07:18:42 +03:00
2018-07-30 09:21:29 +03:00
2012-02-26 23:51:04 +04:00
MAX_NETBIOS_NAME_LEN = 15
2018-07-30 09:20:39 +03:00
2012-02-26 23:51:04 +04:00
def is_valid_netbios_char ( c ) :
return ( c . isalnum ( ) or c in " !#$ % & ' ()-.@^_ {} ~ " )
2009-10-30 07:18:42 +03:00
2012-05-27 16:17:52 +04:00
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)
2012-02-26 23:51:04 +04:00
if len ( name ) > MAX_NETBIOS_NAME_LEN :
2007-12-10 12:29:45 +03:00
return False
2012-05-27 16:17:52 +04:00
for x in name :
if not is_valid_netbios_char ( x ) :
return False
return True
2007-12-10 12:29:45 +03:00
2009-08-13 13:37:38 +04:00
2011-08-24 09:32:57 +04:00
def dn_from_dns_name ( dnsdomain ) :
""" return a DN from a DNS name domain/forest root """
return " DC= " + " ,DC= " . join ( dnsdomain . split ( " . " ) )
2018-07-30 09:20:39 +03:00
2015-01-22 14:23:09 +03:00
def current_unix_time ( ) :
return int ( time . time ( ) )
2018-07-30 09:20:39 +03:00
2016-01-28 15:44:33 +03:00
def string_to_byte_array ( string ) :
2022-10-12 03:56:19 +03:00
return [ c if isinstance ( c , int ) else ord ( c ) for c in string ]
2016-01-28 15:44:33 +03:00
2018-07-30 09:20:39 +03:00
2016-01-28 15:52:44 +03:00
def arcfour_encrypt ( key , data ) :
2017-03-10 17:20:06 +03:00
from samba . crypto import arcfour_crypt_blob
return arcfour_crypt_blob ( data , key )
2016-01-28 15:52:44 +03:00
2018-07-30 09:21:29 +03:00
2020-07-04 07:20:47 +03:00
def enable_net_export_keytab ( ) :
""" This function modifies the samba.net.Net class to contain
an export_keytab ( ) method . """
# This looks very strange because it is.
#
# The dckeytab modules contains nothing, but the act of importing
# it pushes a method into samba.net.Net. It ended up this way
# because Net.export_keytab() only works on Heimdal builds, and
# people sometimes want to compile Samba without Heimdal while
# still having a working samba-tool.
#
# There is probably a better way to do this than a magic module
# import (yes, that's a FIXME if you can be bothered).
from samba import net
from samba import dckeytab
2010-04-08 22:34:40 +04:00
version = _glue . version
interface_ips = _glue . interface_ips
2018-05-25 08:52:02 +03:00
fault_setup = _glue . fault_setup
2010-04-08 22:34:40 +04:00
set_debug_level = _glue . set_debug_level
2010-11-29 05:26:48 +03:00
get_debug_level = _glue . get_debug_level
2020-12-02 17:42:10 +03:00
float2nttime = _glue . float2nttime
nttime2float = _glue . nttime2float
2010-11-27 15:47:30 +03:00
nttime2string = _glue . nttime2string
nttime2unix = _glue . nttime2unix
unix2nttime = _glue . unix2nttime
2010-04-08 22:34:40 +04:00
generate_random_password = _glue . generate_random_password
2016-08-23 10:35:50 +03:00
generate_random_machine_password = _glue . generate_random_machine_password
2017-11-28 05:45:30 +03:00
check_password_quality = _glue . check_password_quality
2017-11-02 00:15:29 +03:00
generate_random_bytes = _glue . generate_random_bytes
2011-05-18 06:06:25 +04:00
strcasecmp_m = _glue . strcasecmp_m
strstr_m = _glue . strstr_m
2015-10-09 23:30:17 +03:00
is_ntvfs_fileserver_built = _glue . is_ntvfs_fileserver_built
2015-10-09 16:06:52 +03:00
is_heimdal_built = _glue . is_heimdal_built
2020-09-18 20:27:24 +03:00
is_ad_dc_built = _glue . is_ad_dc_built
2021-03-19 21:31:42 +03:00
is_selftest_enabled = _glue . is_selftest_enabled
2016-11-01 05:23:58 +03:00
NTSTATUSError = _glue . NTSTATUSError
HRESULTError = _glue . HRESULTError
WERRORError = _glue . WERRORError
2016-11-01 06:09:20 +03:00
DsExtendedError = _glue . DsExtendedError