diff --git a/0001-AD-read-flat-name-and-SID-of-the-AD-domain.patch b/0001-AD-read-flat-name-and-SID-of-the-AD-domain.patch new file mode 100644 index 0000000..c866125 --- /dev/null +++ b/0001-AD-read-flat-name-and-SID-of-the-AD-domain.patch @@ -0,0 +1,702 @@ +From 4cdaf239d4504966bed8ecd5e3fa07def74c7302 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 2 May 2013 20:28:30 +0200 +Subject: [PATCH 1/6] AD: read flat name and SID of the AD domain + +For various features either the flat/short/NetBIOS domain name or the +domain SID is needed. Since the responders already try to do a subdomain +lookup when and known domain name is encountered I added a subdomain +lookup to the AD provider which currently only reads the SID from the +base DN and the NetBIOS name from a reply of a LDAP ping. The results +are written to the cache to have them available even if SSSD is started +in offline mode. Looking up trusted domains can be added later. + +Since all the needed responder code is already available from the +corresponding work for the IPA provider this patch fixes + +https://fedorahosted.org/sssd/ticket/1468 +--- + Makefile.am | 2 + + src/config/etc/sssd.api.d/sssd-ad.conf | 2 + + src/man/sssd-ad.5.xml | 4 + + src/man/sssd.conf.5.xml | 4 + + src/providers/ad/ad_init.c | 31 ++ + src/providers/ad/ad_subdomains.c | 522 +++++++++++++++++++++++++++++++++ + src/providers/ad/ad_subdomains.h | 37 +++ + 7 files changed, 602 insertions(+) + create mode 100644 src/providers/ad/ad_subdomains.c + create mode 100644 src/providers/ad/ad_subdomains.h + +diff --git a/Makefile.am b/Makefile.am +index 3abea76c18f49df623ff9c38ebc5d604596d2516..b72384a77fe5bb3d2d40229026c463fefabc1387 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1621,6 +1621,8 @@ libsss_ad_la_SOURCES = \ + src/providers/ad/ad_access.h \ + src/providers/ad/ad_opts.h \ + src/providers/ad/ad_srv.c \ ++ src/providers/ad/ad_subdomains.c \ ++ src/providers/ad/ad_subdomains.h \ + src/util/find_uid.c \ + src/util/user_info_msg.c \ + src/util/sss_krb5.c \ +diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf +index b4b1d0ba11d600a8b9a300f15cc8058be470f422..3be25e8da05ee6b1bbdb947e919421358591cdde 100644 +--- a/src/config/etc/sssd.api.d/sssd-ad.conf ++++ b/src/config/etc/sssd.api.d/sssd-ad.conf +@@ -126,3 +126,5 @@ krb5_use_enterprise_principal = bool, None, false + [provider/ad/chpass] + krb5_kpasswd = str, None, false + krb5_backup_kpasswd = str, None, false ++ ++[provider/ad/subdomains] +diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml +index c19607715dafd39f167c3066831ae7ad09ffe459..4dcd552d7f1fe3235af9c582c49c553441a014e9 100644 +--- a/src/man/sssd-ad.5.xml ++++ b/src/man/sssd-ad.5.xml +@@ -95,6 +95,10 @@ ldap_id_mapping = False + specified as the lower-case version of the long + version of the Active Directory domain. + ++ ++ The short domain name (also known as the NetBIOS ++ or the flat name) is autodetected by the SSSD. ++ + + + +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index 04c699948b093c235762c0d9f8223911a6fd2ada..99337fbba9fb8d39a62eb84313c5b89761ee950d 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -1481,6 +1481,10 @@ override_homedir = /home/%u + Regular expression for this domain that describes + how to parse the string containing user name and + domain into these components. ++ The "domain" can match either the SSSD ++ configuration domain name, or, in the case ++ of IPA trust subdomains and Active Directory ++ domains, the flat (NetBIOS) name of the domain. + + + Default for the AD and IPA provider: +diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c +index 2f5a5da1510b0241a4bee12fc93da174fdd0d116..f90df2a6913222b023704e9e1d1dce9561772e98 100644 +--- a/src/providers/ad/ad_init.c ++++ b/src/providers/ad/ad_init.c +@@ -37,6 +37,7 @@ + #include "providers/ad/ad_id.h" + #include "providers/ad/ad_srv.h" + #include "providers/dp_dyndns.h" ++#include "providers/ad/ad_subdomains.h" + + struct ad_options *ad_options = NULL; + +@@ -361,3 +362,33 @@ ad_shutdown(struct be_req *req) + /* TODO: Clean up any internal data */ + sdap_handler_done(req, DP_ERR_OK, EOK, NULL); + } ++ ++int sssm_ad_subdomains_init(struct be_ctx *bectx, ++ struct bet_ops **ops, ++ void **pvt_data) ++{ ++ int ret; ++ struct ad_id_ctx *id_ctx; ++ const char *ad_domain; ++ ++ ret = sssm_ad_id_init(bectx, ops, (void **) &id_ctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ("sssm_ad_id_init failed.\n")); ++ return ret; ++ } ++ ++ if (ad_options == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ("Global AD options not available.\n")); ++ return EINVAL; ++ } ++ ++ ad_domain = dp_opt_get_string(ad_options->basic, AD_DOMAIN); ++ ++ ret = ad_subdom_init(bectx, id_ctx, ad_domain, ops, pvt_data); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ("ad_subdom_init failed.\n")); ++ return ret; ++ } ++ ++ return EOK; ++} +diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c +new file mode 100644 +index 0000000000000000000000000000000000000000..1da343f8711b2b99a7afff6a4a398a1aa515a875 +--- /dev/null ++++ b/src/providers/ad/ad_subdomains.c +@@ -0,0 +1,522 @@ ++/* ++ SSSD ++ ++ AD Subdomains Module ++ ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2013 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "providers/ldap/sdap_async.h" ++#include "providers/ad/ad_subdomains.h" ++#include ++#include ++#include ++ ++#define AD_AT_OBJECT_SID "objectSID" ++#define AD_AT_DNS_DOMAIN "DnsDomain" ++#define AD_AT_NT_VERSION "NtVer" ++#define AD_AT_NETLOGON "netlogon" ++ ++#define MASTER_DOMAIN_SID_FILTER "objectclass=domain" ++ ++/* do not refresh more often than every 5 seconds for now */ ++#define AD_SUBDOMAIN_REFRESH_LIMIT 5 ++ ++/* refresh automatically every 4 hours */ ++#define AD_SUBDOMAIN_REFRESH_PERIOD (3600 * 4) ++ ++struct ad_subdomains_ctx { ++ struct be_ctx *be_ctx; ++ struct sdap_id_ctx *sdap_id_ctx; ++ struct sss_idmap_ctx *idmap_ctx; ++ char *domain_name; ++ ++ time_t last_refreshed; ++ struct tevent_timer *timer_event; ++}; ++ ++struct ad_subdomains_req_ctx { ++ struct be_req *be_req; ++ struct ad_subdomains_ctx *sd_ctx; ++ struct sdap_id_op *sdap_op; ++ ++ char *current_filter; ++ size_t base_iter; ++ ++ size_t reply_count; ++ struct sysdb_attrs **reply; ++ ++ char *master_sid; ++ char *flat_name; ++}; ++ ++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 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->sdap_id_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->sdap_id_ctx->opts->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; ++ ++ 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")); ++ } 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 = EOK; ++ ++done: ++ ++ if (ret == EOK) { ++ ctx->sd_ctx->last_refreshed = time(NULL); ++ } ++ be_req_terminate(ctx->be_req, DP_ERR_FATAL, 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->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; ++} +diff --git a/src/providers/ad/ad_subdomains.h b/src/providers/ad/ad_subdomains.h +new file mode 100644 +index 0000000000000000000000000000000000000000..b1a418f132595c10abd8448f78a5df62402314a8 +--- /dev/null ++++ b/src/providers/ad/ad_subdomains.h +@@ -0,0 +1,37 @@ ++/* ++ SSSD ++ ++ AD Subdomains Module ++ ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2013 Red Hat ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#ifndef _IPA_SUBDOMAINS_H_ ++#define _IPA_SUBDOMAINS_H_ ++ ++#include "providers/dp_backend.h" ++#include "providers/ad/ad_common.h" ++ ++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); ++ ++#endif /* _IPA_SUBDOMAINS_H_ */ +-- +1.8.2.1 + diff --git a/0002-Actually-use-the-index-parameter-in-resolv_get_socka.patch b/0002-Actually-use-the-index-parameter-in-resolv_get_socka.patch new file mode 100644 index 0000000..c0d4494 --- /dev/null +++ b/0002-Actually-use-the-index-parameter-in-resolv_get_socka.patch @@ -0,0 +1,65 @@ +From 7c091610f5b35e8ba89da839322f6591f1e7619b Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 6 May 2013 15:10:22 +0200 +Subject: [PATCH 2/6] Actually use the index parameter in + resolv_get_sockaddr_address_index + +--- + src/resolv/async_resolv.c | 11 +++++++---- + src/resolv/async_resolv.h | 5 +++-- + 2 files changed, 10 insertions(+), 6 deletions(-) + +diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c +index 1eb0acf83cb5bb48d92a58b8baf872f4ca4d8278..ad9d58297d0701e2a48ef86179c93c71320654fb 100644 +--- a/src/resolv/async_resolv.c ++++ b/src/resolv/async_resolv.c +@@ -1453,8 +1453,9 @@ resolv_get_string_ptr_address(TALLOC_CTX *mem_ctx, + } + + struct sockaddr_storage * +-resolv_get_sockaddr_address_index(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent, +- int port, int index) ++resolv_get_sockaddr_address_index(TALLOC_CTX *mem_ctx, ++ struct resolv_hostent *hostent, ++ int port, int addrindex) + { + struct sockaddr_storage *sockaddr; + +@@ -1470,14 +1471,16 @@ resolv_get_sockaddr_address_index(TALLOC_CTX *mem_ctx, struct resolv_hostent *ho + case AF_INET: + sockaddr->ss_family = AF_INET; + memcpy(&((struct sockaddr_in *) sockaddr)->sin_addr, +- hostent->addr_list[0]->ipaddr, sizeof(struct in_addr)); ++ hostent->addr_list[addrindex]->ipaddr, ++ sizeof(struct in_addr)); + ((struct sockaddr_in *) sockaddr)->sin_port = (in_port_t) htons(port); + + break; + case AF_INET6: + sockaddr->ss_family = AF_INET6; + memcpy(&((struct sockaddr_in6 *) sockaddr)->sin6_addr, +- hostent->addr_list[0]->ipaddr, sizeof(struct in6_addr)); ++ hostent->addr_list[addrindex]->ipaddr, ++ sizeof(struct in6_addr)); + ((struct sockaddr_in6 *) sockaddr)->sin6_port = (in_port_t) htons(port); + break; + default: +diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h +index 9bf5e0c40e44c385858fa4d85adebe6e022ca006..a8207884c79d7a12af2bc1fc9da9c1304b2c252d 100644 +--- a/src/resolv/async_resolv.h ++++ b/src/resolv/async_resolv.h +@@ -129,8 +129,9 @@ resolv_get_string_ptr_address(TALLOC_CTX *mem_ctx, + resolv_get_string_address_index(mem_ctx, hostent, 0) + + struct sockaddr_storage * +-resolv_get_sockaddr_address_index(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent, +- int port, int index); ++resolv_get_sockaddr_address_index(TALLOC_CTX *mem_ctx, ++ struct resolv_hostent *hostent, ++ int port, int addrindex); + + #define resolv_get_sockaddr_address(mem_ctx, rhostent, port) \ + resolv_get_sockaddr_address_index(mem_ctx, rhostent, port, 0) +-- +1.8.2.1 + diff --git a/0003-UTIL-Add-function-sss_names_init_from_args.patch b/0003-UTIL-Add-function-sss_names_init_from_args.patch new file mode 100644 index 0000000..d250c91 --- /dev/null +++ b/0003-UTIL-Add-function-sss_names_init_from_args.patch @@ -0,0 +1,205 @@ +From f54b271376b23cb968eafb9ffd5100c6dadad2a7 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Fri, 26 Apr 2013 09:40:53 +0200 +Subject: [PATCH 3/6] UTIL: Add function sss_names_init_from_args + +This function allows initializing sss_names_ctx using a regular expression and +fully qualified format string specified in its arguments. +--- + src/util/usertools.c | 107 +++++++++++++++++++++++++++++++-------------------- + src/util/util.h | 7 ++++ + 2 files changed, 73 insertions(+), 41 deletions(-) + +diff --git a/src/util/usertools.c b/src/util/usertools.c +index 7323d9f8260580f32b4ab55c8c2db5bd7eec20ed..91110f263657de9ba53ed305e7c4710eb006bec6 100644 +--- a/src/util/usertools.c ++++ b/src/util/usertools.c +@@ -135,13 +135,11 @@ done: + #endif + } + +-int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, +- const char *domain, struct sss_names_ctx **out) ++int sss_names_init_from_args(TALLOC_CTX *mem_ctx, const char *re_pattern, ++ const char *fq_fmt, struct sss_names_ctx **out) + { + struct sss_names_ctx *ctx; +- TALLOC_CTX *tmpctx = NULL; + const char *errstr; +- char *conf_path; + int errval; + int errpos; + int ret; +@@ -150,6 +148,49 @@ int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, + if (!ctx) return ENOMEM; + talloc_set_destructor(ctx, sss_names_ctx_destructor); + ++ ctx->re_pattern = talloc_strdup(ctx, re_pattern); ++ if (ctx->re_pattern == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_CONF_SETTINGS, ("Using re [%s].\n", ctx->re_pattern)); ++ ++ ctx->fq_fmt = talloc_strdup(ctx, fq_fmt); ++ if (ctx->fq_fmt == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ctx->re = pcre_compile2(ctx->re_pattern, ++ NAME_DOMAIN_PATTERN_OPTIONS, ++ &errval, &errstr, &errpos, NULL); ++ if (!ctx->re) { ++ DEBUG(1, ("Invalid Regular Expression pattern at position %d." ++ " (Error: %d [%s])\n", errpos, errval, errstr)); ++ ret = EFAULT; ++ goto done; ++ } ++ ++ *out = ctx; ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ talloc_free(ctx); ++ } ++ return ret; ++} ++ ++int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, ++ const char *domain, struct sss_names_ctx **out) ++{ ++ TALLOC_CTX *tmpctx = NULL; ++ char *conf_path; ++ char *re_pattern; ++ char *fq_fmt; ++ int ret; ++ + tmpctx = talloc_new(NULL); + if (tmpctx == NULL) { + ret = ENOMEM; +@@ -162,19 +203,19 @@ int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, + goto done; + } + +- ret = confdb_get_string(cdb, ctx, conf_path, +- CONFDB_NAME_REGEX, NULL, &ctx->re_pattern); ++ ret = confdb_get_string(cdb, tmpctx, conf_path, ++ CONFDB_NAME_REGEX, NULL, &re_pattern); + if (ret != EOK) goto done; + + /* If not found in the domain, look in globals */ +- if (ctx->re_pattern == NULL) { +- ret = confdb_get_string(cdb, ctx, CONFDB_MONITOR_CONF_ENTRY, +- CONFDB_NAME_REGEX, NULL, &ctx->re_pattern); ++ if (re_pattern == NULL) { ++ ret = confdb_get_string(cdb, tmpctx, CONFDB_MONITOR_CONF_ENTRY, ++ CONFDB_NAME_REGEX, NULL, &re_pattern); + if (ret != EOK) goto done; + } + +- if (ctx->re_pattern == NULL) { +- ret = get_id_provider_default_re(ctx, cdb, conf_path, &ctx->re_pattern); ++ if (re_pattern == NULL) { ++ ret = get_id_provider_default_re(tmpctx, cdb, conf_path, &re_pattern); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Failed to get provider default regular " \ + "expression for domain [%s].\n", domain)); +@@ -182,10 +223,10 @@ int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, + } + } + +- if (!ctx->re_pattern) { +- ctx->re_pattern = talloc_strdup(ctx, +- "(?P[^@]+)@?(?P[^@]*$)"); +- if (!ctx->re_pattern) { ++ if (!re_pattern) { ++ re_pattern = talloc_strdup(tmpctx, ++ "(?P[^@]+)@?(?P[^@]*$)"); ++ if (!re_pattern) { + ret = ENOMEM; + goto done; + } +@@ -195,49 +236,33 @@ int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, + "not support non-unique named subpatterns.\n")); + DEBUG(2, ("Please make sure that your pattern [%s] only contains " + "subpatterns with a unique name and uses " +- "the Python syntax (?P).\n", ctx->re_pattern)); ++ "the Python syntax (?P).\n", re_pattern)); + #endif + } + +- DEBUG(SSSDBG_CONF_SETTINGS, ("Using re [%s].\n", ctx->re_pattern)); +- +- ret = confdb_get_string(cdb, ctx, conf_path, +- CONFDB_FULL_NAME_FORMAT, NULL, &ctx->fq_fmt); ++ ret = confdb_get_string(cdb, tmpctx, conf_path, ++ CONFDB_FULL_NAME_FORMAT, NULL, &fq_fmt); + if (ret != EOK) goto done; + + /* If not found in the domain, look in globals */ +- if (ctx->fq_fmt == NULL) { +- ret = confdb_get_string(cdb, ctx, CONFDB_MONITOR_CONF_ENTRY, +- CONFDB_FULL_NAME_FORMAT, NULL, &ctx->fq_fmt); ++ if (fq_fmt == NULL) { ++ ret = confdb_get_string(cdb, tmpctx, CONFDB_MONITOR_CONF_ENTRY, ++ CONFDB_FULL_NAME_FORMAT, NULL, &fq_fmt); + if (ret != EOK) goto done; + } + +- if (!ctx->fq_fmt) { +- ctx->fq_fmt = talloc_strdup(ctx, "%1$s@%2$s"); +- if (!ctx->fq_fmt) { ++ if (!fq_fmt) { ++ fq_fmt = talloc_strdup(tmpctx, "%1$s@%2$s"); ++ if (!fq_fmt) { + ret = ENOMEM; + goto done; + } + } + +- ctx->re = pcre_compile2(ctx->re_pattern, +- NAME_DOMAIN_PATTERN_OPTIONS, +- &errval, &errstr, &errpos, NULL); +- if (!ctx->re) { +- DEBUG(1, ("Invalid Regular Expression pattern at position %d." +- " (Error: %d [%s])\n", errpos, errval, errstr)); +- ret = EFAULT; +- goto done; +- } +- +- *out = ctx; +- ret = EOK; ++ ret = sss_names_init_from_args(mem_ctx, re_pattern, fq_fmt, out); + + done: + talloc_free(tmpctx); +- if (ret != EOK) { +- talloc_free(ctx); +- } + return ret; + } + +diff --git a/src/util/util.h b/src/util/util.h +index 33725f63591a4d165a084c1fd361f9651e80e50b..49dc850c36cedd83755034367357fab41bd32ec6 100644 +--- a/src/util/util.h ++++ b/src/util/util.h +@@ -401,6 +401,13 @@ struct sss_names_ctx { + pcre *re; + }; + ++/* initialize sss_names_ctx directly from arguments */ ++int sss_names_init_from_args(TALLOC_CTX *mem_ctx, ++ const char *re_pattern, ++ const char *fq_fmt, ++ struct sss_names_ctx **out); ++ ++/* initialize sss_names_ctx from domain configuration */ + int sss_names_init(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + const char *domain, +-- +1.8.2.1 + diff --git a/0004-SSH-Fix-parsing-of-names-from-client-requests.patch b/0004-SSH-Fix-parsing-of-names-from-client-requests.patch new file mode 100644 index 0000000..b063bbd --- /dev/null +++ b/0004-SSH-Fix-parsing-of-names-from-client-requests.patch @@ -0,0 +1,116 @@ +From 728b10c81204929be5669c1e67bd086e09c47c00 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Fri, 26 Apr 2013 09:53:47 +0200 +Subject: [PATCH 4/6] SSH: Fix parsing of names from client requests + +Try to parse names in the form user@domain first, as that's what sss_ssh_* +send in requests when the --domain option is used. Do not parse host names +using domain-specific regular expression. +--- + src/responder/ssh/sshsrv.c | 8 ++++++++ + src/responder/ssh/sshsrv_cmd.c | 23 ++++++++++++++++++++--- + src/responder/ssh/sshsrv_private.h | 2 ++ + 3 files changed, 30 insertions(+), 3 deletions(-) + +diff --git a/src/responder/ssh/sshsrv.c b/src/responder/ssh/sshsrv.c +index 8a66f2239ac370218ec48d4cfc003d40dc1b7aec..410e631af43b8e8ef160334bab9a540ea913804c 100644 +--- a/src/responder/ssh/sshsrv.c ++++ b/src/responder/ssh/sshsrv.c +@@ -118,6 +118,14 @@ int ssh_process_init(TALLOC_CTX *mem_ctx, + ssh_ctx->rctx = rctx; + ssh_ctx->rctx->pvt_ctx = ssh_ctx; + ++ ret = sss_names_init_from_args(ssh_ctx, ++ "(?P[^@]+)@?(?P[^@]*$)", ++ "%1$s@%2$s", &ssh_ctx->snctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ("fatal error initializing regex data\n")); ++ goto fail; ++ } ++ + /* Enable automatic reconnection to the Data Provider */ + ret = confdb_get_int(ssh_ctx->rctx->cdb, + CONFDB_SSH_CONF_ENTRY, +diff --git a/src/responder/ssh/sshsrv_cmd.c b/src/responder/ssh/sshsrv_cmd.c +index 671160ea77904bc5d9a74fee1e351fec8b7cb3fb..374abe6c6ef4ffe1abeeafa2fe94602f5bff3414 100644 +--- a/src/responder/ssh/sshsrv_cmd.c ++++ b/src/responder/ssh/sshsrv_cmd.c +@@ -55,6 +55,7 @@ sss_ssh_cmd_get_user_pubkeys(struct cli_ctx *cctx) + return ENOMEM; + } + cmd_ctx->cctx = cctx; ++ cmd_ctx->is_user = true; + + ret = ssh_cmd_parse_request(cmd_ctx); + if (ret != EOK) { +@@ -101,6 +102,7 @@ sss_ssh_cmd_get_host_pubkeys(struct cli_ctx *cctx) + return ENOMEM; + } + cmd_ctx->cctx = cctx; ++ cmd_ctx->is_user = false; + + ret = ssh_cmd_parse_request(cmd_ctx); + if (ret != EOK) { +@@ -673,6 +675,8 @@ static errno_t + ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx) + { + struct cli_ctx *cctx = cmd_ctx->cctx; ++ struct ssh_ctx *ssh_ctx = talloc_get_type(cctx->rctx->pvt_ctx, ++ struct ssh_ctx); + errno_t ret; + uint8_t *body; + size_t body_len; +@@ -705,14 +709,27 @@ ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx) + } + c += name_len; + +- ret = sss_parse_name_for_domains(cmd_ctx, cctx->rctx->domains, +- cctx->rctx->default_domain,name, +- &cmd_ctx->domname, &cmd_ctx->name); ++ ret = sss_parse_name(cmd_ctx, ssh_ctx->snctx, name, ++ &cmd_ctx->domname, &cmd_ctx->name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", name)); + return ENOENT; + } + ++ if (cmd_ctx->is_user && cmd_ctx->domname == NULL) { ++ name = cmd_ctx->name; ++ ++ ret = sss_parse_name_for_domains(cmd_ctx, cctx->rctx->domains, ++ cctx->rctx->default_domain, name, ++ &cmd_ctx->domname, ++ &cmd_ctx->name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ ("Invalid name received [%s]\n", name)); ++ return ENOENT; ++ } ++ } ++ + if (flags & 1) { + SAFEALIGN_COPY_UINT32_CHECK(&alias_len, body+c, body_len, &c); + if (alias_len == 0 || alias_len > body_len - c) { +diff --git a/src/responder/ssh/sshsrv_private.h b/src/responder/ssh/sshsrv_private.h +index 296bd94a2947796198a0559c06d904b389283ade..ebb30ce7cbc982bb29b73592d5873e7d3652228a 100644 +--- a/src/responder/ssh/sshsrv_private.h ++++ b/src/responder/ssh/sshsrv_private.h +@@ -28,6 +28,7 @@ + + struct ssh_ctx { + struct resp_ctx *rctx; ++ struct sss_names_ctx *snctx; + + bool hash_known_hosts; + int known_hosts_timeout; +@@ -38,6 +39,7 @@ struct ssh_cmd_ctx { + char *name; + char *alias; + char *domname; ++ bool is_user; + + struct sss_domain_info *domain; + bool check_next; +-- +1.8.2.1 + diff --git a/0005-SSH-Use-separate-field-for-domain-name-in-client-req.patch b/0005-SSH-Use-separate-field-for-domain-name-in-client-req.patch new file mode 100644 index 0000000..2faed0f --- /dev/null +++ b/0005-SSH-Use-separate-field-for-domain-name-in-client-req.patch @@ -0,0 +1,315 @@ +From 28e55560008f21a532b103b3f612c6fca2a54d76 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Fri, 26 Apr 2013 10:45:42 +0200 +Subject: [PATCH 5/6] SSH: Use separate field for domain name in client + requests + +Instead of appending @domain to names when the --domain option of sss_ssh_* is +used, put domain name in a separate field in client requests. +--- + src/responder/ssh/sshsrv_cmd.c | 91 +++++++++++++++++++--------- + src/sss_client/ssh/sss_ssh_authorizedkeys.c | 15 +---- + src/sss_client/ssh/sss_ssh_client.c | 38 ++++++++---- + src/sss_client/ssh/sss_ssh_client.h | 1 + + src/sss_client/ssh/sss_ssh_knownhostsproxy.c | 12 +--- + src/util/sss_ssh.h | 4 ++ + 6 files changed, 97 insertions(+), 64 deletions(-) + +diff --git a/src/responder/ssh/sshsrv_cmd.c b/src/responder/ssh/sshsrv_cmd.c +index 374abe6c6ef4ffe1abeeafa2fe94602f5bff3414..d2f889fa6ac1e414dfa9bbd943b8ef6af125ae74 100644 +--- a/src/responder/ssh/sshsrv_cmd.c ++++ b/src/responder/ssh/sshsrv_cmd.c +@@ -685,12 +685,14 @@ ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx) + uint32_t name_len; + char *name; + uint32_t alias_len; +- char *alias; ++ char *alias = NULL; ++ uint32_t domain_len; ++ char *domain = cctx->rctx->default_domain; + + sss_packet_get_body(cctx->creq->in, &body, &body_len); + + SAFEALIGN_COPY_UINT32_CHECK(&flags, body+c, body_len, &c); +- if (flags > 1) { ++ if (flags & ~(uint32_t)SSS_SSH_REQ_MASK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid flags received [0x%x]\n", flags)); + return EINVAL; + } +@@ -709,28 +711,7 @@ ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx) + } + c += name_len; + +- ret = sss_parse_name(cmd_ctx, ssh_ctx->snctx, name, +- &cmd_ctx->domname, &cmd_ctx->name); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", name)); +- return ENOENT; +- } +- +- if (cmd_ctx->is_user && cmd_ctx->domname == NULL) { +- name = cmd_ctx->name; +- +- ret = sss_parse_name_for_domains(cmd_ctx, cctx->rctx->domains, +- cctx->rctx->default_domain, name, +- &cmd_ctx->domname, +- &cmd_ctx->name); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- ("Invalid name received [%s]\n", name)); +- return ENOENT; +- } +- } +- +- if (flags & 1) { ++ if (flags & SSS_SSH_REQ_ALIAS) { + SAFEALIGN_COPY_UINT32_CHECK(&alias_len, body+c, body_len, &c); + if (alias_len == 0 || alias_len > body_len - c) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid alias length\n")); +@@ -744,11 +725,67 @@ ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx) + return EINVAL; + } + c += alias_len; ++ } + +- if (strcmp(cmd_ctx->name, alias) != 0) { +- cmd_ctx->alias = talloc_strdup(cmd_ctx, alias); +- if (!cmd_ctx->alias) return ENOMEM; ++ if (flags & SSS_SSH_REQ_DOMAIN) { ++ SAFEALIGN_COPY_UINT32_CHECK(&domain_len, body+c, body_len, &c); ++ if (domain_len > 0) { ++ if (domain_len > body_len - c) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid domain length\n")); ++ return EINVAL; ++ } ++ ++ domain = (char *)(body+c); ++ if (!sss_utf8_check((const uint8_t *)domain, domain_len-1) || ++ domain[domain_len-1] != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ ("Domain is not valid UTF-8 string\n")); ++ return EINVAL; ++ } ++ c += domain_len; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, ++ ("Requested domain [%s]\n", domain ? domain : "")); ++ } else { ++ DEBUG(SSSDBG_TRACE_FUNC, ("Splitting domain from name [%s]\n", name)); ++ ++ ret = sss_parse_name(cmd_ctx, ssh_ctx->snctx, name, ++ &cmd_ctx->domname, &cmd_ctx->name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", name)); ++ return ENOENT; + } ++ ++ name = cmd_ctx->name; ++ } ++ ++ if (cmd_ctx->is_user && cmd_ctx->domname == NULL) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ ("Parsing name [%s][%s]\n", name, domain ? domain : "")); ++ ++ ret = sss_parse_name_for_domains(cmd_ctx, cctx->rctx->domains, ++ domain, name, ++ &cmd_ctx->domname, ++ &cmd_ctx->name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ ("Invalid name received [%s]\n", name)); ++ return ENOENT; ++ } ++ } else if (cmd_ctx->name == NULL && cmd_ctx->domname == NULL) { ++ cmd_ctx->name = talloc_strdup(cmd_ctx, name); ++ if (!cmd_ctx->name) return ENOMEM; ++ ++ if (domain != NULL) { ++ cmd_ctx->domname = talloc_strdup(cmd_ctx, domain); ++ if (!cmd_ctx->domname) return ENOMEM; ++ } ++ } ++ ++ if (alias != NULL && strcmp(cmd_ctx->name, alias) != 0) { ++ cmd_ctx->alias = talloc_strdup(cmd_ctx, alias); ++ if (!cmd_ctx->alias) return ENOMEM; + } + + return EOK; +diff --git a/src/sss_client/ssh/sss_ssh_authorizedkeys.c b/src/sss_client/ssh/sss_ssh_authorizedkeys.c +index 11deff9a6bb2592ce102ff314bcb2b92f90fa1da..bc991a837635186449b1fd5f1c6bdc944176c43d 100644 +--- a/src/sss_client/ssh/sss_ssh_authorizedkeys.c ++++ b/src/sss_client/ssh/sss_ssh_authorizedkeys.c +@@ -43,7 +43,6 @@ int main(int argc, const char **argv) + POPT_TABLEEND + }; + poptContext pc = NULL; +- const char *user; + struct sss_ssh_ent *ent; + size_t i; + char *repr; +@@ -84,21 +83,9 @@ int main(int argc, const char **argv) + BAD_POPT_PARAMS(pc, _("User not specified\n"), ret, fini); + } + +- /* append domain to username if domain is specified */ +- if (pc_domain) { +- user = talloc_asprintf(mem_ctx, "%s@%s", pc_user, pc_domain); +- if (!user) { +- ERROR("Not enough memory\n"); +- ret = EXIT_FAILURE; +- goto fini; +- } +- } else { +- user = pc_user; +- } +- + /* look up public keys */ + ret = sss_ssh_get_ent(mem_ctx, SSS_SSH_GET_USER_PUBKEYS, +- user, NULL, &ent); ++ pc_user, pc_domain, NULL, &ent); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("sss_ssh_get_ent() failed (%d): %s\n", ret, strerror(ret))); +diff --git a/src/sss_client/ssh/sss_ssh_client.c b/src/sss_client/ssh/sss_ssh_client.c +index 645f2928985637f26213ab7a0d48a626b088ad58..5312dba2be32aa0cc8813dedfc4189edeff7085c 100644 +--- a/src/sss_client/ssh/sss_ssh_client.c ++++ b/src/sss_client/ssh/sss_ssh_client.c +@@ -70,29 +70,34 @@ int set_locale(void) + + /* SSH public key request: + * +- * 0..3: flags (unsigned int, must be 0 or 1) +- * 4..7: name length (unsigned int) +- * 8..(X-1): name (null-terminated UTF-8 string) +- * if (flags & 1) { +- * X..(X+3): alias length (unsigned int) +- * (X+4)..Y: alias (null-terminated UTF-8 string) +- * } ++ * header: ++ * 0..3: flags (unsigned int, must be combination of SSS_SSH_REQ_* flags) ++ * 4..7: name length (unsigned int) ++ * 8..X: name (null-terminated UTF-8 string) ++ * alias (only included if flags & SSS_SSH_REQ_ALIAS): ++ * 0..3: alias length (unsigned int) ++ * 4..X: alias (null-terminated UTF-8 string) ++ * domain (ony included if flags & SSS_SSH_REQ_DOMAIN): ++ * 0..3: domain length (unsigned int, 0 means default domain) ++ * 4..X: domain (null-terminated UTF-8 string) + * + * SSH public key reply: + * +- * 0..3: number of results (unsigned int) +- * 4..7: reserved (unsigned int, must be 0) +- * 8..$: array of results: ++ * header: ++ * 0..3: number of results (unsigned int) ++ * 4..7: reserved (unsigned int, must be 0) ++ * results (repeated for each result): + * 0..3: flags (unsigned int, must be 0) + * 4..7: name length (unsigned int) + * 8..(X-1): name (null-terminated UTF-8 string) + * X..(X+3): key length (unsigned int) +- * (X+4)..Y: key (public key blob as defined in RFC4253, section 6.6) ++ * (X+4)..Y: key (public key data) + */ + errno_t + sss_ssh_get_ent(TALLOC_CTX *mem_ctx, + enum sss_cli_command command, + const char *name, ++ const char *domain, + const char *alias, + struct sss_ssh_ent **result) + { +@@ -102,6 +107,7 @@ sss_ssh_get_ent(TALLOC_CTX *mem_ctx, + uint32_t flags; + uint32_t name_len; + uint32_t alias_len; ++ uint32_t domain_len; + size_t req_len; + uint8_t *req = NULL; + size_t c = 0; +@@ -122,11 +128,15 @@ sss_ssh_get_ent(TALLOC_CTX *mem_ctx, + req_len = 2*sizeof(uint32_t) + name_len; + + if (alias) { +- flags |= 1; ++ flags |= SSS_SSH_REQ_ALIAS; + alias_len = strlen(alias)+1; + req_len += sizeof(uint32_t) + alias_len; + } + ++ flags |= SSS_SSH_REQ_DOMAIN; ++ domain_len = domain ? (strlen(domain)+1) : 0; ++ req_len += sizeof(uint32_t) + domain_len; ++ + req = talloc_array(tmp_ctx, uint8_t, req_len); + if (!req) { + ret = ENOMEM; +@@ -140,6 +150,10 @@ sss_ssh_get_ent(TALLOC_CTX *mem_ctx, + SAFEALIGN_SET_UINT32(req+c, alias_len, &c); + safealign_memcpy(req+c, alias, alias_len, &c); + } ++ SAFEALIGN_SET_UINT32(req+c, domain_len, &c); ++ if (domain_len > 0) { ++ safealign_memcpy(req+c, domain, domain_len, &c); ++ } + + /* send request */ + rd.data = req; +diff --git a/src/sss_client/ssh/sss_ssh_client.h b/src/sss_client/ssh/sss_ssh_client.h +index 7ffc3983e11c4cfb5fcef9ff417592f63fef3b74..5ad0643f9b821d1ceec85c477ee2037c73e73d7f 100644 +--- a/src/sss_client/ssh/sss_ssh_client.h ++++ b/src/sss_client/ssh/sss_ssh_client.h +@@ -34,6 +34,7 @@ errno_t + sss_ssh_get_ent(TALLOC_CTX *mem_ctx, + enum sss_cli_command command, + const char *name, ++ const char *domain, + const char *alias, + struct sss_ssh_ent **result); + +diff --git a/src/sss_client/ssh/sss_ssh_knownhostsproxy.c b/src/sss_client/ssh/sss_ssh_knownhostsproxy.c +index 600895d1fec81be59f0a6e0092b8a6c9f17890ec..e2202b1839214a165d5a94e3c70ce6af47cb9187 100644 +--- a/src/sss_client/ssh/sss_ssh_knownhostsproxy.c ++++ b/src/sss_client/ssh/sss_ssh_knownhostsproxy.c +@@ -282,19 +282,9 @@ int main(int argc, const char **argv) + } + + if (host) { +- /* append domain to hostname if domain is specified */ +- if (pc_domain) { +- host = talloc_asprintf(mem_ctx, "%s@%s", host, pc_domain); +- if (!host) { +- DEBUG(SSSDBG_CRIT_FAILURE, ("Not enough memory\n")); +- ret = EXIT_FAILURE; +- goto fini; +- } +- } +- + /* look up public keys */ + ret = sss_ssh_get_ent(mem_ctx, SSS_SSH_GET_HOST_PUBKEYS, +- host, pc_host, &ent); ++ host, pc_domain, pc_host, &ent); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + ("sss_ssh_get_ent() failed (%d): %s\n", ret, strerror(ret))); +diff --git a/src/util/sss_ssh.h b/src/util/sss_ssh.h +index fec7c732bdb319a906e01ec185d7ff0e7f2de0fe..1ba50a6552904096f8950b1f53563d7903eaf786 100644 +--- a/src/util/sss_ssh.h ++++ b/src/util/sss_ssh.h +@@ -21,6 +21,10 @@ + #ifndef _SSS_SSH_H_ + #define _SSS_SSH_H_ + ++#define SSS_SSH_REQ_ALIAS 0x01 ++#define SSS_SSH_REQ_DOMAIN 0x02 ++#define SSS_SSH_REQ_MASK 0x03 ++ + struct sss_ssh_pubkey { + uint8_t *data; + size_t data_len; +-- +1.8.2.1 + diff --git a/0006-SSH-Do-not-skip-domains-with-use_fully_qualified_nam.patch b/0006-SSH-Do-not-skip-domains-with-use_fully_qualified_nam.patch new file mode 100644 index 0000000..c9a81e3 --- /dev/null +++ b/0006-SSH-Do-not-skip-domains-with-use_fully_qualified_nam.patch @@ -0,0 +1,30 @@ +From d5fcc4c497eb17404812be7600fb1181a75cbfd3 Mon Sep 17 00:00:00 2001 +From: Jan Cholasta +Date: Fri, 26 Apr 2013 10:07:22 +0200 +Subject: [PATCH 6/6] SSH: Do not skip domains with use_fully_qualified_names + in host key requests + +--- + src/responder/ssh/sshsrv_cmd.c | 6 ------ + 1 file changed, 6 deletions(-) + +diff --git a/src/responder/ssh/sshsrv_cmd.c b/src/responder/ssh/sshsrv_cmd.c +index d2f889fa6ac1e414dfa9bbd943b8ef6af125ae74..bb765c62890aa326cfad87f511622167df61f0a1 100644 +--- a/src/responder/ssh/sshsrv_cmd.c ++++ b/src/responder/ssh/sshsrv_cmd.c +@@ -300,12 +300,6 @@ ssh_host_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx) + struct tevent_req *req; + struct dp_callback_ctx *cb_ctx; + +- /* if it is a domainless search, skip domains that require fully +- * qualified names instead */ +- while (cmd_ctx->domain && cmd_ctx->check_next && cmd_ctx->domain->fqnames) { +- cmd_ctx->domain = get_next_domain(cmd_ctx->domain, false); +- } +- + if (!cmd_ctx->domain) { + DEBUG(SSSDBG_OP_FAILURE, + ("No matching domain found for [%s], fail!\n", cmd_ctx->name)); +-- +1.8.2.1 + diff --git a/sssd.spec b/sssd.spec index 0b6057a..3639f3d 100644 --- a/sssd.spec +++ b/sssd.spec @@ -16,7 +16,7 @@ Name: sssd Version: 1.10.0 -Release: 3%{?dist}.beta1 +Release: 4%{?dist}.beta1 Group: Applications/System Summary: System Security Services Daemon License: GPLv3+ @@ -25,6 +25,13 @@ Source0: https://fedorahosted.org/released/sssd/%{name}-%{version}beta1.tar.gz BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) ### Patches ### +Patch0001: 0001-AD-read-flat-name-and-SID-of-the-AD-domain.patch +Patch0002: 0002-Actually-use-the-index-parameter-in-resolv_get_socka.patch +Patch0003: 0003-UTIL-Add-function-sss_names_init_from_args.patch +Patch0004: 0004-SSH-Fix-parsing-of-names-from-client-requests.patch +Patch0005: 0005-SSH-Use-separate-field-for-domain-name-in-client-req.patch +Patch0006: 0006-SSH-Do-not-skip-domains-with-use_fully_qualified_nam.patch + Patch0501: 0501-FEDORA-Switch-the-default-ccache-location.patch ### Dependencies ### @@ -38,6 +45,7 @@ Requires: libipa_hbac%{?_isa} = %{version}-%{release} Requires: libsss_idmap%{?_isa} = %{version}-%{release} Requires: python-sssdconfig = %{version}-%{release} Requires: krb5-libs%{?_isa} >= 1.10 +Requires: libini_config >= 1.0.0.1 Requires(post): systemd-units initscripts chkconfig Requires(preun): systemd-units initscripts chkconfig Requires(postun): systemd-units initscripts chkconfig @@ -595,6 +603,12 @@ fi %postun -n libsss_sudo -p /sbin/ldconfig %changelog +* Tue May 7 2013 Jakub Hrozek - 1.10.0-4.beta1 +- Explicitly Require libini_config >= 1.0.0.1 to work around a SONAME bug + in ding-libs +- Fix SSH integration with fully-qualified domains +- Add the ability to dynamically discover the NetBIOS name + * Fri May 3 2013 Jakub Hrozek - 1.10.0-3.beta1 - New upstream release 1.10 beta1 - https://fedorahosted.org/sssd/wiki/Releases/Notes-1.10.0beta1