s4:samba-tool: add "delegation" subcommands for S4U2Proxy and related stuff
authorStefan Metzmacher <metze@samba.org>
Fri, 24 Jun 2011 14:59:24 +0000 (16:59 +0200)
committerStefan Metzmacher <metze@samba.org>
Fri, 24 Jun 2011 17:06:44 +0000 (19:06 +0200)
For now this only works on the local sam.ldb, but it shouldn't be hard
to improve it to talk to remove servers.

Pair-Programmed-With: Bj√∂rn Baumbach <bb@sernet.de>

metze

source4/scripting/python/samba/netcmd/__init__.py
source4/scripting/python/samba/netcmd/delegation.py [new file with mode: 0644]

index 1373cb289b679004a99593c0b423ad19664a520f..bc5b0f67ae26adf86b3a19f967226b6a8a94858c 100644 (file)
@@ -214,3 +214,5 @@ from samba.netcmd.testparm import cmd_testparm
 commands["testparm"] =  cmd_testparm()
 from samba.netcmd.dbcheck import cmd_dbcheck
 commands["dbcheck"] =  cmd_dbcheck()
+from samba.netcmd.delegation import cmd_delegation
+commands["delegation"] = cmd_delegation()
diff --git a/source4/scripting/python/samba/netcmd/delegation.py b/source4/scripting/python/samba/netcmd/delegation.py
new file mode 100644 (file)
index 0000000..1307c14
--- /dev/null
@@ -0,0 +1,267 @@
+#!/usr/bin/env python
+#
+# delegation management
+#
+# Copyright Matthieu Patou mat@samba.org 2010
+# Copyright Stefan Metzmacher metze@samba.org 2011
+# Copyright Bjoern Baumbach bb@sernet.de 2011
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import samba.getopt as options
+import ldb
+import re
+from samba import provision
+from samba import dsdb
+from samba.samdb import SamDB
+from samba.auth import system_session
+from samba.netcmd import (
+    Command,
+    CommandError,
+    SuperCommand,
+    Option
+    )
+
+def _get_user_realm_domain(user):
+    """ get the realm or the domain and the base user
+        from user like:
+        * username
+        * DOMAIN\username
+        * username@REALM
+    """
+    baseuser = user
+    realm = ""
+    domain = ""
+    m = re.match(r"(\w+)\\(\w+$)", user)
+    if m:
+        domain = m.group(1)
+        baseuser = m.group(2)
+        return (baseuser.lower(), domain.upper(), realm)
+    m = re.match(r"(\w+)@(\w+)", user)
+    if m:
+        baseuser = m.group(1)
+        realm = m.group(2)
+    return (baseuser.lower(), domain, realm.upper())
+
+class cmd_delegation_show(Command):
+    """Show the delegation setting of an account."""
+    synopsis = "%prog delegation show <accountname>"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "credopts": options.CredentialsOptions,
+        "versionopts": options.VersionOptions,
+        }
+
+    takes_args = ["accountname"]
+
+    def run(self, accountname, credopts=None, sambaopts=None, versionopts=None):
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+        paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
+        sam = SamDB(paths.samdb, session_info=system_session(),
+                    credentials=creds, lp=lp)
+        # TODO once I understand how, use the domain info to naildown
+        # to the correct domain
+        (cleanedaccount, realm, domain) = _get_user_realm_domain(accountname)
+        print "Searching for: %s" % (cleanedaccount)
+        res = sam.search(expression="sAMAccountName=%s" % cleanedaccount,
+                            scope=ldb.SCOPE_SUBTREE,
+                            attrs=["userAccountControl", "msDS-AllowedToDelegateTo"])
+        if len(res) != 1:
+            raise CommandError("Account %s found %d times" % (accountname, len(res)))
+
+        uac = int(res[0].get("userAccountControl")[0])
+        allowed = res[0].get("msDS-AllowedToDelegateTo")
+
+        print "Account-DN: %s" %  str(res[0].dn)
+
+        if uac & dsdb.UF_TRUSTED_FOR_DELEGATION:
+            print "UF_TRUSTED_FOR_DELEGATION: 1"
+        else:
+            print "UF_TRUSTED_FOR_DELEGATION: 0"
+
+        if uac & dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION:
+            print "UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: 1"
+        else:
+            print "UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: 0"
+
+        if allowed != None:
+            for a in allowed:
+                print "msDS-AllowedToDelegateTo: %s" % (str(a))
+
+class cmd_delegation_for_any_service(Command):
+    """Set/unset UF_TRUSTED_FOR_DELEGATION for an account."""
+    synopsis = "%prog delegation for-any-service <accountname> on|off"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "credopts": options.CredentialsOptions,
+        "versionopts": options.VersionOptions,
+        }
+
+    takes_args = ["accountname", "onoff"]
+
+    def run(self, accountname, onoff, credopts=None, sambaopts=None, versionopts=None):
+
+        on = False
+        if onoff == "on":
+            on = True
+        elif onoff == "off":
+            on = False
+        else:
+            raise CommandError("Invalid argument [%s]" % onoff)
+
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+        paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
+        sam = SamDB(paths.samdb, session_info=system_session(),
+                    credentials=creds, lp=lp)
+        # TODO once I understand how, use the domain info to naildown
+        # to the correct domain
+        (cleanedaccount, realm, domain) = _get_user_realm_domain(accountname)
+
+       search_filter = "sAMAccountName=%s" % cleanedaccount
+        flag = dsdb.UF_TRUSTED_FOR_DELEGATION
+        try:
+            sam.toggle_userAccountFlags(search_filter, flag, on=on, strict=True)
+        except Exception, err:
+            raise CommandError(err)
+
+class cmd_delegation_for_any_protocol(Command):
+    """Set/unset UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION (S4U2Proxy) for an account."""
+    synopsis = "%prog delegation for-any-protocol <accountname> on|off"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "credopts": options.CredentialsOptions,
+        "versionopts": options.VersionOptions,
+        }
+
+    takes_args = ["accountname", "onoff"]
+
+    def run(self, accountname, onoff, credopts=None, sambaopts=None, versionopts=None):
+
+        on = False
+        if onoff == "on":
+            on = True
+        elif onoff == "off":
+            on = False
+        else:
+            raise CommandError("Invalid argument [%s]" % onoff)
+
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+        paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
+        sam = SamDB(paths.samdb, session_info=system_session(),
+                    credentials=creds, lp=lp)
+        # TODO once I understand how, use the domain info to naildown
+        # to the correct domain
+        (cleanedaccount, realm, domain) = _get_user_realm_domain(accountname)
+
+       search_filter = "sAMAccountName=%s" % cleanedaccount
+        flag = dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
+        try:
+            sam.toggle_userAccountFlags(search_filter, flag, on=on, strict=True)
+        except Exception, err:
+            raise CommandError(err)
+
+class cmd_delegation_add_service(Command):
+    """Add a service principal as msDS-AllowedToDelegateTo"""
+    synopsis = "%prog delegation add-service  <accountname> <principal>"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "credopts": options.CredentialsOptions,
+        "versionopts": options.VersionOptions,
+        }
+
+    takes_args = ["accountname", "principal"]
+
+    def run(self, accountname, principal, credopts=None, sambaopts=None, versionopts=None):
+
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+        paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
+        sam = SamDB(paths.samdb, session_info=system_session(),
+                    credentials=creds, lp=lp)
+        # TODO once I understand how, use the domain info to naildown
+        # to the correct domain
+        (cleanedaccount, realm, domain) = _get_user_realm_domain(accountname)
+
+        res = sam.search(expression="sAMAccountName=%s" % cleanedaccount,
+                            scope=ldb.SCOPE_SUBTREE,
+                            attrs=["msDS-AllowedToDelegateTo"])
+        if len(res) != 1:
+            raise CommandError("Account %s found %d times" % (accountname, len(res)))
+
+        msg = ldb.Message()
+        msg.dn = res[0].dn
+        msg["msDS-AllowedToDelegateTo"] = ldb.MessageElement([principal],
+                                              ldb.FLAG_MOD_ADD,
+                                              "msDS-AllowedToDelegateTo")
+        try:
+            sam.modify(msg)
+        except Exception, err:
+            raise CommandError(err)
+
+class cmd_delegation_del_service(Command):
+    """Add a service principal as msDS-AllowedToDelegateTo"""
+    synopsis = "%prog delegation del-service  <accountname> <principal>"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "credopts": options.CredentialsOptions,
+        "versionopts": options.VersionOptions,
+        }
+
+    takes_args = ["accountname", "principal"]
+
+    def run(self, accountname, principal, credopts=None, sambaopts=None, versionopts=None):
+
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+        paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
+        sam = SamDB(paths.samdb, session_info=system_session(),
+                    credentials=creds, lp=lp)
+        # TODO once I understand how, use the domain info to naildown
+        # to the correct domain
+        (cleanedaccount, realm, domain) = _get_user_realm_domain(accountname)
+
+        res = sam.search(expression="sAMAccountName=%s" % cleanedaccount,
+                            scope=ldb.SCOPE_SUBTREE,
+                            attrs=["msDS-AllowedToDelegateTo"])
+        if len(res) != 1:
+            raise CommandError("Account %s found %d times" % (accountname, len(res)))
+
+        msg = ldb.Message()
+        msg.dn = res[0].dn
+        msg["msDS-AllowedToDelegateTo"] = ldb.MessageElement([principal],
+                                              ldb.FLAG_MOD_DELETE,
+                                              "msDS-AllowedToDelegateTo")
+        try:
+            sam.modify(msg)
+        except Exception, err:
+            raise CommandError(err)
+
+class cmd_delegation(SuperCommand):
+    """Delegation management [server connection needed]"""
+
+    subcommands = {}
+    subcommands["show"] = cmd_delegation_show()
+    subcommands["for-any-service"] = cmd_delegation_for_any_service()
+    subcommands["for-any-protocol"] = cmd_delegation_for_any_protocol()
+    subcommands["add-service"] = cmd_delegation_add_service()
+    subcommands["del-service"] = cmd_delegation_del_service()