Pass a threadsafe ctx into fr_connection_pool create callback
[freeradius.git] / src / modules / rlm_krb5 / rlm_krb5.c
index 2ee7154..14743d5 100644 (file)
  * @copyright 2000  Nathan Neulinger <nneul@umr.edu>
  * @copyright 2000  Alan DeKok <aland@ox.org>
  */
-#include       <freeradius-devel/ident.h>
 RCSID("$Id$")
 
-#include       <freeradius-devel/radiusd.h>
-#include       <freeradius-devel/modules.h>
-#include       <freeradius-devel/rad_assert.h>
-
-/* krb5 includes */
-#include <krb5.h>
-#include <com_err.h>
-
-/** Instance configuration for rlm_krb5
- *
- * Holds the configuration and preparsed data for a instance of rlm_krb5.
- */
-typedef struct rlm_krb5_t {
-       const char      *xlat_name;     //!< This module's instance name.
-       const char      *keytabname;    //!< The keytab to resolve the service in.
-       const char      *service_princ; //!< The service name provided by the
-                                       //!< config parser.
-       
-       char            *hostname;      //!< The hostname component of
-                                       //!< service_princ, or NULL.
-       char            *service;       //!< The service component of
-                                       //!< service_princ, or NULL.
-       
-       krb5_context context;           //!< The kerberos context (cloned once per
-                                       //!< request).
-       
-#ifndef HEIMDAL_KRB5
-       krb5_get_init_creds_opt         *gic_options;   //!< Options to pass to the
-                                                       //!< get_initial_credentials.
-                                                       //!< function.
-       krb5_verify_init_creds_opt      *vic_options;   //!< Options to pass to the
-                                                       //!< validate_initial_creds
-                                                       //!< function.
-
-       krb5_principal server;          //!< A structure representing the parsed
-                                       //!< service_princ.
-#endif
-       
-} rlm_krb5_t;
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/rad_assert.h>
+#include "krb5.h"
 
 static const CONF_PARSER module_config[] = {
-       { "keytab", PW_TYPE_STRING_PTR,
-         offsetof(rlm_krb5_t,keytabname), NULL, NULL },
-       { "service_principal", PW_TYPE_STRING_PTR,
-         offsetof(rlm_krb5_t,service_princ), NULL, NULL },
+       { "keytab", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_krb5_t, keytabname), NULL },
+       { "service_principal", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_krb5_t, service_princ), NULL },
        { NULL, -1, 0, NULL, NULL }
 };
 
-static int krb5_detach(void *instance)
+static int mod_detach(void *instance)
 {
        rlm_krb5_t *inst = instance;
-               
+
 #ifndef HEIMDAL_KRB5
        talloc_free(inst->vic_options);
 
        if (inst->gic_options) {
                krb5_get_init_creds_opt_free(inst->context, inst->gic_options);
        }
+
+       if (inst->server) {
+               krb5_free_principal(inst->context, inst->server);
+       }
 #endif
 
        /* Don't free hostname, it's just a pointer into service_princ */
        talloc_free(inst->service);
-               
+
        if (inst->context) {
                krb5_free_context(inst->context);
        }
+#ifdef KRB5_IS_THREAD_SAFE
+       fr_connection_pool_delete(inst->pool);
+#endif
 
        return 0;
 }
 
-static int krb5_instantiate(CONF_SECTION *conf, void *instance)
+static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
        rlm_krb5_t *inst = instance;
        krb5_error_code ret;
+#ifndef HEIMDAL_KRB5
+       krb5_keytab keytab;
+       char keytab_name[200];
        char *princ_name;
+#endif
 
 #ifdef HEIMDAL_KRB5
        DEBUG("Using Heimdal Kerberos library");
@@ -109,30 +82,44 @@ static int krb5_instantiate(CONF_SECTION *conf, void *instance)
        DEBUG("Using MIT Kerberos library");
 #endif
 
-#ifndef KRB5_IS_THREAD_SAFE
        if (!krb5_is_thread_safe()) {
-               DEBUGW("libkrb5 is not threadsafe, recompile it with thread support enabled");
-               DEBUGW("rlm_krb5 will run in single threaded mode");
+/*
+ *     rlm_krb5 was built as threadsafe
+ */
+#ifdef KRB5_IS_THREAD_SAFE
+               ERROR("Build time libkrb5 was threadsafe, but run time library claims not to be");
+               ERROR("Modify runtime linker path (LD_LIBRARY_PATH on most systems), to prefer threadsafe libkrb5");
+               return -1;
+/*
+ *     rlm_krb5 was not built as threadsafe
+ */
+#else
+               WARN("libkrb5 is not threadsafe, recompile it with thread support enabled ("
+#  ifdef HEIMDAL_KRB5
+                      "--enable-pthread-support"
+#  else
+                      "--disable-thread-support=no"
+#  endif
+                      ")");
+               WARN("rlm_krb5 will run in single threaded mode, performance may be degraded");
        } else {
-               DEBUGW("Build time krb5 library was not threadsafe, but run time library claims to be");
-               DEBUGW("Reconfigure and recompile rlm_krb5 to enable thread support");
-       }
+               WARN("Build time libkrb5 was not threadsafe, but run time library claims to be");
+               WARN("Reconfigure and recompile rlm_krb5 to enable thread support");
 #endif
+       }
 
        inst->xlat_name = cf_section_name2(conf);
        if (!inst->xlat_name) {
                inst->xlat_name = cf_section_name1(conf);
        }
-       
+
        ret = krb5_init_context(&inst->context);
        if (ret) {
-               radlog(L_ERR, "rlm_krb5 (%s): Context initialisation failed: %s", inst->xlat_name, error_message(ret));
+               ERROR("rlm_krb5 (%s): context initialisation failed: %s", inst->xlat_name,
+                     rlm_krb5_error(NULL, ret));
 
                return -1;
        }
-       
-       radlog(L_DBG, "rlm_krb5 (%s): Context initialised successfully",
-              inst->xlat_name);
 
        /*
         *      Split service principal into service and host components
@@ -143,94 +130,129 @@ static int krb5_instantiate(CONF_SECTION *conf, void *instance)
                size_t len;
                /* Service principal appears to contain a host component */
                inst->hostname = strchr(inst->service_princ, '/');
-               if (inst->hostname) {   
-                       len = (inst->hostname - inst->service_princ);                   
+               if (inst->hostname) {
+                       len = (inst->hostname - inst->service_princ);
                        inst->hostname++;
                } else {
                        len = strlen(inst->service_princ);
                }
-               
+
                if (len) {
                        inst->service = talloc_array(inst, char, (len + 1));
-                       strlcpy(inst->service, inst->service_princ, len);
+                       strlcpy(inst->service, inst->service_princ, len + 1);
                }
        }
-       
+
 #ifdef HEIMDAL_KRB5
        if (inst->hostname) {
-               radlog(L_DBG, "rlm_krb5 (%s): Ignoring hostname component of "
-                      "service principal \"%s\", not needed/supported by "
-                      "Heimdal", inst->xlat_name, inst->hostname);
+               DEBUG("rlm_krb5 (%s): Ignoring hostname component of service principal \"%s\", not "
+                     "needed/supported by Heimdal", inst->xlat_name, inst->hostname);
        }
 #else
 
        /*
         *      Convert the service principal string to a krb5 principal.
         */
-       ret = krb5_sname_to_principal(inst->context, inst->hostname,
-                                     inst->service, KRB5_NT_SRV_HST,
-                                     &(inst->server));
+       ret = krb5_sname_to_principal(inst->context, inst->hostname, inst->service, KRB5_NT_SRV_HST, &(inst->server));
        if (ret) {
-               radlog(L_ERR, "rlm_krb5 (%s): Failed parsing service "
-                      "principal: %s", inst->xlat_name, error_message(ret));
+               ERROR("rlm_krb5 (%s): Failed parsing service principal: %s", inst->xlat_name,
+                     rlm_krb5_error(inst->context, ret));
 
                return -1;
        }
-       
+
        ret = krb5_unparse_name(inst->context, inst->server, &princ_name);
        if (ret) {
                /* Uh? */
-               radlog(L_ERR, "rlm_krb5 (%s): Failed constructing service "
-                      "principal string: %s", inst->xlat_name,
-                      error_message(ret));
+               ERROR("rlm_krb5 (%s): Failed constructing service principal string: %s", inst->xlat_name,
+                     rlm_krb5_error(inst->context, ret));
 
                return -1;
        }
-       
+
        /*
         *      Not necessarily the same as the config item
         */
-       radlog(L_DBG, "rlm_krb5 (%s): Using service principal \"%s\"",
-              inst->xlat_name, princ_name);
-       
+       DEBUG("rlm_krb5 (%s): Using service principal \"%s\"", inst->xlat_name, princ_name);
+
        krb5_free_unparsed_name(inst->context, princ_name);
-       
+
        /*
         *      Setup options for getting credentials and verifying them
         */
-       
+
        /* For some reason the 'init' version of this function is deprecated */
        ret = krb5_get_init_creds_opt_alloc(inst->context, &(inst->gic_options));
        if (ret) {
-               radlog(L_ERR, "rlm_krb5 (%s): Couldn't allocated inital "
-                      "credential options: %s", inst->xlat_name,
-                      error_message(ret));
-               
+               ERROR("rlm_krb5 (%s): Couldn't allocated inital credential options: %s", inst->xlat_name,
+                     rlm_krb5_error(inst->context, ret));
+
                return -1;
        }
-       
+
+       /*
+        *      Perform basic checks on the keytab
+        */
+       ret = inst->keytabname ?
+               krb5_kt_resolve(inst->context, inst->keytabname, &keytab) :
+               krb5_kt_default(inst->context, &keytab);
+       if (ret) {
+               ERROR("rlm_krb5 (%s): Resolving keytab failed: %s", inst->xlat_name,
+                     rlm_krb5_error(inst->context, ret));
+
+               return -1;
+       }
+
+       ret = krb5_kt_get_name(inst->context, keytab, keytab_name, sizeof(keytab_name));
+       krb5_kt_close(inst->context, keytab);
+       if (ret) {
+               ERROR("rlm_krb5 (%s): Can't retrieve keytab name: %s", inst->xlat_name,
+                     rlm_krb5_error(inst->context, ret));
+
+               return -1;
+       }
+
+       DEBUG("rlm_krb5 (%s): Using keytab \"%s\"", inst->xlat_name, keytab_name);
+
        MEM(inst->vic_options = talloc_zero(inst, krb5_verify_init_creds_opt));
-       
        krb5_verify_init_creds_opt_init(inst->vic_options);
-       krb5_verify_init_creds_opt_set_ap_req_nofail(inst->vic_options, TRUE);
-       
 #endif
-       
+
+#ifdef KRB5_IS_THREAD_SAFE
+       /*
+        *      Initialize the socket pool.
+        */
+       inst->pool = fr_connection_pool_init(conf, inst, mod_conn_create, NULL, NULL, NULL);
+       if (!inst->pool) {
+               return -1;
+       }
+#else
+       inst->conn = mod_conn_create(inst, inst);
+       if (!inst->conn) {
+               return -1;
+       }
+#endif
        return 0;
 }
 
-static rlm_rcode_t krb5_parse_user(REQUEST *request, krb5_context context, krb5_principal *client)
+/** Common function for transforming a User-Name string into a principal.
+ *
+ * @param[out] client Where to write the client principal.
+ * @param[in] request Current request.
+ * @param[in] context Kerberos context.
+ */
+static rlm_rcode_t krb5_parse_user(krb5_principal *client, REQUEST *request, krb5_context context)
 {
        krb5_error_code ret;
        char *princ_name;
-       
+
        /*
         *      We can only authenticate user requests which HAVE
         *      a User-Name attribute.
         */
        if (!request->username) {
-               RDEBUGE("Attribute \"User-Name\" is required for authentication");
-               
+               REDEBUG("Attribute \"User-Name\" is required for authentication");
+
                return RLM_MODULE_INVALID;
        }
 
@@ -239,8 +261,8 @@ static rlm_rcode_t krb5_parse_user(REQUEST *request, krb5_context context, krb5_
         *      a User-Password attribute.
         */
        if (!request->password) {
-               RDEBUGE("Attribute \"User-Password\" is required for authentication");
-               
+               REDEBUG("Attribute \"User-Password\" is required for authentication");
+
                return RLM_MODULE_INVALID;
        }
 
@@ -249,191 +271,165 @@ static rlm_rcode_t krb5_parse_user(REQUEST *request, krb5_context context, krb5_
         *      and not anything else.
         */
        if (request->password->da->attr != PW_USER_PASSWORD) {
-               RDEBUGE("Attribute \"User-Password\" is required for authentication.  Cannot use \"%s\".",
-                      request->password->da->name);
-               
+               REDEBUG("Attribute \"User-Password\" is required for authentication.  Cannot use \"%s\".",
+                       request->password->da->name);
+
                return RLM_MODULE_INVALID;
        }
-       
+
        ret = krb5_parse_name(context, request->username->vp_strvalue, client);
        if (ret) {
-               RDEBUGE("Failed parsing username as principal: %s", error_message(ret));
-               
+               REDEBUG("Failed parsing username as principal: %s", rlm_krb5_error(context, ret));
+
                return RLM_MODULE_FAIL;
        }
 
        krb5_unparse_name(context, *client, &princ_name);
        RDEBUG("Using client principal \"%s\"", princ_name);
+#ifdef HEIMDAL_KRB5
+       free(princ_name);
+#else
        krb5_free_unparsed_name(context, princ_name);
-
+#endif
        return RLM_MODULE_OK;
 }
 
+/** Log error message and return appropriate rcode
+ *
+ * Translate kerberos error codes into return codes.
+ * @param request Current request.
+ * @param ret code from kerberos.
+ * @param conn used in the last operation.
+ */
+static rlm_rcode_t krb5_process_error(REQUEST *request, rlm_krb5_handle_t *conn, int ret)
+{
+       rad_assert(ret != 0);
+       rad_assert(conn);       /* Silences warnings */
+
+       switch (ret) {
+       case KRB5_LIBOS_BADPWDMATCH:
+       case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+               REDEBUG("Provided password was incorrect (%i): %s", ret, rlm_krb5_error(conn->context, ret));
+               return RLM_MODULE_REJECT;
+
+       case KRB5KDC_ERR_KEY_EXP:
+       case KRB5KDC_ERR_CLIENT_REVOKED:
+       case KRB5KDC_ERR_SERVICE_REVOKED:
+               REDEBUG("Account has been locked out (%i): %s", ret, rlm_krb5_error(conn->context, ret));
+               return RLM_MODULE_USERLOCK;
+
+       case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
+               RDEBUG("User not found (%i): %s", ret, rlm_krb5_error(conn->context, ret));
+               return RLM_MODULE_NOTFOUND;
+
+       default:
+               REDEBUG("Error verifying credentials (%i): %s", ret, rlm_krb5_error(conn->context, ret));
+               return RLM_MODULE_FAIL;
+       }
+}
+
 #ifdef HEIMDAL_KRB5
 
 /*
  *     Validate user/pass (Heimdal)
  */
-static rlm_rcode_t krb5_auth(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        rlm_krb5_t *inst = instance;
        rlm_rcode_t rcode;
-       
        krb5_error_code ret;
-       
+
+       rlm_krb5_handle_t *conn;
+
        krb5_principal client;
-       krb5_ccache ccache;
-       krb5_keytab keytab;
-       krb5_verify_opt options;
-       krb5_context context;
-       
-       rad_assert(inst->context);
 
 #ifdef KRB5_IS_THREAD_SAFE
-       /*
-        *      See above in MIT krb5_auth
-        */
-       ret = krb5_copy_context(inst->context, &context);
-       if (ret) {
-               RDEBUGE("Error cloning krb5 context: %s", error_message(ret));
-               
-               return RLM_MODULE_FAIL;
-       }
+       conn = fr_connection_get(inst->pool);
+       if (!conn) return RLM_MODULE_FAIL;
 #else
-       context = inst->context;
+       conn = inst->conn;
 #endif
 
        /*
         *      Zero out local storage
         */
-       memset(&keytab, 0, sizeof(keytab));
        memset(&client, 0, sizeof(client));
-       memset(&init_creds, 0, sizeof(init_creds));
-       
+
+       rcode = krb5_parse_user(&client, request, conn->context);
+       if (rcode != RLM_MODULE_OK) goto cleanup;
+
        /*
-        *      Setup krb5_verify_user options
-        *
-        *      Not entirely sure this is necessary, but as we use context
-        *      to get the cache handle, we probably do have to do this with
-        *      the cloned context.
+        *      Verify the user, using the options we set in instantiate
         */
-       krb5_cc_default(context, &ccache);
-       
-       krb5_verify_opt_init(&options);
-       krb5_verify_opt_set_ccache(&options, ccache);
-       ret = inst->keytabname ?
-               krb5_kt_resolve(context, inst->keytabname, &keytab) :
-               krb5_kt_default(context, &keytab);
+       ret = krb5_verify_user_opt(conn->context, client, request->password->vp_strvalue, &conn->options);
        if (ret) {
-               RDEBUGE("Resolving keytab failed: %s", error_message(ret));
-               
+               rcode = krb5_process_error(request, conn, ret);
                goto cleanup;
        }
-       
-       RDEBUG("Using keytab \"%s\"", keytab);
-       
-       krb5_verify_opt_set_keytab(&options, keytab);
-       krb5_verify_opt_set_secure(&options, TRUE);
-
-       if (inst->service) {
-               krb5_verify_opt_set_service(&options, inst->service);
-       }
-       
-       rcode = krb5_parse_user(request, context, &client);
-       if (rcode != RLM_MODULE_OK) goto cleanup;
 
        /*
-        *      Verify the user, using the options we set in instantiate
+        *      krb5_verify_user_opt adds the credentials to the ccache
+        *      we specified with krb5_verify_opt_set_ccache.
+        *
+        *      To make sure we don't accumulate thousands of sets of
+        *      credentials, remove them again here.
+        *
+        * @todo This should definitely be optional, which means writing code for the MIT
+        *       variant as well.
         */
-       ret = krb5_verify_user_opt(context, client,
-                                  request->password->vp_strvalue,
-                                  &options);
-       if (ret) {
-               switch (ret) {
-               case KRB5_LIBOS_BADPWDMATCH:
-               case KRB5KRB_AP_ERR_BAD_INTEGRITY:
-                       RDEBUGE("Provided password was incorrect (%i): %s", ret, error_message(ret));
-                       rcode = RLM_MODULE_REJECT;
-               
-                       break;
-               case KRB5KDC_ERR_KEY_EXP:
-               case KRB5KDC_ERR_CLIENT_REVOKED:
-               case KRB5KDC_ERR_SERVICE_REVOKED:
-                       RDEBUGE("Account has been locked out (%i): %s", ret, error_message(ret));
-                       rcode = RLM_MODULE_USERLOCK;
-               
-                       break;
-               case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
-                       RDEBUG("User not found: %s (%i)", ret, error_message(ret));
-                       rcode = RLM_MODULE_NOTFOUND;
-                                       
-               default:
-                       RDEBUGE("Error verifying credentials (%i): %s", ret, error_message(ret));
-                       rcode = RLM_MODULE_FAIL;
-               
-                       break;
+       {
+               krb5_cc_cursor cursor;
+               krb5_creds cred;
+
+               krb5_cc_start_seq_get(conn->context, conn->ccache, &cursor);
+               for ((ret = krb5_cc_next_cred(conn->context, conn->ccache, &cursor, &cred));
+                    ret == 0;
+                    (ret = krb5_cc_next_cred(conn->context, conn->ccache, &cursor, &cred))) {
+                    krb5_cc_remove_cred(conn->context, conn->ccache, 0, &cred);
                }
-
-               goto cleanup;
+               krb5_cc_end_seq_get(conn->context, conn->ccache, &cursor);
        }
-       
-       cleanup:
+
+cleanup:
        if (client) {
-               krb5_free_principal(context, client);
-       }
-       if (keytab) {
-               krb5_kt_close(context, keytab);
+               krb5_free_principal(conn->context, client);
        }
+
 #ifdef KRB5_IS_THREAD_SAFE
-       krb5_free_context(context);
+       fr_connection_release(inst->pool, conn);
 #endif
        return rcode;
 }
 
-#else
+#else  /* HEIMDAL_KRB5 */
 
 /*
  *  Validate userid/passwd (MIT)
  */
-static rlm_rcode_t krb5_auth(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        rlm_krb5_t *inst = instance;
-       rlm_rcode_t rcode;      
+       rlm_rcode_t rcode;
        krb5_error_code ret;
 
+       rlm_krb5_handle_t *conn;
+
        krb5_principal client;
        krb5_creds init_creds;
-       krb5_keytab keytab;     /* ktid */
-       krb5_context context;
-       
+       char *password;         /* compiler warnings */
+
        rad_assert(inst->context);
 
-#ifdef KRB5_IS_THREAD_SAFE     
-       /*
-        *      All the snippets on threadsafety say that individual threads
-        *      must each use their own copy of context.
-        *
-        *      As we don't have any per thread instantiation, we either have
-        *      to clone inst->context on every request, or use the connection
-        *      API.
-        *
-        *      @todo Use the connection API (3.0 only).
-        */
-       ret = krb5_copy_context(inst->context, &context);
-       if (ret) {
-               RDEBUGE("Error cloning krb5 context: %s", inst->xlat_name, error_message(ret));
-               
-               return RLM_MODULE_FAIL;
-       }
-       rad_assert(context != NULL); /* tell coverity copy context copies it */
+#ifdef KRB5_IS_THREAD_SAFE
+       conn = fr_connection_get(inst->pool);
+       if (!conn) return RLM_MODULE_FAIL;
 #else
-       context = inst->context;
+       conn = inst->conn;
 #endif
 
        /*
         *      Zero out local storage
         */
-       memset(&keytab, 0, sizeof(keytab));
        memset(&client, 0, sizeof(client));
        memset(&init_creds, 0, sizeof(init_creds));
 
@@ -441,99 +437,55 @@ static rlm_rcode_t krb5_auth(void *instance, REQUEST *request)
         *      Check we have all the required VPs, and convert the username
         *      into a principal.
         */
-       rcode = krb5_parse_user(request, context, &client);
+       rcode = krb5_parse_user(&client, request, conn->context);
        if (rcode != RLM_MODULE_OK) goto cleanup;
 
        /*
-        *      Setup the keytab
-        */
-       ret = inst->keytabname ?
-               krb5_kt_resolve(context, inst->keytabname, &keytab) :
-               krb5_kt_default(context, &keytab);
-       if (ret) {
-               RDEBUGE("Resolving keytab failed: %s", inst->xlat_name, error_message(ret));
-               
-               goto cleanup;
-       }
-       
-       RDEBUG("Using keytab \"%s\"", keytab);
-       
-       /*
         *      Retrieve the TGT from the TGS/KDC and check we can decrypt it.
         */
-       ret = krb5_get_init_creds_password(context, &init_creds, client, request->password->vp_strvalue,
+       memcpy(&password, &request->password->vp_strvalue, sizeof(password));
+       RDEBUG("Retrieving and decrypting TGT");
+       ret = krb5_get_init_creds_password(conn->context, &init_creds, client, password,
                                           NULL, NULL, 0, NULL, inst->gic_options);
        if (ret) {
-               error:
-               switch (ret) {
-               case KRB5_LIBOS_BADPWDMATCH:
-               case KRB5KRB_AP_ERR_BAD_INTEGRITY:
-                       RDEBUGE("Provided password was incorrect (%i): %s", ret, error_message(ret));
-                       rcode = RLM_MODULE_REJECT;
-                       break;
-                       
-               case KRB5KDC_ERR_KEY_EXP:
-               case KRB5KDC_ERR_CLIENT_REVOKED:
-               case KRB5KDC_ERR_SERVICE_REVOKED:
-                       RDEBUGE("Account has been locked out (%i): %s", ret, error_message(ret));
-                       rcode = RLM_MODULE_USERLOCK;
-                       break;
-                       
-               case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
-                       RDEBUG("User not found (%i): %s", ret,  error_message(ret));
-                       rcode = RLM_MODULE_NOTFOUND;
-                       break;
-                       
-               default:
-                       RDEBUGE("Error retrieving or verifying credentials (%i): %s", ret, error_message(ret));
-                       rcode = RLM_MODULE_FAIL;
-                       break;
-               }
-
+               rcode = krb5_process_error(request, conn, ret);
                goto cleanup;
        }
-       
-       RDEBUG("Successfully retrieved and decrypted TGT");
 
-       ret = krb5_verify_init_creds(context, &init_creds, inst->server, keytab, NULL, inst->vic_options);
-       if (ret) goto error;
-
-       cleanup:
+       RDEBUG("Attempting to authenticate against service principal");
+       ret = krb5_verify_init_creds(conn->context, &init_creds, inst->server, conn->keytab, NULL, inst->vic_options);
+       if (ret) {
+               rcode = krb5_process_error(request, conn, ret);
+       }
 
-       if (context) {
-               if (client) {
-                       krb5_free_principal(context, client);
-               }
-               if (keytab) {
-                       krb5_kt_close(context, keytab);
-               }
-               
-               krb5_free_cred_contents(context, &init_creds);
+cleanup:
+       if (client) {
+               krb5_free_principal(conn->context, client);
+       }
+       krb5_free_cred_contents(conn->context, &init_creds);
 
 #ifdef KRB5_IS_THREAD_SAFE
-               krb5_free_context(context);
+       fr_connection_release(inst->pool, conn);
 #endif
-       }
-       
        return rcode;
 }
 
-#endif /* HEIMDAL_KRB5 */
+#endif /* MIT_KRB5 */
 
 module_t rlm_krb5 = {
        RLM_MODULE_INIT,
        "krb5",
-       RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE
+       RLM_TYPE_HUP_SAFE
 #ifdef KRB5_IS_THREAD_SAFE
        | RLM_TYPE_THREAD_SAFE
 #endif
        ,
        sizeof(rlm_krb5_t),
        module_config,
-       krb5_instantiate,               /* instantiation */
-       krb5_detach,                    /* detach */
+       mod_instantiate,                /* instantiation */
+       mod_detach,                     /* detach */
        {
-               krb5_auth,              /* authenticate */
+               mod_authenticate,       /* authenticate */
                NULL,                   /* authorize */
                NULL,                   /* pre-accounting */
                NULL,                   /* accounting */