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
.dsdb
import DS_DOMAIN_FUNCTION_2000
32 from ldb
import SCOPE_SUBTREE
, SCOPE_ONELEVEL
, SCOPE_BASE
34 from samba
.provision
import (ProvisionNames
, provision_paths_from_lp
,
35 FILL_FULL
, provision
, 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
,
66 """Get key provision parameters (realm, domain, ...) from a given provision
68 :param param: Param object
69 :param credentials: Credentials for the authentification
70 :param session_info: Session object
71 :param paths: A list of path to provision object
72 :param smbconf: Path to the smb.conf file
73 :return: A list of key provision parameters"""
76 lp
.load(paths
.smbconf
)
77 names
= ProvisionNames()
78 names
.adminpass
= None
79 # NT domain, kerberos realm, root dn, domain dn, domain dns name
80 names
.domain
= string
.upper(lp
.get("workgroup"))
81 names
.realm
= lp
.get("realm")
82 basedn
= "DC=" + names
.realm
.replace(".",",DC=")
83 names
.dnsdomain
= names
.realm
84 names
.realm
= string
.upper(names
.realm
)
86 secrets_ldb
= Ldb(paths
.secrets
, session_info
=session_info
,
87 credentials
=credentials
,lp
=lp
, options
=["modules:samba_secrets"])
88 # Get the netbiosname first (could be obtained from smb.conf in theory)
89 res
= secrets_ldb
.search(expression
="(flatname=%s)"%names
.domain
,base
="CN=Primary Domains", scope
=SCOPE_SUBTREE
, attrs
=["sAMAccountName"])
90 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
92 names
.smbconf
= smbconf
93 # It's important here to let ldb load with the old module or it's quite
94 # certain that the LDB won't load ...
95 samdb
= Ldb(paths
.samdb
, session_info
=session_info
,
96 credentials
=credentials
, lp
=lp
, options
=["modules:samba_dsdb"])
98 # That's a bit simplistic but it's ok as long as we have only 3
100 current
= samdb
.search(expression
="(objectClass=*)",
101 base
="", scope
=SCOPE_BASE
,
102 attrs
=["defaultNamingContext", "schemaNamingContext",
103 "configurationNamingContext","rootDomainNamingContext"])
105 names
.configdn
= current
[0]["configurationNamingContext"]
106 configdn
= str(names
.configdn
)
107 names
.schemadn
= current
[0]["schemaNamingContext"]
108 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
, current
[0]["defaultNamingContext"][0]))):
109 raise ProvisioningError(("basedn in %s (%s) and from %s (%s) is not the same ..." % (paths
.samdb
, str(current
[0]["defaultNamingContext"][0]), paths
.smbconf
, basedn
)))
111 names
.domaindn
=current
[0]["defaultNamingContext"]
112 names
.rootdn
=current
[0]["rootDomainNamingContext"]
114 res3
= samdb
.search(expression
="(objectClass=*)",
115 base
="CN=Sites,"+configdn
, scope
=SCOPE_ONELEVEL
, attrs
=["cn"])
116 names
.sitename
= str(res3
[0]["cn"])
118 # dns hostname and server dn
119 res4
= samdb
.search(expression
="(CN=%s)" % names
.netbiosname
,
120 base
="OU=Domain Controllers,"+basedn
, scope
=SCOPE_ONELEVEL
, attrs
=["dNSHostName"])
121 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("."+names
.dnsdomain
,"")
123 server_res
= samdb
.search(expression
="serverReference=%s"%res4[0].dn
, attrs
=[], base
=configdn
)
124 names
.serverdn
= server_res
[0].dn
126 # invocation id/objectguid
127 res5
= samdb
.search(expression
="(objectClass=*)",
128 base
="CN=NTDS Settings,%s" % str(names
.serverdn
), scope
=SCOPE_BASE
,
129 attrs
=["invocationID", "objectGUID"])
130 names
.invocation
= str(ndr_unpack(misc
.GUID
, res5
[0]["invocationId"][0]))
131 names
.ntdsguid
= str(ndr_unpack(misc
.GUID
, res5
[0]["objectGUID"][0]))
134 res6
= samdb
.search(expression
="(objectClass=*)",base
=basedn
,
135 scope
=SCOPE_BASE
, attrs
=["objectGUID",
136 "objectSid","msDS-Behavior-Version" ])
137 names
.domainguid
= str(ndr_unpack( misc
.GUID
,res6
[0]["objectGUID"][0]))
138 names
.domainsid
= ndr_unpack( security
.dom_sid
,res6
[0]["objectSid"][0])
139 if res6
[0].get("msDS-Behavior-Version") == None or int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
140 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
142 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
145 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",base
="CN=Policies,CN=System,"+basedn
, \
146 scope
=SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
147 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
149 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers Policy)",base
="CN=Policies,CN=System,"+basedn
, \
150 scope
=SCOPE_ONELEVEL
, attrs
=["cn","displayName"])
152 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
154 names
.policyid_dc
= None
159 def newprovision(names
,setup_dir
,creds
,session
,smbconf
,provdir
,messagefunc
):
160 """Create a new provision.
162 This provision will be the reference for knowing what has changed in the
163 since the latest upgrade in the current provision
165 :param names: List of provision parameters
166 :param setup_dis: Directory where the setup files are stored
167 :param creds: Credentials for the authentification
168 :param session: Session object
169 :param smbconf: Path to the smb.conf file
170 :param provdir: Directory where the provision will be stored
171 :param messagefunc: A function for displaying the message of the provision
173 if os
.path
.isdir(provdir
):
174 shutil
.rmtree(provdir
)
175 os
.chdir(os
.path
.join(setup_dir
,".."))
177 messagefunc("Provision stored in %s"%provdir
)
178 provision(setup_dir
, messagefunc
, session
, creds
, smbconf
=smbconf
,
179 targetdir
=provdir
, samdb_fill
=FILL_FULL
, realm
=names
.realm
,
180 domain
=names
.domain
, domainguid
=names
.domainguid
,
181 domainsid
=str(names
.domainsid
), ntdsguid
=names
.ntdsguid
,
182 policyguid
=names
.policyid
, policyguid_dc
=names
.policyid_dc
,
183 hostname
=names
.netbiosname
, hostip
=None, hostip6
=None,
184 invocationid
=names
.invocation
, adminpass
=names
.adminpass
,
185 krbtgtpass
=None, machinepass
=None, dnspass
=None, root
=None,
186 nobody
=None, wheel
=None, users
=None,
187 serverrole
="domain controller", ldap_backend_extra_port
=None,
188 backend_type
=None, ldapadminpass
=None, ol_mmr_urls
=None,
189 slapd_path
=None, setup_ds_path
=None, nosync
=None,
190 dom_for_fun_level
=names
.domainlevel
,
191 ldap_dryrun_mode
=None, useeadb
=True)
195 """Sorts two DNs in the lexicographical order it and put higher level DN
198 So given the dns cn=bar,cn=foo and cn=foo the later will be return as
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
)