From 45937f85384ef79b67e763fa7a16b95744261d89 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Tue, 20 Jul 2010 18:35:50 +0200
Subject: [PATCH 2/2] Validate keytab at startup

In addition to validating the keytab everytime a TGT is requested, we
also validate the keytab on back end startup to give early warning that
the keytab is not usable.

Fixes: #556
---
 src/providers/ldap/ldap_child.c         |   51 +----------
 src/providers/ldap/sdap_child_helpers.c |   16 +++
 src/util/sss_krb5.c                     |  152 +++++++++++++++++++++++++++++++
 src/util/sss_krb5.h                     |    9 ++
 4 files changed, 180 insertions(+), 48 deletions(-)

diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c
index 17c2685..f51040a 100644
--- a/src/providers/ldap/ldap_child.c
+++ b/src/providers/ldap/ldap_child.c
@@ -143,10 +143,6 @@ static int ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
     krb5_error_code krberr;
     krb5_timestamp kdc_time_offset;
     int kdc_time_offset_usec;
-    krb5_kt_cursor cursor;
-    krb5_keytab_entry entry;
-    char *principal;
-    bool found;
     int ret;
 
     krberr = krb5_init_context(&context);
@@ -227,50 +223,9 @@ static int ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
     }
 
     /* Verify the keytab */
-    krberr = krb5_kt_start_seq_get(context, keytab, &cursor);
-    if (krberr) {
-        DEBUG(0, ("Cannot read keytab [%s].\n", keytab_name));
-
-        sss_log(SSS_LOG_ERR, "Error reading keytab file [%s]: [%d][%s]. "
-                             "Unable to create GSSAPI-encrypted LDAP connection.",
-                             keytab_name, krberr,
-                             sss_krb5_get_error_message(context, krberr));
-
-        ret = EFAULT;
-        goto done;
-    }
-
-    found = false;
-    while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){
-        krb5_unparse_name(context, entry.principal, &principal);
-        if (strcmp(full_princ, principal) == 0) {
-            found = true;
-        }
-        free(principal);
-        krb5_free_keytab_entry_contents(context, &entry);
-
-        if (found) {
-            break;
-        }
-    }
-    krberr = krb5_kt_end_seq_get(context, keytab, &cursor);
-    if (krberr) {
-        DEBUG(0, ("Could not close keytab.\n"));
-        sss_log(SSS_LOG_ERR, "Could not close keytab file [%s].",
-                             keytab_name);
-        ret = EFAULT;
-        goto done;
-    }
-
-    if (!found) {
-        DEBUG(0, ("Principal [%s] not found in keytab [%s]\n",
-                  full_princ, keytab_name));
-        sss_log(SSS_LOG_ERR, "Error processing keytab file [%s]: "
-                             "Principal [%s] was not found. "
-                             "Unable to create GSSAPI-encrypted LDAP connection.",
-                             keytab_name, full_princ);
-
-        ret = EFAULT;
+    ret = sss_krb5_verify_keytab_ex(full_princ, keytab_name, context, keytab);
+    if (ret) {
+        DEBUG(2, ("Unable to verify principal is present in the keytab\n"));
         goto done;
     }
 
diff --git a/src/providers/ldap/sdap_child_helpers.c b/src/providers/ldap/sdap_child_helpers.c
index 58b757e..1b12248 100644
--- a/src/providers/ldap/sdap_child_helpers.c
+++ b/src/providers/ldap/sdap_child_helpers.c
@@ -29,6 +29,7 @@
 #include <fcntl.h>
 
 #include "util/util.h"
+#include "util/sss_krb5.h"
 #include "providers/ldap/ldap_common.h"
 #include "providers/ldap/sdap_async_private.h"
 #include "providers/child_common.h"
@@ -455,6 +456,21 @@ int setup_child(struct sdap_id_ctx *ctx)
         return EOK;
     }
 
+    if (mech && (strcasecmp(mech, "GSSAPI") == 0)) {
+        ret = sss_krb5_verify_keytab(dp_opt_get_string(ctx->opts->basic,
+                                                       SDAP_SASL_AUTHID),
+                                     dp_opt_get_string(ctx->opts->basic,
+                                                       SDAP_KRB5_REALM),
+                                     dp_opt_get_string(ctx->opts->basic,
+                                                       SDAP_KRB5_KEYTAB));
+
+        if (ret != EOK) {
+            DEBUG(0, ("Could not verify keytab\n"))
+            return ret;
+        }
+
+    }
+
     if (debug_to_file != 0 && ldap_child_debug_fd == -1) {
         ret = open_debug_file_ex("ldap_child", &debug_filep);
         if (ret != EOK) {
diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c
index 0bc25df..f589415 100644
--- a/src/util/sss_krb5.c
+++ b/src/util/sss_krb5.c
@@ -26,7 +26,159 @@
 #include "util/util.h"
 #include "util/sss_krb5.h"
 
+int sss_krb5_verify_keytab(const char *principal,
+                           const char *realm_str,
+                           const char *keytab_name)
+{
+    krb5_context context = NULL;
+    krb5_keytab keytab = NULL;
+    krb5_error_code krberr;
+    int ret;
+    char *full_princ = NULL;
+    char *realm_name = NULL;
+    char *default_realm = NULL;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    krberr = krb5_init_context(&context);
+    if (krberr) {
+        DEBUG(2, ("Failed to init kerberos context\n"));
+        return EFAULT;
+    }
+
+    if (keytab_name) {
+        krberr = krb5_kt_resolve(context, keytab_name, &keytab);
+    } else {
+        krberr = krb5_kt_default(context, &keytab);
+    }
+
+    if (krberr) {
+        DEBUG(0, ("Failed to read keytab file: %s\n",
+                  sss_krb5_get_error_message(context, krberr)));
+        ret = EFAULT;
+        goto done;
+    }
+
+    if (!realm_str) {
+        krberr = krb5_get_default_realm(context, &default_realm);
+        if (krberr) {
+            DEBUG(2, ("Failed to get default realm name: %s\n",
+                      sss_krb5_get_error_message(context, krberr)));
+            ret = EFAULT;
+            goto done;
+        }
+
+        realm_name = talloc_strdup(tmp_ctx, default_realm);
+        krb5_free_default_realm(context, default_realm);
+        if (!realm_name) {
+            ret = ENOMEM;
+            goto done;
+        }
+    } else {
+        realm_name = talloc_strdup(tmp_ctx, realm_str);
+        if (!realm_name) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    if (principal) {
+        if (!strchr(principal, '@')) {
+            full_princ = talloc_asprintf(tmp_ctx, "%s@%s",
+                                         principal, realm_name);
+        } else {
+            full_princ = talloc_strdup(tmp_ctx, principal);
+        }
+    } else {
+        char hostname[512];
+
+        ret = gethostname(hostname, 511);
+        if (ret == -1) {
+            ret = errno;
+            goto done;
+        }
+        hostname[511] = '\0';
+
+        full_princ = talloc_asprintf(tmp_ctx, "host/%s@%s",
+                                     hostname, realm_name);
+    }
+    if (!full_princ) {
+        ret = ENOMEM;
+        goto done;
+    }
+    DEBUG(4, ("Principal name is: [%s]\n", full_princ));
+
+    ret = sss_krb5_verify_keytab_ex(full_princ, keytab_name, context, keytab);
+    if (ret) goto done;
+
+    ret = EOK;
+done:
+    if (keytab) krb5_kt_close(context, keytab);
+    if (context) krb5_free_context(context);
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+int sss_krb5_verify_keytab_ex(const char *principal, const char *keytab_name,
+                              krb5_context context, krb5_keytab keytab)
+{
+    bool found;
+    char *kt_principal;
+    krb5_error_code krberr;
+    krb5_kt_cursor cursor;
+    krb5_keytab_entry entry;
+
+    krberr = krb5_kt_start_seq_get(context, keytab, &cursor);
+    if (krberr) {
+        DEBUG(0, ("Cannot read keytab [%s].\n", keytab_name));
+
+        sss_log(SSS_LOG_ERR, "Error reading keytab file [%s]: [%d][%s]. "
+                             "Unable to create GSSAPI-encrypted LDAP connection.",
+                             keytab_name, krberr,
+                             sss_krb5_get_error_message(context, krberr));
 
+        return EIO;
+    }
+
+    found = false;
+    while((krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){
+        krb5_unparse_name(context, entry.principal, &kt_principal);
+        if (strcmp(principal, kt_principal) == 0) {
+            found = true;
+        }
+        free(kt_principal);
+        krb5_free_keytab_entry_contents(context, &entry);
+
+        if (found) {
+            break;
+        }
+    }
+
+    krberr = krb5_kt_end_seq_get(context, keytab, &cursor);
+    if (krberr) {
+        DEBUG(0, ("Could not close keytab.\n"));
+        sss_log(SSS_LOG_ERR, "Could not close keytab file [%s].",
+                             keytab_name);
+        return EIO;
+    }
+
+    if (!found) {
+        DEBUG(0, ("Principal [%s] not found in keytab [%s]\n",
+                  principal, keytab_name ? keytab_name : "default"));
+        sss_log(SSS_LOG_ERR, "Error processing keytab file [%s]: "
+                             "Principal [%s] was not found. "
+                             "Unable to create GSSAPI-encrypted LDAP connection.",
+                             keytab_name, principal);
+
+        return EFAULT;
+    }
+
+    return EOK;
+}
 
 const char *KRB5_CALLCONV sss_krb5_get_error_message(krb5_context ctx,
                                                krb5_error_code ec)
diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h
index 60994e1..bc7a4f8 100644
--- a/src/util/sss_krb5.h
+++ b/src/util/sss_krb5.h
@@ -24,6 +24,7 @@
 #include "config.h"
 
 #include <stdbool.h>
+#include <talloc.h>
 
 #ifdef HAVE_KRB5_KRB5_H
 #include <krb5/krb5.h>
@@ -47,4 +48,12 @@ void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name)
 
 krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm,
                                     const char *client_princ_str, bool *result);
+
+int sss_krb5_verify_keytab(const char *principal,
+                           const char *realm_str,
+                           const char *keytab_name);
+
+int sss_krb5_verify_keytab_ex(const char *principal, const char *keytab_name,
+                              krb5_context context, krb5_keytab keytab);
+
 #endif /* __SSS_KRB5_H__ */
-- 
1.7.1.1

