/*
- * rlm_krb5.c module to authenticate against krb5
- *
- * Version: $Id$
- *
* 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 2 of the License, or
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file rlm_krb5.c
+ * @brief Authenticate users, retrieving their TGT from a Kerberos V5 TDC.
*
- * Copyright 2000 The FreeRADIUS server project
- * Copyright 2000 Nathan Neulinger <nneul@umr.edu>
- * Copyright 2000 Alan DeKok <aland@ox.org>
+ * @copyright 2000,2006,2012-2013 The FreeRADIUS server project
+ * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2000 Nathan Neulinger <nneul@umr.edu>
+ * @copyright 2000 Alan DeKok <aland@ox.org>
*/
+RCSID("$Id$")
+
+#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", 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 const char rcsid[] = "$Id$";
+static int mod_detach(void *instance)
+{
+ rlm_krb5_t *inst = instance;
-#include "autoconf.h"
-#include "libradius.h"
+#ifndef HEIMDAL_KRB5
+ talloc_free(inst->vic_options);
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+ if (inst->gic_options) {
+ krb5_get_init_creds_opt_free(inst->context, inst->gic_options);
+ }
-#include "radiusd.h"
-#include "modules.h"
+ if (inst->server) {
+ krb5_free_principal(inst->context, inst->server);
+ }
+#endif
-/* krb5 includes */
-#include <krb5.h>
-#include <com_err.h>
+ /* Don't free hostname, it's just a pointer into service_princ */
+ talloc_free(inst->service);
-typedef struct rlm_krb5_t {
- const char *keytab;
- const char *service_princ;
- krb5_context *context;
-} rlm_krb5_t;
+ if (inst->context) {
+ krb5_free_context(inst->context);
+ }
+#ifdef KRB5_IS_THREAD_SAFE
+ fr_connection_pool_delete(inst->pool);
+#endif
-static CONF_PARSER module_config[] = {
- { "keytab", PW_TYPE_STRING_PTR,
- offsetof(rlm_krb5_t,keytab), NULL, NULL },
- { "service_principal", PW_TYPE_STRING_PTR,
- offsetof(rlm_krb5_t,service_princ), NULL, NULL },
- { NULL, -1, 0, NULL, NULL }
-};
+ return 0;
+}
-#ifndef HEIMDAL_KRB5
-static int verify_krb5_tgt(krb5_context context, rlm_krb5_t *instance,
- const char *user, krb5_ccache ccache)
+static int mod_instantiate(CONF_SECTION *conf, void *instance)
{
- int r;
- char phost[BUFSIZ];
- krb5_principal princ;
- krb5_keyblock *keyblock = 0;
- krb5_data packet;
- krb5_auth_context auth_context = NULL;
+ rlm_krb5_t *inst = instance;
+ krb5_error_code ret;
+#ifndef HEIMDAL_KRB5
krb5_keytab keytab;
- /* arbitrary 64-byte limit on service names; I've never seen a
- service name this long, and hope never to. -srl */
- char service[64] = "host";
- char *servername = NULL;
-
- if (instance->service_princ != NULL) {
- servername = strchr(instance->service_princ, '/');
- if (servername != NULL) {
- *servername = '\0';
- }
+ char keytab_name[200];
+ char *princ_name;
+#endif
- strncpy(service,instance->service_princ,sizeof(service));
- service[sizeof(service)-1] = '\0';
+#ifdef HEIMDAL_KRB5
+ DEBUG("Using Heimdal Kerberos library");
+#else
+ DEBUG("Using MIT Kerberos library");
+#endif
- if (servername != NULL) {
- *servername = '/';
- servername++;
- }
+ if (!krb5_is_thread_safe()) {
+/*
+ * 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 {
+ 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
}
- memset(&packet, 0, sizeof packet);
- if ((r = krb5_sname_to_principal(context, servername, service,
- KRB5_NT_SRV_HST, &princ)))
- {
- radlog(L_DBG, "rlm_krb5: [%s] krb5_sname_to_principal failed: %s",
- user, error_message(r));
- return RLM_MODULE_REJECT;
+ inst->xlat_name = cf_section_name2(conf);
+ if (!inst->xlat_name) {
+ inst->xlat_name = cf_section_name1(conf);
}
- strncpy(phost, krb5_princ_component(c, princ, 1)->data, BUFSIZ);
- phost[BUFSIZ - 1] = '\0';
+ ret = krb5_init_context(&inst->context);
+ if (ret) {
+ ERROR("rlm_krb5 (%s): context initialisation failed: %s", inst->xlat_name,
+ rlm_krb5_error(NULL, ret));
+
+ return -1;
+ }
/*
- * Do we have host/<host> keys?
- * (use default/configured keytab, kvno IGNORE_VNO to get the
- * first match, and enctype is currently ignored anyhow.)
+ * Split service principal into service and host components
+ * they're needed to build the server principal in MIT,
+ * and to set the validation service in Heimdal.
*/
- if ((r = krb5_kt_read_service_key(context, instance->keytab, princ, 0,
- ENCTYPE_DES_CBC_MD5, &keyblock)))
- {
- /* Keytab or service key does not exist */
- radlog(L_DBG, "rlm_krb5: verify_krb_v5_tgt: host key not found : %s",
- error_message(r));
- return RLM_MODULE_OK;
- }
- if (keyblock)
- krb5_free_keyblock(context, keyblock);
-
- /* Talk to the kdc and construct the ticket. */
- r = krb5_mk_req(context, &auth_context, 0, service, phost, NULL,
- ccache, &packet);
- if (auth_context) {
- krb5_auth_con_free(context, auth_context);
- auth_context = NULL; /* setup for rd_req */
- }
+ if (inst->service_princ) {
+ 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);
+ inst->hostname++;
+ } else {
+ len = strlen(inst->service_princ);
+ }
- if (r) {
- radlog(L_DBG, "rlm_krb5: [%s] krb5_mk_req() failed: %s",
- user, error_message(r));
- r = RLM_MODULE_REJECT;
- goto cleanup;
+ if (len) {
+ inst->service = talloc_array(inst, char, (len + 1));
+ strlcpy(inst->service, inst->service_princ, len + 1);
+ }
}
- if (instance->keytab != NULL) {
- r = krb5_kt_resolve(context, instance->keytab, &keytab);
+#ifdef HEIMDAL_KRB5
+ if (inst->hostname) {
+ DEBUG("rlm_krb5 (%s): Ignoring hostname component of service principal \"%s\", not "
+ "needed/supported by Heimdal", inst->xlat_name, inst->hostname);
}
+#else
- if (instance->keytab == NULL || r) {
- r = krb5_kt_default(context, &keytab);
- }
+ /*
+ * 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));
+ if (ret) {
+ ERROR("rlm_krb5 (%s): Failed parsing service principal: %s", inst->xlat_name,
+ rlm_krb5_error(inst->context, ret));
- /* Hmm? The keytab was just fine a second ago! */
- if (r) {
- radlog(L_AUTH, "rlm_krb5: [%s] krb5_kt_resolve failed: %s",
- user, error_message(r));
- r = RLM_MODULE_REJECT;
- goto cleanup;
+ return -1;
}
- /* Try to use the ticket. */
- r = krb5_rd_req(context, &auth_context, &packet, princ,
- keytab, NULL, NULL);
- if (auth_context)
- krb5_auth_con_free(context, auth_context);
-
- krb5_kt_close(context, keytab);
+ ret = krb5_unparse_name(inst->context, inst->server, &princ_name);
+ if (ret) {
+ /* Uh? */
+ ERROR("rlm_krb5 (%s): Failed constructing service principal string: %s", inst->xlat_name,
+ rlm_krb5_error(inst->context, ret));
- if (r) {
- radlog(L_AUTH, "rlm_krb5: [%s] krb5_rd_req() failed: %s",
- user, error_message(r));
- r = RLM_MODULE_REJECT;
- } else {
- r = RLM_MODULE_OK;
+ return -1;
}
-cleanup:
- if (packet.data)
- krb5_free_data_contents(context, &packet);
- return r;
-}
-#endif
+ /*
+ * Not necessarily the same as the config item
+ */
+ DEBUG("rlm_krb5 (%s): Using service principal \"%s\"", inst->xlat_name, princ_name);
-/* instantiate */
-static int krb5_instantiate(CONF_SECTION *conf, void **instance)
-{
- int r;
- rlm_krb5_t *data;
- krb5_context *context;
+ 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) {
+ ERROR("rlm_krb5 (%s): Couldn't allocated inital credential options: %s", inst->xlat_name,
+ rlm_krb5_error(inst->context, ret));
- data = rad_malloc(sizeof(*data));
+ return -1;
+ }
- memset(data, 0, sizeof(*data));
+ /*
+ * 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));
- if (cf_section_parse(conf, data, module_config) < 0) {
- free(data);
return -1;
}
- context = data->context = rad_malloc(sizeof(*context));
+ 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));
- if ((r = krb5_init_context(context)) ) {
- radlog(L_AUTH, "rlm_krb5: krb5_init failed: %s",
- error_message(r));
- free(data);
- return -1;
- } else {
- radlog(L_AUTH, "rlm_krb5: krb5_init ok");
+ return -1;
}
- *instance = data;
- return 0;
-}
+ DEBUG("rlm_krb5 (%s): Using keytab \"%s\"", inst->xlat_name, keytab_name);
-/* detach */
-static int krb5_detach(void *instance)
-{
- free(((rlm_krb5_t *)instance)->context);
- free(instance);
+ MEM(inst->vic_options = talloc_zero(inst, krb5_verify_init_creds_opt));
+ krb5_verify_init_creds_opt_init(inst->vic_options);
+#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;
}
-/* validate userid/passwd */
-/* MIT case */
-#ifndef HEIMDAL_KRB5
-static int krb5_auth(void *instance, REQUEST *request)
+/** 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)
{
- int r;
-
- krb5_data tgtname = {
- 0,
- KRB5_TGS_NAME_SIZE,
- KRB5_TGS_NAME
- };
- krb5_creds kcreds;
- krb5_ccache ccache;
- char cache_name[L_tmpnam + 8];
-
- krb5_context context = *((rlm_krb5_t *)instance)->context; /* copy data */
- const char *user, *pass;
+ krb5_error_code ret;
+ char *princ_name;
/*
- * We can only authenticate user requests which HAVE
- * a User-Name attribute.
+ * We can only authenticate user requests which HAVE
+ * a User-Name attribute.
*/
if (!request->username) {
- radlog(L_AUTH, "rlm_krb5: Attribute \"User-Name\" is required for authentication.");
+ REDEBUG("Attribute \"User-Name\" is required for authentication");
+
return RLM_MODULE_INVALID;
}
/*
- * We can only authenticate user requests which HAVE
- * a User-Password attribute.
+ * We can only authenticate user requests which HAVE
+ * a User-Password attribute.
*/
if (!request->password) {
- radlog(L_AUTH, "rlm_krb5: Attribute \"User-Password\" is required for authentication.");
+ REDEBUG("Attribute \"User-Password\" is required for authentication");
+
return RLM_MODULE_INVALID;
}
/*
- * Ensure that we're being passed a plain-text password,
- * and not anything else.
+ * Ensure that we're being passed a plain-text password,
+ * and not anything else.
*/
- if (request->password->attribute != PW_PASSWORD) {
- radlog(L_AUTH, "rlm_krb5: Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
+ if (request->password->da->attr != PW_USER_PASSWORD) {
+ REDEBUG("Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".",
+ request->password->da->name);
+
return RLM_MODULE_INVALID;
}
- /*
- * shortcuts
- */
- user = request->username->strvalue;
- pass = request->password->strvalue;
+ ret = krb5_parse_name(context, request->username->vp_strvalue, client);
+ if (ret) {
+ REDEBUG("Failed parsing username as principal: %s", rlm_krb5_error(context, ret));
- /* Generate a unique cache_name */
- memset(cache_name, 0, sizeof(cache_name));
- strcpy(cache_name, "MEMORY:");
- (void) tmpnam(&cache_name[7]);
+ 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 */
- if ((r = krb5_cc_resolve(context, cache_name, &ccache))) {
- radlog(L_AUTH, "rlm_krb5: [%s] krb5_cc_resolve(): %s",
- user, error_message(r));
+ 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 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;
+
+#ifdef KRB5_IS_THREAD_SAFE
+ conn = fr_connection_get(inst->pool);
+ if (!conn) return RLM_MODULE_FAIL;
+#else
+ conn = inst->conn;
+#endif
/*
- * Actually perform the authentication
+ * Zero out local storage
*/
- memset((char *)&kcreds, 0, sizeof(kcreds));
+ memset(&client, 0, sizeof(client));
- if ( (r = krb5_parse_name(context, user, &kcreds.client)) ) {
- radlog(L_AUTH, "rlm_krb5: [%s] krb5_parse_name failed: %s",
- user, error_message(r));
- return RLM_MODULE_REJECT;
- }
+ rcode = krb5_parse_user(&client, request, conn->context);
+ if (rcode != RLM_MODULE_OK) goto cleanup;
- if ((r = krb5_cc_initialize(context, ccache, kcreds.client))) {
- radlog(L_AUTH, "rlm_krb5: [%s] krb5_cc_initialize(): %s",
- user, error_message(r));
- return RLM_MODULE_REJECT;
+ /*
+ * Verify the user, using the options we set in instantiate
+ */
+ ret = krb5_verify_user_opt(conn->context, client, request->password->vp_strvalue, &conn->options);
+ if (ret) {
+ rcode = krb5_process_error(request, conn, ret);
+ goto cleanup;
}
/*
- * MIT krb5 verification
+ * 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.
*/
- if ( (r = krb5_build_principal_ext(context, &kcreds.server,
- krb5_princ_realm(context, kcreds.client)->length,
- krb5_princ_realm(context, kcreds.client)->data,
- tgtname.length,
- tgtname.data,
- krb5_princ_realm(context, kcreds.client)->length,
- krb5_princ_realm(context, kcreds.client)->data,
- 0)) ) {
- radlog(L_AUTH, "rlm_krb5: [%s] krb5_build_principal_ext failed: %s",
- user, error_message(r));
- krb5_cc_destroy(context, ccache);
- return RLM_MODULE_REJECT;
+ {
+ 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);
+ }
+ krb5_cc_end_seq_get(conn->context, conn->ccache, &cursor);
}
- if ( (r = krb5_get_in_tkt_with_password(context,
- 0, NULL, NULL, NULL, pass, ccache, &kcreds, 0)) ) {
- radlog(L_AUTH, "rlm_krb5: [%s] krb5_g_i_t_w_p failed: %s",
- user, error_message(r));
- krb5_free_cred_contents(context, &kcreds);
- krb5_cc_destroy(context, ccache);
- return RLM_MODULE_REJECT;
- } else {
- /* Now verify the KDC's identity. */
- r = verify_krb5_tgt(context, (rlm_krb5_t *)instance, user, ccache);
- krb5_free_cred_contents(context, &kcreds);
- krb5_cc_destroy(context, ccache);
- return r;
+cleanup:
+ if (client) {
+ krb5_free_principal(conn->context, client);
}
- return RLM_MODULE_REJECT;
+#ifdef KRB5_IS_THREAD_SAFE
+ fr_connection_release(inst->pool, conn);
+#endif
+ return rcode;
}
-#else /* HEIMDAL_KRB5 */
+#else /* HEIMDAL_KRB5 */
-/* validate user/pass, heimdal krb5 way */
-static int krb5_auth(void *instance, REQUEST *request)
+/*
+ * Validate userid/passwd (MIT)
+ */
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
{
- int r;
+ rlm_krb5_t *inst = instance;
+ rlm_rcode_t rcode;
krb5_error_code ret;
- krb5_ccache id;
- krb5_principal userP;
- krb5_context context = *((rlm_krb5_t *)instance)->context; /* copy data */
- const char *user, *pass;
+ rlm_krb5_handle_t *conn;
- /*
- * We can only authenticate user requests which HAVE
- * a User-Name attribute.
- */
- if (!request->username) {
- radlog(L_AUTH, "rlm_krb5: Attribute \"User-Name\" is required for authentication.");
- return RLM_MODULE_INVALID;
- }
+ krb5_principal client;
+ krb5_creds init_creds;
+ char *password; /* compiler warnings */
- /*
- * We can only authenticate user requests which HAVE
- * a User-Password attribute.
- */
- if (!request->password) {
- radlog(L_AUTH, "rlm_krb5: Attribute \"User-Password\" is required for authentication.");
- return RLM_MODULE_INVALID;
- }
+ rad_assert(inst->context);
+
+#ifdef KRB5_IS_THREAD_SAFE
+ conn = fr_connection_get(inst->pool);
+ if (!conn) return RLM_MODULE_FAIL;
+#else
+ conn = inst->conn;
+#endif
/*
- * Ensure that we're being passed a plain-text password,
- * and not anything else.
+ * Zero out local storage
*/
- if (request->password->attribute != PW_PASSWORD) {
- radlog(L_AUTH, "rlm_krb5: Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
- return RLM_MODULE_INVALID;
- }
+ memset(&client, 0, sizeof(client));
+ memset(&init_creds, 0, sizeof(init_creds));
/*
- * shortcuts
+ * Check we have all the required VPs, and convert the username
+ * into a principal.
*/
- user = request->username->strvalue;
- pass = request->password->strvalue;
-
- if ( (r = krb5_parse_name(context, user, &userP)) ) {
- radlog(L_AUTH, "rlm_krb5: [%s] krb5_parse_name failed: %s",
- user, error_message(r));
- return RLM_MODULE_REJECT;
- }
+ rcode = krb5_parse_user(&client, request, conn->context);
+ if (rcode != RLM_MODULE_OK) goto cleanup;
/*
- * Heimdal krb5 verification
+ * Retrieve the TGT from the TGS/KDC and check we can decrypt it.
*/
- radlog(L_AUTH, "rlm_krb5: Parsed name is: %s@%s\n",
- *userP->name.name_string.val,
- userP->realm);
-
- krb5_cc_default(context, &id);
-
- ret = krb5_verify_user(context,
- userP,
- id,
- pass, 1, "radius");
+ 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) {
+ rcode = krb5_process_error(request, conn, ret);
+ goto cleanup;
+ }
- if (ret == 0)
- return RLM_MODULE_OK;
+ 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);
+ }
- radlog(L_AUTH, "rlm_krb5: failed verify_user: %s (%s@%s )",
- error_message(ret),
- *userP->name.name_string.val,
- userP->realm);
+cleanup:
+ if (client) {
+ krb5_free_principal(conn->context, client);
+ }
+ krb5_free_cred_contents(conn->context, &init_creds);
- return RLM_MODULE_REJECT;
+#ifdef KRB5_IS_THREAD_SAFE
+ fr_connection_release(inst->pool, conn);
+#endif
+ return rcode;
}
-#endif /* HEIMDAL_KRB5 */
+#endif /* MIT_KRB5 */
module_t rlm_krb5 = {
- "Kerberos",
- RLM_TYPE_THREAD_UNSAFE, /* type: not thread safe */
- NULL, /* initialize */
- krb5_instantiate, /* instantiation */
- {
- krb5_auth, /* authenticate */
- NULL, /* authorize */
- NULL, /* pre-accounting */
- NULL, /* accounting */
- NULL, /* checksimul */
- NULL, /* pre-proxy */
- NULL, /* post-proxy */
- NULL /* post-auth */
- },
- krb5_detach, /* detach */
- NULL, /* destroy */
+ RLM_MODULE_INIT,
+ "krb5",
+ RLM_TYPE_HUP_SAFE
+#ifdef KRB5_IS_THREAD_SAFE
+ | RLM_TYPE_THREAD_SAFE
+#endif
+ ,
+ sizeof(rlm_krb5_t),
+ module_config,
+ mod_instantiate, /* instantiation */
+ mod_detach, /* detach */
+ {
+ mod_authenticate, /* authenticate */
+ NULL, /* authorize */
+ NULL, /* pre-accounting */
+ NULL, /* accounting */
+ NULL, /* checksimul */
+ NULL, /* pre-proxy */
+ NULL, /* post-proxy */
+ NULL /* post-auth */
+ },
};