From 4be3de451c8b2a6314c29df43e5ade17f39d8777 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Fri, 22 Mar 2019 18:56:52 +0200
Subject: [PATCH] trusts: add support for one-way shared secret trust
Refactor ipa-sam code to generate principals with additional POSIX
information so that FreeIPA is capable to establish trust when using a
shared secret from Active Directory domain controller side.
Trust verification process from Samba AD DC or Microsoft Windows AD DC
side requires us to have a working local TDO object with POSIX
attributes so that smbd would be able to map incoming authenticated
Kerberos principal for the TDO to a local POSIX account.
Note that FreeIPA stores TDO objects in a subtree of cn=trusts,$SUFFIX
and thus SSSD is not able to see these POSIX accounts unless
specifically instructed to do so via multiple search bases. The support
for automatically enabling cn=trusts,$SUFFIX search base in IPA server
mode was added to SSSD 1.16.3 and 2.1.0 with the commit
https://pagure.io/SSSD/sssd/c/14faec9cd9437ef116ae054412d25ec2e820e409
Fixes: https://pagure.io/freeipa/issue/6077
(cherry picked from commit f30f7e380ef9d327ced3e1b0e5c800a8b1069097)
Reviewed-By: Christian Heimes <cheimes@redhat.com>
---
daemons/ipa-sam/ipa_sam.c | 232 +++++++++++++++++++++++++++++---------
1 file changed, 179 insertions(+), 53 deletions(-)
diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c
index 675a511f0febf13cc5e00b547c18a050ac534f2e..3cf878c3f99774f7715f776c31d70e2950f9451c 100644
--- a/daemons/ipa-sam/ipa_sam.c
+++ b/daemons/ipa-sam/ipa_sam.c
@@ -140,6 +140,7 @@ bool E_md4hash(const char *passwd, uint8_t p16[16]); /* available in libcliauth-
#define LDAP_ATTRIBUTE_OBJECTCLASS "objectClass"
#define LDAP_ATTRIBUTE_HOME_DRIVE "ipaNTHomeDirectoryDrive"
#define LDAP_ATTRIBUTE_HOME_PATH "ipaNTHomeDirectory"
+#define LDAP_ATTRIBUTE_HOMEDIRECTORY "homeDirectory"
#define LDAP_ATTRIBUTE_LOGON_SCRIPT "ipaNTLogonScript"
#define LDAP_ATTRIBUTE_PROFILE_PATH "ipaNTProfilePath"
#define LDAP_ATTRIBUTE_SID_BLACKLIST_INCOMING "ipaNTSIDBlacklistIncoming"
@@ -1797,9 +1798,10 @@ done:
#define KRB_PRINC_CREATE_DISABLED 0x00000001
#define KRB_PRINC_CREATE_AGENT_PERMISSION 0x00000002
+
static bool set_krb_princ(struct ipasam_private *ipasam_state,
TALLOC_CTX *mem_ctx,
- const char *princ, const char *saltprinc,
+ const char *princ, const char *alias,
const char *pwd,
const char *base_dn,
uint32_t create_flags)
@@ -1857,14 +1859,15 @@ static bool set_krb_princ(struct ipasam_private *ipasam_state,
LDAP_ATTRIBUTE_KRB_CANONICAL, princ);
smbldap_set_mod(&mods, LDAP_MOD_ADD,
LDAP_ATTRIBUTE_KRB_PRINCIPAL, princ);
- if (saltprinc) {
- smbldap_set_mod(&mods, LDAP_MOD_ADD,
- LDAP_ATTRIBUTE_KRB_PRINCIPAL, saltprinc);
- }
+ if (alias) {
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ LDAP_ATTRIBUTE_KRB_PRINCIPAL, alias);
+ }
if ((create_flags & KRB_PRINC_CREATE_DISABLED)) {
- smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
- LDAP_ATTRIBUTE_KRB_TICKET_FLAGS, __TALLOC_STRING_LINE2__(IPASAM_DISALLOW_ALL_TIX));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ LDAP_ATTRIBUTE_KRB_TICKET_FLAGS,
+ __TALLOC_STRING_LINE2__(IPASAM_DISALLOW_ALL_TIX));
}
if ((create_flags & KRB_PRINC_CREATE_AGENT_PERMISSION)) {
@@ -1877,18 +1880,19 @@ static bool set_krb_princ(struct ipasam_private *ipasam_state,
smbldap_set_mod(&mods, LDAP_MOD_ADD,
LDAP_ATTRIBUTE_OBJECTCLASS,
LDAP_OBJ_IPAOPALLOW);
- smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
- LDAP_ATTRIBUTE_IPAOPALLOW, agent_dn);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ LDAP_ATTRIBUTE_IPAOPALLOW,
+ agent_dn);
agent_dn = talloc_asprintf(mem_ctx, LDAP_CN_ADTRUST_ADMINS",%s", ipasam_state->base_dn);
if (agent_dn == NULL) {
DEBUG(1, ("error configuring cross realm principal data for trust admins!\n"));
return false;
}
- smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
- LDAP_ATTRIBUTE_IPAOPALLOW, agent_dn);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ LDAP_ATTRIBUTE_IPAOPALLOW,
+ agent_dn);
}
-
if (entry == NULL) {
ret = smbldap_add(ipasam_state->ldap_state, dn, mods);
} else {
@@ -1899,7 +1903,7 @@ static bool set_krb_princ(struct ipasam_private *ipasam_state,
return false;
}
- ret = set_cross_realm_pw(ipasam_state, saltprinc ? saltprinc : princ, pwd);
+ ret = set_cross_realm_pw(ipasam_state, princ, pwd);
if (ret != 0) {
DEBUG(1, ("set_cross_realm_pw failed.\n"));
return false;
@@ -1941,18 +1945,21 @@ enum princ_mod {
};
static bool handle_cross_realm_princs(struct ipasam_private *ipasam_state,
- const char *domain, const char *pwd,
+ const char *domain, const char *flat_name,
+ const char *pwd_incoming,
+ const char *pwd_outgoing,
uint32_t trust_direction,
enum princ_mod mod)
{
char *trusted_dn;
char *princ_l;
char *princ_r;
- char *princ_tdo;
- char *saltprinc_tdo;
+ char *princ_r_tdo, *princ_l_tdo;
char *remote_realm;
bool ok;
+ int failed = 0;
TALLOC_CTX *tmp_ctx;
+ const char *r_tdo_alias, *l_tdo_alias;
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
@@ -1967,46 +1974,111 @@ static bool handle_cross_realm_princs(struct ipasam_private *ipasam_state,
trusted_dn = trusted_domain_dn(tmp_ctx, ipasam_state, domain);
- princ_l = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", remote_realm,
- ipasam_state->realm);
- princ_r = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
- ipasam_state->realm, remote_realm);
+ princ_l = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
+ remote_realm, ipasam_state->realm);
+ princ_l_tdo = talloc_asprintf(tmp_ctx, "%s$@%s",
+ flat_name, ipasam_state->realm);
+ l_tdo_alias = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
+ flat_name, ipasam_state->realm);
- princ_tdo = talloc_asprintf(tmp_ctx, "%s$@%s",
- ipasam_state->flat_name, remote_realm);
+ princ_r = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
+ ipasam_state->realm, remote_realm);
+ princ_r_tdo = talloc_asprintf(tmp_ctx, "%s$@%s",
+ ipasam_state->flat_name, remote_realm);
- saltprinc_tdo = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
+ r_tdo_alias = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
ipasam_state->flat_name, remote_realm);
- if (trusted_dn == NULL || princ_l == NULL ||
- princ_r == NULL || princ_tdo == NULL || saltprinc_tdo == NULL) {
+ if (trusted_dn == NULL || princ_l == NULL || princ_l_tdo == NULL ||
+ l_tdo_alias == NULL || princ_r == NULL || princ_r_tdo == NULL ||
+ r_tdo_alias == NULL) {
ok = false;
goto done;
}
switch (mod) {
case SET_PRINC:
- /* Create Kerberos principal for inbound trust, enabled by default */
- ok = set_krb_princ(ipasam_state, tmp_ctx, princ_r, NULL, pwd, trusted_dn, KRB_PRINC_CREATE_DEFAULT);
- /* Create Kerberos principal corresponding to TDO in AD for SSSD usage, disabled by default */
- ok |= set_krb_princ(ipasam_state, tmp_ctx, princ_tdo, saltprinc_tdo, pwd, trusted_dn,
- KRB_PRINC_CREATE_DISABLED | KRB_PRINC_CREATE_AGENT_PERMISSION);
- if ((trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) != 0) {
- /* Create Kerberos principal for outbound trust, enabled by default */
- ok |= set_krb_princ(ipasam_state, tmp_ctx, princ_l, NULL, pwd, trusted_dn, KRB_PRINC_CREATE_DEFAULT);
+ /* We must use two sets by two principals here because
+ * they are used for different needs and must have
+ * different salts */
+
+ failed = 0;
+ /* INBOUND TRUST */
+ if ((trust_direction & LSA_TRUST_DIRECTION_INBOUND) != 0) {
+ /* First: krbtgt/<OUR REALM>@<REMOTE REALM>, enabled by default
+ * in case of the inboud trust */
+ failed += !set_krb_princ(ipasam_state, tmp_ctx, princ_r, NULL,
+ pwd_outgoing, trusted_dn,
+ KRB_PRINC_CREATE_DEFAULT);
+
+ /* Second: <OUR FLATNAME$>@<REMOTE REALM> is only used
+ * for SSSD to be able to talk to AD DCs but it has to
+ * have canonical name set to <OUR FLATNAME>$ because
+ * this is the salt used by AD DCs when using this
+ * principal, otherwise authentication will fail.
+ *
+ * *disable* use of this principal on our side as it is
+ * only used to retrieve trusted domain credentials by
+ * AD Trust Agents across the IPA topology */
+ failed += !set_krb_princ(ipasam_state, tmp_ctx,
+ r_tdo_alias, princ_r_tdo,
+ pwd_incoming, trusted_dn,
+ (KRB_PRINC_CREATE_DISABLED |
+ KRB_PRINC_CREATE_AGENT_PERMISSION));
+
+ ok = (failed == 0);
+ if (!ok) {
+ goto done;
+ }
}
- if (!ok) {
- goto done;
+
+ failed = 0;
+ /* OUTBOUND TRUST */
+ if ((trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) != 0) {
+ /* First: krbtgt/<REMOTE REALM>@<OUR REALM>, enabled by default */
+ failed += !set_krb_princ(ipasam_state, tmp_ctx,
+ princ_l, NULL,
+ pwd_outgoing, trusted_dn,
+ KRB_PRINC_CREATE_DEFAULT);
+
+ /* Second: <REMOTE FLAT NAME>$@<OUR REALM>, enabled by default
+ * as it is used for a remote DC to authenticate against IPA Samba
+ *
+ * A local account for the outbound trust must have
+ * POSIX and SMB identities associated with our domain but we associate
+ * them with the trust domain object itself */
+ failed += !set_krb_princ(ipasam_state, tmp_ctx,
+ princ_l_tdo, l_tdo_alias,
+ pwd_incoming, trusted_dn,
+ KRB_PRINC_CREATE_DEFAULT);
+
+ ok = (failed == 0);
+ if (!ok) {
+ goto done;
+ }
}
break;
case DEL_PRINC:
- ok = del_krb_princ(ipasam_state, tmp_ctx, princ_r, trusted_dn);
- ok |= del_krb_princ(ipasam_state, tmp_ctx, princ_tdo, trusted_dn);
- if ((trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) != 0) {
- ok |= del_krb_princ(ipasam_state, tmp_ctx, princ_l, trusted_dn);
+ failed = 0;
+ if ((trust_direction & LSA_TRUST_DIRECTION_INBOUND) != 0) {
+ failed += !del_krb_princ(ipasam_state, tmp_ctx, princ_r, trusted_dn);
+ failed += !del_krb_princ(ipasam_state, tmp_ctx, princ_r_tdo, trusted_dn);
+
+ ok = (failed == 0);
+ if (!ok) {
+ goto done;
+ }
}
- if (!ok) {
- goto done;
+
+ failed = 0;
+ if ((trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) != 0) {
+ failed += !del_krb_princ(ipasam_state, tmp_ctx, princ_l, trusted_dn);
+ failed += !del_krb_princ(ipasam_state, tmp_ctx, princ_l_tdo, trusted_dn);
+
+ ok = (failed == 0);
+ if (!ok) {
+ goto done;
+ }
}
break;
default:
@@ -2022,16 +2094,22 @@ done:
}
static bool set_cross_realm_princs(struct ipasam_private *ipasam_state,
- const char *domain, const char *pwd, uint32_t trust_direction)
+ const char *domain, const char* flat_name,
+ const char *pwd_incoming, const char *pwd_outgoing,
+ uint32_t trust_direction)
{
- return handle_cross_realm_princs(ipasam_state, domain, pwd, trust_direction, SET_PRINC);
+ return handle_cross_realm_princs(ipasam_state, domain, flat_name,
+ pwd_incoming,
+ pwd_outgoing,
+ trust_direction, SET_PRINC);
}
static bool del_cross_realm_princs(struct ipasam_private *ipasam_state,
- const char *domain)
+ const char *domain, const char *flat_name)
{
uint32_t trust_direction = LSA_TRUST_DIRECTION_INBOUND | LSA_TRUST_DIRECTION_OUTBOUND;
- return handle_cross_realm_princs(ipasam_state, domain, NULL, trust_direction, DEL_PRINC);
+ return handle_cross_realm_princs(ipasam_state, domain, flat_name,
+ NULL, NULL, trust_direction, DEL_PRINC);
}
static bool get_trusted_domain_int(struct ipasam_private *ipasam_state,
@@ -2439,8 +2517,8 @@ static NTSTATUS ipasam_set_trusted_domain(struct pdb_methods *methods,
int ret, i, count;
NTSTATUS status;
TALLOC_CTX *tmp_ctx;
- char *trustpw;
- char *sid;
+ char *trustpw_incoming, *trustpw_outgoing;
+ char *sid, *tda_name;
char **in_blacklist = NULL;
char **out_blacklist = NULL;
uint32_t enctypes, trust_offset;
@@ -2465,6 +2543,8 @@ static NTSTATUS ipasam_set_trusted_domain(struct pdb_methods *methods,
LDAP_OBJ_TRUSTED_DOMAIN);
smbldap_make_mod(priv2ld(ipasam_state), entry, &mods, "objectClass",
LDAP_OBJ_ID_OBJECT);
+ smbldap_make_mod(priv2ld(ipasam_state), entry, &mods, "objectClass",
+ LDAP_OBJ_POSIXACCOUNT);
}
if (entry != NULL) {
@@ -2477,12 +2557,23 @@ static NTSTATUS ipasam_set_trusted_domain(struct pdb_methods *methods,
smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
LDAP_ATTRIBUTE_GIDNUMBER,
ipasam_state->fallback_primary_group_gid_str);
+ smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
+ LDAP_ATTRIBUTE_HOMEDIRECTORY,
+ "/dev/null");
}
if (td->netbios_name != NULL) {
+ tda_name = talloc_asprintf(tmp_ctx, "%s$", td->netbios_name);
+ if (!tda_name) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
LDAP_ATTRIBUTE_FLAT_NAME,
td->netbios_name);
+ smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
+ LDAP_ATTRIBUTE_UID,
+ tda_name);
}
if (td->domain_name != NULL) {
@@ -2618,13 +2709,38 @@ static NTSTATUS ipasam_set_trusted_domain(struct pdb_methods *methods,
if (entry == NULL) { /* FIXME: allow password updates here */
status = get_trust_pwd(tmp_ctx, &td->trust_auth_incoming,
- &trustpw, NULL);
+ &trustpw_incoming, NULL);
if (!NT_STATUS_IS_OK(status)) {
goto done;
}
- res = set_cross_realm_princs(ipasam_state, td->domain_name,
- trustpw, td->trust_direction);
- memset(trustpw, 0, strlen(trustpw));
+ status = get_trust_pwd(tmp_ctx, &td->trust_auth_outgoing,
+ &trustpw_outgoing, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ res = set_cross_realm_princs(ipasam_state, td->domain_name, td->netbios_name,
+ trustpw_incoming, trustpw_outgoing,
+ td->trust_direction);
+ {
+ /* Replace memset() use by an explicit loop to avoid
+ * both compile time and link time optimisations.
+ * We could have used memset_s() from C++11 but it is
+ * currently not implemented by GCC or glibc.
+ */
+ volatile char *p = (void *) trustpw_incoming;
+ volatile char *q = (void *) trustpw_outgoing;
+ size_t plen = strlen(trustpw_incoming);
+ size_t qlen = strlen(trustpw_outgoing);
+
+ while (plen--) {
+ *p++ = '\0';
+ }
+
+ while (qlen--) {
+ *q++ = '\0';
+ }
+ }
+
if (!res) {
DEBUG(1, ("error writing cross realm principals!\n"));
status = NT_STATUS_UNSUCCESSFUL;
@@ -2693,7 +2809,7 @@ static NTSTATUS ipasam_del_trusted_domain(struct pdb_methods *methods,
talloc_get_type_abort(methods->private_data, struct ipasam_private);
LDAPMessage *entry = NULL;
char *dn;
- const char *domain_name;
+ const char *domain_name, *flat_name;
TALLOC_CTX *tmp_ctx;
NTSTATUS status;
@@ -2731,7 +2847,17 @@ static NTSTATUS ipasam_del_trusted_domain(struct pdb_methods *methods,
goto done;
}
- if (!del_cross_realm_princs(ipasam_state, domain_name)) {
+ flat_name = get_single_attribute(tmp_ctx, priv2ld(ipasam_state), entry,
+ LDAP_ATTRIBUTE_FLAT_NAME);
+ if (flat_name == NULL) {
+ DEBUG(1, ("Attribute %s not present.\n",
+ LDAP_ATTRIBUTE_FLAT_NAME));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+
+ if (!del_cross_realm_princs(ipasam_state, domain_name, flat_name)) {
DEBUG(1, ("error deleting cross realm principals!\n"));
status = NT_STATUS_UNSUCCESSFUL;
goto done;
--
2.20.1