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