ipa_subdomains_server.c revision d2633d922eeed68f92be4248b9172b928c189920
1516N/A/*
49N/A SSSD
49N/A
49N/A IPA Subdomains Module - server mode
49N/A
49N/A Authors:
49N/A Sumit Bose <sbose@redhat.com>
49N/A
49N/A Copyright (C) 2015 Red Hat
49N/A
49N/A This program is free software; you can redistribute it and/or modify
49N/A it under the terms of the GNU General Public License as published by
49N/A the Free Software Foundation; either version 3 of the License, or
49N/A (at your option) any later version.
49N/A
49N/A This program is distributed in the hope that it will be useful,
49N/A but WITHOUT ANY WARRANTY; without even the implied warranty of
49N/A MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49N/A GNU General Public License for more details.
49N/A
49N/A You should have received a copy of the GNU General Public License
49N/A along with this program. If not, see <http://www.gnu.org/licenses/>.
2205N/A*/
3339N/A
2205N/A#include "providers/ldap/sdap_async.h"
49N/A#include "providers/ldap/sdap_idmap.h"
49N/A#include "providers/ipa/ipa_subdomains.h"
49N/A#include "providers/ipa/ipa_common.h"
49N/A#include "providers/ipa/ipa_id.h"
49N/A
49N/A/* These constants are defined in MS-ADTS 6.1.6.7.1
1859N/A * https://msdn.microsoft.com/en-us/library/cc223768.aspx
3339N/A */
51N/A#define LSA_TRUST_DIRECTION_INBOUND 0x00000001
289N/A#define LSA_TRUST_DIRECTION_OUTBOUND 0x00000002
941N/A
1859N/Astatic char *forest_keytab(TALLOC_CTX *mem_ctx, const char *forest)
1859N/A{
49N/A return talloc_asprintf(mem_ctx,
49N/A "%s/%s.keytab", IPA_TRUST_KEYTAB_DIR, forest);
49N/A}
49N/A
1846N/Astatic char *subdomain_trust_princ(TALLOC_CTX *mem_ctx,
1846N/A const char *forest_realm,
51N/A struct sss_domain_info *sd)
72N/A{
2205N/A if (sd->parent->flat_name == NULL) {
2205N/A DEBUG(SSSDBG_CRIT_FAILURE,
2205N/A "Unknown flat name for parent %s\n", sd->parent->name);
2205N/A return NULL;
2639N/A }
51N/A
307N/A return talloc_asprintf(mem_ctx, "%s$@%s",
3245N/A sd->parent->flat_name, forest_realm);
3245N/A}
307N/A
2205N/Astatic uint32_t default_direction(TALLOC_CTX *mem_ctx,
2205N/A struct ldb_context *ldb_ctx,
2205N/A struct sysdb_attrs *attrs)
2205N/A{
2205N/A struct ldb_dn *dn = NULL;
2205N/A uint32_t direction;
2205N/A
2205N/A dn = ipa_subdom_ldb_dn(mem_ctx, ldb_ctx, attrs);
2205N/A if (dn == NULL) {
2205N/A /* Shouldn't happen, but let's try system keytab in this case */
2205N/A DEBUG(SSSDBG_CRIT_FAILURE,
2205N/A "Cannot determine subdomain DN, falling back to two-way trust\n");
2205N/A return (LSA_TRUST_DIRECTION_INBOUND|LSA_TRUST_DIRECTION_OUTBOUND);
2205N/A }
2205N/A
307N/A if (ipa_subdom_is_member_dom(dn) == true) {
307N/A /* It's expected member domains do not have the direction */
281N/A direction = 0;
3176N/A } else {
3109N/A /* Old server? Default to 2way trust */
3109N/A direction = (LSA_TRUST_DIRECTION_INBOUND|LSA_TRUST_DIRECTION_OUTBOUND);
3109N/A }
3109N/A
3176N/A talloc_free(dn);
3171N/A return direction;
3109N/A}
3109N/A
3109N/Aerrno_t ipa_server_get_trust_direction(struct sysdb_attrs *sd,
3109N/A struct ldb_context *ldb_ctx,
3109N/A uint32_t *_direction)
3109N/A{
3109N/A uint32_t ipa_trust_direction = 0;
3109N/A uint32_t direction;
3109N/A int ret;
3109N/A
3109N/A ret = sysdb_attrs_get_uint32_t(sd, IPA_TRUST_DIRECTION,
3109N/A &ipa_trust_direction);
3109N/A DEBUG(SSSDBG_TRACE_INTERNAL,
3109N/A "Raw %s value: %d\n", IPA_TRUST_DIRECTION, ipa_trust_direction);
3109N/A if (ret == ENOENT) {
3109N/A direction = default_direction(sd, ldb_ctx, sd);
3109N/A } else if (ret == EOK) {
3109N/A /* Just store the AD value in SYSDB, we will check it while we're
3109N/A * trying to use the trust */
3109N/A direction = ipa_trust_direction;
3109N/A } else {
3109N/A return ret;
3109N/A }
3109N/A
3109N/A *_direction = direction;
3158N/A return EOK;
3109N/A}
3109N/A
3109N/Aconst char *ipa_trust_dir2str(uint32_t direction)
3109N/A{
3158N/A if ((direction & LSA_TRUST_DIRECTION_OUTBOUND)
3109N/A && (direction & LSA_TRUST_DIRECTION_INBOUND)) {
3109N/A return "two-way trust";
3109N/A } else if (direction & LSA_TRUST_DIRECTION_OUTBOUND) {
3109N/A return "one-way outbound: local domain is trusted by remote domain";
3109N/A } else if (direction & LSA_TRUST_DIRECTION_INBOUND) {
3109N/A return "one-way inbound: local domain trusts the remote domain";
3109N/A } else if (direction == 0) {
3109N/A return "not set";
3109N/A }
3109N/A
3109N/A return "unknown";
3109N/A}
3109N/A
3109N/A#ifndef IPA_GETKEYTAB_TIMEOUT
3109N/A#define IPA_GETKEYTAB_TIMEOUT 5
3109N/A#endif /* IPA_GETKEYTAB_TIMEOUT */
136N/A
49N/Astatic struct ad_options *
1755N/Aipa_create_1way_trust_ctx(struct ipa_id_ctx *id_ctx,
1755N/A struct be_ctx *be_ctx,
1755N/A const char *subdom_conf_path,
1755N/A const char *forest,
1755N/A const char *forest_realm,
1755N/A struct sss_domain_info *subdom)
1755N/A{
1755N/A char *keytab;
1755N/A char *principal;
1859N/A struct ad_options *ad_options;
1784N/A
1784N/A keytab = forest_keytab(id_ctx, forest);
72N/A principal = subdomain_trust_princ(id_ctx, forest_realm, subdom);
1784N/A if (keytab == NULL || principal == NULL) {
1784N/A return NULL;
1784N/A }
1784N/A
1784N/A ad_options = ad_create_1way_trust_options(id_ctx,
1784N/A be_ctx->cdb,
1784N/A subdom_conf_path,
1784N/A be_ctx->provider,
72N/A subdom,
3047N/A id_ctx->server_mode->hostname,
1859N/A keytab,
1859N/A principal);
1859N/A if (ad_options == NULL) {
51N/A talloc_free(keytab);
95N/A talloc_free(principal);
289N/A return NULL;
1507N/A }
95N/A
72N/A return ad_options;
3109N/A}
1859N/A
95N/Astatic struct ad_options *ipa_ad_options_new(struct be_ctx *be_ctx,
95N/A struct ipa_id_ctx *id_ctx,
95N/A struct sss_domain_info *subdom)
95N/A{
95N/A struct ad_options *ad_options = NULL;
95N/A uint32_t direction;
95N/A const char *forest;
95N/A const char *forest_realm;
95N/A char *subdom_conf_path;
95N/A
95N/A /* Trusts are only established with forest roots */
72N/A direction = subdom->forest_root->trust_direction;
3109N/A forest_realm = subdom->forest_root->realm;
3109N/A forest = subdom->forest_root->forest;
3171N/A
3109N/A subdom_conf_path = subdomain_create_conf_path(id_ctx, subdom);
3109N/A if (subdom_conf_path == NULL) {
3109N/A DEBUG(SSSDBG_CRIT_FAILURE, "subdom_conf_path failed\n");
3109N/A return NULL;
3109N/A }
3109N/A
3109N/A if (direction & LSA_TRUST_DIRECTION_OUTBOUND) {
49N/A ad_options = ad_create_2way_trust_options(id_ctx,
2818N/A be_ctx->cdb,
2818N/A subdom_conf_path,
2818N/A be_ctx->provider,
2818N/A id_ctx->server_mode->realm,
2818N/A subdom,
2818N/A id_ctx->server_mode->hostname,
2818N/A NULL);
2818N/A } else if (direction & LSA_TRUST_DIRECTION_INBOUND) {
2818N/A ad_options = ipa_create_1way_trust_ctx(id_ctx, be_ctx,
2818N/A subdom_conf_path, forest,
2818N/A forest_realm, subdom);
72N/A } else {
72N/A DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported trust direction!\n");
289N/A ad_options = NULL;
3171N/A }
271N/A talloc_free(subdom_conf_path);
271N/A
3176N/A if (ad_options == NULL) {
3176N/A DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD options\n");
3176N/A return NULL;
3176N/A }
66N/A return ad_options;
235N/A}
1685N/A
1685N/A
1685N/Astatic errno_t
222N/Aipa_ad_ctx_new(struct be_ctx *be_ctx,
1685N/A struct ipa_id_ctx *id_ctx,
1281N/A struct sss_domain_info *subdom,
1685N/A struct ad_id_ctx **_ad_id_ctx)
289N/A{
136N/A struct ad_options *ad_options;
3047N/A struct ad_id_ctx *ad_id_ctx;
462N/A const char *gc_service_name;
462N/A const char *service_name;
3171N/A struct ad_srv_plugin_ctx *srv_ctx;
462N/A const char *ad_domain;
462N/A const char *ad_site_override;
1859N/A const char *ad_servers;
1859N/A const char *ad_backup_servers;
1859N/A struct sdap_domain *sdom;
2293N/A errno_t ret;
1859N/A const char *extra_attrs;
1859N/A
1859N/A ad_domain = subdom->name;
1859N/A DEBUG(SSSDBG_TRACE_LIBS, "Setting up AD subdomain %s\n", subdom->name);
2293N/A
1859N/A ad_options = ipa_ad_options_new(be_ctx, id_ctx, subdom);
1859N/A if (ad_options == NULL) {
1859N/A DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD options\n");
3176N/A talloc_free(ad_options);
3158N/A return ENOMEM;
2987N/A }
2987N/A
2987N/A extra_attrs = dp_opt_get_string(id_ctx->sdap_id_ctx->opts->basic,
3158N/A SDAP_USER_EXTRA_ATTRS);
3158N/A if (extra_attrs != NULL) {
2987N/A DEBUG(SSSDBG_TRACE_ALL,
2987N/A "Setting extra attrs for subdomain [%s] to [%s].\n", ad_domain,
2987N/A extra_attrs);
1859N/A
1859N/A ret = dp_opt_set_string(ad_options->id->basic, SDAP_USER_EXTRA_ATTRS,
1859N/A extra_attrs);
1859N/A if (ret != EOK) {
3176N/A DEBUG(SSSDBG_OP_FAILURE, "dp_opt_set_string failed.\n");
3158N/A talloc_free(ad_options);
2987N/A return ret;
3158N/A }
3158N/A
2987N/A ret = sdap_extend_map_with_list(ad_options->id, ad_options->id,
2987N/A SDAP_USER_EXTRA_ATTRS,
2987N/A ad_options->id->user_map,
462N/A SDAP_OPTS_USER,
462N/A &ad_options->id->user_map,
140N/A &ad_options->id->user_map_cnt);
144N/A if (ret != EOK) {
1100N/A DEBUG(SSSDBG_OP_FAILURE, "sdap_extend_map_with_list failed.\n");
1100N/A talloc_free(ad_options);
1100N/A return ret;
941N/A }
2204N/A } else {
941N/A DEBUG(SSSDBG_TRACE_ALL, "No extra attrs set.\n");
941N/A }
2204N/A
941N/A gc_service_name = talloc_asprintf(ad_options, "sd_gc_%s", subdom->forest);
941N/A if (gc_service_name == NULL) {
1755N/A talloc_free(ad_options);
1755N/A return ENOMEM;
1755N/A }
1755N/A
1755N/A service_name = talloc_asprintf(ad_options, "sd_%s", subdom->name);
1755N/A if (service_name == NULL) {
1755N/A talloc_free(ad_options);
1755N/A return ENOMEM;
1755N/A }
1755N/A
1755N/A ad_servers = dp_opt_get_string(ad_options->basic, AD_SERVER);
1755N/A ad_backup_servers = dp_opt_get_string(ad_options->basic, AD_BACKUP_SERVER);
2476N/A
2476N/A /* Set KRB5 realm to same as the one of IPA when IPA
2476N/A * is able to attach PAC. For testing, use hardcoded. */
2476N/A ret = ad_failover_init(ad_options, be_ctx, ad_servers, ad_backup_servers,
2476N/A id_ctx->server_mode->realm,
2476N/A service_name, gc_service_name,
subdom->name, &ad_options->service);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n");
talloc_free(ad_options);
return ret;
}
ad_id_ctx = ad_id_ctx_init(ad_options, be_ctx);
if (ad_id_ctx == NULL) {
talloc_free(ad_options);
return ENOMEM;
}
ad_id_ctx->sdap_id_ctx->opts = ad_options->id;
ad_options->id_ctx = ad_id_ctx;
ad_site_override = dp_opt_get_string(ad_options->basic, AD_SITE);
/* use AD plugin */
srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx, be_ctx->be_res,
default_host_dbs,
ad_id_ctx->ad_options->id,
id_ctx->server_mode->hostname,
ad_domain,
ad_site_override);
if (srv_ctx == NULL) {
DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n");
return ENOMEM;
}
be_fo_set_srv_lookup_plugin(be_ctx, ad_srv_plugin_send,
ad_srv_plugin_recv, srv_ctx, "AD");
ret = sdap_domain_subdom_add(ad_id_ctx->sdap_id_ctx,
ad_id_ctx->sdap_id_ctx->opts->sdom,
subdom->parent);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize sdap domain\n");
talloc_free(ad_options);
return ret;
}
sdom = sdap_domain_get(ad_id_ctx->sdap_id_ctx->opts, subdom);
if (sdom == NULL) {
return EFAULT;
}
ret = ad_set_search_bases(ad_options->id, sdom);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD search bases\n");
talloc_free(ad_options);
return ret;
}
sdap_inherit_options(subdom->parent->sd_inherit,
id_ctx->sdap_id_ctx->opts,
ad_id_ctx->sdap_id_ctx->opts);
ret = sdap_id_setup_tasks(be_ctx,
ad_id_ctx->sdap_id_ctx,
sdom,
ldap_enumeration_send,
ldap_enumeration_recv,
ad_id_ctx->sdap_id_ctx);
if (ret != EOK) {
talloc_free(ad_options);
return ret;
}
sdom->pvt = ad_id_ctx;
/* Set up the ID mapping object */
ad_id_ctx->sdap_id_ctx->opts->idmap_ctx =
id_ctx->sdap_id_ctx->opts->idmap_ctx;
/* Set up the certificate mapping context */
ad_id_ctx->sdap_id_ctx->opts->sdap_certmap_ctx =
id_ctx->sdap_id_ctx->opts->sdap_certmap_ctx;
*_ad_id_ctx = ad_id_ctx;
return EOK;
}
struct ipa_getkeytab_state {
int child_status;
struct sss_child_ctx_old *child_ctx;
struct tevent_timer *timeout_handler;
};
static void ipa_getkeytab_exec(const char *ccache,
const char *server,
const char *principal,
const char *keytab_path);
static void ipa_getkeytab_done(int child_status,
struct tevent_signal *sige,
void *pvt);
static void ipa_getkeytab_timeout(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval tv, void *pvt);
static struct tevent_req *ipa_getkeytab_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
const char *ccache,
const char *server,
const char *principal,
const char *keytab)
{
errno_t ret;
struct tevent_req *req = NULL;
struct ipa_getkeytab_state *state;
pid_t child_pid;
struct timeval tv;
req = tevent_req_create(mem_ctx, &state, struct ipa_getkeytab_state);
if (req == NULL) {
return NULL;
}
state->child_status = EFAULT;
if (server == NULL || principal == NULL || keytab == NULL) {
ret = EINVAL;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC,
"Retrieving keytab for %s from %s into %s using ccache %s\n",
principal, server, keytab, ccache);
child_pid = fork();
if (child_pid == 0) { /* child */
ipa_getkeytab_exec(ccache, server, principal, keytab);
} else if (child_pid > 0) { /* parent */
/* Set up SIGCHLD handler */
ret = child_handler_setup(ev, child_pid, ipa_getkeytab_done, req,
&state->child_ctx);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n",
ret, sss_strerror(ret));
ret = ERR_IPA_GETKEYTAB_FAILED;
goto done;
}
/* Set up timeout handler */
tv = tevent_timeval_current_ofs(IPA_GETKEYTAB_TIMEOUT, 0);
state->timeout_handler = tevent_add_timer(ev, req, tv,
ipa_getkeytab_timeout, req);
if(state->timeout_handler == NULL) {
ret = ERR_IPA_GETKEYTAB_FAILED;
goto done;
}
/* Now either wait for the timeout to fire or the child
* to finish
*/
} else { /* error */
ret = errno;
DEBUG(SSSDBG_CRIT_FAILURE,
"fork failed [%d][%s].\n", ret, sss_strerror(ret));
goto done;
}
ret = EOK;
done:
if (ret != EOK) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static void ipa_getkeytab_exec(const char *ccache,
const char *server,
const char *principal,
const char *keytab_path)
{
errno_t ret;
int debug_fd;
const char *gkt_env[2] = { NULL, NULL };
if (debug_level >= SSSDBG_TRACE_LIBS) {
debug_fd = get_fd_from_debug_file();
ret = dup2(debug_fd, STDERR_FILENO);
if (ret == -1) {
ret = errno;
DEBUG(SSSDBG_MINOR_FAILURE,
"dup2 failed [%d][%s].\n", ret, sss_strerror(ret));
/* stderr is not fatal */
}
}
gkt_env[0] = talloc_asprintf(NULL, "KRB5CCNAME=%s", ccache);
if (gkt_env[0] == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to format KRB5CCNAME\n");
exit(1);
}
/* ipa-getkeytab cannot add keys to an empty file, let's unlink it and only
* use the filename */
ret = unlink(keytab_path);
if (ret == -1) {
ret = errno;
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to unlink the temporary ccname [%d][%s]\n",
ret, sss_strerror(ret));
exit(1);
}
errno = 0;
ret = execle(IPA_GETKEYTAB_PATH, IPA_GETKEYTAB_PATH,
"-r", "-s", server, "-p", principal, "-k", keytab_path, NULL,
gkt_env);
DEBUG(SSSDBG_CRIT_FAILURE,
"execle returned %d, this shouldn't happen!\n", ret);
/* The child should never end up here */
ret = errno;
DEBUG(SSSDBG_CRIT_FAILURE,
"execle failed [%d][%s].\n", ret, sss_strerror(ret));
exit(1);
}
static void ipa_getkeytab_done(int child_status,
struct tevent_signal *sige,
void *pvt)
{
struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
struct ipa_getkeytab_state *state =
tevent_req_data(req, struct ipa_getkeytab_state);
state->child_status = child_status;
if (WIFEXITED(child_status) && WEXITSTATUS(child_status) != 0) {
DEBUG(SSSDBG_OP_FAILURE,
"ipa-getkeytab failed with status [%d]\n", child_status);
tevent_req_error(req, ERR_IPA_GETKEYTAB_FAILED);
return;
}
if (WIFSIGNALED(child_status)) {
DEBUG(SSSDBG_OP_FAILURE,
"ipa-getkeytab was terminated by signal [%d]\n",
WTERMSIG(child_status));
tevent_req_error(req, ERR_IPA_GETKEYTAB_FAILED);
return;
}
tevent_req_done(req);
}
static void ipa_getkeytab_timeout(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval tv, void *pvt)
{
struct tevent_req *req =
talloc_get_type(pvt, struct tevent_req);
struct ipa_getkeytab_state *state =
tevent_req_data(req, struct ipa_getkeytab_state);
DEBUG(SSSDBG_CRIT_FAILURE, "Timeout reached for retrieving keytab from IPA server\n");
child_handler_destroy(state->child_ctx);
state->child_ctx = NULL;
state->child_status = ETIMEDOUT;
tevent_req_error(req, ERR_IPA_GETKEYTAB_FAILED);
}
static errno_t ipa_getkeytab_recv(struct tevent_req *req, int *child_status)
{
struct ipa_getkeytab_state *state =
tevent_req_data(req, struct ipa_getkeytab_state);
DEBUG(SSSDBG_TRACE_INTERNAL,
"ipa-getkeytab status %d\n", state->child_status);
if (child_status) {
*child_status = state->child_status;
}
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
static errno_t ipa_check_keytab(const char *keytab,
uid_t kt_owner_uid,
gid_t kt_owner_gid)
{
errno_t ret;
ret = check_file(keytab, getuid(), getgid(), S_IFREG|0600, 0, NULL, false);
if (ret == ENOENT) {
DEBUG(SSSDBG_TRACE_FUNC, "Keytab %s is not present\n", keytab);
goto done;
} else if (ret != EOK) {
if (kt_owner_uid) {
ret = check_file(keytab, kt_owner_uid, kt_owner_gid,
S_IFREG|0600, 0, NULL, false);
}
if (ret != EOK) {
if (ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to check for %s\n", keytab);
} else {
DEBUG(SSSDBG_TRACE_FUNC, "Keytab %s is not present\n", keytab);
}
}
goto done;
}
DEBUG(SSSDBG_TRACE_ALL, "keytab %s already exists\n", keytab);
ret = EOK;
done:
return ret;
}
struct ipa_server_trusted_dom_setup_state {
struct tevent_context *ev;
struct be_ctx *be_ctx;
struct ipa_id_ctx *id_ctx;
struct sss_domain_info *subdom;
uint32_t direction;
const char *forest;
const char *keytab;
char *new_keytab;
const char *principal;
const char *forest_realm;
const char *ccache;
};
static errno_t ipa_server_trusted_dom_setup_1way(struct tevent_req *req);
static void ipa_server_trust_1way_kt_done(struct tevent_req *subreq);
struct tevent_req *
ipa_server_trusted_dom_setup_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct be_ctx *be_ctx,
struct ipa_id_ctx *id_ctx,
struct sss_domain_info *subdom)
{
struct tevent_req *req = NULL;
struct ipa_server_trusted_dom_setup_state *state = NULL;
errno_t ret;
req = tevent_req_create(mem_ctx, &state,
struct ipa_server_trusted_dom_setup_state);
if (req == NULL) {
return NULL;
}
state->ev = ev;
state->be_ctx = be_ctx;
state->id_ctx = id_ctx;
state->subdom = subdom;
/* Trusts are only established with forest roots */
if (subdom->forest_root == NULL) {
DEBUG(SSSDBG_OP_FAILURE,
"Subdomain %s has no forest root?\n", subdom->name);
ret = ERR_TRUST_FOREST_UNKNOWN;
goto immediate;
}
state->direction = subdom->forest_root->trust_direction;
state->forest = subdom->forest_root->forest;
state->forest_realm = subdom->forest_root->realm;
state->ccache = talloc_asprintf(state, "%s/ccache_%s",
DB_PATH, subdom->parent->realm);
if (state->ccache == NULL) {
ret = ENOMEM;
goto immediate;
}
DEBUG(SSSDBG_TRACE_LIBS,
"Trust direction of subdom %s from forest %s is: %s\n",
subdom->name, state->forest,
ipa_trust_dir2str(state->direction));
if (state->direction & LSA_TRUST_DIRECTION_OUTBOUND) {
/* Use system keytab, nothing to do here */
ret = EOK;
goto immediate;
} else if (state->direction & LSA_TRUST_DIRECTION_INBOUND) {
/* Need special keytab */
ret = ipa_server_trusted_dom_setup_1way(req);
if (ret == EAGAIN) {
/* In progress.. */
return req;
} else if (ret == EOK) {
/* Keytab available, shortcut */
ret = EOK;
goto immediate;
}
} else {
/* Even unset is an error at this point */
DEBUG(SSSDBG_OP_FAILURE,
"Subdomain %s has trust direction %d\n",
subdom->name, subdom->trust_direction);
ret = ERR_TRUST_NOT_SUPPORTED;
}
immediate:
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Could not add trusted subdomain %s from forest %s\n",
subdom->name, state->forest);
tevent_req_error(req, ret);
} else {
tevent_req_done(req);
}
tevent_req_post(req, ev);
return req;
}
static errno_t ipa_server_trusted_dom_setup_1way(struct tevent_req *req)
{
errno_t ret;
struct tevent_req *subreq = NULL;
struct ipa_server_trusted_dom_setup_state *state =
tevent_req_data(req, struct ipa_server_trusted_dom_setup_state);
const char *hostname;
state->keytab = forest_keytab(state, state->forest);
if (state->keytab == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set up ipa_get_keytab\n");
return EIO;
}
state->new_keytab = talloc_asprintf(state, "%sXXXXXX", state->keytab);
if (state->new_keytab == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set up ipa_get_keytab\n");
return ENOMEM;
}
ret = sss_unique_filename(state, state->new_keytab);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create temporary keytab name\n");
return ret;
}
DEBUG(SSSDBG_TRACE_FUNC,
"Will re-fetch keytab for %s\n", state->subdom->name);
hostname = dp_opt_get_string(state->id_ctx->ipa_options->basic,
IPA_HOSTNAME);
state->principal = subdomain_trust_princ(state,
state->forest_realm,
state->subdom);
if (state->principal == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set up ipa_get_keytab\n");
return EIO;
}
subreq = ipa_getkeytab_send(state->be_ctx, state->be_ctx->ev,
state->ccache,
hostname,
state->principal,
state->new_keytab);
if (subreq == NULL) {
return ENOMEM;
}
tevent_req_set_callback(subreq, ipa_server_trust_1way_kt_done, req);
return EAGAIN;
}
static void ipa_server_trust_1way_kt_done(struct tevent_req *subreq)
{
errno_t ret;
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct ipa_server_trusted_dom_setup_state *state =
tevent_req_data(req, struct ipa_server_trusted_dom_setup_state);
ret = ipa_getkeytab_recv(subreq, NULL);
talloc_zfree(subreq);
if (ret != EOK) {
/* Do not fail here, but try to check and use the previous keytab,
* if any */
DEBUG(SSSDBG_MINOR_FAILURE, "ipa_getkeytab_recv failed: %d\n", ret);
} else {
DEBUG(SSSDBG_TRACE_FUNC,
"Keytab successfully retrieved to %s\n", state->new_keytab);
}
ret = ipa_check_keytab(state->new_keytab,
state->id_ctx->server_mode->kt_owner_uid,
state->id_ctx->server_mode->kt_owner_gid);
if (ret == EOK) {
ret = rename(state->new_keytab, state->keytab);
if (ret == -1) {
ret = errno;
DEBUG(SSSDBG_CRIT_FAILURE,
"rename failed [%d][%s].\n", ret, strerror(ret));
tevent_req_error(req, ret);
return;
}
DEBUG(SSSDBG_TRACE_INTERNAL, "Keytab renamed to %s\n", state->keytab);
} else if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Trying to recover and use the previous keytab, if available\n");
ret = ipa_check_keytab(state->keytab,
state->id_ctx->server_mode->kt_owner_uid,
state->id_ctx->server_mode->kt_owner_gid);
if (ret == EOK) {
DEBUG(SSSDBG_TRACE_FUNC,
"The previous keytab %s contains the expected principal\n",
state->keytab);
} else {
DEBUG(SSSDBG_OP_FAILURE,
"Cannot use the old keytab: %d\n", ret);
/* Nothing we can do now */
tevent_req_error(req, ret);
return;
}
}
DEBUG(SSSDBG_TRACE_FUNC,
"Keytab %s contains the expected principals\n", state->new_keytab);
DEBUG(SSSDBG_TRACE_FUNC,
"Established trust context for %s\n", state->subdom->name);
tevent_req_done(req);
}
errno_t ipa_server_trusted_dom_setup_recv(struct tevent_req *req)
{
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
struct ipa_server_create_trusts_state {
struct tevent_context *ev;
struct be_ctx *be_ctx;
struct ipa_id_ctx *id_ctx;
struct sss_domain_info *domiter;
};
static errno_t ipa_server_create_trusts_step(struct tevent_req *req);
static errno_t ipa_server_create_trusts_ctx(struct tevent_req *req);
static void ipa_server_create_trusts_done(struct tevent_req *subreq);
struct tevent_req *
ipa_server_create_trusts_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct be_ctx *be_ctx,
struct ipa_id_ctx *id_ctx,
struct sss_domain_info *parent)
{
struct tevent_req *req = NULL;
struct ipa_server_create_trusts_state *state = NULL;
errno_t ret;
req = tevent_req_create(mem_ctx, &state,
struct ipa_server_create_trusts_state);
if (req == NULL) {
return NULL;
}
state->ev = ev;
state->be_ctx = be_ctx;
state->id_ctx = id_ctx;
state->domiter = parent;
ret = ipa_server_create_trusts_step(req);
if (ret != EAGAIN) {
goto immediate;
}
return req;
immediate:
if (ret != EOK) {
tevent_req_error(req, ret);
} else {
tevent_req_done(req);
}
tevent_req_post(req, ev);
return req;
}
static errno_t ipa_server_create_trusts_step(struct tevent_req *req)
{
struct tevent_req *subreq = NULL;
struct ipa_ad_server_ctx *trust_iter;
struct ipa_ad_server_ctx *trust_i;
struct ipa_server_create_trusts_state *state = NULL;
state = tevent_req_data(req, struct ipa_server_create_trusts_state);
for (state->domiter = get_next_domain(state->domiter, SSS_GND_DESCEND);
state->domiter && IS_SUBDOMAIN(state->domiter);
state->domiter = get_next_domain(state->domiter, 0)) {
/* Check if we already have an ID context for this subdomain */
DLIST_FOR_EACH(trust_iter, state->id_ctx->server_mode->trusts) {
if (trust_iter->dom == state->domiter) {
break;
}
}
/* Newly detected trust */
if (trust_iter == NULL) {
subreq = ipa_server_trusted_dom_setup_send(state,
state->ev,
state->be_ctx,
state->id_ctx,
state->domiter);
if (subreq == NULL) {
return ENOMEM;
}
tevent_req_set_callback(subreq, ipa_server_create_trusts_done, req);
return EAGAIN;
}
}
/* Refresh all sdap_dom lists in all ipa_ad_server_ctx contexts */
DLIST_FOR_EACH(trust_iter, state->id_ctx->server_mode->trusts) {
struct sdap_domain *sdom_a;
sdom_a = sdap_domain_get(trust_iter->ad_id_ctx->sdap_id_ctx->opts,
trust_iter->dom);
if (sdom_a == NULL) {
continue;
}
DLIST_FOR_EACH(trust_i, state->id_ctx->server_mode->trusts) {
struct sdap_domain *sdom_b;
if (strcmp(trust_iter->dom->name, trust_i->dom->name) == 0) {
continue;
}
sdom_b = sdap_domain_get(trust_i->ad_id_ctx->sdap_id_ctx->opts,
sdom_a->dom);
if (sdom_b == NULL) {
continue;
}
/* Replace basedn and search bases from sdom_b with values
* from sdom_a */
sdap_domain_copy_search_bases(sdom_b, sdom_a);
}
}
return EOK;
}
static void ipa_server_create_trusts_done(struct tevent_req *subreq)
{
errno_t ret;
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
ret = ipa_server_trusted_dom_setup_recv(subreq);
talloc_zfree(subreq);
if (ret != EOK) {
tevent_req_error(req, ret);
return;
}
ret = ipa_server_create_trusts_ctx(req);
if (ret != EOK) {
tevent_req_error(req, ret);
return;
}
ret = ipa_server_create_trusts_step(req);
if (ret == EOK) {
tevent_req_done(req);
return;
} else if (ret != EAGAIN) {
tevent_req_error(req, ret);
return;
}
/* Will cycle back */
}
static errno_t ipa_server_create_trusts_ctx(struct tevent_req *req)
{
struct ipa_ad_server_ctx *trust_ctx;
struct ad_id_ctx *ad_id_ctx;
errno_t ret;
struct ipa_server_create_trusts_state *state = NULL;
state = tevent_req_data(req, struct ipa_server_create_trusts_state);
ret = ipa_ad_ctx_new(state->be_ctx, state->id_ctx, state->domiter, &ad_id_ctx);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Cannot create ad_id_ctx for subdomain %s\n", state->domiter->name);
return ret;
}
trust_ctx = talloc(state->id_ctx->server_mode, struct ipa_ad_server_ctx);
if (trust_ctx == NULL) {
return ENOMEM;
}
trust_ctx->dom = state->domiter;
trust_ctx->ad_id_ctx = ad_id_ctx;
DLIST_ADD(state->id_ctx->server_mode->trusts, trust_ctx);
return EOK;
}
errno_t ipa_server_create_trusts_recv(struct tevent_req *req)
{
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
void ipa_ad_subdom_remove(struct be_ctx *be_ctx,
struct ipa_id_ctx *id_ctx,
struct sss_domain_info *subdom)
{
struct ipa_ad_server_ctx *iter;
struct sdap_domain *sdom;
if (dp_opt_get_bool(id_ctx->ipa_options->basic,
IPA_SERVER_MODE) == false) {
return;
}
DLIST_FOR_EACH(iter, id_ctx->server_mode->trusts) {
if (iter->dom == subdom) break;
}
if (iter == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "No IPA-AD context for subdomain %s\n",
subdom->name);
return;
}
sdom = sdap_domain_get(iter->ad_id_ctx->sdap_id_ctx->opts, subdom);
if (sdom == NULL) return;
be_ptask_destroy(&sdom->enum_task);
be_ptask_destroy(&sdom->cleanup_task);
sdap_domain_remove(iter->ad_id_ctx->sdap_id_ctx->opts, subdom);
DLIST_REMOVE(id_ctx->server_mode->trusts, iter);
/* terminate all requests for this subdomain so we can free it */
dp_terminate_domain_requests(be_ctx->provider, subdom->name);
talloc_zfree(sdom);
}
struct ipa_ad_subdom_reinit_state {
struct tevent_context *ev;
struct be_ctx *be_ctx;
struct ipa_id_ctx *id_ctx;
struct sss_domain_info *parent;
};
static void create_trusts_at_startup_done(struct tevent_req *req)
{
errno_t ret;
ret = ipa_server_create_trusts_recv(req);
talloc_free(req);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"ipa_server_create_trusts_send request failed [%d]: %s\n",
ret, sss_strerror(ret));
}
}
static void create_trusts_at_startup(struct tevent_context *ev,
struct tevent_immediate *imm,
void *pvt)
{
struct tevent_req *req;
struct ipa_ad_subdom_reinit_state *state;
state = talloc_get_type(pvt, struct ipa_ad_subdom_reinit_state);
req = ipa_server_create_trusts_send(state, state->ev, state->be_ctx,
state->id_ctx, state->parent);
if (req == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "ipa_server_create_trusts_send failed.\n");
talloc_free(state);
return;
}
tevent_req_set_callback(req, create_trusts_at_startup_done, state);
return;
}
static errno_t ipa_ad_subdom_reinit(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct be_ctx *be_ctx,
struct ipa_id_ctx *id_ctx,
struct sss_domain_info *parent)
{
struct tevent_immediate *imm;
struct ipa_ad_subdom_reinit_state *state;
state = talloc(mem_ctx, struct ipa_ad_subdom_reinit_state);
if (state == NULL) {
return ENOMEM;
}
state->ev = ev;
state->be_ctx = be_ctx;
state->id_ctx = id_ctx;
state->parent = parent;
if (dp_opt_get_bool(id_ctx->ipa_options->basic,
IPA_SERVER_MODE) == false) {
return EOK;
}
imm = tevent_create_immediate(mem_ctx);
if (imm == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "tevent_create_immediate failed.\n");
talloc_free(state);
return ENOMEM;
}
tevent_schedule_immediate(imm, ev, create_trusts_at_startup, state);
return EOK;
}
int ipa_ad_subdom_init(struct be_ctx *be_ctx,
struct ipa_id_ctx *id_ctx)
{
char *realm;
char *hostname;
errno_t ret;
if (dp_opt_get_bool(id_ctx->ipa_options->basic,
IPA_SERVER_MODE) == false) {
return EOK;
}
/* The IPA code relies on the default FQDN format to unparse user
* names. Warn loudly if the full_name_format was customized on the
* IPA server
*/
if ((strcmp(be_ctx->domain->names->fq_fmt,
CONFDB_DEFAULT_FULL_NAME_FORMAT) != 0)
&& (strcmp(be_ctx->domain->names->fq_fmt,
CONFDB_DEFAULT_FULL_NAME_FORMAT_INTERNAL) != 0)) {
DEBUG(SSSDBG_FATAL_FAILURE, "%s is set to a non-default value [%s] " \
"lookups of subdomain users will likely fail!\n",
CONFDB_FULL_NAME_FORMAT, be_ctx->domain->names->fq_fmt);
sss_log(SSS_LOG_ERR, "%s is set to a non-default value [%s] " \
"lookups of subdomain users will likely fail!\n",
CONFDB_FULL_NAME_FORMAT, be_ctx->domain->names->fq_fmt);
/* Attempt to continue */
}
realm = dp_opt_get_string(id_ctx->ipa_options->basic, IPA_KRB5_REALM);
if (realm == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "No Kerberos realm for IPA?\n");
return EINVAL;
}
hostname = dp_opt_get_string(id_ctx->ipa_options->basic, IPA_HOSTNAME);
if (hostname == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "No host name for IPA?\n");
return EINVAL;
}
id_ctx->server_mode = talloc_zero(id_ctx, struct ipa_server_mode_ctx);
if (id_ctx->server_mode == NULL) {
return ENOMEM;
}
id_ctx->server_mode->realm = realm;
id_ctx->server_mode->hostname = hostname;
id_ctx->server_mode->trusts = NULL;
id_ctx->server_mode->ext_groups = NULL;
id_ctx->server_mode->kt_owner_uid = 0;
id_ctx->server_mode->kt_owner_gid = 0;
if (getuid() == 0) {
/* We need to handle keytabs created by IPA oddjob script gracefully
* even if we're running as root and IPA creates them as the SSSD user
*/
ret = sss_user_by_name_or_uid(SSSD_USER,
&id_ctx->server_mode->kt_owner_uid,
&id_ctx->server_mode->kt_owner_gid);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE, "Failed to get ID of %s\n", SSSD_USER);
}
}
ret = ipa_ad_subdom_reinit(be_ctx, be_ctx->ev,
be_ctx, id_ctx, be_ctx->domain);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "ipa_ad_subdom_refresh failed.\n");
return ret;
}
return EOK;
}