[sssd] Update SSSD for the F19 test day

Jakub Hrozek jhrozek at fedoraproject.org
Tue May 7 13:03:15 UTC 2013


commit 14fef6c6d9a152c6d6d45fbb4ca3e5a485eb9e07
Author: Jakub Hrozek <jhrozek at redhat.com>
Date:   Tue May 7 10:44:52 2013 +0200

    Update SSSD for the F19 test day
    
    - 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

 ...D-read-flat-name-and-SID-of-the-AD-domain.patch |  702 ++++++++++++++++++++
 ...e-the-index-parameter-in-resolv_get_socka.patch |   65 ++
 ...TIL-Add-function-sss_names_init_from_args.patch |  205 ++++++
 ...Fix-parsing-of-names-from-client-requests.patch |  116 ++++
 ...arate-field-for-domain-name-in-client-req.patch |  315 +++++++++
 ...skip-domains-with-use_fully_qualified_nam.patch |   30 +
 sssd.spec                                          |   16 +-
 7 files changed, 1448 insertions(+), 1 deletions(-)
---
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 <sbose at redhat.com>
+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.
+                         </para>
++                        <para>
++                            The short domain name (also known as the NetBIOS
++                            or the flat name) is autodetected by the SSSD.
++                        </para>
+                     </listitem>
+                 </varlistentry>
+ 
+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.
+                         </para>
+                         <para>
+                             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 <sbose at redhat.com>
++
++    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 <http://www.gnu.org/licenses/>.
++*/
++
++#include "providers/ldap/sdap_async.h"
++#include "providers/ad/ad_subdomains.h"
++#include <ctype.h>
++#include <ndr.h>
++#include <ndr/ndr_nbt.h>
++
++#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 <sbose at redhat.com>
++
++    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 <http://www.gnu.org/licenses/>.
++*/
++
++#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 <jhrozek at redhat.com>
+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 <jcholast at redhat.com>
+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<name>[^@]+)@?(?P<domain>[^@]*$)");
+-        if (!ctx->re_pattern) {
++    if (!re_pattern) {
++        re_pattern = talloc_strdup(tmpctx,
++                                   "(?P<name>[^@]+)@?(?P<domain>[^@]*$)");
++        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<name>).\n", ctx->re_pattern));
++                  "the Python syntax (?P<name>).\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 <jcholast at redhat.com>
+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 at 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<name>[^@]+)@?(?P<domain>[^@]*$)",
++                                   "%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 <jcholast at redhat.com>
+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 : "<ALL>"));
++    } 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 : "<ALL>"));
++
++        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 <jcholast at redhat.com>
+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 <jhrozek at redhat.com> - 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 <jhrozek at redhat.com> - 1.10.0-3.beta1
 - New upstream release 1.10 beta1
 - https://fedorahosted.org/sssd/wiki/Releases/Notes-1.10.0beta1


More information about the scm-commits mailing list