s4:netcmd/gpo.py: we don't need to set autogenerated attributes
[metze/samba/wip.git] / source4 / scripting / python / samba / netcmd / dsacl.py
1 #!/usr/bin/env python
2 #
3 # Manipulate ACLs on directory objects
4 #
5 # Copyright (C) Nadezhda Ivanova <nivanova@samba.org> 2010
6 # Copyright Giampaolo Lauria 2011 <lauria2@yahoo.com>
7 #
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.
12 #
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.
17 #
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/>.
20 #
21
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)
34
35
36 import ldb
37 from ldb import SCOPE_BASE
38 import re
39
40 from samba.auth import system_session
41 from samba.netcmd import (
42     Command,
43     CommandError,
44     SuperCommand,
45     Option,
46     )
47
48 class cmd_ds_acl_set(Command):
49     """Modify access list on a directory object"""
50
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 """
53
54     takes_options = [
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",
58                                                 "change-pdc",
59                                                 "change-infrastructure",
60                                                 "change-schema",
61                                                 "change-naming",
62                                                 "allocate_rids",
63                                                 "get-changes",
64                                                 "get-changes-all",
65                                                 "get-changes-filtered",
66                                                 "topology-manage",
67                                                 "topology-monitor",
68                                                 "repl-sync",
69                                                 "ro-repl-secret-sync"],
70                help=car_help),
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",
74             type="string"),
75         Option("--trusteedn", help="DN of the entity that gets access",
76             type="string"),
77         Option("--sddl", help="An ACE or group of ACEs to be added on the object",
78             type="string"),
79         ]
80
81     def find_trustee_sid(self, samdb, trusteedn):
82         res = samdb.search(base=trusteedn, expression="(objectClass=*)",
83             scope=SCOPE_BASE)
84         assert(len(res) == 1)
85         return ndr_unpack( security.dom_sid,res[0]["objectSid"][0])
86
87     def modify_descriptor(self, samdb, object_dn, desc, controls=None):
88         assert(isinstance(desc, security.descriptor))
89         m = ldb.Message()
90         m.dn = ldb.Dn(samdb, object_dn)
91         m["nTSecurityDescriptor"]= ldb.MessageElement(
92                 (ndr_pack(desc)), ldb.FLAG_MOD_REPLACE,
93                 "nTSecurityDescriptor")
94         samdb.modify(m)
95
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)
103
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])
108
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:
116             if ("ID" in ace):
117                 desc_sddl = desc_sddl.replace(ace, "")
118         if new_ace in desc_sddl:
119             return
120         if desc_sddl.find("(") >= 0:
121             desc_sddl = desc_sddl[:desc_sddl.index("(")] + new_ace + desc_sddl[desc_sddl.index("("):]
122         else:
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)
126
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
131         print desc_sddl
132
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)
137
138         if sddl is None and (car is None or action is None
139                              or objectdn is None or trusteedn is None):
140             return self.usage()
141
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,
157                 }
158         sid = self.find_trustee_sid(samdb, trusteedn)
159         if sddl:
160             new_ace = sddl
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))
165         else:
166             raise CommandError("Wrong argument '%s'!" % action)
167
168         self.print_new_acl(samdb, objectdn)
169         self.add_ace(samdb, objectdn, new_ace)
170         self.print_new_acl(samdb, objectdn)
171
172
173 class cmd_ds_acl(SuperCommand):
174     """DS ACLs manipulation"""
175
176     subcommands = {}
177     subcommands["set"] = cmd_ds_acl_set()