Hello all,

with this set of patches it is possible to allow constrained delegation
of credentials so that a service can impersonate a user when
communicating with another service w/o requiring the user to actually
forward their TGT. This makes for a much better method of delegating
credentials as it prevents exposure of the short term secret of the
user.

Plus, I added a relatively simple access control method that allow the
KDC to decide exactly which services are allowed to impersonate which
users against other services. A simple grouping mechanism is used so
that large environments clusters and otherwise classes of services can
be much more easily managed.

The grouping mechanism has been built so that lookup is highly optimized
and is basically reduced to a single search that uses the derefernce
control. Speed is very important in this case because KDC operations
time out very quickly and unless we add a caching layer in ipa-kdb we
must keep the number of searches down to avoid client timeouts.

The grouping mechanism is very simple a groupOfPrincipals object is
introduced, this Auxiliary class have a single optional attribute called
memberPrincipal which is a string containing a principal name.

A separate objectclass is also introduced called ipaKrb5DelegationACL,
it is a subclass of groupOfPrincipals and is a Structural class.

It has 2 additional optional attributes: ipaAllowedTarget and
ipaAllowToImpersonate. They are both DNs

The memberPrincipal attribute in this class contains the list of
principals that are being considered as 'proxies', that is the
principals of the services that want to impersonate users against other
services.

The ipaAllowedToImpersonate must point to a groupOfPrincipal based
object that contains the list of users that can be impersonated by this
service. If the attribute is missing than the service is allowed to
impersonate *any* user.

The ipaAllowedTarget DN must point to a groupOfPrincipal based object
that contains the list of services that the proxy service is allowed
taret when impersonating users. A target must be specified in order to
allow a service to access it impersonating another principal.


At the moment no wildcarding is implemented so services have to be
explicitly listed in their respective groups.
I have some idea of adding wildcard support at least for the
ipaAllowedToImpersonate group in order to separate user principals by
REALM. So you can say all users of REALM1 can be impersonated by this
service but no users of REALM2.

It is unclear how this wildcarding may be implemented, but it must be
simple to avoid potentially very expensive computations every time a
ticket for the target services is requested.

I have briefly tested this patch by manually creating a few objects then
using the kvno command to test that I could get a ldap ticket just using
the HTTP credentials (in order to do this I had to allow also s4u2self
operations for the HTTP service, but this is *not* generally required
and it is *not* desired in the IPA framework implementation).

This patchset does not contain any CLI or UI nor installation changes to
create ipaKrb5DelegationACL obujects. It is indeed yet unclear where we
want to store them (suggestions are welcome) and how/when we may want to
expose this mechanism through UI/CLI for general usage.

The initial intended usage is to allow us to move away from using
forwarded TGTs in the IPA framework and instead use S4U2Proxy in order
to access the ldap service. In order to do this some changes will need
to be made in installation scripts and replica management scripts later.

How to test:

Create 2 objects like these:

dn: cn=ipa-http-delegation,...
objectClass: ipaKrb5DelegationACL
objectClass: groupOfPrincipals
cn: ipa-http-delegation
memberPrincipal: HTTP/ipaserver.example....@example.com
ipaAllowedTarget: cn=ipa-ldap-delegation-targets,...

dn: cn=ipa-ldap-delegation-targets,...
objectClass: groupOfPrincipals
cn: ipa-ldap-delegation-targets
memberPrincipal: ldap/ipaserver.example....@example.com


In order to test with kvno which pretend to do s4u2self too you will
need to allow the HTTP service to impersonate arbitrary users.

This is done with:
kdamin.local
modprinc +ok_to_auth_as_delegate HTTP/ipaserver.example.com

Then run kvno as follows:

# Init credntials as HTTP
kinit -kt /etc/httpd/conf/ipa.keytab HTTP/ipaserver.example.com

# Perform S4U2Self
kvno -U admin HTTP/ipaserver.example.com

# Perform S4U2Proxy 
kvno -k /etc/httpd/conf/ipa.keytab -U admin -P HTTP/ipaserver.example.com 
ldap/ipaserver.example.com


If this works it means you successfully impersonated the admin user with
the HTTP service against the ldap service.

Simo.

-- 
Simo Sorce * Red Hat, Inc * New York
>From f953d45f87837c41965767a1ff311131f7d37abb Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Sun, 20 Nov 2011 17:04:05 -0500
Subject: [PATCH 1/3] ipa-kdb: Delegation ACL schema

---
 install/share/60basev3.ldif |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif
index f518541586b2df9ed08718098a7f170563aa4e1d..b31a3d4dd77be57adcc0d97d24e23ecf71025756 100644
--- a/install/share/60basev3.ldif
+++ b/install/share/60basev3.ldif
@@ -14,7 +14,12 @@ attributeTypes: (2.16.840.1.113730.3.8.11.7 NAME 'ipaNTProfilePath' DESC 'User P
 attributeTypes: (2.16.840.1.113730.3.8.11.8 NAME 'ipaNTHomeDirectory' DESC 'User Home Directory Path' EQUALITY caseIgnoreMatch OREDRING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3' )
 attributeTypes: (2.16.840.1.113730.3.8.11.9 NAME 'ipaNTHomeDirectoryDrive' DESC 'User Home Drive Letter' EQUALITY caseIgnoreMatch OREDRING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3' )
 attributeTypes: (2.16.840.1.113730.3.8.11.10 NAME 'ipaNTDomainGUID' DESC 'NT Domain GUID' EQUALITY caseIgnoreIA5Match OREDRING caseIgnoreIA5OrderingMatch SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+attributeTypes: ( 2.16.840.1.113730.3.8.11.20 NAME 'memberPrincipal' DESC 'Principal names member of a groupOfPrincipals group' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA-v3')
+attributeTypes: ( 2.16.840.1.113730.3.8.11.21 NAME 'ipaAllowToImpersonate' DESC 'Principals that can be impersonated' SUP distinguishedName X-ORIGIN 'IPA-v3')
+attributeTypes: ( 2.16.840.1.113730.3.8.11.22 NAME 'ipaAllowedTarget' DESC 'Target principals alowed to get a ticket for' SUP distinguishedName X-ORIGIN 'IPA-v3')
 objectClasses: (2.16.840.1.113730.3.8.12.1 NAME 'ipaExternalGroup' SUP top STRUCTURAL MUST ( cn ) MAY ( ipaExternalMember $ memberOf $ description $ owner) X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.2 NAME 'ipaNTUserAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) MAY ( ipaNTHash $ ipaNTLogonScript $ ipaNTProfilePath $ ipaNTHomeDirectory $ ipaNTHomeDirectoryDrive ) X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.3 NAME 'ipaNTGroupAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.4 NAME 'ipaNTDomainAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier $ ipaNTFlatName $ ipaNTDomainGUID ) MAY ( ipaNTFallbackPrimaryGroup ) X-ORIGIN 'IPA v3' )
+objectClasses: (2.16.840.1.113730.3.8.12.6 NAME 'groupOfPrincipals' SUP top AUXILIARY MUST ( cn ) MAY ( memberPrincipal ) X-ORIGIN 'IPA v3' )
+objectClasses: (2.16.840.1.113730.3.8.12.7 NAME 'ipaKrb5DelegationACL' SUP groupOfPrincipals STRUCTURAL MAY ( ipaAllowToImpersonate $ ipaAllowedTarget ) X-ORIGIN 'IPA v3' )
-- 
1.7.7.1

>From 5fac2ec7c5afb26b0348a2e0528bee0bdf84bee7 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Sun, 20 Nov 2011 20:50:27 -0500
Subject: [PATCH 2/3] ipa-kdb: enhance deref searches

Allow to deref more than one attribute.
The attrs searched are the same for all deref attributes at this time.
---
 daemons/ipa-kdb/ipa_kdb.h        |   14 ++++++++++++--
 daemons/ipa-kdb/ipa_kdb_common.c |   34 +++++++++++++++++++++++++---------
 daemons/ipa-kdb/ipa_kdb_mspac.c  |   11 +++++++++--
 3 files changed, 46 insertions(+), 13 deletions(-)

diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
index 8c907c448d0f497786f7b66fb4e17e6590d4cc29..2531d03281e899152b9418b545dd7f1122cf61bb 100644
--- a/daemons/ipa-kdb/ipa_kdb.h
+++ b/daemons/ipa-kdb/ipa_kdb.h
@@ -123,8 +123,11 @@ krb5_error_code ipadb_simple_modify(struct ipadb_context *ipactx,
 krb5_error_code ipadb_simple_delete_val(struct ipadb_context *ipactx,
                                         char *dn, char *attr, char *value);
 krb5_error_code ipadb_deref_search(struct ipadb_context *ipactx,
-                                   char *entry_dn, char **entry_attrs,
-                                   char *deref_attr_name, char **deref_attrs,
+                                   char *base_dn, int scope,
+                                   char *filter,
+                                   char **entry_attrs,
+                                   char **deref_attr_names,
+                                   char **deref_attrs,
                                    LDAPMessage **res);
 
 int ipadb_ldap_attr_to_int(LDAP *lcontext, LDAPMessage *le,
@@ -220,3 +223,10 @@ krb5_error_code ipadb_sign_authdata(krb5_context context,
                                     krb5_authdata ***signed_auth_data);
 
 krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx);
+
+/* DELEGATION CHECKS */
+
+krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
+                                                krb5_const_principal client,
+                                                const krb5_db_entry *server,
+                                                krb5_const_principal proxy);
diff --git a/daemons/ipa-kdb/ipa_kdb_common.c b/daemons/ipa-kdb/ipa_kdb_common.c
index d3e8e9c4cb5438cd9c918daf5a19fc736fc698cb..6f5ac1d74f04c03bccdb19187a34d07b9784fa59 100644
--- a/daemons/ipa-kdb/ipa_kdb_common.c
+++ b/daemons/ipa-kdb/ipa_kdb_common.c
@@ -265,24 +265,39 @@ done:
 }
 
 krb5_error_code ipadb_deref_search(struct ipadb_context *ipactx,
-                                   char *entry_dn, char **entry_attrs,
-                                   char *deref_attr_name, char **deref_attrs,
+                                   char *base_dn, int scope,
+                                   char *filter,
+                                   char **entry_attrs,
+                                   char **deref_attr_names,
+                                   char **deref_attrs,
                                    LDAPMessage **res)
 {
     struct berval derefval = { 0, NULL };
     LDAPControl *ctrl[2] = { NULL, NULL };
-    LDAPDerefSpec ds[2];
+    LDAPDerefSpec *ds;
     krb5_error_code kerr;
     int times;
     int ret;
+    int c;
 
-    ds[0].derefAttr = deref_attr_name;
-    ds[0].attributes = deref_attrs;
-    ds[1].derefAttr = NULL;
+    for (c = 0; deref_attr_names[c]; c++) {
+        /* count */ ;
+    }
+
+    ds = calloc(c, sizeof(LDAPDerefSpec));
+    if (!ds) {
+        return ENOMEM;
+    }
+
+    for (c = 0; deref_attr_names[c]; c++) {
+        ds[c].derefAttr = deref_attr_names[c];
+        ds[c].attributes = deref_attrs;
+    }
 
     ret = ldap_create_deref_control_value(ipactx->lcontext, ds, &derefval);
     if (ret != LDAP_SUCCESS) {
-        return ENOMEM;
+        kerr = ENOMEM;
+        goto done;
     }
 
     ret = ldap_control_create(LDAP_CONTROL_X_DEREF,
@@ -297,8 +312,8 @@ krb5_error_code ipadb_deref_search(struct ipadb_context *ipactx,
     ret = LDAP_SUCCESS;
     while (!ipadb_need_retry(ipactx, ret) && times > 0) {
         times--;
-        ret = ldap_search_ext_s(ipactx->lcontext, entry_dn,
-                                LDAP_SCOPE_BASE, "(objectclass=*)",
+        ret = ldap_search_ext_s(ipactx->lcontext, base_dn,
+                                scope, filter,
                                 entry_attrs, 0,
                                 ctrl, NULL,
                                 &std_timeout, LDAP_NO_LIMIT,
@@ -309,6 +324,7 @@ krb5_error_code ipadb_deref_search(struct ipadb_context *ipactx,
 
 done:
     ldap_memfree(derefval.bv_val);
+    free(ds);
     return kerr;
 }
 
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
index 0c0da75ca8dbedf12e39e8ec5d87bfd4cd485d4a..c544da92a949f44e4aa662ce6d06b09d376e4520 100644
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
@@ -84,6 +84,11 @@ static char *user_pac_attrs[] = {
     NULL
 };
 
+char *deref_search_attrs[] = {
+    "memberOf",
+    NULL
+};
+
 static char *memberof_pac_attrs[] = {
     "gidNumber",
     "ipaNTSecurityIdentifier",
@@ -502,8 +507,10 @@ static krb5_error_code ipadb_get_pac(krb5_context kcontext,
 
 
     /* == Search PAC info == */
-    kerr = ipadb_deref_search(ipactx, ied->entry_dn, user_pac_attrs,
-                              "memberOf", memberof_pac_attrs, &results);
+    kerr = ipadb_deref_search(ipactx, ied->entry_dn, LDAP_SCOPE_BASE,
+                              "(objectclass=*)", user_pac_attrs,
+                              deref_search_attrs, memberof_pac_attrs,
+                              &results);
     if (kerr) {
         goto done;
     }
-- 
1.7.7.1

>From e3bf0407f9b6fd725c5dc38f86625259c60c45a8 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Sun, 20 Nov 2011 18:36:26 -0500
Subject: [PATCH 3/3] ipa-kdb: Add delgation access control support

---
 daemons/ipa-kdb/Makefile.am          |    1 +
 daemons/ipa-kdb/ipa_kdb.c            |    2 +-
 daemons/ipa-kdb/ipa_kdb_delegation.c |  212 ++++++++++++++++++++++++++++++++++
 3 files changed, 214 insertions(+), 1 deletions(-)
 create mode 100644 daemons/ipa-kdb/ipa_kdb_delegation.c

diff --git a/daemons/ipa-kdb/Makefile.am b/daemons/ipa-kdb/Makefile.am
index c0a579bf4c74ac75a8da5d3f2dd774724e786bc8..77b92e8db865eb4bcc8ced50e0ef0352b6ea334f 100644
--- a/daemons/ipa-kdb/Makefile.am
+++ b/daemons/ipa-kdb/Makefile.am
@@ -34,6 +34,7 @@ ipadb_la_SOURCES = 		\
 	ipa_kdb_principals.c	\
 	ipa_kdb_pwdpolicy.c	\
 	ipa_kdb_mspac.c		\
+	ipa_kdb_delegation.c	\
 	$(KRB5_UTIL_SRCS)	\
 	$(NULL)
 
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
index 05ee18720a11fc6b8579fd00206d1cbb9d5a1a34..ca266d546b9529778eec1b27bc10e0150c952b1f 100644
--- a/daemons/ipa-kdb/ipa_kdb.c
+++ b/daemons/ipa-kdb/ipa_kdb.c
@@ -458,6 +458,6 @@ kdb_vftabl kdb_function_table = {
     NULL,                               /* check_policy_tgs */
     NULL,                               /* audit_as_req */
     NULL,                               /* refresh_config */
-    NULL                                /* check_allowed_to_delegate */
+    ipadb_check_allowed_to_delegate     /* check_allowed_to_delegate */
 };
 
diff --git a/daemons/ipa-kdb/ipa_kdb_delegation.c b/daemons/ipa-kdb/ipa_kdb_delegation.c
new file mode 100644
index 0000000000000000000000000000000000000000..f351b0543f5870d885a20817abfbe8b420cdb404
--- /dev/null
+++ b/daemons/ipa-kdb/ipa_kdb_delegation.c
@@ -0,0 +1,212 @@
+/*
+ * MIT Kerberos KDC database backend for FreeIPA
+ *
+ * Authors: Simo Sorce <sso...@redhat.com>
+ *
+ * Copyright (C) 2011  Simo Sorce, Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * 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/>.
+ */
+
+#include "ipa_kdb.h"
+
+static char *acl_attrs[] = {
+    "objectClass",
+    "memberPrincipal",
+    NULL
+};
+
+static char *search_attrs[] = {
+    "ipaAllowToImpersonate",
+    "ipaAllowedTarget",
+    NULL
+};
+
+static krb5_error_code ipadb_get_delegation_acl(krb5_context kcontext,
+                                                char *srv_principal,
+                                                LDAPMessage **results)
+{
+    struct ipadb_context *ipactx;
+    krb5_error_code kerr;
+    char *filter = NULL;
+    int ret;
+
+    ipactx = ipadb_get_context(kcontext);
+    if (!ipactx) {
+        return KRB5_KDB_DBNOTINITED;
+    }
+
+    ret = asprintf(&filter,
+                   "(&(objectclass=ipaKrb5DelegationACL)"
+                     "(memberPrincipal=%s))", srv_principal);
+    if (ret == -1) {
+        kerr = ENOMEM;
+        goto done;
+    }
+
+    /* == Search ACL info == */
+    kerr = ipadb_deref_search(ipactx, ipactx->base,
+                              LDAP_SCOPE_SUBTREE, filter, acl_attrs,
+                              search_attrs, acl_attrs, results);
+    if (kerr) {
+        goto done;
+    }
+
+done:
+    free(filter);
+    return kerr;
+}
+
+static bool ipadb_match_member(char *princ, LDAPDerefRes *dres)
+{
+    LDAPDerefVal *dval;
+    int i;
+
+    for (dval = dres->attrVals; dval; dval = dval->next) {
+        if (strcasecmp(dval->type, "memberPrincipal") != 0) {
+            continue;
+        }
+
+        for (i = 0; dval->vals[i].bv_val != NULL; i++) {
+            /* FIXME: use utf8 aware comparison ? */
+            /* FIXME: support wildcards ? */
+            if (strncasecmp(princ, dval->vals[i].bv_val,
+                                    dval->vals[i].bv_len) == 0) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+static krb5_error_code ipadb_match_acl(krb5_context kcontext,
+                                       LDAPMessage *results,
+                                       krb5_const_principal client,
+                                       krb5_const_principal target)
+{
+    struct ipadb_context *ipactx;
+    krb5_error_code kerr = ENOENT;
+    LDAPMessage *lentry;
+    LDAPDerefRes *deref_results;
+    LDAPDerefRes *dres;
+    char *client_princ = NULL;
+    char *target_princ = NULL;
+    bool client_missing;
+    bool client_found;
+    bool target_found;
+    int ret;
+
+    ipactx = ipadb_get_context(kcontext);
+    if (!ipactx) {
+        return KRB5_KDB_DBNOTINITED;
+    }
+
+    kerr = krb5_unparse_name(kcontext, client, &client_princ);
+    if (kerr != 0) {
+        goto done;
+    }
+    kerr = krb5_unparse_name(kcontext, target, &target_princ);
+    if (kerr != 0) {
+        goto done;
+    }
+
+    lentry = ldap_first_entry(ipactx->lcontext, results);
+    if (!lentry) {
+        kerr = ENOENT;
+        goto done;
+    }
+
+    while (lentry) {
+        /* both client and target must be found in the same ACI */
+        client_missing = true;
+        client_found = false;
+        target_found = false;
+
+        ret = ipadb_ldap_deref_results(ipactx->lcontext, lentry,
+                                       &deref_results);
+        switch (ret) {
+        case 0:
+            for (dres = deref_results; dres; dres = dres->next) {
+                if (strcasecmp(dres->derefAttr, "ipaAllowToImpersonate") == 0) {
+                    /* NOTE: client_missing is used to signal that the
+                     * attribute was completely missing. This signals that
+                     * ANY client is allowed to be impersonated.
+                     * This logic is valid only for clients, not for targets */
+                    client_missing = false;
+                    client_found = ipadb_match_member(client_princ, dres);
+                }
+                if (strcasecmp(dres->derefAttr, "ipaAllowedTarget") == 0) {
+                    target_found = ipadb_match_member(target_princ, dres);
+                }
+            }
+
+            ldap_derefresponse_free(deref_results);
+            break;
+        case ENOENT:
+            break;
+        default:
+            kerr = ret;
+            goto done;
+        }
+
+        if ((client_found == true || client_missing == true) &&
+            target_found == true) {
+            kerr = 0;
+            goto done;
+        }
+
+        lentry = ldap_next_entry(ipactx->lcontext, lentry);
+    }
+
+done:
+    krb5_free_unparsed_name(kcontext, client_princ);
+    krb5_free_unparsed_name(kcontext, target_princ);
+    return kerr;
+}
+
+/* Ok terminology is confusing here so read carefully:
+ * here 'proxy' is the service for which 'server' wants a ticket on behalf of
+ * 'client' */
+
+krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
+                                                krb5_const_principal client,
+                                                const krb5_db_entry *server,
+                                                krb5_const_principal proxy)
+{
+    krb5_error_code kerr;
+    char *srv_principal = NULL;
+    LDAPMessage *res = NULL;
+
+    kerr = krb5_unparse_name(kcontext, server->princ, &srv_principal);
+    if (kerr) {
+        goto done;
+    }
+
+    kerr = ipadb_get_delegation_acl(kcontext, srv_principal, &res);
+    if (kerr) {
+        goto done;
+    }
+
+    kerr = ipadb_match_acl(kcontext, res, client, proxy);
+    if (kerr) {
+        goto done;
+    }
+
+done:
+    krb5_free_unparsed_name(kcontext, srv_principal);
+    ldap_msgfree(res);
+    return kerr;
+}
-- 
1.7.7.1

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to