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

Sumit Bose sbose at redhat.com
Wed Oct 24 08:57:15 UTC 2012


On Thu, Oct 18, 2012 at 05:47:19PM -0400, Simo Sorce wrote:
> On Thu, 2012-10-18 at 17:12 -0400, Simo Sorce wrote:
> > On Thu, 2012-10-18 at 22:36 +0200, Sumit Bose wrote:
> > > 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
> > 
> > Sumit you have nice comments here, and none in the git commits,
> > Can you please rebase the patchset and add nice comments in the actual
> > commits.
> > They will come handy much after this review has been done and buried in
> > the archives when someone is looking for info on why a particular change
> > has been done and looks into git log/git blame.
> 
> Also nack to patch 0007, as far as I can understand reading the code and
> discussing on IRC it seem the only reason you are changing to use be_req
> is to use a different sysdb struct from what is available in be_ctx.
> 
> In this case though creating a whole fake be_req makes it completely
> opaque, it took me asking in person why that was done. This is bad,
> because proliferating fake/half filled functions is a recipe for issues.
> 
> So NACK.
> Please add a 'subdom_sysdb' argument to the function instead (you can
> also replace be_ctx with a list of explicit arguments needed), and do
> not create a fake struct be_req.
> 
> Also if this kind of scheme has been used elsewhere I would ask you to
> consider fixing those places as well.
> 
> Creating fake structures is confusing, makes reading the code hard,
> makes using those structures dangerous as you do not know if they are
> real or fake, and generally masks what arguments are really needed by a
> function.
> 
> I am actually regretting allowing to put the domain struct in sysdb
> which is the real reason all this dance is being made. I never like it
> as it is a bit of a layer violation, however it would be a major effort
> to change that, so I will just end the rant here and now :)
> 
> Simo.
> 

Thank you for your comments. I added the comments to the commits and
changed the approach used in patch 0007 which also made patch 0010
obsolete. All other patches are unchanged, but I had to add a few more.
The new patch 0014 just fixes a minor memory hierarchy issue. Patch
15-18 make sure we have a principle with the right case in the cache for
AD users.

bye,
Sumit
-------------- next part --------------
From 412687a2902bfaab99cb4cee078024df1c3f8ae9 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/18] 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 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;
+            }
+
+            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 244a590b7f0a4c82f8ee9afc1cad389ebd51f729 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/18] 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 f04cd302746e279cdf12d0d35aeae30831009f68 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/18] 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 e246a1691ff5f125258e58a336fde8f275f1f756 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/18] 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 eab7bd4aa658faf3461ce9f6cc3580da881cc0ca 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/18] 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 9fcadd25bae70306e29da715cebe55c99c45bd91 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 07/18] 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 5742e87c1c2c7ff4c0ec581dc9d3abce8262a44e 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/18] 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 b89fe8057c9506dc0f3cb0310308fb9d5a51a28b 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/18] 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 4e1aa76be8497082cc60efa76e3484244b87b991 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 10/18] 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 3d3f3dc24b621189c1daf2ea81890c67a3d7a1d4 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 11/18] 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 2769bf1312bde71e3af781ce9ffffb814fd8515f 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 12/18] 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 fd0786c62ac4ce51596fb91718a3533d95da29b1 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 13/18] 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 48cdd1baffb7b70eb3f60844fbc53919414bb772 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 14/18] 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 14722c0986ce4d261001a6af22fcc740fe910183 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 15/18] 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 663ea2771fb6dc1df17c6ce9b772d3077a44beb4 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 16/18] 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.
---
 src/providers/krb5/krb5_utils.c |   25 +++++++++++++++++++++++++
 src/providers/krb5/krb5_utils.h |    4 ++++
 2 files changed, 29 insertions(+), 0 deletions(-)

diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index 7a68b0f..a90047b 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -30,6 +30,31 @@
 #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 *user,
+                          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, user, _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..719d62f 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -32,6 +32,10 @@
 #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 *user,
+                          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 4fa382194c576597b77006c45c0c9a73fa4cb77b 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 17/18] Use find_or_guess_upn() where needed

---
 src/providers/krb5/krb5_access.c    |   15 ++++++---------
 src/providers/krb5/krb5_auth.c      |   26 ++++++++++++--------------
 src/providers/krb5/krb5_auth.h      |    2 +-
 src/providers/krb5/krb5_common.c    |    2 +-
 src/providers/krb5/krb5_common.h    |    2 +-
 src/providers/krb5/krb5_renew_tgt.c |   16 ++++++----------
 6 files changed, 27 insertions(+), 36 deletions(-)

diff --git a/src/providers/krb5/krb5_access.c b/src/providers/krb5/krb5_access.c
index afa3a89..a7c42dd 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,11 @@ 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, pd->user,
+                                &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..ee3b5fc 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -420,20 +420,18 @@ 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, pd->user,
+                                &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_common.c b/src/providers/krb5/krb5_common.c
index 45f126f..13a9bcb 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -858,7 +858,7 @@ 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 *username, char **_upn)
 {
     const char *realm;
     char *upn;
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index bc63bf9..4b29058 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -175,7 +175,7 @@ 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 *username, char **_upn);
 
 errno_t compare_principal_realm(const char *upn, const char *realm,
                                 bool *different_realm);
diff --git a/src/providers/krb5/krb5_renew_tgt.c b/src/providers/krb5/krb5_renew_tgt.c
index 217e03d..d59bad1 100644
--- a/src/providers/krb5/krb5_renew_tgt.c
+++ b/src/providers/krb5/krb5_renew_tgt.c
@@ -381,7 +381,7 @@ 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;
 
@@ -421,15 +421,11 @@ 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));
+        ret = find_or_guess_upn(tmp_ctx, msgs[c], renew_tgt_ctx->krb5_ctx,
+                                user_name, &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,
-- 
1.7.7.6

-------------- next part --------------
From d900baf92ba124205cc5216492ad908edd0d97e6 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 18/18] 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 ee3b5fc..f4ce170 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -781,6 +781,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 a90047b..6c828b6 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -55,6 +55,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 719d62f..67638bd 100644
--- a/src/providers/krb5/krb5_utils.h
+++ b/src/providers/krb5/krb5_utils.h
@@ -36,6 +36,10 @@ errno_t find_or_guess_upn(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
                           struct krb5_ctx *krb5_ctx, const char *user,
                           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