[SSSD] [PATCHES] PAC responder and client

Sumit Bose sbose at redhat.com
Sun Jun 17 09:38:41 UTC 2012


On Thu, Jun 14, 2012 at 04:00:32PM +0200, Jan Zelený wrote:
> First I'd like to point out that I could not try full functionality since 
> there are some pieces missing on the server side. That said, I have couple 
> minor comments for some patches.

Thank you for the review. Please find new patches attached. I added two
now patches to support the new ID range objects on the IPA server. I
didn't squash them into the other patches to hopefully make review
easier.

bye
Sumit

> 
> Patch #0001:                                                                                         
> In pac_responder.m4 you are checking for the right version of kerberos. Would 
> it be possible to make the check more generic, like "if the version is 1.9 or 
> higher"? Otherwise we would need to update this every time new Kerberos is 
> out. The error message should be corrected accordingly (right now it only 
> specifies Kerberos 1.9 is necessary)                                                                            

Corrected the message. But I would like to keep the current scheme of
detecting the MIT Kerberos version, because the used plugin interface is
currently not public (maybe in 1.11) and it should be carefully checked
if everything is working with new versions of MIT Kerberos.

> 
> idmap_talloc() and idmap_free(): I just noticed that we use practically the 
> same functions in ldap provider (sdap_idmap.c). How about moving both 
> functions to util/ and utilize them both in ldap provider and pac responder?                                                                                 
> 
> 
> Patch #0002:
> Ack
> 
> 
> Patch #0003:
> Nitpick: pac_store_memberships() should be more accuratelly called 
> pac_store_membership()

renamed

> 
> > > You are not freeing pr_ctx in some error cases. The only place pr_ctx is
> > > free'd is pac_save_memberships_done(), i.e. only when everything went ok.
> > > 
> > > Please add some talloc_free() calls where necessary. My first guess is
> > > that pac_get_domains_done() and pac_add_pac_user() are the only two
> > > places where it is necessary.
> > 
> > I have added a talloc_free() at both places. But since pr_ctx is a child
> > of the client context which is freed shortly afterwards one might argue
> > if the talloc_free() is even needed in pac_save_memberships_done(). But
> > I would agree with you that it is a good idea to remove pr_ctx when it
> > is not needed anymore.
> 
> Just for the record, I don't think the cctx context is freed before responder 
> starts shutting down. That's why I asked you to free those pr_ctx contexts.
> 
> 
> Patch #0004:
> Please add build dependencies (conditionally of course) to our example spec 
> file.

added

> 
> Patch #0005:
> Ack
> 
> Patch #0006:
> Ack
> 
> Patch #0007:
> Are there any changes that should be done in spec file for this?

no, MIT Kerberos libraries are all that is needed

> 
> Nitpick: the "if" statement on line 361 of the patch is missing curly braces. 
> The same on line 425.

added braces

bye,
Sumit

> 
> Thanks
> Jan
-------------- next part --------------
From 7fe5c563d6b515cc13b9a16b823ffd72fef0da83 Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny at redhat.com>
Date: Mon, 11 Jun 2012 06:03:45 -0400
Subject: [PATCH 1/8] PAC responder: add some utility functions

---
 Makefile.am                      |    1 +
 src/responder/pac/pacsrv.h       |   17 +++++++++
 src/responder/pac/pacsrv_utils.c |   69 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 87 insertions(+), 0 deletions(-)
 create mode 100644 src/responder/pac/pacsrv_utils.c

diff --git a/Makefile.am b/Makefile.am
index fa6cf00..4899c27 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -589,6 +589,7 @@ endif
 sssd_pac_SOURCES = \
     src/responder/pac/pacsrv.c \
     src/responder/pac/pacsrv_cmd.c \
+    src/responder/pac/pacsrv_utils.c \
     $(SSSD_UTIL_OBJ) \
     $(SSSD_RESPONDER_OBJ)
 sssd_pac_CFLAGS = \
diff --git a/src/responder/pac/pacsrv.h b/src/responder/pac/pacsrv.h
index 0dfe7f9..b8a2f45 100644
--- a/src/responder/pac/pacsrv.h
+++ b/src/responder/pac/pacsrv.h
@@ -40,13 +40,30 @@
 #define PAC_PACKET_MAX_RECV_SIZE 1024
 
 struct getent_ctx;
+struct dom_sid;
 
 struct pac_ctx {
     struct resp_ctx *rctx;
+    struct sss_idmap_ctx *idmap_ctx;
+    struct dom_sid *my_dom_sid;
+    struct local_mapping_ranges *range_map;
+};
+
+struct range {
+    uint32_t min;
+    uint32_t max;
+};
+
+struct local_mapping_ranges {
+    struct range local_ids;
+    struct range primary_rids;
+    struct range secondary_rids;
 };
 
 int pac_cmd_execute(struct cli_ctx *cctx);
 
 struct sss_cmd_table *get_pac_cmds(void);
 
+errno_t local_sid_to_id(struct local_mapping_ranges *map, struct dom_sid *sid,
+                        uint32_t *id);
 #endif /* __PACSRV_H__ */
diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c
new file mode 100644
index 0000000..fc7fec7
--- /dev/null
+++ b/src/responder/pac/pacsrv_utils.c
@@ -0,0 +1,69 @@
+/*
+   SSSD
+
+   PAC Responder - utility finctions
+
+   Copyright (C) Sumit Bose <sbose at redhat.com> 2012
+
+   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 <stdbool.h>
+#include <util/data_blob.h>
+#include <gen_ndr/security.h>
+
+#include "util/util.h"
+#include "responder/pac/pacsrv.h"
+
+static errno_t get_rid(struct dom_sid *sid, uint32_t *rid)
+{
+    if (sid == NULL || sid->num_auths < 1 || rid == NULL) {
+        return EINVAL;
+    }
+
+    *rid = sid->sub_auths[sid->num_auths - 1];
+
+    return EOK;
+}
+
+errno_t local_sid_to_id(struct local_mapping_ranges *map, struct dom_sid *sid,
+                        uint32_t *id)
+{
+    int ret;
+    uint32_t rid;
+
+    if (map == NULL || sid == NULL || id == NULL) {
+        return EINVAL;
+    }
+
+    ret = get_rid(sid, &rid);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("get_rid failed.\n"));
+        return ret;
+    }
+
+    if (rid >= map->primary_rids.min && rid <= map->primary_rids.max) {
+        *id = map->local_ids.min + (rid -  map->primary_rids.min);
+    } else if (rid >= map->secondary_rids.min &&
+               rid <= map->secondary_rids.max) {
+        *id = map->local_ids.min + (rid -  map->secondary_rids.min);
+    } else {
+        return ENOENT;
+    }
+
+    if (*id < map->local_ids.min || *id > map->local_ids.max) {
+        return ERANGE;
+    }
+
+    return EOK;
+}
-- 
1.7.7.6

-------------- next part --------------
From 6faa1f1922ca466b9254c003ad87c2daa8c2b370 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Wed, 18 May 2011 13:28:17 +0200
Subject: [PATCH 2/8] PAC responder: add the core functionality

This adds support for parsing PAC and storing information contained
within. In particular the user and all his memberships are stored. In
case it is necessary, getgrgid() requests are sent to provider for group
resolution.
---
 src/responder/pac/pacsrv.c     |    2 +-
 src/responder/pac/pacsrv_cmd.c |  875 ++++++++++++++++++++++++++++++++++++++++
 src/sss_client/sss_cli.h       |    3 +
 3 files changed, 879 insertions(+), 1 deletions(-)

diff --git a/src/responder/pac/pacsrv.c b/src/responder/pac/pacsrv.c
index feee3ae..b21bb96 100644
--- a/src/responder/pac/pacsrv.c
+++ b/src/responder/pac/pacsrv.c
@@ -48,7 +48,7 @@
 struct sbus_method monitor_pac_methods[] = {
     { MON_CLI_METHOD_PING, monitor_common_pong },
     { MON_CLI_METHOD_RES_INIT, monitor_common_res_init },
-    { MON_CLI_METHOD_ROTATE, monitor_common_rotate_logs },
+    { MON_CLI_METHOD_ROTATE, responder_logrotate },
     { NULL, NULL }
 };
 
diff --git a/src/responder/pac/pacsrv_cmd.c b/src/responder/pac/pacsrv_cmd.c
index 892ef85..42ab9ec 100644
--- a/src/responder/pac/pacsrv_cmd.c
+++ b/src/responder/pac/pacsrv_cmd.c
@@ -19,12 +19,886 @@
    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 <stdbool.h>
+#include <util/data_blob.h>
+#include <ndr.h>
+#include <gen_ndr/krb5pac.h>
+#include <gen_ndr/ndr_krb5pac.h>
 
 #include "util/util.h"
+#include "util/sss_nss.h"
 #include "responder/pac/pacsrv.h"
 #include "confdb/confdb.h"
 #include "db/sysdb.h"
 
+static errno_t pac_cmd_done(struct cli_ctx *cctx, int cmd_ret)
+{
+    int ret;
+
+    if (cmd_ret == EAGAIN) {
+        /* async processing, just return here */
+        return EOK;
+    }
+
+    ret = sss_packet_new(cctx->creq, 0, sss_packet_get_cmd(cctx->creq->in),
+                         &cctx->creq->out);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sss_packet_new failed [%d][%s].\n",
+                                  ret, strerror(ret)));
+        return ret;
+    }
+
+    sss_packet_set_error(cctx->creq->out, cmd_ret);
+
+    sss_cmd_done(cctx, NULL);
+
+    return EOK;
+}
+
+static errno_t add_idmap_domain(struct sss_idmap_ctx *idmap_ctx,
+                                struct sysdb_ctx *sysdb,
+                                const char *domain_name,
+                                const char *dom_sid_str)
+{
+    struct sss_idmap_range range;
+    enum idmap_error_code err;
+
+    /* TODO: read range form sysdb if
+     * https://fedorahosted.org/freeipa/ticket/2185 is fixed */
+    range.min = 200000;
+    range.max = 400000;
+
+    err = sss_idmap_add_domain(idmap_ctx, domain_name, dom_sid_str, &range);
+    if (err != IDMAP_SUCCESS) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_add_domain failed.\n"));
+        return EFAULT;
+    }
+
+    return EOK;
+}
+
+static errno_t domsid_rid_to_uid(struct pac_ctx *pac_ctx,
+                                 struct sysdb_ctx *sysdb,
+                                 const char *domain_name,
+                                 struct dom_sid2 *domsid, uint32_t rid,
+                                 uid_t *uid)
+{
+    enum idmap_error_code err;
+    char *sid_str = NULL;
+    char *dom_sid_str = NULL;
+    uint32_t id;
+    int ret;
+
+    err = sss_idmap_smb_sid_to_sid(pac_ctx->idmap_ctx, domsid,
+                                   &dom_sid_str);
+    if (err != IDMAP_SUCCESS) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_smb_sid_to_sid failed.\n"));
+        ret = EFAULT;
+        goto done;
+    }
+
+    sid_str = talloc_asprintf(NULL, "%s-%lu", dom_sid_str, (unsigned long) rid);
+    if (sid_str == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("dom_sid_and_rid_string failed.\n"));
+        return ENOMEM;
+    }
+
+    err = sss_idmap_smb_sid_to_sid(pac_ctx->idmap_ctx, domsid, &sid_str);
+    if (err != IDMAP_SUCCESS) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_smb_sid_to_sid failed.\n"));
+        ret = EFAULT;
+        goto done;
+    }
+
+    err = sss_idmap_sid_to_unix(pac_ctx->idmap_ctx, sid_str, &id);
+    if (err == IDMAP_NO_DOMAIN) {
+        ret = add_idmap_domain(pac_ctx->idmap_ctx, sysdb, domain_name,
+                               dom_sid_str);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("add_idmap_domain failed.\n"));
+            goto done;
+        }
+
+        err = sss_idmap_sid_to_unix(pac_ctx->idmap_ctx, sid_str, &id);
+        if (err != IDMAP_SUCCESS) {
+            DEBUG(SSSDBG_FATAL_FAILURE, ("sss_idmap_sid_to_unix failed "
+                                         "even in the second attempt.\n"));
+            ret = ENOENT;
+            goto done;
+        }
+    } else if (err != IDMAP_SUCCESS && err != IDMAP_NO_DOMAIN) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_sid_to_unix failed.\n"));
+        ret = EFAULT;
+        goto done;
+    }
+
+    *uid = (uid_t) id;
+
+    ret = EOK;
+
+done:
+    talloc_free(dom_sid_str);
+    talloc_free(sid_str);
+    return ret;
+}
+
+static errno_t get_my_domain_sid(struct pac_ctx *pac_ctx,
+                                 struct sss_domain_info *dom,
+                                 struct dom_sid **_sid)
+{
+    struct sysdb_ctx *sysdb;
+    int ret;
+    struct ldb_dn *basedn;
+    const char *attrs[] = {SYSDB_SUBDOMAIN_ID,
+                           NULL};
+    size_t msgs_count;
+    const char *sid_str;
+    struct ldb_message **msgs;
+    TALLOC_CTX *tmp_ctx = NULL;
+    struct dom_sid *sid = NULL;
+    char *dom_name;
+    enum idmap_error_code err;
+
+    if (pac_ctx->my_dom_sid == NULL) {
+        if (dom->parent != NULL) {
+            sysdb = dom->parent->sysdb;
+            dom_name = dom->parent->name;
+        } else {
+            sysdb = dom->sysdb;
+            dom_name = dom->name;
+        }
+
+        if (sysdb == NULL) {
+            DEBUG(SSSDBG_FATAL_FAILURE, ("Missing sysdb context.\n"));
+            ret = EINVAL;
+            goto done;
+        }
+
+        tmp_ctx = talloc_new(NULL);
+        if (tmp_ctx == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("talloc_new failed.\n"));
+            ret = ENOMEM;
+            goto done;
+        }
+
+        basedn = sysdb_domain_dn(sysdb, tmp_ctx, dom_name);
+        if (basedn == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = sysdb_search_entry(tmp_ctx, sysdb, basedn, LDB_SCOPE_BASE, NULL,
+                                 attrs, &msgs_count, &msgs);
+        if (ret != LDB_SUCCESS) {
+            ret = EIO;
+            goto done;
+        }
+
+        if (msgs_count != 1) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Base search returned [%d] results, "
+                                     "expected 1.\n", msgs_count));
+            ret = EINVAL;
+            goto done;
+        }
+
+        sid_str = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUBDOMAIN_ID, NULL);
+        if (sid_str == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("SID of my domain is not available.\n"));
+            ret = EINVAL;
+            goto done;
+        }
+
+        err = sss_idmap_sid_to_smb_sid(pac_ctx->idmap_ctx, sid_str, &sid);
+        if (err != IDMAP_SUCCESS) {
+            DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_sid_to_smb_sid failed.\n"));
+            ret = EFAULT;
+            goto done;
+        }
+
+        pac_ctx->my_dom_sid = talloc_memdup(pac_ctx, sid,
+                                            sizeof(struct dom_sid));
+        if (pac_ctx->my_dom_sid == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("talloc_memdup failed.\n"));
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    *_sid = pac_ctx->my_dom_sid;
+
+    ret = EOK;
+
+done:
+    talloc_free(sid);
+    talloc_free(tmp_ctx);
+
+    return ret;
+}
+
+static bool dom_sid_in_domain(const struct dom_sid *domain_sid,
+                              const struct dom_sid *sid)
+{
+    size_t c;
+
+    if (!domain_sid || !sid) {
+        return false;
+    }
+
+    if (domain_sid->sid_rev_num != sid->sid_rev_num) {
+        return false;
+    }
+
+    for (c = 0; c < 6; c++) {
+        if (domain_sid->id_auth[c] != sid->id_auth[c]) {
+            return false;
+        }
+    }
+
+    if (domain_sid->num_auths > sid->num_auths) {
+        return false;
+    }
+
+    for (c = 0; c < domain_sid->num_auths-1; c++) {
+        if (domain_sid->sub_auths[c] != sid->sub_auths[c]) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static errno_t get_gids_from_pac(TALLOC_CTX *mem_ctx,
+                                 struct local_mapping_ranges *range_map,
+                                 struct dom_sid *domain_sid,
+                                 struct PAC_LOGON_INFO *logon_info,
+                                 size_t *_gid_count, gid_t **_gids)
+{
+    int ret;
+    size_t g = 0;
+    size_t s;
+    struct netr_SamInfo3 *info3;
+    gid_t *gids = NULL;
+
+    info3 = &logon_info->info3;
+
+    if (info3->sidcount == 0) {
+        DEBUG(SSSDBG_TRACE_ALL, ("No extra groups found.\n"));
+        ret = EOK;
+        goto done;
+    }
+
+    gids = talloc_array(mem_ctx, gid_t, info3->sidcount);
+    if (gids == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("talloc_array failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for(s = 0; s < info3->sidcount; s++) {
+        if (dom_sid_in_domain(domain_sid, info3->sids[s].sid)) {
+            ret = local_sid_to_id(range_map, info3->sids[s].sid, &gids[g]);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, ("get_rid failed.\n"));
+                goto done;
+            }
+            DEBUG(SSSDBG_TRACE_ALL, ("Found extra group "
+                                     "with gid [%d].\n", gids[g]));
+            g++;
+        }
+    }
+
+    ret = EOK;
+
+done:
+    if (ret == EOK) {
+        *_gid_count = g;
+        *_gids = gids;
+    } else {
+        talloc_free(gids);
+    }
+
+    return ret;
+}
+
+static errno_t get_data_from_pac(TALLOC_CTX *mem_ctx,
+                                 uint8_t *pac_blob, size_t pac_len,
+                                 struct PAC_LOGON_INFO **_logon_info)
+{
+    DATA_BLOB blob;
+    struct ndr_pull *ndr_pull;
+    struct PAC_DATA *pac_data;
+    enum ndr_err_code ndr_err;
+    size_t c;
+    int ret;
+
+    blob.data = pac_blob;
+    blob.length = pac_len;
+
+    ndr_pull = ndr_pull_init_blob(&blob, mem_ctx);
+    if (ndr_pull == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("ndr_pull_init_blob failed.\n"));
+        return ENOMEM;
+    }
+    ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC; /* FIXME: is this really needed ? */
+
+    pac_data = talloc_zero(mem_ctx, struct PAC_DATA);
+    if (pac_data == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed.\n"));
+        return ENOMEM;
+    }
+
+    ndr_err = ndr_pull_PAC_DATA(ndr_pull, NDR_SCALARS|NDR_BUFFERS, pac_data);
+    if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+        DEBUG(SSSDBG_OP_FAILURE, ("ndr_pull_PAC_DATA failed [%d]\n", ndr_err));
+        return EBADMSG;
+    }
+
+    for(c = 0; c < pac_data->num_buffers; c++) {
+        if (pac_data->buffers[c].type == PAC_TYPE_LOGON_INFO) {
+            *_logon_info = pac_data->buffers[c].info->logon_info.info;
+
+            return EOK;
+        }
+    }
+
+    ret = EINVAL;
+
+    talloc_free(pac_data);
+    return ret;
+}
+
+static errno_t get_pwd_from_pac(TALLOC_CTX *mem_ctx,
+                                struct pac_ctx *pac_ctx,
+                                struct sss_domain_info *dom,
+                                struct PAC_LOGON_INFO *logon_info,
+                                struct passwd **_pwd)
+{
+    struct passwd *pwd = NULL;
+    struct netr_SamBaseInfo *base_info;
+    int ret;
+
+    pwd = talloc_zero(mem_ctx, struct passwd);
+    if (pwd == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed.\n"));
+        return ENOMEM;
+    }
+
+    base_info = &logon_info->info3.base;
+
+    if (base_info->account_name.size != 0) {
+        pwd->pw_name = talloc_strdup(pwd,
+                                     base_info->account_name.string);
+        if (pwd->pw_name == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+            ret = ENOMEM;
+            goto done;
+        }
+    } else {
+        DEBUG(SSSDBG_OP_FAILURE, ("Missing account name in PAC.\n"));
+        ret = EINVAL;
+        goto done;
+    }
+
+    if (base_info->rid > 0) {
+        ret = domsid_rid_to_uid(pac_ctx, dom->sysdb, dom->name,
+                                base_info->domain_sid,
+                                base_info->rid, &pwd->pw_uid);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("domsid_rid_to_uid failed.\n"));
+            goto done;
+        }
+    } else {
+        DEBUG(SSSDBG_OP_FAILURE, ("Missing user RID in PAC.\n"));
+        ret = EINVAL;
+        goto done;
+    }
+
+    pwd->pw_gid = 0; /* We use MPGs for sub-domains */
+
+    if (base_info->full_name.size != 0) {
+        pwd->pw_gecos = talloc_strdup(pwd, base_info->full_name.string);
+    } else {
+        DEBUG(SSSDBG_OP_FAILURE, ("Missing full name in PAC "
+                                  "using account name for gecos.\n"));
+        pwd->pw_gecos = talloc_strdup(pwd,
+                                      base_info->account_name.string);
+    }
+    if (pwd->pw_gecos == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+
+    if (dom->subdomain_homedir) {
+        pwd->pw_dir = expand_homedir_template(pwd, dom->subdomain_homedir,
+                                              pwd->pw_name, pwd->pw_uid,
+                                              dom->name);
+        if (pwd->pw_dir == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    pwd->pw_shell = NULL; /* Using default */
+
+    *_pwd = pwd;
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        talloc_free(pwd);
+    }
+
+    return ret;
+}
+
+struct pac_req_ctx {
+    struct cli_ctx *cctx;
+    struct pac_ctx *pac_ctx;
+    const char *domain_name;
+    const char *user_name;
+    struct sss_domain_info *dom;
+
+    struct PAC_LOGON_INFO *logon_info;
+    struct dom_sid2 *domain_sid;
+
+    size_t gid_count;
+    gid_t *gids;
+};
+
+static errno_t pac_add_user_next(struct pac_req_ctx *pr_ctx);
+static void pac_get_domains_done(struct tevent_req *req);
+static errno_t save_pac_user(struct pac_req_ctx *pr_ctx);
+static void pac_get_group_done(struct tevent_req *subreq);
+static errno_t pac_save_memberships_next(struct tevent_req *req);
+static errno_t pac_store_membership(struct pac_req_ctx *pr_ctx,
+                                     struct sysdb_ctx *group_sysdb,
+                                     struct ldb_dn *user_dn,
+                                     int gid_iter);
+struct tevent_req *pac_save_memberships_send(struct pac_req_ctx *pr_ctx);
+static void pac_save_memberships_done(struct tevent_req *req);
+
+
+static errno_t pac_add_pac_user(struct cli_ctx *cctx)
+{
+    int ret;
+    uint8_t *body;
+    size_t blen;
+    struct pac_req_ctx *pr_ctx;
+    struct tevent_req *req;
+
+    sss_packet_get_body(cctx->creq->in, &body, &blen);
+
+    pr_ctx = talloc_zero(cctx, struct pac_req_ctx);
+    if (pr_ctx == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("talloc_new failed.\n"));
+        return ENOMEM;
+    }
+
+    pr_ctx->cctx = cctx;
+
+    pr_ctx->pac_ctx = talloc_get_type(cctx->rctx->pvt_ctx,  struct pac_ctx);
+    if (pr_ctx->pac_ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("Cannot find pac responder context.\n"));
+        return EINVAL;
+    }
+
+    ret = get_data_from_pac(pr_ctx, body, blen,
+                            &pr_ctx->logon_info);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("get_data_from_pac failed.\n"));
+        goto done;
+    }
+
+    pr_ctx->domain_name = pr_ctx->logon_info->info3.base.logon_domain.string;
+    if (pr_ctx->domain_name == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("No domain name in PAC"));
+        ret = EINVAL;
+        goto done;
+    }
+
+    pr_ctx->user_name = pr_ctx->logon_info->info3.base.account_name.string;
+    if (pr_ctx->user_name == NULL) {
+        ret = EINVAL;
+        DEBUG(SSSDBG_FATAL_FAILURE, ("Missing account name in PAC.\n"));
+        goto done;
+    }
+
+
+    pr_ctx->dom = responder_get_domain(pr_ctx, cctx->rctx, pr_ctx->domain_name);
+    if (pr_ctx->dom == NULL) {
+        req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true,
+                                      pr_ctx->domain_name);
+        if (req == NULL) {
+            ret = ENOMEM;
+        } else {
+            tevent_req_set_callback(req, pac_get_domains_done, pr_ctx);
+            ret = EAGAIN;
+        }
+        goto done;
+    }
+
+    ret = pac_add_user_next(pr_ctx);
+
+done:
+    if (ret != EAGAIN) {
+        talloc_free(pr_ctx);
+    }
+    return pac_cmd_done(cctx, ret);
+}
+
+static void pac_get_domains_done(struct tevent_req *req)
+{
+    struct pac_req_ctx *pr_ctx = tevent_req_callback_data(req,
+                                                          struct pac_req_ctx);
+    struct cli_ctx *cctx = pr_ctx->cctx;
+    int ret;
+
+    ret = sss_dp_get_domains_recv(req);
+    talloc_free(req);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    pr_ctx->dom = responder_get_domain(pr_ctx, cctx->rctx, pr_ctx->domain_name);
+    if (pr_ctx->dom == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Corresponding domain [%s] has not been "
+                                  "found\n", pr_ctx->domain_name));
+        ret = ENOENT;
+        goto done;
+    }
+
+    ret = pac_add_user_next(pr_ctx);
+
+done:
+    if (ret != EAGAIN) {
+        talloc_free(pr_ctx);
+    }
+    pac_cmd_done(cctx, ret);
+}
+
+static errno_t pac_add_user_next(struct pac_req_ctx *pr_ctx)
+{
+    int ret;
+    struct tevent_req *req;
+    struct dom_sid *my_dom_sid;
+
+    ret = save_pac_user(pr_ctx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("save_pac_user failed.\n"));
+        goto done;
+    }
+
+    ret = get_my_domain_sid(pr_ctx->pac_ctx, pr_ctx->dom, &my_dom_sid);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("get_my_domain_sid failed.\n"));
+        goto done;
+    }
+
+    ret = get_gids_from_pac(pr_ctx, pr_ctx->pac_ctx->range_map, my_dom_sid,
+                            pr_ctx->logon_info, &pr_ctx->gid_count,
+                            &pr_ctx->gids);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("get_gids_from_pac failed.\n"));
+        goto done;
+    }
+
+    req = pac_save_memberships_send(pr_ctx);
+    if (req == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    tevent_req_set_callback(req, pac_save_memberships_done, pr_ctx);
+
+    ret = EAGAIN;
+
+done:
+    return ret;
+}
+
+static errno_t save_pac_user(struct pac_req_ctx *pr_ctx)
+{
+    struct sysdb_ctx *sysdb;
+    int ret;
+    const char *attrs[] = {SYSDB_NAME, SYSDB_UIDNUM, SYSDB_GIDNUM, NULL};
+    struct ldb_message *msg;
+    struct passwd *pwd = NULL;
+    TALLOC_CTX *tmp_ctx = NULL;
+
+    sysdb = pr_ctx->dom->sysdb;
+    if (sysdb == NULL) {
+        ret = EINVAL;
+        DEBUG(SSSDBG_FATAL_FAILURE, ("Fatal: Sysdb CTX not found for this domain!\n"));
+        goto done;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        DEBUG(SSSDBG_OP_FAILURE, ("talloc_new failed.\n"));
+        goto done;
+    }
+
+    ret = sysdb_search_user_by_name(tmp_ctx, sysdb, pr_ctx->user_name, attrs,
+                                    &msg);
+    if (ret == EOK) {
+        /* TODO: check id uid and gid are equal. */
+    } else if (ret == ENOENT) {
+        ret = get_pwd_from_pac(tmp_ctx, pr_ctx->pac_ctx, pr_ctx->dom,
+                               pr_ctx->logon_info, &pwd);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("get_pwd_from_pac failed.\n"));
+            goto done;
+        }
+
+        ret = sysdb_store_user(sysdb, pwd->pw_name, NULL,
+                               pwd->pw_uid, pwd->pw_gid, pwd->pw_gecos,
+                               pwd->pw_dir,
+                               pwd->pw_shell, NULL, NULL,
+                               pr_ctx->dom->user_timeout, 0);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_store_user failed [%d][%s].\n",
+                                      ret, strerror(ret)));
+            goto done;
+        }
+    } else {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_search_user_by_name failed.\n"));
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+struct pac_save_memberships_state {
+    int gid_iter;
+    struct ldb_dn *user_dn;
+
+    struct pac_req_ctx *pr_ctx;
+    struct sss_domain_info *group_dom;
+};
+
+struct tevent_req *pac_save_memberships_send(struct pac_req_ctx *pr_ctx)
+{
+    struct pac_save_memberships_state *state;
+    struct sss_domain_info *dom = pr_ctx->dom;
+    struct tevent_req *req;
+    errno_t ret;
+
+    req = tevent_req_create(pr_ctx, &state, struct pac_save_memberships_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    state->gid_iter = 0;
+    state->user_dn = sysdb_user_dn(dom->sysdb, state, dom->name,
+                                   pr_ctx->user_name);
+    if (state->user_dn == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+    state->pr_ctx = pr_ctx;
+
+    /* Remote users are members of local groups */
+    if (pr_ctx->dom->parent != NULL) {
+        state->group_dom = pr_ctx->dom->parent;
+    } else {
+        state->group_dom = pr_ctx->dom;
+    }
+
+    ret = pac_save_memberships_next(req);
+    if (ret == EOK) {
+        tevent_req_done(req);
+        tevent_req_post(req, pr_ctx->cctx->ev);
+    }
+
+done:
+    if (ret != EOK && ret != EAGAIN) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, pr_ctx->cctx->ev);
+    }
+
+    return req;
+}
+
+static errno_t pac_save_memberships_next(struct tevent_req *req)
+{
+    errno_t ret;
+    uint32_t gid;
+    struct tevent_req *subreq;
+    struct pac_save_memberships_state *state;
+    struct pac_req_ctx *pr_ctx;
+
+    state = tevent_req_data(req, struct pac_save_memberships_state);
+    pr_ctx = state->pr_ctx;
+
+    while (state->gid_iter < pr_ctx->gid_count) {
+        gid = pr_ctx->gids[state->gid_iter];
+
+        ret = pac_store_membership(state->pr_ctx, state->group_dom->sysdb,
+                                    state->user_dn, state->gid_iter);
+        if (ret == EOK) {
+            state->gid_iter++;
+            continue;
+        } else if (ret == ENOENT) {
+            subreq = sss_dp_get_account_send(state, pr_ctx->cctx->rctx,
+                                             state->group_dom, true,
+                                             SSS_DP_GROUP, NULL,
+                                             gid, NULL);
+            if (subreq == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+            tevent_req_set_callback(subreq, pac_get_group_done, req);
+
+            return EAGAIN;
+        } else  {
+            goto done;
+        }
+    }
+
+    ret = EOK;
+done:
+    return ret;
+}
+
+static void pac_get_group_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct pac_save_memberships_state *state;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct pac_save_memberships_state);
+
+    errno_t ret;
+    dbus_uint16_t err_maj;
+    dbus_uint32_t err_min;
+    char *err_msg;
+
+    ret = sss_dp_get_account_recv(req, subreq,
+                                  &err_maj, &err_min,
+                                  &err_msg);
+    talloc_zfree(subreq);
+    talloc_zfree(err_msg);
+    if (ret != EOK) {
+        goto error;
+    }
+
+    ret = pac_store_membership(state->pr_ctx, state->group_dom->sysdb,
+                                state->user_dn, state->gid_iter);
+    if (ret != EOK) {
+        goto error;
+    }
+    state->gid_iter++;
+
+    ret = pac_save_memberships_next(req);
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else if (ret != EAGAIN) {
+        goto error;
+    }
+
+    return;
+
+error:
+    tevent_req_error(req, ret);
+}
+
+static errno_t
+pac_store_membership(struct pac_req_ctx *pr_ctx,
+                      struct sysdb_ctx *group_sysdb,
+                      struct ldb_dn *user_dn,
+                      int gid_iter)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char *group_name;
+    struct sysdb_attrs *group_attrs;
+    struct ldb_message *group;
+    uint32_t gid;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    gid = pr_ctx->gids[gid_iter];
+
+    ret = sysdb_search_group_by_gid(tmp_ctx, group_sysdb,
+                                    gid, NULL, &group);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    group_name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL);
+    if (group_name == NULL) {
+        ret = EIO;
+        goto done;
+    }
+
+    group_attrs = talloc_zero(tmp_ctx, struct sysdb_attrs);
+    if (group_attrs == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+    group_attrs->num = 1;
+    group_attrs->a = ldb_msg_find_element(group, SYSDB_MEMBER);
+    if (group_attrs->a == NULL) {
+        group_attrs->a = talloc_zero(group_attrs, struct ldb_message_element);
+        if (group_attrs->a == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        group_attrs->a[0].name = talloc_strdup(group_attrs->a, SYSDB_MEMBER);
+        if (group_attrs->a[0].name == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    ret = sysdb_attrs_add_string(group_attrs, SYSDB_MEMBER,
+                                 ldb_dn_get_linearized(user_dn));
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = sysdb_store_group(group_sysdb, group_name, gid,
+                            group_attrs, pr_ctx->dom->group_timeout, 0);
+    if (ret != EOK) {
+        goto done;
+    }
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static errno_t pac_save_memberships_recv(struct tevent_req *subreq)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(subreq);
+
+    return EOK;
+}
+
+static void pac_save_memberships_done(struct tevent_req *req)
+{
+    struct pac_req_ctx *pr_ctx = tevent_req_callback_data(req, struct pac_req_ctx);
+    struct cli_ctx *cctx = pr_ctx->cctx;
+    errno_t ret;
+
+    ret = pac_save_memberships_recv(req);
+    talloc_zfree(req);
+
+    talloc_free(pr_ctx);
+    pac_cmd_done(cctx, ret);
+}
+
 struct cli_protocol_version *register_cli_protocol_version(void)
 {
     static struct cli_protocol_version pac_cli_protocol_version[] = {
@@ -37,6 +911,7 @@ struct cli_protocol_version *register_cli_protocol_version(void)
 
 static struct sss_cmd_table pac_cmds[] = {
     {SSS_GET_VERSION, sss_cmd_get_version},
+    {SSS_PAC_ADD_PAC_USER, pac_add_pac_user},
     {SSS_CLI_NULL, NULL}
 };
 
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index 647d233..30018d8 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -175,6 +175,9 @@ enum sss_cli_command {
     SSS_SSH_GET_USER_PUBKEYS = 0x00E1,
     SSS_SSH_GET_HOST_PUBKEYS = 0x00E2,
 
+/* PAC responder calls */
+    SSS_PAC_ADD_PAC_USER     = 0x00E9,
+
 /* PAM related calls */
     SSS_PAM_AUTHENTICATE     = 0x00F1, /**< see pam_sm_authenticate(3) for
                                         * details.
-- 
1.7.7.6

-------------- next part --------------
From ed18a18a46b62c8558a4b7d36da5f83558123782 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 24 May 2011 11:57:09 +0200
Subject: [PATCH 3/8] PAC responder: support in spec file

---
 contrib/sssd.spec.in |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index e087e0f..adb545a 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -107,6 +107,9 @@ BuildRequires: libnl-devel
 BuildRequires: gettext-devel
 BuildRequires: pkgconfig
 BuildRequires: findutils
+%if (0%{?enable_experimental} == 1)
+BuildRequires: samba4-devel
+%endif
 
 %if 0%{?is_rhel5} > 0
 BuildRequires: glib2-devel
@@ -322,6 +325,7 @@ rm -rf $RPM_BUILD_ROOT
 %{_libexecdir}/%{servicename}/sssd_autofs
 %{_libexecdir}/%{servicename}/sssd_ssh
 %{_libexecdir}/%{servicename}/sssd_sudo
+%{_libexecdir}/%{servicename}/sssd_pac
 %endif
 
 %{_libdir}/%{name}/libsss_ipa.so
@@ -368,6 +372,9 @@ rm -rf $RPM_BUILD_ROOT
 /%{_lib}/libnss_sss.so.2
 /%{_lib}/security/pam_sss.so
 %{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.so
+%if (0%{?enable_experimental} == 1)
+%{_libdir}/krb5/plugins/authdata/sssd_pac_plugin.so
+%endif
 %{_mandir}/man8/pam_sss.8*
 %{_mandir}/man8/sssd_krb5_locator_plugin.8*
 
-- 
1.7.7.6

-------------- next part --------------
From e39d4f573dcfca4783dd41c0276178c4a178eca5 Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny at redhat.com>
Date: Mon, 11 Jun 2012 06:00:22 -0400
Subject: [PATCH 4/8] PAC responder: test suite

---
 Makefile.am                     |   15 +++++-
 src/tests/pac_responder-tests.c |  106 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 120 insertions(+), 1 deletions(-)
 create mode 100644 src/tests/pac_responder-tests.c

diff --git a/Makefile.am b/Makefile.am
index 4899c27..cdb03be 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -119,7 +119,8 @@ if HAVE_CHECK
         util-tests \
         debug-tests \
         ipa_hbac-tests \
-        sss_idmap-tests
+        sss_idmap-tests \
+        pac_responder-tests
 endif
 
 check_PROGRAMS = \
@@ -985,6 +986,18 @@ sss_idmap_tests_LDADD = \
     libsss_test_common.la \
     libsss_idmap.la
 
+pac_responder_tests_SOURCES = \
+    src/tests/pac_responder-tests.c \
+    src/responder/pac/pacsrv_utils.c
+pac_responder_tests_CFLAGS = \
+    $(AM_CFLAGS) \
+    $(NDR_KRB5PAC_CFLAGS) \
+    $(CHECK_CFLAGS)
+pac_responder_tests_LDADD = \
+    $(CHECK_LIBS) \
+    $(TALLOC_LIBS) \
+    libsss_debug.la \
+    libsss_test_common.la
 endif
 
 stress_tests_SOURCES = \
diff --git a/src/tests/pac_responder-tests.c b/src/tests/pac_responder-tests.c
new file mode 100644
index 0000000..720793c
--- /dev/null
+++ b/src/tests/pac_responder-tests.c
@@ -0,0 +1,106 @@
+/*
+    SSSD - Test for PAC reponder functions
+
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2012 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 <check.h>
+
+#include <stdbool.h>
+#include <util/data_blob.h>
+#include <gen_ndr/security.h>
+
+#include "tests/common.h"
+#include "responder/pac/pacsrv.h"
+
+struct dom_sid test_smb_sid = {1, 5, {0, 0, 0, 0, 0, 5},
+                               {21, 2127521184, 1604012920, 1887927527, 1123,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+const uint32_t test_id = 1200123;
+
+struct dom_sid test_smb_sid_2nd = {1, 5, {0, 0, 0, 0, 0, 5},
+                               {21, 2127521184, 1604012920, 1887927527, 201456,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+const uint32_t test_id_2nd = 1200456;
+
+struct local_mapping_ranges test_map = {{1200000, 1399999},
+                                        {1000, 200999},
+                                        {201000, 400999}};
+
+
+START_TEST(pac_test_local_sid_to_id)
+{
+    int ret;
+    uint32_t id;
+
+    ret = local_sid_to_id(&test_map, &test_smb_sid, &id);
+    fail_unless(ret == EOK,
+                "Failed to convert local sid to id.");
+    fail_unless(id == test_id, "Wrong id returne, expected [%d], got [%d].",
+                               test_id, id);
+}
+END_TEST
+
+START_TEST(pac_test_seondary_local_sid_to_id)
+{
+    int ret;
+    uint32_t id;
+
+    ret = local_sid_to_id(&test_map, &test_smb_sid_2nd, &id);
+    fail_unless(ret == EOK,
+                "Failed to convert local sid to id.");
+    fail_unless(id == test_id_2nd, "Wrong id returne, expected [%d], got [%d].",
+                               test_id_2nd, id);
+}
+END_TEST
+
+
+Suite *idmap_test_suite (void)
+{
+    Suite *s = suite_create ("PAC responder");
+
+    TCase *tc_pac = tcase_create("PAC responder tests");
+    /*tcase_add_checked_fixture(tc_init,
+                              leak_check_setup,
+                              leak_check_teardown);*/
+
+    tcase_add_test(tc_pac, pac_test_local_sid_to_id);
+    tcase_add_test(tc_pac, pac_test_seondary_local_sid_to_id);
+
+    suite_add_tcase(s, tc_pac);
+
+    return s;
+}
+
+int main(int argc, const char *argv[])
+{
+    int number_failed;
+
+    tests_set_cwd();
+
+    Suite *s = idmap_test_suite();
+    SRunner *sr = srunner_create(s);
+
+    /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */
+    srunner_run_all(sr, CK_ENV);
+    number_failed = srunner_ntests_failed (sr);
+    srunner_free (sr);
+
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
-- 
1.7.7.6

-------------- next part --------------
From 775c6ced31e1069529fcc36fb29c8b749fa0d9c8 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Fri, 13 May 2011 18:41:16 +0200
Subject: [PATCH 5/8] PAC client: add basic support in common client code

---
 src/sss_client/common.c  |   33 +++++++++++++++++++++++++++++++++
 src/sss_client/sss_cli.h |    5 +++++
 2 files changed, 38 insertions(+), 0 deletions(-)

diff --git a/src/sss_client/common.c b/src/sss_client/common.c
index 28adb44..83c95b2 100644
--- a/src/sss_client/common.c
+++ b/src/sss_client/common.c
@@ -383,6 +383,8 @@ static bool sss_cli_check_version(const char *socket_name)
         expected_version = SSS_AUTOFS_PROTOCOL_VERSION;
     } else if (strcmp(socket_name, SSS_SSH_SOCKET_NAME) == 0) {
         expected_version = SSS_SSH_PROTOCOL_VERSION;
+    } else if (strcmp(socket_name, SSS_PAC_SOCKET_NAME) == 0) {
+        expected_version = SSS_PAC_PROTOCOL_VERSION;
     } else {
         return false;
     }
@@ -715,6 +717,37 @@ enum nss_status sss_nss_make_request(enum sss_cli_command cmd,
     }
 }
 
+int sss_pac_make_request(enum sss_cli_command cmd,
+                         struct sss_cli_req_data *rd,
+                         uint8_t **repbuf, size_t *replen,
+                         int *errnop)
+{
+    enum sss_status ret;
+    char *envval;
+
+    /* avoid looping in the nss daemon */
+    envval = getenv("_SSS_LOOPS");
+    if (envval && strcmp(envval, "NO") == 0) {
+        return NSS_STATUS_NOTFOUND;
+    }
+
+    ret = sss_cli_check_socket(errnop, SSS_PAC_SOCKET_NAME);
+    if (ret != SSS_STATUS_SUCCESS) {
+        return NSS_STATUS_UNAVAIL;
+    }
+
+    ret = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
+    switch (ret) {
+    case SSS_STATUS_TRYAGAIN:
+        return NSS_STATUS_TRYAGAIN;
+    case SSS_STATUS_SUCCESS:
+        return NSS_STATUS_SUCCESS;
+    case SSS_STATUS_UNAVAIL:
+    default:
+        return NSS_STATUS_UNAVAIL;
+    }
+}
+
 errno_t check_server_cred(int sockfd)
 {
 #ifdef HAVE_UCRED
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index 30018d8..70e4f1f 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -47,6 +47,7 @@ typedef int errno_t;
 #define SSS_SUDO_PROTOCOL_VERSION 0
 #define SSS_AUTOFS_PROTOCOL_VERSION 1
 #define SSS_SSH_PROTOCOL_VERSION 0
+#define SSS_PAC_PROTOCOL_VERSION 1
 
 #ifdef LOGIN_NAME_MAX
 #define SSS_NAME_MAX LOGIN_NAME_MAX
@@ -483,6 +484,10 @@ int sss_pam_make_request(enum sss_cli_command cmd,
                                      struct sss_cli_req_data *rd,
                                      uint8_t **repbuf, size_t *replen,
                                      int *errnop);
+int sss_pac_make_request(enum sss_cli_command cmd,
+                         struct sss_cli_req_data *rd,
+                         uint8_t **repbuf, size_t *replen,
+                         int *errnop);
 
 int sss_sudo_make_request(enum sss_cli_command cmd,
                           struct sss_cli_req_data *rd,
-- 
1.7.7.6

-------------- next part --------------
From 14894f087625f44e584678b07d6fd575d7a8bbe0 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 24 May 2011 11:12:28 +0200
Subject: [PATCH 6/8] PAC client: add krb5 authdata plugin

---
 Makefile.am                        |   21 +++
 configure.ac                       |    1 +
 src/conf_macros.m4                 |   14 ++
 src/sss_client/krb5_authdata_int.h |  185 +++++++++++++++++++++++
 src/sss_client/sssd_pac.c          |  282 ++++++++++++++++++++++++++++++++++++
 5 files changed, 503 insertions(+), 0 deletions(-)
 create mode 100644 src/sss_client/krb5_authdata_int.h
 create mode 100644 src/sss_client/sssd_pac.c

diff --git a/Makefile.am b/Makefile.am
index cdb03be..6cfeb34 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,6 +23,9 @@ ldblibdir = @ldblibdir@
 if BUILD_KRB5_LOCATOR_PLUGIN
 krb5plugindir = @krb5pluginpath@
 endif
+if BUILD_PAC_RESPONDER
+krb5authdata_plugindir = @krb5authdatapluginpath@
+endif
 sssdconfdir = $(sysconfdir)/sssd
 sssddatadir = $(datadir)/sssd
 sssdapiplugindir = $(sssddatadir)/sssd.api.d
@@ -153,6 +156,11 @@ krb5plugin_LTLIBRARIES = \
     sssd_krb5_locator_plugin.la
 endif
 
+if BUILD_PAC_RESPONDER
+krb5authdata_plugin_LTLIBRARIES = \
+    sssd_pac_plugin.la
+endif
+
 noinst_LTLIBRARIES = \
     libsss_crypt.la
 
@@ -1366,6 +1374,19 @@ sssd_krb5_locator_plugin_la_LDFLAGS = \
     -module
 endif
 
+sssd_pac_plugin_la_SOURCES = \
+    src/sss_client/sssd_pac.c \
+    src/sss_client/common.c \
+    src/sss_client/sss_cli.h \
+    src/sss_client/krb5_authdata_int.h
+sssd_pac_plugin_la_CFLAGS = \
+    $(AM_CFLAGS) \
+    $(KRB5_CFLAGS)
+sssd_pac_plugin_la_LDFLAGS = \
+    -lkrb5 \
+    -avoid-version \
+    -module
+
 if BUILD_PYTHON_BINDINGS
 pysss_la_SOURCES = \
     $(SSSD_TOOLS_OBJ) \
diff --git a/configure.ac b/configure.ac
index 0dee070..102ed7b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -88,6 +88,7 @@ WITH_MANPAGES
 WITH_XML_CATALOG
 WITH_KRB5_PLUGIN_PATH
 WITH_KRB5_RCACHE_DIR
+WITH_KRB5AUTHDATA_PLUGIN_PATH
 WITH_PYTHON_BINDINGS
 WITH_SELINUX
 WITH_NSCD
diff --git a/src/conf_macros.m4 b/src/conf_macros.m4
index 8c13e5d..26adb6a 100644
--- a/src/conf_macros.m4
+++ b/src/conf_macros.m4
@@ -247,6 +247,20 @@ AC_DEFUN([WITH_KRB5_RCACHE_DIR],
     AC_DEFINE_UNQUOTED(KRB5_RCACHE_DIR, "$krb5rcachedir", [Directory used for storing Kerberos replay caches])
   ])
 
+AC_DEFUN([WITH_KRB5AUTHDATA_PLUGIN_PATH],
+  [ AC_ARG_WITH([krb5authdata-plugin-path],
+                [AC_HELP_STRING([--with-krb5authdata-plugin-path=PATH],
+                                [Path to kerberos authdata plugin store [/usr/lib/krb5/plugins/authdata]]
+                               )
+                ]
+               )
+    krb5authdatapluginpath="${libdir}/krb5/plugins/authdata"
+    if test x"$with_krb5authdata_plugin_path" != x; then
+        krb5authdatapluginpath=$with_krb5authdata_plugin_path
+    fi
+    AC_SUBST(krb5authdatapluginpath)
+  ])
+
 AC_DEFUN([WITH_PYTHON_BINDINGS],
   [ AC_ARG_WITH([python-bindings],
                 [AC_HELP_STRING([--with-python-bindings],
diff --git a/src/sss_client/krb5_authdata_int.h b/src/sss_client/krb5_authdata_int.h
new file mode 100644
index 0000000..5e0cf5e
--- /dev/null
+++ b/src/sss_client/krb5_authdata_int.h
@@ -0,0 +1,185 @@
+/*
+    SSSD - MIT Kerberos authdata plugin
+
+    This file contains definitions and declarations to build authdata plugins
+    for MIT Kerberos outside of the MIT Kerberos source tree.
+*/
+
+#ifndef _KRB5_AUTHDATA_INT_H
+#define _KRB5_AUTHDATA_INT_H
+
+krb5_error_code KRB5_CALLCONV
+krb5_ser_pack_int32(krb5_int32, krb5_octet **, size_t *);
+
+krb5_error_code KRB5_CALLCONV
+krb5_ser_unpack_int32(krb5_int32 *, krb5_octet **, size_t *);
+
+krb5_error_code KRB5_CALLCONV
+krb5_ser_pack_bytes(krb5_octet *, size_t, krb5_octet **, size_t *);
+
+#define AD_USAGE_AS_REQ         0x01
+#define AD_USAGE_TGS_REQ        0x02
+#define AD_USAGE_AP_REQ         0x04
+#define AD_USAGE_KDC_ISSUED     0x08
+#define AD_USAGE_MASK           0x0F
+#define AD_INFORMATIONAL        0x10
+
+struct _krb5_authdata_context;
+typedef struct _krb5_authdata_context *krb5_authdata_context;
+
+typedef void
+(*authdata_client_plugin_flags_proc)(krb5_context kcontext,
+                                     void *plugin_context,
+                                     krb5_authdatatype ad_type,
+                                     krb5_flags *flags);
+
+typedef krb5_error_code
+(*authdata_client_plugin_init_proc)(krb5_context context,
+                                    void **plugin_context);
+typedef void
+(*authdata_client_plugin_fini_proc)(krb5_context kcontext,
+                                    void *plugin_context);
+
+typedef krb5_error_code
+(*authdata_client_request_init_proc)(krb5_context kcontext,
+                                     struct _krb5_authdata_context *context,
+                                     void *plugin_context,
+                                     void **request_context);
+
+typedef void
+(*authdata_client_request_fini_proc)(krb5_context kcontext,
+                                     struct _krb5_authdata_context *context,
+                                     void *plugin_context,
+                                     void *request_context);
+
+typedef krb5_error_code
+(*authdata_client_import_authdata_proc)(krb5_context kcontext,
+                                        struct _krb5_authdata_context *context,
+                                        void *plugin_context,
+                                        void *request_context,
+                                        krb5_authdata **authdata,
+                                        krb5_boolean kdc_issued_flag,
+                                        krb5_const_principal issuer);
+
+typedef krb5_error_code
+(*authdata_client_export_authdata_proc)(krb5_context kcontext,
+                                        struct _krb5_authdata_context *context,
+                                        void *plugin_context,
+                                        void *request_context,
+                                        krb5_flags usage,
+                                        krb5_authdata ***authdata);
+
+typedef krb5_error_code
+(*authdata_client_get_attribute_types_proc)(krb5_context kcontext,
+                                            struct _krb5_authdata_context *context,
+                                            void *plugin_context,
+                                            void *request_context,
+                                            krb5_data **attrs);
+
+typedef krb5_error_code
+(*authdata_client_get_attribute_proc)(krb5_context kcontext,
+                                      struct _krb5_authdata_context *context,
+                                      void *plugin_context,
+                                      void *request_context,
+                                      const krb5_data *attribute,
+                                      krb5_boolean *authenticated,
+                                      krb5_boolean *complete,
+                                      krb5_data *value,
+                                      krb5_data *display_value,
+                                      int *more);
+
+typedef krb5_error_code
+(*authdata_client_set_attribute_proc)(krb5_context kcontext,
+                                      struct _krb5_authdata_context *context,
+                                      void *plugin_context,
+                                      void *request_context,
+                                      krb5_boolean complete,
+                                      const krb5_data *attribute,
+                                      const krb5_data *value);
+
+typedef krb5_error_code
+(*authdata_client_delete_attribute_proc)(krb5_context kcontext,
+                                         struct _krb5_authdata_context *context,
+                                         void *plugin_context,
+                                         void *request_context,
+                                         const krb5_data *attribute);
+
+typedef krb5_error_code
+(*authdata_client_export_internal_proc)(krb5_context kcontext,
+                                        struct _krb5_authdata_context *context,
+                                        void *plugin_context,
+                                        void *request_context,
+                                        krb5_boolean restrict_authenticated,
+                                        void **ptr);
+
+typedef void
+(*authdata_client_free_internal_proc)(krb5_context kcontext,
+                                      struct _krb5_authdata_context *context,
+                                      void *plugin_context,
+                                      void *request_context,
+                                      void *ptr);
+
+typedef krb5_error_code
+(*authdata_client_verify_proc)(krb5_context kcontext,
+                               struct _krb5_authdata_context *context,
+                               void *plugin_context,
+                               void *request_context,
+                               const krb5_auth_context *auth_context,
+                               const krb5_keyblock *key,
+                               const krb5_ap_req *req);
+
+typedef krb5_error_code
+(*authdata_client_size_proc)(krb5_context kcontext,
+                             struct _krb5_authdata_context *context,
+                             void *plugin_context,
+                             void *request_context,
+                             size_t *sizep);
+
+typedef krb5_error_code
+(*authdata_client_externalize_proc)(krb5_context kcontext,
+                                    struct _krb5_authdata_context *context,
+                                    void *plugin_context,
+                                    void *request_context,
+                                    krb5_octet **buffer,
+                                    size_t *lenremain);
+
+typedef krb5_error_code
+(*authdata_client_internalize_proc)(krb5_context kcontext,
+                                    struct _krb5_authdata_context *context,
+                                    void *plugin_context,
+                                    void *request_context,
+                                    krb5_octet **buffer,
+                                    size_t *lenremain);
+
+typedef krb5_error_code
+(*authdata_client_copy_proc)(krb5_context kcontext,
+                             struct _krb5_authdata_context *context,
+                             void *plugin_context,
+                             void *request_context,
+                             void *dst_plugin_context,
+                             void *dst_request_context);
+
+typedef struct krb5plugin_authdata_client_ftable_v0 {
+    char *name;
+    krb5_authdatatype *ad_type_list;
+    authdata_client_plugin_init_proc init;
+    authdata_client_plugin_fini_proc fini;
+    authdata_client_plugin_flags_proc flags;
+    authdata_client_request_init_proc request_init;
+    authdata_client_request_fini_proc request_fini;
+    authdata_client_get_attribute_types_proc get_attribute_types;
+    authdata_client_get_attribute_proc get_attribute;
+    authdata_client_set_attribute_proc set_attribute;
+    authdata_client_delete_attribute_proc delete_attribute;
+    authdata_client_export_authdata_proc export_authdata;
+    authdata_client_import_authdata_proc import_authdata;
+    authdata_client_export_internal_proc export_internal;
+    authdata_client_free_internal_proc free_internal;
+    authdata_client_verify_proc verify;
+    authdata_client_size_proc size;
+    authdata_client_externalize_proc externalize;
+    authdata_client_internalize_proc internalize;
+    authdata_client_copy_proc copy; /* optional */
+} krb5plugin_authdata_client_ftable_v0;
+
+#endif /* _KRB5_AUTHDATA_INT_H */
diff --git a/src/sss_client/sssd_pac.c b/src/sss_client/sssd_pac.c
new file mode 100644
index 0000000..cbcfe1d
--- /dev/null
+++ b/src/sss_client/sssd_pac.c
@@ -0,0 +1,282 @@
+/*
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2011 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* A short documentation about authdata plugins can be found in
+ * http://http://k5wiki.kerberos.org/wiki/Projects/VerifyAuthData */
+
+#include <krb5/krb5.h>
+#include <errno.h>
+
+#include "krb5_authdata_int.h"
+#include "sss_cli.h"
+
+
+struct sssd_context {
+    krb5_data data;
+};
+
+static krb5_error_code
+sssdpac_init(krb5_context kcontext, void **plugin_context)
+{
+    *plugin_context = NULL;
+    return 0;
+}
+
+static void
+sssdpac_flags(krb5_context kcontext,
+              void *plugin_context,
+              krb5_authdatatype ad_type,
+              krb5_flags *flags)
+{
+    *flags = AD_USAGE_KDC_ISSUED | AD_INFORMATIONAL | AD_USAGE_TGS_REQ;
+}
+
+static void
+sssdpac_fini(krb5_context kcontext, void *plugin_context)
+{
+    return;
+}
+
+static krb5_error_code
+sssdpac_request_init(krb5_context kcontext,
+                     krb5_authdata_context context,
+                     void *plugin_context,
+                     void **request_context)
+{
+    struct sssd_context *sssdctx;
+
+    sssdctx = (struct sssd_context *)calloc(1, sizeof(*sssdctx));
+    if (sssdctx == NULL) {
+        return ENOMEM;
+    }
+
+    *request_context = sssdctx;
+
+    return 0;
+}
+
+static krb5_error_code
+sssdpac_import_authdata(krb5_context kcontext,
+                        krb5_authdata_context context,
+                        void *plugin_context,
+                        void *request_context,
+                        krb5_authdata **authdata,
+                        krb5_boolean kdc_issued,
+                        krb5_const_principal kdc_issuer)
+{
+    struct sss_cli_req_data sss_data;
+    int ret;
+    uint8_t *repbuf;
+    size_t replen;
+    int errnop;
+    char *data = NULL;
+    struct sssd_context *sssdctx = (struct sssd_context *)request_context;
+
+    if (authdata[0] == NULL) {
+        return EINVAL;
+    }
+
+    sss_data.len = authdata[0]->length;
+    sss_data.data = authdata[0]->contents;
+
+    ret = sss_pac_make_request(SSS_PAC_ADD_PAC_USER, &sss_data,
+                               &repbuf, &replen, &errnop);
+    if (ret != 0) {
+        /* Ignore the error */
+    }
+
+    if (authdata[0]->length > 0) {
+        data = malloc(sizeof(char) * authdata[0]->length);
+        if (data == NULL) {
+            return ENOMEM;
+        }
+        memcpy(data, authdata[0]->contents, authdata[0]->length);
+    }
+
+    if (sssdctx->data.data != NULL) {
+        krb5_free_data_contents(kcontext, &sssdctx->data);
+    }
+
+    sssdctx->data.length = authdata[0]->length;
+    sssdctx->data.data = data;
+    return 0;
+}
+
+static void
+sssdpac_request_fini(krb5_context kcontext,
+                     krb5_authdata_context context,
+                     void *plugin_context,
+                     void *request_context)
+{
+    struct sssd_context *sssdctx = (struct sssd_context *)request_context;
+
+    if (sssdctx != NULL) {
+        if (sssdctx->data.data != NULL) {
+            krb5_free_data_contents(kcontext, &sssdctx->data);
+        }
+
+        free(sssdctx);
+    }
+}
+
+static krb5_error_code
+sssdpac_size(krb5_context kcontext,
+             krb5_authdata_context context,
+             void *plugin_context,
+             void *request_context,
+             size_t *sizep)
+{
+    struct sssd_context *sssdctx = (struct sssd_context *)request_context;
+
+    *sizep += sizeof(krb5_int32);
+
+    *sizep += sssdctx->data.length;
+
+    *sizep += sizeof(krb5_int32);
+
+    return 0;
+}
+
+static krb5_error_code
+sssdpac_externalize(krb5_context kcontext,
+                    krb5_authdata_context context,
+                    void *plugin_context,
+                    void *request_context,
+                    krb5_octet **buffer,
+                    size_t *lenremain)
+{
+    krb5_error_code code = 0;
+    struct sssd_context *sssdctx = (struct sssd_context *)request_context;
+    size_t required = 0;
+    krb5_octet *bp;
+    size_t remain;
+
+    bp = *buffer;
+    remain = *lenremain;
+
+    if (sssdctx->data.data != NULL) {
+        sssdpac_size(kcontext, context, plugin_context,
+                   request_context, &required);
+
+        if (required <= remain) {
+            krb5_ser_pack_int32((krb5_int32)sssdctx->data.length,
+                                &bp, &remain);
+            krb5_ser_pack_bytes((krb5_octet *)sssdctx->data.data,
+                                (size_t)sssdctx->data.length,
+                                &bp, &remain);
+            krb5_ser_pack_int32(0,
+                                &bp, &remain);
+        } else {
+            code = ENOMEM;
+        }
+    } else {
+        krb5_ser_pack_int32(0, &bp, &remain); /* length */
+        krb5_ser_pack_int32(0, &bp, &remain); /* verified */
+    }
+
+    *buffer = bp;
+    *lenremain = remain;
+
+    return code;
+}
+
+static krb5_error_code
+sssdpac_internalize(krb5_context kcontext,
+                    krb5_authdata_context context,
+                    void *plugin_context,
+                    void *request_context,
+                    krb5_octet **buffer,
+                    size_t *lenremain)
+{
+    struct sssd_context *sssdctx = (struct sssd_context *)request_context;
+    krb5_error_code code;
+    krb5_int32 ibuf;
+    krb5_octet *bp;
+    size_t remain;
+    krb5_data data;
+
+    bp = *buffer;
+    remain = *lenremain;
+
+    /* length */
+    code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+    if (code != 0) {
+        return code;
+    }
+
+    if (ibuf != 0) {
+
+        data.length = ibuf;
+        data.data = malloc(sizeof(char) * ibuf);
+        if (data.data == NULL) {
+            return ENOMEM;
+        }
+        memcpy(data.data, bp, ibuf);
+
+        bp += ibuf;
+        remain -= ibuf;
+    } else {
+        data.length = 0;
+        data.data = NULL;
+    }
+
+    /* verified */
+    code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+    if (code != 0) {
+        return code;
+    }
+
+    if (sssdctx->data.data != NULL) {
+        krb5_free_data_contents(kcontext, &sssdctx->data);
+    }
+
+    sssdctx->data.length = data.length;
+    sssdctx->data.data = data.data;
+
+    *buffer = bp;
+    *lenremain = remain;
+
+    return 0;
+}
+
+static krb5_authdatatype sssdpac_ad_types[] = { KRB5_AUTHDATA_WIN2K_PAC, 0 };
+
+krb5plugin_authdata_client_ftable_v0 authdata_client_0 = {
+    ((void *)((uintptr_t)("sssd_sssdpac"))),
+    sssdpac_ad_types,
+    sssdpac_init,
+    sssdpac_fini,
+    sssdpac_flags,
+    sssdpac_request_init,
+    sssdpac_request_fini,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    sssdpac_import_authdata,
+    NULL,
+    NULL,
+    NULL,
+    sssdpac_size,
+    sssdpac_externalize,
+    sssdpac_internalize,
+    NULL
+};
-- 
1.7.7.6

-------------- next part --------------
From 8d7d69e36077b33abf047ebf9e58b63b79802e04 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 14 Jun 2012 10:18:44 +0200
Subject: [PATCH 7/8] Add support for ID ranges

---
 Makefile.am                             |    1 +
 src/config/SSSDConfig/__init__.py.in    |    1 +
 src/config/etc/sssd.api.d/sssd-ipa.conf |    1 +
 src/db/sysdb.h                          |   39 ++++
 src/db/sysdb_ranges.c                   |  345 +++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_common.c          |   26 +++
 src/providers/ipa/ipa_common.h          |    2 +
 src/providers/ipa/ipa_init.c            |    1 +
 src/providers/ipa/ipa_opts.h            |    1 +
 src/providers/ipa/ipa_subdomains.c      |  176 +++++++++++++++-
 src/providers/ipa/ipa_subdomains.h      |    1 +
 11 files changed, 584 insertions(+), 10 deletions(-)
 create mode 100644 src/db/sysdb_ranges.c

diff --git a/Makefile.am b/Makefile.am
index 6cfeb34..6a21646 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -461,6 +461,7 @@ libsss_util_la_SOURCES = \
     src/db/sysdb_services.c \
     src/db/sysdb_autofs.c \
     src/db/sysdb_subdomains.c \
+    src/db/sysdb_ranges.c \
     src/db/sysdb_idmap.c \
     src/monitor/monitor_sbus.c \
     src/providers/dp_auth_util.c \
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index d7895b4..fdd18ba 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -130,6 +130,7 @@ option_strings = {
     'ipa_hbac_support_srchost' : _("If set to false, host argument given by PAM will be ignored"),
     'ipa_automount_location' : _("The automounter location this IPA client is using"),
     'ipa_master_domain_search_base': _("Search base for object containing info about IPA domain"),
+    'ipa_ranges_search_base': _("Search base for objects containing info about ID ranges"),
 
     # [provider/krb5]
     'krb5_kdcip' : _('Kerberos server address'),
diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
index 24f3c68..27573cb 100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -8,6 +8,7 @@ ipa_hbac_search_base = str, None, false
 ipa_host_search_base = str, None, false
 ipa_subdomains_search_base = str, None, false
 ipa_master_domain_search_base = str, None, false
+ipa_ranges_search_base = str, None, false
 ldap_uri = str, None, false
 ldap_search_base = str, None, false
 ldap_schema = str, None, false
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 5867b19..f0ff6f5 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -36,12 +36,15 @@
 #define SYSDB_GROUPS_CONTAINER "cn=groups"
 #define SYSDB_CUSTOM_CONTAINER "cn=custom"
 #define SYSDB_NETGROUP_CONTAINER "cn=Netgroups"
+#define SYSDB_RANGE_CONTAINER "cn=ranges"
 #define SYSDB_TMPL_USER_BASE SYSDB_USERS_CONTAINER",cn=%s,"SYSDB_BASE
 #define SYSDB_TMPL_GROUP_BASE SYSDB_GROUPS_CONTAINER",cn=%s,"SYSDB_BASE
 #define SYSDB_TMPL_CUSTOM_BASE SYSDB_CUSTOM_CONTAINER",cn=%s,"SYSDB_BASE
 #define SYSDB_TMPL_NETGROUP_BASE SYSDB_NETGROUP_CONTAINER",cn=%s,"SYSDB_BASE
+#define SYSDB_TMPL_RANGE_BASE SYSDB_RANGE_CONTAINER",cn=%s,"SYSDB_BASE
 
 #define SYSDB_SUBDOMAIN_CLASS "subdomain"
+#define SYSDB_RANGE_CLASS "idrange"
 #define SYSDB_USER_CLASS "user"
 #define SYSDB_GROUP_CLASS "group"
 #define SYSDB_NETGROUP_CLASS "netgroup"
@@ -49,6 +52,19 @@
 #define SYSDB_HOSTGROUP_CLASS "hostgroup"
 #define SYSDB_SELINUX_USERMAP_CLASS "selinuxusermap"
 #define SYSDB_SELINUX_CLASS "selinux"
+#define SYSDB_ID_RANGE_CLASS "idRange"
+#define SYSDB_DOMAIN_ID_RANGE_CLASS "domainIDRange"
+#define SYSDB_TRUSTED_AD_DOMAIN_RANGE_CLASS "TrustedADDomainRange"
+
+#define SYSDB_NAME "name"
+#define SYSDB_NAME_ALIAS "nameAlias"
+#define SYSDB_OBJECTCLASS "objectClass"
+
+#define SYSDB_NEXTID "nextID"
+#define SYSDB_UIDNUM "uidNumber"
+#define SYSDB_GIDNUM "gidNumber"
+#define SYSDB_CREATE_TIME "createTimestamp"
+
 
 #define SYSDB_NAME "name"
 #define SYSDB_NAME_ALIAS "nameAlias"
@@ -128,6 +144,12 @@
 #define SYSDB_SUBDOMAIN_FLAT "flatName"
 #define SYSDB_SUBDOMAIN_ID "domainID"
 
+#define SYSDB_BASE_ID "baseID"
+#define SYSDB_ID_RANGE_SIZE "idRangeSize"
+#define SYSDB_BASE_RID "baseRID"
+#define SYSDB_SECONDARY_BASE_RID "secondaryBaseRID"
+#define SYSDB_DOMAIN_ID "domainID"
+
 #define SYSDB_NEXTID_FILTER "("SYSDB_NEXTID"=*)"
 
 #define SYSDB_UC "objectclass="SYSDB_USER_CLASS
@@ -193,6 +215,7 @@
 #define SYSDB_TMPL_NETGROUP SYSDB_NAME"=%s,"SYSDB_TMPL_NETGROUP_BASE
 #define SYSDB_TMPL_CUSTOM_SUBTREE "cn=%s,"SYSDB_TMPL_CUSTOM_BASE
 #define SYSDB_TMPL_CUSTOM SYSDB_NAME"=%s,cn=%s,"SYSDB_TMPL_CUSTOM_BASE
+#define SYSDB_TMPL_RANGE SYSDB_NAME"=%s,"SYSDB_TMPL_RANGE_BASE
 
 #define SYSDB_MOD_ADD LDB_FLAG_MOD_ADD
 #define SYSDB_MOD_DEL LDB_FLAG_MOD_DELETE
@@ -222,6 +245,15 @@ struct subdomain_info {
     char *id;
 };
 
+struct range_info {
+    char *name;
+    uint32_t base_id;
+    uint32_t id_range_size;
+    uint32_t base_rid;
+    uint32_t secondary_base_rid;
+    char *trusted_dom_sid;
+};
+
 
 /* values are copied in the structure, allocated on "attrs" */
 int sysdb_attrs_add_val(struct sysdb_attrs *attrs,
@@ -392,6 +424,13 @@ errno_t sysdb_store_domgroup(struct sss_domain_info *domain,
 errno_t sysdb_delete_domgroup(struct sss_domain_info *domain,
                               const char *name, gid_t gid);
 
+errno_t sysdb_get_ranges(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+                             size_t *range_count,
+                             struct range_info ***range_list);
+errno_t sysdb_range_create(struct sysdb_ctx *sysdb, struct range_info *range);
+errno_t sysdb_update_ranges(struct sysdb_ctx *sysdb,
+                            struct range_info **ranges);
+
 /* Sysdb initialization.
  * call this function *only* once to initialize the database and get
  * the sysdb ctx */
diff --git a/src/db/sysdb_ranges.c b/src/db/sysdb_ranges.c
new file mode 100644
index 0000000..99518a2
--- /dev/null
+++ b/src/db/sysdb_ranges.c
@@ -0,0 +1,345 @@
+/*
+   SSSD
+
+   System Database - ID ranges related calls
+
+   Copyright (C) 2012 Sumit Bose <sbose at redhat.com>
+
+   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 "util/util.h"
+#include "db/sysdb_private.h"
+
+static errno_t find_attr_as_uint32_t(const struct ldb_message *msg,
+                                     const char *attr_name, uint32_t *result)
+{
+    uint64_t val;
+
+    val = ldb_msg_find_attr_as_uint64(msg, attr_name, UINT64_MAX);
+
+    if (val == UINT64_MAX) {
+        return ENOENT;
+    } else if (val >= UINT32_MAX) {
+        return EINVAL;
+    }
+
+    *result = val;
+    return EOK;
+}
+
+errno_t sysdb_get_ranges(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+                             size_t *range_count,
+                             struct range_info ***range_list)
+{
+    size_t c;
+    errno_t ret;
+    TALLOC_CTX *tmp_ctx;
+    struct ldb_result *res;
+    const char *attrs[] = {SYSDB_NAME,
+                           SYSDB_BASE_ID,
+                           SYSDB_ID_RANGE_SIZE,
+                           SYSDB_BASE_RID,
+                           SYSDB_SECONDARY_BASE_RID,
+                           SYSDB_DOMAIN_ID,
+                           NULL};
+    struct range_info **list;
+    struct ldb_dn *basedn;
+    const char *tmp_str;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    basedn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_RANGE_BASE,
+                            sysdb->domain->name);
+    if (basedn == NULL) {
+        ret = EIO;
+        goto done;
+    }
+    ret = ldb_search(sysdb->ldb, tmp_ctx, &res,
+                     basedn, LDB_SCOPE_ONELEVEL,
+                     attrs, "objectclass=%s", SYSDB_RANGE_CLASS);
+    if (ret != LDB_SUCCESS) {
+        ret = EIO;
+        goto done;
+    }
+
+    list = talloc_zero_array(tmp_ctx, struct range_info *, res->count);
+    if (list == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (c = 0; c < res->count; c++) {
+        list[c] = talloc_zero(list, struct range_info);
+        if (list[c] == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], SYSDB_NAME, NULL);
+        if (tmp_str == NULL) {
+            DEBUG(SSSDBG_MINOR_FAILURE, ("The object [%s] doesn't have a name.\n",
+                                       ldb_dn_get_linearized(res->msgs[c]->dn)));
+            ret = EINVAL;
+            goto done;
+        }
+
+        list[c]->name = talloc_strdup(list, tmp_str);
+        if (list[c]->name == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], SYSDB_DOMAIN_ID,
+                                              NULL);
+        if (tmp_str != NULL) {
+            list[c]->trusted_dom_sid = talloc_strdup(list, tmp_str);
+            if (list[c]->trusted_dom_sid == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+        }
+
+        ret = find_attr_as_uint32_t(res->msgs[c], SYSDB_BASE_ID,
+                                    &list[c]->base_id);
+        if (ret != EOK && ret != ENOENT) {
+            DEBUG(SSSDBG_MINOR_FAILURE, ("find_attr_as_uint32_t failed.\n"));
+            goto done;
+        }
+
+        ret = find_attr_as_uint32_t(res->msgs[c], SYSDB_ID_RANGE_SIZE,
+                                    &list[c]->id_range_size);
+        if (ret != EOK && ret != ENOENT) {
+            DEBUG(SSSDBG_MINOR_FAILURE, ("find_attr_as_uint32_t failed.\n"));
+            goto done;
+        }
+
+        ret = find_attr_as_uint32_t(res->msgs[c], SYSDB_BASE_RID,
+                                    &list[c]->base_rid);
+        if (ret != EOK && ret != ENOENT) {
+            DEBUG(SSSDBG_MINOR_FAILURE, ("find_attr_as_uint32_t failed.\n"));
+            goto done;
+        }
+
+        ret = find_attr_as_uint32_t(res->msgs[c], SYSDB_SECONDARY_BASE_RID,
+                                    &list[c]->secondary_base_rid);
+        if (ret != EOK && ret != ENOENT) {
+            DEBUG(SSSDBG_MINOR_FAILURE, ("find_attr_as_uint32_t failed.\n"));
+            goto done;
+        }
+    }
+
+    *range_count = res->count;
+    *range_list = talloc_steal(mem_ctx, list);
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+errno_t sysdb_range_create(struct sysdb_ctx *sysdb, struct range_info *range)
+{
+    struct ldb_message *msg;
+    int ret;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    msg = ldb_msg_new(tmp_ctx);
+    if (!msg) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    msg->dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_RANGE,
+                             range->name, sysdb->domain->name);
+    if (!msg->dn) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_OBJECTCLASS,
+                     SYSDB_ID_RANGE_CLASS);
+    if (ret) goto done;
+
+    if (range->trusted_dom_sid == NULL && range->secondary_base_rid != 0) {
+        ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_OBJECTCLASS,
+                         SYSDB_DOMAIN_ID_RANGE_CLASS);
+        if (ret) goto done;
+
+        ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_SECONDARY_BASE_RID,
+                        (unsigned long) range->secondary_base_rid);
+        if (ret) goto done;
+    } else if (range->trusted_dom_sid != NULL &&
+               range->secondary_base_rid == 0) {
+        ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_OBJECTCLASS,
+                         SYSDB_TRUSTED_AD_DOMAIN_RANGE_CLASS);
+        if (ret) goto done;
+
+        ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_DOMAIN_ID,
+                         range->trusted_dom_sid);
+        if (ret) goto done;
+    } else {
+        DEBUG(SSSDBG_OP_FAILURE, ("Invalid range, expected that either "
+                                  "the secondary base rid or the SID of the "
+                                  "trusted domain is set, but not both or "
+                                  "none of them.\n"));
+        ret = EINVAL;
+        goto done;
+    }
+
+    ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_NAME, range->name);
+    if (ret) goto done;
+
+    ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_BASE_ID,
+                    (unsigned long) range->base_id);
+    if (ret) goto done;
+
+    ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_ID_RANGE_SIZE,
+                    (unsigned long) range->id_range_size);
+    if (ret) goto done;
+
+    ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_BASE_RID,
+                    (unsigned long) range->base_rid);
+    if (ret) goto done;
+
+    ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME,
+                    (unsigned long)time(NULL));
+    if (ret) goto done;
+
+    ret = ldb_add(sysdb->ldb, msg);
+    if (ret) goto done;
+
+    ret = sysdb_error_to_errno(ret);
+
+done:
+    if (ret) {
+        DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
+    }
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
+errno_t sysdb_update_ranges(struct sysdb_ctx *sysdb,
+                            struct range_info **ranges)
+{
+    int ret;
+    int sret;
+    size_t c;
+    size_t d;
+    TALLOC_CTX *tmp_ctx = NULL;
+    size_t cur_range_count;
+    struct range_info **cur_ranges;
+    struct ldb_dn *dn;
+    bool in_transaction = false;
+    bool *keep_range;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Retrieve all ranges that are currently in sysdb */
+    ret = sysdb_get_ranges(tmp_ctx, sysdb, &cur_range_count,
+                               &cur_ranges);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_get_ranges failed.\n"));
+        goto done;
+    }
+
+    keep_range = talloc_zero_array(tmp_ctx, bool, cur_range_count);
+    if (keep_range == NULL) {
+        ret = ENOMEM;
+        DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero_array failed.\n"));
+        goto done;
+    }
+
+    ret = sysdb_transaction_start(sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_transaction_start failed.\n"));
+        goto done;
+    }
+    in_transaction = true;
+
+    /* Go through a list of retrieved ranges and:
+     * - if a range already exists in sysdb, mark it for preservation
+     * - if the range doesn't exist in sysdb, create it
+     */
+    for (c = 0; ranges[c] != NULL; c++) {
+        for (d = 0; d < cur_range_count; d++) {
+            if (strcasecmp(ranges[c]->name, cur_ranges[d]->name) == 0) {
+                keep_range[d] = true;
+                /* range already in cache, nothing to do */
+                break;
+            }
+        }
+
+        if (d == cur_range_count) {
+            DEBUG(SSSDBG_TRACE_FUNC, ("Adding range [%s].\n", ranges[c]->name));
+            ret = sysdb_range_create(sysdb, ranges[c]);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, ("sysdb_range_create failed.\n"));
+                goto done;
+            }
+        }
+    }
+
+    /* Now delete all ranges that have been in sysdb prior to
+     * refreshing the list and are not marked for preservation
+     * (i.e. they are not in the new list of ranges)
+     */
+    for (d = 0; d < cur_range_count; d++) {
+        if (!keep_range[d]) {
+            DEBUG(SSSDBG_TRACE_FUNC, ("Removing range [%s].\n",
+                                      cur_ranges[d]->name));
+            dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_RANGE,
+                                cur_ranges[d]->name, sysdb->domain->name);
+            if (dn == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+
+            ret = sysdb_delete_entry(sysdb, dn, true);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, ("sysdb_delete_entry failed.\n"));
+                goto done;
+            }
+        }
+    }
+
+    ret = sysdb_transaction_commit(sysdb);
+    if (ret == EOK) {
+        in_transaction = false;
+    } else {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("Could not commit transaction\n"));
+    }
+
+done:
+    if (in_transaction) {
+        sret = sysdb_transaction_cancel(sysdb);
+        if (sret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Could not cancel transaction\n"));
+        }
+    }
+    talloc_free(tmp_ctx);
+    return ret;
+}
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 09fbead..b816f99 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -149,6 +149,9 @@ static errno_t ipa_parse_search_base(TALLOC_CTX *mem_ctx,
     case IPA_MASTER_DOMAIN_SEARCH_BASE:
         class_name = "IPA_MASTER_DOMAIN";
         break;
+    case IPA_RANGES_SEARCH_BASE:
+        class_name = "IPA_RANGES";
+        break;
     default:
         DEBUG(SSSDBG_CONF_SETTINGS,
               ("Unknown search base type: [%d]\n", class));
@@ -539,6 +542,29 @@ int ipa_get_id_options(struct ipa_options *ipa_opts,
                                 &ipa_opts->master_domain_search_bases);
     if (ret != EOK) goto done;
 
+    if (NULL == dp_opt_get_string(ipa_opts->basic,
+                                  IPA_RANGES_SEARCH_BASE)) {
+        value = talloc_asprintf(tmpctx, "cn=ranges,cn=etc,%s", basedn);
+        if (value == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = dp_opt_set_string(ipa_opts->basic, IPA_RANGES_SEARCH_BASE, value);
+        if (ret != EOK) {
+            goto done;
+        }
+
+        DEBUG(SSSDBG_CONF_SETTINGS, ("Option %s set to %s\n",
+                  ipa_opts->basic[IPA_RANGES_SEARCH_BASE].opt_name,
+                  dp_opt_get_string(ipa_opts->basic,
+                                    IPA_RANGES_SEARCH_BASE)));
+    }
+    ret = ipa_parse_search_base(ipa_opts, ipa_opts->basic,
+                                IPA_RANGES_SEARCH_BASE,
+                                &ipa_opts->ranges_search_bases);
+    if (ret != EOK) goto done;
+
     ret = sdap_get_map(ipa_opts->id, cdb, conf_path,
                        ipa_attr_map,
                        SDAP_AT_GENERAL,
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index 0a18ec1..3e2ef28 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -48,6 +48,7 @@ enum ipa_basic_opt {
     IPA_HBAC_DENY_METHOD,
     IPA_HBAC_SUPPORT_SRCHOST,
     IPA_AUTOMOUNT_LOCATION,
+    IPA_RANGES_SEARCH_BASE,
 
     IPA_OPTS_BASIC /* opts counter */
 };
@@ -127,6 +128,7 @@ struct ipa_options {
     struct sdap_search_base **selinux_search_bases;
     struct sdap_search_base **subdomains_search_bases;
     struct sdap_search_base **master_domain_search_bases;
+    struct sdap_search_base **ranges_search_bases;
     struct ipa_service *service;
 
     /* id provider */
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index b58058a..0524ddf 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -542,6 +542,7 @@ int sssm_ipa_subdomains_init(struct be_ctx *bectx,
     subdomains_ctx->sdap_id_ctx = id_ctx->sdap_id_ctx;
     subdomains_ctx->search_bases = id_ctx->ipa_options->subdomains_search_bases;
     subdomains_ctx->master_search_bases = id_ctx->ipa_options->master_domain_search_bases;
+    subdomains_ctx->ranges_search_bases = id_ctx->ipa_options->ranges_search_bases;
     *ops = &ipa_subdomains_ops;
     *pvt_data = subdomains_ctx;
 
diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
index a0714cb..1d5d4e1 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -45,6 +45,7 @@ struct dp_option ipa_basic_opts[] = {
     { "ipa_hbac_treat_deny_as", DP_OPT_STRING, { "DENY_ALL" }, NULL_STRING },
     { "ipa_hbac_support_srchost", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ipa_automount_location", DP_OPT_STRING, { "default" }, NULL_STRING },
+    { "ipa_ranges_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index 185b3c5..55ecea0 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -28,14 +28,23 @@
 
 #define SUBDOMAINS_FILTER "objectclass=ipaNTTrustedDomain"
 #define MASTER_DOMAIN_FILTER "objectclass=ipaNTDomainAttrs"
+#define RANGE_FILTER "objectclass=ipaIDRange"
 
 #define IPA_CN "cn"
 #define IPA_FLATNAME "ipaNTFlatName"
-#define IPA_SID "ipaNTTrustedDomainSID"
+#define IPA_SID "ipaNTSecurityIdentifier"
+#define IPA_TRUSTED_DOMAIN_SID "ipaNTTrustedDomainSID"
+
+#define IPA_BASE_ID "ipaBaseID"
+#define IPA_ID_RANGE_SIZE "ipaIDRangeSize"
+#define IPA_BASE_RID "ipaBaseRID"
+#define IPA_SECONDARY_BASE_RID "ipaSecondaryBaseRID"
+#define OBJECTCLASS "objectClass"
 
 enum ipa_subdomains_req_type {
     IPA_SUBDOMAINS_MASTER,
     IPA_SUBDOMAINS_SLAVE,
+    IPA_SUBDOMAINS_RANGES,
 
     IPA_SUBDOMAINS_MAX /* Counter */
 };
@@ -43,6 +52,7 @@ enum ipa_subdomains_req_type {
 struct ipa_subdomains_req_params {
     const char *filter;
     tevent_req_fn cb;
+    const char *attrs[8];
 };
 
 static void ipa_subdomains_reply(struct be_req *be_req, int dp_err, int result)
@@ -50,6 +60,97 @@ static void ipa_subdomains_reply(struct be_req *be_req, int dp_err, int result)
     be_req->fn(be_req, dp_err, result, NULL);
 }
 
+static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
+                                        size_t count,
+                                        struct sysdb_attrs **reply,
+                                        struct range_info ***_range_list)
+{
+    struct range_info **range_list = NULL;
+    const char *value;
+    size_t c;
+    int ret;
+
+    range_list = talloc_array(mem_ctx, struct range_info *, count + 1);
+    if (range_list == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("talloc_array failed.\n"));
+        return ENOMEM;
+    }
+
+    for (c = 0; c < count; c++) {
+        range_list[c] = talloc_zero(range_list, struct range_info);
+        if (range_list[c] == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed.\n"));
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = sysdb_attrs_get_string(reply[c], IPA_CN, &value);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+            goto done;
+        }
+        range_list[c]->name = talloc_strdup(range_list[c], value);
+        if (range_list[c]->name == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = sysdb_attrs_get_string(reply[c], IPA_TRUSTED_DOMAIN_SID, &value);
+        if (ret == EOK) {
+            range_list[c]->trusted_dom_sid = talloc_strdup(range_list[c],
+                                                           value);
+            if (range_list[c]->trusted_dom_sid == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+                ret = ENOMEM;
+                goto done;
+            }
+        } else if (ret != ENOENT) {
+            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+            goto done;
+        }
+
+        ret = sysdb_attrs_get_uint32_t(reply[c], IPA_BASE_ID,
+                                       &range_list[c]->base_id);
+        if (ret != EOK && ret != ENOENT) {
+            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+            goto done;
+        }
+
+        ret = sysdb_attrs_get_uint32_t(reply[c], IPA_ID_RANGE_SIZE,
+                                       &range_list[c]->id_range_size);
+        if (ret != EOK && ret != ENOENT) {
+            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+            goto done;
+        }
+
+        ret = sysdb_attrs_get_uint32_t(reply[c], IPA_BASE_RID,
+                                       &range_list[c]->base_rid);
+        if (ret != EOK && ret != ENOENT) {
+            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+            goto done;
+        }
+
+        ret = sysdb_attrs_get_uint32_t(reply[c], IPA_SECONDARY_BASE_RID,
+                                       &range_list[c]->secondary_base_rid);
+        if (ret != EOK && ret != ENOENT) {
+            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+            goto done;
+        }
+    }
+    range_list[c] = NULL;
+
+    *_range_list = range_list;
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        talloc_free(range_list);
+    }
+
+    return ret;
+}
+
 static errno_t ipa_subdomains_parse_results(
                                         struct be_get_subdomains_req *sd_data,
                                         size_t count,
@@ -101,7 +202,7 @@ static errno_t ipa_subdomains_parse_results(
             goto done;
         }
 
-        ret = sysdb_attrs_get_string(reply[c], IPA_SID, &value);
+        ret = sysdb_attrs_get_string(reply[c], IPA_TRUSTED_DOMAIN_SID, &value);
         if (ret == EOK) {
             new_domain_list[c]->id = talloc_strdup(new_domain_list[c], value);
             if (new_domain_list[c]->id == NULL) {
@@ -150,10 +251,16 @@ ipa_subdomains_handler_get(struct ipa_subdomains_req_ctx *ctx,
                            enum ipa_subdomains_req_type type);
 static void ipa_subdomains_handler_done(struct tevent_req *req);
 static void ipa_subdomains_handler_master_done(struct tevent_req *req);
+static void ipa_subdomains_handler_ranges_done(struct tevent_req *req);
 
 static struct ipa_subdomains_req_params subdomain_requests[] = {
-    { MASTER_DOMAIN_FILTER, ipa_subdomains_handler_master_done },
-    { SUBDOMAINS_FILTER, ipa_subdomains_handler_done }
+    { MASTER_DOMAIN_FILTER, ipa_subdomains_handler_master_done,
+        {IPA_CN, IPA_FLATNAME, IPA_SID, NULL }},
+    { SUBDOMAINS_FILTER, ipa_subdomains_handler_done,
+        {IPA_CN, IPA_FLATNAME, IPA_TRUSTED_DOMAIN_SID, NULL }},
+    { RANGE_FILTER, ipa_subdomains_handler_ranges_done,
+        {OBJECTCLASS, IPA_CN, IPA_BASE_ID, IPA_ID_RANGE_SIZE, IPA_BASE_RID,
+         IPA_SECONDARY_BASE_RID, IPA_TRUSTED_DOMAIN_SID, NULL }}
 };
 
 void ipa_subdomains_handler(struct be_req *be_req)
@@ -250,10 +357,6 @@ ipa_subdomains_handler_get(struct ipa_subdomains_req_ctx *ctx,
     struct tevent_req *req;
     struct sdap_search_base *base;
     struct ipa_subdomains_req_params *params;
-    const char *attrs[] = {IPA_CN,
-                           IPA_FLATNAME,
-                           IPA_SID,
-                           NULL};
 
     if (type >= IPA_SUBDOMAINS_MAX) {
         return EINVAL;
@@ -276,7 +379,7 @@ ipa_subdomains_handler_get(struct ipa_subdomains_req_ctx *ctx,
                         ctx->sd_ctx->sdap_id_ctx->opts,
                         sdap_id_op_handle(ctx->sdap_op),
                         base->basedn, base->scope,
-                        ctx->current_filter, attrs, NULL, 0,
+                        ctx->current_filter, params->attrs, NULL, 0,
                         dp_opt_get_int(ctx->sd_ctx->sdap_id_ctx->opts->basic,
                                        SDAP_SEARCH_TIMEOUT), false);
 
@@ -299,7 +402,6 @@ static void ipa_subdomains_handler_done(struct tevent_req *req)
                                                        struct ipa_subdomains_req_ctx);
     struct be_req *be_req = ctx->be_req;
     struct sysdb_ctx *sysdb;
-    struct subdomain_info *domain_info;
 
     sysdb = (be_req->sysdb)?be_req->sysdb:be_req->be_ctx->sysdb;
 
@@ -342,6 +444,60 @@ static void ipa_subdomains_handler_done(struct tevent_req *req)
         goto done;
     }
 
+
+    ctx->search_base_iter = 0;
+    ctx->search_bases = ctx->sd_ctx->ranges_search_bases;
+    ret = ipa_subdomains_handler_get(ctx, IPA_SUBDOMAINS_RANGES);
+    if (ret == EAGAIN) {
+        return;
+    } else if (ret != EOK) {
+        goto done;
+    }
+
+    DEBUG(SSSDBG_OP_FAILURE, ("No search base for ranges available.\n"));
+    ret = EINVAL;
+
+done:
+    talloc_free(ctx);
+    ipa_subdomains_reply(be_req, (ret == EOK ? DP_ERR_OK : DP_ERR_FATAL), ret);
+}
+
+
+static void ipa_subdomains_handler_ranges_done(struct tevent_req *req)
+{
+    errno_t ret;
+    size_t reply_count;
+    struct sysdb_attrs **reply = NULL;
+    struct ipa_subdomains_req_ctx *ctx = tevent_req_callback_data(req,
+                                                       struct ipa_subdomains_req_ctx);
+    struct be_req *be_req = ctx->be_req;
+    struct subdomain_info *domain_info;
+    struct range_info **range_list = NULL;
+    struct sysdb_ctx *sysdb;
+
+    sysdb = (be_req->sysdb)?be_req->sysdb:be_req->be_ctx->sysdb;
+
+    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;
+    }
+
+    ret = ipa_ranges_parse_results(ctx, reply_count, reply, &range_list);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("ipa_ranges_parse_results request failed.\n"));
+        goto done;
+    }
+
+    ret = sysdb_update_ranges(sysdb, range_list);
+    talloc_free(range_list);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_update_ranges failed.\n"));
+        goto done;
+    }
+
+
     ret = sysdb_master_domain_get_info(ctx, sysdb, &domain_info);
     if (ret != EOK) {
         goto done;
diff --git a/src/providers/ipa/ipa_subdomains.h b/src/providers/ipa/ipa_subdomains.h
index 76406f1..895ba29 100644
--- a/src/providers/ipa/ipa_subdomains.h
+++ b/src/providers/ipa/ipa_subdomains.h
@@ -32,6 +32,7 @@ struct ipa_subdomains_ctx {
     struct sdap_id_ctx *sdap_id_ctx;
     struct sdap_search_base **search_bases;
     struct sdap_search_base **master_search_bases;
+    struct sdap_search_base **ranges_search_bases;
 };
 
 
-- 
1.7.7.6

-------------- next part --------------
From ac120dec7a6bdefe0abcbaaba76b94a57ac4dd69 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Fri, 15 Jun 2012 12:20:59 +0200
Subject: [PATCH 8/8] Add range support to PAC responder

---
 src/responder/pac/pacsrv_cmd.c |  175 ++++++++++++++++++++++++++++++---------
 1 files changed, 134 insertions(+), 41 deletions(-)

diff --git a/src/responder/pac/pacsrv_cmd.c b/src/responder/pac/pacsrv_cmd.c
index 42ab9ec..8e3b45f 100644
--- a/src/responder/pac/pacsrv_cmd.c
+++ b/src/responder/pac/pacsrv_cmd.c
@@ -60,13 +60,48 @@ static errno_t add_idmap_domain(struct sss_idmap_ctx *idmap_ctx,
                                 const char *domain_name,
                                 const char *dom_sid_str)
 {
-    struct sss_idmap_range range;
+    struct sss_idmap_range range = {0, 0};
     enum idmap_error_code err;
+    TALLOC_CTX *tmp_ctx = NULL;
+    size_t range_count;
+    struct range_info **range_list;
+    size_t c;
+    int ret;
+
+    if (domain_name == NULL || dom_sid_str == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Missing domain name or SID.\n"));
+        return EINVAL;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("talloc_new failed.\n"));
+        return ENOMEM;
+    }
+
+    ret = sysdb_get_ranges(tmp_ctx, sysdb, &range_count, &range_list);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_get_ranges failed.\n"));
+        goto done;
+    }
 
-    /* TODO: read range form sysdb if
-     * https://fedorahosted.org/freeipa/ticket/2185 is fixed */
-    range.min = 200000;
-    range.max = 400000;
+    for (c = 0; c < range_count; c++) {
+        if (range_list[c]->trusted_dom_sid != NULL &&
+            strcmp(range_list[c]->trusted_dom_sid, dom_sid_str) == 0) {
+                range.min = range_list[c]->base_id;
+                range.max = range_list[c]->base_id +
+                            range_list[c]->id_range_size - 1;
+                /* TODO: add support for multiple ranges. */
+            break;
+        }
+    }
+
+    if (range.min == 0 && range.max == 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Failed to find mapping range for domain "
+                                  "[%s][%s].\n", domain_name, dom_sid_str));
+        ret = ENOENT;
+        goto done;
+    }
 
     err = sss_idmap_add_domain(idmap_ctx, domain_name, dom_sid_str, &range);
     if (err != IDMAP_SUCCESS) {
@@ -74,7 +109,11 @@ static errno_t add_idmap_domain(struct sss_idmap_ctx *idmap_ctx,
         return EFAULT;
     }
 
-    return EOK;
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
 }
 
 static errno_t domsid_rid_to_uid(struct pac_ctx *pac_ctx,
@@ -142,9 +181,10 @@ done:
     return ret;
 }
 
-static errno_t get_my_domain_sid(struct pac_ctx *pac_ctx,
-                                 struct sss_domain_info *dom,
-                                 struct dom_sid **_sid)
+static errno_t get_my_domain_data(struct pac_ctx *pac_ctx,
+                                  struct sss_domain_info *dom,
+                                  struct dom_sid **_sid,
+                                  struct local_mapping_ranges **_range_map)
 {
     struct sysdb_ctx *sysdb;
     int ret;
@@ -158,8 +198,12 @@ static errno_t get_my_domain_sid(struct pac_ctx *pac_ctx,
     struct dom_sid *sid = NULL;
     char *dom_name;
     enum idmap_error_code err;
+    size_t range_count;
+    struct range_info **range_list;
+    struct local_mapping_ranges *r_map = NULL;
+    size_t c;
 
-    if (pac_ctx->my_dom_sid == NULL) {
+    if (pac_ctx->my_dom_sid == NULL || pac_ctx->range_map == NULL) {
         if (dom->parent != NULL) {
             sysdb = dom->parent->sysdb;
             dom_name = dom->parent->name;
@@ -187,44 +231,91 @@ static errno_t get_my_domain_sid(struct pac_ctx *pac_ctx,
             goto done;
         }
 
-        ret = sysdb_search_entry(tmp_ctx, sysdb, basedn, LDB_SCOPE_BASE, NULL,
-                                 attrs, &msgs_count, &msgs);
-        if (ret != LDB_SUCCESS) {
-            ret = EIO;
-            goto done;
-        }
+        if (pac_ctx->my_dom_sid == NULL) {
+            ret = sysdb_search_entry(tmp_ctx, sysdb, basedn, LDB_SCOPE_BASE, NULL,
+                                     attrs, &msgs_count, &msgs);
+            if (ret != LDB_SUCCESS) {
+                ret = EIO;
+                goto done;
+            }
 
-        if (msgs_count != 1) {
-            DEBUG(SSSDBG_OP_FAILURE, ("Base search returned [%d] results, "
-                                     "expected 1.\n", msgs_count));
-            ret = EINVAL;
-            goto done;
-        }
+            if (msgs_count != 1) {
+                DEBUG(SSSDBG_OP_FAILURE, ("Base search returned [%d] results, "
+                                         "expected 1.\n", msgs_count));
+                ret = EINVAL;
+                goto done;
+            }
 
-        sid_str = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUBDOMAIN_ID, NULL);
-        if (sid_str == NULL) {
-            DEBUG(SSSDBG_OP_FAILURE, ("SID of my domain is not available.\n"));
-            ret = EINVAL;
-            goto done;
-        }
+            sid_str = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUBDOMAIN_ID, NULL);
+            if (sid_str == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE, ("SID of my domain is not available.\n"));
+                ret = EINVAL;
+                goto done;
+            }
 
-        err = sss_idmap_sid_to_smb_sid(pac_ctx->idmap_ctx, sid_str, &sid);
-        if (err != IDMAP_SUCCESS) {
-            DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_sid_to_smb_sid failed.\n"));
-            ret = EFAULT;
-            goto done;
+            err = sss_idmap_sid_to_smb_sid(pac_ctx->idmap_ctx, sid_str, &sid);
+            if (err != IDMAP_SUCCESS) {
+                DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_sid_to_smb_sid failed.\n"));
+                ret = EFAULT;
+                goto done;
+            }
+
+            pac_ctx->my_dom_sid = talloc_memdup(pac_ctx, sid,
+                                                sizeof(struct dom_sid));
+            if (pac_ctx->my_dom_sid == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE, ("talloc_memdup failed.\n"));
+                ret = ENOMEM;
+                goto done;
+            }
         }
 
-        pac_ctx->my_dom_sid = talloc_memdup(pac_ctx, sid,
-                                            sizeof(struct dom_sid));
-        if (pac_ctx->my_dom_sid == NULL) {
-            DEBUG(SSSDBG_OP_FAILURE, ("talloc_memdup failed.\n"));
-            ret = ENOMEM;
-            goto done;
+        if (pac_ctx->range_map == NULL) {
+            ret = sysdb_get_ranges(tmp_ctx, sysdb, &range_count, &range_list);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, ("sysdb_get_ranges failed.\n"));
+                goto done;
+            }
+
+            for (c = 0; c < range_count; c++) {
+                if (range_list[c]->trusted_dom_sid == NULL &&
+                    range_list[c]->secondary_base_rid != 0) {
+                        r_map = talloc_zero(pac_ctx,
+                                            struct local_mapping_ranges);
+                        if (r_map == NULL) {
+                            DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed.\n"));
+                            ret = ENOMEM;
+                            goto done;
+                        }
+
+                        r_map->local_ids.min = range_list[c]->base_id;
+                        r_map->local_ids.max = range_list[c]->base_id +
+                                                   range_list[c]->id_range_size - 1;
+
+                        r_map->primary_rids.min = range_list[c]->base_rid;
+                        r_map->primary_rids.max = range_list[c]->base_rid +
+                                                  range_list[c]->id_range_size - 1;
+
+                        r_map->secondary_rids.min = range_list[c]->secondary_base_rid;
+                        r_map->secondary_rids.max = range_list[c]->secondary_base_rid +
+                                                    range_list[c]->id_range_size - 1;
+
+                        /* TODO: add support for multiple ranges. */
+                        break;
+                }
+            }
+
+            if (r_map == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE, ("Failed to find local id map.\n"));
+                ret = ENOENT;
+                goto done;
+            }
+
+            pac_ctx->range_map = r_map;
         }
     }
 
     *_sid = pac_ctx->my_dom_sid;
+    *_range_map = pac_ctx->range_map;
 
     ret = EOK;
 
@@ -584,6 +675,7 @@ static errno_t pac_add_user_next(struct pac_req_ctx *pr_ctx)
     int ret;
     struct tevent_req *req;
     struct dom_sid *my_dom_sid;
+    struct local_mapping_ranges *my_range_map;
 
     ret = save_pac_user(pr_ctx);
     if (ret != EOK) {
@@ -591,13 +683,14 @@ static errno_t pac_add_user_next(struct pac_req_ctx *pr_ctx)
         goto done;
     }
 
-    ret = get_my_domain_sid(pr_ctx->pac_ctx, pr_ctx->dom, &my_dom_sid);
+    ret = get_my_domain_data(pr_ctx->pac_ctx, pr_ctx->dom,
+                             &my_dom_sid, &my_range_map);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, ("get_my_domain_sid failed.\n"));
         goto done;
     }
 
-    ret = get_gids_from_pac(pr_ctx, pr_ctx->pac_ctx->range_map, my_dom_sid,
+    ret = get_gids_from_pac(pr_ctx, my_range_map, my_dom_sid,
                             pr_ctx->logon_info, &pr_ctx->gid_count,
                             &pr_ctx->gids);
     if (ret != EOK) {
-- 
1.7.7.6



More information about the sssd-devel mailing list