summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules/kinit
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/slapd-modules/kinit')
-rw-r--r--contrib/slapd-modules/kinit/Makefile46
-rw-r--r--contrib/slapd-modules/kinit/README36
-rw-r--r--contrib/slapd-modules/kinit/kinit.c295
3 files changed, 377 insertions, 0 deletions
diff --git a/contrib/slapd-modules/kinit/Makefile b/contrib/slapd-modules/kinit/Makefile
new file mode 100644
index 0000000..2a925ed
--- /dev/null
+++ b/contrib/slapd-modules/kinit/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap_r/libldap_r.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2 -Wall
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB) -lkrb5
+
+PROGRAMS = kinit.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(OPT) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+kinit.la: kinit.lo
+ $(LIBTOOL) --mode=link $(CC) $(OPT) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/kinit/README b/contrib/slapd-modules/kinit/README
new file mode 100644
index 0000000..66cb5eb
--- /dev/null
+++ b/contrib/slapd-modules/kinit/README
@@ -0,0 +1,36 @@
+This directory contains the "kinit" slapd module. It is a simple plugin to
+have slapd request a Kerberos TGT and keep it renewed as long as slapd is
+running.
+
+The current implementation has only been tested against the MIT variant of
+the Kerberos libraries. (Heimdal support might come later)
+
+To use the overlay just load it into the slapd process:
+
+ moduleload </path/to>/kinit.so <principal> </path/to/key.tab>
+
+The module accepts two arguments. The first one being the principal for which
+to request the TGT (it defaults to "ldap/<your hostname>@<DEFAULTREALM>")
+and the second one is the path to the keytab file to use for
+authentication, defaulting to whatever your system wide kerberos settings
+default to).
+
+Use Makefile or the following commands should work to
+build it from inside the unpacked slapd sources, provided the required KRB5
+header files and libaries are installed on your system:
+
+ gcc -fPIC -c -I ../../../include/ -I ../../../servers/slapd kinit.c
+ gcc -shared -o kinit.so kinit.o -lkrb5
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2010-2018 The OpenLDAP Foundation.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
diff --git a/contrib/slapd-modules/kinit/kinit.c b/contrib/slapd-modules/kinit/kinit.c
new file mode 100644
index 0000000..2fc79f3
--- /dev/null
+++ b/contrib/slapd-modules/kinit/kinit.c
@@ -0,0 +1,295 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2010-2018 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include <portable.h>
+
+#ifndef SLAPD_MOD_KINIT
+#define SLAPD_MOD_KINIT SLAPD_MOD_DYNAMIC
+#endif
+
+#ifdef SLAPD_MOD_KINIT
+
+#include <slap.h>
+#include "ldap_rq.h"
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <krb5/krb5.h>
+
+typedef struct kinit_data {
+ krb5_context ctx;
+ krb5_ccache ccache;
+ krb5_keytab keytab;
+ krb5_principal princ;
+ krb5_get_init_creds_opt *opts;
+} kinit_data;
+
+static char* principal;
+static char* kt_name;
+static kinit_data *kid;
+
+static void
+log_krb5_errmsg( krb5_context ctx, const char* func, krb5_error_code rc )
+{
+ const char* errmsg = krb5_get_error_message(ctx, rc);
+ Log2(LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, "slapd-kinit: %s: %s\n", func, errmsg);
+ krb5_free_error_message(ctx, errmsg);
+ return;
+}
+
+static int
+kinit_check_tgt(kinit_data *kid, int *remaining)
+{
+ int ret=3;
+ krb5_principal princ;
+ krb5_error_code rc;
+ krb5_cc_cursor cursor;
+ krb5_creds creds;
+ char *name;
+ time_t now=time(NULL);
+
+ rc = krb5_cc_get_principal(kid->ctx, kid->ccache, &princ);
+ if (rc) {
+ log_krb5_errmsg(kid->ctx, "krb5_cc_get_principal", rc);
+ return 2;
+ } else {
+ if (!krb5_principal_compare(kid->ctx, kid->princ, princ)) {
+ Log0(LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "Principal in ccache does not match requested principal\n");
+ krb5_free_principal(kid->ctx, princ);
+ return 2;
+ }
+ }
+
+ rc = krb5_cc_start_seq_get(kid->ctx, kid->ccache, &cursor);
+ if (rc) {
+ log_krb5_errmsg(kid->ctx, "krb5_cc_start_seq_get", rc);
+ krb5_free_principal(kid->ctx, princ);
+ return -1;
+ }
+
+ while (!(rc = krb5_cc_next_cred(kid->ctx, kid->ccache, &cursor, &creds))) {
+ if (krb5_is_config_principal(kid->ctx, creds.server)) {
+ krb5_free_cred_contents(kid->ctx, &creds);
+ continue;
+ }
+
+ if (creds.server->length==2 &&
+ (!strcmp(creds.server->data[0].data, "krbtgt")) &&
+ (!strcmp(creds.server->data[1].data, princ->realm.data))) {
+
+ krb5_unparse_name(kid->ctx, creds.server, &name);
+
+ *remaining = (time_t)creds.times.endtime-now;
+ if ( *remaining <= 0) {
+ Log1(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: TGT (%s) expired\n", name);
+ } else {
+ Log4(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: TGT (%s) expires in %dh:%02dm:%02ds\n",
+ name, *remaining/3600, (*remaining%3600)/60, *remaining%60);
+ }
+ free(name);
+
+ if (*remaining <= 30) {
+ if (creds.times.renew_till-60 > now) {
+ int renewal=creds.times.renew_till-now;
+ Log3(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Time remaining for renewal: %dh:%02dm:%02ds\n",
+ renewal/3600, (renewal%3600)/60, renewal%60);
+ ret = 1;
+ } else {
+ Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Only short time left for renewal. "
+ "Trying to re-init.\n");
+ ret = 2;
+ }
+ } else {
+ ret=0;
+ }
+ krb5_free_cred_contents(kid->ctx, &creds);
+ break;
+ }
+ krb5_free_cred_contents(kid->ctx, &creds);
+
+ }
+ krb5_cc_end_seq_get(kid->ctx, kid->ccache, &cursor);
+ krb5_free_principal(kid->ctx, princ);
+ return ret;
+}
+
+void*
+kinit_qtask( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ kinit_data *kid = (kinit_data*)rtask->arg;
+ krb5_error_code rc;
+ krb5_creds creds;
+ int nextcheck, remaining, renew=0;
+ Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "kinit_qtask: running TGT check\n");
+
+ memset(&creds, 0, sizeof(creds));
+
+ renew = kinit_check_tgt(kid, &remaining);
+
+ if (renew > 0) {
+ if (renew==1) {
+ Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Trying to renew TGT: ");
+ rc = krb5_get_renewed_creds(kid->ctx, &creds, kid->princ, kid->ccache, NULL);
+ if (rc!=0) {
+ Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Failed\n");
+ log_krb5_errmsg( kid->ctx,
+ "kinit_qtask, Renewal failed: krb5_get_renewed_creds", rc );
+ renew++;
+ } else {
+ Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Success\n");
+ krb5_cc_initialize(kid->ctx, kid->ccache, creds.client);
+ krb5_cc_store_cred(kid->ctx, kid->ccache, &creds);
+ krb5_free_cred_contents(kid->ctx, &creds);
+ renew=kinit_check_tgt(kid, &remaining);
+ }
+ }
+ if (renew > 1) {
+ Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Trying to get new TGT: ");
+ rc = krb5_get_init_creds_keytab( kid->ctx, &creds, kid->princ,
+ kid->keytab, 0, NULL, kid->opts);
+ if (rc) {
+ Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Failed\n");
+ log_krb5_errmsg(kid->ctx, "krb5_get_init_creds_keytab", rc);
+ } else {
+ Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Success\n");
+ renew=kinit_check_tgt(kid, &remaining);
+ }
+ krb5_free_cred_contents(kid->ctx, &creds);
+ }
+ }
+ if (renew == 0) {
+ nextcheck = remaining-30;
+ } else {
+ nextcheck = 60;
+ }
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ }
+ Log3(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Next TGT check in %dh:%02dm:%02ds\n",
+ nextcheck/3600, (nextcheck%3600)/60, nextcheck%60);
+ rtask->interval.tv_sec = nextcheck;
+ ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
+ slap_wake_listener();
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ return NULL;
+}
+
+int
+kinit_initialize(void)
+{
+ Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "kinit_initialize\n" );
+ krb5_error_code rc;
+ struct re_s *task = NULL;
+
+ kid = ch_calloc(1, sizeof(kinit_data) );
+
+ rc = krb5_init_context( &kid->ctx );
+ if ( !rc )
+ rc = krb5_cc_default(kid->ctx, &kid->ccache );
+
+ if ( !rc ) {
+ if (!principal) {
+ int len=STRLENOF("ldap/")+global_host_bv.bv_len+1;
+ principal=ch_calloc(len, 1);
+ snprintf(principal, len, "ldap/%s", global_host_bv.bv_val);
+ Log1(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Principal <%s>\n", principal);
+
+ }
+ rc = krb5_parse_name(kid->ctx, principal, &kid->princ);
+ }
+
+ if ( !rc && kt_name) {
+ rc = krb5_kt_resolve(kid->ctx, kt_name, &kid->keytab);
+ }
+
+ if ( !rc )
+ rc = krb5_get_init_creds_opt_alloc(kid->ctx, &kid->opts);
+
+ if ( !rc )
+ rc = krb5_get_init_creds_opt_set_out_ccache( kid->ctx, kid->opts, kid->ccache);
+
+ if ( !rc ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ task = ldap_pvt_runqueue_insert( &slapd_rq, 10, kinit_qtask, (void*)kid,
+ "kinit_qtask", "ldap/bronsted.g17.lan@G17.LAN" );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+
+ if (rc) {
+ log_krb5_errmsg(kid->ctx, "kinit_initialize", rc);
+ rc = -1;
+ }
+ return rc;
+}
+
+#if SLAPD_MOD_KINIT == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ if (argc > 0) {
+ principal = ch_strdup(argv[0]);
+ }
+ if (argc > 1) {
+ kt_name = ch_strdup(argv[1]);
+ }
+ if (argc > 2) {
+ return -1;
+ }
+ return kinit_initialize();
+}
+
+int
+term_module() {
+ if (principal)
+ ch_free(principal);
+ if (kt_name)
+ ch_free(kt_name);
+ if (kid) {
+ struct re_s *task;
+
+ task=ldap_pvt_runqueue_find( &slapd_rq, kinit_qtask, (void*)kid);
+ if (task) {
+ if ( ldap_pvt_runqueue_isrunning(&slapd_rq, task) ) {
+ ldap_pvt_runqueue_stoptask(&slapd_rq, task);
+ }
+ ldap_pvt_runqueue_remove(&slapd_rq, task);
+ }
+ if ( kid->ctx ) {
+ if ( kid->princ )
+ krb5_free_principal(kid->ctx, kid->princ);
+ if ( kid->ccache )
+ krb5_cc_close(kid->ctx, kid->ccache);
+ if ( kid->keytab )
+ krb5_kt_close(kid->ctx, kid->keytab);
+ if ( kid->opts )
+ krb5_get_init_creds_opt_free(kid->ctx, kid->opts);
+ krb5_free_context(kid->ctx);
+ }
+ ch_free(kid);
+ }
+ return 0;
+}
+#endif
+
+#endif /* SLAPD_MOD_KINIT */
+