s4:samba-tool: add "delegation" subcommands for S4U2Proxy and related stuff
[samba.git] / source4 / scripting / python / samba / netcmd / delegation.py
1 #!/usr/bin/env python
2 #
3 # delegation management
4 #
5 # Copyright Matthieu Patou mat@samba.org 2010
6 # Copyright Stefan Metzmacher metze@samba.org 2011
7 # Copyright Bjoern Baumbach bb@sernet.de 2011
8 #
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 #
22
23 import samba.getopt as options
24 import ldb
25 import re
26 from samba import provision
27 from samba import dsdb
28 from samba.samdb import SamDB
29 from samba.auth import system_session
30 from samba.netcmd import (
31     Command,
32     CommandError,
33     SuperCommand,
34     Option
35     )
36
37 def _get_user_realm_domain(user):
38     """ get the realm or the domain and the base user
39         from user like:
40         * username
41         * DOMAIN\username
42         * username@REALM
43     """
44     baseuser = user
45     realm = ""
46     domain = ""
47     m = re.match(r"(\w+)\\(\w+$)", user)
48     if m:
49         domain = m.group(1)
50         baseuser = m.group(2)
51         return (baseuser.lower(), domain.upper(), realm)
52     m = re.match(r"(\w+)@(\w+)", user)
53     if m:
54         baseuser = m.group(1)
55         realm = m.group(2)
56     return (baseuser.lower(), domain, realm.upper())
57
58 class cmd_delegation_show(Command):
59     """Show the delegation setting of an account."""
60     synopsis = "%prog delegation show <accountname>"
61
62     takes_optiongroups = {
63         "sambaopts": options.SambaOptions,
64         "credopts": options.CredentialsOptions,
65         "versionopts": options.VersionOptions,
66         }
67
68     takes_args = ["accountname"]
69
70     def run(self, accountname, credopts=None, sambaopts=None, versionopts=None):
71         lp = sambaopts.get_loadparm()
72         creds = credopts.get_credentials(lp)
73         paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
74         sam = SamDB(paths.samdb, session_info=system_session(),
75                     credentials=creds, lp=lp)
76         # TODO once I understand how, use the domain info to naildown
77         # to the correct domain
78         (cleanedaccount, realm, domain) = _get_user_realm_domain(accountname)
79         print "Searching for: %s" % (cleanedaccount)
80         res = sam.search(expression="sAMAccountName=%s" % cleanedaccount,
81                             scope=ldb.SCOPE_SUBTREE,
82                             attrs=["userAccountControl", "msDS-AllowedToDelegateTo"])
83         if len(res) != 1:
84             raise CommandError("Account %s found %d times" % (accountname, len(res)))
85
86         uac = int(res[0].get("userAccountControl")[0])
87         allowed = res[0].get("msDS-AllowedToDelegateTo")
88
89         print "Account-DN: %s" %  str(res[0].dn)
90
91         if uac & dsdb.UF_TRUSTED_FOR_DELEGATION:
92             print "UF_TRUSTED_FOR_DELEGATION: 1"
93         else:
94             print "UF_TRUSTED_FOR_DELEGATION: 0"
95
96         if uac & dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION:
97             print "UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: 1"
98         else:
99             print "UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: 0"
100
101         if allowed != None:
102             for a in allowed:
103                 print "msDS-AllowedToDelegateTo: %s" % (str(a))
104
105 class cmd_delegation_for_any_service(Command):
106     """Set/unset UF_TRUSTED_FOR_DELEGATION for an account."""
107     synopsis = "%prog delegation for-any-service <accountname> on|off"
108
109     takes_optiongroups = {
110         "sambaopts": options.SambaOptions,
111         "credopts": options.CredentialsOptions,
112         "versionopts": options.VersionOptions,
113         }
114
115     takes_args = ["accountname", "onoff"]
116
117     def run(self, accountname, onoff, credopts=None, sambaopts=None, versionopts=None):
118
119         on = False
120         if onoff == "on":
121             on = True
122         elif onoff == "off":
123             on = False
124         else:
125             raise CommandError("Invalid argument [%s]" % onoff)
126
127         lp = sambaopts.get_loadparm()
128         creds = credopts.get_credentials(lp)
129         paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
130         sam = SamDB(paths.samdb, session_info=system_session(),
131                     credentials=creds, lp=lp)
132         # TODO once I understand how, use the domain info to naildown
133         # to the correct domain
134         (cleanedaccount, realm, domain) = _get_user_realm_domain(accountname)
135
136         search_filter = "sAMAccountName=%s" % cleanedaccount
137         flag = dsdb.UF_TRUSTED_FOR_DELEGATION
138         try:
139             sam.toggle_userAccountFlags(search_filter, flag, on=on, strict=True)
140         except Exception, err:
141             raise CommandError(err)
142
143 class cmd_delegation_for_any_protocol(Command):
144     """Set/unset UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION (S4U2Proxy) for an account."""
145     synopsis = "%prog delegation for-any-protocol <accountname> on|off"
146
147     takes_optiongroups = {
148         "sambaopts": options.SambaOptions,
149         "credopts": options.CredentialsOptions,
150         "versionopts": options.VersionOptions,
151         }
152
153     takes_args = ["accountname", "onoff"]
154
155     def run(self, accountname, onoff, credopts=None, sambaopts=None, versionopts=None):
156
157         on = False
158         if onoff == "on":
159             on = True
160         elif onoff == "off":
161             on = False
162         else:
163             raise CommandError("Invalid argument [%s]" % onoff)
164
165         lp = sambaopts.get_loadparm()
166         creds = credopts.get_credentials(lp)
167         paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
168         sam = SamDB(paths.samdb, session_info=system_session(),
169                     credentials=creds, lp=lp)
170         # TODO once I understand how, use the domain info to naildown
171         # to the correct domain
172         (cleanedaccount, realm, domain) = _get_user_realm_domain(accountname)
173
174         search_filter = "sAMAccountName=%s" % cleanedaccount
175         flag = dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
176         try:
177             sam.toggle_userAccountFlags(search_filter, flag, on=on, strict=True)
178         except Exception, err:
179             raise CommandError(err)
180
181 class cmd_delegation_add_service(Command):
182     """Add a service principal as msDS-AllowedToDelegateTo"""
183     synopsis = "%prog delegation add-service  <accountname> <principal>"
184
185     takes_optiongroups = {
186         "sambaopts": options.SambaOptions,
187         "credopts": options.CredentialsOptions,
188         "versionopts": options.VersionOptions,
189         }
190
191     takes_args = ["accountname", "principal"]
192
193     def run(self, accountname, principal, credopts=None, sambaopts=None, versionopts=None):
194
195         lp = sambaopts.get_loadparm()
196         creds = credopts.get_credentials(lp)
197         paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
198         sam = SamDB(paths.samdb, session_info=system_session(),
199                     credentials=creds, lp=lp)
200         # TODO once I understand how, use the domain info to naildown
201         # to the correct domain
202         (cleanedaccount, realm, domain) = _get_user_realm_domain(accountname)
203
204         res = sam.search(expression="sAMAccountName=%s" % cleanedaccount,
205                             scope=ldb.SCOPE_SUBTREE,
206                             attrs=["msDS-AllowedToDelegateTo"])
207         if len(res) != 1:
208             raise CommandError("Account %s found %d times" % (accountname, len(res)))
209
210         msg = ldb.Message()
211         msg.dn = res[0].dn
212         msg["msDS-AllowedToDelegateTo"] = ldb.MessageElement([principal],
213                                               ldb.FLAG_MOD_ADD,
214                                               "msDS-AllowedToDelegateTo")
215         try:
216             sam.modify(msg)
217         except Exception, err:
218             raise CommandError(err)
219
220 class cmd_delegation_del_service(Command):
221     """Add a service principal as msDS-AllowedToDelegateTo"""
222     synopsis = "%prog delegation del-service  <accountname> <principal>"
223
224     takes_optiongroups = {
225         "sambaopts": options.SambaOptions,
226         "credopts": options.CredentialsOptions,
227         "versionopts": options.VersionOptions,
228         }
229
230     takes_args = ["accountname", "principal"]
231
232     def run(self, accountname, principal, credopts=None, sambaopts=None, versionopts=None):
233
234         lp = sambaopts.get_loadparm()
235         creds = credopts.get_credentials(lp)
236         paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
237         sam = SamDB(paths.samdb, session_info=system_session(),
238                     credentials=creds, lp=lp)
239         # TODO once I understand how, use the domain info to naildown
240         # to the correct domain
241         (cleanedaccount, realm, domain) = _get_user_realm_domain(accountname)
242
243         res = sam.search(expression="sAMAccountName=%s" % cleanedaccount,
244                             scope=ldb.SCOPE_SUBTREE,
245                             attrs=["msDS-AllowedToDelegateTo"])
246         if len(res) != 1:
247             raise CommandError("Account %s found %d times" % (accountname, len(res)))
248
249         msg = ldb.Message()
250         msg.dn = res[0].dn
251         msg["msDS-AllowedToDelegateTo"] = ldb.MessageElement([principal],
252                                               ldb.FLAG_MOD_DELETE,
253                                               "msDS-AllowedToDelegateTo")
254         try:
255             sam.modify(msg)
256         except Exception, err:
257             raise CommandError(err)
258
259 class cmd_delegation(SuperCommand):
260     """Delegation management [server connection needed]"""
261
262     subcommands = {}
263     subcommands["show"] = cmd_delegation_show()
264     subcommands["for-any-service"] = cmd_delegation_for_any_service()
265     subcommands["for-any-protocol"] = cmd_delegation_for_any_protocol()
266     subcommands["add-service"] = cmd_delegation_add_service()
267     subcommands["del-service"] = cmd_delegation_del_service()