ad_subdomains.c revision 58dd26b1c5b60ee992dd5d1214bb168aebb42d54
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski/*
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski SSSD
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski
97018cf5fa25b494adffd7e9b4e87320dae6bf47Christian Maeder AD Subdomains Module
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski
b4fbc96e05117839ca409f5f20f97b3ac872d1edTill Mossakowski Authors:
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski Sumit Bose <sbose@redhat.com>
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski Copyright (C) 2013 Red Hat
f3a94a197960e548ecd6520bb768cb0d547457bbChristian Maeder
684ada8af5c3e6da5c1a69edb6f233c9f2db4ebdWiebke Herding This program is free software; you can redistribute it and/or modify
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski it under the terms of the GNU General Public License as published by
781d04c5e02635caed8b98f0adcf559f9426a39cTill Mossakowski the Free Software Foundation; either version 3 of the License, or
781d04c5e02635caed8b98f0adcf559f9426a39cTill Mossakowski (at your option) any later version.
781d04c5e02635caed8b98f0adcf559f9426a39cTill Mossakowski
781d04c5e02635caed8b98f0adcf559f9426a39cTill Mossakowski This program is distributed in the hope that it will be useful,
e4e1509ff358e739fddf1483ad39467e0e1becc2Christian Maeder but WITHOUT ANY WARRANTY; without even the implied warranty of
684ada8af5c3e6da5c1a69edb6f233c9f2db4ebdWiebke Herding MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
e4e1509ff358e739fddf1483ad39467e0e1becc2Christian Maeder GNU General Public License for more details.
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder You should have received a copy of the GNU General Public License
76647324ed70f33b95a881b536d883daccf9568dChristian Maeder along with this program. If not, see <http://www.gnu.org/licenses/>.
d183a4514d8a5b6a5d48d15a8dff52d0c96691eaChristian Maeder*/
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski#include "providers/ldap/sdap_async.h"
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski#include "providers/ad/ad_subdomains.h"
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder#include <ctype.h>
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder#include <ndr.h>
50dce6b011347f92377adb8bbabaeeb80975e86dChristian Maeder#include <ndr/ndr_nbt.h>
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder
50dce6b011347f92377adb8bbabaeeb80975e86dChristian Maeder#define AD_AT_OBJECT_SID "objectSID"
c74040e2ca9d0534d0c4244f69a3e76a01341f05Klaus Luettich#define AD_AT_DNS_DOMAIN "DnsDomain"
c74040e2ca9d0534d0c4244f69a3e76a01341f05Klaus Luettich#define AD_AT_NT_VERSION "NtVer"
d183a4514d8a5b6a5d48d15a8dff52d0c96691eaChristian Maeder#define AD_AT_NETLOGON "netlogon"
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder
2f4ab3efb20e52aa207201ecc22ece1d4ccc655dHeng Jiang/* Attributes of AD trusted domains */
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski#define AD_AT_FLATNAME "flatName"
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski#define AD_AT_SID "securityIdentifier"
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski#define AD_AT_TRUST_TYPE "trustType"
05ca76b03b6d16bcfb3e7654c31e41a220e85663Till Mossakowski#define AD_AT_TRUST_PARTNER "trustPartner"
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder#define AD_AT_TRUST_ATTRS "trustAttributes"
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder#define MASTER_DOMAIN_SID_FILTER "objectclass=domain"
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder/* trustType=2 denotes uplevel (NT5 and later) trusted domains. See
05ca76b03b6d16bcfb3e7654c31e41a220e85663Till Mossakowski * http://msdn.microsoft.com/en-us/library/windows/desktop/ms680342%28v=vs.85%29.aspx
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski * for example.
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder *
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski * The absence of msDS-TrustForestTrustInfo attribute denotes a domain from
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder * the same forest. See http://msdn.microsoft.com/en-us/library/cc223786.aspx
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski * for more information.
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder */
684ada8af5c3e6da5c1a69edb6f233c9f2db4ebdWiebke Herding#define SLAVE_DOMAIN_FILTER "(&(objectclass=trustedDomain)(trustType=2)(!(msDS-TrustForestTrustInfo=*)))"
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder/* do not refresh more often than every 5 seconds for now */
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski#define AD_SUBDOMAIN_REFRESH_LIMIT 5
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski/* refresh automatically every 4 hours */
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder#define AD_SUBDOMAIN_REFRESH_PERIOD (3600 * 4)
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowskistruct ad_subdomains_ctx {
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder struct be_ctx *be_ctx;
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski struct sdap_id_ctx *sdap_id_ctx;
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder struct sdap_domain *sdom;
684ada8af5c3e6da5c1a69edb6f233c9f2db4ebdWiebke Herding struct sdap_id_conn_ctx *ldap_ctx;
684ada8af5c3e6da5c1a69edb6f233c9f2db4ebdWiebke Herding struct sss_idmap_ctx *idmap_ctx;
684ada8af5c3e6da5c1a69edb6f233c9f2db4ebdWiebke Herding char *domain_name;
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder time_t last_refreshed;
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder struct tevent_timer *timer_event;
50dce6b011347f92377adb8bbabaeeb80975e86dChristian Maeder};
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maederstruct ad_subdomains_req_ctx {
c2db39a683438b0f3d484519f4c93db26eec9d2eWiebke Herding struct be_req *be_req;
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski struct ad_subdomains_ctx *sd_ctx;
c2db39a683438b0f3d484519f4c93db26eec9d2eWiebke Herding struct sdap_id_op *sdap_op;
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder char *current_filter;
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder size_t base_iter;
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder size_t reply_count;
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder struct sysdb_attrs **reply;
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder char *master_sid;
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder char *flat_name;
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder};
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowskistatic errno_t
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowskiads_store_sdap_subdom(struct ad_subdomains_ctx *ctx,
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski struct sss_domain_info *parent)
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski{
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder struct sss_domain_info *dom;
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski struct sdap_domain *sdom, *sditer;
bec7e681b0ba4d085638ec7af0cf7ae5068840caChristian Maeder char *basedn;
7b27b67b1c8516d7ccf1610a17fec93662d6a93fChristian Maeder errno_t ret;
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder
0d9160e906743b226d4768707f84151ab6c66253Heng Jiang for (dom = get_next_domain(parent, true);
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder dom = get_next_domain(dom, false)) {
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder
bec7e681b0ba4d085638ec7af0cf7ae5068840caChristian Maeder DLIST_FOR_EACH(sditer, ctx->sdom) {
7b27b67b1c8516d7ccf1610a17fec93662d6a93fChristian Maeder if (sditer->dom == dom) {
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder break;
bec7e681b0ba4d085638ec7af0cf7ae5068840caChristian Maeder }
c2db39a683438b0f3d484519f4c93db26eec9d2eWiebke Herding }
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder
f6c04b8534762854072795add026d4551156a410Heng Jiang if (sditer == NULL) {
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder /* New sdap domain */
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder DEBUG(SSSDBG_TRACE_FUNC, ("subdomain %s is a new one, will "
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski "create a new sdap domain object\n", dom->name));
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder ret = sdap_domain_add(ctx->sdap_id_ctx->opts, dom, &sdom);
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski if (ret != EOK) {
7b27b67b1c8516d7ccf1610a17fec93662d6a93fChristian Maeder DEBUG(SSSDBG_OP_FAILURE,
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski ("Cannot add new sdap domain for domain %s [%d]: %s\n",
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski parent->name, ret, strerror(ret)));
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder return ret;
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder }
c2db39a683438b0f3d484519f4c93db26eec9d2eWiebke Herding sditer = sdom;
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder }
c2db39a683438b0f3d484519f4c93db26eec9d2eWiebke Herding
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski /* Convert the domain name into search base */
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski ret = domain_to_basedn(sditer, sditer->dom->name, &basedn);
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder if (ret != EOK) {
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder DEBUG(SSSDBG_OP_FAILURE,
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder ("Cannot convert domain name [%s] to base DN [%d]: %s\n",
eca4db63ed0bdbd93b62678feea6e3eb80aa47bbChristian Maeder dom->name, ret, strerror(ret)));
eca4db63ed0bdbd93b62678feea6e3eb80aa47bbChristian Maeder talloc_free(basedn);
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder return ret;
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder }
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder /* Update search bases */
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski talloc_zfree(sdom->search_bases);
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski sdom->search_bases = talloc_array(sdom, struct sdap_search_base *, 2);
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski if (sdom->search_bases == NULL) {
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder return ret;
0e2ae85e2453466d03c1fc5884a3d693235bb9d9Christian Maeder }
7b27b67b1c8516d7ccf1610a17fec93662d6a93fChristian Maeder sdom->search_bases[1] = NULL;
c2db39a683438b0f3d484519f4c93db26eec9d2eWiebke Herding
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder ret = sdap_create_search_base(sdom, basedn, LDAP_SCOPE_SUBTREE, NULL,
3a761fd74f4f3c5587a199553c0ee7383e5d8ff3Christian Maeder &sdom->search_bases[0]);
e85b224577b78d08ba5c39fe9dcc2e53995454a2Christian Maeder talloc_free(basedn);
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski if (ret) {
2a693c01b154f1e25931ff6c754d2d02096e2662Till Mossakowski DEBUG(SSSDBG_OP_FAILURE, ("Cannot create new sdap search base\n"));
b65e16b9e5652ff341ab0f49be5da51e2c0e10a5Till Mossakowski 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)
{
struct ad_subdomains_req_ctx *req_ctx = NULL;
struct tevent_req *req;
int dp_error = DP_ERR_FATAL;
int ret;
req_ctx = talloc(be_req, struct ad_subdomains_req_ctx);
if (req_ctx == NULL) {
ret = ENOMEM;
goto done;
}
req_ctx->be_req = be_req;
req_ctx->sd_ctx = ctx;
req_ctx->current_filter = NULL;
req_ctx->base_iter = 0;
req_ctx->reply_count = 0;
req_ctx->reply = NULL;
req_ctx->sdap_op = sdap_id_op_create(req_ctx,
ctx->ldap_ctx->conn_cache);
if (req_ctx->sdap_op == NULL) {
DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed.\n"));
ret = ENOMEM;
goto done;
}
req = sdap_id_op_connect_send(req_ctx->sdap_op, req_ctx, &ret);
if (req == NULL) {
DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_connect_send failed: %d(%s).\n",
ret, strerror(ret)));
goto done;
}
tevent_req_set_callback(req, ad_subdomains_get_conn_done, req_ctx);
return;
done:
talloc_free(req_ctx);
if (ret == EOK) {
dp_error = DP_ERR_OK;
}
be_req_terminate(be_req, dp_error, ret, NULL);
}
static void ad_subdomains_get_conn_done(struct tevent_req *req)
{
int ret;
int dp_error = DP_ERR_FATAL;
struct ad_subdomains_req_ctx *ctx;
ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx);
ret = sdap_id_op_connect_recv(req, &dp_error);
talloc_zfree(req);
if (ret) {
if (dp_error == DP_ERR_OFFLINE) {
DEBUG(SSSDBG_MINOR_FAILURE,
("No AD server is available, cannot get the "
"subdomain list while offline\n"));
} else {
DEBUG(SSSDBG_OP_FAILURE,
("Failed to connect to AD server: [%d](%s)\n",
ret, strerror(ret)));
}
goto fail;
}
ret = ad_subdomains_get_master_sid(ctx);
if (ret == EAGAIN) {
return;
} else if (ret != EOK) {
goto fail;
}
DEBUG(SSSDBG_OP_FAILURE, ("No search base available.\n"));
ret = EINVAL;
fail:
be_req_terminate(ctx->be_req, dp_error, ret, NULL);
}
static errno_t ad_subdomains_get_master_sid(struct ad_subdomains_req_ctx *ctx)
{
struct tevent_req *req;
struct sdap_search_base *base;
const char *master_sid_attrs[] = {AD_AT_OBJECT_SID, NULL};
base = ctx->sd_ctx->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_BASE,
MASTER_DOMAIN_SID_FILTER, master_sid_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_master_sid_done, ctx);
return EAGAIN;
}
static void ad_subdomains_get_master_sid_done(struct tevent_req *req)
{
int ret;
size_t reply_count;
struct sysdb_attrs **reply = NULL;
struct ad_subdomains_req_ctx *ctx;
struct ldb_message_element *el;
char *sid_str;
enum idmap_error_code err;
static const char *attrs[] = {AD_AT_NETLOGON, NULL};
char *filter;
char *ntver;
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 == 0) {
ctx->base_iter++;
ret = ad_subdomains_get_master_sid(ctx);
if (ret == EAGAIN) {
return;
} else if (ret != EOK) {
goto done;
}
} else if (reply_count == 1) {
ret = sysdb_attrs_get_el(reply[0], AD_AT_OBJECT_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->sd_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;
}
ctx->master_sid = talloc_steal(ctx, sid_str);
} else {
DEBUG(SSSDBG_OP_FAILURE,
("More than one result for domain SID found.\n"));
ret = EINVAL;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC, ("Found SID [%s].\n", ctx->master_sid));
ntver = sss_ldap_encode_ndr_uint32(ctx, NETLOGON_NT_VERSION_5EX |
NETLOGON_NT_VERSION_WITH_CLOSEST_SITE);
if (ntver == NULL) {
DEBUG(SSSDBG_OP_FAILURE, ("sss_ldap_encode_ndr_uint32 failed.\n"));
ret = ENOMEM;
goto done;
}
filter = talloc_asprintf(ctx, "(&(%s=%s)(%s=%s))",
AD_AT_DNS_DOMAIN, ctx->sd_ctx->domain_name,
AD_AT_NT_VERSION, ntver);
if (filter == NULL) {
DEBUG(SSSDBG_OP_FAILURE, ("talloc_asprintf failed.\n"));
ret = ENOMEM;
goto done;
}
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),
"", LDAP_SCOPE_BASE, filter, 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"));
ret = ENOMEM;
goto done;
}
tevent_req_set_callback(req, ad_subdomains_get_netlogon_done, ctx);
return;
done:
be_req_terminate(ctx->be_req, DP_ERR_FATAL, ret, NULL);
}
static void ad_subdomains_get_netlogon_done(struct tevent_req *req)
{
int ret;
size_t reply_count;
struct sysdb_attrs **reply = NULL;
struct ad_subdomains_req_ctx *ctx;
struct ldb_message_element *el;
DATA_BLOB blob;
enum ndr_err_code ndr_err;
struct ndr_pull *ndr_pull = NULL;
struct netlogon_samlogon_response response;
int dp_error = DP_ERR_FATAL;
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 == 0) {
DEBUG(SSSDBG_TRACE_FUNC, ("No netlogon data available.\n"));
ret = ENOENT;
goto done;
} else if (reply_count > 1) {
DEBUG(SSSDBG_OP_FAILURE,
("More than one netlogon info returned.\n"));
ret = EINVAL;
goto done;
}
ret = sysdb_attrs_get_el(reply[0], AD_AT_NETLOGON, &el);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_el() failed\n"));
goto done;
}
if (el->num_values == 0) {
DEBUG(SSSDBG_OP_FAILURE, ("netlogon has no value\n"));
ret = ENOENT;
goto done;
} else if (el->num_values > 1) {
DEBUG(SSSDBG_OP_FAILURE, ("More than one netlogon value?\n"));
ret = EIO;
goto done;
}
blob.data = el->values[0].data;
blob.length = el->values[0].length;
ndr_pull = ndr_pull_init_blob(&blob, ctx);
if (ndr_pull == NULL) {
DEBUG(SSSDBG_OP_FAILURE, ("ndr_pull_init_blob() failed.\n"));
ret = ENOMEM;
goto done;
}
ndr_err = ndr_pull_netlogon_samlogon_response(ndr_pull, NDR_SCALARS,
&response);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
DEBUG(SSSDBG_OP_FAILURE, ("ndr_pull_netlogon_samlogon_response() "
"failed [%d]\n", ndr_err));
ret = EBADMSG;
goto done;
}
if (!(response.ntver & NETLOGON_NT_VERSION_5EX)) {
DEBUG(SSSDBG_OP_FAILURE, ("Wrong version returned [%x]\n",
response.ntver));
ret = EBADMSG;
goto done;
}
if (response.data.nt5_ex.domain_name != NULL &&
*response.data.nt5_ex.domain_name != '\0') {
ctx->flat_name = talloc_strdup(ctx, response.data.nt5_ex.domain_name);
if (ctx->flat_name == NULL) {
DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
ret = ENOMEM;
goto done;
}
}
DEBUG(SSSDBG_TRACE_FUNC, ("Found flat name [%s].\n", ctx->flat_name));
ret = sysdb_master_domain_add_info(ctx->sd_ctx->be_ctx->domain,
NULL, ctx->flat_name, ctx->master_sid);
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 = sss_write_domain_mappings(ctx->sd_ctx->be_ctx->domain);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
("sss_krb5_write_mappings failed.\n"));
/* Just continue */
}
}
ret = EOK;
done:
if (ret == EOK) {
ctx->sd_ctx->last_refreshed = time(NULL);
dp_error = DP_ERR_OK;
}
be_req_terminate(ctx->be_req, dp_error, ret, NULL);
}
static void ad_subdom_online_cb(void *pvt);
static void ad_subdom_timer_refresh(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval current_time,
void *pvt)
{
ad_subdom_online_cb(pvt);
}
static void ad_subdom_be_req_callback(struct be_req *be_req,
int dp_err, int dp_ret,
const char *errstr)
{
talloc_free(be_req);
}
static void ad_subdom_online_cb(void *pvt)
{
struct ad_subdomains_ctx *ctx;
struct be_req *be_req;
struct timeval tv;
ctx = talloc_get_type(pvt, struct ad_subdomains_ctx);
if (!ctx) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Bad private pointer\n"));
return;
}
be_req = be_req_create(ctx, NULL, ctx->be_ctx,
ad_subdom_be_req_callback, NULL);
if (be_req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("be_req_create() failed.\n"));
return;
}
ad_subdomains_retrieve(ctx, be_req);
tv = tevent_timeval_current_ofs(AD_SUBDOMAIN_REFRESH_PERIOD, 0);
ctx->timer_event = tevent_add_timer(ctx->be_ctx->ev, ctx, tv,
ad_subdom_timer_refresh, ctx);
if (!ctx->timer_event) {
DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to add subdom timer event\n"));
}
}
static void ad_subdom_offline_cb(void *pvt)
{
struct ad_subdomains_ctx *ctx;
ctx = talloc_get_type(pvt, struct ad_subdomains_ctx);
if (ctx) {
talloc_zfree(ctx->timer_event);
}
}
void ad_subdomains_handler(struct be_req *be_req)
{
struct be_ctx *be_ctx = be_req_get_be_ctx(be_req);
struct ad_subdomains_ctx *ctx;
time_t now;
ctx = talloc_get_type(be_ctx->bet_info[BET_SUBDOMAINS].pvt_bet_data,
struct ad_subdomains_ctx);
if (!ctx) {
be_req_terminate(be_req, DP_ERR_FATAL, EINVAL, NULL);
return;
}
now = time(NULL);
if (ctx->last_refreshed > now - AD_SUBDOMAIN_REFRESH_LIMIT) {
be_req_terminate(be_req, DP_ERR_OK, EOK, NULL);
return;
}
ad_subdomains_retrieve(ctx, be_req);
}
struct bet_ops ad_subdomains_ops = {
.handler = ad_subdomains_handler,
.finalize = NULL
};
static void *idmap_talloc(size_t size, void *pvt)
{
return talloc_size(pvt, size);
}
static void idmap_free(void *ptr, void *pvt)
{
talloc_free(ptr);
}
int ad_subdom_init(struct be_ctx *be_ctx,
struct ad_id_ctx *id_ctx,
const char *ad_domain,
struct bet_ops **ops,
void **pvt_data)
{
struct ad_subdomains_ctx *ctx;
int ret;
enum idmap_error_code err;
ctx = talloc_zero(id_ctx, struct ad_subdomains_ctx);
if (ctx == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero failed.\n"));
return ENOMEM;
}
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) {
DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
return ENOMEM;
}
*ops = &ad_subdomains_ops;
*pvt_data = ctx;
ret = be_add_online_cb(ctx, be_ctx, ad_subdom_online_cb, ctx, NULL);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to add subdom online callback"));
}
ret = be_add_offline_cb(ctx, be_ctx, ad_subdom_offline_cb, ctx, NULL);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to add subdom offline callback"));
}
err = sss_idmap_init(idmap_talloc, ctx, idmap_free, &ctx->idmap_ctx);
if (err != IDMAP_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to initialize idmap context.\n"));
return EFAULT;
}
return EOK;
}