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)
37 from ldb import SCOPE_BASE
40 from samba.auth import system_session
41 from samba.netcmd import (
48 class cmd_ds_acl_set(Command):
49 """Modify access list on a directory object"""
51 synopsis = "set --objectdn=objectdn --car=control right --action=[deny|allow] --trusteedn=trustee-dn"
52 car_help = """ The access control right to allow or deny """
55 Option("-H", "--URL", help="LDB URL for database or target server",
56 type=str, metavar="URL", dest="H"),
57 Option("--car", type="choice", choices=["change-rid",
59 "change-infrastructure",
65 "get-changes-filtered",
69 "ro-repl-secret-sync"],
71 Option("--action", type="choice", choices=["allow", "deny"],
72 help="""Deny or allow access"""),
73 Option("--objectdn", help="DN of the object whose SD to modify",
75 Option("--trusteedn", help="DN of the entity that gets access",
77 Option("--sddl", help="An ACE or group of ACEs to be added on the object",
81 def find_trustee_sid(self, samdb, trusteedn):
82 res = samdb.search(base=trusteedn, expression="(objectClass=*)",
85 return ndr_unpack( security.dom_sid,res[0]["objectSid"][0])
87 def modify_descriptor(self, samdb, object_dn, desc, controls=None):
88 assert(isinstance(desc, security.descriptor))
90 m.dn = ldb.Dn(samdb, object_dn)
91 m["nTSecurityDescriptor"]= ldb.MessageElement(
92 (ndr_pack(desc)), ldb.FLAG_MOD_REPLACE,
93 "nTSecurityDescriptor")
96 def read_descriptor(self, samdb, object_dn):
97 res = samdb.search(base=object_dn, scope=SCOPE_BASE,
98 attrs=["nTSecurityDescriptor"])
99 # we should theoretically always have an SD
100 assert(len(res) == 1)
101 desc = res[0]["nTSecurityDescriptor"][0]
102 return ndr_unpack(security.descriptor, desc)
104 def get_domain_sid(self, samdb):
105 res = samdb.search(base=samdb.domain_dn(),
106 expression="(objectClass=*)", scope=SCOPE_BASE)
107 return ndr_unpack( security.dom_sid,res[0]["objectSid"][0])
109 def add_ace(self, samdb, object_dn, new_ace):
110 """Add new ace explicitly."""
111 desc = self.read_descriptor(samdb, object_dn)
112 desc_sddl = desc.as_sddl(self.get_domain_sid(samdb))
113 #TODO add bindings for descriptor manipulation and get rid of this
114 desc_aces = re.findall("\(.*?\)", desc_sddl)
115 for ace in desc_aces:
117 desc_sddl = desc_sddl.replace(ace, "")
118 if new_ace in desc_sddl:
120 if desc_sddl.find("(") >= 0:
121 desc_sddl = desc_sddl[:desc_sddl.index("(")] + new_ace + desc_sddl[desc_sddl.index("("):]
123 desc_sddl = desc_sddl + new_ace
124 desc = security.descriptor.from_sddl(desc_sddl, self.get_domain_sid(samdb))
125 self.modify_descriptor(samdb, object_dn, desc)
127 def print_new_acl(self, samdb, object_dn):
128 desc = self.read_descriptor(samdb, object_dn)
129 desc_sddl = desc.as_sddl(self.get_domain_sid(samdb))
130 print "new descriptor for %s:" % object_dn
133 def run(self, car, action, objectdn, trusteedn, sddl,
134 H=None, credopts=None, sambaopts=None, versionopts=None):
135 lp = sambaopts.get_loadparm()
136 creds = credopts.get_credentials(lp)
138 if sddl is None and (car is None or action is None
139 or objectdn is None or trusteedn is None):
142 samdb = SamDB(url=H, session_info=system_session(),
143 credentials=creds, lp=lp)
144 cars = {'change-rid' : GUID_DRS_CHANGE_RID_MASTER,
145 'change-pdc' : GUID_DRS_CHANGE_PDC,
146 'change-infrastructure' : GUID_DRS_CHANGE_INFR_MASTER,
147 'change-schema' : GUID_DRS_CHANGE_SCHEMA_MASTER,
148 'change-naming' : GUID_DRS_CHANGE_DOMAIN_MASTER,
149 'allocate_rids' : GUID_DRS_ALLOCATE_RIDS,
150 'get-changes' : GUID_DRS_GET_CHANGES,
151 'get-changes-all' : GUID_DRS_GET_ALL_CHANGES,
152 'get-changes-filtered' : GUID_DRS_GET_FILTERED_ATTRIBUTES,
153 'topology-manage' : GUID_DRS_MANAGE_TOPOLOGY,
154 'topology-monitor' : GUID_DRS_MONITOR_TOPOLOGY,
155 'repl-sync' : GUID_DRS_REPL_SYNCRONIZE,
156 'ro-repl-secret-sync' : GUID_DRS_RO_REPL_SECRET_SYNC,
158 sid = self.find_trustee_sid(samdb, trusteedn)
161 elif action == "allow":
162 new_ace = "(OA;;CR;%s;;%s)" % (cars[car], str(sid))
163 elif action == "deny":
164 new_ace = "(OD;;CR;%s;;%s)" % (cars[car], str(sid))
166 raise CommandError("Wrong argument '%s'!" % action)
168 self.print_new_acl(samdb, objectdn)
169 self.add_ace(samdb, objectdn, new_ace)
170 self.print_new_acl(samdb, objectdn)
173 class cmd_ds_acl(SuperCommand):
174 """DS ACLs manipulation"""
177 subcommands["set"] = cmd_ds_acl_set()