diff options
-rw-r--r-- | src/providers/ad/ad_subdomains.c | 366 |
1 files changed, 364 insertions, 2 deletions
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c index 098663cc..f4eec6a4 100644 --- a/src/providers/ad/ad_subdomains.c +++ b/src/providers/ad/ad_subdomains.c @@ -33,8 +33,25 @@ #define AD_AT_NT_VERSION "NtVer" #define AD_AT_NETLOGON "netlogon" +/* Attributes of AD trusted domains */ +#define AD_AT_FLATNAME "flatName" +#define AD_AT_SID "securityIdentifier" +#define AD_AT_TRUST_TYPE "trustType" +#define AD_AT_TRUST_PARTNER "trustPartner" +#define AD_AT_TRUST_ATTRS "trustAttributes" + #define MASTER_DOMAIN_SID_FILTER "objectclass=domain" +/* trustType=2 denotes uplevel (NT5 and later) trusted domains. See + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms680342%28v=vs.85%29.aspx + * for example. + * + * The absence of msDS-TrustForestTrustInfo attribute denotes a domain from + * the same forest. See http://msdn.microsoft.com/en-us/library/cc223786.aspx + * for more information. + */ +#define SLAVE_DOMAIN_FILTER "(&(objectclass=trustedDomain)(trustType=2)(!(msDS-TrustForestTrustInfo=*)))" + /* do not refresh more often than every 5 seconds for now */ #define AD_SUBDOMAIN_REFRESH_LIMIT 5 @@ -45,6 +62,7 @@ struct ad_subdomains_ctx { struct be_ctx *be_ctx; struct sdap_id_ctx *sdap_id_ctx; struct sdap_domain *sdom; + struct sdap_id_conn_ctx *ldap_ctx; struct sss_idmap_ctx *idmap_ctx; char *domain_name; @@ -67,10 +85,250 @@ struct ad_subdomains_req_ctx { char *flat_name; }; +static errno_t +ads_store_sdap_subdom(struct ad_subdomains_ctx *ctx, + struct sss_domain_info *parent) +{ + struct sss_domain_info *dom; + struct sdap_domain *sdom, *sditer; + char *basedn; + errno_t ret; + + for (dom = get_next_domain(parent, true); + dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */ + dom = get_next_domain(dom, false)) { + + DLIST_FOR_EACH(sditer, ctx->sdom) { + if (sditer->dom == dom) { + break; + } + } + + if (sditer == NULL) { + /* New sdap domain */ + DEBUG(SSSDBG_TRACE_FUNC, ("subdomain %s is a new one, will " + "create a new sdap domain object\n", dom->name)); + + ret = sdap_domain_add(ctx->sdap_id_ctx->opts, dom, &sdom); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + ("Cannot add new sdap domain for domain %s [%d]: %s\n", + parent->name, ret, strerror(ret))); + return ret; + } + sditer = sdom; + } + + /* Convert the domain name into search base */ + ret = domain_to_basedn(sdom, sditer->dom->name, &basedn); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + ("Cannot convert domain name [%s] to base DN [%d]: %s\n", + dom->name, ret, strerror(ret))); + talloc_free(basedn); + return ret; + } + + /* Update search bases */ + talloc_zfree(sdom->search_bases); + sdom->search_bases = talloc_array(sdom, struct sdap_search_base *, 2); + if (sdom->search_bases == NULL) { + return ret; + } + sdom->search_bases[1] = NULL; + + ret = sdap_create_search_base(sdom, basedn, LDAP_SCOPE_SUBTREE, NULL, + &sdom->search_bases[0]); + talloc_free(basedn); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, ("Cannot create new sdap search base\n")); + return ret; + } + + sdom->user_search_bases = sdom->search_bases; + sdom->group_search_bases = sdom->search_bases; + sdom->netgroup_search_bases = sdom->search_bases; + sdom->sudo_search_bases = sdom->search_bases; + sdom->service_search_bases = sdom->search_bases; + sdom->autofs_search_bases = sdom->search_bases; + } + + return EOK; +} + +static errno_t +ad_subdom_store(struct ad_subdomains_ctx *ctx, + struct sss_domain_info *domain, + struct sysdb_attrs *subdom_attrs) +{ + TALLOC_CTX *tmp_ctx; + const char *name; + char *realm; + const char *flat; + errno_t ret; + enum idmap_error_code err; + struct ldb_message_element *el; + char *sid_str; + uint32_t trust_type; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_attrs_get_uint32_t(subdom_attrs, AD_AT_TRUST_TYPE, + &trust_type); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_uint32_t failed.\n")); + goto done; + } + + ret = sysdb_attrs_get_string(subdom_attrs, AD_AT_TRUST_PARTNER, &name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("failed to get subdomain name\n")); + goto done; + } + + realm = get_uppercase_realm(tmp_ctx, name); + if (!realm) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_attrs_get_string(subdom_attrs, AD_AT_FLATNAME, &flat); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, ("failed to get flat name of subdomain %s\n", + name)); + goto done; + } + + ret = sysdb_attrs_get_el(subdom_attrs, AD_AT_SID, &el); + if (ret != EOK || el->num_values != 1) { + DEBUG(SSSDBG_OP_FAILURE, ("sdap_attrs_get_el failed.\n")); + goto done; + } + + err = sss_idmap_bin_sid_to_sid(ctx->idmap_ctx, + el->values[0].data, + el->values[0].length, + &sid_str); + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not convert SID: [%s].\n", idmap_error_string(err))); + ret = EFAULT; + goto done; + } + + ret = sysdb_subdomain_store(domain->sysdb, name, realm, flat, sid_str); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("sysdb_subdomain_store failed.\n")); + goto done; + } + + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t ad_subdomains_refresh(struct ad_subdomains_ctx *ctx, + int count, struct sysdb_attrs **reply, + bool *changes) +{ + struct sss_domain_info *domain, *dom; + bool handled[count]; + const char *value; + int c, h; + int ret; + + domain = ctx->be_ctx->domain; + memset(handled, 0, sizeof(bool) * count); + h = 0; + + /* check existing subdomains */ + for (dom = get_next_domain(domain, true); + dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */ + dom = get_next_domain(dom, false)) { + for (c = 0; c < count; c++) { + if (handled[c]) { + continue; + } + ret = sysdb_attrs_get_string(reply[c], AD_AT_TRUST_PARTNER, &value); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n")); + goto done; + } + if (strcmp(value, dom->name) == 0) { + break; + } + } + + if (c >= count) { + /* ok this subdomain does not exist anymore, let's clean up */ + dom->disabled = true; + ret = sysdb_subdomain_delete(dom->sysdb, dom->name); + if (ret != EOK) { + goto done; + } + + /* Remove the subdomain from the list of LDAP domains */ + sdap_domain_remove(ctx->sdap_id_ctx->opts, dom); + } else { + /* ok let's try to update it */ + ret = ad_subdom_store(ctx, domain, reply[c]); + if (ret) { + /* Nothing we can do about the error. Let's at least try + * to reuse the existing domains + */ + DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to parse subdom data, " + "will try to use cached subdomain\n")); + } + handled[c] = true; + h++; + } + } + + if (count == h) { + /* all domains were already accounted for and have been updated */ + ret = EOK; + goto done; + } + + /* if we get here it means we have changes to the subdomains list */ + *changes = true; + + for (c = 0; c < count; c++) { + if (handled[c]) { + continue; + } + /* Nothing we can do about the error. Let's at least try + * to reuse the existing domains. + */ + ret = ad_subdom_store(ctx, domain, reply[c]); + if (ret) { + DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to parse subdom data, " + "will try to use cached subdomain\n")); + } + } + + ret = EOK; + +done: + if (ret != EOK) { + ctx->last_refreshed = 0; + } else { + ctx->last_refreshed = time(NULL); + } + + return ret; +} + static void ad_subdomains_get_conn_done(struct tevent_req *req); static errno_t ad_subdomains_get_master_sid(struct ad_subdomains_req_ctx *ctx); static void ad_subdomains_get_master_sid_done(struct tevent_req *req); static void ad_subdomains_get_netlogon_done(struct tevent_req *req); +static errno_t ad_subdomains_get_slave(struct ad_subdomains_req_ctx *ctx); static void ad_subdomains_retrieve(struct ad_subdomains_ctx *ctx, struct be_req *be_req) @@ -94,7 +352,7 @@ static void ad_subdomains_retrieve(struct ad_subdomains_ctx *ctx, req_ctx->reply = NULL; req_ctx->sdap_op = sdap_id_op_create(req_ctx, - ctx->sdap_id_ctx->conn->conn_cache); + ctx->ldap_ctx->conn_cache); if (req_ctx->sdap_op == NULL) { DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed.\n")); ret = ENOMEM; @@ -375,10 +633,113 @@ static void ad_subdomains_get_netlogon_done(struct tevent_req *req) ret = sysdb_master_domain_add_info(ctx->sd_ctx->be_ctx->domain, NULL, ctx->flat_name, ctx->master_sid); - ret = EOK; + ret = ad_subdomains_get_slave(ctx); + if (ret == EAGAIN) { + return; + } else if (ret != EOK) { + goto done; + } done: + be_req_terminate(ctx->be_req, dp_error, ret, NULL); +} + +static void ad_subdomains_get_slave_domain_done(struct tevent_req *req); + +static errno_t ad_subdomains_get_slave(struct ad_subdomains_req_ctx *ctx) +{ + struct tevent_req *req; + struct sdap_search_base *base; + const char *slave_dom_attrs[] = { AD_AT_FLATNAME, AD_AT_TRUST_PARTNER, + AD_AT_SID, AD_AT_TRUST_TYPE, + AD_AT_TRUST_ATTRS, NULL }; + + base = ctx->sd_ctx->sdap_id_ctx->opts->sdom->search_bases[ctx->base_iter]; + if (base == NULL) { + return EOK; + } + + req = sdap_get_generic_send(ctx, ctx->sd_ctx->be_ctx->ev, + ctx->sd_ctx->sdap_id_ctx->opts, + sdap_id_op_handle(ctx->sdap_op), + base->basedn, LDAP_SCOPE_SUBTREE, + SLAVE_DOMAIN_FILTER, slave_dom_attrs, + NULL, 0, + dp_opt_get_int(ctx->sd_ctx->sdap_id_ctx->opts->basic, + SDAP_SEARCH_TIMEOUT), + false); + + if (req == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("sdap_get_generic_send failed.\n")); + return ENOMEM; + } + tevent_req_set_callback(req, ad_subdomains_get_slave_domain_done, ctx); + return EAGAIN; +} + +static void ad_subdomains_get_slave_domain_done(struct tevent_req *req) +{ + int ret; + size_t reply_count; + struct sysdb_attrs **reply = NULL; + struct ad_subdomains_req_ctx *ctx; + int dp_error = DP_ERR_FATAL; + bool refresh_has_changes = false; + + ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx); + + ret = sdap_get_generic_recv(req, ctx, &reply_count, &reply); + talloc_zfree(req); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("sdap_get_generic_send request failed.\n")); + goto done; + } + + if (reply_count) { + ctx->reply = talloc_realloc(ctx, ctx->reply, struct sysdb_attrs *, + ctx->reply_count + reply_count); + if (ctx->reply == NULL) { + ret = ENOMEM; + goto done; + } + memcpy(ctx->reply+ctx->reply_count, reply, + reply_count * sizeof(struct sysdb_attrs *)); + ctx->reply_count += reply_count; + } + + ctx->base_iter++; + ret = ad_subdomains_get_slave(ctx); + if (ret == EAGAIN) { + return; + } else if (ret != EOK) { + goto done; + } + + /* Got all the subdomains, let's process them */ + ret = ad_subdomains_refresh(ctx->sd_ctx, ctx->reply_count, ctx->reply, + &refresh_has_changes); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Failed to refresh subdomains.\n")); + goto done; + } + + if (refresh_has_changes) { + ret = sysdb_update_subdomains(ctx->sd_ctx->be_ctx->domain); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("sysdb_update_subdomains failed.\n")); + goto done; + } + + ret = ads_store_sdap_subdom(ctx->sd_ctx, ctx->sd_ctx->be_ctx->domain); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("ads_store_sdap_subdom failed.\n")); + goto done; + } + } + + ret = EOK; +done: if (ret == EOK) { ctx->sd_ctx->last_refreshed = time(NULL); dp_error = DP_ERR_OK; @@ -499,6 +860,7 @@ int ad_subdom_init(struct be_ctx *be_ctx, ctx->be_ctx = be_ctx; ctx->sdom = id_ctx->sdap_id_ctx->opts->sdom; + ctx->ldap_ctx = id_ctx->ldap_ctx; ctx->sdap_id_ctx = id_ctx->sdap_id_ctx; ctx->domain_name = talloc_strdup(ctx, ad_domain); if (ctx->domain_name == NULL) { |