From bd0d85804320e840db9b5cf19a5e69b3a0804e20 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Tue, 15 May 2012 20:03:16 +0300 Subject: Add trust-related ACIs A high-level description of the design and ACIs for trusts is available at https://www.redhat.com/archives/freeipa-devel/2011-December/msg00224.html and https://www.redhat.com/archives/freeipa-devel/2011-December/msg00248.html Ticket #1731 --- daemons/ipa-sam/ipa_sam.c | 144 ++++++++++++++++++++++++++++++++--- install/share/smb.conf.template | 2 +- install/tools/ipa-adtrust-install | 5 +- install/updates/60-trusts.update | 36 +++++++++ ipaserver/install/adtrustinstance.py | 90 ++++++---------------- 5 files changed, 196 insertions(+), 81 deletions(-) diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c index c362988d3..2fa670ebd 100644 --- a/daemons/ipa-sam/ipa_sam.c +++ b/daemons/ipa-sam/ipa_sam.c @@ -26,6 +26,10 @@ #include +#include +#include +#include + /* TODO: remove if smbrunsecret() is removed */ typedef struct connection_structi {} connection_struct; struct current_user { @@ -600,7 +604,6 @@ static bool ldapsam_sid_to_id(struct pdb_methods *methods, *gid = strtoul(gid_str, NULL, 10); *type = SID_NAME_DOM_GRP; - store_gid_sid_cache(sid, *gid); ret = true; goto done; } @@ -617,7 +620,6 @@ static bool ldapsam_sid_to_id(struct pdb_methods *methods, *uid = strtoul(value, NULL, 10); *type = SID_NAME_USER; - store_uid_sid_cache(sid, *uid); ret = true; done: @@ -683,8 +685,6 @@ static bool ldapsam_uid_to_sid(struct pdb_methods *methods, uid_t uid, sid_copy(sid, &user_sid); - store_uid_sid_cache(sid, uid); - ret = true; done: @@ -748,8 +748,6 @@ static bool ldapsam_gid_to_sid(struct pdb_methods *methods, gid_t gid, sid_copy(sid, &group_sid); - store_gid_sid_cache(sid, gid); - ret = true; done: @@ -3048,6 +3046,115 @@ done: return status; } +struct ipasam_sasl_interact_priv { + krb5_context context; + krb5_principal principal; + krb5_keytab keytab; + krb5_get_init_creds_opt *options; + krb5_creds creds; + krb5_ccache ccache; + const char *name; + int name_len; +}; + +static int ldap_sasl_interact(LDAP *ld, unsigned flags, void *priv_data, void *sit) +{ + sasl_interact_t *in = NULL; + int ret = LDAP_OTHER; + struct ipasam_sasl_interact_priv *data = (struct ipasam_sasl_interact_priv*) priv_data; + krb5_context krbctx; + char *outname = NULL; + krb5_error_code krberr; + + if (!ld) return LDAP_PARAM_ERROR; + + for (in = sit; in && in->id != SASL_CB_LIST_END; in++) { + switch(in->id) { + case SASL_CB_USER: + in->result = data->name; + in->len = data->name_len; + ret = LDAP_SUCCESS; + break; + case SASL_CB_GETREALM: + in->result = data->principal->realm.data; + in->len = data->principal->realm.length; + ret = LDAP_SUCCESS; + break; + default: + in->result = NULL; + in->len = 0; + ret = LDAP_OTHER; + } + } + return ret; +} + +extern const char *lp_parm_const_string(int snum, const char *type, const char *option, const char *def); +extern void become_root(); +extern void unbecome_root(); +static int bind_callback(LDAP *ldap_struct, struct smbldap_state *ldap_state) +{ + char *ccache_name = NULL; + krb5_error_code rc; + + struct ipasam_sasl_interact_priv data; + int ret; + + data.name = lp_parm_const_string(-1, "ipasam", "principal", NULL); + if (data.name == NULL) { + DEBUG(0, ("bind_callback: ipasam:principal is not set, cannot use GSSAPI bind\n")); + return LDAP_LOCAL_ERROR; + } + + /* + * In order to modify the ccache we need to wrap in become/unbecome root here + */ + become_root(); + data.name_len = strlen(data.name); + + rc = krb5_init_context(&data.context); + + rc = krb5_parse_name(data.context, data.name, &data.principal); + DEBUG(0,("principal is %p (%d)\n", (void*) data.principal, rc)); + + rc = krb5_cc_default(data.context, &data.ccache); + + rc = krb5_cc_initialize(data.context, data.ccache, data.principal); + + rc = krb5_cc_get_full_name(data.context, data.ccache, &ccache_name); + rc = krb5_cc_set_default_name(data.context, ccache_name); + DEBUG(0, ("default ccache is %s\n", krb5_cc_default_name(data.context))); + + rc = krb5_kt_resolve(data.context, "FILE:/etc/samba/samba.keytab", &data.keytab); + DEBUG(0,("keytab is %p (%d)\n", (void*) data.keytab, rc)); + + rc = krb5_get_init_creds_opt_alloc(data.context, &data.options); + DEBUG(0,("options are %p (%d)\n", (void*) data.options, rc)); + + rc = krb5_get_init_creds_opt_set_out_ccache(data.context, data.options, data.ccache); + DEBUG(0,("options are using the ccache (%d)\n", rc)); + + rc = krb5_get_init_creds_keytab(data.context, &data.creds, data.principal, data.keytab, + 0, NULL, data.options); + DEBUG(0,("creds uses keytab (%d)\n", rc)); + + ret = ldap_sasl_interactive_bind_s(ldap_struct, + NULL, "GSSAPI", + NULL, NULL, + LDAP_SASL_QUIET, + ldap_sasl_interact, &data); + if (ret != LDAP_SUCCESS) { + DEBUG(0, ("bind_callback: cannot perform interactive SASL bind with GSSAPI\n")); + } + + krb5_get_init_creds_opt_free(data.context, data.options); + krb5_kt_close(data.context, data.keytab); + krb5_cc_close(data.context, data.ccache); + krb5_free_context(data.context); + unbecome_root(); + return ret; +} + static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method, const char *location) { @@ -3060,6 +3167,7 @@ static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method, struct dom_sid ldap_domain_sid; char *bind_dn = NULL; char *bind_secret = NULL; + const char *service_principal = NULL; LDAPMessage *result = NULL; LDAPMessage *entry = NULL; @@ -3088,13 +3196,27 @@ static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method, return NT_STATUS_NO_MEMORY; } trim_char( uri, '\"', '\"' ); - if (!fetch_ldap_pw(&bind_dn, &bind_secret)) { - DEBUG(0, ("pdb_init_ipasam: Failed to retrieve LDAP password from secrets.tdb\n")); - return NT_STATUS_NO_MEMORY; - } - status = smbldap_init(*pdb_method, pdb_get_tevent_context(), + + service_principal = lp_parm_const_string(-1, "ipasam", "principal", NULL); + + if (service_principal == NULL) { + if (!fetch_ldap_pw(&bind_dn, &bind_secret)) { + DEBUG(0, ("pdb_init_ipasam: Failed to retrieve LDAP password from secrets.tdb\n")); + return NT_STATUS_NO_MEMORY; + } + status = smbldap_init(*pdb_method, pdb_get_tevent_context(), uri, false, bind_dn, bind_secret, &ldap_state->smbldap_state); + } else { + /* We authenticate via GSSAPI and thus will use kerberos principal to bind our access */ + status = smbldap_init(*pdb_method, pdb_get_tevent_context(), + uri, false, NULL, NULL, + &ldap_state->smbldap_state); + if (NT_STATUS_IS_OK(status)) { + ldap_state->smbldap_state->bind_callback = bind_callback; + } + } + talloc_free(uri); if (!NT_STATUS_IS_OK(status)) { return status; diff --git a/install/share/smb.conf.template b/install/share/smb.conf.template index 8ed521b50..3107350aa 100644 --- a/install/share/smb.conf.template +++ b/install/share/smb.conf.template @@ -14,11 +14,11 @@ passdb backend = ipasam:ldapi://$LDAPI_SOCKET disable spoolss = yes ldapsam:trusted=yes ldap ssl = off -ldap admin dn = $SMB_DN ldap suffix = $SUFFIX ldap user suffix = cn=users,cn=accounts ldap group suffix = cn=groups,cn=accounts ldap machine suffix = cn=computers,cn=accounts +ipasam:principal = cifs/$FQDN@$REALM rpc_server:epmapper = external rpc_server:lsarpc = external rpc_server:lsass = external diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install index f82d5bb82..c0b477102 100755 --- a/install/tools/ipa-adtrust-install +++ b/install/tools/ipa-adtrust-install @@ -224,13 +224,16 @@ def main(): print "\t\t * 389: (C)LDAP" print "\t\t * 445: microsoft-ds" print "" - print "\tAdditionally you have to make sure the FreeIPA LDAP server cannot reached" + print "\tAdditionally you have to make sure the FreeIPA LDAP server cannot be reached" print "\tby any domain controller in the Active Directory domain by closing the" print "\tfollowing ports for these servers:" print "\t\tTCP Ports:" print "\t\t * 389, 636: LDAP/LDAPS" print "\tYou may want to choose to REJECT the network packets instead of DROPing them" print "\tto avoid timeouts on the AD domain controllers." + print "" + print "\tWARNING: you MUST re-kinit admin user before using 'ipa trust-*' commands family" + print "\tin order to re-generate Kerberos tickets to include AD-specific information" return 0 diff --git a/install/updates/60-trusts.update b/install/updates/60-trusts.update index 9a320fc46..cfd1ad7e5 100644 --- a/install/updates/60-trusts.update +++ b/install/updates/60-trusts.update @@ -24,3 +24,39 @@ add:objectClasses: (2.16.840.1.113730.3.8.12.4 NAME 'ipaNTDomainAttrs' SUP top A replace:objectClasses: (2.16.840.1.113730.3.8.12.5 NAME 'ipaNTTrustedDomain' SUP top STRUCTURAL DESC 'Trusted Domain Object' MUST ( cn ) MAY ( ipaNTTrustType $$ ipaNTTrustAttributes $$ ipaNTTrustDirection $$ ipaNTTrustPartner $$ ipaNTFlatName $$ ipaNTTrustAuthOutgoing $$ ipaNTTrustAuthIncoming $$ ipaNTSecurityIdentifier $$ ipaNTTrustForestTrustInfo $$ ipaNTTrustPosixOffset $$ ipaNTSupportedEncryptionTypes) )::objectClasses: (2.16.840.1.113730.3.8.12.5 NAME 'ipaNTTrustedDomain' SUP top STRUCTURAL DESC 'Trusted Domain Object' MUST ( cn ) MAY ( ipaNTTrustType $$ ipaNTTrustAttributes $$ ipaNTTrustDirection $$ ipaNTTrustPartner $$ ipaNTFlatName $$ ipaNTTrustAuthOutgoing $$ ipaNTTrustAuthIncoming $$ ipaNTTrustedDomainSID $$ ipaNTTrustForestTrustInfo $$ ipaNTTrustPosixOffset $$ ipaNTSupportedEncryptionTypes) ) add:objectClasses: (2.16.840.1.113730.3.8.12.5 NAME 'ipaNTTrustedDomain' SUP top STRUCTURAL DESC 'Trusted Domain Object' MUST ( cn ) MAY ( ipaNTTrustType $$ ipaNTTrustAttributes $$ ipaNTTrustDirection $$ ipaNTTrustPartner $$ ipaNTFlatName $$ ipaNTTrustAuthOutgoing $$ ipaNTTrustAuthIncoming $$ ipaNTTrustedDomainSID $$ ipaNTTrustForestTrustInfo $$ ipaNTTrustPosixOffset $$ ipaNTSupportedEncryptionTypes) ) +dn: cn=trust admins,cn=groups,cn=accounts,$SUFFIX +default: objectClass: top +default: objectClass: groupofnames +default: objectClass: ipausergroup +default: objectClass: nestedgroup +default: objectClass: ipaobject +default: cn: trust admins +default: description: Trusts administrators group +default: member: uid=admin,cn=users,cn=accounts,$SUFFIX +default: nsAccountLock: FALSE +default: ipaUniqueID: autogenerate + +dn: cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX +default: objectClass: GroupOfNames +default: objectClass: top +default: cn: adtrust agents +default: member: krbprincipalname=cifs/$FQDN@$REALM,cn=services,cn=accounts,$SUFFIX + +dn: cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX +add: member: krbprincipalname=cifs/$FQDN@$REALM,cn=services,cn=accounts,$SUFFIX + +dn: cn=trusts,$SUFFIX +default: objectClass: top +default: objectClass: nsContainer +default: cn: trusts + +# Trust management +# 1. cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX can manage trusts, to allow modification via CIFS +# 2. cn=trust admins,cn=groups,cn=accounts,$SUFFIX can manage trusts (via ipa tools) +dn: cn=trusts,$SUFFIX +add:aci: '(target = "ldap:///cn=trusts,$SUFFIX")(targetattr = "ipaNTTrustType || ipaNTTrustAttributes || ipaNTTrustDirection || ipaNTTrustPartner || ipaNTFlatName || ipaNTTrustAuthOutgoing || ipaNTTrustAuthIncoming || ipaNTSecurityIdentifier || ipaNTTrustForestTrustInfo || ipaNTTrustPosixOffset || ipaNTSupportedEncryptionTypes")(version 3.0;acl "Allow trust system user to create and delete trust accounts"; allow (read,write,add,delete) groupdn="ldap:///cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX";)' +add:aci: '(target = "ldap:///cn=trusts,$SUFFIX")(targetattr = "ipaNTTrustType || ipaNTTrustAttributes || ipaNTTrustDirection || ipaNTTrustPartner || ipaNTFlatName || ipaNTTrustAuthOutgoing || ipaNTTrustAuthIncoming || ipaNTSecurityIdentifier || ipaNTTrustForestTrustInfo || ipaNTTrustPosixOffset || ipaNTSupportedEncryptionTypes")(version 3.0;acl "Allow trust admins manage trust accounts"; allow (read,write,add,delete) groupdn="ldap:///cn=trust admins,cn=groups,cn=accounts,$SUFFIX";)' + +# Samba user should be able to read NT passwords to authenticate +dn: $SUFFIX +add:aci: '(targetattr = "ipaNTHash")(version 3.0; acl "Samba system principals can read NT passwords"; allow (read) groupdn="ldap:///cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX";)' diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py index d9609f4c2..fffa062cd 100644 --- a/ipaserver/install/adtrustinstance.py +++ b/ipaserver/install/adtrustinstance.py @@ -103,6 +103,8 @@ class ADTRUSTInstance(service.Service): self.trust_dn = None self.smb_dom_dn = None self.sub_dict = None + self.cifs_principal = None + self.cifs_agent = None service.Service.__init__(self, "smb", dm_password=dm_password) @@ -111,55 +113,6 @@ class ADTRUSTInstance(service.Service): else: self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore') - def __create_samba_user(self): - print "The user for Samba is %s" % self.smb_dn - try: - self.admin_conn.getEntry(self.smb_dn, ldap.SCOPE_BASE) - root_logger.info("Samba user entry exists, resetting password") - - self.admin_conn.modify_s(self.smb_dn, \ - [(ldap.MOD_REPLACE, "userPassword", self.smb_dn_pwd)]) - return - - except errors.NotFound: - pass - - # The user doesn't exist, add it - entry = ipaldap.Entry(self.smb_dn) - entry.setValues("objectclass", ["account", "simplesecurityobject"]) - entry.setValues("uid", "samba") - entry.setValues("userPassword", self.smb_dn_pwd) - self.admin_conn.addEntry(entry) - - # And finally grant it permission to read NT passwords, we do not want - # to support LM passwords so there is no need to allow access to them. - # Also the premission to create trusted domain objects below the - # domain object is granted. - mod = [(ldap.MOD_ADD, 'aci', - str('(targetattr = "ipaNTHash")' \ - '(version 3.0; acl "Samba user can read NT passwords";' \ - 'allow (read) userdn="ldap:///%s";)' % self.smb_dn)), - (ldap.MOD_ADD, 'aci', - str('(target = "ldap:///cn=ad,cn=trusts,%s")' \ - '(targetattr = "ipaNTTrustType || ipaNTTrustAttributes || ' \ - 'ipaNTTrustDirection || ' \ - 'ipaNTTrustPartner || ipaNTFlatName || ' \ - 'ipaNTTrustAuthOutgoing || ' \ - 'ipaNTTrustAuthIncoming || ' \ - 'ipaNTSecurityIdentifier || ' \ - 'ipaNTTrustForestTrustInfo || ' \ - 'ipaNTTrustPosixOffset || ' \ - 'ipaNTSupportedEncryptionTypes")' \ - '(version 3.0;acl "Allow samba user to create and delete ' \ - 'trust accounts";' \ - 'allow (write,add,delete) userdn = "ldap:///%s";)' % \ - (self.suffix, self.smb_dn)))] - - try: - self.admin_conn.modify_s(self.suffix, mod) - except ldap.TYPE_OR_VALUE_EXISTS: - root_logger.debug("samba user aci already exists in suffix %s on %s" % (self.suffix, self.admin_conn.host)) - def __gen_sid_string(self): sub_ids = struct.unpack("