events.c revision 178825
1251875Speter/* 2251875Speter * Copyright (c) 2005, PADL Software Pty Ltd. 3251875Speter * All rights reserved. 4251875Speter * 5251875Speter * Redistribution and use in source and binary forms, with or without 6251875Speter * modification, are permitted provided that the following conditions 7251875Speter * are met: 8251875Speter * 9251875Speter * 1. Redistributions of source code must retain the above copyright 10251875Speter * notice, this list of conditions and the following disclaimer. 11251875Speter * 12251875Speter * 2. Redistributions in binary form must reproduce the above copyright 13251875Speter * notice, this list of conditions and the following disclaimer in the 14251875Speter * documentation and/or other materials provided with the distribution. 15251875Speter * 16251875Speter * 3. Neither the name of PADL Software nor the names of its contributors 17251875Speter * may be used to endorse or promote products derived from this software 18251875Speter * without specific prior written permission. 19251875Speter * 20251875Speter * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND 21251875Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22251875Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23251875Speter * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE 24251875Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25251875Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26251875Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27251875Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28251875Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29251875Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30251875Speter * SUCH DAMAGE. 31251875Speter */ 32251875Speter 33251875Speter#include "kcm_locl.h" 34251875Speter 35251875SpeterRCSID("$Id: events.c 15294 2005-05-30 01:43:23Z lukeh $"); 36251875Speter 37251875Speter/* thread-safe in case we multi-thread later */ 38251875Speterstatic HEIMDAL_MUTEX events_mutex = HEIMDAL_MUTEX_INITIALIZER; 39251875Speterstatic kcm_event *events_head = NULL; 40251875Speterstatic time_t last_run = 0; 41251875Speter 42251875Speterstatic char *action_strings[] = { 43251875Speter "NONE", "ACQUIRE_CREDS", "RENEW_CREDS", 44251875Speter "DESTROY_CREDS", "DESTROY_EMPTY_CACHE" }; 45251875Speter 46251875Speterkrb5_error_code 47251875Speterkcm_enqueue_event(krb5_context context, 48251875Speter kcm_event *event) 49251875Speter{ 50251875Speter krb5_error_code ret; 51251875Speter 52251875Speter if (event->action == KCM_EVENT_NONE) { 53251875Speter return 0; 54251875Speter } 55251875Speter 56251875Speter HEIMDAL_MUTEX_lock(&events_mutex); 57251875Speter ret = kcm_enqueue_event_internal(context, event); 58251875Speter HEIMDAL_MUTEX_unlock(&events_mutex); 59251875Speter 60251875Speter return ret; 61251875Speter} 62251875Speter 63251875Speterstatic void 64251875Speterprint_times(time_t time, char buf[64]) 65251875Speter{ 66251875Speter if (time) 67251875Speter strftime(buf, 64, "%m-%dT%H:%M", gmtime(&time)); 68251875Speter else 69251875Speter strlcpy(buf, "never", 64); 70251875Speter} 71251875Speter 72251875Speterstatic void 73251875Speterlog_event(kcm_event *event, char *msg) 74251875Speter{ 75251875Speter char fire_time[64], expire_time[64]; 76251875Speter 77251875Speter print_times(event->fire_time, fire_time); 78251875Speter print_times(event->expire_time, expire_time); 79251875Speter 80251875Speter kcm_log(7, "%s event %08x: fire_time %s fire_count %d expire_time %s " 81251875Speter "backoff_time %d action %s cache %s", 82251875Speter msg, event, fire_time, event->fire_count, expire_time, 83251875Speter event->backoff_time, action_strings[event->action], 84251875Speter event->ccache->name); 85251875Speter} 86251875Speter 87251875Speterkrb5_error_code 88251875Speterkcm_enqueue_event_internal(krb5_context context, 89251875Speter kcm_event *event) 90251875Speter{ 91251875Speter kcm_event **e; 92251875Speter 93251875Speter if (event->action == KCM_EVENT_NONE) 94251875Speter return 0; 95251875Speter 96251875Speter for (e = &events_head; *e != NULL; e = &(*e)->next) 97251875Speter ; 98251875Speter 99251875Speter *e = (kcm_event *)malloc(sizeof(kcm_event)); 100251875Speter if (*e == NULL) { 101251875Speter return KRB5_CC_NOMEM; 102251875Speter } 103251875Speter 104251875Speter (*e)->valid = 1; 105251875Speter (*e)->fire_time = event->fire_time; 106251875Speter (*e)->fire_count = 0; 107251875Speter (*e)->expire_time = event->expire_time; 108251875Speter (*e)->backoff_time = event->backoff_time; 109251875Speter 110251875Speter (*e)->action = event->action; 111251875Speter 112251875Speter kcm_retain_ccache(context, event->ccache); 113251875Speter (*e)->ccache = event->ccache; 114251875Speter (*e)->next = NULL; 115251875Speter 116251875Speter log_event(*e, "enqueuing"); 117251875Speter 118251875Speter return 0; 119251875Speter} 120251875Speter 121251875Speter/* 122251875Speter * Dump events list on SIGUSR2 123251875Speter */ 124251875Speterkrb5_error_code 125251875Speterkcm_debug_events(krb5_context context) 126251875Speter{ 127251875Speter kcm_event *e; 128251875Speter 129251875Speter for (e = events_head; e != NULL; e = e->next) 130251875Speter log_event(e, "debug"); 131251875Speter 132251875Speter return 0; 133251875Speter} 134251875Speter 135251875Speterkrb5_error_code 136251875Speterkcm_enqueue_event_relative(krb5_context context, 137251875Speter kcm_event *event) 138251875Speter{ 139251875Speter krb5_error_code ret; 140251875Speter kcm_event e; 141251875Speter 142251875Speter e = *event; 143251875Speter e.backoff_time = e.fire_time; 144251875Speter e.fire_time += time(NULL); 145251875Speter 146251875Speter ret = kcm_enqueue_event(context, &e); 147251875Speter 148251875Speter return ret; 149251875Speter} 150251875Speter 151251875Speterstatic krb5_error_code 152251875Speterkcm_remove_event_internal(krb5_context context, 153251875Speter kcm_event **e) 154251875Speter{ 155251875Speter kcm_event *next; 156251875Speter 157251875Speter next = (*e)->next; 158251875Speter 159251875Speter (*e)->valid = 0; 160251875Speter (*e)->fire_time = 0; 161251875Speter (*e)->fire_count = 0; 162251875Speter (*e)->expire_time = 0; 163251875Speter (*e)->backoff_time = 0; 164251875Speter kcm_release_ccache(context, &(*e)->ccache); 165251875Speter (*e)->next = NULL; 166251875Speter free(*e); 167251875Speter 168251875Speter *e = next; 169251875Speter 170251875Speter return 0; 171251875Speter} 172251875Speter 173251875Speterstatic int 174251875Speteris_primary_credential_p(krb5_context context, 175251875Speter kcm_ccache ccache, 176251875Speter krb5_creds *newcred) 177251875Speter{ 178251875Speter krb5_flags whichfields; 179251875Speter 180251875Speter if (ccache->client == NULL) 181251875Speter return 0; 182251875Speter 183251875Speter if (newcred->client == NULL || 184251875Speter !krb5_principal_compare(context, ccache->client, newcred->client)) 185251875Speter return 0; 186251875Speter 187251875Speter /* XXX just checks whether it's the first credential in the cache */ 188251875Speter if (ccache->creds == NULL) 189251875Speter return 0; 190251875Speter 191251875Speter whichfields = KRB5_TC_MATCH_KEYTYPE | KRB5_TC_MATCH_FLAGS_EXACT | 192251875Speter KRB5_TC_MATCH_TIMES_EXACT | KRB5_TC_MATCH_AUTHDATA | 193251875Speter KRB5_TC_MATCH_2ND_TKT | KRB5_TC_MATCH_IS_SKEY; 194251875Speter 195251875Speter return krb5_compare_creds(context, whichfields, newcred, &ccache->creds->cred); 196251875Speter} 197251875Speter 198251875Speter/* 199251875Speter * Setup default events for a new credential 200251875Speter */ 201251875Speterstatic krb5_error_code 202251875Speterkcm_ccache_make_default_event(krb5_context context, 203251875Speter kcm_event *event, 204251875Speter krb5_creds *newcred) 205251875Speter{ 206251875Speter krb5_error_code ret = 0; 207251875Speter kcm_ccache ccache = event->ccache; 208251875Speter 209251875Speter event->fire_time = 0; 210251875Speter event->expire_time = 0; 211251875Speter event->backoff_time = KCM_EVENT_DEFAULT_BACKOFF_TIME; 212251875Speter 213251875Speter if (newcred == NULL) { 214251875Speter /* no creds, must be acquire creds request */ 215251875Speter if ((ccache->flags & KCM_MASK_KEY_PRESENT) == 0) { 216251875Speter kcm_log(0, "Cannot acquire credentials without a key"); 217251875Speter return KRB5_FCC_INTERNAL; 218251875Speter } 219251875Speter 220251875Speter event->fire_time = time(NULL); /* right away */ 221251875Speter event->action = KCM_EVENT_ACQUIRE_CREDS; 222251875Speter } else if (is_primary_credential_p(context, ccache, newcred)) { 223251875Speter if (newcred->flags.b.renewable) { 224251875Speter event->action = KCM_EVENT_RENEW_CREDS; 225251875Speter ccache->flags |= KCM_FLAGS_RENEWABLE; 226251875Speter } else { 227251875Speter if (ccache->flags & KCM_MASK_KEY_PRESENT) 228251875Speter event->action = KCM_EVENT_ACQUIRE_CREDS; 229251875Speter else 230251875Speter event->action = KCM_EVENT_NONE; 231251875Speter ccache->flags &= ~(KCM_FLAGS_RENEWABLE); 232251875Speter } 233251875Speter /* requeue with some slop factor */ 234251875Speter event->fire_time = newcred->times.endtime - KCM_EVENT_QUEUE_INTERVAL; 235251875Speter } else { 236251875Speter event->action = KCM_EVENT_NONE; 237251875Speter } 238251875Speter 239251875Speter return ret; 240251875Speter} 241251875Speter 242251875Speterkrb5_error_code 243251875Speterkcm_ccache_enqueue_default(krb5_context context, 244251875Speter kcm_ccache ccache, 245251875Speter krb5_creds *newcred) 246251875Speter{ 247251875Speter kcm_event event; 248251875Speter krb5_error_code ret; 249251875Speter 250251875Speter memset(&event, 0, sizeof(event)); 251251875Speter event.ccache = ccache; 252251875Speter 253251875Speter ret = kcm_ccache_make_default_event(context, &event, newcred); 254251875Speter if (ret) 255251875Speter return ret; 256251875Speter 257251875Speter ret = kcm_enqueue_event_internal(context, &event); 258251875Speter if (ret) 259251875Speter return ret; 260251875Speter 261251875Speter return 0; 262251875Speter} 263251875Speter 264251875Speterkrb5_error_code 265251875Speterkcm_remove_event(krb5_context context, 266251875Speter kcm_event *event) 267251875Speter{ 268251875Speter krb5_error_code ret; 269251875Speter kcm_event **e; 270251875Speter int found = 0; 271251875Speter 272251875Speter log_event(event, "removing"); 273251875Speter 274251875Speter HEIMDAL_MUTEX_lock(&events_mutex); 275251875Speter for (e = &events_head; *e != NULL; e = &(*e)->next) { 276251875Speter if (event == *e) { 277251875Speter *e = event->next; 278251875Speter found++; 279251875Speter break; 280251875Speter } 281251875Speter } 282251875Speter 283251875Speter if (!found) { 284251875Speter ret = KRB5_CC_NOTFOUND; 285251875Speter goto out; 286251875Speter } 287251875Speter 288251875Speter ret = kcm_remove_event_internal(context, &event); 289251875Speter 290251875Speterout: 291251875Speter HEIMDAL_MUTEX_unlock(&events_mutex); 292251875Speter 293251875Speter return ret; 294251875Speter} 295251875Speter 296251875Speterkrb5_error_code 297251875Speterkcm_cleanup_events(krb5_context context, 298251875Speter kcm_ccache ccache) 299251875Speter{ 300251875Speter kcm_event **e; 301251875Speter 302251875Speter KCM_ASSERT_VALID(ccache); 303251875Speter 304251875Speter HEIMDAL_MUTEX_lock(&events_mutex); 305251875Speter 306251875Speter for (e = &events_head; *e != NULL; e = &(*e)->next) { 307251875Speter if ((*e)->valid && (*e)->ccache == ccache) { 308251875Speter kcm_remove_event_internal(context, e); 309251875Speter } 310251875Speter if (*e == NULL) 311251875Speter break; 312251875Speter } 313251875Speter 314251875Speter HEIMDAL_MUTEX_unlock(&events_mutex); 315251875Speter 316251875Speter return 0; 317251875Speter} 318251875Speter 319251875Speterstatic krb5_error_code 320251875Speterkcm_fire_event(krb5_context context, 321251875Speter kcm_event **e) 322251875Speter{ 323251875Speter kcm_event *event; 324251875Speter krb5_error_code ret; 325251875Speter krb5_creds *credp = NULL; 326251875Speter int oneshot = 1; 327251875Speter 328251875Speter event = *e; 329251875Speter 330251875Speter switch (event->action) { 331251875Speter case KCM_EVENT_ACQUIRE_CREDS: 332251875Speter ret = kcm_ccache_acquire(context, event->ccache, &credp); 333251875Speter oneshot = 0; 334251875Speter break; 335251875Speter case KCM_EVENT_RENEW_CREDS: 336251875Speter ret = kcm_ccache_refresh(context, event->ccache, &credp); 337251875Speter if (ret == KRB5KRB_AP_ERR_TKT_EXPIRED) { 338251875Speter ret = kcm_ccache_acquire(context, event->ccache, &credp); 339251875Speter } 340251875Speter oneshot = 0; 341251875Speter break; 342251875Speter case KCM_EVENT_DESTROY_CREDS: 343251875Speter ret = kcm_ccache_destroy(context, event->ccache->name); 344251875Speter break; 345251875Speter case KCM_EVENT_DESTROY_EMPTY_CACHE: 346251875Speter ret = kcm_ccache_destroy_if_empty(context, event->ccache); 347251875Speter break; 348251875Speter default: 349251875Speter ret = KRB5_FCC_INTERNAL; 350251875Speter break; 351251875Speter } 352251875Speter 353251875Speter event->fire_count++; 354251875Speter 355251875Speter if (ret) { 356251875Speter /* Reschedule failed event for another time */ 357251875Speter event->fire_time += event->backoff_time; 358251875Speter if (event->backoff_time < KCM_EVENT_MAX_BACKOFF_TIME) 359251875Speter event->backoff_time *= 2; 360251875Speter 361251875Speter /* Remove it if it would never get executed */ 362251875Speter if (event->expire_time && 363251875Speter event->fire_time > event->expire_time) 364251875Speter kcm_remove_event_internal(context, e); 365251875Speter } else { 366251875Speter if (!oneshot) { 367251875Speter char *cpn; 368251875Speter 369251875Speter if (krb5_unparse_name(context, event->ccache->client, 370251875Speter &cpn)) 371251875Speter cpn = NULL; 372251875Speter 373251875Speter kcm_log(0, "%s credentials in cache %s for principal %s", 374251875Speter (event->action == KCM_EVENT_ACQUIRE_CREDS) ? 375251875Speter "Acquired" : "Renewed", 376251875Speter event->ccache->name, 377251875Speter (cpn != NULL) ? cpn : "<none>"); 378251875Speter 379251875Speter if (cpn != NULL) 380251875Speter free(cpn); 381251875Speter 382251875Speter /* Succeeded, but possibly replaced with another event */ 383251875Speter ret = kcm_ccache_make_default_event(context, event, credp); 384251875Speter if (ret || event->action == KCM_EVENT_NONE) 385251875Speter oneshot = 1; 386251875Speter else 387251875Speter log_event(event, "requeuing"); 388251875Speter } 389251875Speter if (oneshot) 390251875Speter kcm_remove_event_internal(context, e); 391251875Speter } 392251875Speter 393251875Speter return ret; 394251875Speter} 395251875Speter 396251875Speterkrb5_error_code 397251875Speterkcm_run_events(krb5_context context, 398251875Speter time_t now) 399251875Speter{ 400251875Speter krb5_error_code ret; 401251875Speter kcm_event **e; 402251875Speter 403251875Speter HEIMDAL_MUTEX_lock(&events_mutex); 404251875Speter 405251875Speter /* Only run event queue every N seconds */ 406251875Speter if (now < last_run + KCM_EVENT_QUEUE_INTERVAL) { 407251875Speter HEIMDAL_MUTEX_unlock(&events_mutex); 408251875Speter return 0; 409251875Speter } 410251875Speter 411251875Speter /* go through events list, fire and expire */ 412251875Speter for (e = &events_head; *e != NULL; e = &(*e)->next) { 413251875Speter if ((*e)->valid == 0) 414251875Speter continue; 415251875Speter 416251875Speter if (now >= (*e)->fire_time) { 417251875Speter ret = kcm_fire_event(context, e); 418251875Speter if (ret) { 419251875Speter kcm_log(1, "Could not fire event for cache %s: %s", 420251875Speter (*e)->ccache->name, krb5_get_err_text(context, ret)); 421251875Speter } 422251875Speter } else if ((*e)->expire_time && now >= (*e)->expire_time) { 423251875Speter ret = kcm_remove_event_internal(context, e); 424251875Speter if (ret) { 425251875Speter kcm_log(1, "Could not expire event for cache %s: %s", 426251875Speter (*e)->ccache->name, krb5_get_err_text(context, ret)); 427251875Speter } 428251875Speter } 429251875Speter 430251875Speter if (*e == NULL) 431251875Speter break; 432251875Speter } 433251875Speter 434251875Speter last_run = now; 435251875Speter 436251875Speter HEIMDAL_MUTEX_unlock(&events_mutex); 437251875Speter 438251875Speter return 0; 439251875Speter} 440251875Speter 441251875Speter