samba-tool: add a function to cleanly demote a DC
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / netcmd / dsacl.py
blobb68af9ba396635d9bf8020c72caf338c310a3da4
1 #!/usr/bin/env python
3 # Manipulate ACLs on directory objects
5 # Copyright (C) Nadezhda Ivanova <nivanova@samba.org> 2010
6 # Copyright Giampaolo Lauria 2011 <lauria2@yahoo.com>
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.
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.
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/>.
22 import samba.getopt as options
23 from samba.dcerpc import security
24 from samba.samdb import SamDB
25 from samba.ndr import ndr_unpack, ndr_pack
26 from samba.dcerpc.security import (
27 GUID_DRS_ALLOCATE_RIDS, GUID_DRS_CHANGE_DOMAIN_MASTER,
28 GUID_DRS_CHANGE_INFR_MASTER, GUID_DRS_CHANGE_PDC,
29 GUID_DRS_CHANGE_RID_MASTER, GUID_DRS_CHANGE_SCHEMA_MASTER,
30 GUID_DRS_GET_CHANGES, GUID_DRS_GET_ALL_CHANGES,
31 GUID_DRS_GET_FILTERED_ATTRIBUTES, GUID_DRS_MANAGE_TOPOLOGY,
32 GUID_DRS_MONITOR_TOPOLOGY, GUID_DRS_REPL_SYNCRONIZE,
33 GUID_DRS_RO_REPL_SECRET_SYNC)
36 import ldb
37 from ldb import SCOPE_BASE
38 import re
40 from samba.auth import system_session
41 from samba.netcmd import (
42 Command,
43 CommandError,
44 SuperCommand,
45 Option,
50 class cmd_dsacl_set(Command):
51 """Modify access list on a directory object"""
53 synopsis = "%prog [options]"
54 car_help = """ The access control right to allow or deny """
56 takes_options = [
57 Option("-H", "--URL", help="LDB URL for database or target server",
58 type=str, metavar="URL", dest="H"),
59 Option("--car", type="choice", choices=["change-rid",
60 "change-pdc",
61 "change-infrastructure",
62 "change-schema",
63 "change-naming",
64 "allocate_rids",
65 "get-changes",
66 "get-changes-all",
67 "get-changes-filtered",
68 "topology-manage",
69 "topology-monitor",
70 "repl-sync",
71 "ro-repl-secret-sync"],
72 help=car_help),
73 Option("--action", type="choice", choices=["allow", "deny"],
74 help="""Deny or allow access"""),
75 Option("--objectdn", help="DN of the object whose SD to modify",
76 type="string"),
77 Option("--trusteedn", help="DN of the entity that gets access",
78 type="string"),
79 Option("--sddl", help="An ACE or group of ACEs to be added on the object",
80 type="string"),
83 def find_trustee_sid(self, samdb, trusteedn):
84 res = samdb.search(base=trusteedn, expression="(objectClass=*)",
85 scope=SCOPE_BASE)
86 assert(len(res) == 1)
87 return ndr_unpack( security.dom_sid,res[0]["objectSid"][0])
89 def modify_descriptor(self, samdb, object_dn, desc, controls=None):
90 assert(isinstance(desc, security.descriptor))
91 m = ldb.Message()
92 m.dn = ldb.Dn(samdb, object_dn)
93 m["nTSecurityDescriptor"]= ldb.MessageElement(
94 (ndr_pack(desc)), ldb.FLAG_MOD_REPLACE,
95 "nTSecurityDescriptor")
96 samdb.modify(m)
98 def read_descriptor(self, samdb, object_dn):
99 res = samdb.search(base=object_dn, scope=SCOPE_BASE,
100 attrs=["nTSecurityDescriptor"])
101 # we should theoretically always have an SD
102 assert(len(res) == 1)
103 desc = res[0]["nTSecurityDescriptor"][0]
104 return ndr_unpack(security.descriptor, desc)
106 def get_domain_sid(self, samdb):
107 res = samdb.search(base=samdb.domain_dn(),
108 expression="(objectClass=*)", scope=SCOPE_BASE)
109 return ndr_unpack( security.dom_sid,res[0]["objectSid"][0])
111 def add_ace(self, samdb, object_dn, new_ace):
112 """Add new ace explicitly."""
113 desc = self.read_descriptor(samdb, object_dn)
114 desc_sddl = desc.as_sddl(self.get_domain_sid(samdb))
115 #TODO add bindings for descriptor manipulation and get rid of this
116 desc_aces = re.findall("\(.*?\)", desc_sddl)
117 for ace in desc_aces:
118 if ("ID" in ace):
119 desc_sddl = desc_sddl.replace(ace, "")
120 if new_ace in desc_sddl:
121 return
122 if desc_sddl.find("(") >= 0:
123 desc_sddl = desc_sddl[:desc_sddl.index("(")] + new_ace + desc_sddl[desc_sddl.index("("):]
124 else:
125 desc_sddl = desc_sddl + new_ace
126 desc = security.descriptor.from_sddl(desc_sddl, self.get_domain_sid(samdb))
127 self.modify_descriptor(samdb, object_dn, desc)
129 def print_new_acl(self, samdb, object_dn):
130 desc = self.read_descriptor(samdb, object_dn)
131 desc_sddl = desc.as_sddl(self.get_domain_sid(samdb))
132 self.outf.write("new descriptor for %s:\n" % object_dn)
133 self.outf.write(desc_sddl + "\n")
135 def run(self, car, action, objectdn, trusteedn, sddl,
136 H=None, credopts=None, sambaopts=None, versionopts=None):
137 lp = sambaopts.get_loadparm()
138 creds = credopts.get_credentials(lp)
140 if sddl is None and (car is None or action is None
141 or objectdn is None or trusteedn is None):
142 return self.usage()
144 samdb = SamDB(url=H, session_info=system_session(),
145 credentials=creds, lp=lp)
146 cars = {'change-rid' : GUID_DRS_CHANGE_RID_MASTER,
147 'change-pdc' : GUID_DRS_CHANGE_PDC,
148 'change-infrastructure' : GUID_DRS_CHANGE_INFR_MASTER,
149 'change-schema' : GUID_DRS_CHANGE_SCHEMA_MASTER,
150 'change-naming' : GUID_DRS_CHANGE_DOMAIN_MASTER,
151 'allocate_rids' : GUID_DRS_ALLOCATE_RIDS,
152 'get-changes' : GUID_DRS_GET_CHANGES,
153 'get-changes-all' : GUID_DRS_GET_ALL_CHANGES,
154 'get-changes-filtered' : GUID_DRS_GET_FILTERED_ATTRIBUTES,
155 'topology-manage' : GUID_DRS_MANAGE_TOPOLOGY,
156 'topology-monitor' : GUID_DRS_MONITOR_TOPOLOGY,
157 'repl-sync' : GUID_DRS_REPL_SYNCRONIZE,
158 'ro-repl-secret-sync' : GUID_DRS_RO_REPL_SECRET_SYNC,
160 sid = self.find_trustee_sid(samdb, trusteedn)
161 if sddl:
162 new_ace = sddl
163 elif action == "allow":
164 new_ace = "(OA;;CR;%s;;%s)" % (cars[car], str(sid))
165 elif action == "deny":
166 new_ace = "(OD;;CR;%s;;%s)" % (cars[car], str(sid))
167 else:
168 raise CommandError("Wrong argument '%s'!" % action)
170 self.print_new_acl(samdb, objectdn)
171 self.add_ace(samdb, objectdn, new_ace)
172 self.print_new_acl(samdb, objectdn)
175 class cmd_dsacl(SuperCommand):
176 """DS ACLs manipulation"""
178 subcommands = {}
179 subcommands["set"] = cmd_dsacl_set()