1 | #!/usr/bin/env python
|
---|
2 | # -*- coding: utf-8 -*-
|
---|
3 | #
|
---|
4 | # script to verify cached prefixMap on remote
|
---|
5 | # server against the prefixMap stored in Schema NC
|
---|
6 | #
|
---|
7 | # Copyright (C) Kamen Mazdrashki <kamenim@samba.org> 2010
|
---|
8 | #
|
---|
9 | # This program is free software; you can redistribute it and/or modify
|
---|
10 | # it under the terms of the GNU General Public License as published by
|
---|
11 | # the Free Software Foundation; either version 3 of the License, or
|
---|
12 | # (at your option) any later version.
|
---|
13 | #
|
---|
14 | # This program is distributed in the hope that it will be useful,
|
---|
15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
17 | # GNU General Public License for more details.
|
---|
18 | #
|
---|
19 | # You should have received a copy of the GNU General Public License
|
---|
20 | # along with this program. If not, see <http://www.gnu.org/licenses/>.
|
---|
21 | #
|
---|
22 |
|
---|
23 | import os
|
---|
24 | import sys
|
---|
25 | from optparse import OptionParser
|
---|
26 |
|
---|
27 | sys.path.insert(0, "bin/python")
|
---|
28 |
|
---|
29 | import samba
|
---|
30 | import samba.getopt as options
|
---|
31 | from ldb import SCOPE_BASE, SCOPE_SUBTREE
|
---|
32 | from samba.dcerpc import drsuapi, misc, drsblobs
|
---|
33 | from samba.drs_utils import drs_DsBind
|
---|
34 | from samba.samdb import SamDB
|
---|
35 | from samba.auth import system_session
|
---|
36 | from samba.ndr import ndr_pack, ndr_unpack
|
---|
37 |
|
---|
38 |
|
---|
39 | def _samdb_fetch_pfm(samdb):
|
---|
40 | """Fetch prefixMap stored in SamDB using LDB connection"""
|
---|
41 | res = samdb.search(base=samdb.get_schema_basedn(), expression="", scope=SCOPE_BASE, attrs=["*"])
|
---|
42 | assert len(res) == 1
|
---|
43 | pfm = ndr_unpack(drsblobs.prefixMapBlob,
|
---|
44 | str(res[0]['prefixMap']))
|
---|
45 |
|
---|
46 | pfm_schi = _samdb_fetch_schi(samdb)
|
---|
47 |
|
---|
48 | return (pfm.ctr, pfm_schi)
|
---|
49 |
|
---|
50 | def _samdb_fetch_schi(samdb):
|
---|
51 | """Fetch schemaInfo stored in SamDB using LDB connection"""
|
---|
52 | res = samdb.search(base=samdb.get_schema_basedn(), expression="", scope=SCOPE_BASE, attrs=["*"])
|
---|
53 | assert len(res) == 1
|
---|
54 | if 'schemaInfo' in res[0]:
|
---|
55 | pfm_schi = ndr_unpack(drsblobs.schemaInfoBlob,
|
---|
56 | str(res[0]['schemaInfo']))
|
---|
57 | else:
|
---|
58 | pfm_schi = drsblobs.schemaInfoBlob()
|
---|
59 | pfm_schi.marker = 0xFF;
|
---|
60 | return pfm_schi
|
---|
61 |
|
---|
62 | def _drs_fetch_pfm(server, samdb, creds, lp):
|
---|
63 | """Fetch prefixMap using DRS interface"""
|
---|
64 | binding_str = "ncacn_ip_tcp:%s[print,seal]" % server
|
---|
65 |
|
---|
66 | drs = drsuapi.drsuapi(binding_str, lp, creds)
|
---|
67 | (drs_handle, supported_extensions) = drs_DsBind(drs)
|
---|
68 | print "DRS Handle: %s" % drs_handle
|
---|
69 |
|
---|
70 | req8 = drsuapi.DsGetNCChangesRequest8()
|
---|
71 |
|
---|
72 | dest_dsa = misc.GUID("9c637462-5b8c-4467-aef2-bdb1f57bc4ef")
|
---|
73 | replica_flags = 0
|
---|
74 |
|
---|
75 | req8.destination_dsa_guid = dest_dsa
|
---|
76 | req8.source_dsa_invocation_id = misc.GUID(samdb.get_invocation_id())
|
---|
77 | req8.naming_context = drsuapi.DsReplicaObjectIdentifier()
|
---|
78 | req8.naming_context.dn = unicode(samdb.get_schema_basedn())
|
---|
79 | req8.highwatermark = drsuapi.DsReplicaHighWaterMark()
|
---|
80 | req8.highwatermark.tmp_highest_usn = 0
|
---|
81 | req8.highwatermark.reserved_usn = 0
|
---|
82 | req8.highwatermark.highest_usn = 0
|
---|
83 | req8.uptodateness_vector = None
|
---|
84 | req8.replica_flags = replica_flags
|
---|
85 | req8.max_object_count = 0
|
---|
86 | req8.max_ndr_size = 402116
|
---|
87 | req8.extended_op = 0
|
---|
88 | req8.fsmo_info = 0
|
---|
89 | req8.partial_attribute_set = None
|
---|
90 | req8.partial_attribute_set_ex = None
|
---|
91 | req8.mapping_ctr.num_mappings = 0
|
---|
92 | req8.mapping_ctr.mappings = None
|
---|
93 |
|
---|
94 | (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
|
---|
95 | pfm = ctr.mapping_ctr
|
---|
96 | # check for schemaInfo element
|
---|
97 | pfm_it = pfm.mappings[-1]
|
---|
98 | assert pfm_it.id_prefix == 0
|
---|
99 | assert pfm_it.oid.length == 21
|
---|
100 | s = ''
|
---|
101 | for x in pfm_it.oid.binary_oid:
|
---|
102 | s += chr(x)
|
---|
103 | pfm_schi = ndr_unpack(drsblobs.schemaInfoBlob, s)
|
---|
104 | assert pfm_schi.marker == 0xFF
|
---|
105 | # remove schemaInfo element
|
---|
106 | pfm.num_mappings -= 1
|
---|
107 | return (pfm, pfm_schi)
|
---|
108 |
|
---|
109 | def _pfm_verify(drs_pfm, ldb_pfm):
|
---|
110 | errors = []
|
---|
111 | if drs_pfm.num_mappings != ldb_pfm.num_mappings:
|
---|
112 | errors.append("Different count of prefixes: drs = %d, ldb = %d"
|
---|
113 | % (drs_pfm.num_mappings, ldb_pfm.num_mappings))
|
---|
114 | count = min(drs_pfm.num_mappings, ldb_pfm.num_mappings)
|
---|
115 | for i in range(0, count):
|
---|
116 | it_err = []
|
---|
117 | drs_it = drs_pfm.mappings[i]
|
---|
118 | ldb_it = ldb_pfm.mappings[i]
|
---|
119 | if drs_it.id_prefix != ldb_it.id_prefix:
|
---|
120 | it_err.append("id_prefix")
|
---|
121 | if drs_it.oid.length != ldb_it.oid.length:
|
---|
122 | it_err.append("oid.length")
|
---|
123 | if drs_it.oid.binary_oid != ldb_it.oid.binary_oid:
|
---|
124 | it_err.append("oid.binary_oid")
|
---|
125 | if len(it_err):
|
---|
126 | errors.append("[%2d] differences in (%s)" % (i, it_err))
|
---|
127 | return errors
|
---|
128 |
|
---|
129 | def _pfm_schi_verify(drs_schi, ldb_schi):
|
---|
130 | errors = []
|
---|
131 | print drs_schi.revision
|
---|
132 | print drs_schi.invocation_id
|
---|
133 | if drs_schi.marker != ldb_schi.marker:
|
---|
134 | errors.append("Different marker in schemaInfo: drs = %d, ldb = %d"
|
---|
135 | % (drs_schi.marker, ldb_schi.marker))
|
---|
136 | if drs_schi.revision != ldb_schi.revision:
|
---|
137 | errors.append("Different revision in schemaInfo: drs = %d, ldb = %d"
|
---|
138 | % (drs_schi.revision, ldb_schi.revision))
|
---|
139 | if drs_schi.invocation_id != ldb_schi.invocation_id:
|
---|
140 | errors.append("Different invocation_id in schemaInfo: drs = %s, ldb = %s"
|
---|
141 | % (drs_schi.invocation_id, ldb_schi.invocation_id))
|
---|
142 | return errors
|
---|
143 |
|
---|
144 | ########### main code ###########
|
---|
145 | if __name__ == "__main__":
|
---|
146 | # command line parsing
|
---|
147 | parser = OptionParser("pfm_verify.py [options] server")
|
---|
148 | sambaopts = options.SambaOptions(parser)
|
---|
149 | parser.add_option_group(sambaopts)
|
---|
150 | credopts = options.CredentialsOptionsDouble(parser)
|
---|
151 | parser.add_option_group(credopts)
|
---|
152 |
|
---|
153 | (opts, args) = parser.parse_args()
|
---|
154 |
|
---|
155 | lp = sambaopts.get_loadparm()
|
---|
156 | creds = credopts.get_credentials(lp)
|
---|
157 |
|
---|
158 | if len(args) != 1:
|
---|
159 | import os
|
---|
160 | if not "DC_SERVER" in os.environ.keys():
|
---|
161 | parser.error("You must supply a server")
|
---|
162 | args.append(os.environ["DC_SERVER"])
|
---|
163 |
|
---|
164 | if creds.is_anonymous():
|
---|
165 | parser.error("You must supply credentials")
|
---|
166 | pass
|
---|
167 |
|
---|
168 | server = args[0]
|
---|
169 |
|
---|
170 | samdb = SamDB(url="ldap://%s" % server,
|
---|
171 | session_info=system_session(lp),
|
---|
172 | credentials=creds, lp=lp)
|
---|
173 |
|
---|
174 | exit_code = 0
|
---|
175 | (drs_pfm, drs_schi) = _drs_fetch_pfm(server, samdb, creds, lp)
|
---|
176 | (ldb_pfm, ldb_schi) = _samdb_fetch_pfm(samdb)
|
---|
177 | # verify prefixMaps
|
---|
178 | errors = _pfm_verify(drs_pfm, ldb_pfm)
|
---|
179 | if len(errors):
|
---|
180 | print "prefixMap verification errors:"
|
---|
181 | print "%s" % errors
|
---|
182 | exit_code = 1
|
---|
183 | # verify schemaInfos
|
---|
184 | errors = _pfm_schi_verify(drs_schi, ldb_schi)
|
---|
185 | if len(errors):
|
---|
186 | print "schemaInfo verification errors:"
|
---|
187 | print "%s" % errors
|
---|
188 | exit_code = 2
|
---|
189 |
|
---|
190 | if exit_code != 0:
|
---|
191 | sys.exit(exit_code)
|
---|