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

Sumit Bose sbose at redhat.com
Thu Oct 25 08:43:03 UTC 2012


On Wed, Oct 24, 2012 at 11:21:06AM -0400, Simo Sorce wrote:
> On Wed, 2012-10-24 at 10:57 +0200, Sumit Bose wrote:
> > From 13ff428b862b90fcbe2eb697565854442d489042 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/18] s2n: save user principal name of sub-domain
> > user
> > 
> > If a sub-domain user of a IPA is looked up via the LDAP extdom
> > extended
> > operation we also add a generated user principal name to the cached
> > entry. This can be used e.g. for password authentication later on.
> > ---
> >  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;
> > +            }
> 
> If this operation is done after inspecting a MS-PAC shouldn't we use the
> principal that comes with it ?
> 
> For AD domains especially that is important because AD domains can use a
> UPN that differs from the actual username + uppercase(domain).
> 
> I am ok acking this as is for now as a short term solution but only if
> you open a ticket to track this and fix it in the next cycle.
> 
> Simo.

Jakub, Simo,

thank you for the review.

As you suggested I opened https://fedorahosted.org/sssd/ticket/1603 to
add the upn to the data which is send to the PAC responder.

Additionally I removed the second patch which adds a constructed upn to
the cache during the lookup of a sub-domain user. We discussed this on
irc can came to the conclusion that there should be only data in the
cache which have a fair chance to be correct.

To compensate this I enhanced patch 'Add new call find_or_guess_upn' to
handle sub-domain users as well.

In the  attaches new series the old second patch is remove and 'Add new
call find_or_guess_upn' and 'Use find_or_guess_upn() where needed' are
modified. All other patches are unchanged.

bye,
Sumit
> 
> 
> -- 
> Simo Sorce * Red Hat, Inc * New York
> 
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
-------------- next part --------------
From e34fc16bf0e936bef979b85ffb59874e095f4281 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/17] 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 4f248fd4b896ccae6f0107a9141707a7b54667eb 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 02/17] pac responder: fix copy-and-paste error

This error prevent proper id-mapping in the PAC responder.
---
 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 ebca46f6798f207545ee597a82fd9d680fb642bd 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 03/17] sysdb: look for ranges in the parent tree

Make sure the right sub-tree in the cache is used to search for ranges.
Sub-domain trees do not have range objects only the tree of the parent
domain.
---
 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 9f09f87323c757fbddeda8a87c512454876ddd03 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 04/17] pac responder: use only lower case user name

Since winbind can only return lower-cased user name the pac responder
must do the same to avoid inconsistent behaviour.
---
 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 0eaf229cc14bedc78abce1f894e5127a310fc5e8 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 05/17] pac responder: add user principal to cached user
 object

The principal name for the user is generated with the user name and the
domain from the PAC. It is stored in the cache so that if e.g. can be
used by password authentication.
---
 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 9d8b384d0b258d96ffc1cf8fd16ef883ca05871e Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Fri, 19 Oct 2012 18:28:41 +0200
Subject: [PATCH 06/17] krb5_auth_send: check for sub-domains

If there is an authentication request for a user from a sub-domain a
temporary sysdb context is generated to allow lookups in the
corresponding sub-tree in the cache.
---
 src/providers/ipa/ipa_auth.c    |    6 ------
 src/providers/krb5/krb5_auth.c  |   20 +++++++++++++++-----
 src/providers/krb5/krb5_utils.c |   19 +++++++++++++++++++
 src/providers/krb5/krb5_utils.h |    3 +++
 4 files changed, 37 insertions(+), 11 deletions(-)

diff --git a/src/providers/ipa/ipa_auth.c b/src/providers/ipa/ipa_auth.c
index 2bd313b..eb62f02 100644
--- a/src/providers/ipa/ipa_auth.c
+++ b/src/providers/ipa/ipa_auth.c
@@ -210,12 +210,6 @@ 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;
-    }
-
     switch (state->pd->cmd) {
         case SSS_PAM_AUTHENTICATE:
             state->ipa_auth_ctx = talloc_get_type(
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index e244cea..c98535b 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -281,6 +281,7 @@ struct krb5_auth_state {
     struct tevent_context *ev;
     struct be_ctx *be_ctx;
     struct pam_data *pd;
+    struct sysdb_ctx *sysdb;
     struct krb5_ctx *krb5_ctx;
     struct krb5child_req *kr;
 
@@ -318,6 +319,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
     struct tevent_req *req;
     struct tevent_req *subreq;
     int ret;
+    struct sss_domain_info *dom;
 
     req = tevent_req_create(mem_ctx, &state, struct krb5_auth_state);
     if (req == NULL) {
@@ -333,6 +335,14 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
     state->pam_status = PAM_SYSTEM_ERR;
     state->dp_err = DP_ERR_FATAL;
 
+    ret = get_domain_or_subdomain(state, be_ctx, pd->domain, &dom);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("get_domain_or_subdomain failed.\n"));
+        goto done;
+    }
+
+    state->sysdb = dom->sysdb;
+
     switch (pd->cmd) {
         case SSS_PAM_AUTHENTICATE:
         case SSS_CMD_RENEW:
@@ -386,7 +396,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, state->sysdb, state->pd->user, attrs,
                               &res);
     if (ret) {
         DEBUG(5, ("sysdb search for upn of user [%s] failed.\n", pd->user));
@@ -793,7 +803,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->sysdb,
                                          pd->user, kr->old_ccname);
                 if (ret != EOK) {
                     DEBUG(1, ("krb5_delete_ccname failed.\n"));
@@ -882,7 +892,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->sysdb,
                            pd->user, store_ccname);
     if (ret) {
         DEBUG(1, ("krb5_save_ccname failed.\n"));
@@ -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->sysdb, pd->user, password);
         if (ret) {
             DEBUG(2, ("Failed to cache password, offline auth may not work."
                       " (%d)[%s]!?\n", ret, strerror(ret)));
@@ -1076,7 +1086,7 @@ 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,
+    ret = sysdb_cache_auth(state->sysdb, pd->user, pd->authtok,
                            pd->authtok_size, state->be_ctx->cdb, true, NULL,
                            NULL);
     if (ret != EOK) {
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index 73a711d..7a68b0f 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -1031,3 +1031,22 @@ struct sss_krb5_cc_be dir_cc = {
 };
 
 #endif /* HAVE_KRB5_DIRCACHE */
+
+errno_t get_domain_or_subdomain(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx,
+                                char *domain_name,
+                                struct sss_domain_info **dom)
+{
+
+    if (domain_name != NULL &&
+        strcasecmp(domain_name, be_ctx->domain->name) != 0) {
+        *dom = new_subdomain(mem_ctx, be_ctx->domain, domain_name, NULL, NULL);
+        if (*dom == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("new_subdomain failed.\n"));
+            return ENOMEM;
+        }
+    } else {
+        *dom = be_ctx->domain;
+    }
+
+    return EOK;
+}
diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h
index 00dfc85..43fe77b 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -83,4 +83,7 @@ errno_t cc_dir_create(const char *location, pcre *illegal_re,
 
 #endif /* HAVE_KRB5_DIRCACHE */
 
+errno_t get_domain_or_subdomain(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx,
+                                char *domain_name,
+                                struct sss_domain_info **dom);
 #endif /* __KRB5_UTILS_H__ */
-- 
1.7.7.6

-------------- next part --------------
From b4c19055908d56743f5e35b34b342be6bf096df7 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 07/17] sysdb: add sysdb_base_dn()

Add a help function which returns the ldb_dn object for the base dn of
the cache.
---
 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 939e4420a929e7f4497e761f1cb161e4ea1ed30a 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 08/17] check_ccache_files: search sub-domains as well

If sssd is configured to renew Kerberos tickets automatically ticket of
sub-domain uses should be renewed as well.
---
 src/providers/krb5/krb5_renew_tgt.c |   18 ++++++++++++++----
 1 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/src/providers/krb5/krb5_renew_tgt.c b/src/providers/krb5/krb5_renew_tgt.c
index 2ad5592..217e03d 100644
--- a/src/providers/krb5/krb5_renew_tgt.c
+++ b/src/providers/krb5/krb5_renew_tgt.c
@@ -373,7 +373,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;
@@ -382,6 +383,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) {
@@ -389,10 +391,18 @@ static errno_t check_ccache_files(struct renew_tgt_ctx *renew_tgt_ctx)
         return ENOMEM;
     }
 
-    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 ab67ff1a62b8fa5a74e4790dd11728726cc9f6a0 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 09/17] Add replacement for krb5_find_authdata()

krb5_find_authdata() is only available in MIT Kerberos 1.10 or higher.
To allow sssd to be compiled on platform with lower version of MIT
Kerberos a replacement call is added. Please note that on those
platform the replacement call will only return an error. If the
krb5_find_authdata functionality is really needed on those platform it
must be implemented by a different patch.
---
 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 66db9b8c57dfdd2b55f78195ac1edbf87af4d3e3 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 10/17] krb5_auth: check if principal belongs to a different
 realm

Add a flag if the principal used for authentication does not belong
to our realm. This can be used to act differently for users from other
realms.
---
 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 c98535b..72f0711 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -427,6 +427,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 cc079ba..a23b8b4 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 d503fa441c097ad4045995205eb0a5c7ec6b4900 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 11/17] krb5_auth: send different_realm flag to krb5_child

The different_realm flag which was set by the responder is send to the
krb5_child so that it can act differently on users from other realms. To
avoid code duplication and inconsistent behaviour the krb5_child will
not set the flag on its own but use the one from the provider.
---
 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 5ea21aee2028c37f8195f147d46548fdb84ba429 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 12/17] krb5_child: send PAC to PAC responder

If the authenticated user comes from a different realm the service
ticket which was returned during the validation of the TGT is used to
extract the PAC which is send to the pac responder for evaluation.
---
 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

-------------- next part --------------
From aedd2c6cf0b8df40661daaef1bc67e06a2119efb Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 23 Oct 2012 20:41:15 +0200
Subject: [PATCH 13/17] krb5_mod_ccname: replace wrong memory context

---
 src/providers/krb5/krb5_auth.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 72f0711..98dc8d8 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -145,7 +145,7 @@ static int krb5_mod_ccname(TALLOC_CTX *mem_ctx,
         return ENOMEM;
     }
 
-    attrs = sysdb_new_attrs(mem_ctx);
+    attrs = sysdb_new_attrs(tmpctx);
     if (!attrs) {
         ret = ENOMEM;
         goto done;
-- 
1.7.7.6

-------------- next part --------------
From 2e0a8bff33e2011072f7df4b815aa990ea030c57 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 23 Oct 2012 21:30:17 +0200
Subject: [PATCH 14/17] krb5_child: send back the client principal

In general Kerberos is case sensitive but the KDC of Active Directory
typically handles request case in-sensitive. In the case where we guess
a user principal by combining the user name and the realm and are not
sure about the cases of the letters used in the user name we might get a
valid ticket from the AD KDC but are not able to access it with the
Kerberos client library because we assume a wrong case.

The client principal in the returned credentials will always have the
right cases. To be able to update the cache user principal name the
krb5_child will return the principal for further processing.
---
 src/providers/data_provider.h           |    1 +
 src/providers/krb5/krb5_auth.h          |    1 +
 src/providers/krb5/krb5_child.c         |   29 ++++++++++++++++++++++++-----
 src/providers/krb5/krb5_child_handler.c |   16 ++++++++++++++++
 4 files changed, 42 insertions(+), 5 deletions(-)

diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h
index b783081..d49fcd5 100644
--- a/src/providers/data_provider.h
+++ b/src/providers/data_provider.h
@@ -195,6 +195,7 @@ struct pam_data {
 #define SSS_PROXY_INFO 0x10000000
 
 #define SSS_KRB5_INFO_TGT_LIFETIME (SSS_SERVER_INFO|SSS_KRB5_INFO|0x01)
+#define SSS_KRB5_INFO_UPN (SSS_SERVER_INFO|SSS_KRB5_INFO|0x02)
 
 errno_t copy_pam_data(TALLOC_CTX *mem_ctx, struct pam_data *old_pd,
                       struct pam_data **new_pd);
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index a23b8b4..bf49f7c 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -80,6 +80,7 @@ struct krb5_child_response {
     int32_t msg_status;
     struct tgt_times tgtt;
     char *ccname;
+    char *correct_upn;
 };
 
 errno_t
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 4bfbf4b..f409b4d 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -785,10 +785,13 @@ static errno_t sendresponse(int fd, krb5_error_code kerr, int pam_status,
     return EOK;
 }
 
-static errno_t add_ticket_times_to_response(struct krb5_req *kr)
+static errno_t add_ticket_times_and_upn_to_response(struct krb5_req *kr)
 {
     int ret;
     int64_t t[4];
+    krb5_error_code kerr;
+    char *upn;
+    unsigned int upn_len;
 
     t[0] = (int64_t) kr->creds->times.authtime;
     t[1] = (int64_t) kr->creds->times.starttime;
@@ -799,8 +802,24 @@ static errno_t add_ticket_times_to_response(struct krb5_req *kr)
                            4*sizeof(int64_t), (uint8_t *) t);
     if (ret != EOK) {
         DEBUG(1, ("pack_response_packet failed.\n"));
+        goto done;
+    }
+
+    kerr = krb5_unparse_name_ext(kr->ctx, kr->creds->client, &upn, &upn_len);
+    if (kerr != 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("krb5_unparse_name failed.\n"));
+        goto done;
+    }
+
+    ret = pam_add_response(kr->pd, SSS_KRB5_INFO_UPN, upn_len,
+                           (uint8_t *) upn);
+    krb5_free_unparsed_name(kr->ctx, upn);
+    if (ret != EOK) {
+        DEBUG(1, ("pack_response_packet failed.\n"));
+        goto done;
     }
 
+done:
     return ret;
 }
 
@@ -1054,9 +1073,9 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
         goto done;
     }
 
-    ret = add_ticket_times_to_response(kr);
+    ret = add_ticket_times_and_upn_to_response(kr);
     if (ret != EOK) {
-        DEBUG(1, ("add_ticket_times_to_response failed.\n"));
+        DEBUG(1, ("add_ticket_times_and_upn_to_response failed.\n"));
     }
 
     kerr = 0;
@@ -1447,9 +1466,9 @@ static errno_t renew_tgt_child(int fd, struct krb5_req *kr)
         goto done;
     }
 
-    ret = add_ticket_times_to_response(kr);
+    ret = add_ticket_times_and_upn_to_response(kr);
     if (ret != EOK) {
-        DEBUG(1, ("add_ticket_times_to_response failed.\n"));
+        DEBUG(1, ("add_ticket_times_and_upn_to_response failed.\n"));
     }
 
     status = PAM_SUCCESS;
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index f0fe81b..e792db3 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -441,6 +441,8 @@ parse_krb5_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, ssize_t len,
     uint32_t *expiration;
     uint32_t *msg_subtype;
     struct krb5_child_response *res;
+    const char *upn = NULL;
+    size_t upn_len;
 
     if ((size_t) len < sizeof(int32_t)) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("message too short.\n"));
@@ -505,6 +507,11 @@ parse_krb5_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, ssize_t len,
                   tgtt.authtime, tgtt.starttime, tgtt.endtime, tgtt.renew_till));
         }
 
+        if (msg_type == SSS_KRB5_INFO_UPN) {
+            upn = (char *) buf + p;
+            upn_len = msg_len;
+        }
+
         if (msg_type == SSS_PAM_USER_INFO) {
             msg_subtype = (uint32_t *)&buf[p];
             if (*msg_subtype == SSS_PAM_USER_INFO_EXPIRE_WARN)
@@ -549,6 +556,15 @@ parse_krb5_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, ssize_t len,
         }
     }
 
+    if (upn != NULL) {
+        res->correct_upn = talloc_strndup(res, upn, upn_len);
+        if (res->correct_upn == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strndup failed.\n"));
+            talloc_free(res);
+            return ENOMEM;
+        }
+    }
+
     *_res = res;
     return EOK;
 }
-- 
1.7.7.6

-------------- next part --------------
From 6acebfb85cad07918dc2882afdc50afd0d47346a Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Wed, 24 Oct 2012 09:33:23 +0200
Subject: [PATCH 15/17] Add new call find_or_guess_upn()

With the current approach the upn was either a pointer to a const string
in a ldb_message or a string created with the help of talloc. This new
function always makes it a talloc'ed value.

Additionally krb5_get_simple_upn() is enhanced to handle sub-domains as
well.
---
 src/providers/krb5/krb5_common.c |   27 ++++++++++++++++++++-------
 src/providers/krb5/krb5_common.h |    3 ++-
 src/providers/krb5/krb5_utils.c  |   27 +++++++++++++++++++++++++++
 src/providers/krb5/krb5_utils.h  |    5 +++++
 4 files changed, 54 insertions(+), 8 deletions(-)

diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 45f126f..ee3d725 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -858,19 +858,32 @@ errno_t krb5_install_sigterm_handler(struct tevent_context *ev,
 }
 
 errno_t krb5_get_simple_upn(TALLOC_CTX *mem_ctx, struct krb5_ctx *krb5_ctx,
-                            const char *username, const char **_upn)
+                            const char *domain_name, const char *username,
+                            const char *user_dom, char **_upn)
 {
-    const char *realm;
+    const char *realm = NULL;
+    char *uc_dom = NULL;
     char *upn;
 
-    realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
-    if (realm == NULL) {
-        DEBUG(1, ("Missing Kerberos realm.\n"));
-        return ENOENT;
+    if (user_dom != NULL && domain_name != NULL &&
+        strcasecmp(domain_name,user_dom) != 0) {
+        uc_dom = get_uppercase_realm(mem_ctx, user_dom);
+        if (uc_dom == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("get_uppercase_realm failed.\n"));
+            return ENOMEM;
+        }
+    } else {
+        realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
+        if (realm == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Missing Kerberos realm.\n"));
+            return ENOENT;
+        }
     }
 
     /* NOTE: this is a hack, works only in some environments */
-    upn = talloc_asprintf(mem_ctx, "%s@%s",  username, realm);
+    upn = talloc_asprintf(mem_ctx, "%s@%s",  username,
+                                             realm != NULL ? realm : uc_dom);
+    talloc_free(uc_dom);
     if (upn == NULL) {
         DEBUG(1, ("talloc_asprintf failed.\n"));
         return ENOMEM;
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index bc63bf9..1303529 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -175,7 +175,8 @@ errno_t write_krb5info_file(const char *realm, const char *kdc,
 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);
+                            const char *domain_name, const char *username,
+                            const char *user_dom, char **_upn);
 
 errno_t compare_principal_realm(const char *upn, const char *realm,
                                 bool *different_realm);
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index 7a68b0f..9837616 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -30,6 +30,33 @@
 #include "src/util/find_uid.h"
 #include "util/util.h"
 
+errno_t find_or_guess_upn(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+                          struct krb5_ctx *krb5_ctx,
+                          const char *domain_name, const char *user,
+                          const char *user_dom, char **_upn)
+{
+    const char *upn;
+    int ret;
+
+    upn = ldb_msg_find_attr_as_string(msg, SYSDB_UPN, NULL);
+    if (upn == NULL) {
+        ret = krb5_get_simple_upn(mem_ctx, krb5_ctx, domain_name, user,
+                                  user_dom, _upn);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("krb5_get_simple_upn failed.\n"));
+            return ret;
+        }
+    } else {
+        *_upn = talloc_strdup(mem_ctx, upn);
+        if (*_upn == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+            return ENOMEM;
+        }
+    }
+
+    return EOK;
+}
+
 char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
                              const char *template, bool file_mode,
                              bool case_sensitive, bool *private_path)
diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h
index 43fe77b..2848545 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -32,6 +32,11 @@
 #include "providers/krb5/krb5_auth.h"
 #include "providers/data_provider.h"
 
+errno_t find_or_guess_upn(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+                          struct krb5_ctx *krb5_ctx,
+                          const char *domain_name, const char *user,
+                          const char *user_dom, char **_upn);
+
 /* Operations on a credential cache */
 typedef errno_t (*cc_be_create_fn)(const char *location, pcre *illegal_re,
                                    uid_t uid, gid_t gid, bool private_path);
-- 
1.7.7.6

-------------- next part --------------
From 53749705079760feddd3d00213cc3b26be363e47 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Wed, 24 Oct 2012 09:47:21 +0200
Subject: [PATCH 16/17] Use find_or_guess_upn() where needed

---
 src/providers/krb5/krb5_access.c    |   16 ++++++--------
 src/providers/krb5/krb5_auth.c      |   27 ++++++++++++------------
 src/providers/krb5/krb5_auth.h      |    2 +-
 src/providers/krb5/krb5_renew_tgt.c |   38 +++++++++++++++++++++++++---------
 src/tests/krb5_child-test.c         |    3 +-
 src/tests/krb5_utils-tests.c        |    2 +-
 6 files changed, 52 insertions(+), 36 deletions(-)

diff --git a/src/providers/krb5/krb5_access.c b/src/providers/krb5/krb5_access.c
index afa3a89..2561280 100644
--- a/src/providers/krb5/krb5_access.c
+++ b/src/providers/krb5/krb5_access.c
@@ -25,6 +25,7 @@
 #include "util/util.h"
 #include "providers/krb5/krb5_auth.h"
 #include "providers/krb5/krb5_common.h"
+#include "providers/krb5/krb5_utils.h"
 
 struct krb5_access_state {
     struct tevent_context *ev;
@@ -101,15 +102,12 @@ struct tevent_req *krb5_access_send(TALLOC_CTX *mem_ctx,
         goto done;
         break;
     case 1:
-        state->kr->upn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_UPN,
-                                                     NULL);
-        if (state->kr->upn == NULL) {
-            ret = krb5_get_simple_upn(state, krb5_ctx, pd->user,
-                                      &state->kr->upn);
-            if (ret != EOK) {
-                DEBUG(1, ("krb5_get_simple_upn failed.\n"));
-                goto done;
-            }
+        ret = find_or_guess_upn(state, res->msgs[0], krb5_ctx,
+                                be_ctx->domain->name, pd->user, pd->domain,
+                                &state->kr->upn);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("find_or_guess_upn failed.\n"));
+            goto done;
         }
 
         state->kr->uid = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_UIDNUM,
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 98dc8d8..c1f9f14 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -420,20 +420,19 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
         break;
 
     case 1:
-        kr->upn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_UPN, NULL);
-        if (kr->upn == NULL) {
-            ret = krb5_get_simple_upn(state, krb5_ctx, pd->user, &kr->upn);
-            if (ret != EOK) {
-                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;
-            }
+        ret = find_or_guess_upn(state, res->msgs[0], krb5_ctx,
+                                be_ctx->domain->name, pd->user, pd->domain,
+                                &kr->upn);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("find_or_guess_upn failed.\n"));
+            goto done;
+        }
+
+        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 bf49f7c..9133472 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -45,7 +45,7 @@ struct krb5child_req {
     const char *ccname;
     const char *old_ccname;
     const char *homedir;
-    const char *upn;
+    char *upn;
     uid_t uid;
     gid_t gid;
     bool is_offline;
diff --git a/src/providers/krb5/krb5_renew_tgt.c b/src/providers/krb5/krb5_renew_tgt.c
index 217e03d..ccb7e6a 100644
--- a/src/providers/krb5/krb5_renew_tgt.c
+++ b/src/providers/krb5/krb5_renew_tgt.c
@@ -381,9 +381,11 @@ static errno_t check_ccache_files(struct renew_tgt_ctx *renew_tgt_ctx)
     struct ldb_message **msgs = NULL;
     size_t c;
     const char *ccache_file;
-    const char *upn;
+    char *upn;
     const char *user_name;
     struct ldb_dn *base_dn;
+    const struct ldb_val *user_dom_val;
+    char *user_dom;
 
     tmp_ctx = talloc_new(NULL);
     if (tmp_ctx == NULL) {
@@ -421,15 +423,31 @@ static errno_t check_ccache_files(struct renew_tgt_ctx *renew_tgt_ctx)
             continue;
         }
 
-        upn = ldb_msg_find_attr_as_string(msgs[c], SYSDB_UPN, NULL);
-        if (upn == NULL) {
-            ret = krb5_get_simple_upn(tmp_ctx, renew_tgt_ctx->krb5_ctx,
-                                      user_name, &upn);
-            if (ret != EOK) {
-                DEBUG(1, ("krb5_get_simple_upn failed.\n"));
-                continue;
-            }
-            DEBUG(9, ("No upn stored in cache, using [%s].\n", upn));
+        /* The DNs of users in sysdb ends with ...,cn=domain.name,cn=sysdb, so
+         * the value of the component before the last (index 1) is the domain
+         * name. */
+
+        user_dom_val = ldb_dn_get_component_val(msgs[c]->dn, 1);
+        if (user_dom_val == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Invalid user DN [%s].\n",
+                                      ldb_dn_get_linearized(msgs[c]->dn)));
+            ret = EINVAL;
+            goto done;
+        }
+        user_dom = talloc_strndup(tmp_ctx, (char *) user_dom_val->data,
+                                 user_dom_val->length);
+        if (user_dom == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("talloc_strndup failed,\n"));
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = find_or_guess_upn(tmp_ctx, msgs[c], renew_tgt_ctx->krb5_ctx,
+                                renew_tgt_ctx->be_ctx->domain->name,
+                                user_name, user_dom, &upn);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("find_or_guess_upn failed.\n"));
+            goto done;
         }
 
         ccache_file = ldb_msg_find_attr_as_string(msgs[c], SYSDB_CCACHE_FILE,
diff --git a/src/tests/krb5_child-test.c b/src/tests/krb5_child-test.c
index b07592a..d55eec0 100644
--- a/src/tests/krb5_child-test.c
+++ b/src/tests/krb5_child-test.c
@@ -221,7 +221,8 @@ create_dummy_req(TALLOC_CTX *mem_ctx, const char *user,
     /* PAM Data structure */
     kr->pd = create_dummy_pam_data(kr, user, password);
 
-    ret = krb5_get_simple_upn(kr, kr->krb5_ctx, kr->pd->user, &kr->upn);
+    ret = krb5_get_simple_upn(kr, kr->krb5_ctx, NULL, kr->pd->user, NULL,
+                              &kr->upn);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, ("krb5_get_simple_upn failed.\n"));
         goto fail;
diff --git a/src/tests/krb5_utils-tests.c b/src/tests/krb5_utils-tests.c
index 636bcd4..fe5d842 100644
--- a/src/tests/krb5_utils-tests.c
+++ b/src/tests/krb5_utils-tests.c
@@ -433,7 +433,7 @@ void setup_talloc_context(void)
 
     pd->user = discard_const(USERNAME);
     kr->uid = atoi(UID);
-    kr->upn = PRINCIPAL_NAME;
+    kr->upn = discard_const(PRINCIPAL_NAME);
     pd->cli_pid = atoi(PID);
 
     krb5_ctx->opts = talloc_zero_array(tmp_ctx, struct dp_option, KRB5_OPTS);
-- 
1.7.7.6

-------------- next part --------------
From e920bc64dbab01da645f12532dcaa12f96b34eb8 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Wed, 24 Oct 2012 10:01:09 +0200
Subject: [PATCH 17/17] krb5_auth: update with correct UPN if needed

The Active Directory KDC handles request case in-sensitive and it might
not always to possible to guess the UPN with the correct case. We check
if the returned principal has a different case then the one used in the
request and updates the principal if needed. This will help using calls
from the Kerberos client libraries later on which would otherwise fail
because the principal is handled case sensitive by those libraries.
---
 src/providers/krb5/krb5_auth.c  |   30 ++++++++++++
 src/providers/krb5/krb5_utils.c |   99 +++++++++++++++++++++++++++++++++++++++
 src/providers/krb5/krb5_utils.h |    4 ++
 3 files changed, 133 insertions(+), 0 deletions(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index c1f9f14..f2e00fa 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -782,6 +782,36 @@ static void krb5_child_done(struct tevent_req *subreq)
         }
     }
 
+    /* Check if the cases of our upn are correct and update it if needed.
+     * Fail if the upn differs by more than just the case. */
+    if (res->correct_upn != NULL &&
+        strcmp(kr->upn, res->correct_upn) != 0) {
+        if (strcasecmp(kr->upn, res->correct_upn) == 0) {
+            talloc_free(kr->upn);
+            kr->upn = talloc_strdup(kr, res->correct_upn);
+            if (kr->upn == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n"));
+                ret = ENOMEM;
+                goto done;
+            }
+
+            ret = check_if_cached_upn_needs_update(state->sysdb, pd->user,
+                                                   res->correct_upn);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE,
+                      ("check_if_cached_upn_needs_update failed.\n"));
+                goto done;
+            }
+        } else {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("UPN used in the request [%s] and " \
+                                        "returned UPN [%s] differ by more " \
+                                        "than just the case.\n",
+                                        kr->upn, res->correct_upn));
+            ret = EINVAL;
+            goto done;
+        }
+    }
+
     /* If the child request failed, but did not return an offline error code,
      * return with the status */
     if (res->msg_status != PAM_SUCCESS &&
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index 9837616..6cfd48c 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -57,6 +57,105 @@ errno_t find_or_guess_upn(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
     return EOK;
 }
 
+errno_t check_if_cached_upn_needs_update(struct sysdb_ctx *sysdb,
+                                         const char *user,
+                                         const char *upn)
+{
+    TALLOC_CTX *tmp_ctx;
+    int ret;
+    int sret;
+    const char *attrs[] = {SYSDB_UPN, NULL};
+    struct sysdb_attrs *new_attrs;
+    struct ldb_result *res;
+    bool in_transaction = false;
+    const char *cached_upn;
+
+    if (sysdb == NULL || user == NULL || upn == NULL) {
+        return EINVAL;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("talloc_new failed.\n"));
+        return ENOMEM;
+    }
+
+    ret = sysdb_get_user_attr(tmp_ctx, sysdb, user, attrs, &res);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_get_user_attr failed.\n"));
+        goto done;
+    }
+
+    if (res->count != 1) {
+        DEBUG(SSSDBG_OP_FAILURE, ("[%d] user objects for name [%s] found, " \
+                                  "expected 1.\n", res->count, user));
+        ret = EINVAL;
+        goto done;
+    }
+
+    cached_upn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_UPN, NULL);
+
+    if (cached_upn != NULL && strcmp(cached_upn, upn) == 0) {
+        DEBUG(SSSDBG_TRACE_ALL, ("Cached UPN and new one match, "
+                                 "nothing to do.\n"));
+        ret = EOK;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_LIBS, ("Replacing UPN [%s] with [%s] for user [%s].\n",
+                              cached_upn, upn, user));
+
+    new_attrs = sysdb_new_attrs(tmp_ctx);
+    if (new_attrs == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_new_attrs failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_attrs_add_string(new_attrs, SYSDB_UPN, upn);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_add_string failed.\n"));
+        goto done;
+    }
+
+    ret = sysdb_transaction_start(sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Error %d starting transaction (%s)\n", ret, strerror(ret)));
+        goto done;
+    }
+    in_transaction = true;
+
+    ret = sysdb_set_entry_attr(sysdb, res->msgs[0]->dn, new_attrs,
+                               SYSDB_MOD_REP);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_set_entry_attr failed [%d][%s].\n",
+                                  ret, strerror(ret)));
+        goto done;
+    }
+
+    ret = sysdb_transaction_commit(sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Failed to commit transaction!\n"));
+        goto done;
+    }
+    in_transaction = false;
+
+    ret = EOK;
+
+done:
+    if (in_transaction) {
+        sret = sysdb_transaction_cancel(sysdb);
+        if (sret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to cancel transaction\n"));
+        }
+    }
+
+    talloc_free(tmp_ctx);
+
+    return ret;
+}
+
 char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
                              const char *template, bool file_mode,
                              bool case_sensitive, bool *private_path)
diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h
index 2848545..25d8c6c 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -37,6 +37,10 @@ errno_t find_or_guess_upn(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
                           const char *domain_name, const char *user,
                           const char *user_dom, char **_upn);
 
+errno_t check_if_cached_upn_needs_update(struct sysdb_ctx *sysdb,
+                                         const char *user,
+                                         const char *upn);
+
 /* Operations on a credential cache */
 typedef errno_t (*cc_be_create_fn)(const char *location, pcre *illegal_re,
                                    uid_t uid, gid_t gid, bool private_path);
-- 
1.7.7.6



More information about the sssd-devel mailing list