summaryrefslogtreecommitdiffstats
path: root/src/modules/rlm_krb5/krb5.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/rlm_krb5/krb5.c')
-rw-r--r--src/modules/rlm_krb5/krb5.c166
1 files changed, 166 insertions, 0 deletions
diff --git a/src/modules/rlm_krb5/krb5.c b/src/modules/rlm_krb5/krb5.c
new file mode 100644
index 0000000..153a10c
--- /dev/null
+++ b/src/modules/rlm_krb5/krb5.c
@@ -0,0 +1,166 @@
+/*
+ * 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
+ * (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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file krb5.h
+ * @brief Context management functions for rlm_krb5
+ *
+ * @copyright 2013 The FreeRADIUS server project
+ * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include "krb5.h"
+
+#ifdef HAVE_KRB5_GET_ERROR_MESSAGE
+# define KRB5_STRERROR_BUFSIZE (2048)
+
+fr_thread_local_setup(char *, krb5_error_buffer) /* macro */
+
+/*
+ * Explicitly cleanup the memory allocated to the error buffer.
+ */
+static void _krb5_logging_free(void *arg)
+{
+ free(arg);
+}
+
+char const *rlm_krb5_error(krb5_context context, krb5_error_code code)
+{
+ char const *msg;
+ char *buffer;
+
+ buffer = fr_thread_local_init(krb5_error_buffer, _krb5_logging_free);
+ if (!buffer) {
+ int ret;
+
+ /*
+ * malloc is thread safe, talloc is not
+ */
+ buffer = malloc(sizeof(char) * KRB5_STRERROR_BUFSIZE);
+ if (!buffer) {
+ ERROR("Failed allocating memory for krb5 error buffer");
+ return NULL;
+ }
+
+ ret = fr_thread_local_set(krb5_error_buffer, buffer);
+ if (ret != 0) {
+ ERROR("Failed setting up TLS for krb5 error buffer: %s", fr_syserror(ret));
+ free(buffer);
+ return NULL;
+ }
+ }
+
+ msg = krb5_get_error_message(context, code);
+ if (msg) {
+ strlcpy(buffer, msg, KRB5_STRERROR_BUFSIZE);
+# ifdef HAVE_KRB5_FREE_ERROR_MESSAGE
+ krb5_free_error_message(context, msg);
+# elif defined(HAVE_KRB5_FREE_ERROR_STRING)
+ {
+ char *free;
+
+ memcpy(&free, &msg, sizeof(free));
+ krb5_free_error_string(context, free);
+ }
+# else
+# error "No way to free error strings, missing krb5_free_error_message() and krb5_free_error_string()"
+# endif
+ } else {
+ strlcpy(buffer, "Unknown error", KRB5_STRERROR_BUFSIZE);
+ }
+
+ return buffer;
+}
+#endif
+
+/** Frees libkrb5 resources associated with the handle
+ *
+ * Must not be called directly.
+ *
+ * @param conn to free.
+ * @return 0 (always indicates success).
+ */
+static int _mod_conn_free(rlm_krb5_handle_t *conn) {
+ krb5_free_context(conn->context);
+
+ if (conn->keytab) krb5_kt_close(conn->context, conn->keytab);
+
+#ifdef HEIMDAL_KRB5
+ if (conn->ccache) krb5_cc_destroy(conn->context, conn->ccache);
+#endif
+
+ return 0;
+}
+
+/** Create and return a new connection
+ *
+ * libkrb5(s) can talk to the KDC over TCP. Were assuming something sane is implemented
+ * by libkrb5 and that it does connection caching associated with contexts, so it's
+ * worth using a connection pool to preserve connections when workers die.
+ */
+void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
+{
+ rlm_krb5_t *inst = instance;
+ rlm_krb5_handle_t *conn;
+ krb5_error_code ret;
+
+ MEM(conn = talloc_zero(ctx, rlm_krb5_handle_t));
+ ret = krb5_init_context(&conn->context);
+ if (ret) {
+ ERROR("rlm_krb5 (%s): Context initialisation failed: %s", inst->xlat_name,
+ rlm_krb5_error(NULL, ret));
+
+ return NULL;
+ }
+ talloc_set_destructor(conn, _mod_conn_free);
+
+ ret = inst->keytabname ?
+ krb5_kt_resolve(conn->context, inst->keytabname, &conn->keytab) :
+ krb5_kt_default(conn->context, &conn->keytab);
+ if (ret) {
+ ERROR("Resolving keytab failed: %s", rlm_krb5_error(conn->context, ret));
+
+ goto cleanup;
+ }
+
+#ifdef HEIMDAL_KRB5
+ ret = krb5_cc_new_unique(conn->context, "MEMORY", NULL, &conn->ccache);
+ if (ret) {
+ ERROR("rlm_krb5 (%s): Credential cache creation failed: %s", inst->xlat_name,
+ rlm_krb5_error(conn->context, ret));
+
+ return NULL;
+ }
+
+ krb5_verify_opt_init(&conn->options);
+ krb5_verify_opt_set_ccache(&conn->options, conn->ccache);
+
+ krb5_verify_opt_set_keytab(&conn->options, conn->keytab);
+ krb5_verify_opt_set_secure(&conn->options, true);
+
+ if (inst->service) krb5_verify_opt_set_service(&conn->options, inst->service);
+#else
+ krb5_verify_init_creds_opt_set_ap_req_nofail(inst->vic_options, true);
+#endif
+ return conn;
+
+cleanup:
+ talloc_free(conn);
+ return NULL;
+}