[SSSD] [PATCHES] Allow password authentication for sub-domains

Sumit Bose sbose at redhat.com
Thu Oct 18 20:36:50 UTC 2012


Hi,

I'm sorry but this patch series is a bit longer than I originally
expected. While testing password authentication I came across some
issues and inconsistencies, some of them were also already seen by
Steeve during his testings. And since they may prevent successful
password authentication I put the patches in the series as well. I also
tried to make the patches as small and self-contained as possible to
make the review easier.

[PATCH 01/14] subdomain-id: Generate homedir only for users 
 - this is the only patch not strictly related to password
   authentication, it remove a useless operation for group requests

[PATCH 02/14] s2n: save user principal name of sub-domain user
 - if the user is looked up via the extdom exop and the IPA server the
   user principal name is generated and store in the cache so that it
   can be used for password authentication

[PATCH 03/14] pac responder: fix copy-and-paste error
 - this error prevent proper id-mapping in the PAC responder

[PATCH 04/14] sysdb: look for ranges in the parent tree
 - always use the right sub-tree in the cache to search for ranges

[PATCH 05/14] pac responder: use only lower case user name
 - since the extdom exop uses winbind to do the SID to name mapping and
   winbind only returns names lower case we have to lower case the names
   in the PAC responder as well to avoid inconsistent behaviour

[PATCH 06/14] pac responder: add user principal to cached user
 - same as patch 0002 but for the case the user is stored by the PAC
   responder

[PATCH 07/14] Accept be_req instead if be_ctx in krb5 auth provider
 - this patch was written by Jan some time ago and makes the same
   changes to the auth provider which  were already done for other
   provider to better handle the needed data for different sub-domains

[PATCH 08/14] sysdb: add sysdb_base_dn()
 - this just adds a helper which is needed by patch 9

[PATCH 09/14] check_ccache_files: search sub-domains as well
 - this fixes a todo in Jan patch, now the renewal task checks the
   ccache for sub-domain users as well

[PATCH 10/14] Detect subdomain request in IPA auth provider
 - checks if the auth request is for a subdomain and creates that
   creates the needed structs to access the cached data for the
   sub-domain

[PATCH 11/14] Add replacement for krb5_find_authdata()
 - add a replacement for a call only available in MIT Kerberos 1.10,
   currently it returns only and error. Is it worth to implement some
   basic functionality for platforms with older MIT versions?

[PATCH 12/14] krb5_auth: check if principal belongs to a different
 - add a flag if the principal used for authentication does not belong
   to our realm

[PATCH 13/14] krb5_auth: send different_realm flag to krb5_child
 - send the flag from patch 12 to the krb5-child

[PATCH 14/14] krb5_child: send PAC to PAC responder
 - look for a PAC and send it to the PAC responder if the principal does
   not belong to our realm

bye,
Sumit
-------------- next part --------------
From 548cfa41aff5484983dda5dd235fda869ee94b3b Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 9 Oct 2012 21:06:58 +0200
Subject: [PATCH 01/14] subdomain-id: Generate homedir only for users not
 groups

---
 src/providers/ipa/ipa_s2n_exop.c |   22 ++++++++++++----------
 1 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
index a96304d..004cdab 100644
--- a/src/providers/ipa/ipa_s2n_exop.c
+++ b/src/providers/ipa/ipa_s2n_exop.c
@@ -612,19 +612,21 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
     }
 
     now = time(NULL);
-    if (state->dom->subdomain_homedir) {
-        homedir =  expand_homedir_template(state, state->dom->subdomain_homedir,
-                                           attrs->a.user.pw_name,
-                                           attrs->a.user.pw_uid,
-                                           state->dom->name);
-        if (homedir == NULL) {
-            ret = ENOMEM;
-            goto done;
-        }
-    }
 
     switch (attrs->response_type) {
         case RESP_USER:
+            if (state->dom->subdomain_homedir) {
+                homedir =  expand_homedir_template(state,
+                                                   state->dom->subdomain_homedir,
+                                                   attrs->a.user.pw_name,
+                                                   attrs->a.user.pw_uid,
+                                                   state->dom->name);
+                if (homedir == NULL) {
+                    ret = ENOMEM;
+                    goto done;
+                }
+            }
+
             ret = sysdb_store_domuser(state->dom, attrs->a.user.pw_name, NULL,
                                       attrs->a.user.pw_uid,
                                       0, NULL, /* gecos */
-- 
1.7.7.6

-------------- next part --------------
From b274ab74756849d6db8c5d84e36a59f029335243 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 9 Oct 2012 21:16:41 +0200
Subject: [PATCH 02/14] s2n: save user principal name of sub-domain user

---
 src/providers/ipa/ipa_s2n_exop.c |   33 ++++++++++++++++++++++++++++++++-
 1 files changed, 32 insertions(+), 1 deletions(-)

diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
index 004cdab..a5e23fb 100644
--- a/src/providers/ipa/ipa_s2n_exop.c
+++ b/src/providers/ipa/ipa_s2n_exop.c
@@ -585,6 +585,9 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
     time_t now;
     uint64_t timeout = 10*60*60; /* FIXME: find a better timeout ! */
     const char *homedir = NULL;
+    char *uc_realm;
+    char *upn;
+    struct sysdb_attrs *user_attrs;
 
     ret = ipa_s2n_exop_recv(subreq, state, &result, &retoid, &retdata);
     talloc_zfree(subreq);
@@ -627,11 +630,39 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
                 }
             }
 
+            uc_realm = get_uppercase_realm(state, state->dom->name);
+            if (uc_realm == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE, ("get_uppercase_realm failed.\n"));
+                ret = ENOMEM;
+                goto done;
+            }
+
+            upn = talloc_asprintf(state, "%s@%s", attrs->a.user.pw_name,
+                                  uc_realm);
+            if (upn == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE, ("talloc_asprintf failed.\n"));
+                ret = ENOMEM;
+                goto done;
+            }
+
+            user_attrs = sysdb_new_attrs(state);
+            if (user_attrs == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE, ("sysdb_new_attrs failed.\n"));
+                ret = ENOMEM;
+                goto done;
+            }
+
+            ret = sysdb_attrs_add_string(user_attrs, SYSDB_UPN, upn);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_add_string failed.\n"));
+                goto done;
+            }
+
             ret = sysdb_store_domuser(state->dom, attrs->a.user.pw_name, NULL,
                                       attrs->a.user.pw_uid,
                                       0, NULL, /* gecos */
                                       homedir, NULL,
-                                      NULL, NULL, timeout, now);
+                                      user_attrs, NULL, timeout, now);
             break;
         case RESP_GROUP:
             ret = sysdb_store_domgroup(state->dom, attrs->a.group.gr_name,
-- 
1.7.7.6

-------------- next part --------------
From ba9c6e2576a3489151a963691df3907c4c3cf38e Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 15 Oct 2012 11:47:01 +0200
Subject: [PATCH 03/14] pac responder: fix copy-and-paste error

---
 src/responder/pac/pacsrv_utils.c |    7 -------
 1 files changed, 0 insertions(+), 7 deletions(-)

diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c
index 7d37587..48caae6 100644
--- a/src/responder/pac/pacsrv_utils.c
+++ b/src/responder/pac/pacsrv_utils.c
@@ -167,13 +167,6 @@ errno_t domsid_rid_to_uid(struct pac_ctx *pac_ctx,
         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,
-- 
1.7.7.6

-------------- next part --------------
From 3ff4601d4c02293935d457e1c5c62e686efd15b1 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 15 Oct 2012 10:31:59 +0200
Subject: [PATCH 04/14] sysdb: look for ranges in the parent tree

---
 src/db/sysdb_ranges.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/src/db/sysdb_ranges.c b/src/db/sysdb_ranges.c
index f100f5b..adbabf7 100644
--- a/src/db/sysdb_ranges.c
+++ b/src/db/sysdb_ranges.c
@@ -64,8 +64,11 @@ errno_t sysdb_get_ranges(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
         goto done;
     }
 
+    /* Ranges are stored in the tree of the parent domain */
     basedn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_RANGE_BASE,
-                            sysdb->domain->name);
+                            sysdb->domain->parent != NULL ?
+                                sysdb->domain->parent->name :
+                                sysdb->domain->name);
     if (basedn == NULL) {
         ret = EIO;
         goto done;
-- 
1.7.7.6

-------------- next part --------------
From f86e757fc4c303168bbcb67672df4ad331ffc36a Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 16 Oct 2012 18:00:03 +0200
Subject: [PATCH 05/14] pac responder: use only lower case user name

Since winbind can only return lower-cased user name the pac responder
must do the same.
---
 src/responder/pac/pacsrv_cmd.c   |   12 ++++++++++--
 src/responder/pac/pacsrv_utils.c |    8 +++++---
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/src/responder/pac/pacsrv_cmd.c b/src/responder/pac/pacsrv_cmd.c
index b7edf81..4cbf14b 100644
--- a/src/responder/pac/pacsrv_cmd.c
+++ b/src/responder/pac/pacsrv_cmd.c
@@ -113,13 +113,21 @@ static errno_t pac_add_pac_user(struct cli_ctx *cctx)
         goto done;
     }
 
-    pr_ctx->user_name = pr_ctx->logon_info->info3.base.account_name.string;
-    if (pr_ctx->user_name == NULL) {
+    if (pr_ctx->logon_info->info3.base.account_name.string == NULL) {
         ret = EINVAL;
         DEBUG(SSSDBG_FATAL_FAILURE, ("Missing account name in PAC.\n"));
         goto done;
     }
 
+    /* To be compatible with winbind based lookups we have to use lower case
+     * names only, effectively making the domain case-insenvitive. */
+    pr_ctx->user_name = sss_tc_utf8_str_tolower(pr_ctx,
+                            pr_ctx->logon_info->info3.base.account_name.string);
+    if (pr_ctx->user_name == NULL) {
+        ret = ENOMEM;
+        DEBUG(SSSDBG_FATAL_FAILURE, ("sss_tc_utf8_str_tolower failed.\n"));
+        goto done;
+    }
 
     pr_ctx->dom = responder_get_domain(pr_ctx, cctx->rctx, pr_ctx->domain_name);
     if (pr_ctx->dom == NULL) {
diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c
index 48caae6..4b55ef3 100644
--- a/src/responder/pac/pacsrv_utils.c
+++ b/src/responder/pac/pacsrv_utils.c
@@ -510,10 +510,12 @@ errno_t get_pwd_from_pac(TALLOC_CTX *mem_ctx,
     base_info = &logon_info->info3.base;
 
     if (base_info->account_name.size != 0) {
-        pwd->pw_name = talloc_strdup(pwd,
-                                     base_info->account_name.string);
+        /* To be compatible with winbind based lookups we have to use lower
+         * case names only, effectively making the domain case-insenvitive. */
+        pwd->pw_name = sss_tc_utf8_str_tolower(pwd,
+                                               base_info->account_name.string);
         if (pwd->pw_name == NULL) {
-            DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+            DEBUG(SSSDBG_OP_FAILURE, ("sss_tc_utf8_str_tolower failed.\n"));
             ret = ENOMEM;
             goto done;
         }
-- 
1.7.7.6

-------------- next part --------------
From f2cd0528a6fd877734e7014671bd1a73a01e5e99 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 15 Oct 2012 22:08:05 +0200
Subject: [PATCH 06/14] pac responder: add user principal to cached user
 object

---
 src/responder/pac/pacsrv.h       |    3 ++-
 src/responder/pac/pacsrv_cmd.c   |    5 +++--
 src/responder/pac/pacsrv_utils.c |   36 +++++++++++++++++++++++++++++++++++-
 3 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/src/responder/pac/pacsrv.h b/src/responder/pac/pacsrv.h
index 08b4461..e088e21 100644
--- a/src/responder/pac/pacsrv.h
+++ b/src/responder/pac/pacsrv.h
@@ -104,5 +104,6 @@ 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,
+                         struct sysdb_attrs **_attrs);
 #endif /* __PACSRV_H__ */
diff --git a/src/responder/pac/pacsrv_cmd.c b/src/responder/pac/pacsrv_cmd.c
index 4cbf14b..7777983 100644
--- a/src/responder/pac/pacsrv_cmd.c
+++ b/src/responder/pac/pacsrv_cmd.c
@@ -231,6 +231,7 @@ static errno_t save_pac_user(struct pac_req_ctx *pr_ctx)
     struct ldb_message *msg;
     struct passwd *pwd = NULL;
     TALLOC_CTX *tmp_ctx = NULL;
+    struct sysdb_attrs *user_attrs = NULL;
 
     sysdb = pr_ctx->dom->sysdb;
     if (sysdb == NULL) {
@@ -252,7 +253,7 @@ static errno_t save_pac_user(struct pac_req_ctx *pr_ctx)
         /* 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);
+                               pr_ctx->logon_info, &pwd, &user_attrs);
         if (ret != EOK) {
             DEBUG(SSSDBG_OP_FAILURE, ("get_pwd_from_pac failed.\n"));
             goto done;
@@ -261,7 +262,7 @@ static errno_t save_pac_user(struct pac_req_ctx *pr_ctx)
         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,
+                               pwd->pw_shell, user_attrs, NULL,
                                pr_ctx->dom->user_timeout, 0);
         if (ret != EOK) {
             DEBUG(SSSDBG_OP_FAILURE, ("sysdb_store_user failed [%d][%s].\n",
diff --git a/src/responder/pac/pacsrv_utils.c b/src/responder/pac/pacsrv_utils.c
index 4b55ef3..39fb09a 100644
--- a/src/responder/pac/pacsrv_utils.c
+++ b/src/responder/pac/pacsrv_utils.c
@@ -495,11 +495,15 @@ 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,
+                         struct sysdb_attrs **_attrs)
 {
     struct passwd *pwd = NULL;
+    struct sysdb_attrs *attrs = NULL;
     struct netr_SamBaseInfo *base_info;
     int ret;
+    char *uc_realm;
+    char *upn;
 
     pwd = talloc_zero(mem_ctx, struct passwd);
     if (pwd == NULL) {
@@ -565,7 +569,37 @@ errno_t get_pwd_from_pac(TALLOC_CTX *mem_ctx,
 
     pwd->pw_shell = NULL; /* Using default */
 
+    attrs = sysdb_new_attrs(mem_ctx);
+    if (attrs == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_new_attrs failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    uc_realm = get_uppercase_realm(mem_ctx, dom->name);
+    if (uc_realm == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("get_uppercase_realm failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    upn = talloc_asprintf(mem_ctx, "%s@%s", pwd->pw_name, uc_realm);
+    talloc_free(uc_realm);
+    if (upn == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("talloc_asprintf failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, upn);
+    talloc_free(upn);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_add_string failed.\n"));
+        goto done;
+    }
+
     *_pwd = pwd;
+    *_attrs = attrs;
 
     ret = EOK;
 
-- 
1.7.7.6

-------------- next part --------------
From aab2ed7eb50356f8eec9de8498b0953358ac93b0 Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny at redhat.com>
Date: Wed, 14 Mar 2012 06:25:44 -0400
Subject: [PATCH 07/14] Accept be_req instead if be_ctx in krb5 auth provider

---
 src/providers/ipa/ipa_auth.c                       |    4 +-
 src/providers/krb5/krb5_auth.c                     |   64 +++++++++++--------
 src/providers/krb5/krb5_auth.h                     |    2 +-
 .../krb5/krb5_delayed_online_authentication.c      |   26 +++++++-
 src/providers/krb5/krb5_renew_tgt.c                |   30 ++++++++-
 src/providers/krb5/krb5_wait_queue.c               |    2 +-
 6 files changed, 91 insertions(+), 37 deletions(-)

diff --git a/src/providers/ipa/ipa_auth.c b/src/providers/ipa/ipa_auth.c
index 2bd313b..9321565 100644
--- a/src/providers/ipa/ipa_auth.c
+++ b/src/providers/ipa/ipa_auth.c
@@ -233,7 +233,7 @@ void ipa_auth(struct be_req *be_req)
             goto fail;
     }
 
-    req = krb5_auth_send(state, state->ev, be_req->be_ctx, state->pd,
+    req = krb5_auth_send(state, state->ev, be_req, state->pd,
                          state->ipa_auth_ctx->krb5_auth_ctx);
     if (req == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, ("krb5_auth_send failed.\n"));
@@ -427,7 +427,7 @@ static void ipa_auth_ldap_done(struct tevent_req *req)
                               "trying Kerberos authentication again.\n"));
 
     req = krb5_auth_send(state, state->ev,
-                         state->be_req->be_ctx, state->pd,
+                         state->be_req, state->pd,
                          state->ipa_auth_ctx->krb5_auth_ctx);
     if (req == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, ("krb5_auth_send failed.\n"));
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index e244cea..1054030 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -279,7 +279,7 @@ static void krb5_pam_handler_cache_auth_step(struct tevent_req *req);
 
 struct krb5_auth_state {
     struct tevent_context *ev;
-    struct be_ctx *be_ctx;
+    struct be_req *be_req;
     struct pam_data *pd;
     struct krb5_ctx *krb5_ctx;
     struct krb5child_req *kr;
@@ -303,9 +303,14 @@ int krb5_auth_recv(struct tevent_req *req, int *pam_status, int *dp_err)
 static struct tevent_req *krb5_next_kdc(struct tevent_req *req);
 static struct tevent_req *krb5_next_kpasswd(struct tevent_req *req);
 
+/*
+ * Beware that the struct be_req *be_req can be only a stub
+ * with only a subset of members set. See krb5_renew_tgt.c
+ * and krb5_delayed_online_authentication.c for details.
+ */
 struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
                                   struct tevent_context *ev,
-                                  struct be_ctx *be_ctx,
+                                  struct be_req *be_req,
                                   struct pam_data *pd,
                                   struct krb5_ctx *krb5_ctx)
 {
@@ -326,7 +331,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
     }
 
     state->ev = ev;
-    state->be_ctx = be_ctx;
+    state->be_req = be_req;
     state->pd = pd;
     state->krb5_ctx = krb5_ctx;
     state->kr = NULL;
@@ -355,7 +360,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
             goto done;
     }
 
-    if (be_is_offline(be_ctx) &&
+    if (be_is_offline(be_req->be_ctx) &&
         (pd->cmd == SSS_PAM_CHAUTHTOK || pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
          pd->cmd == SSS_CMD_RENEW)) {
         DEBUG(9, ("Password changes and ticket renewal are not possible "
@@ -386,7 +391,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
     }
     kr = state->kr;
 
-    ret = sysdb_get_user_attr(state, be_ctx->sysdb, state->pd->user, attrs,
+    ret = sysdb_get_user_attr(state, be_req->sysdb, state->pd->user, attrs,
                               &res);
     if (ret) {
         DEBUG(5, ("sysdb search for upn of user [%s] failed.\n", pd->user));
@@ -521,7 +526,7 @@ static void krb5_resolve_kdc_done(struct tevent_req *subreq)
          * was found good, setting offline,
          * but we still have to call the child to setup
          * the ccache file if we are performing auth */
-        be_mark_offline(state->be_ctx);
+        be_mark_offline(state->be_req->be_ctx);
         kr->is_offline = true;
 
         if (kr->pd->cmd == SSS_PAM_CHAUTHTOK ||
@@ -587,7 +592,7 @@ static void krb5_find_ccache_step(struct tevent_req *req)
     struct tevent_req *subreq = NULL;
 
     if (!kr->is_offline) {
-        kr->is_offline = be_is_offline(state->be_ctx);
+        kr->is_offline = be_is_offline(state->be_req->be_ctx);
     }
 
     /* The ccache file should be (re)created if one of the following conditions
@@ -613,7 +618,7 @@ static void krb5_find_ccache_step(struct tevent_req *req)
                                           dp_opt_get_cstring(kr->krb5_ctx->opts,
                                                              KRB5_CCNAME_TMPL),
                                                 true,
-                                                state->be_ctx->domain->case_sensitive,
+                                                state->be_req->domain->case_sensitive,
                                                 &private_path);
             if (kr->ccname == NULL) {
                 DEBUG(1, ("expand_ccname_template failed.\n"));
@@ -750,7 +755,7 @@ static void krb5_child_done(struct tevent_req *subreq)
     }
 
     ret = parse_krb5_child_response(state, buf, len, pd,
-                        state->be_ctx->domain->pwd_expiration_warning,
+                        state->be_req->domain->pwd_expiration_warning,
                         &res);
     if (ret) {
         DEBUG(SSSDBG_OP_FAILURE, ("Could not parse child response [%d]: %s\n",
@@ -793,7 +798,7 @@ static void krb5_child_done(struct tevent_req *subreq)
                               "please remove it manually.\n", kr->old_ccname));
                 }
 
-                ret = krb5_delete_ccname(state, state->be_ctx->sysdb,
+                ret = krb5_delete_ccname(state, state->be_req->sysdb,
                                          pd->user, kr->old_ccname);
                 if (ret != EOK) {
                     DEBUG(1, ("krb5_delete_ccname failed.\n"));
@@ -821,7 +826,7 @@ static void krb5_child_done(struct tevent_req *subreq)
         (pd->cmd == SSS_PAM_CHAUTHTOK || pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM)) {
         /* ..which is unreachable by now.. */
         if (res->msg_status == PAM_AUTHTOK_LOCK_BUSY) {
-            be_fo_set_port_status(state->be_ctx,
+            be_fo_set_port_status(state->be_req->be_ctx,
                                   state->krb5_ctx->service->name,
                                   kr->kpasswd_srv, PORT_NOT_WORKING);
             /* ..try to resolve next kpasswd server */
@@ -830,7 +835,7 @@ static void krb5_child_done(struct tevent_req *subreq)
             }
             return;
         } else {
-            be_fo_set_port_status(state->be_ctx,
+            be_fo_set_port_status(state->be_req->be_ctx,
                                   state->krb5_ctx->service->name,
                                   kr->kpasswd_srv, PORT_WORKING);
         }
@@ -842,8 +847,9 @@ static void krb5_child_done(struct tevent_req *subreq)
     if (res->msg_status == PAM_AUTHINFO_UNAVAIL ||
         (kr->kpasswd_srv == NULL && res->msg_status == PAM_AUTHTOK_LOCK_BUSY)) {
         if (kr->srv != NULL) {
-            be_fo_set_port_status(state->be_ctx, state->krb5_ctx->service->name,
-                                  kr->srv, PORT_NOT_WORKING);
+            be_fo_set_port_status(state->be_req->be_ctx,
+                                  state->krb5_ctx->service->name, kr->srv,
+                                  PORT_NOT_WORKING);
             /* ..try to resolve next KDC */
             if (krb5_next_kdc(req) == NULL) {
                 tevent_req_error(req, ENOMEM);
@@ -851,8 +857,9 @@ static void krb5_child_done(struct tevent_req *subreq)
             return;
         }
     } else if (kr->srv != NULL) {
-        be_fo_set_port_status(state->be_ctx, state->krb5_ctx->service->name,
-                              kr->srv, PORT_WORKING);
+        be_fo_set_port_status(state->be_req->be_ctx,
+                              state->krb5_ctx->service->name, kr->srv,
+                              PORT_WORKING);
     }
 
     /* Now only a successful authentication or password change is left.
@@ -882,7 +889,7 @@ static void krb5_child_done(struct tevent_req *subreq)
                "please remove it manually.\n", kr->old_ccname));
     }
 
-    ret = krb5_save_ccname(state, state->be_ctx->sysdb,
+    ret = krb5_save_ccname(state, state->be_req->sysdb,
                            pd->user, store_ccname);
     if (ret) {
         DEBUG(1, ("krb5_save_ccname failed.\n"));
@@ -924,19 +931,22 @@ static struct tevent_req *krb5_next_server(struct tevent_req *req)
     switch (pd->cmd) {
         case SSS_PAM_AUTHENTICATE:
         case SSS_CMD_RENEW:
-            be_fo_set_port_status(state->be_ctx, state->krb5_ctx->service->name,
+            be_fo_set_port_status(state->be_req->be_ctx,
+                                  state->krb5_ctx->service->name,
                                   state->kr->srv, PORT_NOT_WORKING);
             next_req = krb5_next_kdc(req);
             break;
         case SSS_PAM_CHAUTHTOK:
         case SSS_PAM_CHAUTHTOK_PRELIM:
             if (state->kr->kpasswd_srv) {
-                be_fo_set_port_status(state->be_ctx, state->krb5_ctx->service->name,
+                be_fo_set_port_status(state->be_req->be_ctx,
+                                      state->krb5_ctx->service->name,
                                       state->kr->kpasswd_srv, PORT_NOT_WORKING);
                 next_req = krb5_next_kpasswd(req);
                 break;
             } else {
-                be_fo_set_port_status(state->be_ctx, state->krb5_ctx->service->name,
+                be_fo_set_port_status(state->be_req->be_ctx,
+                                      state->krb5_ctx->service->name,
                                       state->kr->srv, PORT_NOT_WORKING);
                 next_req = krb5_next_kdc(req);
                 break;
@@ -954,7 +964,7 @@ static struct tevent_req *krb5_next_kdc(struct tevent_req *req)
     struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
 
     next_req = be_resolve_server_send(state, state->ev,
-                                      state->be_ctx,
+                                      state->be_req->be_ctx,
                                       state->krb5_ctx->service->name,
                                       state->kr->srv == NULL ? true : false);
     if (next_req == NULL) {
@@ -972,7 +982,7 @@ static struct tevent_req *krb5_next_kpasswd(struct tevent_req *req)
     struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
 
     next_req = be_resolve_server_send(state, state->ev,
-                                state->be_ctx,
+                                state->be_req->be_ctx,
                                 state->krb5_ctx->kpasswd_service->name,
                                 state->kr->kpasswd_srv == NULL ? true : false);
     if (next_req == NULL) {
@@ -1005,7 +1015,7 @@ static void krb5_save_ccname_done(struct tevent_req *req)
         goto done;
     }
 
-    if (state->be_ctx->domain->cache_credentials == TRUE) {
+    if (state->be_req->domain->cache_credentials == TRUE) {
 
         /* password caching failures are not fatal errors */
         state->pam_status = PAM_SUCCESS;
@@ -1048,7 +1058,7 @@ static void krb5_save_ccname_done(struct tevent_req *req)
 
         talloc_set_destructor((TALLOC_CTX *)password, password_destructor);
 
-        ret = sysdb_cache_password(state->be_ctx->sysdb, pd->user, password);
+        ret = sysdb_cache_password(state->be_req->sysdb, pd->user, password);
         if (ret) {
             DEBUG(2, ("Failed to cache password, offline auth may not work."
                       " (%d)[%s]!?\n", ret, strerror(ret)));
@@ -1076,8 +1086,8 @@ static void krb5_pam_handler_cache_auth_step(struct tevent_req *req)
     struct krb5_ctx *krb5_ctx = state->kr->krb5_ctx;
     int ret;
 
-    ret = sysdb_cache_auth(state->be_ctx->sysdb, pd->user, pd->authtok,
-                           pd->authtok_size, state->be_ctx->cdb, true, NULL,
+    ret = sysdb_cache_auth(state->be_req->sysdb, pd->user, pd->authtok,
+                           pd->authtok_size, state->be_req->be_ctx->cdb, true, NULL,
                            NULL);
     if (ret != EOK) {
         DEBUG(1, ("Offline authentication failed\n"));
@@ -1140,7 +1150,7 @@ void krb5_pam_handler(struct be_req *be_req)
                           "running request immediately.\n", pd->user));
             }
 
-            req = krb5_auth_send(be_req, be_req->be_ctx->ev, be_req->be_ctx, pd,
+            req = krb5_auth_send(be_req, be_req->be_ctx->ev, be_req, pd,
                                  krb5_ctx);
             if (req == NULL) {
                 DEBUG(1, ("krb5_auth_send failed.\n"));
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index cc079ba..87583c5 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -63,7 +63,7 @@ void krb5_pam_handler(struct be_req *be_req);
 
 struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
                                   struct tevent_context *ev,
-                                  struct be_ctx *be_ctx,
+                                  struct be_req *be_req,
                                   struct pam_data *pd,
                                   struct krb5_ctx *krb5_ctx);
 int krb5_auth_recv(struct tevent_req *req, int *pam_status, int *dp_err);
diff --git a/src/providers/krb5/krb5_delayed_online_authentication.c b/src/providers/krb5/krb5_delayed_online_authentication.c
index d5dea3b..aacdc58 100644
--- a/src/providers/krb5/krb5_delayed_online_authentication.c
+++ b/src/providers/krb5/krb5_delayed_online_authentication.c
@@ -43,7 +43,7 @@ struct deferred_auth_ctx {
 };
 
 struct auth_data {
-    struct be_ctx *be_ctx;
+    struct be_req *breq;
     struct krb5_ctx *krb5_ctx;
     struct pam_data *pd;
 };
@@ -98,7 +98,7 @@ static void authenticate_user(struct tevent_context *ev,
     }
 #endif
 
-    req = krb5_auth_send(auth_data, ev, auth_data->be_ctx, auth_data->pd,
+    req = krb5_auth_send(auth_data, ev, auth_data->breq, auth_data->pd,
                          auth_data->krb5_ctx);
     if (req == NULL) {
         DEBUG(1, ("krb5_auth_send failed.\n"));
@@ -144,6 +144,7 @@ static errno_t authenticate_stored_users(
     hash_value_t value;
     struct pam_data *pd;
     struct auth_data *auth_data;
+    struct be_req *breq;
     struct tevent_timer *te;
 
     ret = get_uid_table(deferred_auth_ctx, &uid_table);
@@ -176,7 +177,26 @@ static errno_t authenticate_stored_users(
             } else {
                 auth_data->pd = talloc_steal(auth_data, pd);
                 auth_data->krb5_ctx = deferred_auth_ctx->krb5_ctx;
-                auth_data->be_ctx = deferred_auth_ctx->be_ctx;
+
+                /* Create a stub of be_req for krb5_auth_send() */
+                auth_data->breq = talloc_zero(auth_data,
+                                              struct be_req);
+                if (auth_data->breq == NULL) {
+                    DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero failed\n"));
+                    return ENOMEM;
+                }
+                breq = auth_data->breq;
+                breq->be_ctx = deferred_auth_ctx->be_ctx;
+                if (strcmp(pd->domain, breq->be_ctx->domain->name) != 0) {
+                    breq->domain = new_subdomain(breq, breq->be_ctx->domain,
+                                                 pd->domain, NULL, NULL);
+                    if (breq->domain == NULL) {
+                        return ENOMEM;
+                    }
+                } else {
+                    breq->domain = breq->be_ctx->domain;
+                }
+                breq->sysdb = breq->domain->sysdb;
 
                 te = tevent_add_timer(deferred_auth_ctx->ev,
                                       auth_data, tevent_timeval_current(),
diff --git a/src/providers/krb5/krb5_renew_tgt.c b/src/providers/krb5/krb5_renew_tgt.c
index 2ad5592..74375dc 100644
--- a/src/providers/krb5/krb5_renew_tgt.c
+++ b/src/providers/krb5/krb5_renew_tgt.c
@@ -48,7 +48,7 @@ struct renew_data {
 };
 
 struct auth_data {
-    struct be_ctx *be_ctx;
+    struct be_req *breq;
     struct krb5_ctx *krb5_ctx;
     struct pam_data *pd;
     struct renew_data *renew_data;
@@ -65,7 +65,7 @@ static void renew_tgt(struct tevent_context *ev, struct tevent_timer *te,
                                                   struct auth_data);
     struct tevent_req *req;
 
-    req = krb5_auth_send(auth_data, ev, auth_data->be_ctx, auth_data->pd,
+    req = krb5_auth_send(auth_data, ev, auth_data->breq, auth_data->pd,
                          auth_data->krb5_ctx);
     if (req == NULL) {
         DEBUG(1, ("krb5_auth_send failed.\n"));
@@ -156,6 +156,7 @@ static errno_t renew_all_tgts(struct renew_tgt_ctx *renew_tgt_ctx)
     size_t c;
     time_t now;
     struct auth_data *auth_data;
+    struct be_req *breq;
     struct renew_data *renew_data;
     struct tevent_timer *te;
 
@@ -189,9 +190,31 @@ static errno_t renew_all_tgts(struct renew_tgt_ctx *renew_tgt_ctx)
  * auth_data to allow a new renewal attempt. */
                 auth_data->pd = talloc_move(auth_data, &renew_data->pd);
                 auth_data->krb5_ctx = renew_tgt_ctx->krb5_ctx;
-                auth_data->be_ctx = renew_tgt_ctx->be_ctx;
                 auth_data->table = renew_tgt_ctx->tgt_table;
                 auth_data->renew_data = renew_data;
+
+                /* Create a stub of be_req for krb5_auth_send() */
+                auth_data->breq = talloc_zero(auth_data,
+                                              struct be_req);
+                if (auth_data->breq == NULL) {
+                    DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero failed\n"));
+                    return ENOMEM;
+                }
+                breq = auth_data->breq;
+                breq->be_ctx = renew_tgt_ctx->be_ctx;
+                if (strcmp(auth_data->pd->domain,
+                           breq->be_ctx->domain->name) != 0) {
+                    breq->domain = new_subdomain(breq, breq->be_ctx->domain,
+                                                 auth_data->pd->domain,
+                                                 NULL, NULL);
+                    if (breq->domain == NULL) {
+                        return ENOMEM;
+                    }
+                } else {
+                    breq->domain = breq->be_ctx->domain;
+                }
+                breq->sysdb = breq->domain->sysdb;
+
                 auth_data->key.type = entries[c].key.type;
                 auth_data->key.str = talloc_strdup(auth_data,
                                                    entries[c].key.str);
@@ -389,6 +412,7 @@ static errno_t check_ccache_files(struct renew_tgt_ctx *renew_tgt_ctx)
         return ENOMEM;
     }
 
+    /* TODO: this HAS to be fixed - the routine has to go through all subdomains */
     ret = sysdb_search_users(tmp_ctx, renew_tgt_ctx->be_ctx->sysdb,
                              ccache_filter, ccache_attrs, &msgs_count, &msgs);
     if (ret != EOK) {
diff --git a/src/providers/krb5/krb5_wait_queue.c b/src/providers/krb5/krb5_wait_queue.c
index 3863b1b..a4e6297 100644
--- a/src/providers/krb5/krb5_wait_queue.c
+++ b/src/providers/krb5/krb5_wait_queue.c
@@ -46,7 +46,7 @@ static void wait_queue_auth(struct tevent_context *ev, struct tevent_timer *te,
     struct tevent_req *req;
 
     req = krb5_auth_send(queue_entry->be_req, queue_entry->be_req->be_ctx->ev,
-                         queue_entry->be_req->be_ctx, queue_entry->pd,
+                         queue_entry->be_req, queue_entry->pd,
                          queue_entry->krb5_ctx);
     if (req == NULL) {
         DEBUG(1, ("krb5_auth_send failed.\n"));
-- 
1.7.7.6

-------------- next part --------------
From 881e92d534937df4a66e9101fe7a50f4742c0354 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 18 Oct 2012 17:40:48 +0200
Subject: [PATCH 08/14] sysdb: add sysdb_base_dn()

---
 src/db/sysdb.c |    4 ++++
 src/db/sysdb.h |    1 +
 2 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/src/db/sysdb.c b/src/db/sysdb.c
index 25ff675..9685163 100644
--- a/src/db/sysdb.c
+++ b/src/db/sysdb.c
@@ -283,6 +283,10 @@ struct ldb_dn *sysdb_domain_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx)
 {
     return ldb_dn_new_fmt(mem_ctx, sysdb->ldb, SYSDB_DOM_BASE, sysdb->domain->name);
 }
+struct ldb_dn *sysdb_base_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx)
+{
+    return ldb_dn_new(mem_ctx, sysdb->ldb, SYSDB_BASE);
+}
 
 struct ldb_context *sysdb_ctx_get_ldb(struct sysdb_ctx *sysdb)
 {
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 0ce6bea..8fe0e81 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -328,6 +328,7 @@ struct ldb_dn *sysdb_netgroup_base_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_c
 errno_t sysdb_group_dn_name(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx,
                             const char *dn_str, char **name);
 struct ldb_dn *sysdb_domain_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx);
+struct ldb_dn *sysdb_base_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx);
 struct ldb_dn *sysdb_custom_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx,
                                const char *object_name,
                                const char *subtree_name);
-- 
1.7.7.6

-------------- next part --------------
From e218e2f06f3c12739089b3817505509f5c8a14e6 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 18 Oct 2012 18:04:06 +0200
Subject: [PATCH 09/14] check_ccache_files: search sub-domains as well

---
 src/providers/krb5/krb5_renew_tgt.c |   19 ++++++++++++++-----
 1 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/providers/krb5/krb5_renew_tgt.c b/src/providers/krb5/krb5_renew_tgt.c
index 74375dc..0f59f8f 100644
--- a/src/providers/krb5/krb5_renew_tgt.c
+++ b/src/providers/krb5/krb5_renew_tgt.c
@@ -396,7 +396,8 @@ static errno_t check_ccache_files(struct renew_tgt_ctx *renew_tgt_ctx)
 {
     TALLOC_CTX *tmp_ctx;
     int ret;
-    const char *ccache_filter = "("SYSDB_CCACHE_FILE"=*)";
+    const char *ccache_filter = "(&("SYSDB_CCACHE_FILE"=*)" \
+                                  "("SYSDB_OBJECTCLASS"="SYSDB_USER_CLASS"))";
     const char *ccache_attrs[] = { SYSDB_CCACHE_FILE, SYSDB_UPN, SYSDB_NAME,
                                    NULL };
     size_t msgs_count = 0;
@@ -405,6 +406,7 @@ static errno_t check_ccache_files(struct renew_tgt_ctx *renew_tgt_ctx)
     const char *ccache_file;
     const char *upn;
     const char *user_name;
+    struct ldb_dn *base_dn;
 
     tmp_ctx = talloc_new(NULL);
     if (tmp_ctx == NULL) {
@@ -412,11 +414,18 @@ static errno_t check_ccache_files(struct renew_tgt_ctx *renew_tgt_ctx)
         return ENOMEM;
     }
 
-    /* TODO: this HAS to be fixed - the routine has to go through all subdomains */
-    ret = sysdb_search_users(tmp_ctx, renew_tgt_ctx->be_ctx->sysdb,
-                             ccache_filter, ccache_attrs, &msgs_count, &msgs);
+    base_dn = sysdb_base_dn(renew_tgt_ctx->be_ctx->sysdb, tmp_ctx);
+    if (base_dn == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_base_dn failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_search_entry(tmp_ctx, renew_tgt_ctx->be_ctx->sysdb, base_dn,
+                             LDB_SCOPE_SUBTREE, ccache_filter, ccache_attrs,
+                             &msgs_count, &msgs);
     if (ret != EOK) {
-        DEBUG(1, ("sysdb_search_users failed.\n"));
+        DEBUG(1, ("sysdb_search_entry failed.\n"));
         goto done;
     }
 
-- 
1.7.7.6

-------------- next part --------------
From 47b168e735a9e338bb0955aa97ac83f5706f5a45 Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny at redhat.com>
Date: Wed, 14 Mar 2012 06:26:47 -0400
Subject: [PATCH 10/14] Detect subdomain request in IPA auth provider

---
 src/providers/ipa/ipa_auth.c |   17 +++++++++++++----
 1 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/src/providers/ipa/ipa_auth.c b/src/providers/ipa/ipa_auth.c
index 9321565..469eb97 100644
--- a/src/providers/ipa/ipa_auth.c
+++ b/src/providers/ipa/ipa_auth.c
@@ -210,10 +210,19 @@ void ipa_auth(struct be_req *be_req)
 
     state->pd = pd;
 
-    if (strcasecmp(pd->domain, be_req->be_ctx->domain->name) != 0 &&
-        state->pd->cmd != SSS_PAM_ACCT_MGMT) {
-        DEBUG(SSSDBG_OP_FAILURE, ("This operation is not allowed for subdomains!\n"));
-        goto fail;
+    if (strcasecmp(pd->domain, be_req->be_ctx->domain->name) != 0) {
+        if (state->pd->cmd != SSS_PAM_AUTHENTICATE) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Invalid sub-domain request!\n"));
+            goto fail;
+        }
+
+        be_req->domain = new_subdomain(be_req, be_req->be_ctx->domain,
+                                       pd->domain, NULL, NULL);
+        if (be_req->domain == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("new_subdomain failed.\n"));
+            goto fail;
+        }
+        be_req->sysdb = be_req->domain->sysdb;
     }
 
     switch (state->pd->cmd) {
-- 
1.7.7.6

-------------- next part --------------
From dd9dea096c6ae28d819352be69da9462cacc51a6 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 15 Oct 2012 13:24:28 +0200
Subject: [PATCH 11/14] Add replacement for krb5_find_authdata()

---
 src/external/krb5.m4 |    1 +
 src/util/sss_krb5.c  |   14 ++++++++++++++
 src/util/sss_krb5.h  |    5 +++++
 3 files changed, 20 insertions(+), 0 deletions(-)

diff --git a/src/external/krb5.m4 b/src/external/krb5.m4
index cb420aa..5bc8711 100644
--- a/src/external/krb5.m4
+++ b/src/external/krb5.m4
@@ -60,6 +60,7 @@ AC_CHECK_FUNCS([krb5_get_init_creds_opt_alloc krb5_get_error_message \
                 krb5_cc_cache_match \
                 krb5_timestamp_to_sfstring \
                 krb5_set_trace_callback \
+                krb5_find_authdata \
                 krb5_cc_get_full_name])
 CFLAGS=$SAVE_CFLAGS
 LIBS=$SAVE_LIBS
diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c
index c36a137..cbee96e 100644
--- a/src/util/sss_krb5.c
+++ b/src/util/sss_krb5.c
@@ -990,3 +990,17 @@ sss_child_set_krb5_tracing(krb5_context ctx)
     return 0;
 }
 #endif /* HAVE_KRB5_SET_TRACE_CALLBACK */
+
+krb5_error_code sss_krb5_find_authdata(krb5_context context,
+                                       krb5_authdata *const *ticket_authdata,
+                                       krb5_authdata *const *ap_req_authdata,
+                                       krb5_authdatatype ad_type,
+                                       krb5_authdata ***results)
+{
+#ifdef HAVE_KRB5_FIND_AUTHDATA
+    return krb5_find_authdata(context, ticket_authdata, ap_req_authdata,
+                              ad_type, results);
+#else
+    return ENOTSUP;
+#endif
+}
diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h
index c16dab5..6a61f0e 100644
--- a/src/util/sss_krb5.h
+++ b/src/util/sss_krb5.h
@@ -170,4 +170,9 @@ typedef krb5_times sss_krb5_ticket_times;
 /* Redirect libkrb5 tracing towards our DEBUG statements */
 errno_t sss_child_set_krb5_tracing(krb5_context ctx);
 
+krb5_error_code sss_krb5_find_authdata(krb5_context context,
+                                       krb5_authdata *const *ticket_authdata,
+                                       krb5_authdata *const *ap_req_authdata,
+                                       krb5_authdatatype ad_type,
+                                       krb5_authdata ***results);
 #endif /* __SSS_KRB5_H__ */
-- 
1.7.7.6

-------------- next part --------------
From 69a0c85bbce0a5fc29312e3d25d9fb2fb6642a87 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 18 Oct 2012 16:14:40 +0200
Subject: [PATCH 12/14] krb5_auth: check if principal belongs to a different
 realm

---
 src/providers/krb5/krb5_auth.c   |    7 ++++++
 src/providers/krb5/krb5_auth.h   |    1 +
 src/providers/krb5/krb5_common.c |   31 ++++++++++++++++++++++++++
 src/providers/krb5/krb5_common.h |    4 +++
 src/tests/krb5_utils-tests.c     |   45 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 88 insertions(+), 0 deletions(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 1054030..530b9c7 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -422,6 +422,13 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
                 DEBUG(1, ("krb5_get_simple_upn failed.\n"));
                 goto done;
             }
+        } else {
+            ret = compare_principal_realm(kr->upn, realm,
+                                          &kr->upn_from_different_realm);
+            if (ret != 0) {
+                DEBUG(SSSDBG_OP_FAILURE, ("compare_principal_realm failed.\n"));
+                goto done;
+            }
         }
 
         kr->homedir = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_HOMEDIR,
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 87583c5..d6cf335 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -54,6 +54,7 @@ struct krb5child_req {
     bool active_ccache_present;
     bool valid_tgt_present;
     bool run_as_user;
+    bool upn_from_different_realm;
 };
 
 errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd,
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 006dac1..45f126f 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -881,3 +881,34 @@ errno_t krb5_get_simple_upn(TALLOC_CTX *mem_ctx, struct krb5_ctx *krb5_ctx,
     *_upn = upn;
     return EOK;
 }
+
+errno_t compare_principal_realm(const char *upn, const char *realm,
+                                bool *different_realm)
+{
+    size_t upn_len;
+    size_t realm_len;
+    char *at_sign;
+
+    if (upn == NULL || realm == NULL || different_realm == NULL) {
+        return EINVAL;
+    }
+
+    upn_len = strlen(upn);
+    realm_len = strlen(realm);
+    at_sign = strchr(upn, '@');
+
+    /* if coming from the same realm the upn must be at least the size of the
+     * realm plus 1 for the '@' char. */
+    if (upn_len == 0 || realm_len == 0 || upn_len <= realm_len + 1 ||
+        at_sign == NULL) {
+        return EINVAL;
+    }
+
+    if (strcmp(realm, at_sign + 1) == 0) {
+        *different_realm = false;
+    } else {
+        *different_realm = true;
+    }
+
+    return EOK;
+}
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index 51bd267..bc63bf9 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -177,6 +177,10 @@ errno_t remove_krb5_info_files(TALLOC_CTX *mem_ctx, const char *realm);
 errno_t krb5_get_simple_upn(TALLOC_CTX *mem_ctx, struct krb5_ctx *krb5_ctx,
                             const char *username, const char **_upn);
 
+errno_t compare_principal_realm(const char *upn, const char *realm,
+                                bool *different_realm);
+
+
 int sssm_krb5_auth_init(struct be_ctx *bectx,
                         struct bet_ops **ops,
                         void **pvt_auth_data);
diff --git a/src/tests/krb5_utils-tests.c b/src/tests/krb5_utils-tests.c
index 5fee454..636bcd4 100644
--- a/src/tests/krb5_utils-tests.c
+++ b/src/tests/krb5_utils-tests.c
@@ -673,6 +673,47 @@ START_TEST(test_no_substitution)
 }
 END_TEST
 
+START_TEST(test_compare_principal_realm)
+{
+    int ret;
+    bool different_realm;
+
+    ret = compare_principal_realm(NULL, "a", &different_realm);
+    fail_unless(ret == EINVAL, "NULL upn does not cause EINVAL.");
+
+    ret = compare_principal_realm("a", NULL, &different_realm);
+    fail_unless(ret == EINVAL, "NULL realm does not cause EINVAL.");
+
+    ret = compare_principal_realm("a", "b", NULL);
+    fail_unless(ret == EINVAL, "NULL different_realmbool " \
+                               "does not cause EINVAL.");
+
+    ret = compare_principal_realm("", "a", &different_realm);
+    fail_unless(ret == EINVAL, "Empty upn does not cause EINVAL.");
+
+    ret = compare_principal_realm("a", "", &different_realm);
+    fail_unless(ret == EINVAL, "Empty realm does not cause EINVAL.");
+
+    ret = compare_principal_realm("ABC", "ABC", &different_realm);
+    fail_unless(ret == EINVAL, "Short UPN does not cause EINVAL.");
+
+    ret = compare_principal_realm("userABC", "ABC", &different_realm);
+    fail_unless(ret == EINVAL, "Missing '@' does not cause EINVAL.");
+
+    fail_unless(different_realm == false, "Same realm but " \
+                                          "different_realm is not false.");
+    ret = compare_principal_realm("user at ABC", "ABC", &different_realm);
+    fail_unless(ret == EOK, "Failure with same realm");
+    fail_unless(different_realm == false, "Same realm but " \
+                                          "different_realm is not false.");
+
+    ret = compare_principal_realm("user at ABC", "DEF", &different_realm);
+    fail_unless(ret == EOK, "Failure with different realm");
+    fail_unless(different_realm == true, "Different realm but " \
+                                          "different_realm is not true.");
+}
+END_TEST
+
 Suite *krb5_utils_suite (void)
 {
     Suite *s = suite_create ("krb5_utils");
@@ -713,6 +754,10 @@ Suite *krb5_utils_suite (void)
     }
     suite_add_tcase (s, tc_create_dir);
 
+    TCase *tc_krb5_helpers = tcase_create("Helper functions");
+    tcase_add_test(tc_krb5_helpers, test_compare_principal_realm);
+    suite_add_tcase(s, tc_krb5_helpers);
+
     return s;
 }
 
-- 
1.7.7.6

-------------- next part --------------
From aa295bc315be8cc8a0dbb64cef8dc8d79047aba8 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 18 Oct 2012 16:25:57 +0200
Subject: [PATCH 13/14] krb5_auth: send different_realm flag to krb5_child

---
 src/providers/krb5/krb5_child.c         |    4 ++++
 src/providers/krb5/krb5_child_handler.c |    5 ++++-
 2 files changed, 8 insertions(+), 1 deletions(-)

diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index a92ba57..6e27df0 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -89,6 +89,7 @@ struct krb5_req {
     char *ccname;
     char *keytab;
     bool validate;
+    bool upn_from_different_realm;
     char *fast_ccname;
 
     const char *upn;
@@ -1359,6 +1360,7 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
     size_t p = 0;
     uint32_t len;
     uint32_t validate;
+    uint32_t different_realm;
 
     DEBUG(SSSDBG_TRACE_LIBS, ("total buffer size: [%d]\n", size));
 
@@ -1370,6 +1372,8 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
     SAFEALIGN_COPY_UINT32_CHECK(&validate, buf + p, size, &p);
     kr->validate = (validate == 0) ? false : true;
     SAFEALIGN_COPY_UINT32_CHECK(offline, buf + p, size, &p);
+    SAFEALIGN_COPY_UINT32_CHECK(&different_realm, buf + p, size, &p);
+    kr->upn_from_different_realm = (different_realm == 0) ? false : true;
     SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
     if ((p + len ) > size) return EINVAL;
     kr->upn = talloc_strndup(pd, (char *)(buf + p), len);
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 768d8c7..f0fe81b 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -92,6 +92,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
     size_t rp;
     const char *keytab;
     uint32_t validate;
+    uint32_t different_realm;
     size_t username_len = 0;
 
     keytab = dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_KEYTAB);
@@ -101,6 +102,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
     }
 
     validate = dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_VALIDATE) ? 1 : 0;
+    different_realm = kr->upn_from_different_realm ? 1 : 0;
 
     buf = talloc(kr, struct io_buffer);
     if (buf == NULL) {
@@ -108,7 +110,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
         return ENOMEM;
     }
 
-    buf->size = 6*sizeof(uint32_t) + strlen(kr->upn);
+    buf->size = 7*sizeof(uint32_t) + strlen(kr->upn);
 
     if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
         kr->pd->cmd == SSS_CMD_RENEW ||
@@ -140,6 +142,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
     SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->gid, &rp);
     SAFEALIGN_COPY_UINT32(&buf->data[rp], &validate, &rp);
     SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->is_offline, &rp);
+    SAFEALIGN_COPY_UINT32(&buf->data[rp], &different_realm, &rp);
 
     SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(kr->upn), &rp);
     safealign_memcpy(&buf->data[rp], kr->upn, strlen(kr->upn), &rp);
-- 
1.7.7.6

-------------- next part --------------
From 64dfb6e5e2626b4ea2f071f166db329aa78e455b Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 18 Oct 2012 16:34:35 +0200
Subject: [PATCH 14/14] krb5_child: send PAC to PAC responder

---
 Makefile.am                     |    6 +-
 src/providers/krb5/krb5_child.c |  140 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 143 insertions(+), 3 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index ee7e198..07dcbd2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1443,7 +1443,8 @@ krb5_child_SOURCES = \
     src/util/sss_krb5.c \
     src/util/atomic_io.c \
     src/util/util.c \
-    src/util/signal.c
+    src/util/signal.c \
+    src/sss_client/common.c
 krb5_child_CFLAGS = \
     $(AM_CFLAGS) \
     $(POPT_CFLAGS) \
@@ -1455,7 +1456,8 @@ krb5_child_LDADD = \
     $(TEVENT_LIBS) \
     $(POPT_LIBS) \
     $(DHASH_LIBS) \
-    $(KRB5_LIBS)
+    $(KRB5_LIBS) \
+    $(CLIENT_LIBS)
 
 ldap_child_SOURCES = \
     src/providers/ldap/ldap_child.c \
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 6e27df0..4bfbf4b 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -36,6 +36,7 @@
 #include "providers/dp_backend.h"
 #include "providers/krb5/krb5_auth.h"
 #include "providers/krb5/krb5_utils.h"
+#include "sss_cli.h"
 
 #define SSSD_KRB5_CHANGEPW_PRINCIPAL "kadmin/changepw"
 
@@ -100,6 +101,124 @@ struct krb5_req {
 static krb5_context krb5_error_ctx;
 #define KRB5_CHILD_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error)
 
+static krb5_error_code extract_and_send_pac(struct krb5_req *kr,
+                                            krb5_ccache ccache,
+                                            krb5_principal server_principal,
+                                            krb5_principal client_principal,
+                                            krb5_keytab keytab)
+{
+    krb5_error_code kerr;
+    krb5_creds mcred;
+    krb5_creds cred;
+    krb5_authdata **pac_authdata = NULL;
+    krb5_pac pac = NULL;
+    struct sss_cli_req_data sss_data;
+    int ret;
+    int errnop;
+    krb5_ticket *ticket = NULL;
+    krb5_keytab_entry entry;
+
+    memset(&entry, 0, sizeof(entry));
+    memset(&mcred, 0, sizeof(mcred));
+    memset(&cred, 0, sizeof(mcred));
+
+    mcred.server = server_principal;
+    mcred.client = client_principal;
+
+    kerr = krb5_cc_retrieve_cred(kr->ctx, ccache, 0, &mcred, &cred);
+    if (kerr != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("krb5_cc_retrieve_cred failed.\n"));
+        goto done;
+    }
+
+    kerr = krb5_decode_ticket(&cred.ticket, &ticket);
+    if (kerr != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("krb5_decode_ticket failed.\n"));
+        goto done;
+    }
+
+    kerr = krb5_server_decrypt_ticket_keytab(kr->ctx, keytab, ticket);
+    if (kerr != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("krb5_server_decrypt_ticket_keytab failed.\n"));
+        goto done;
+    }
+
+    kerr = sss_krb5_find_authdata(kr->ctx,
+                                  ticket->enc_part2->authorization_data, NULL,
+                                  KRB5_AUTHDATA_WIN2K_PAC, &pac_authdata);
+    if (kerr != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("krb5_find_authdata failed.\n"));
+        goto done;
+    }
+
+    if (pac_authdata == NULL || pac_authdata[0] == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("No PAC authdata available.\n"));
+        kerr = ENOENT;
+        goto done;
+    }
+
+    if (pac_authdata[1] != NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("More than one PAC autdata found.\n"));
+        kerr = EINVAL;
+        goto done;
+    }
+
+    kerr = krb5_pac_parse(kr->ctx, pac_authdata[0]->contents,
+                          pac_authdata[0]->length, &pac);
+    if (kerr != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("krb5_pac_parse failed.\n"));
+        goto done;
+    }
+
+    kerr = krb5_kt_get_entry(kr->ctx, keytab, ticket->server,
+                             ticket->enc_part.kvno, ticket->enc_part.enctype,
+                             &entry);
+    if (kerr != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("krb5_kt_get_entry failed.\n"));
+        goto done;
+    }
+
+    kerr = krb5_pac_verify(kr->ctx, pac, 0, NULL, &entry.key, NULL);
+    if (kerr != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("krb5_pac_verify failed.\n"));
+        goto done;
+    }
+
+    ret = unsetenv("_SSS_LOOPS");
+    if (ret != EOK) {
+        DEBUG(1, ("Failed to unset _SSS_LOOPS, "
+                  "sss_pac_make_request will most certainly fail.\n"));
+    }
+
+    sss_data.len = pac_authdata[0]->length;
+    sss_data.data = pac_authdata[0]->contents;
+
+    ret = sss_pac_make_request(SSS_PAC_ADD_PAC_USER, &sss_data,
+                               NULL, NULL, &errnop);
+    if (ret != NSS_STATUS_SUCCESS || errnop != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sss_pac_make_request failed [%d][%d].\n",
+                                  ret, errnop));
+        kerr = EIO;
+        goto done;
+    }
+
+    kerr = 0;
+
+done:
+    if (entry.magic != 0) {
+        krb5_free_keytab_entry_contents(kr->ctx, &entry);
+    }
+    krb5_pac_free(kr->ctx, pac);
+    krb5_free_authdata(kr->ctx, pac_authdata);
+    if (ticket != NULL) {
+        krb5_free_ticket(kr->ctx, ticket);
+    }
+
+    krb5_free_cred_contents(kr->ctx, &cred);
+
+    return kerr;
+}
+
 static void sss_krb5_expire_callback_func(krb5_context context, void *data,
                                           krb5_timestamp password_expiration,
                                           krb5_timestamp account_expiration,
@@ -696,6 +815,7 @@ static krb5_error_code validate_tgt(struct krb5_req *kr)
     krb5_verify_init_creds_opt opt;
     krb5_principal validation_princ = NULL;
     bool realm_entry_found = false;
+    krb5_ccache validation_ccache = NULL;
 
     memset(&keytab, 0, sizeof(keytab));
     kerr = krb5_kt_resolve(kr->ctx, kr->keytab, &keytab);
@@ -778,7 +898,7 @@ static krb5_error_code validate_tgt(struct krb5_req *kr)
 
     krb5_verify_init_creds_opt_init(&opt);
     kerr = krb5_verify_init_creds(kr->ctx, kr->creds, validation_princ, keytab,
-                                  NULL, &opt);
+                                  &validation_ccache, &opt);
 
     if (kerr == 0) {
         DEBUG(SSSDBG_TRACE_FUNC, ("TGT verified using key for [%s].\n",
@@ -786,9 +906,27 @@ static krb5_error_code validate_tgt(struct krb5_req *kr)
     } else {
         DEBUG(SSSDBG_CRIT_FAILURE ,("TGT failed verification using key " \
                                     "for [%s].\n", principal));
+        goto done;
+    }
+
+    /* Try to find and send the PAC to the PAC responder for principals which
+     * do not belong to our realm. Failures are not critical. */
+    if (kr->upn_from_different_realm) {
+        kerr = extract_and_send_pac(kr, validation_ccache, validation_princ,
+                                    kr->creds->client, keytab);
+        if (kerr != 0) {
+            DEBUG(SSSDBG_OP_FAILURE, ("extract_and_send_pac failed, group " \
+                                      "membership for user with principal [%s] " \
+                                      "might not be correct.\n", kr->name));
+            kerr = 0;
+        }
     }
 
 done:
+    if (validation_ccache != NULL) {
+        krb5_cc_destroy(kr->ctx, validation_ccache);
+    }
+
     if (krb5_kt_close(kr->ctx, keytab) != 0) {
         DEBUG(SSSDBG_MINOR_FAILURE, ("krb5_kt_close failed"));
     }
-- 
1.7.7.6



More information about the sssd-devel mailing list