mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-12 17:21:55 -06:00
443a99060c
When working with aliased entries, we need a reliable way to detect whether two principals reference the same database entry. This is important in S4U checks. Ideally, we should be using SIDs for these checks as S4U requires PAC record presence which cannot be issued without a SID associated with an entry. This is true for user principals and a number of host/service principals associated with Samba. Other service principals do not have SIDs because we do not allocate POSIX IDs to them in FreeIPA. When PAC is issued for these principals, they get SID of a domain computer or domain controller depending on their placement (IPA client or IPA server). Since 389-ds always returns unique entry DN for the same entry, rely on this value instead. We could have used ipaUniqueID but for Kerberos principals created through the KDB (kadmin/kdb5_util) we don't have ipaUniqueID in the entry. Fixes: https://pagure.io/freeipa/issue/9031 Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com> Reviewed-by: Rob Crittenden <rcritten@redhat.com> Reviewed-By: Rob Crittenden <rcritten@redhat.com>
249 lines
7.3 KiB
C
249 lines
7.3 KiB
C
/*
|
|
* 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"
|
|
#include <strings.h>
|
|
#include <unicase.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;
|
|
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;
|
|
}
|
|
|
|
/* the default is that we fail */
|
|
kerr = ENOENT;
|
|
|
|
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 (client_found == false &&
|
|
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 (target_found == false &&
|
|
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, result;
|
|
char *srv_principal = NULL;
|
|
krb5_db_entry *proxy_entry = NULL;
|
|
struct ipadb_e_data *ied_server, *ied_proxy;
|
|
LDAPMessage *res = NULL;
|
|
|
|
/* Handle the case where server == proxy, this is allowed in S4U*/
|
|
kerr = ipadb_get_principal(kcontext, proxy,
|
|
KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY,
|
|
&proxy_entry);
|
|
if (kerr) {
|
|
goto done;
|
|
}
|
|
|
|
ied_server = (struct ipadb_e_data *) server->e_data;
|
|
ied_proxy = (struct ipadb_e_data *) proxy_entry->e_data;
|
|
|
|
/* If we have SIDs for both entries, compare SIDs */
|
|
if ((ied_server->has_sid && ied_server->sid != NULL) &&
|
|
(ied_proxy->has_sid && ied_proxy->sid != NULL)) {
|
|
|
|
if (dom_sid_check(ied_server->sid, ied_proxy->sid, true)) {
|
|
kerr = 0;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Otherwise, compare entry DNs */
|
|
kerr = ulc_casecmp(ied_server->entry_dn, strlen(ied_server->entry_dn),
|
|
ied_proxy->entry_dn, strlen(ied_proxy->entry_dn),
|
|
NULL, NULL, &result);
|
|
if (kerr == 0 && result == 0) {
|
|
goto done;
|
|
}
|
|
|
|
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:
|
|
ipadb_free_principal(kcontext, proxy_entry);
|
|
krb5_free_unparsed_name(kcontext, srv_principal);
|
|
ldap_msgfree(res);
|
|
return kerr;
|
|
}
|