3 # Helpers for provision stuff
4 # Copyright (C) Matthieu Patou <mat@matws.net> 2009-2010
6 # Based on provision a Samba4 server by
7 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
8 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
31 from samba
import Ldb
, DS_DOMAIN_FUNCTION_2000
32 from ldb
import SCOPE_SUBTREE
, SCOPE_ONELEVEL
, SCOPE_BASE
34 from samba
.provision
import ProvisionNames
, provision_paths_from_lp
, FILL_FULL
, provision
35 from samba
.provisionexceptions
import ProvisioningError
36 from samba
.dcerpc
import misc
, security
37 from samba
.ndr
import ndr_unpack
40 def get_paths(param
, targetdir
=None, smbconf
=None):
41 """Get paths to important provision objects (smb.conf, ldb files, ...)
43 :param param: Param object
44 :param targetdir: Directory where the provision is (or will be) stored
45 :param smbconf: Path to the smb.conf file
46 :return: A list with the path of important provision objects"""
47 if targetdir
is not None:
48 etcdir
= os
.path
.join(targetdir
, "etc")
49 if not os
.path
.exists(etcdir
):
51 smbconf
= os
.path
.join(etcdir
, "smb.conf")
53 smbconf
= param
.default_path()
55 if not os
.path
.exists(smbconf
):
56 raise ProvisioningError("Unable to find smb.conf ...")
60 paths
= provision_paths_from_lp(lp
,lp
.get("realm"))
64 def find_provision_key_parameters(param
, credentials
, session_info
, paths
, smbconf
):
65 """Get key provision parameters (realm, domain, ...) from a given provision
67 :param param: Param object
68 :param credentials: Credentials for the authentification
69 :param session_info: Session object
70 :param paths: A list of path to provision object
71 :param smbconf: Path to the smb.conf file
72 :return: A list of key provision parameters"""
75 lp
.load(paths
.smbconf
)
76 names
= ProvisionNames()
77 names
.adminpass
= None
78 # NT domain, kerberos realm, root dn, domain dn, domain dns name
79 names
.domain
= string
.upper(lp
.get("workgroup"))
80 names
.realm
= lp
.get("realm")
81 basedn
= "DC=" + names
.realm
.replace(".",",DC=")
82 names
.dnsdomain
= names
.realm
83 names
.realm
= string
.upper(names
.realm
)
85 secrets_ldb
= Ldb(paths
.secrets
, session_info
=session_info
, credentials
=credentials
,lp
=lp
, options
=["modules:samba_secrets"])
86 # Get the netbiosname first (could be obtained from smb.conf in theory)
87 attrs
= ["sAMAccountName"]
88 res
= secrets_ldb
.search(expression
="(flatname=%s)"%names
.domain
,base
="CN=Primary Domains", scope
=SCOPE_SUBTREE
, attrs
=attrs
)
89 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
91 names
.smbconf
= smbconf
92 # It's important here to let ldb load with the old module or it's quite
93 # certain that the LDB won't load ...
94 samdb
= Ldb(paths
.samdb
, session_info
=session_info
,
95 credentials
=credentials
, lp
=lp
, options
=["modules:samba_dsdb"])
97 # That's a bit simplistic but it's ok as long as we have only 3
99 attrs2
= ["defaultNamingContext", "schemaNamingContext","configurationNamingContext","rootDomainNamingContext"]
100 current
= samdb
.search(expression
="(objectClass=*)",base
="", scope
=SCOPE_BASE
, attrs
=attrs2
)
102 names
.configdn
= current
[0]["configurationNamingContext"]
103 configdn
= str(names
.configdn
)
104 names
.schemadn
= current
[0]["schemaNamingContext"]
105 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
, current
[0]["defaultNamingContext"][0]))):
106 raise ProvisioningError(("basedn in %s (%s) and from %s (%s) is not the same ..." % (paths
.samdb
, str(current
[0]["defaultNamingContext"][0]), paths
.smbconf
, basedn
)))
108 names
.domaindn
=current
[0]["defaultNamingContext"]
109 names
.rootdn
=current
[0]["rootDomainNamingContext"]
112 res3
= samdb
.search(expression
="(objectClass=*)",base
="CN=Sites,"+configdn
, scope
=SCOPE_ONELEVEL
, attrs
=attrs3
)
113 names
.sitename
= str(res3
[0]["cn"])
115 # dns hostname and server dn
116 attrs4
= ["dNSHostName"]
117 res4
= samdb
.search(expression
="(CN=%s)"%names
.netbiosname
,base
="OU=Domain Controllers,"+basedn
, \
118 scope
=SCOPE_ONELEVEL
, attrs
=attrs4
)
119 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("."+names
.dnsdomain
,"")
121 server_res
= samdb
.search(expression
="serverReference=%s"%res4[0].dn
, attrs
=[], base
=configdn
)
122 names
.serverdn
= server_res
[0].dn
124 # invocation id/objectguid
125 res5
= samdb
.search(expression
="(objectClass=*)",base
="CN=NTDS Settings,%s" % str(names
.serverdn
), scope
=SCOPE_BASE
, attrs
=["invocationID","objectGUID"])
126 names
.invocation
= str(ndr_unpack( misc
.GUID
,res5
[0]["invocationId"][0]))
127 names
.ntdsguid
= str(ndr_unpack( misc
.GUID
,res5
[0]["objectGUID"][0]))
130 attrs6
= ["objectGUID", "objectSid","msDS-Behavior-Version" ]
131 res6
= samdb
.search(expression
="(objectClass=*)",base
=basedn
, scope
=SCOPE_BASE
, attrs
=attrs6
)
132 names
.domainguid
= str(ndr_unpack( misc
.GUID
,res6
[0]["objectGUID"][0]))
133 names
.domainsid
= ndr_unpack( security
.dom_sid
,res6
[0]["objectSid"][0])
134 if res6
[0].get("msDS-Behavior-Version") == None or int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
135 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
137 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
140 attrs7
= ["cn","displayName"]
141 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",base
="CN=Policies,CN=System,"+basedn
, \
142 scope
=SCOPE_ONELEVEL
, attrs
=attrs7
)
143 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
145 attrs8
= ["cn","displayName"]
146 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers Policy)",base
="CN=Policies,CN=System,"+basedn
, \
147 scope
=SCOPE_ONELEVEL
, attrs
=attrs7
)
149 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
151 names
.policyid_dc
= None
156 def newprovision(names
,setup_dir
,creds
,session
,smbconf
,provdir
,messagefunc
):
157 """Create a new provision.
159 This provision will be the reference for knowing what has changed in the
160 since the latest upgrade in the current provision
162 :param names: List of provision parameters
163 :param setup_dis: Directory where the setup files are stored
164 :param creds: Credentials for the authentification
165 :param session: Session object
166 :param smbconf: Path to the smb.conf file
167 :param provdir: Directory where the provision will be stored
168 :param messagefunc: A function for displaying the message of the provision"""
169 if os
.path
.isdir(provdir
):
170 shutil
.rmtree(provdir
)
171 logstd
=os
.path
.join(provdir
,"log.std")
172 os
.chdir(os
.path
.join(setup_dir
,".."))
174 messagefunc("Provision stored in %s"%provdir
)
175 provision(setup_dir
, messagefunc
,
176 session
, creds
, smbconf
=smbconf
, targetdir
=provdir
,
177 samdb_fill
=FILL_FULL
, realm
=names
.realm
, domain
=names
.domain
,
178 domainguid
=names
.domainguid
, domainsid
=str(names
.domainsid
),ntdsguid
=names
.ntdsguid
,
179 policyguid
=names
.policyid
,policyguid_dc
=names
.policyid_dc
,hostname
=names
.netbiosname
,
180 hostip
=None, hostip6
=None,
181 invocationid
=names
.invocation
, adminpass
=names
.adminpass
,
182 krbtgtpass
=None, machinepass
=None,
183 dnspass
=None, root
=None, nobody
=None,
184 wheel
=None, users
=None,
185 serverrole
="domain controller",
186 ldap_backend_extra_port
=None,
193 dom_for_fun_level
=names
.domainlevel
,
194 ldap_dryrun_mode
=None,useeadb
=True)
198 """Sorts two DNs in the lexicographical order it and put higher level DN before.
200 So given the dns cn=bar,cn=foo and cn=foo the later will be return as smaller
201 :param x: First object to compare
202 :param y: Second object to compare
204 p
= re
.compile(r
'(?<!\\),')
205 tab1
= p
.split(str(x
))
206 tab2
= p
.split(str(y
))
207 minimum
= min(len(tab1
), len(tab2
))
210 # Note: python range go up to upper limit but do not include it
211 for i
in range(0,minimum
):
212 ret
= cmp(tab1
[len1
-i
],tab2
[len2
-i
])
217 assert len1
!=len2
,"PB PB PB"+" ".join(tab1
)+" / "+" ".join(tab2
)