1/* $OpenLDAP$ */ 2/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 2010-2011 The OpenLDAP Foundation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted only as authorized by the OpenLDAP 9 * Public License. 10 * 11 * A copy of this license is available in the file LICENSE in the 12 * top-level directory of the distribution or, alternatively, at 13 * <http://www.OpenLDAP.org/license.html>. 14 */ 15 16#include <portable.h> 17 18#ifndef SLAPD_MOD_KINIT 19#define SLAPD_MOD_KINIT SLAPD_MOD_DYNAMIC 20#endif 21 22#ifdef SLAPD_MOD_KINIT 23 24#include <slap.h> 25#include "ldap_rq.h" 26#include <ac/errno.h> 27#include <ac/string.h> 28#include <krb5/krb5.h> 29 30typedef struct kinit_data { 31 krb5_context ctx; 32 krb5_ccache ccache; 33 krb5_keytab keytab; 34 krb5_principal princ; 35 krb5_get_init_creds_opt *opts; 36} kinit_data; 37 38static char* principal; 39static char* kt_name; 40static kinit_data *kid; 41 42static void 43log_krb5_errmsg( krb5_context ctx, const char* func, krb5_error_code rc ) 44{ 45 const char* errmsg = krb5_get_error_message(ctx, rc); 46 Log2(LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, "slapd-kinit: %s: %s\n", func, errmsg); 47 krb5_free_error_message(ctx, errmsg); 48 return; 49} 50 51static int 52kinit_check_tgt(kinit_data *kid, int *remaining) 53{ 54 int ret=3; 55 krb5_principal princ; 56 krb5_error_code rc; 57 krb5_cc_cursor cursor; 58 krb5_creds creds; 59 char *name; 60 time_t now=time(NULL); 61 62 rc = krb5_cc_get_principal(kid->ctx, kid->ccache, &princ); 63 if (rc) { 64 log_krb5_errmsg(kid->ctx, "krb5_cc_get_principal", rc); 65 return 2; 66 } else { 67 if (!krb5_principal_compare(kid->ctx, kid->princ, princ)) { 68 Log0(LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 69 "Principal in ccache does not match requested principal\n"); 70 krb5_free_principal(kid->ctx, princ); 71 return 2; 72 } 73 } 74 75 rc = krb5_cc_start_seq_get(kid->ctx, kid->ccache, &cursor); 76 if (rc) { 77 log_krb5_errmsg(kid->ctx, "krb5_cc_start_seq_get", rc); 78 krb5_free_principal(kid->ctx, princ); 79 return -1; 80 } 81 82 while (!(rc = krb5_cc_next_cred(kid->ctx, kid->ccache, &cursor, &creds))) { 83 if (krb5_is_config_principal(kid->ctx, creds.server)) { 84 krb5_free_cred_contents(kid->ctx, &creds); 85 continue; 86 } 87 88 if (creds.server->length==2 && 89 (!strcmp(creds.server->data[0].data, "krbtgt")) && 90 (!strcmp(creds.server->data[1].data, princ->realm.data))) { 91 92 krb5_unparse_name(kid->ctx, creds.server, &name); 93 94 *remaining = (time_t)creds.times.endtime-now; 95 if ( *remaining <= 0) { 96 Log1(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, 97 "kinit_qtask: TGT (%s) expired\n", name); 98 } else { 99 Log4(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, 100 "kinit_qtask: TGT (%s) expires in %dh:%02dm:%02ds\n", 101 name, *remaining/3600, (*remaining%3600)/60, *remaining%60); 102 } 103 free(name); 104 105 if (*remaining <= 30) { 106 if (creds.times.renew_till-60 > now) { 107 int renewal=creds.times.renew_till-now; 108 Log3(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, 109 "kinit_qtask: Time remaining for renewal: %dh:%02dm:%02ds\n", 110 renewal/3600, (renewal%3600)/60, renewal%60); 111 ret = 1; 112 } else { 113 Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, 114 "kinit_qtask: Only short time left for renewal. " 115 "Trying to re-init.\n"); 116 ret = 2; 117 } 118 } else { 119 ret=0; 120 } 121 krb5_free_cred_contents(kid->ctx, &creds); 122 break; 123 } 124 krb5_free_cred_contents(kid->ctx, &creds); 125 126 } 127 krb5_cc_end_seq_get(kid->ctx, kid->ccache, &cursor); 128 krb5_free_principal(kid->ctx, princ); 129 return ret; 130} 131 132void* 133kinit_qtask( void *ctx, void *arg ) 134{ 135 struct re_s *rtask = arg; 136 kinit_data *kid = (kinit_data*)rtask->arg; 137 krb5_error_code rc; 138 krb5_creds creds; 139 int nextcheck, remaining, renew=0; 140 Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "kinit_qtask: running TGT check\n"); 141 142 memset(&creds, 0, sizeof(creds)); 143 144 renew = kinit_check_tgt(kid, &remaining); 145 146 if (renew > 0) { 147 if (renew==1) { 148 Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, 149 "kinit_qtask: Trying to renew TGT: "); 150 rc = krb5_get_renewed_creds(kid->ctx, &creds, kid->princ, kid->ccache, NULL); 151 if (rc!=0) { 152 Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Failed\n"); 153 log_krb5_errmsg( kid->ctx, 154 "kinit_qtask, Renewal failed: krb5_get_renewed_creds", rc ); 155 renew++; 156 } else { 157 Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Success\n"); 158 krb5_cc_initialize(kid->ctx, kid->ccache, creds.client); 159 krb5_cc_store_cred(kid->ctx, kid->ccache, &creds); 160 krb5_free_cred_contents(kid->ctx, &creds); 161 renew=kinit_check_tgt(kid, &remaining); 162 } 163 } 164 if (renew > 1) { 165 Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, 166 "kinit_qtask: Trying to get new TGT: "); 167 rc = krb5_get_init_creds_keytab( kid->ctx, &creds, kid->princ, 168 kid->keytab, 0, NULL, kid->opts); 169 if (rc) { 170 Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Failed\n"); 171 log_krb5_errmsg(kid->ctx, "krb5_get_init_creds_keytab", rc); 172 } else { 173 Log0(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Success\n"); 174 renew=kinit_check_tgt(kid, &remaining); 175 } 176 krb5_free_cred_contents(kid->ctx, &creds); 177 } 178 } 179 if (renew == 0) { 180 nextcheck = remaining-30; 181 } else { 182 nextcheck = 60; 183 } 184 185 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 186 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) { 187 ldap_pvt_runqueue_stoptask( &slapd_rq, rtask ); 188 } 189 Log3(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, 190 "kinit_qtask: Next TGT check in %dh:%02dm:%02ds\n", 191 nextcheck/3600, (nextcheck%3600)/60, nextcheck%60); 192 rtask->interval.tv_sec = nextcheck; 193 ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 ); 194 slap_wake_listener(); 195 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 196 return NULL; 197} 198 199int 200kinit_initialize(void) 201{ 202 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "kinit_initialize\n" ); 203 krb5_error_code rc; 204 struct re_s *task = NULL; 205 206 kid = ch_calloc(1, sizeof(kinit_data) ); 207 208 rc = krb5_init_context( &kid->ctx ); 209 if ( !rc ) 210 rc = krb5_cc_default(kid->ctx, &kid->ccache ); 211 212 if ( !rc ) { 213 if (!principal) { 214 int len=STRLENOF("ldap/")+global_host_bv.bv_len+1; 215 principal=ch_calloc(len, 1); 216 snprintf(principal, len, "ldap/%s", global_host_bv.bv_val); 217 Log1(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Principal <%s>\n", principal); 218 219 } 220 rc = krb5_parse_name(kid->ctx, principal, &kid->princ); 221 } 222 223 if ( !rc && kt_name) { 224 rc = krb5_kt_resolve(kid->ctx, kt_name, &kid->keytab); 225 } 226 227 if ( !rc ) 228 rc = krb5_get_init_creds_opt_alloc(kid->ctx, &kid->opts); 229 230 if ( !rc ) 231 rc = krb5_get_init_creds_opt_set_out_ccache( kid->ctx, kid->opts, kid->ccache); 232 233 if ( !rc ) { 234 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 235 task = ldap_pvt_runqueue_insert( &slapd_rq, 10, kinit_qtask, (void*)kid, 236 "kinit_qtask", "ldap/bronsted.g17.lan@G17.LAN" ); 237 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 238 } 239 240 if (rc) { 241 log_krb5_errmsg(kid->ctx, "kinit_initialize", rc); 242 rc = -1; 243 } 244 return rc; 245} 246 247#if SLAPD_MOD_KINIT == SLAPD_MOD_DYNAMIC 248int init_module(int argc, char *argv[]) { 249 if (argc > 0) { 250 principal = ch_strdup(argv[0]); 251 } 252 if (argc > 1) { 253 kt_name = ch_strdup(argv[1]); 254 } 255 if (argc > 2) { 256 return -1; 257 } 258 return kinit_initialize(); 259} 260 261int 262term_module() { 263 if (principal) 264 ch_free(principal); 265 if (kt_name) 266 ch_free(kt_name); 267 if (kid) { 268 struct re_s *task; 269 270 task=ldap_pvt_runqueue_find( &slapd_rq, kinit_qtask, (void*)kid); 271 if (task) { 272 if ( ldap_pvt_runqueue_isrunning(&slapd_rq, task) ) { 273 ldap_pvt_runqueue_stoptask(&slapd_rq, task); 274 } 275 ldap_pvt_runqueue_remove(&slapd_rq, task); 276 } 277 if ( kid->ctx ) { 278 if ( kid->princ ) 279 krb5_free_principal(kid->ctx, kid->princ); 280 if ( kid->ccache ) 281 krb5_cc_close(kid->ctx, kid->ccache); 282 if ( kid->keytab ) 283 krb5_kt_close(kid->ctx, kid->keytab); 284 if ( kid->opts ) 285 krb5_get_init_creds_opt_free(kid->ctx, kid->opts); 286 krb5_free_context(kid->ctx); 287 } 288 ch_free(kid); 289 } 290 return 0; 291} 292#endif 293 294#endif /* SLAPD_MOD_KINIT */ 295 296