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 (
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 """
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",
61 "change-infrastructure",
67 "get-changes-filtered",
71 "ro-repl-secret-sync"],
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",
77 Option("--trusteedn", help="DN of the entity that gets access",
79 Option("--sddl", help="An ACE or group of ACEs to be added on the object",
83 def find_trustee_sid(self
, samdb
, trusteedn
):
84 res
= samdb
.search(base
=trusteedn
, expression
="(objectClass=*)",
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
))
92 m
.dn
= ldb
.Dn(samdb
, object_dn
)
93 m
["nTSecurityDescriptor"]= ldb
.MessageElement(
94 (ndr_pack(desc
)), ldb
.FLAG_MOD_REPLACE
,
95 "nTSecurityDescriptor")
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
:
119 desc_sddl
= desc_sddl
.replace(ace
, "")
120 if new_ace
in desc_sddl
:
122 if desc_sddl
.find("(") >= 0:
123 desc_sddl
= desc_sddl
[:desc_sddl
.index("(")] + new_ace
+ desc_sddl
[desc_sddl
.index("("):]
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):
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
)
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
))
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"""
179 subcommands
["set"] = cmd_dsacl_set()