mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
ipa-kdb: Add delgation access control support
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
124
daemons/ipa-kdb/README.s4u2proxy.txt
Normal file
124
daemons/ipa-kdb/README.s4u2proxy.txt
Normal file
@@ -0,0 +1,124 @@
|
||||
It is now 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.
|
||||
|
||||
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 in
|
||||
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 proxies[1]. That is: the
|
||||
principals of the services that want to impersonate client principals
|
||||
against other services.
|
||||
|
||||
The ipaAllowedToImpersonate must point to a groupOfPrincipal based
|
||||
object that contains the list of client principals (normally these are
|
||||
user principals) 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 service principals that the proxy service is
|
||||
allowed target 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.com@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.com@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.
|
||||
|
||||
|
||||
[1]
|
||||
Note that here I use the term proxy in a different way than it is used in
|
||||
the krb interfaces. It may seem a bit confusing but I think people will
|
||||
understand it better this way.
|
||||
|
||||
In this document 'client' connects to 'proxy' which impersonates 'client'
|
||||
against 'service'.
|
||||
In the Code/API the 'client' connects to 'server' which impersonates
|
||||
'client' against 'proxy'.
|
||||
@@ -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 */
|
||||
};
|
||||
|
||||
|
||||
@@ -223,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);
|
||||
|
||||
209
daemons/ipa-kdb/ipa_kdb_delegation.c
Normal file
209
daemons/ipa-kdb/ipa_kdb_delegation.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* MIT Kerberos KDC database backend for FreeIPA
|
||||
*
|
||||
* Authors: Simo Sorce <ssorce@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);
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user