[SSSD] [PATCH] Validate Kerberos cerdentials with local keytab

Sumit Bose sbose at redhat.com
Thu Nov 19 12:58:38 UTC 2009


On Wed, Nov 18, 2009 at 05:13:30PM -0500, Stephen Gallagher wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> On 11/13/2009 09:29 AM, Sumit Bose wrote:
> > On Thu, Nov 12, 2009 at 01:46:39PM -0500, Stephen Gallagher wrote:
> > On 11/12/2009 06:46 AM, Sumit Bose wrote:
> >>>> Hi,
> >>>>
> >>>> this patch add the possibility to validate the credentials obtained from
> >>>> a Kerberos server with a local keytab. The boolean option krb5_validate
> >>>> switches the validation on and off. It is disabled by default in the
> >>>> kerberos provider and enabled by default in the IPA provider.
> >>>>
> >>>> Typically root privileges are needed to read a keytab. As a consequence
> >>>> if validation is enabled the privileges cannot be drop before starting
> >>>> krb5_child, but only after reading the keytab.
> >>>>
> >>>> bye,
> >>>> Sumit
> >>>>
> >>>>
> >>>>
> >>>> _______________________________________________
> >>>> sssd-devel mailing list
> >>>> sssd-devel at lists.fedorahosted.org
> >>>> https://fedorahosted.org/mailman/listinfo/sssd-devel
> > 
> > Nack.
> > 
> > In the sssd-ipa manpage, I think we should change the "please note" to
> > "Please note that this default differs from the traditional kerberos
> > provider backend."
> > 
> > I think that referring to the "underlying Kerberos provider" makes it
> > unclear.
> > 
> >> done
> > 
> > 
> > In create_send_buffer(), you assign buf->size based on sizeof(int), but
> > you're using uint32_t for the actual data. This is a waste of memory on
> > 64-bit integer systems, and a serious error on a 16-bit integer system.
> > (Not that we ever expect to support such a system) If you're copying in
> > a 32-bit number, please guarantee that the space is allocated for a
> > 32-bit number.
> > 
> > 
> >> done
> > 
> > Please add a comment in fork_child() stating why the value of
> > KRB5_VALIDATE dictates whether to assume the user's identity.
> > 
> > 
> >> done
> > 
> > I think this is a serious error: you're only validating against the
> > first entry in the keytab. It's possible for a keytab to have many
> > different principals, as well as multiple enctypes for the same
> > principal. We need to iterate through all keytab entries and test first
> > for the principal we need to validate against and not fail until all
> > enctypes for the sought-after principal have been tried.
> > 
> > 
> >> ok, I look for the first key with a matching realm or try the last one
> >> in the keytab file.
> > 
> > get_and_save_tgt(): Again a comment would be nice around become_user()
> > noting that it was being done here after being deferred from earlier so
> > that we can validate the TGT.
> > 
> >> done
> > 
> > 
> > General question: if we're moving where become_user() is called, will
> > this affect our SELinux policy?
> > 
> > 
> >> I think it will not affect the policy, because the krb5_child inherits
> >> the SELinux labels from the parent, but I will check with Dan.
> > 
> >> bye,
> >> Sumit
> > 
> 
> Nack.
> If you're adding new options to the SSSDConfig API, please run the
> SSSDConfigTest.py in-tree. You need to update its expected results here
> because you've set an explicit default.

Sorry, I didn't realised that I need to add the options more that once.
New version attached.

bye,
Sumit

> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
> >>
> >>
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
> 
> - -- 
> Stephen Gallagher
> RHCE 804006346421761
> 
> Delivering value year after year.
> Red Hat ranks #1 in value among software vendors.
> http://www.redhat.com/promo/vendor/
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.9 (GNU/Linux)
> Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/
> 
> iEYEARECAAYFAksEcYUACgkQeiVVYja6o6N9EwCeObdan1m+JozK/H+Wyfxu29z1
> aCoAoKRPauxEQSnt9MiOmGDqcykbg8nk
> =jkfY
> -----END PGP SIGNATURE-----
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
-------------- next part --------------
>From 5e923a96b70c1124d393bb4f82beb3a5a4b6cc8f Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Wed, 11 Nov 2009 14:16:41 +0100
Subject: [PATCH] Validate Kerberos credentials with local keytab

---
 server/Makefile.am                          |    3 +
 server/config/SSSDConfig.py                 |    2 +
 server/config/SSSDConfigTest.py             |    6 +
 server/config/etc/sssd.api.d/sssd-krb5.conf |    4 +-
 server/external/krb5.m4                     |    3 +-
 server/man/sssd-ipa.5.xml                   |   17 +++
 server/man/sssd-krb5.5.xml                  |   25 +++++
 server/providers/ipa/ipa_common.c           |    2 +
 server/providers/krb5/krb5_auth.c           |   74 +++++++-------
 server/providers/krb5/krb5_become_user.c    |   61 +++++++++++
 server/providers/krb5/krb5_child.c          |  148 ++++++++++++++++++++++++++-
 server/providers/krb5/krb5_common.c         |    2 +
 server/providers/krb5/krb5_common.h         |    2 +
 server/providers/krb5/krb5_utils.h          |    2 +
 server/util/sss_krb5.c                      |   16 +++-
 server/util/sss_krb5.h                      |    2 +
 16 files changed, 326 insertions(+), 43 deletions(-)
 create mode 100644 server/providers/krb5/krb5_become_user.c

diff --git a/server/Makefile.am b/server/Makefile.am
index 08c0295..6dfc2ae 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -564,6 +564,7 @@ libsss_proxy_la_LDFLAGS = \
 
 libsss_krb5_la_SOURCES = \
     providers/krb5/krb5_utils.c \
+    providers/krb5/krb5_become_user.c \
     providers/krb5/krb5_auth.c \
     providers/krb5/krb5_common.c \
     providers/krb5/krb5_init.c
@@ -591,6 +592,7 @@ libsss_ipa_la_SOURCES = \
     util/sss_ldap.c \
     util/sss_krb5.c \
     providers/krb5/krb5_utils.c \
+    providers/krb5/krb5_become_user.c \
     providers/krb5/krb5_common.c \
     providers/krb5/krb5_auth.c
 libsss_ipa_la_CFLAGS = \
@@ -606,6 +608,7 @@ libsss_ipa_la_LDFLAGS = \
 
 krb5_child_SOURCES = \
     $(SSSD_DEBUG_OBJ) \
+    providers/krb5/krb5_become_user.c \
     providers/krb5/krb5_child.c \
     util/sss_krb5.c
 krb5_child_CFLAGS = \
diff --git a/server/config/SSSDConfig.py b/server/config/SSSDConfig.py
index 162354b..77222ae 100644
--- a/server/config/SSSDConfig.py
+++ b/server/config/SSSDConfig.py
@@ -90,6 +90,8 @@ option_strings = {
     # [provider/krb5/auth]
     'krb5_ccachedir' : _('Directory to store credential caches'),
     'krb5_ccname_template' : _("Location of the user's credential cache"),
+    'krb5_keytab' : _("Location of the keytab to validate credentials"),
+    'krb5_validate' : _("Enable credential validation"),
 
     # [provider/krb5/chpass]
     'krb5_changepw_principal' : _('The principal of the change password service'),
diff --git a/server/config/SSSDConfigTest.py b/server/config/SSSDConfigTest.py
index 944f86f..56b6ef4 100644
--- a/server/config/SSSDConfigTest.py
+++ b/server/config/SSSDConfigTest.py
@@ -479,6 +479,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_realm',
              'krb5_ccachedir',
              'krb5_ccname_template',
+             'krb5_keytab',
+             'krb5_validate',
              'krb5_auth_timeout'])
 
         options = domain.list_options()
@@ -550,6 +552,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'krb5_realm',
             'krb5_ccachedir',
             'krb5_ccname_template',
+            'krb5_keytab',
+            'krb5_validate',
             'krb5_auth_timeout']
 
         self.assertTrue(type(options) == dict,
@@ -698,6 +702,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_realm',
              'krb5_ccachedir',
              'krb5_ccname_template',
+             'krb5_keytab',
+             'krb5_validate',
              'krb5_auth_timeout'])
 
         options = domain.list_options()
diff --git a/server/config/etc/sssd.api.d/sssd-krb5.conf b/server/config/etc/sssd.api.d/sssd-krb5.conf
index 85067e9..860f8b8 100644
--- a/server/config/etc/sssd.api.d/sssd-krb5.conf
+++ b/server/config/etc/sssd.api.d/sssd-krb5.conf
@@ -6,8 +6,10 @@ krb5_auth_timeout = int, None
 [provider/krb5/auth]
 krb5_ccachedir = str, None
 krb5_ccname_template = str, None
+krb5_keytab = str, None
+krb5_validate = bool, None
 
 [provider/krb5/access]
 
 [provider/krb5/chpass]
-krb5_changepw_principal = str, None
\ No newline at end of file
+krb5_changepw_principal = str, None
diff --git a/server/external/krb5.m4 b/server/external/krb5.m4
index d02732b..84bb441 100644
--- a/server/external/krb5.m4
+++ b/server/external/krb5.m4
@@ -37,7 +37,8 @@ SAVE_LIBS=$LIBS
 CFLAGS="$CFLAGS $KRB5_CFLAGS"
 LIBS="$LIBS $KRB5_LIBS"
 AC_CHECK_HEADERS([krb5.h krb5/krb5.h])
-AC_CHECK_FUNCS([krb5_get_init_creds_opt_alloc krb5_get_error_message])
+AC_CHECK_FUNCS([krb5_get_init_creds_opt_alloc krb5_get_error_message \
+                krb5_free_unparsed_name])
 CFLAGS=$SAVE_CFLAGS
 LIBS=$SAVE_LIBS
 
diff --git a/server/man/sssd-ipa.5.xml b/server/man/sssd-ipa.5.xml
index 31ce824..2751591 100644
--- a/server/man/sssd-ipa.5.xml
+++ b/server/man/sssd-ipa.5.xml
@@ -94,6 +94,23 @@
                     </listitem>
                 </varlistentry>
 
+                <varlistentry>
+                    <term>krb5_validate (boolean)</term>
+                    <listitem>
+                        <para>
+                            Verify with the help of krb5_keytab that the TGT
+                            obtained has not been spoofed.
+                        </para>
+                        <para>
+                            Default: true
+                        </para>
+                        <para>
+                             Please note that this default differs from the
+                             traditional kerberos provider backend.
+                        </para>
+                    </listitem>
+                </varlistentry>
+
             </variablelist>
         </para>
     </refsect1>
diff --git a/server/man/sssd-krb5.5.xml b/server/man/sssd-krb5.5.xml
index 1f86b49..1ca2831 100644
--- a/server/man/sssd-krb5.5.xml
+++ b/server/man/sssd-krb5.5.xml
@@ -178,6 +178,31 @@
                     </listitem>
                 </varlistentry>
 
+                <varlistentry>
+                    <term>krb5_validate (boolean)</term>
+                    <listitem>
+                        <para>
+                            Verify with the help of krb5_keytab that the TGT obtained has not been spoofed.
+                        </para>
+                        <para>
+                            Default: false
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                    <term>krb5_keytab (string)</term>
+                    <listitem>
+                        <para>
+                            The location of the keytab to use when validating
+                            credentials obtained from KDCs.
+                        </para>
+                        <para>
+                            Default: /etc/krb5.keytab
+                        </para>
+                    </listitem>
+                </varlistentry>
+
             </variablelist>
         </para>
     </refsect1>
diff --git a/server/providers/ipa/ipa_common.c b/server/providers/ipa/ipa_common.c
index 54044b9..38e4d53 100644
--- a/server/providers/ipa/ipa_common.c
+++ b/server/providers/ipa/ipa_common.c
@@ -112,6 +112,8 @@ struct dp_option ipa_def_krb5_opts[] = {
     { "krb5_ccname_template", DP_OPT_STRING, { "FILE:%d/krb5cc_%U_XXXXXX" }, NULL_STRING},
     { "krb5_changepw_principal", DP_OPT_STRING, { "kadmin/changepw" }, NULL_STRING },
     { "krb5_auth_timeout", DP_OPT_NUMBER, { .number = 15 }, NULL_NUMBER },
+    { "krb5_keytab", DP_OPT_STRING, { "/etc/krb5.keytab" }, NULL_STRING },
+    { "krb5_validate", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }
 };
 
 int domain_to_basedn(TALLOC_CTX *memctx, const char *domain, char **basedn)
diff --git a/server/providers/krb5/krb5_auth.c b/server/providers/krb5/krb5_auth.c
index a02147e..d3e05e1 100644
--- a/server/providers/krb5/krb5_auth.c
+++ b/server/providers/krb5/krb5_auth.c
@@ -43,36 +43,6 @@
 #define KRB5_CHILD SSSD_LIBEXEC_PATH"/krb5_child"
 #endif
 
-static errno_t become_user(uid_t uid, gid_t gid)
-{
-    int ret;
-    ret = setgid(gid);
-    if (ret == -1) {
-        DEBUG(1, ("setgid failed [%d][%s].\n", errno, strerror(errno)));
-        return errno;
-    }
-
-    ret = setuid(uid);
-    if (ret == -1) {
-        DEBUG(1, ("setuid failed [%d][%s].\n", errno, strerror(errno)));
-        return errno;
-    }
-
-    ret = setegid(gid);
-    if (ret == -1) {
-        DEBUG(1, ("setegid failed [%d][%s].\n", errno, strerror(errno)));
-        return errno;
-    }
-
-    ret = seteuid(uid);
-    if (ret == -1) {
-        DEBUG(1, ("seteuid failed [%d][%s].\n", errno, strerror(errno)));
-        return errno;
-    }
-
-    return EOK;
-}
-
 struct io_buffer {
     uint8_t *data;
     size_t size;
@@ -82,6 +52,16 @@ errno_t create_send_buffer(struct krb5child_req *kr, struct io_buffer **io_buf)
 {
     struct io_buffer *buf;
     size_t rp;
+    const char *keytab;
+    uint32_t validate;
+
+    keytab = dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_KEYTAB);
+    if (keytab == NULL) {
+        DEBUG(1, ("Missing keytab option.\n"));
+        return EINVAL;
+    }
+
+    validate = dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_VALIDATE) ? 1 : 0;
 
     buf = talloc(kr, struct io_buffer);
     if (buf == NULL) {
@@ -89,10 +69,11 @@ errno_t create_send_buffer(struct krb5child_req *kr, struct io_buffer **io_buf)
         return ENOMEM;
     }
 
-    buf->size = 4*sizeof(int) + strlen(kr->pd->upn) + strlen(kr->ccname) +
+    buf->size = 8*sizeof(uint32_t) + strlen(kr->pd->upn) + strlen(kr->ccname) +
+                strlen(keytab) +
                 kr->pd->authtok_size;
     if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
-        buf->size += sizeof(int) + kr->pd->newauthtok_size;
+        buf->size += sizeof(uint32_t) + kr->pd->newauthtok_size;
     }
 
     buf->data = talloc_size(kr, buf->size);
@@ -106,6 +87,15 @@ errno_t create_send_buffer(struct krb5child_req *kr, struct io_buffer **io_buf)
     ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->cmd;
     rp += sizeof(uint32_t);
 
+    ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->pw_uid;
+    rp += sizeof(uint32_t);
+
+    ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->gr_gid;
+    rp += sizeof(uint32_t);
+
+    ((uint32_t *)(&buf->data[rp]))[0] = validate;
+    rp += sizeof(uint32_t);
+
     ((uint32_t *)(&buf->data[rp]))[0] = (uint32_t) strlen(kr->pd->upn);
     rp += sizeof(uint32_t);
 
@@ -118,6 +108,12 @@ errno_t create_send_buffer(struct krb5child_req *kr, struct io_buffer **io_buf)
     memcpy(&buf->data[rp], kr->ccname, strlen(kr->ccname));
     rp += strlen(kr->ccname);
 
+    ((uint32_t *)(&buf->data[rp]))[0] = (uint32_t) strlen(keytab);
+    rp += sizeof(uint32_t);
+
+    memcpy(&buf->data[rp], keytab, strlen(keytab));
+    rp += strlen(keytab);
+
     ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->authtok_size;
     rp += sizeof(uint32_t);
 
@@ -436,13 +432,17 @@ static errno_t fork_child(struct krb5child_req *kr)
             return err;
         }
 
-        ret = become_user(kr->pd->pw_uid, kr->pd->gr_gid);
-        if (ret != EOK) {
-            DEBUG(1, ("become_user failed.\n"));
-            return ret;
+        /* We need to keep the root privileges to read the keytab file if
+         * validation is enabled, otherwise we can drop them here and run
+         * krb5_child with user privileges. */
+        if (!dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_VALIDATE)) {
+            ret = become_user(kr->pd->pw_uid, kr->pd->gr_gid);
+            if (ret != EOK) {
+                DEBUG(1, ("become_user failed.\n"));
+                return ret;
+            }
         }
 
-
         close(pipefd_to_child[1]);
         ret = dup2(pipefd_to_child[0],STDIN_FILENO);
         if (ret == -1) {
diff --git a/server/providers/krb5/krb5_become_user.c b/server/providers/krb5/krb5_become_user.c
new file mode 100644
index 0000000..351f539
--- /dev/null
+++ b/server/providers/krb5/krb5_become_user.c
@@ -0,0 +1,61 @@
+/*
+    SSSD
+
+    Kerberos 5 Backend Module -- Utilities
+
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2009 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "util/util.h"
+
+errno_t become_user(uid_t uid, gid_t gid)
+{
+    int ret;
+
+    DEBUG(9, ("Trying to become user [%d][%d].\n", uid, gid));
+    ret = setgid(gid);
+    if (ret == -1) {
+        DEBUG(1, ("setgid failed [%d][%s].\n", errno, strerror(errno)));
+        return errno;
+    }
+
+    ret = setuid(uid);
+    if (ret == -1) {
+        DEBUG(1, ("setuid failed [%d][%s].\n", errno, strerror(errno)));
+        return errno;
+    }
+
+    ret = setegid(gid);
+    if (ret == -1) {
+        DEBUG(1, ("setegid failed [%d][%s].\n", errno, strerror(errno)));
+        return errno;
+    }
+
+    ret = seteuid(uid);
+    if (ret == -1) {
+        DEBUG(1, ("seteuid failed [%d][%s].\n", errno, strerror(errno)));
+        return errno;
+    }
+
+    return EOK;
+}
+
diff --git a/server/providers/krb5/krb5_child.c b/server/providers/krb5/krb5_child.c
index 2630e0f..66fccf8 100644
--- a/server/providers/krb5/krb5_child.c
+++ b/server/providers/krb5/krb5_child.c
@@ -88,6 +88,8 @@ struct krb5_req {
     errno_t (*child_req)(int fd, struct krb5_req *kr);
 
     char *ccname;
+    char *keytab;
+    bool validate;
 };
 
 static krb5_context krb5_error_ctx;
@@ -193,10 +195,104 @@ static struct response *prepare_response_message(struct krb5_req *kr,
     return resp;
 }
 
+static krb5_error_code validate_tgt(struct krb5_req *kr)
+{
+    krb5_error_code kerr;
+    krb5_error_code kt_err;
+    char *principal;
+    krb5_keytab keytab;
+    krb5_kt_cursor cursor;
+    krb5_keytab_entry entry;
+    krb5_verify_init_creds_opt opt;
+
+    memset(&keytab, 0, sizeof(keytab));
+    kerr = krb5_kt_resolve(kr->ctx, kr->keytab, &keytab);
+    if (kerr != 0) {
+        DEBUG(1, ("error resolving keytab [%s], not verifying TGT.\n",
+                  kr->keytab));
+        return kerr;
+    }
+
+    memset(&cursor, 0, sizeof(cursor));
+    kerr = krb5_kt_start_seq_get(kr->ctx, keytab, &cursor);
+    if (kerr != 0) {
+        DEBUG(1, ("error reading keytab [%s], not verifying TGT.\n",
+                  kr->keytab));
+        return kerr;
+    }
+
+    /* We look for the first entry from our realm or take the last one */
+    memset(&entry, 0, sizeof(entry));
+    while ((kt_err = krb5_kt_next_entry(kr->ctx, keytab, &entry, &cursor)) == 0) {
+        if (krb5_realm_compare(kr->ctx, entry.principal, kr->princ)) {
+            DEBUG(9, ("Found keytab entry with the realm of the credential.\n"));
+            break;
+        }
+
+        kerr = krb5_free_keytab_entry_contents(kr->ctx, &entry);
+        if (kerr != 0) {
+            DEBUG(1, ("Failed to free keytab entry.\n"));
+        }
+        memset(&entry, 0, sizeof(entry));
+    }
+
+    /* Close the keytab here.  Even though we're using cursors, the file
+     * handle is stored in the krb5_keytab structure, and it gets
+     * overwritten when the verify_init_creds() call below creates its own
+     * cursor, creating a leak. */
+    kerr = krb5_kt_end_seq_get(kr->ctx, keytab, &cursor);
+    if (kerr != 0) {
+        DEBUG(1, ("krb5_kt_end_seq_get failed, not verifying TGT.\n"));
+        goto done;
+    }
+
+    /* check if we got any errors from krb5_kt_next_entry */
+    if (kt_err != 0 && kt_err != KRB5_KT_END) {
+        DEBUG(1, ("error reading keytab [%s], not verifying TGT.\n",
+                  kr->keytab));
+        goto done;
+    }
+
+    /* Get the principal to which the key belongs, for logging purposes. */
+    principal = NULL;
+    kerr = krb5_unparse_name(kr->ctx, entry.principal, &principal);
+    if (kerr != 0) {
+        DEBUG(1, ("internal error parsing principal name, "
+                  "not verifying TGT.\n"));
+        goto done;
+    }
+
+
+    krb5_verify_init_creds_opt_init(&opt);
+    kerr = krb5_verify_init_creds(kr->ctx, kr->creds, entry.principal, keytab,
+                                  NULL, &opt);
+
+    if (kerr == 0) {
+        DEBUG(5, ("TGT verified using key for [%s].\n", principal));
+    } else {
+        DEBUG(1 ,("TGT failed verification using key for [%s].\n", principal));
+    }
+
+done:
+    if (krb5_kt_close(kr->ctx, keytab) != 0) {
+        DEBUG(1, ("krb5_kt_close failed"));
+    }
+    if (krb5_free_keytab_entry_contents(kr->ctx, &entry) != 0) {
+        DEBUG(1, ("Failed to free keytab entry.\n"));
+    }
+    if (principal != NULL) {
+        sss_krb5_free_unparsed_name(kr->ctx, principal);
+    }
+
+    return kerr;
+
+}
+
 static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
                                         char *password)
 {
     krb5_error_code kerr = 0;
+    int ret;
     int fd = -1;
     size_t ccname_len = 0;
     size_t offset = 0;
@@ -209,6 +305,25 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
         return kerr;
     }
 
+    if (kr->validate) {
+        kerr = validate_tgt(kr);
+        if (kerr != 0) {
+            KRB5_DEBUG(1, kerr);
+            return kerr;
+        }
+
+        /* We drop root privileges which were needed to read the keytab file
+         * for the validation validation of the credentials here to run the
+         * ccache I/O operations with user privileges. */
+        ret = become_user(kr->pd->pw_uid, kr->pd->gr_gid);
+        if (ret != EOK) {
+            DEBUG(1, ("become_user failed.\n"));
+            return ret;
+        }
+    } else {
+        DEBUG(9, ("TGT validation is disabled.\n"));
+    }
+
     if (kr->ccname[0] == '/' || strncmp(kr->ccname, "FILE:", 5) == 0) {
         offset = 0;
         if (kr->ccname[0] == 'F') {
@@ -467,7 +582,7 @@ uint8_t *copy_buffer_and_add_zero(TALLOC_CTX *mem_ctx, const uint8_t *src, size_
 }
 
 static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
-                             char **ccname)
+                             char **ccname, char **keytab, uint32_t *validate)
 {
     size_t p = 0;
     uint32_t *len;
@@ -479,6 +594,21 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
 
     if ((p + sizeof(uint32_t)) > size) return EINVAL;
     len = ((uint32_t *)(buf+p));
+    pd->pw_uid = *len;
+    p += sizeof(uint32_t);
+
+    if ((p + sizeof(uint32_t)) > size) return EINVAL;
+    len = ((uint32_t *)(buf+p));
+    pd->gr_gid = *len;
+    p += sizeof(uint32_t);
+
+    if ((p + sizeof(uint32_t)) > size) return EINVAL;
+    len = ((uint32_t *)(buf+p));
+    *validate = *len;
+    p += sizeof(uint32_t);
+
+    if ((p + sizeof(uint32_t)) > size) return EINVAL;
+    len = ((uint32_t *)(buf+p));
     p += sizeof(uint32_t);
 
     if ((p + *len ) > size) return EINVAL;
@@ -501,6 +631,16 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
     len = ((uint32_t *)(buf+p));
     p += sizeof(uint32_t);
 
+    if ((p + *len ) > size) return EINVAL;
+    *keytab = (char *) copy_buffer_and_add_zero(pd, buf+p,
+                                                sizeof(char) * (*len));
+    if (*keytab == NULL) return ENOMEM;
+    p += *len;
+
+    if ((p + sizeof(uint32_t)) > size) return EINVAL;
+    len = ((uint32_t *)(buf+p));
+    p += sizeof(uint32_t);
+
     if ((p + *len) > size) return EINVAL;
     pd->authtok = copy_buffer_and_add_zero(pd, buf+p, sizeof(char) * (*len));
     if (pd->authtok == NULL) return ENOMEM;
@@ -667,6 +807,8 @@ int main(int argc, const char *argv[])
     struct pam_data *pd = NULL;
     struct krb5_req *kr = NULL;
     char *ccname;
+    char *keytab;
+    uint32_t validate;
     int opt;
     poptContext pc;
     int debug_fd = -1;
@@ -738,7 +880,7 @@ int main(int argc, const char *argv[])
     }
     close(STDIN_FILENO);
 
-    ret = unpack_buffer(buf, len, pd, &ccname);
+    ret = unpack_buffer(buf, len, pd, &ccname, &keytab, &validate);
     if (ret != EOK) {
         DEBUG(1, ("unpack_buffer failed.\n"));
         goto fail;
@@ -750,6 +892,8 @@ int main(int argc, const char *argv[])
         goto fail;
     }
     kr->ccname = ccname;
+    kr->keytab = keytab;
+    kr->validate = (validate == 0) ? false : true;
 
     ret = kr->child_req(STDOUT_FILENO, kr);
     if (ret != EOK) {
diff --git a/server/providers/krb5/krb5_common.c b/server/providers/krb5/krb5_common.c
index de069cd..30878de 100644
--- a/server/providers/krb5/krb5_common.c
+++ b/server/providers/krb5/krb5_common.c
@@ -35,6 +35,8 @@ struct dp_option default_krb5_opts[] = {
     { "krb5_ccname_template", DP_OPT_STRING, { "FILE:%d/krb5cc_%U_XXXXXX" }, NULL_STRING},
     { "krb5_changepw_principal", DP_OPT_STRING, { "kadmin/changepw" }, NULL_STRING },
     { "krb5_auth_timeout", DP_OPT_NUMBER, { .number = 15 }, NULL_NUMBER },
+    { "krb5_keytab", DP_OPT_STRING, { "/etc/krb5.keytab" }, NULL_STRING },
+    { "krb5_validate", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }
 };
 
 errno_t check_and_export_options(struct dp_option *opts,
diff --git a/server/providers/krb5/krb5_common.h b/server/providers/krb5/krb5_common.h
index 60f6a82..cb60f42 100644
--- a/server/providers/krb5/krb5_common.h
+++ b/server/providers/krb5/krb5_common.h
@@ -44,6 +44,8 @@ enum krb5_opts {
     KRB5_CCNAME_TMPL,
     KRB5_CHANGEPW_PRINC,
     KRB5_AUTH_TIMEOUT,
+    KRB5_KEYTAB,
+    KRB5_VALIDATE,
 
     KRB5_OPTS
 };
diff --git a/server/providers/krb5/krb5_utils.h b/server/providers/krb5/krb5_utils.h
index dec6f7f..7637041 100644
--- a/server/providers/krb5/krb5_utils.h
+++ b/server/providers/krb5/krb5_utils.h
@@ -34,4 +34,6 @@
 char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
                              const char *template);
 
+errno_t become_user(uid_t uid, gid_t gid);
+
 #endif /* __KRB5_UTILS_H__ */
diff --git a/server/util/sss_krb5.c b/server/util/sss_krb5.c
index 59e278e..e96e1ba 100644
--- a/server/util/sss_krb5.c
+++ b/server/util/sss_krb5.c
@@ -83,10 +83,22 @@ void KRB5_CALLCONV sss_krb5_get_init_creds_opt_free (krb5_context context,
                                                    krb5_get_init_creds_opt *opt)
 {
 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
-        krb5_get_init_creds_opt_free(context, opt);
+    krb5_get_init_creds_opt_free(context, opt);
 #else
-        free(opt);
+    free(opt);
 #endif
 
     return;
 }
+
+void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name)
+{
+#ifdef HAVE_KRB5_FREE_UNPARSED_NAME
+    krb5_free_unparsed_name(context, name);
+#else
+    if (name != NULL) {
+        memset(name, 0, strlen(name));
+        free(name);
+    }
+#endif
+}
diff --git a/server/util/sss_krb5.h b/server/util/sss_krb5.h
index 755cf81..342196d 100644
--- a/server/util/sss_krb5.h
+++ b/server/util/sss_krb5.h
@@ -42,4 +42,6 @@ krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_alloc(
 
 void KRB5_CALLCONV sss_krb5_get_init_creds_opt_free (krb5_context context,
                                                   krb5_get_init_creds_opt *opt);
+
+void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name);
 #endif /* __SSS_KRB5_H__ */
-- 
1.6.2.5



More information about the sssd-devel mailing list