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/>.
30 from samba
import Ldb
, DS_DOMAIN_FUNCTION_2000
31 from ldb
import SCOPE_SUBTREE
, SCOPE_ONELEVEL
, SCOPE_BASE
33 from samba
.provision
import ProvisionNames
, provision_paths_from_lp
, FILL_FULL
, provision
34 from samba
.provisionexceptions
import ProvisioningError
35 from samba
.dcerpc
import misc
, security
36 from samba
.ndr
import ndr_unpack
39 def get_paths(param
, targetdir
=None, smbconf
=None):
40 """Get paths to important provision objects (smb.conf, ldb files, ...)
42 :param param: Param object
43 :param targetdir: Directory where the provision is (or will be) stored
44 :param smbconf: Path to the smb.conf file
45 :return: A list with the path of important provision objects"""
46 if targetdir
is not None:
47 if (not os
.path
.exists(os
.path
.join(targetdir
, "etc"))):
48 os
.makedirs(os
.path
.join(targetdir
, "etc"))
49 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
51 smbconf
= param
.default_path()
53 if not os
.path
.exists(smbconf
):
54 raise ProvisioningError("Unable to find smb.conf ...")
58 paths
= provision_paths_from_lp(lp
,lp
.get("realm"))
62 def find_provision_key_parameters(param
, credentials
, session_info
, paths
, smbconf
):
63 """Get key provision parameters (realm, domain, ...) from a given provision
65 :param param: Param object
66 :param credentials: Credentials for the authentification
67 :param session_info: Session object
68 :param paths: A list of path to provision object
69 :param smbconf: Path to the smb.conf file
70 :return: A list of key provision parameters"""
73 lp
.load(paths
.smbconf
)
74 names
= ProvisionNames()
75 names
.adminpass
= None
76 # NT domain, kerberos realm, root dn, domain dn, domain dns name
77 names
.domain
= string
.upper(lp
.get("workgroup"))
78 names
.realm
= lp
.get("realm")
79 basedn
= "DC=" + names
.realm
.replace(".",",DC=")
80 names
.dnsdomain
= names
.realm
81 names
.realm
= string
.upper(names
.realm
)
83 secrets_ldb
= Ldb(paths
.secrets
, session_info
=session_info
, credentials
=credentials
,lp
=lp
, options
=["modules:samba_secrets"])
84 # Get the netbiosname first (could be obtained from smb.conf in theory)
85 attrs
= ["sAMAccountName"]
86 res
= secrets_ldb
.search(expression
="(flatname=%s)"%names
.domain
,base
="CN=Primary Domains", scope
=SCOPE_SUBTREE
, attrs
=attrs
)
87 names
.netbiosname
= str(res
[0]["sAMAccountName"]).replace("$","")
89 names
.smbconf
= smbconf
90 # It's important here to let ldb load with the old module or it's quite
91 # certain that the LDB won't load ...
92 samdb
= Ldb(paths
.samdb
, session_info
=session_info
,
93 credentials
=credentials
, lp
=lp
, options
=["modules:samba_dsdb"])
95 # That's a bit simplistic but it's ok as long as we have only 3
97 attrs2
= ["defaultNamingContext", "schemaNamingContext","configurationNamingContext","rootDomainNamingContext"]
98 current
= samdb
.search(expression
="(objectClass=*)",base
="", scope
=SCOPE_BASE
, attrs
=attrs2
)
100 names
.configdn
= current
[0]["configurationNamingContext"]
101 configdn
= str(names
.configdn
)
102 names
.schemadn
= current
[0]["schemaNamingContext"]
103 if not (ldb
.Dn(samdb
, basedn
) == (ldb
.Dn(samdb
, current
[0]["defaultNamingContext"][0]))):
104 raise ProvisioningError(("basedn in %s (%s) and from %s (%s) is not the same ..." % (paths
.samdb
, str(current
[0]["defaultNamingContext"][0]), paths
.smbconf
, basedn
)))
106 names
.domaindn
=current
[0]["defaultNamingContext"]
107 names
.rootdn
=current
[0]["rootDomainNamingContext"]
110 res3
= samdb
.search(expression
="(objectClass=*)",base
="CN=Sites,"+configdn
, scope
=SCOPE_ONELEVEL
, attrs
=attrs3
)
111 names
.sitename
= str(res3
[0]["cn"])
113 # dns hostname and server dn
114 attrs4
= ["dNSHostName"]
115 res4
= samdb
.search(expression
="(CN=%s)"%names
.netbiosname
,base
="OU=Domain Controllers,"+basedn
, \
116 scope
=SCOPE_ONELEVEL
, attrs
=attrs4
)
117 names
.hostname
= str(res4
[0]["dNSHostName"]).replace("."+names
.dnsdomain
,"")
119 server_res
= samdb
.search(expression
="serverReference=%s"%res4[0].dn
, attrs
=[], base
=configdn
)
120 names
.serverdn
= server_res
[0].dn
122 # invocation id/objectguid
123 res5
= samdb
.search(expression
="(objectClass=*)",base
="CN=NTDS Settings,%s" % str(names
.serverdn
), scope
=SCOPE_BASE
, attrs
=["invocationID","objectGUID"])
124 names
.invocation
= str(ndr_unpack( misc
.GUID
,res5
[0]["invocationId"][0]))
125 names
.ntdsguid
= str(ndr_unpack( misc
.GUID
,res5
[0]["objectGUID"][0]))
128 attrs6
= ["objectGUID", "objectSid","msDS-Behavior-Version" ]
129 res6
= samdb
.search(expression
="(objectClass=*)",base
=basedn
, scope
=SCOPE_BASE
, attrs
=attrs6
)
130 names
.domainguid
= str(ndr_unpack( misc
.GUID
,res6
[0]["objectGUID"][0]))
131 names
.domainsid
= ndr_unpack( security
.dom_sid
,res6
[0]["objectSid"][0])
132 if res6
[0].get("msDS-Behavior-Version") == None or int(res6
[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000
:
133 names
.domainlevel
= DS_DOMAIN_FUNCTION_2000
135 names
.domainlevel
= int(res6
[0]["msDS-Behavior-Version"][0])
138 attrs7
= ["cn","displayName"]
139 res7
= samdb
.search(expression
="(displayName=Default Domain Policy)",base
="CN=Policies,CN=System,"+basedn
, \
140 scope
=SCOPE_ONELEVEL
, attrs
=attrs7
)
141 names
.policyid
= str(res7
[0]["cn"]).replace("{","").replace("}","")
143 attrs8
= ["cn","displayName"]
144 res8
= samdb
.search(expression
="(displayName=Default Domain Controllers Policy)",base
="CN=Policies,CN=System,"+basedn
, \
145 scope
=SCOPE_ONELEVEL
, attrs
=attrs7
)
147 names
.policyid_dc
= str(res8
[0]["cn"]).replace("{","").replace("}","")
149 names
.policyid_dc
= None
154 def newprovision(names
,setup_dir
,creds
,session
,smbconf
,provdir
,messagefunc
):
155 """Create a new provision.
157 This provision will be the reference for knowing what has changed in the
158 since the latest upgrade in the current provision
160 :param names: List of provision parameters
161 :param setup_dis: Directory where the setup files are stored
162 :param creds: Credentials for the authentification
163 :param session: Session object
164 :param smbconf: Path to the smb.conf file
165 :param provdir: Directory where the provision will be stored
166 :param messagefunc: A function for displaying the message of the provision"""
167 if os
.path
.isdir(provdir
):
168 shutil
.rmtree(provdir
)
169 logstd
=os
.path
.join(provdir
,"log.std")
170 os
.chdir(os
.path
.join(setup_dir
,".."))
172 messagefunc("Provision stored in %s"%provdir
)
173 provision(setup_dir
, messagefunc
,
174 session
, creds
, smbconf
=smbconf
, targetdir
=provdir
,
175 samdb_fill
=FILL_FULL
, realm
=names
.realm
, domain
=names
.domain
,
176 domainguid
=names
.domainguid
, domainsid
=str(names
.domainsid
),ntdsguid
=names
.ntdsguid
,
177 policyguid
=names
.policyid
,policyguid_dc
=names
.policyid_dc
,hostname
=names
.netbiosname
,
178 hostip
=None, hostip6
=None,
179 invocationid
=names
.invocation
, adminpass
=names
.adminpass
,
180 krbtgtpass
=None, machinepass
=None,
181 dnspass
=None, root
=None, nobody
=None,
182 wheel
=None, users
=None,
183 serverrole
="domain controller",
184 ldap_backend_extra_port
=None,
191 dom_for_fun_level
=names
.domainlevel
,
192 ldap_dryrun_mode
=None,useeadb
=True)
195 """Sorts two DNs in the lexicographical order it and put higher level DN before.
197 So given the dns cn=bar,cn=foo and cn=foo the later will be return as smaller
198 :param x: First object to compare
199 :param y: Second object to compare
201 p
= re
.compile(r
'(?<!\\),')
202 tab1
= p
.split(str(x
))
203 tab2
= p
.split(str(y
))
205 if (len(tab1
) > len(tab2
)):
207 elif (len(tab1
) < len(tab2
)):
214 # Note: python range go up to upper limit but do not include it
215 for i
in range(0,min):
216 ret
=cmp(tab1
[len1
-i
],tab2
[len2
-i
])
221 assert len1
!=len2
,"PB PB PB"+space
.join(tab1
)+" / "+space
.join(tab2
)