plugin for retrieving and updating KRB5 TGT

This commit is contained in:
Ralf Haferkamp 2010-10-22 14:31:56 +00:00
parent 76c611ad42
commit 026713f497
2 changed files with 331 additions and 0 deletions

View File

@ -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).
Currently no Makefile is provided. 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 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>.

View File

@ -0,0 +1,295 @@
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2010 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 */