2009-10-30 06:31:25 +03:00
#
# Unix SMB/CIFS implementation.
# backend code for provisioning a Samba4 server
#
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
# Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
#
# 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.
2010-11-28 05:34:47 +03:00
#
2009-10-30 06:31:25 +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.
2010-11-28 05:34:47 +03:00
#
2009-10-30 06:31:25 +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/>.
#
""" Functions for setting up a Samba Schema. """
from base64 import b64encode
from samba import read_and_sub_file , substitute_var , check_all_substituted
2010-06-11 01:12:53 +04:00
from samba . dcerpc import security
from samba . ms_schema import read_ms_schema
2009-10-30 06:31:25 +03:00
from samba . ndr import ndr_pack
2010-06-11 01:12:53 +04:00
from samba . samdb import SamDB
2010-06-20 03:43:51 +04:00
from samba import dsdb
2010-03-01 06:46:40 +03:00
from ldb import SCOPE_SUBTREE , SCOPE_ONELEVEL
2009-11-10 07:18:52 +03:00
import os
2009-10-30 06:31:25 +03:00
2013-01-23 18:53:00 +04:00
def get_schema_descriptor ( domain_sid , name_map = { } ) :
2010-10-23 22:27:50 +04:00
sddl = " O:SAG:SAD:AI(OA;;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;SA) " \
" (OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED) " \
" (OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED) " \
" (OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED) " \
" (OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA) " \
" (OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA) " \
" (OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA) " \
" (A;CI;RPLCLORC;;;AU) " \
" (A;CI;RPWPCRCCLCLORCWOWDSW;;;SA) " \
" (A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY) " \
" (OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED) " \
" (OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED) " \
" (OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA) " \
" (OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA) " \
" (OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER) " \
" (OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ER) " \
" (OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ER) " \
" S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD) " \
" (AU;CISA;WP;;;WD) " \
" (AU;SA;CR;;;BA) " \
" (AU;SA;CR;;;DU) " \
" (OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD) " \
" (OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD) "
2009-10-30 06:31:25 +03:00
sec = security . descriptor . from_sddl ( sddl , domain_sid )
2009-11-27 17:37:14 +03:00
return ndr_pack ( sec )
2009-10-30 06:31:25 +03:00
2010-11-28 05:34:47 +03:00
2009-10-30 06:31:25 +03:00
class Schema ( object ) :
2010-04-09 00:14:50 +04:00
2011-02-05 02:34:51 +03:00
def __init__ ( self , domain_sid , invocationid = None , schemadn = None ,
2010-10-19 02:12:57 +04:00
files = None , override_prefixmap = None , additional_prefixmap = None ) :
2011-02-05 02:34:51 +03:00
from samba . provision import setup_path
2010-11-28 05:34:47 +03:00
""" Load schema for the SamDB from the AD schema files and
samba4_schema . ldif
2009-10-30 06:31:25 +03:00
: param samdb : Load a schema into a SamDB .
: param schemadn : DN of the schema
2010-11-28 05:34:47 +03:00
Returns the schema data loaded , to avoid double - parsing when then
needing to add it to the db
2009-10-30 06:31:25 +03:00
"""
self . schemadn = schemadn
2010-11-28 16:09:30 +03:00
# We need to have the am_rodc=False just to keep some warnings quiet -
# this isn't a real SAM, so it's meaningless.
2010-06-21 17:18:53 +04:00
self . ldb = SamDB ( global_schema = False , am_rodc = False )
2010-04-20 05:48:51 +04:00
if invocationid is not None :
self . ldb . set_invocation_id ( invocationid )
2010-04-09 00:14:50 +04:00
self . schema_data = read_ms_schema (
setup_path ( ' ad-schema/MS-AD_Schema_2K8_R2_Attributes.txt ' ) ,
setup_path ( ' ad-schema/MS-AD_Schema_2K8_R2_Classes.txt ' ) )
2009-11-13 18:58:20 +03:00
if files is not None :
for file in files :
self . schema_data + = open ( file , ' r ' ) . read ( )
2010-04-09 00:14:50 +04:00
self . schema_data = substitute_var ( self . schema_data ,
{ " SCHEMADN " : schemadn } )
2009-10-30 06:31:25 +03:00
check_all_substituted ( self . schema_data )
2010-04-09 00:14:50 +04:00
self . schema_dn_modify = read_and_sub_file (
setup_path ( " provision_schema_basedn_modify.ldif " ) ,
2010-10-19 02:12:57 +04:00
{ " SCHEMADN " : schemadn } )
2009-10-30 06:31:25 +03:00
2009-11-27 17:37:14 +03:00
descr = b64encode ( get_schema_descriptor ( domain_sid ) )
2010-04-09 00:14:50 +04:00
self . schema_dn_add = read_and_sub_file (
setup_path ( " provision_schema_basedn.ldif " ) ,
{ " SCHEMADN " : schemadn , " DESCRIPTOR " : descr } )
2009-10-30 06:31:25 +03:00
2010-06-10 02:58:44 +04:00
if override_prefixmap is not None :
self . prefixmap_data = override_prefixmap
else :
self . prefixmap_data = open ( setup_path ( " prefixMap.txt " ) , ' r ' ) . read ( )
2009-11-13 18:58:20 +03:00
2010-06-10 02:58:44 +04:00
if additional_prefixmap is not None :
for map in additional_prefixmap :
2009-11-13 18:58:20 +03:00
self . prefixmap_data + = " %s \n " % map
self . prefixmap_data = b64encode ( self . prefixmap_data )
2009-10-30 06:31:25 +03:00
# We don't actually add this ldif, just parse it
2011-11-11 19:32:05 +04:00
prefixmap_ldif = " dn: %s \n prefixMap:: %s \n \n " % ( self . schemadn , self . prefixmap_data )
self . set_from_ldif ( prefixmap_ldif , self . schema_data , self . schemadn )
2010-06-20 04:32:23 +04:00
2011-11-11 19:32:05 +04:00
def set_from_ldif ( self , pf , df , dn ) :
2011-11-11 19:34:48 +04:00
dsdb . _dsdb_set_schema_from_ldif ( self . ldb , pf , df , dn )
2009-10-30 06:31:25 +03:00
def write_to_tmp_ldb ( self , schemadb_path ) :
2010-04-20 05:48:51 +04:00
self . ldb . connect ( url = schemadb_path )
2009-10-30 06:31:25 +03:00
self . ldb . transaction_start ( )
2010-04-09 00:14:50 +04:00
try :
self . ldb . add_ldif ( """ dn: @ATTRIBUTES
2009-10-30 06:31:25 +03:00
linkID : INTEGER
dn : @INDEXLIST
@IDXATTR : linkID
@IDXATTR : attributeSyntax
""" )
2010-04-09 00:14:50 +04:00
# These bits of LDIF are supplied when the Schema object is created
self . ldb . add_ldif ( self . schema_dn_add )
self . ldb . modify_ldif ( self . schema_dn_modify )
self . ldb . add_ldif ( self . schema_data )
2012-02-25 18:56:25 +04:00
except :
2010-04-09 00:14:50 +04:00
self . ldb . transaction_cancel ( )
raise
else :
self . ldb . transaction_commit ( )
2009-10-30 06:31:25 +03:00
2010-11-28 05:34:47 +03:00
# Return a hash with the forward attribute as a key and the back as the
# value
2009-10-30 06:31:25 +03:00
def linked_attributes ( self ) :
return get_linked_attributes ( self . schemadn , self . ldb )
def dnsyntax_attributes ( self ) :
return get_dnsyntax_attributes ( self . schemadn , self . ldb )
2010-06-20 03:43:51 +04:00
def convert_to_openldap ( self , target , mapping ) :
2010-06-20 17:22:49 +04:00
return dsdb . _dsdb_convert_schema_to_openldap ( self . ldb , target , mapping )
2010-06-20 03:43:51 +04:00
2012-09-27 20:30:47 +04:00
# Return a hash with the forward attribute as a key and the back as the value
2014-06-02 04:53:01 +04:00
def get_linked_attributes ( schemadn , schemaldb ) :
2009-10-30 06:31:25 +03:00
attrs = [ " linkID " , " lDAPDisplayName " ]
2014-06-02 04:53:01 +04:00
res = schemaldb . search (
expression = " (&(linkID=*) "
" (!(linkID:1.2.840.113556.1.4.803:=1)) "
" (objectclass=attributeSchema) "
" (attributeSyntax=2.5.5.1)) " ,
base = schemadn , scope = SCOPE_ONELEVEL , attrs = attrs )
2009-10-30 06:31:25 +03:00
attributes = { }
2014-06-02 03:33:43 +04:00
for i in range ( 0 , len ( res ) ) :
2014-06-02 04:53:01 +04:00
expression = ( " (&(objectclass=attributeSchema)(linkID= %d ) "
" (attributeSyntax=2.5.5.1)) " %
( int ( res [ i ] [ " linkID " ] [ 0 ] ) + 1 ) )
2012-09-27 20:30:47 +04:00
target = schemaldb . searchone ( basedn = schemadn ,
expression = expression ,
attribute = " lDAPDisplayName " ,
2009-10-30 06:31:25 +03:00
scope = SCOPE_SUBTREE )
if target is not None :
attributes [ str ( res [ i ] [ " lDAPDisplayName " ] ) ] = str ( target )
2010-11-28 05:34:47 +03:00
2009-10-30 06:31:25 +03:00
return attributes
2010-04-09 00:14:50 +04:00
2009-10-30 06:31:25 +03:00
def get_dnsyntax_attributes ( schemadn , schemaldb ) :
2010-04-09 00:14:50 +04:00
res = schemaldb . search (
expression = " (&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1)) " ,
base = schemadn , scope = SCOPE_ONELEVEL ,
attrs = [ " linkID " , " lDAPDisplayName " ] )
2009-10-30 06:31:25 +03:00
attributes = [ ]
2014-06-02 03:33:43 +04:00
for i in range ( 0 , len ( res ) ) :
2009-10-30 06:31:25 +03:00
attributes . append ( str ( res [ i ] [ " lDAPDisplayName " ] ) )
return attributes
2010-04-09 00:14:50 +04:00
2011-02-05 02:34:51 +03:00
def ldb_with_schema ( schemadn = " cn=schema,cn=configuration,dc=example,dc=com " ,
domainsid = None ,
override_prefixmap = None ) :
2009-11-10 07:18:52 +03:00
""" Load schema for the SamDB from the AD schema files and samba4_schema.ldif
2010-11-28 05:34:47 +03:00
2009-11-10 07:18:52 +03:00
: param schemadn : DN of the schema
: param serverdn : DN of the server
2010-11-28 05:34:47 +03:00
2009-11-10 07:18:52 +03:00
Returns the schema data loaded as an object , with . ldb being a
new ldb with the schema loaded . This allows certain tests to
operate without a remote or local schema .
"""
2010-11-28 05:34:47 +03:00
2009-11-10 07:18:52 +03:00
if domainsid is None :
domainsid = security . random_sid ( )
else :
domainsid = security . dom_sid ( domainsid )
2011-02-05 02:34:51 +03:00
return Schema ( domainsid , schemadn = schemadn ,
2010-11-28 05:34:47 +03:00
override_prefixmap = override_prefixmap )