From 4be3de451c8b2a6314c29df43e5ade17f39d8777 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy 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 --- 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/@, 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: @ is only used + * for SSSD to be able to talk to AD DCs but it has to + * have canonical name set to $ 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/@, enabled by default */ + failed += !set_krb_princ(ipasam_state, tmp_ctx, + princ_l, NULL, + pwd_outgoing, trusted_dn, + KRB_PRINC_CREATE_DEFAULT); + + /* Second: $@, 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