s4-drs: split out drs utility python functions
[nivanova/samba-autobuild/.git] / source4 / scripting / python / samba / drs_utils.py
1 #!/usr/bin/env python
2 #
3 # DRS utility code
4 #
5 # Copyright Andrew Tridgell 2010
6 # Copyright Andrew Bartlett 2010
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21
22 from samba.dcerpc import drsuapi, misc
23 from samba.net import Net
24 import samba, ldb
25
26 class drs_Replicate():
27     '''DRS replication calls'''
28
29     def __init__(self, binding_string, lp, creds, samdb):
30         self.drs = drsuapi.drsuapi(binding_string, lp, creds)
31         self.drs_handle = self.drs_DsBind()
32         self.net = Net(creds=creds, lp=lp)
33         self.samdb = samdb
34         self.replication_state = self.net.replicate_init(self.samdb, lp, self.drs)
35
36
37     def drs_DsBind(self):
38         '''make a DsBind call, returning the binding handle'''
39         bind_info = drsuapi.DsBindInfoCtr()
40         bind_info.length = 28
41         bind_info.info = drsuapi.DsBindInfo28()
42         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_BASE;
43         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
44         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
45         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
46         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
47         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
48         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
49         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
50         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
51         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
52         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
53         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
54         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
55         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
56         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
57         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
58         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
59         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
60         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
61         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
62         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
63         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
64         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
65         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
66         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
67         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
68         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
69         bind_info.info.supported_extensions     |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
70         (info, handle) = self.drs.DsBind(misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID), bind_info)
71         return handle
72
73
74     def drs_get_rodc_partial_attribute_set(self):
75         '''get a list of attributes for RODC replication'''
76         partial_attribute_set = drsuapi.DsPartialAttributeSet()
77         partial_attribute_set.version = 1
78
79         attids = []
80
81         # the exact list of attids we send is quite critical. Note that
82         # we do ask for the secret attributes, but set set SPECIAL_SECRET_PROCESSING
83         # to zero them out
84         schema_dn = self.samdb.get_schema_basedn()
85         res = self.samdb.search(base=schema_dn, scope=ldb.SCOPE_SUBTREE,
86                                       expression="objectClass=attributeSchema",
87                                       attrs=["lDAPDisplayName", "systemFlags",
88                                              "searchFlags"])
89
90         for r in res:
91             ldap_display_name = r["lDAPDisplayName"][0]
92             if "systemFlags" in r:
93                 system_flags      = r["systemFlags"][0]
94                 if (int(system_flags) & (samba.dsdb.DS_FLAG_ATTR_NOT_REPLICATED |
95                                          samba.dsdb.DS_FLAG_ATTR_IS_CONSTRUCTED)):
96                     continue
97             search_flags = r["searchFlags"][0]
98             if (int(search_flags) & samba.dsdb.SEARCH_FLAG_RODC_ATTRIBUTE):
99                 continue
100             attid = self.samdb.get_attid_from_lDAPDisplayName(ldap_display_name)
101             attids.append(int(attid))
102
103         # the attids do need to be sorted, or windows doesn't return
104         # all the attributes we need
105         attids.sort()
106         partial_attribute_set.attids         = attids
107         partial_attribute_set.num_attids = len(attids)
108         return partial_attribute_set
109
110
111     def replicate(self, dn, source_dsa_invocation_id, destination_dsa_guid,
112                   schema=False, exop=drsuapi.DRSUAPI_EXOP_NONE):
113         '''replicate a single DN'''
114
115         # setup for a GetNCChanges call
116         req8 = drsuapi.DsGetNCChangesRequest8()
117
118         req8.destination_dsa_guid           = destination_dsa_guid
119         req8.source_dsa_invocation_id       = source_dsa_invocation_id
120         req8.naming_context                 = drsuapi.DsReplicaObjectIdentifier()
121         req8.naming_context.dn              = dn.decode("utf-8")
122         req8.highwatermark                  = drsuapi.DsReplicaHighWaterMark()
123         req8.highwatermark.tmp_highest_usn  = 0
124         req8.highwatermark.reserved_usn     = 0
125         req8.highwatermark.highest_usn      = 0
126         req8.uptodateness_vector            = None
127         if exop == drsuapi.DRSUAPI_EXOP_REPL_SECRET:
128             req8.replica_flags              = 0
129         else:
130             req8.replica_flags              =  (drsuapi.DRSUAPI_DRS_INIT_SYNC |
131                                                 drsuapi.DRSUAPI_DRS_PER_SYNC |
132                                                 drsuapi.DRSUAPI_DRS_GET_ANC |
133                                                 drsuapi.DRSUAPI_DRS_NEVER_SYNCED |
134                                                 drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING)
135         req8.max_object_count                = 402
136         req8.max_ndr_size                    = 402116
137         req8.extended_op                     = exop
138         req8.fsmo_info                       = 0
139         req8.partial_attribute_set           = None
140         req8.partial_attribute_set_ex        = None
141         req8.mapping_ctr.num_mappings        = 0
142         req8.mapping_ctr.mappings            = None
143
144         if not schema:
145             req8.partial_attribute_set = self.drs_get_rodc_partial_attribute_set()
146
147         while True:
148             (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 8, req8)
149             self.net.replicate_chunk(self.replication_state, level, ctr, schema=schema)
150             if ctr.more_data == 0:
151                 break
152             req8.highwatermark.tmp_highest_usn = ctr.new_highwatermark.tmp_highest_usn