diff --git a/daemons/configure.ac b/daemons/configure.ac index 2f6a3e787..3e8e81f5e 100644 --- a/daemons/configure.ac +++ b/daemons/configure.ac @@ -252,6 +252,16 @@ AC_CHECK_LIB([wbclient], [$SAMBA40EXTRA_LIBPATH]) AC_SUBST(WBCLIENT_LIBS) +AC_CHECK_LIB([pdb], + [make_pdb_method], + [HAVE_LIBPDB=1], + [AC_MSG_ERROR([libpdb does not have make_pdb_method])], + [$SAMBA40EXTRA_LIBPATH]) +AC_CHECK_LIB([pdb],[pdb_enum_upn_suffixes], + [AC_DEFINE([HAVE_PDB_ENUM_UPN_SUFFIXES], [1], [Ability to enumerate UPN suffixes])], + [AC_MSG_WARN([libpdb does not have pdb_enum_upn_suffixes, no support for realm domains in ipasam])], + [$SAMBA40EXTRA_LIBPATH]) + dnl --------------------------------------------------------------------------- dnl - Check for check unit test framework http://check.sourceforge.net/ dnl --------------------------------------------------------------------------- diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c index dd3ad617b..4a2fca563 100644 --- a/daemons/ipa-sam/ipa_sam.c +++ b/daemons/ipa-sam/ipa_sam.c @@ -1,6 +1,7 @@ #define HAVE_IMMEDIATE_STRUCTURES 1 #define LDAP_DEPRECATED 1 +#include "config.h" #include #include #include @@ -127,6 +128,7 @@ bool secrets_store(const char *key, const void *data, size_t size); /* available #define LDAP_ATTRIBUTE_NTHASH "ipaNTHash" #define LDAP_ATTRIBUTE_UIDNUMBER "uidnumber" #define LDAP_ATTRIBUTE_GIDNUMBER "gidnumber" +#define LDAP_ATTRIBUTE_ASSOCIATED_DOMAIN "associatedDomain" #define LDAP_OBJ_KRB_PRINCIPAL "krbPrincipal" #define LDAP_OBJ_KRB_PRINCIPAL_AUX "krbPrincipalAux" @@ -141,6 +143,9 @@ bool secrets_store(const char *key, const void *data, size_t size); /* available #define LDAP_OBJ_NESTEDGROUP "nestedGroup" #define LDAP_OBJ_IPAUSERGROUP "ipaUserGroup" #define LDAP_OBJ_POSIXGROUP "posixGroup" +#define LDAP_OBJ_DOMAINRELATED "domainRelatedObject" + +#define LDAP_CN_REALM_DOMAINS "cn=Realm Domains,cn=ipa,cn=etc" #define HAS_KRB_PRINCIPAL (1<<0) #define HAS_KRB_PRINCIPAL_AUX (1<<1) @@ -167,6 +172,12 @@ struct ipasam_privates { struct sss_idmap_ctx *idmap_ctx; }; + +static NTSTATUS ipasam_get_domain_name(struct ldapsam_privates *ldap_state, + TALLOC_CTX *mem_ctx, + char **domain_name); + + static void *idmap_talloc(size_t size, void *pvt) { return talloc_size(pvt, size); @@ -295,6 +306,56 @@ static LDAP *priv2ld(struct ldapsam_privates *priv) return priv->smbldap_state->ldap_struct; } +/* + * get_attribute_values() returns array of all values of the attribute + * allocated over mem_ctx + */ +static char **get_attribute_values(TALLOC_CTX *mem_ctx, LDAP *ldap_struct, + LDAPMessage *entry, const char *attribute, int *num_values) +{ + struct berval **values; + int count, i; + char **result = NULL; + size_t conv_size; + + if (attribute == NULL || entry == NULL) { + return NULL; + } + + values = ldap_get_values_len(ldap_struct, entry, attribute); + if (values == NULL) { + DEBUG(10, ("Attribute [%s] not found.\n", attribute)); + return NULL; + } + + count = ldap_count_values_len(values); + if (count == 0) { + goto done; + } + + result = talloc_array(mem_ctx, char *, count); + if (result == NULL) { + goto done; + } + + *num_values = count; + for (i = 0; i < count; i++) { + if (!convert_string_talloc(result, CH_UTF8, CH_UNIX, + values[i]->bv_val, values[i]->bv_len, + &result[i], &conv_size)) { + DEBUG(10, ("Failed to convert %dth value of [%s] out of %d.\n", + i, attribute, count)); + talloc_free(result); + result = NULL; + goto done; + } + } + +done: + ldap_value_free_len(values); + return result; +} + static char *get_single_attribute(TALLOC_CTX *mem_ctx, LDAP *ldap_struct, LDAPMessage *entry, const char *attribute) { @@ -3250,9 +3311,8 @@ static struct pdb_domain_info *pdb_ipasam_get_domain_info(struct pdb_methods *pd goto fail; } - /* TODO: read dns_domain, dns_forest and guid from LDAP */ - info->dns_domain = strlower_talloc(info, ldap_state->ipasam_privates->realm); - if (info->dns_domain == NULL) { + status = ipasam_get_domain_name(ldap_state, info, &info->dns_domain); + if (!NT_STATUS_IS_OK(status) || (info->dns_domain == NULL)) { goto fail; } info->dns_forest = talloc_strdup(info, info->dns_domain); @@ -3464,7 +3524,7 @@ static NTSTATUS ipasam_get_base_dn(struct smbldap_state *smbldap_state, static NTSTATUS ipasam_get_domain_name(struct ldapsam_privates *ldap_state, TALLOC_CTX *mem_ctx, - const char **domain_name) + char **domain_name) { int ret; LDAPMessage *result; @@ -3473,14 +3533,14 @@ static NTSTATUS ipasam_get_domain_name(struct ldapsam_privates *ldap_state, char *cn; struct smbldap_state *smbldap_state = ldap_state->smbldap_state; const char *attr_list[] = { - "associatedDomain", + LDAP_ATTRIBUTE_ASSOCIATED_DOMAIN, NULL }; ret = smbldap_search(smbldap_state, ldap_state->ipasam_privates->base_dn, LDAP_SCOPE_BASE, - "objectclass=domainRelatedObject", attr_list, 0, + "objectclass=" LDAP_OBJ_DOMAINRELATED, attr_list, 0, &result); if (ret != LDAP_SUCCESS) { DEBUG(1, ("Failed to get domain name: %s\n", @@ -3494,11 +3554,10 @@ static NTSTATUS ipasam_get_domain_name(struct ldapsam_privates *ldap_state, DEBUG(1, ("Unexpected number of results [%d] for domain name " "search.\n", count)); ldap_msgfree(result); - return NT_STATUS_OK; + return NT_STATUS_UNSUCCESSFUL; } - entry = ldap_first_entry(smbldap_state->ldap_struct, - result); + entry = ldap_first_entry(smbldap_state->ldap_struct, result); if (entry == NULL) { DEBUG(0, ("Could not get domainRelatedObject entry\n")); ldap_msgfree(result); @@ -3506,7 +3565,7 @@ static NTSTATUS ipasam_get_domain_name(struct ldapsam_privates *ldap_state, } cn = get_single_attribute(mem_ctx, smbldap_state->ldap_struct, entry, - "associatedDomain"); + LDAP_ATTRIBUTE_ASSOCIATED_DOMAIN); if (cn == NULL) { ldap_msgfree(result); return NT_STATUS_UNSUCCESSFUL; @@ -3572,6 +3631,112 @@ static NTSTATUS ipasam_get_realm(struct ldapsam_privates *ldap_state, return NT_STATUS_OK; } +#ifdef HAVE_PDB_ENUM_UPN_SUFFIXES +static NTSTATUS ipasam_enum_upn_suffixes(struct pdb_methods *pdb_methods, + TALLOC_CTX *mem_ctx, + uint32_t *num_suffixes, + char ***suffixes) +{ + int ret; + LDAPMessage *result; + LDAPMessage *entry = NULL; + int count, i; + char *realmdomains_dn = NULL; + char **domains = NULL; + struct ldapsam_privates *ldap_state; + struct smbldap_state *smbldap_state; + const char *attr_list[] = { + LDAP_ATTRIBUTE_ASSOCIATED_DOMAIN, + NULL + }; + + if ((suffixes == NULL) || (num_suffixes == NULL)) { + return NT_STATUS_UNSUCCESSFUL; + } + + ldap_state = (struct ldapsam_privates *)pdb_methods->private_data; + smbldap_state = ldap_state->smbldap_state; + + realmdomains_dn = talloc_asprintf(mem_ctx, "%s,%s", LDAP_CN_REALM_DOMAINS, + ldap_state->ipasam_privates->base_dn); + if (realmdomains_dn == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ret = smbldap_search(smbldap_state, + realmdomains_dn, + LDAP_SCOPE_BASE, + "objectclass=" LDAP_OBJ_DOMAINRELATED, attr_list, 0, + &result); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("Failed to get list of realm domains: %s\n", + ldap_err2string (ret))); + return NT_STATUS_UNSUCCESSFUL; + } + + count = ldap_count_entries(smbldap_state->ldap_struct, result); + if (count != 1) { + DEBUG(1, ("Unexpected number of results [%d] for realm domains " + "search.\n", count)); + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + entry = ldap_first_entry(smbldap_state->ldap_struct, result); + if (entry == NULL) { + DEBUG(0, ("Could not get domainRelatedObject entry\n")); + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + domains = get_attribute_values(mem_ctx, smbldap_state->ldap_struct, entry, + LDAP_ATTRIBUTE_ASSOCIATED_DOMAIN, &count); + if (domains == NULL) { + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + /* Since associatedDomain has attributeType MUST, there must be at least one domain */ + for (i = 0; i < count ; i++) { + /* TODO: use comparison function friendly to IDN */ + if (strcasecmp(ldap_state->domain_name, domains[i]) == 0) { + break; + } + } + + if (i < count) { + /* If we found our primary domain in the list and it is alone, exit with empty list */ + if (count == 1) { + ldap_msgfree(result); + talloc_free(domains); + return NT_STATUS_UNSUCCESSFUL; + } + + talloc_free(domains[i]); + + /* if i is not last element, move everything down */ + if (i != (count - 1)) { + memmove(domains + i, domains + i + 1, sizeof(char *) * (count - i - 1)); + } + + /* we don't resize whole list, only reduce number of elements in it + * since sizing down a single pointer will not reduce memory usage in talloc + */ + domains[count - 1] = NULL; + *suffixes = domains; + *num_suffixes = count - 1; + } else { + /* There is no our primary domain in the list */ + *suffixes = domains; + *num_suffixes = count; + } + + ldap_msgfree(result); + return NT_STATUS_OK; +} +#endif /* HAVE_PDB_ENUM_UPN_SUFFIXES */ + + #define SECRETS_DOMAIN_SID "SECRETS/SID" static char *sec_key(TALLOC_CTX *mem_ctx, const char *d) { @@ -4030,7 +4195,7 @@ static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method, } status = ipasam_get_domain_name(ldap_state, ldap_state, - &ldap_state->domain_name); + (char**) &ldap_state->domain_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Failed to get domain name.\n")); return status; @@ -4150,6 +4315,11 @@ static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method, (*pdb_method)->set_trusted_domain = ipasam_set_trusted_domain; (*pdb_method)->del_trusted_domain = ipasam_del_trusted_domain; (*pdb_method)->enum_trusted_domains = ipasam_enum_trusted_domains; +#ifdef HAVE_PDB_ENUM_UPN_SUFFIXES + (*pdb_method)->enum_upn_suffixes = ipasam_enum_upn_suffixes; + DEBUG(1, ("pdb_init_ipasam: support for pdb_enum_upn_suffixes " + "enabled for domain %s\n", ldap_state->domain_name)); +#endif return NT_STATUS_OK; }