s4-net: added initial implemention of RODC join
[nivanova/samba-autobuild/.git] / source4 / scripting / python / samba / join.py
1 #!/usr/bin/env python
2 #
3 # python join code
4 # Copyright Andrew Tridgell 2010
5 # Copyright Andrew Bartlett 2010
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20
21 import samba.getopt as options
22 from samba.auth import system_session
23 from samba.samdb import SamDB
24 from samba import gensec
25 import ldb, samba
26 from samba.ndr import ndr_pack, ndr_unpack, ndr_print
27 from samba.dcerpc import security
28 from samba.dcerpc import drsuapi, misc, netlogon
29 from samba.credentials import Credentials, DONT_USE_KERBEROS
30 from samba.provision import secretsdb_self_join
31
32 def join_rodc(server=None, creds=None, lp=None, site=None, netbios_name=None):
33     """join as a RODC"""
34
35     if server is None:
36         raise Exception("You must supply a server for a RODC join")
37
38     def del_noerror(samdb, dn):
39         try:
40             samdb.delete(dn)
41             print "Deleted %s" % dn
42         except:
43             pass
44
45     def cleanup_old_join(samdb, acct_dn, server_dn, ntds_dn,
46                          krbtgt_dn, connection_dn, topology_dn):
47         try:
48             # find the krbtgt link
49             res = samdb.search(base=acct_dn, scope=ldb.SCOPE_BASE, attrs=["msDS-krbTgtLink"])
50             del_noerror(samdb, acct_dn)
51             del_noerror(samdb, connection_dn)
52             del_noerror(samdb, krbtgt_dn)
53             del_noerror(samdb, ntds_dn)
54             del_noerror(samdb, server_dn)
55             del_noerror(samdb, topology_dn)
56             new_krbtgt_dn = res[0]["msDS-Krbtgtlink"][0]
57             del_noerror(samdb, new_krbtgt_dn)
58         except:
59             pass
60
61     def get_dsServiceName(samdb):
62         res = samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"])
63         return res[0]["dsServiceName"][0]
64
65     def get_dnsHostName(samdb):
66         res = samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dnsHostName"])
67         return res[0]["dnsHostName"][0]
68
69     def get_mysid(samdb):
70         res = samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["tokenGroups"])
71         binsid = res[0]["tokenGroups"][0]
72         return samdb.schema_format_value("objectSID", binsid)
73
74     def do_DsBind(drs):
75         '''make a DsBind call, returning the binding handle'''
76         bind_info = drsuapi.DsBindInfoCtr()
77         bind_info.length = 28
78         bind_info.info = drsuapi.DsBindInfo28()
79         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_BASE;
80         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
81         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
82         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
83         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
84         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
85         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
86         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
87         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
88         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
89         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
90         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
91         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
92         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
93         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
94         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
95         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
96         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
97         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
98         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
99         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
100         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
101         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
102         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
103         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
104         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
105         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
106         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
107         (info, handle) = drs.DsBind(misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID), bind_info)
108         return handle
109
110
111     creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
112
113     samdb = SamDB(url="ldap://%s" % server,
114                   session_info=system_session(),
115                   credentials=creds, lp=lp)
116
117     myname = netbios_name
118     samname = "%s$" % myname
119     base_dn = str(samdb.get_default_basedn())
120     domsid = samdb.get_domain_sid()
121     dc_ntds_dn = get_dsServiceName(samdb)
122     dc_dnsHostName = get_dnsHostName(samdb)
123     acct_pass = samba.generate_random_password(12, 32)
124     mysid = get_mysid(samdb)
125
126     # work out the DNs of all the objects we will be adding
127     admin_dn = "<SID=%s>" % mysid
128     krbtgt_dn = "CN=krbtgt_%s,CN=Users,%s" % (myname, base_dn)
129     server_dn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (myname, site, samdb.get_config_basedn())
130     ntds_dn = "CN=NTDS Settings,%s" % server_dn
131     connection_dn = "CN=RODC Connection (FRS),%s" % ntds_dn
132     topology_dn = "CN=%s,CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,CN=System,%s" % (myname, base_dn)
133
134     never_reveal_sid = "%s-572" % domsid;
135     reveal_sid = "%s-571" % domsid;
136
137     dnsdomain = ldb.Dn(samdb, base_dn).canonical_str().split('/')[0]
138     dnshostname = "%s.%s" % (myname, dnsdomain)
139
140     acct_dn = "CN=%s,OU=Domain Controllers,%s" % (myname, base_dn)
141
142     cleanup_old_join(samdb, acct_dn, server_dn, ntds_dn,
143                           krbtgt_dn, connection_dn, topology_dn)
144
145     print "Adding %s" % acct_dn
146     rec = {
147         "dn" : acct_dn,
148         "objectClass": "computer",
149         "displayname": samname,
150         "samaccountname" : samname,
151         "useraccountcontrol" : "83890176",
152         "managedby" : admin_dn,
153         "dnshostname" : dnshostname,
154         "msDS-NeverRevealGroup" : "<SID=%s>" % never_reveal_sid,
155         "msDS-RevealOnDemandGroup" : "<SID=%s>" % reveal_sid}
156     samdb.add(rec)
157
158     print "Adding %s" % krbtgt_dn
159     rec = {
160         "dn" : krbtgt_dn,
161         "objectclass" : "user",
162         "useraccountcontrol" : "514",
163         "showinadvancedviewonly" : "TRUE",
164         "description" : "tricky account"}
165     samdb.add(rec, ["rodc_join:1:1"])
166
167     # now we need to search for the samAccountName attribute on the krbtgt DN,
168     # as this will have been magically set to the krbtgt number
169     res = samdb.search(base=krbtgt_dn, scope=ldb.SCOPE_BASE, attrs=["samAccountName"])
170     krbtgt_name = res[0]["samAccountName"][0]
171
172     print "Got krbtgt_name=%s" % krbtgt_name
173
174     m = ldb.Message()
175     m.dn = ldb.Dn(samdb, acct_dn)
176     m["msDS-krbTgtLink"] = ldb.MessageElement(krbtgt_dn,
177                                               ldb.FLAG_MOD_REPLACE, "msDS-krbTgtLink")
178     samdb.modify(m)
179
180     new_krbtgt_dn = "CN=%s,CN=Users,%s" % (krbtgt_name, base_dn)
181     print "Renaming %s to %s" % (krbtgt_dn, new_krbtgt_dn)
182     samdb.rename(krbtgt_dn, new_krbtgt_dn)
183
184     print "Adding %s" % server_dn
185     rec = {
186         "dn": server_dn,
187         "objectclass" : "server",
188         "systemFlags" : "1375731712",
189         "serverReference" : acct_dn,
190         "dnsHostName" : dnshostname}
191     samdb.add(rec)
192
193     print "Adding %s" % ntds_dn
194     rec = {
195         "dn" : ntds_dn,
196         "objectclass" : "nTDSDSA",
197         "objectCategory" : "CN=NTDS-DSA-RO,%s" % samdb.get_schema_basedn(),
198         "systemFlags" : "33554432",
199         "dMDLocation" : str(samdb.get_schema_basedn()),
200         "options" : "37",
201         "msDS-Behavior-Version" : "4",
202         "msDS-HasDomainNCs" : str(samdb.get_default_basedn()),
203         "msDS-HasFullReplicaNCs" : [ str(samdb.get_default_basedn()),
204                                      str(samdb.get_config_basedn()),
205                                      str(samdb.get_schema_basedn()) ]}
206     samdb.add(rec, ["rodc_join:1:1"])
207
208     print "Adding %s" % connection_dn
209     rec = {
210         "dn" : connection_dn,
211         "objectclass" : "nTDSConnection",
212         "enabledconnection" : "TRUE",
213         "options" : "65",
214         "fromServer" : dc_ntds_dn}
215     samdb.add(rec)
216
217     print "Adding %s" % topology_dn
218     rec = {
219         "dn" : topology_dn,
220         "objectclass" : "msDFSR-Member",
221         "msDFSR-ComputerReference" : acct_dn,
222         "serverReference" : ntds_dn}
223     samdb.add(rec)
224
225     print "Adding HOST SPNs to %s" % acct_dn
226     m = ldb.Message()
227     m.dn = ldb.Dn(samdb, acct_dn)
228     SPNs = [ "HOST/%s" % myname,
229              "HOST/%s" % dnshostname ]
230     m["servicePrincipalName"] = ldb.MessageElement(SPNs,
231                                                    ldb.FLAG_MOD_ADD,
232                                                    "servicePrincipalName")
233     samdb.modify(m)
234
235     print "Adding RestrictedKrbHost SPNs to %s" % acct_dn
236     m = ldb.Message()
237     m.dn = ldb.Dn(samdb, acct_dn)
238     SPNs = [ "RestrictedKrbHost/%s" % myname,
239              "RestrictedKrbHost/%s" % dnshostname ]
240     m["servicePrincipalName"] = ldb.MessageElement(SPNs,
241                                                    ldb.FLAG_MOD_ADD,
242                                                    "servicePrincipalName")
243     samdb.modify(m)
244
245     print "Setting account password for %s" % samname
246     samdb.setpassword("(&(objectClass=user)(sAMAccountName=%s))" % samname,
247                            acct_pass,
248                            force_change_at_next_login=False,
249                            username=samname)
250
251     print "Enabling account %s" % acct_dn
252     # weird, its already enabled, but w2k8r2 disables then re-enables again
253     m = ldb.Message()
254     m.dn = ldb.Dn(samdb, acct_dn)
255     m["userAccountControl"] = ldb.MessageElement("83890178",
256                                                  ldb.FLAG_MOD_REPLACE,
257                                                  "userAccountControl")
258     samdb.modify(m)
259
260     m["userAccountControl"] = ldb.MessageElement("83890176",
261                                                  ldb.FLAG_MOD_REPLACE,
262                                                  "userAccountControl")
263     samdb.modify(m)
264
265     print "Doing DsBind as %s" % samname
266
267     acct_creds = Credentials()
268     acct_creds.guess(lp)
269     acct_creds.set_kerberos_state(DONT_USE_KERBEROS)
270     acct_creds.set_username(samname)
271     acct_creds.set_password(acct_pass)
272
273     drs = drsuapi.drsuapi("ncacn_ip_tcp:w2k8[seal,print]", lp, acct_creds)
274     drs_handle = do_DsBind(drs)
275     print "DRS Handle: %s" % drs_handle
276
277     print "Calling DsRGetDCNameEx2"
278     netr = netlogon.netlogon("ncacn_np:w2k8[print]", lp, acct_creds)
279     dcname = netr.netr_DsRGetDCNameEx2(server_unc=dc_dnsHostName.decode("utf-8"),
280                                             client_account=None,
281                                             mask=0,
282                                             domain_name=dnsdomain.decode("utf-8"),
283                                             domain_guid=None, site_name=None, flags=0x40001020)
284
285     print "Calling secrets self join"
286     secretsdb_self_join()
287
288     print "Note: RODC join is a work in progress - replication not done"
289     #print ndr_print(dcname)