1 # Manipulate ACLs on directory objects
3 # Copyright (C) William Brown <william@blackhats.net.au> 2018
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 import samba.getopt as options
21 from samba.ms_schema import bitFields
22 from samba.auth import system_session
23 from samba.samdb import SamDB
24 from samba.netcmd import (
31 class cmd_schema_attribute_modify(Command):
32 """Modify attribute settings in the schema partition.
34 This commands allows minor modifications to attributes in the schema. Active
35 Directory does not allow many changes to schema, but important modifications
36 are related to indexing. This command overwrites the value of searchflags,
37 so be sure to view the current content before making changes.
40 samba-tool schema attribute modify uid \
41 --searchflags="fATTINDEX,fPRESERVEONDELETE"
43 This alters the uid attribute to be indexed and to be preserved when
44 converted to a tombstone.
46 Important search flag values are:
48 fATTINDEX: create an equality index for this attribute.
49 fPDNTATTINDEX: create a container index for this attribute (ie OU).
50 fANR: specify that this attribute is a member of the ambiguous name
52 fPRESERVEONDELETE: indicate that the value of this attribute should be
53 preserved when the object is converted to a tombstone (deleted).
54 fCOPY: hint to clients that this attribute should be copied.
55 fTUPLEINDEX: create a tuple index for this attribute. This is used in
57 fSUBTREEATTINDEX: create a browsing index for this attribute. VLV searches
59 fCONFIDENTIAL: indicate that the attribute is confidental and requires
60 special access checks.
61 fNEVERVALUEAUDIT: indicate that changes to this value should NOT be audited.
62 fRODCFILTEREDATTRIBUTE: indicate that this value should not be replicated to
64 fEXTENDEDLINKTRACKING: indicate to the DC to perform extra link tracking.
65 fBASEONLY: indicate that this attribute should only be displayed when the
66 search scope of the query is SCOPE_BASE or a single object result.
67 fPARTITIONSECRET: indicate that this attribute is a partition secret and
68 requires special access checks.
70 The authoritative source of this information is the MS-ADTS.
72 synopsis = "%prog attribute [options]"
74 takes_optiongroups = {
75 "sambaopts": options.SambaOptions,
76 "versionopts": options.VersionOptions,
77 "credopts": options.CredentialsOptions,
81 Option("--searchflags", help="Search Flags for the attribute", type=str),
82 Option("-H", "--URL", help="LDB URL for database or target server",
83 type=str, metavar="URL", dest="H"),
86 takes_args = ["attribute"]
88 def run(self, attribute, H=None, credopts=None, sambaopts=None,
89 versionopts=None, searchflags=None):
91 if searchflags is None:
92 raise CommandError('A value to modify must be provided.')
94 # Parse the search flags to a set of bits to modify.
96 searchflags_int = None
97 if searchflags is not None:
99 flags = searchflags.split(',')
100 # We have to normalise all the values. To achieve this predictably
101 # we title case (Fattrindex), then swapcase (fATTINDEX)
102 flags = [ x.capitalize().swapcase() for x in flags ]
104 if flag not in bitFields['searchflags'].keys():
105 raise CommandError("Unknown flag '%s', please see --help" % flag)
106 bit_loc = 31 - bitFields['searchflags'][flag]
108 searchflags_int = searchflags_int | (1 << bit_loc)
110 lp = sambaopts.get_loadparm()
111 creds = credopts.get_credentials(lp)
113 samdb = SamDB(url=H, session_info=system_session(),
114 credentials=creds, lp=lp)
116 schema_dn = samdb.schema_dn()
117 # For now we make assumptions about the CN
118 attr_dn = 'cn=%s,%s' % (attribute, schema_dn)
121 m.dn = ldb.Dn(samdb, attr_dn)
123 if searchflags_int is not None:
124 m['searchFlags'] = ldb.MessageElement(
125 str(searchflags_int), ldb.FLAG_MOD_REPLACE, 'searchFlags')
128 samdb.set_schema_update_now()
129 self.outf.write("modified %s" % attr_dn)
131 class cmd_schema_attribute_show(Command):
132 """Show details about an attribute from the schema.
134 Schema attribute definitions define and control the behaviour of directory
135 attributes on objects. This displays the details of a single attribute.
137 synopsis = "%prog attribute [options]"
139 takes_optiongroups = {
140 "sambaopts": options.SambaOptions,
141 "versionopts": options.VersionOptions,
142 "credopts": options.CredentialsOptions,
146 Option("-H", "--URL", help="LDB URL for database or target server",
147 type=str, metavar="URL", dest="H"),
150 takes_args = ["attribute"]
152 def run(self, attribute, H=None, credopts=None, sambaopts=None, versionopts=None):
153 lp = sambaopts.get_loadparm()
154 creds = credopts.get_credentials(lp)
156 samdb = SamDB(url=H, session_info=system_session(),
157 credentials=creds, lp=lp)
159 schema_dn = samdb.schema_dn()
161 filt = '(&(objectClass=attributeSchema)(|(lDAPDisplayName={0})(cn={0})(name={0})))'.format(attribute)
163 res = samdb.search(base=schema_dn, scope=ldb.SCOPE_SUBTREE,
167 raise CommandError('No schema objects matched "%s"' % attribute)
169 raise CommandError('Multiple schema objects matched "%s": this is a serious issue you should report!' % attribute)
171 # Get the content of searchFlags (if any) and manipulate them to
172 # show our friendly names.
174 # WARNING: If you are reading this in the future trying to change an
175 # ldb message dynamically, and wondering why you get an operations
176 # error, it's related to talloc references.
178 # When you create *any* python reference, IE:
179 # flags = res[0]['attr']
180 # this creates a talloc_reference that may live forever due to pythons
181 # memory management model. However, when you create this reference it
182 # blocks talloc_realloc from functions in msg.add(element).
184 # As a result, you MUST avoid ALL new variable references UNTIL you have
185 # modified the message as required, even if it makes your code more
188 if 'searchFlags' in res[0].keys():
192 flags_i = int(str(res[0]['searchFlags']))
194 raise CommandError('Invalid schemaFlags value "%s": this is a serious issue you should report!' % res[0]['searchFlags'])
195 # Work out what keys we have.
197 for flag in bitFields['searchflags'].keys():
198 if flags_i & (1 << (31 - bitFields['searchflags'][flag])) != 0:
201 res[0].add(ldb.MessageElement(out, ldb.FLAG_MOD_ADD, 'searchFlagsDecoded'))
203 user_ldif = samdb.write_ldif(res[0], ldb.CHANGETYPE_NONE)
204 self.outf.write(user_ldif)
206 class cmd_schema_attribute_show_oc(Command):
207 """Show what objectclasses MAY or MUST contain an attribute.
209 This is useful to determine "if I need uid, what objectclasses could be
210 applied to achieve this."
212 synopsis = "%prog attribute [options]"
214 takes_optiongroups = {
215 "sambaopts": options.SambaOptions,
216 "versionopts": options.VersionOptions,
217 "credopts": options.CredentialsOptions,
221 Option("-H", "--URL", help="LDB URL for database or target server",
222 type=str, metavar="URL", dest="H"),
225 takes_args = ["attribute"]
227 def run(self, attribute, H=None, credopts=None, sambaopts=None, versionopts=None):
228 lp = sambaopts.get_loadparm()
229 creds = credopts.get_credentials(lp)
231 samdb = SamDB(url=H, session_info=system_session(),
232 credentials=creds, lp=lp)
234 schema_dn = samdb.schema_dn()
236 may_filt = '(&(objectClass=classSchema)' \
237 '(|(mayContain={0})(systemMayContain={0})))'.format(attribute)
238 must_filt = '(&(objectClass=classSchema)' \
239 '(|(mustContain={0})(systemMustContain={0})))'.format(attribute)
241 may_res = samdb.search(base=schema_dn, scope=ldb.SCOPE_SUBTREE,
242 expression=may_filt, attrs=['cn'])
243 must_res = samdb.search(base=schema_dn, scope=ldb.SCOPE_SUBTREE,
244 expression=must_filt, attrs=['cn'])
246 self.outf.write('--- MAY contain ---\n')
248 self.outf.write('%s\n' % msg['cn'][0])
250 self.outf.write('--- MUST contain ---\n')
252 self.outf.write('%s\n' % msg['cn'][0])
255 class cmd_schema_objectclass_show(Command):
256 """Show details about an objectClass from the schema.
258 Schema objectClass definitions define and control the behaviour of directory
259 objects including what attributes they may contain. This displays the
260 details of an objectClass.
262 synopsis = "%prog objectclass [options]"
264 takes_optiongroups = {
265 "sambaopts": options.SambaOptions,
266 "versionopts": options.VersionOptions,
267 "credopts": options.CredentialsOptions,
271 Option("-H", "--URL", help="LDB URL for database or target server",
272 type=str, metavar="URL", dest="H"),
275 takes_args = ["objectclass"]
277 def run(self, objectclass, H=None, credopts=None, sambaopts=None, versionopts=None):
278 lp = sambaopts.get_loadparm()
279 creds = credopts.get_credentials(lp)
281 samdb = SamDB(url=H, session_info=system_session(),
282 credentials=creds, lp=lp)
284 schema_dn = samdb.schema_dn()
286 filt = '(&(objectClass=classSchema)' \
287 '(|(lDAPDisplayName={0})(cn={0})(name={0})))'.format(objectclass)
289 res = samdb.search(base=schema_dn, scope=ldb.SCOPE_SUBTREE,
293 user_ldif = samdb.write_ldif(msg, ldb.CHANGETYPE_NONE)
294 self.outf.write(user_ldif)
296 class cmd_schema_attribute(SuperCommand):
297 """Query and manage attributes in the schema partition."""
299 subcommands["modify"] = cmd_schema_attribute_modify()
300 subcommands["show"] = cmd_schema_attribute_show()
301 subcommands["show_oc"] = cmd_schema_attribute_show_oc()
303 class cmd_schema_objectclass(SuperCommand):
304 """Query and manage objectclasses in the schema partition."""
306 subcommands["show"] = cmd_schema_objectclass_show()
308 class cmd_schema(SuperCommand):
309 """Schema querying and management."""
312 subcommands["attribute"] = cmd_schema_attribute()
313 subcommands["objectclass"] = cmd_schema_objectclass()