events.c revision 233294
169408Sache/* 259243Sobrien * Copyright (c) 2005, PADL Software Pty Ltd. 359243Sobrien * All rights reserved. 459243Sobrien * 559243Sobrien * Redistribution and use in source and binary forms, with or without 659243Sobrien * modification, are permitted provided that the following conditions 759243Sobrien * are met: 859243Sobrien * 959243Sobrien * 1. Redistributions of source code must retain the above copyright 1059243Sobrien * notice, this list of conditions and the following disclaimer. 1159243Sobrien * 1259243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1359243Sobrien * notice, this list of conditions and the following disclaimer in the 1459243Sobrien * documentation and/or other materials provided with the distribution. 1559243Sobrien * 1659243Sobrien * 3. Neither the name of PADL Software nor the names of its contributors 1759243Sobrien * may be used to endorse or promote products derived from this software 1859243Sobrien * without specific prior written permission. 1959243Sobrien * 2059243Sobrien * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND 2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2359243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE 2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3059243Sobrien * SUCH DAMAGE. 3159243Sobrien */ 3259243Sobrien 3359243Sobrien#include "kcm_locl.h" 3459243Sobrien 3559243SobrienRCSID("$Id$"); 3659243Sobrien 3759243Sobrien/* thread-safe in case we multi-thread later */ 3859243Sobrienstatic HEIMDAL_MUTEX events_mutex = HEIMDAL_MUTEX_INITIALIZER; 3969408Sachestatic kcm_event *events_head = NULL; 4059243Sobrienstatic time_t last_run = 0; 4159243Sobrien 4259243Sobrienstatic char *action_strings[] = { 4359243Sobrien "NONE", "ACQUIRE_CREDS", "RENEW_CREDS", 4459243Sobrien "DESTROY_CREDS", "DESTROY_EMPTY_CACHE" }; 4559243Sobrien 4659243Sobrienkrb5_error_code 4759243Sobrienkcm_enqueue_event(krb5_context context, 4859243Sobrien kcm_event *event) 4959243Sobrien{ 5059243Sobrien krb5_error_code ret; 5159243Sobrien 5259243Sobrien if (event->action == KCM_EVENT_NONE) { 5359243Sobrien return 0; 5459243Sobrien } 5559243Sobrien 5659243Sobrien HEIMDAL_MUTEX_lock(&events_mutex); 5759243Sobrien ret = kcm_enqueue_event_internal(context, event); 5859243Sobrien HEIMDAL_MUTEX_unlock(&events_mutex); 5959243Sobrien 6059243Sobrien return ret; 6159243Sobrien} 6259243Sobrien 6359243Sobrienstatic void 6459243Sobrienprint_times(time_t time, char buf[64]) 6559243Sobrien{ 6659243Sobrien if (time) 6759243Sobrien strftime(buf, 64, "%m-%dT%H:%M", gmtime(&time)); 6859243Sobrien else 6959243Sobrien strlcpy(buf, "never", 64); 7059243Sobrien} 7159243Sobrien 7259243Sobrienstatic void 7359243Sobrienlog_event(kcm_event *event, char *msg) 7459243Sobrien{ 7559243Sobrien char fire_time[64], expire_time[64]; 7659243Sobrien 7759243Sobrien print_times(event->fire_time, fire_time); 7859243Sobrien print_times(event->expire_time, expire_time); 7959243Sobrien 8059243Sobrien kcm_log(7, "%s event %08x: fire_time %s fire_count %d expire_time %s " 8159243Sobrien "backoff_time %d action %s cache %s", 8259243Sobrien msg, event, fire_time, event->fire_count, expire_time, 8359243Sobrien event->backoff_time, action_strings[event->action], 8459243Sobrien event->ccache->name); 8559243Sobrien} 8659243Sobrien 8759243Sobrienkrb5_error_code 8859243Sobrienkcm_enqueue_event_internal(krb5_context context, 8959243Sobrien kcm_event *event) 9059243Sobrien{ 9159243Sobrien kcm_event **e; 9259243Sobrien 9359243Sobrien if (event->action == KCM_EVENT_NONE) 9459243Sobrien return 0; 9559243Sobrien 9659243Sobrien for (e = &events_head; *e != NULL; e = &(*e)->next) 9759243Sobrien ; 9859243Sobrien 9959243Sobrien *e = (kcm_event *)malloc(sizeof(kcm_event)); 10059243Sobrien if (*e == NULL) { 10159243Sobrien return KRB5_CC_NOMEM; 10259243Sobrien } 10359243Sobrien 10459243Sobrien (*e)->valid = 1; 10559243Sobrien (*e)->fire_time = event->fire_time; 10659243Sobrien (*e)->fire_count = 0; 10759243Sobrien (*e)->expire_time = event->expire_time; 10859243Sobrien (*e)->backoff_time = event->backoff_time; 10959243Sobrien 11059243Sobrien (*e)->action = event->action; 11159243Sobrien 11259243Sobrien kcm_retain_ccache(context, event->ccache); 11359243Sobrien (*e)->ccache = event->ccache; 11459243Sobrien (*e)->next = NULL; 11559243Sobrien 11659243Sobrien log_event(*e, "enqueuing"); 11759243Sobrien 11859243Sobrien return 0; 11959243Sobrien} 12059243Sobrien 12159243Sobrien/* 12259243Sobrien * Dump events list on SIGUSR2 12359243Sobrien */ 12459243Sobrienkrb5_error_code 12559243Sobrienkcm_debug_events(krb5_context context) 12659243Sobrien{ 12759243Sobrien kcm_event *e; 12859243Sobrien 12959243Sobrien for (e = events_head; e != NULL; e = e->next) 13059243Sobrien log_event(e, "debug"); 13159243Sobrien 13259243Sobrien return 0; 13359243Sobrien} 13459243Sobrien 13559243Sobrienkrb5_error_code 13659243Sobrienkcm_enqueue_event_relative(krb5_context context, 13759243Sobrien kcm_event *event) 13859243Sobrien{ 13959243Sobrien krb5_error_code ret; 14059243Sobrien kcm_event e; 14159243Sobrien 14259243Sobrien e = *event; 14359243Sobrien e.backoff_time = e.fire_time; 14459243Sobrien e.fire_time += time(NULL); 14559243Sobrien 14659243Sobrien ret = kcm_enqueue_event(context, &e); 14759243Sobrien 14859243Sobrien return ret; 14959243Sobrien} 15059243Sobrien 15159243Sobrienstatic krb5_error_code 15259243Sobrienkcm_remove_event_internal(krb5_context context, 15359243Sobrien kcm_event **e) 15459243Sobrien{ 15559243Sobrien kcm_event *next; 15659243Sobrien 15759243Sobrien next = (*e)->next; 15859243Sobrien 15959243Sobrien (*e)->valid = 0; 16059243Sobrien (*e)->fire_time = 0; 16159243Sobrien (*e)->fire_count = 0; 16259243Sobrien (*e)->expire_time = 0; 16359243Sobrien (*e)->backoff_time = 0; 16459243Sobrien kcm_release_ccache(context, (*e)->ccache); 16559243Sobrien (*e)->next = NULL; 16659243Sobrien free(*e); 16759243Sobrien 16859243Sobrien *e = next; 16959243Sobrien 17059243Sobrien return 0; 17159243Sobrien} 17259243Sobrien 17359243Sobrienstatic int 17459243Sobrienis_primary_credential_p(krb5_context context, 17559243Sobrien kcm_ccache ccache, 17659243Sobrien krb5_creds *newcred) 17759243Sobrien{ 17859243Sobrien krb5_flags whichfields; 17959243Sobrien 18059243Sobrien if (ccache->client == NULL) 18159243Sobrien return 0; 18259243Sobrien 18359243Sobrien if (newcred->client == NULL || 18459243Sobrien !krb5_principal_compare(context, ccache->client, newcred->client)) 18559243Sobrien return 0; 18659243Sobrien 18759243Sobrien /* XXX just checks whether it's the first credential in the cache */ 18859243Sobrien if (ccache->creds == NULL) 18959243Sobrien return 0; 19059243Sobrien 19159243Sobrien whichfields = KRB5_TC_MATCH_KEYTYPE | KRB5_TC_MATCH_FLAGS_EXACT | 19259243Sobrien KRB5_TC_MATCH_TIMES_EXACT | KRB5_TC_MATCH_AUTHDATA | 19359243Sobrien KRB5_TC_MATCH_2ND_TKT | KRB5_TC_MATCH_IS_SKEY; 19459243Sobrien 19559243Sobrien return krb5_compare_creds(context, whichfields, newcred, &ccache->creds->cred); 19659243Sobrien} 19759243Sobrien 19859243Sobrien/* 19959243Sobrien * Setup default events for a new credential 20059243Sobrien */ 20159243Sobrienstatic krb5_error_code 20259243Sobrienkcm_ccache_make_default_event(krb5_context context, 20359243Sobrien kcm_event *event, 20459243Sobrien krb5_creds *newcred) 20559243Sobrien{ 20659243Sobrien krb5_error_code ret = 0; 20759243Sobrien kcm_ccache ccache = event->ccache; 20859243Sobrien 20959243Sobrien event->fire_time = 0; 21059243Sobrien event->expire_time = 0; 21159243Sobrien event->backoff_time = KCM_EVENT_DEFAULT_BACKOFF_TIME; 21259243Sobrien 21359243Sobrien if (newcred == NULL) { 21459243Sobrien /* no creds, must be acquire creds request */ 21559243Sobrien if ((ccache->flags & KCM_MASK_KEY_PRESENT) == 0) { 21659243Sobrien kcm_log(0, "Cannot acquire credentials without a key"); 21759243Sobrien return KRB5_FCC_INTERNAL; 21859243Sobrien } 21959243Sobrien 22059243Sobrien event->fire_time = time(NULL); /* right away */ 22159243Sobrien event->action = KCM_EVENT_ACQUIRE_CREDS; 22259243Sobrien } else if (is_primary_credential_p(context, ccache, newcred)) { 22359243Sobrien if (newcred->flags.b.renewable) { 22459243Sobrien event->action = KCM_EVENT_RENEW_CREDS; 22559243Sobrien ccache->flags |= KCM_FLAGS_RENEWABLE; 22659243Sobrien } else { 22759243Sobrien if (ccache->flags & KCM_MASK_KEY_PRESENT) 22859243Sobrien event->action = KCM_EVENT_ACQUIRE_CREDS; 22959243Sobrien else 23059243Sobrien event->action = KCM_EVENT_NONE; 23159243Sobrien ccache->flags &= ~(KCM_FLAGS_RENEWABLE); 23259243Sobrien } 23359243Sobrien /* requeue with some slop factor */ 23459243Sobrien event->fire_time = newcred->times.endtime - KCM_EVENT_QUEUE_INTERVAL; 23559243Sobrien } else { 23659243Sobrien event->action = KCM_EVENT_NONE; 23759243Sobrien } 23859243Sobrien 23959243Sobrien return ret; 24059243Sobrien} 24159243Sobrien 24259243Sobrienkrb5_error_code 24359243Sobrienkcm_ccache_enqueue_default(krb5_context context, 24459243Sobrien kcm_ccache ccache, 24559243Sobrien krb5_creds *newcred) 24659243Sobrien{ 24759243Sobrien kcm_event event; 24859243Sobrien krb5_error_code ret; 24959243Sobrien 25059243Sobrien memset(&event, 0, sizeof(event)); 25159243Sobrien event.ccache = ccache; 25259243Sobrien 25359243Sobrien ret = kcm_ccache_make_default_event(context, &event, newcred); 25459243Sobrien if (ret) 25559243Sobrien return ret; 25659243Sobrien 25759243Sobrien ret = kcm_enqueue_event_internal(context, &event); 25859243Sobrien if (ret) 25959243Sobrien return ret; 26059243Sobrien 26159243Sobrien return 0; 26259243Sobrien} 26359243Sobrien 26459243Sobrienkrb5_error_code 26559243Sobrienkcm_remove_event(krb5_context context, 26659243Sobrien kcm_event *event) 26759243Sobrien{ 26859243Sobrien krb5_error_code ret; 26959243Sobrien kcm_event **e; 27059243Sobrien int found = 0; 27159243Sobrien 27259243Sobrien log_event(event, "removing"); 27359243Sobrien 27459243Sobrien HEIMDAL_MUTEX_lock(&events_mutex); 27559243Sobrien for (e = &events_head; *e != NULL; e = &(*e)->next) { 27659243Sobrien if (event == *e) { 27759243Sobrien *e = event->next; 27859243Sobrien found++; 27959243Sobrien break; 28059243Sobrien } 28159243Sobrien } 28259243Sobrien 28359243Sobrien if (!found) { 28459243Sobrien ret = KRB5_CC_NOTFOUND; 28559243Sobrien goto out; 28659243Sobrien } 28759243Sobrien 28859243Sobrien ret = kcm_remove_event_internal(context, &event); 28959243Sobrien 29059243Sobrienout: 29159243Sobrien HEIMDAL_MUTEX_unlock(&events_mutex); 29259243Sobrien 29359243Sobrien return ret; 29459243Sobrien} 29559243Sobrien 29659243Sobrienkrb5_error_code 29759243Sobrienkcm_cleanup_events(krb5_context context, 29859243Sobrien kcm_ccache ccache) 29959243Sobrien{ 30059243Sobrien kcm_event **e; 30159243Sobrien 30259243Sobrien KCM_ASSERT_VALID(ccache); 30359243Sobrien 30459243Sobrien HEIMDAL_MUTEX_lock(&events_mutex); 30559243Sobrien 30659243Sobrien for (e = &events_head; *e != NULL; e = &(*e)->next) { 30759243Sobrien if ((*e)->valid && (*e)->ccache == ccache) { 30859243Sobrien kcm_remove_event_internal(context, e); 30959243Sobrien } 31059243Sobrien if (*e == NULL) 31159243Sobrien break; 31259243Sobrien } 31359243Sobrien 31459243Sobrien HEIMDAL_MUTEX_unlock(&events_mutex); 31559243Sobrien 31659243Sobrien return 0; 31759243Sobrien} 31859243Sobrien 31959243Sobrienstatic krb5_error_code 32059243Sobrienkcm_fire_event(krb5_context context, 32159243Sobrien kcm_event **e) 32259243Sobrien{ 32359243Sobrien kcm_event *event; 32459243Sobrien krb5_error_code ret; 32559243Sobrien krb5_creds *credp = NULL; 32659243Sobrien int oneshot = 1; 32759243Sobrien 32859243Sobrien event = *e; 32959243Sobrien 33059243Sobrien switch (event->action) { 33159243Sobrien case KCM_EVENT_ACQUIRE_CREDS: 33259243Sobrien ret = kcm_ccache_acquire(context, event->ccache, &credp); 33359243Sobrien oneshot = 0; 33459243Sobrien break; 33559243Sobrien case KCM_EVENT_RENEW_CREDS: 33659243Sobrien ret = kcm_ccache_refresh(context, event->ccache, &credp); 33759243Sobrien if (ret == KRB5KRB_AP_ERR_TKT_EXPIRED) { 33859243Sobrien ret = kcm_ccache_acquire(context, event->ccache, &credp); 33959243Sobrien } 34059243Sobrien oneshot = 0; 34159243Sobrien break; 34259243Sobrien case KCM_EVENT_DESTROY_CREDS: 34359243Sobrien ret = kcm_ccache_destroy(context, event->ccache->name); 34459243Sobrien break; 34559243Sobrien case KCM_EVENT_DESTROY_EMPTY_CACHE: 34659243Sobrien ret = kcm_ccache_destroy_if_empty(context, event->ccache); 34759243Sobrien break; 34859243Sobrien default: 34959243Sobrien ret = KRB5_FCC_INTERNAL; 35059243Sobrien break; 35159243Sobrien } 35259243Sobrien 35359243Sobrien event->fire_count++; 35459243Sobrien 35559243Sobrien if (ret) { 35659243Sobrien /* Reschedule failed event for another time */ 35759243Sobrien event->fire_time += event->backoff_time; 35859243Sobrien if (event->backoff_time < KCM_EVENT_MAX_BACKOFF_TIME) 35959243Sobrien event->backoff_time *= 2; 36059243Sobrien 36159243Sobrien /* Remove it if it would never get executed */ 36259243Sobrien if (event->expire_time && 36359243Sobrien event->fire_time > event->expire_time) 36459243Sobrien kcm_remove_event_internal(context, e); 36559243Sobrien } else { 36659243Sobrien if (!oneshot) { 36759243Sobrien char *cpn; 36859243Sobrien 36959243Sobrien if (krb5_unparse_name(context, event->ccache->client, 37059243Sobrien &cpn)) 37159243Sobrien cpn = NULL; 37259243Sobrien 37359243Sobrien kcm_log(0, "%s credentials in cache %s for principal %s", 37459243Sobrien (event->action == KCM_EVENT_ACQUIRE_CREDS) ? 37559243Sobrien "Acquired" : "Renewed", 37659243Sobrien event->ccache->name, 37759243Sobrien (cpn != NULL) ? cpn : "<none>"); 37859243Sobrien 37959243Sobrien if (cpn != NULL) 38059243Sobrien free(cpn); 38159243Sobrien 38259243Sobrien /* Succeeded, but possibly replaced with another event */ 38359243Sobrien ret = kcm_ccache_make_default_event(context, event, credp); 38459243Sobrien if (ret || event->action == KCM_EVENT_NONE) 38559243Sobrien oneshot = 1; 38659243Sobrien else 38759243Sobrien log_event(event, "requeuing"); 38859243Sobrien } 38959243Sobrien if (oneshot) 39059243Sobrien kcm_remove_event_internal(context, e); 39159243Sobrien } 39259243Sobrien 39359243Sobrien return ret; 39459243Sobrien} 39559243Sobrien 39659243Sobrienkrb5_error_code 39759243Sobrienkcm_run_events(krb5_context context, time_t now) 39859243Sobrien{ 39959243Sobrien krb5_error_code ret; 40059243Sobrien kcm_event **e; 40159243Sobrien 40259243Sobrien HEIMDAL_MUTEX_lock(&events_mutex); 40359243Sobrien 40459243Sobrien /* Only run event queue every N seconds */ 40559243Sobrien if (now < last_run + KCM_EVENT_QUEUE_INTERVAL) { 40659243Sobrien HEIMDAL_MUTEX_unlock(&events_mutex); 40759243Sobrien return 0; 40859243Sobrien } 40959243Sobrien 41059243Sobrien /* go through events list, fire and expire */ 41159243Sobrien for (e = &events_head; *e != NULL; e = &(*e)->next) { 41259243Sobrien if ((*e)->valid == 0) 41359243Sobrien continue; 41459243Sobrien 41559243Sobrien if (now >= (*e)->fire_time) { 41659243Sobrien ret = kcm_fire_event(context, e); 41759243Sobrien if (ret) { 41859243Sobrien kcm_log(1, "Could not fire event for cache %s: %s", 41959243Sobrien (*e)->ccache->name, krb5_get_err_text(context, ret)); 42059243Sobrien } 42159243Sobrien } else if ((*e)->expire_time && now >= (*e)->expire_time) { 42259243Sobrien ret = kcm_remove_event_internal(context, e); 42359243Sobrien if (ret) { 42459243Sobrien kcm_log(1, "Could not expire event for cache %s: %s", 42559243Sobrien (*e)->ccache->name, krb5_get_err_text(context, ret)); 42659243Sobrien } 42759243Sobrien } 42859243Sobrien 42959243Sobrien if (*e == NULL) 43059243Sobrien break; 43159243Sobrien } 43259243Sobrien 43359243Sobrien last_run = now; 43459243Sobrien 43559243Sobrien HEIMDAL_MUTEX_unlock(&events_mutex); 43659243Sobrien 43759243Sobrien return 0; 43859243Sobrien} 43959243Sobrien 44059243Sobrien