summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Bokovoy <abokovoy@redhat.com>2012-05-15 20:03:16 +0300
committerMartin Kosek <mkosek@redhat.com>2012-06-07 09:39:10 +0200
commitbd0d85804320e840db9b5cf19a5e69b3a0804e20 (patch)
tree0741271180b207f68b652570c9930ebc3fdbe105
parent000bcfe34f318f613ec7c8744b3f886ef4ffb8ba (diff)
downloadfreeipa-bd0d85804320e840db9b5cf19a5e69b3a0804e20.tar.gz
freeipa-bd0d85804320e840db9b5cf19a5e69b3a0804e20.tar.xz
freeipa-bd0d85804320e840db9b5cf19a5e69b3a0804e20.zip
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
-rw-r--r--daemons/ipa-sam/ipa_sam.c144
-rw-r--r--install/share/smb.conf.template2
-rwxr-xr-xinstall/tools/ipa-adtrust-install5
-rw-r--r--install/updates/60-trusts.update36
-rw-r--r--ipaserver/install/adtrustinstance.py90
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 <passdb.h>
+#include <sasl/sasl.h>
+#include <krb5/krb5.h>
+#include <time.h>
+
/* 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("<LLL", os.urandom(12))
return "S-1-5-21-%d-%d-%d" % (sub_ids[0], sub_ids[1], sub_ids[2])
@@ -275,17 +228,18 @@ class ADTRUSTInstance(service.Service):
finally:
os.remove(tmp_name)
- def __set_smb_ldap_password(self):
- args = ["/usr/bin/smbpasswd", "-c", self.smb_conf, "-s", "-W" ]
-
- ipautil.run(args, stdin = self.smb_dn_pwd + "\n" + \
- self.smb_dn_pwd + "\n" )
-
def __setup_principal(self):
- cifs_principal = "cifs/" + self.fqdn + "@" + self.realm_name
-
try:
- api.Command.service_add(unicode(cifs_principal))
+ api.Command.service_add(unicode(self.cifs_principal))
+ # Add the principal to the 'adtrust agents' group
+ # as 389-ds only operates with GroupOfNames, we have to use
+ # the principal's proper dn as defined in self.cifs_agent
+ entry = self.admin_conn.getEntry(self.smb_dn, ldap.SCOPE_BASE)
+ current = ipaldap.Entry(self.smb_dn, entry.toDict())
+ if not('member' in current):
+ current['member'] = []
+ entry.setValues("member", current['member'] + [self.cifs_agent])
+ self.admin_conn.updateEntry(self.smb_dn, current, entry)
except Exception, e:
# CIFS principal already exists, it is not the first time adtrustinstance is managed
# That's fine, we we'll re-extract the key again.
@@ -294,18 +248,18 @@ class ADTRUSTInstance(service.Service):
samba_keytab = "/etc/samba/samba.keytab"
if os.path.exists(samba_keytab):
try:
- ipautil.run(["ipa-rmkeytab", "--principal", cifs_principal,
+ ipautil.run(["ipa-rmkeytab", "--principal", self.cifs_principal,
"-k", samba_keytab])
except ipautil.CalledProcessError, e:
if e.returncode != 5:
- root_logger.critical("Failed to remove old key for %s" % cifs_principal)
+ root_logger.critical("Failed to remove old key for %s" % self.cifs_principal)
try:
ipautil.run(["ipa-getkeytab", "--server", self.fqdn,
- "--principal", cifs_principal,
+ "--principal", self.cifs_principal,
"-k", samba_keytab])
except ipautil.CalledProcessError, e:
- root_logger.critical("Failed to add key for %s" % cifs_principal)
+ root_logger.critical("Failed to add key for %s" % self.cifs_principal)
def __add_dns_service_records(self):
"""
@@ -393,7 +347,8 @@ class ADTRUSTInstance(service.Service):
SUFFIX = self.suffix,
NETBIOS_NAME = self.netbios_name,
SMB_DN = self.smb_dn,
- LDAPI_SOCKET = self.ldapi_socket)
+ LDAPI_SOCKET = self.ldapi_socket,
+ FQDN = self.fqdn)
def setup(self, fqdn, ip_address, realm_name, domain_name, netbios_name,
no_msdcs=False, smbd_user="samba"):
@@ -410,12 +365,14 @@ class ADTRUSTInstance(service.Service):
self.smb_conf = "/etc/samba/smb.conf"
- self.smb_dn = "uid=samba,cn=sysaccounts,cn=etc,%s" % self.suffix
- self.smb_dn_pwd = ipautil.ipa_generate_password()
+ self.smb_dn = "cn=adtrust agents,cn=sysaccounts,cn=etc,%s" % self.suffix
self.trust_dn = "cn=trusts,%s" % self.suffix
self.smb_dom_dn = "cn=%s,cn=ad,cn=etc,%s" % (self.domain_name, \
self.suffix)
+ self.cifs_principal = "cifs/" + self.fqdn + "@" + self.realm_name
+ self.cifs_agent = "krbprincipalname=%s,cn=services,cn=accounts,%s" % \
+ (self.cifs_principal.lower(), self.suffix)
self.__setup_sub_dict()
@@ -425,13 +382,10 @@ class ADTRUSTInstance(service.Service):
self.ldap_connect()
self.step("stopping smbd", self.__stop)
- self.step("creating samba user", self.__create_samba_user)
self.step("creating samba domain object", \
self.__create_samba_domain_object)
self.step("creating samba config registry", self.__write_smb_registry)
self.step("writing samba config file", self.__write_smb_conf)
- self.step("setting password for the samba user", \
- self.__set_smb_ldap_password)
self.step("adding cifs Kerberos principal", self.__setup_principal)
self.step("adding admin(group) SIDs", self.__add_admin_sids)
self.step("activating CLDAP plugin", self.__add_cldap_module)