1 # Manipulate ACLs on directory objects
3 # Copyright (C) Nadezhda Ivanova <nivanova@samba.org> 2010
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/>.
19 import samba
.getopt
as options
20 from samba
.dcerpc
import security
21 from samba
.samdb
import SamDB
22 from samba
.ndr
import ndr_unpack
, ndr_pack
23 from samba
.dcerpc
.security
import (
24 GUID_DRS_ALLOCATE_RIDS
, GUID_DRS_CHANGE_DOMAIN_MASTER
,
25 GUID_DRS_CHANGE_INFR_MASTER
, GUID_DRS_CHANGE_PDC
,
26 GUID_DRS_CHANGE_RID_MASTER
, GUID_DRS_CHANGE_SCHEMA_MASTER
,
27 GUID_DRS_GET_CHANGES
, GUID_DRS_GET_ALL_CHANGES
,
28 GUID_DRS_GET_FILTERED_ATTRIBUTES
, GUID_DRS_MANAGE_TOPOLOGY
,
29 GUID_DRS_MONITOR_TOPOLOGY
, GUID_DRS_REPL_SYNCRONIZE
,
30 GUID_DRS_RO_REPL_SECRET_SYNC
)
34 from ldb
import SCOPE_BASE
37 from samba
.auth
import system_session
38 from samba
.netcmd
import (
47 class cmd_dsacl_set(Command
):
48 """Modify access list on a directory object."""
50 synopsis
= "%prog [options]"
51 car_help
= """ The access control right to allow or deny """
53 takes_optiongroups
= {
54 "sambaopts": options
.SambaOptions
,
55 "credopts": options
.CredentialsOptions
,
56 "versionopts": options
.VersionOptions
,
60 Option("-H", "--URL", help="LDB URL for database or target server",
61 type=str, metavar
="URL", dest
="H"),
62 Option("--car", type="choice", choices
=["change-rid",
64 "change-infrastructure",
70 "get-changes-filtered",
74 "ro-repl-secret-sync"],
76 Option("--action", type="choice", choices
=["allow", "deny"],
77 help="""Deny or allow access"""),
78 Option("--objectdn", help="DN of the object whose SD to modify",
80 Option("--trusteedn", help="DN of the entity that gets access",
82 Option("--sddl", help="An ACE or group of ACEs to be added on the object",
86 def find_trustee_sid(self
, samdb
, trusteedn
):
87 res
= samdb
.search(base
=trusteedn
, expression
="(objectClass=*)",
90 return ndr_unpack( security
.dom_sid
,res
[0]["objectSid"][0])
92 def modify_descriptor(self
, samdb
, object_dn
, desc
, controls
=None):
93 assert(isinstance(desc
, security
.descriptor
))
95 m
.dn
= ldb
.Dn(samdb
, object_dn
)
96 m
["nTSecurityDescriptor"]= ldb
.MessageElement(
97 (ndr_pack(desc
)), ldb
.FLAG_MOD_REPLACE
,
98 "nTSecurityDescriptor")
101 def read_descriptor(self
, samdb
, object_dn
):
102 res
= samdb
.search(base
=object_dn
, scope
=SCOPE_BASE
,
103 attrs
=["nTSecurityDescriptor"])
104 # we should theoretically always have an SD
105 assert(len(res
) == 1)
106 desc
= res
[0]["nTSecurityDescriptor"][0]
107 return ndr_unpack(security
.descriptor
, desc
)
109 def get_domain_sid(self
, samdb
):
110 res
= samdb
.search(base
=samdb
.domain_dn(),
111 expression
="(objectClass=*)", scope
=SCOPE_BASE
)
112 return ndr_unpack( security
.dom_sid
,res
[0]["objectSid"][0])
114 def add_ace(self
, samdb
, object_dn
, new_ace
):
115 """Add new ace explicitly."""
116 desc
= self
.read_descriptor(samdb
, object_dn
)
117 desc_sddl
= desc
.as_sddl(self
.get_domain_sid(samdb
))
118 #TODO add bindings for descriptor manipulation and get rid of this
119 desc_aces
= re
.findall("\(.*?\)", desc_sddl
)
120 for ace
in desc_aces
:
122 desc_sddl
= desc_sddl
.replace(ace
, "")
123 if new_ace
in desc_sddl
:
125 if desc_sddl
.find("(") >= 0:
126 desc_sddl
= desc_sddl
[:desc_sddl
.index("(")] + new_ace
+ desc_sddl
[desc_sddl
.index("("):]
128 desc_sddl
= desc_sddl
+ new_ace
129 desc
= security
.descriptor
.from_sddl(desc_sddl
, self
.get_domain_sid(samdb
))
130 self
.modify_descriptor(samdb
, object_dn
, desc
)
132 def print_new_acl(self
, samdb
, object_dn
):
133 desc
= self
.read_descriptor(samdb
, object_dn
)
134 desc_sddl
= desc
.as_sddl(self
.get_domain_sid(samdb
))
135 self
.outf
.write("new descriptor for %s:\n" % object_dn
)
136 self
.outf
.write(desc_sddl
+ "\n")
138 def run(self
, car
, action
, objectdn
, trusteedn
, sddl
,
139 H
=None, credopts
=None, sambaopts
=None, versionopts
=None):
140 lp
= sambaopts
.get_loadparm()
141 creds
= credopts
.get_credentials(lp
)
143 if sddl
is None and (car
is None or action
is None
144 or objectdn
is None or trusteedn
is None):
147 samdb
= SamDB(url
=H
, session_info
=system_session(),
148 credentials
=creds
, lp
=lp
)
149 cars
= {'change-rid' : GUID_DRS_CHANGE_RID_MASTER
,
150 'change-pdc' : GUID_DRS_CHANGE_PDC
,
151 'change-infrastructure' : GUID_DRS_CHANGE_INFR_MASTER
,
152 'change-schema' : GUID_DRS_CHANGE_SCHEMA_MASTER
,
153 'change-naming' : GUID_DRS_CHANGE_DOMAIN_MASTER
,
154 'allocate_rids' : GUID_DRS_ALLOCATE_RIDS
,
155 'get-changes' : GUID_DRS_GET_CHANGES
,
156 'get-changes-all' : GUID_DRS_GET_ALL_CHANGES
,
157 'get-changes-filtered' : GUID_DRS_GET_FILTERED_ATTRIBUTES
,
158 'topology-manage' : GUID_DRS_MANAGE_TOPOLOGY
,
159 'topology-monitor' : GUID_DRS_MONITOR_TOPOLOGY
,
160 'repl-sync' : GUID_DRS_REPL_SYNCRONIZE
,
161 'ro-repl-secret-sync' : GUID_DRS_RO_REPL_SECRET_SYNC
,
163 sid
= self
.find_trustee_sid(samdb
, trusteedn
)
166 elif action
== "allow":
167 new_ace
= "(OA;;CR;%s;;%s)" % (cars
[car
], str(sid
))
168 elif action
== "deny":
169 new_ace
= "(OD;;CR;%s;;%s)" % (cars
[car
], str(sid
))
171 raise CommandError("Wrong argument '%s'!" % action
)
173 self
.print_new_acl(samdb
, objectdn
)
174 self
.add_ace(samdb
, objectdn
, new_ace
)
175 self
.print_new_acl(samdb
, objectdn
)
178 class cmd_dsacl(SuperCommand
):
179 """DS ACLs manipulation."""
182 subcommands
["set"] = cmd_dsacl_set()