[SSSD] [PATCHES] PAC responder and client

Sumit Bose sbose at redhat.com
Mon Jun 18 14:34:23 UTC 2012


On Sun, Jun 17, 2012 at 06:47:05PM -0400, Simo Sorce wrote:
> On Sun, 2012-06-17 at 11:38 +0200, Sumit Bose wrote:
> > 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
> 
> 
> Just giving a quick look.

thank you.

I forgot to attach the very first patch, so patch number no differ by 1.

> 
> 0. Can you add more comments on what the various functions are supposed
> to do ?

comments added

> 
> 1. Patch 0002 is *really* big, I would split it in smaller pieces.

I moved all the utility calls into 'PAC responder: add some utility
functions' and also into the pacsrv_utils.c file.

> 
> 2. In patch 0002 leave the gecos empty if there is no full name.

done

> 
> 3. In pac_add_pac_user you seem to be trusting the logon_domain w/o any
> check. I would rather we verify it is valid wrt the realm of the
> principal that owns the PAC. Otherwise technically one trust domain
> could try to attack another by giving us a same named user and injecting
> a domain name of a different trusted domain.

we talked about this on irc and agreed that this check should be done by
the KDC before resigning the PAC. I opened ticket
https://fedorahosted.org/freeipa/ticket/2849 to track this.

> 
> 4. In pac_add_pac_user() you are returning pac_cmd_done() whether that
> whole funciton completed or had to fork off async requests, is that
> intentional ?

yes, as done in other responders as well pac_cmd_done() returns to the
main loop when called with EAGAIN as second parameter.

> 
> 5. SSS_PAC_ADD_PAC_USER     = 0x00E9,
>   Make this 0x0101
> 

done

> 
> Simo.
> 

new versions attached.

bye,
Sumit
-------------- next part --------------
From 24de7805bd94e714f19d83c6547560e3ca11128d Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Fri, 13 May 2011 18:44:15 +0200
Subject: [PATCH 1/9] PAC responder: add basic infrastructure

This adds only the basic outline of the PAC responder, it won't support
any operations, it will just start and initialize itself.
---
 Makefile.am                    |   21 ++++
 configure.ac                   |    1 +
 src/confdb/confdb.h            |    3 +
 src/external/pac_responder.m4  |   34 ++++++
 src/monitor/monitor.c          |    2 +-
 src/responder/pac/pacsrv.c     |  227 ++++++++++++++++++++++++++++++++++++++++
 src/responder/pac/pacsrv.h     |   52 +++++++++
 src/responder/pac/pacsrv_cmd.c |   61 +++++++++++
 8 files changed, 400 insertions(+), 1 deletions(-)
 create mode 100644 src/external/pac_responder.m4
 create mode 100644 src/responder/pac/pacsrv.c
 create mode 100644 src/responder/pac/pacsrv.h
 create mode 100644 src/responder/pac/pacsrv_cmd.c

diff --git a/Makefile.am b/Makefile.am
index c5bde07..ad79de3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -97,6 +97,10 @@ if BUILD_SSH
 sssdlibexec_PROGRAMS += sssd_ssh
 endif
 
+if BUILD_PAC_RESPONDER
+    sssdlibexec_PROGRAMS += sssd_pac
+endif
+
 if HAVE_CHECK
     non_interactive_check_based_tests = \
         sysdb-tests \
@@ -236,6 +240,7 @@ AM_CPPFLAGS = \
     -DSSS_NSS_MCACHE_DIR=\"$(mcpath)\" \
     -DSSS_NSS_SOCKET_NAME=\"$(pipepath)/nss\" \
     -DSSS_PAM_SOCKET_NAME=\"$(pipepath)/pam\" \
+    -DSSS_PAC_SOCKET_NAME=\"$(pipepath)/pac\" \
     -DSSS_PAM_PRIV_SOCKET_NAME=\"$(pipepath)/private/pam\" \
     -DSSS_SUDO_SOCKET_NAME=\"$(pipepath)/sudo\" \
     -DSSS_AUTOFS_SOCKET_NAME=\"$(pipepath)/autofs\" \
@@ -349,6 +354,7 @@ dist_noinst_HEADERS = \
     src/responder/nss/nsssrv_netgroup.h \
     src/responder/nss/nsssrv_services.h \
     src/responder/nss/nsssrv_mmap_cache.h \
+    src/responder/pac/pacsrv.h \
     src/responder/common/negcache.h \
     src/responder/sudo/sudosrv_private.h \
     src/responder/autofs/autofs_private.h \
@@ -581,6 +587,21 @@ sssd_ssh_LDADD = \
     libsss_util.la
 endif
 
+sssd_pac_SOURCES = \
+    src/responder/pac/pacsrv.c \
+    src/responder/pac/pacsrv_cmd.c \
+    $(SSSD_UTIL_OBJ) \
+    $(SSSD_RESPONDER_OBJ)
+sssd_pac_CFLAGS = \
+    $(AM_CFLAGS) \
+    $(NDR_KRB5PAC_CFLAGS)
+sssd_pac_LDADD = \
+    $(NDR_KRB5PAC_LIBS) \
+    $(TDB_LIBS) \
+    $(SSSD_LIBS) \
+    libsss_idmap.la \
+    libsss_util.la
+
 sssd_be_SOURCES = \
     src/providers/data_provider_be.c \
     src/providers/data_provider_fo.c \
diff --git a/configure.ac b/configure.ac
index f43c32a..7f24209 100644
--- a/configure.ac
+++ b/configure.ac
@@ -126,6 +126,7 @@ m4_include([src/external/nsupdate.m4])
 m4_include([src/external/libkeyutils.m4])
 m4_include([src/external/libnl.m4])
 m4_include([src/external/systemd.m4])
+m4_include([src/external/pac_responder.m4])
 m4_include([src/util/signal.m4])
 
 WITH_UNICODE_LIB
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index db247b1..562a999 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -117,6 +117,9 @@
 #define CONFDB_SSH_HASH_KNOWN_HOSTS "ssh_hash_known_hosts"
 #define CONFDB_DEFAULT_SSH_HASH_KNOWN_HOSTS true
 
+/* PAC */
+#define CONFDB_PAC_CONF_ENTRY "config/pac"
+
 /* Data Provider */
 #define CONFDB_DP_CONF_ENTRY "config/dp"
 
diff --git a/src/external/pac_responder.m4 b/src/external/pac_responder.m4
new file mode 100644
index 0000000..f284192
--- /dev/null
+++ b/src/external/pac_responder.m4
@@ -0,0 +1,34 @@
+AC_SUBST(NDR_KRB5PAC_CFLAGS)
+AC_SUBST(NDR_KRB5PAC_LIBS)
+
+AC_ARG_ENABLE([experimental-pac-responder],
+              [AS_HELP_STRING([--enable-experimental-pac-responder],
+                              [build experimental pac responder])],
+              [build_pac_responder=$enableval],
+              [build_pac_responder=no])
+
+if test x$build_all_experimental_features != xno
+then
+    build_pac_responder=yes
+fi
+
+if test x$build_pac_responder == xyes
+then
+    PKG_CHECK_MODULES(NDR_KRB5PAC, ndr_krb5pac,,
+        AC_MSG_ERROR([Cannot build pac responder without libndr_krb5pac]))
+
+    AC_PATH_PROG(KRB5_CONFIG, krb5-config)
+    AC_MSG_CHECKING(for supported MIT krb5 version)
+    KRB5_VERSION="`$KRB5_CONFIG --version`"
+    case $KRB5_VERSION in
+        Kerberos\ 5\ release\ 1.9* | \
+        Kerberos\ 5\ release\ 1.10*)
+            AC_MSG_RESULT(yes)
+            ;;
+        *)
+            AC_MSG_ERROR([Cannot build authdata plugin with this version of
+                          MIT Kerberos, please use 1.9.x or 1.10.x])
+    esac
+fi
+
+AM_CONDITIONAL([BUILD_PAC_RESPONDER], [test x$build_pac_responder = xyes ])
diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
index 041f576..3df1b10 100644
--- a/src/monitor/monitor.c
+++ b/src/monitor/monitor.c
@@ -759,7 +759,7 @@ static int check_local_domain_unique(struct sss_domain_info *domains)
 static char *check_services(char **services)
 {
     const char *known_services[] = { "nss", "pam", "sudo", "autofs", "ssh",
-                                     NULL };
+                                     "pac", NULL };
     int i;
     int ii;
 
diff --git a/src/responder/pac/pacsrv.c b/src/responder/pac/pacsrv.c
new file mode 100644
index 0000000..feee3ae
--- /dev/null
+++ b/src/responder/pac/pacsrv.c
@@ -0,0 +1,227 @@
+/*
+   SSSD
+
+   PAC Responder
+
+   Copyright (C) Sumit Bose <sbose at redhat.com> 2011
+
+   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 <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include "popt.h"
+#include "util/util.h"
+#include "responder/pac/pacsrv.h"
+#include "db/sysdb.h"
+#include "confdb/confdb.h"
+#include "dbus/dbus.h"
+#include "sbus/sssd_dbus.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/responder.h"
+#include "providers/data_provider.h"
+#include "monitor/monitor_interfaces.h"
+#include "sbus/sbus_client.h"
+
+#define SSS_PAC_PIPE_NAME "pac"
+
+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 },
+    { NULL, NULL }
+};
+
+struct sbus_interface monitor_pac_interface = {
+    MONITOR_INTERFACE,
+    MONITOR_PATH,
+    SBUS_DEFAULT_VTABLE,
+    monitor_pac_methods,
+    NULL
+};
+
+static struct sbus_method pac_dp_methods[] = {
+    { NULL, NULL }
+};
+
+struct sbus_interface pac_dp_interface = {
+    DP_INTERFACE,
+    DP_PATH,
+    SBUS_DEFAULT_VTABLE,
+    pac_dp_methods,
+    NULL
+};
+
+
+/* TODO: check if this can be made generic for all responders */
+static void pac_dp_reconnect_init(struct sbus_connection *conn,
+                                  int status, void *pvt)
+{
+    struct be_conn *be_conn = talloc_get_type(pvt, struct be_conn);
+    int ret;
+
+    /* Did we reconnect successfully? */
+    if (status == SBUS_RECONNECT_SUCCESS) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Reconnected to the Data Provider.\n"));
+
+        /* Identify ourselves to the data provider */
+        ret = dp_common_send_id(be_conn->conn,
+                                DATA_PROVIDER_VERSION,
+                                "PAC");
+        /* all fine */
+        if (ret == EOK) {
+            handle_requests_after_reconnect(be_conn->rctx);
+            return;
+        }
+    }
+
+    /* Failed to reconnect */
+    DEBUG(SSSDBG_FATAL_FAILURE, ("Could not reconnect to %s provider.\n",
+              be_conn->domain->name));
+
+    /* FIXME: kill the frontend and let the monitor restart it ? */
+    /* nss_shutdown(rctx); */
+}
+
+static void *idmap_talloc(size_t size, void *pvt)
+{
+    return talloc_size(pvt, size);
+}
+
+static void idmap_free(void *ptr, void *pvt)
+{
+    talloc_free(ptr);
+}
+
+int pac_process_init(TALLOC_CTX *mem_ctx,
+                     struct tevent_context *ev,
+                     struct confdb_ctx *cdb)
+{
+    struct sss_cmd_table *pac_cmds;
+    struct be_conn *iter;
+    struct pac_ctx *pac_ctx;
+    int ret, max_retries;
+    enum idmap_error_code err;
+
+    pac_ctx = talloc_zero(mem_ctx, struct pac_ctx);
+    if (!pac_ctx) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("fatal error initializing pac_ctx\n"));
+        return ENOMEM;
+    }
+
+    pac_cmds = get_pac_cmds();
+
+    ret = sss_process_init(pac_ctx, ev, cdb,
+                           pac_cmds,
+                           SSS_PAC_SOCKET_NAME, NULL,
+                           CONFDB_PAC_CONF_ENTRY,
+                           PAC_SBUS_SERVICE_NAME,
+                           PAC_SBUS_SERVICE_VERSION,
+                           &monitor_pac_interface,
+                           "PAC", &pac_dp_interface,
+                           &pac_ctx->rctx);
+    if (ret != EOK) {
+        return ret;
+    }
+    pac_ctx->rctx->pvt_ctx = pac_ctx;
+
+    /* Enable automatic reconnection to the Data Provider */
+    ret = confdb_get_int(pac_ctx->rctx->cdb,
+                         CONFDB_PAC_CONF_ENTRY,
+                         CONFDB_SERVICE_RECON_RETRIES,
+                         3, &max_retries);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("Failed to set up automatic reconnection\n"));
+        return ret;
+    }
+
+    for (iter = pac_ctx->rctx->be_conns; iter; iter = iter->next) {
+        sbus_reconnect_init(iter->conn, max_retries,
+                            pac_dp_reconnect_init, iter);
+    }
+
+    err = sss_idmap_init(idmap_talloc, pac_ctx, idmap_free,
+                         &pac_ctx->idmap_ctx);
+    if (err != IDMAP_SUCCESS) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("sss_idmap_init failed.\n"));
+        return EFAULT;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("PAC Initialization complete\n"));
+
+    return EOK;
+}
+
+int main(int argc, const char *argv[])
+{
+    int opt;
+    poptContext pc;
+    struct main_context *main_ctx;
+    int ret;
+
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_MAIN_OPTS
+        POPT_TABLEEND
+    };
+
+    /* Set debug level to invalid value so we can decide if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                  poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+
+    poptFreeContext(pc);
+
+    CONVERT_AND_SET_DEBUG_LEVEL(debug_level);
+
+    /* set up things like debug, signals, daemonization, etc... */
+    debug_log_file = "sssd_pac";
+
+    ret = server_setup("sssd[pac]", 0, CONFDB_PAC_CONF_ENTRY, &main_ctx);
+    if (ret != EOK) return 2;
+
+    ret = die_if_parent_died();
+    if (ret != EOK) {
+        /* This is not fatal, don't return */
+        DEBUG(SSSDBG_OP_FAILURE, ("Could not set up to exit when parent process does\n"));
+    }
+
+    ret = pac_process_init(main_ctx,
+                           main_ctx->event_ctx,
+                           main_ctx->confdb_ctx);
+    if (ret != EOK) return 3;
+
+    /* loop on main */
+    server_loop(main_ctx);
+
+    return 0;
+}
diff --git a/src/responder/pac/pacsrv.h b/src/responder/pac/pacsrv.h
new file mode 100644
index 0000000..0dfe7f9
--- /dev/null
+++ b/src/responder/pac/pacsrv.h
@@ -0,0 +1,52 @@
+/*
+   SSSD
+
+   PAC Responder, header file
+
+   Copyright (C) Sumit Bose <sbose at redhat.com> 2011
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __PACSRV_H__
+#define __PACSRV_H__
+
+#include <stdint.h>
+#include <sys/un.h>
+#include "config.h"
+#include "talloc.h"
+#include "tevent.h"
+#include "ldb.h"
+#include "dbus/dbus.h"
+#include "sbus/sssd_dbus.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/responder.h"
+#include "lib/idmap/sss_idmap.h"
+
+#define PAC_SBUS_SERVICE_VERSION 0x0001
+#define PAC_SBUS_SERVICE_NAME "pac"
+
+#define PAC_PACKET_MAX_RECV_SIZE 1024
+
+struct getent_ctx;
+
+struct pac_ctx {
+    struct resp_ctx *rctx;
+};
+
+int pac_cmd_execute(struct cli_ctx *cctx);
+
+struct sss_cmd_table *get_pac_cmds(void);
+
+#endif /* __PACSRV_H__ */
diff --git a/src/responder/pac/pacsrv_cmd.c b/src/responder/pac/pacsrv_cmd.c
new file mode 100644
index 0000000..892ef85
--- /dev/null
+++ b/src/responder/pac/pacsrv_cmd.c
@@ -0,0 +1,61 @@
+/*
+   SSSD
+
+   PAC Responder
+
+   Copyright (C) Sumit Bose <sbose at redhat.com> 2012
+                 Jan Zeleny <jzeleny 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 "util/util.h"
+#include "responder/pac/pacsrv.h"
+#include "confdb/confdb.h"
+#include "db/sysdb.h"
+
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+    static struct cli_protocol_version pac_cli_protocol_version[] = {
+        {1, "2011-04-12", "initial version"},
+        {0, NULL, NULL}
+    };
+
+    return pac_cli_protocol_version;
+}
+
+static struct sss_cmd_table pac_cmds[] = {
+    {SSS_GET_VERSION, sss_cmd_get_version},
+    {SSS_CLI_NULL, NULL}
+};
+
+struct sss_cmd_table *get_pac_cmds(void) {
+    return pac_cmds;
+}
+
+int pac_cmd_execute(struct cli_ctx *cctx)
+{
+    enum sss_cli_command cmd;
+    int i;
+
+    cmd = sss_packet_get_cmd(cctx->creq->in);
+
+    for (i = 0; pac_cmds[i].cmd != SSS_CLI_NULL; i++) {
+        if (cmd == pac_cmds[i].cmd) {
+            return pac_cmds[i].fn(cctx);
+        }
+    }
+
+    return EINVAL;
+}
-- 
1.7.7.6

-------------- next part --------------
From 95148f325c958944f8f20bc16bff3a6106c777d4 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 2/9] PAC responder: add some utility functions

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

diff --git a/Makefile.am b/Makefile.am
index ad79de3..63adbae 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -590,6 +590,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..f0ffea2 100644
--- a/src/responder/pac/pacsrv.h
+++ b/src/responder/pac/pacsrv.h
@@ -21,6 +21,11 @@
 
 #ifndef __PACSRV_H__
 #define __PACSRV_H__
+#include <stdbool.h>
+#include <util/data_blob.h>
+#include <ndr.h>
+#include <gen_ndr/krb5pac.h>
+#include <gen_ndr/ndr_krb5pac.h>
 
 #include <stdint.h>
 #include <sys/un.h>
@@ -33,6 +38,8 @@
 #include "responder/common/responder_packet.h"
 #include "responder/common/responder.h"
 #include "lib/idmap/sss_idmap.h"
+#include "util/sss_nss.h"
+#include "db/sysdb.h"
 
 #define PAC_SBUS_SERVICE_VERSION 0x0001
 #define PAC_SBUS_SERVICE_NAME "pac"
@@ -40,13 +47,61 @@
 #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);
+
+errno_t add_idmap_domain(struct sss_idmap_ctx *idmap_ctx,
+                         struct sysdb_ctx *sysdb,
+                         const char *domain_name,
+                         const char *dom_sid_str);
+
+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);
+
+errno_t get_my_domain_sid(struct pac_ctx *pac_ctx,
+                          struct sss_domain_info *dom,
+                          struct dom_sid **_sid);
+
+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);
+
+errno_t get_data_from_pac(TALLOC_CTX *mem_ctx,
+                          uint8_t *pac_blob, size_t pac_len,
+                          struct PAC_LOGON_INFO **_logon_info);
+
+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);
 #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..43d8412
--- /dev/null
+++ b/src/responder/pac/pacsrv_utils.c
@@ -0,0 +1,494 @@
+/*
+   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;
+}
+
+/**
+ * Find the Posix ID to a SID from the local IPA domain
+ */
+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;
+}
+
+/**
+ * Add a new remote domain and the corresponding ID range to the context of
+ * the libsss_idmap. Without this it is not possible to find the Posix UID for
+ * a user fo the remote domain.
+ */
+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;
+}
+
+/**
+ * Find the corresponding UID for a user from a remote domain based on the
+ * domain SID of the remote domain and the RID of the user.
+ */
+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;
+}
+
+/**
+ * Return information about the local domain from the main PAC responder
+ * context or try to read it from cache and store it in the context.
+ */
+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;
+}
+
+/**
+ * Check if a given SID belongs to a domain identified by the domain SID.
+ */
+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;
+}
+
+/**
+ * Find all Posix GIDs from a PAC by searching for group SIDs from the local
+ * domain and convert them to GIDs.
+ */
+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;
+}
+
+/**
+ * Extract the PAC logon data from an NDR blob.
+ */
+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;
+}
+
+/**
+ * Fill up the passwd struct with data from the PAC logon info
+ */
+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);
+        if (pwd->pw_gecos == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+            ret = ENOMEM;
+            goto done;
+        }
+    } else {
+        DEBUG(SSSDBG_OP_FAILURE, ("Missing full name in PAC, "
+                                  "gecos field will by empty.\n "));
+    }
+
+    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;
+}
-- 
1.7.7.6

-------------- next part --------------
From f3e93e8471d4681988f3c0e4fd86320212bf8070 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 18 Jun 2012 12:01:27 +0200
Subject: [PATCH 3/9] 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 |  471 +++++++++++++++++++++++++++++++++++++++-
 src/sss_client/sss_cli.h       |    4 +
 3 files changed, 475 insertions(+), 2 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..1d67657 100644
--- a/src/responder/pac/pacsrv_cmd.c
+++ b/src/responder/pac/pacsrv_cmd.c
@@ -23,7 +23,475 @@
 #include "util/util.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;
+}
+
+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)
 {
@@ -37,6 +505,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..483b809 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -217,6 +217,10 @@ enum sss_cli_command {
     SSS_CMD_RENEW            = 0x00F8, /**< Renew a credential with a limited
                                         * lifetime, e.g. a Kerberos Ticket
                                         * Granting Ticket (TGT) */
+
+/* PAC responder calls */
+    SSS_PAC_ADD_PAC_USER     = 0x0101,
+
 };
 
 /**
-- 
1.7.7.6

-------------- next part --------------
From ea50f5988e55e4baeecb8bee8273c475634741ab 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 4/9] 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 6e4ef97..d7f5db7 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -111,6 +111,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
@@ -327,6 +330,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
@@ -373,6 +377,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 5f2dd811b38bfef92f245556c700413d40dc434f 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 5/9] PAC responder: test suite

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

diff --git a/Makefile.am b/Makefile.am
index 63adbae..fd989c4 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 = \
@@ -987,6 +988,22 @@ 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) \
+    $(NDR_KRB5PAC_LIBS) \
+    $(SSSD_LIBS) \
+    libsss_idmap.la \
+    libsss_debug.la \
+    libsss_util.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 fbff2c5d0c2a0f82fe745fed495cefd3a5b5df6e 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 6/9] 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 483b809..077111c 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
@@ -484,6 +485,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 33325f6a6dac549951da8713fafc07788cded48b 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 7/9] 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 fd989c4..445492e 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
@@ -154,6 +157,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
 
@@ -1397,6 +1405,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 7f24209..0f5391b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -90,6 +90,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 2e98b84..a1da753 100644
--- a/src/conf_macros.m4
+++ b/src/conf_macros.m4
@@ -277,6 +277,20 @@ AC_DEFUN([WITH_DEFAULT_CCNAME_TEMPLATE],
     AC_DEFINE_UNQUOTED(DEFAULT_CCNAME_TEMPLATE, "$config_def_ccname_template", [The default value of krb5_ccname_template])
   ])
 
+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 96c572d5f8505582e36f7c61c2c8fd42dadcfb07 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 8/9] 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 445492e..9d32818 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -462,6 +462,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 4366a12..2a901bd 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 f694f4da0b7368d7a903e4fcff70cc5a25a049da Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 18 Jun 2012 12:32:58 +0200
Subject: [PATCH 9/9] Add range support to PAC responder

---
 src/responder/pac/pacsrv.h       |    7 +-
 src/responder/pac/pacsrv_cmd.c   |    6 +-
 src/responder/pac/pacsrv_utils.c |  172 +++++++++++++++++++++++++++++---------
 3 files changed, 140 insertions(+), 45 deletions(-)

diff --git a/src/responder/pac/pacsrv.h b/src/responder/pac/pacsrv.h
index f0ffea2..08b4461 100644
--- a/src/responder/pac/pacsrv.h
+++ b/src/responder/pac/pacsrv.h
@@ -85,9 +85,10 @@ errno_t domsid_rid_to_uid(struct pac_ctx *pac_ctx,
                           struct dom_sid2 *domsid, uint32_t rid,
                           uid_t *uid);
 
-errno_t get_my_domain_sid(struct pac_ctx *pac_ctx,
-                          struct sss_domain_info *dom,
-                          struct dom_sid **_sid);
+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);
 
 errno_t get_gids_from_pac(TALLOC_CTX *mem_ctx,
                           struct local_mapping_ranges *range_map,
diff --git a/src/responder/pac/pacsrv_cmd.c b/src/responder/pac/pacsrv_cmd.c
index 1d67657..0696cac 100644
--- a/src/responder/pac/pacsrv_cmd.c
+++ b/src/responder/pac/pacsrv_cmd.c
@@ -178,6 +178,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) {
@@ -185,13 +186,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) {
diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c
index 43d8412..d4df5b1 100644
--- a/src/responder/pac/pacsrv_utils.c
+++ b/src/responder/pac/pacsrv_utils.c
@@ -81,13 +81,48 @@ 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) {
@@ -95,7 +130,11 @@ errno_t add_idmap_domain(struct sss_idmap_ctx *idmap_ctx,
         return EFAULT;
     }
 
-    return EOK;
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
 }
 
 /**
@@ -171,9 +210,10 @@ done:
  * Return information about the local domain from the main PAC responder
  * context or try to read it from cache and store it in the context.
  */
-errno_t get_my_domain_sid(struct pac_ctx *pac_ctx,
-                          struct sss_domain_info *dom,
-                          struct dom_sid **_sid)
+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;
@@ -187,8 +227,12 @@ 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;
@@ -216,44 +260,92 @@ 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;
 
-- 
1.7.7.6



More information about the sssd-devel mailing list