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