3 # Manipulate ACLs on directory objects
5 # Copyright (C) Nadezhda Ivanova <nivanova@samba.org> 2010
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 import samba
.getopt
as options
22 from samba
.dcerpc
import security
23 from samba
.samdb
import SamDB
24 from samba
.ndr
import ndr_unpack
, ndr_pack
25 from samba
.dcerpc
.security
import (
26 GUID_DRS_ALLOCATE_RIDS
, GUID_DRS_CHANGE_DOMAIN_MASTER
,
27 GUID_DRS_CHANGE_INFR_MASTER
, GUID_DRS_CHANGE_PDC
,
28 GUID_DRS_CHANGE_RID_MASTER
, GUID_DRS_CHANGE_SCHEMA_MASTER
,
29 GUID_DRS_GET_CHANGES
, GUID_DRS_GET_ALL_CHANGES
,
30 GUID_DRS_GET_FILTERED_ATTRIBUTES
, GUID_DRS_MANAGE_TOPOLOGY
,
31 GUID_DRS_MONITOR_TOPOLOGY
, GUID_DRS_REPL_SYNCRONIZE
,
32 GUID_DRS_RO_REPL_SECRET_SYNC
)
36 from ldb
import SCOPE_BASE
39 from samba
.auth
import system_session
40 from samba
.netcmd
import (
47 class cmd_ds_acl_set(Command
):
48 """Modify access list on a directory object"""
50 synopsis
= "set --objectdn=objectdn --car=control right --action=[deny|allow] --trusteedn=trustee-dn"
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("--host", help="LDB URL for database or target server",
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",
84 def find_trustee_sid(self
, samdb
, trusteedn
):
85 res
= samdb
.search(base
=trusteedn
, expression
="(objectClass=*)",
88 return ndr_unpack( security
.dom_sid
,res
[0]["objectSid"][0])
90 def modify_descriptor(self
, samdb
, object_dn
, desc
, controls
=None):
91 assert(isinstance(desc
, security
.descriptor
))
93 m
.dn
= ldb
.Dn(samdb
, object_dn
)
94 m
["nTSecurityDescriptor"]= ldb
.MessageElement(
95 (ndr_pack(desc
)), ldb
.FLAG_MOD_REPLACE
,
96 "nTSecurityDescriptor")
99 def read_descriptor(self
, samdb
, object_dn
):
100 res
= samdb
.search(base
=object_dn
, scope
=SCOPE_BASE
,
101 attrs
=["nTSecurityDescriptor"])
102 # we should theoretically always have an SD
103 assert(len(res
) == 1)
104 desc
= res
[0]["nTSecurityDescriptor"][0]
105 return ndr_unpack(security
.descriptor
, desc
)
107 def get_domain_sid(self
, samdb
):
108 res
= samdb
.search(base
=samdb
.domain_dn(),
109 expression
="(objectClass=*)", scope
=SCOPE_BASE
)
110 return ndr_unpack( security
.dom_sid
,res
[0]["objectSid"][0])
112 def add_ace(self
, samdb
, object_dn
, new_ace
):
113 """Add new ace explicitly."""
114 desc
= self
.read_descriptor(samdb
, object_dn
)
115 desc_sddl
= desc
.as_sddl(self
.get_domain_sid(samdb
))
116 #TODO add bindings for descriptor manipulation and get rid of this
117 desc_aces
= re
.findall("\(.*?\)", desc_sddl
)
118 for ace
in desc_aces
:
120 desc_sddl
= desc_sddl
.replace(ace
, "")
121 if new_ace
in desc_sddl
:
123 if desc_sddl
.find("(") >= 0:
124 desc_sddl
= desc_sddl
[:desc_sddl
.index("(")] + new_ace
+ desc_sddl
[desc_sddl
.index("("):]
126 desc_sddl
= desc_sddl
+ new_ace
127 desc
= security
.descriptor
.from_sddl(desc_sddl
, self
.get_domain_sid(samdb
))
128 self
.modify_descriptor(samdb
, object_dn
, desc
)
130 def print_new_acl(self
, samdb
, object_dn
):
131 desc
= self
.read_descriptor(samdb
, object_dn
)
132 desc_sddl
= desc
.as_sddl(self
.get_domain_sid(samdb
))
133 print "new descriptor for %s:" % object_dn
136 def run(self
, car
, action
, objectdn
, trusteedn
,
137 host
=None, credopts
=None, sambaopts
=None, versionopts
=None):
138 lp
= sambaopts
.get_loadparm()
139 creds
= credopts
.get_credentials(lp
)
141 if (car
is None or action
is None or objectdn
is None or
145 samdb
= SamDB(url
=host
, session_info
=system_session(),
146 credentials
=creds
, lp
=lp
)
147 cars
= {'change-rid' : GUID_DRS_CHANGE_RID_MASTER
,
148 'change-pdc' : GUID_DRS_CHANGE_PDC
,
149 'change-infrastructure' : GUID_DRS_CHANGE_INFR_MASTER
,
150 'change-schema' : GUID_DRS_CHANGE_SCHEMA_MASTER
,
151 'change-naming' : GUID_DRS_CHANGE_DOMAIN_MASTER
,
152 'allocate_rids' : GUID_DRS_ALLOCATE_RIDS
,
153 'get-changes' : GUID_DRS_GET_CHANGES
,
154 'get-changes-all' : GUID_DRS_GET_ALL_CHANGES
,
155 'get-changes-filtered' : GUID_DRS_GET_FILTERED_ATTRIBUTES
,
156 'topology-manage' : GUID_DRS_MANAGE_TOPOLOGY
,
157 'topology-monitor' : GUID_DRS_MONITOR_TOPOLOGY
,
158 'repl-sync' : GUID_DRS_REPL_SYNCRONIZE
,
159 'ro-repl-secret-sync' : GUID_DRS_RO_REPL_SECRET_SYNC
,
161 sid
= self
.find_trustee_sid(samdb
, trusteedn
)
162 if action
== "allow":
163 new_ace
= "(OA;;CR;%s;;%s)" % (cars
[car
], str(sid
))
164 elif action
== "deny":
165 new_ace
= "(OD;;CR;%s;;%s)" % (cars
[car
], str(sid
))
167 raise CommandError("Wrong argument '%s'!" % action
)
169 self
.print_new_acl(samdb
, objectdn
)
170 self
.add_ace(samdb
, objectdn
, new_ace
)
171 self
.print_new_acl(samdb
, objectdn
)
174 class cmd_ds_acl(SuperCommand
):
175 """DS ACLs manipulation"""
178 subcommands
["set"] = cmd_ds_acl_set()