mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-04-18 15:20:35 +08:00
plugin for retrieving and updating KRB5 TGT
This commit is contained in:
parent
76c611ad42
commit
026713f497
36
contrib/slapd-modules/kinit/README
Normal file
36
contrib/slapd-modules/kinit/README
Normal 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>.
|
295
contrib/slapd-modules/kinit/kinit.c
Normal file
295
contrib/slapd-modules/kinit/kinit.c
Normal 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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user