2 * Copyright (c) 2005, PADL Software Pty Ltd.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of PADL Software nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 __RCSID("$Heimdal: events.c 15294 2005-05-30 01:43:23Z lukeh $"
38 /* thread-safe in case we multi-thread later */
39 static HEIMDAL_MUTEX events_mutex
= HEIMDAL_MUTEX_INITIALIZER
;
40 static kcm_event
*events_head
= NULL
;
41 static time_t last_run
= 0;
43 static char *action_strings
[] = {
44 "NONE", "ACQUIRE_CREDS", "RENEW_CREDS",
45 "DESTROY_CREDS", "DESTROY_EMPTY_CACHE" };
48 kcm_enqueue_event(krb5_context context
,
53 if (event
->action
== KCM_EVENT_NONE
) {
57 HEIMDAL_MUTEX_lock(&events_mutex
);
58 ret
= kcm_enqueue_event_internal(context
, event
);
59 HEIMDAL_MUTEX_unlock(&events_mutex
);
65 print_times(time_t time
, char buf
[64])
68 strftime(buf
, 64, "%m-%dT%H:%M", gmtime(&time
));
70 strlcpy(buf
, "never", 64);
74 log_event(kcm_event
*event
, char *msg
)
76 char fire_time
[64], expire_time
[64];
78 print_times(event
->fire_time
, fire_time
);
79 print_times(event
->expire_time
, expire_time
);
81 kcm_log(7, "%s event %08x: fire_time %s fire_count %d expire_time %s "
82 "backoff_time %d action %s cache %s",
83 msg
, event
, fire_time
, event
->fire_count
, expire_time
,
84 event
->backoff_time
, action_strings
[event
->action
],
89 kcm_enqueue_event_internal(krb5_context context
,
94 if (event
->action
== KCM_EVENT_NONE
)
97 for (e
= &events_head
; *e
!= NULL
; e
= &(*e
)->next
)
100 *e
= (kcm_event
*)malloc(sizeof(kcm_event
));
102 return KRB5_CC_NOMEM
;
106 (*e
)->fire_time
= event
->fire_time
;
107 (*e
)->fire_count
= 0;
108 (*e
)->expire_time
= event
->expire_time
;
109 (*e
)->backoff_time
= event
->backoff_time
;
111 (*e
)->action
= event
->action
;
113 kcm_retain_ccache(context
, event
->ccache
);
114 (*e
)->ccache
= event
->ccache
;
117 log_event(*e
, "enqueuing");
123 * Dump events list on SIGUSR2
126 kcm_debug_events(krb5_context context
)
130 for (e
= events_head
; e
!= NULL
; e
= e
->next
)
131 log_event(e
, "debug");
137 kcm_enqueue_event_relative(krb5_context context
,
144 e
.backoff_time
= e
.fire_time
;
145 e
.fire_time
+= time(NULL
);
147 ret
= kcm_enqueue_event(context
, &e
);
152 static krb5_error_code
153 kcm_remove_event_internal(krb5_context context
,
162 (*e
)->fire_count
= 0;
163 (*e
)->expire_time
= 0;
164 (*e
)->backoff_time
= 0;
165 kcm_release_ccache(context
, &(*e
)->ccache
);
175 is_primary_credential_p(krb5_context context
,
179 krb5_flags whichfields
;
181 if (ccache
->client
== NULL
)
184 if (newcred
->client
== NULL
||
185 !krb5_principal_compare(context
, ccache
->client
, newcred
->client
))
188 /* XXX just checks whether it's the first credential in the cache */
189 if (ccache
->creds
== NULL
)
192 whichfields
= KRB5_TC_MATCH_KEYTYPE
| KRB5_TC_MATCH_FLAGS_EXACT
|
193 KRB5_TC_MATCH_TIMES_EXACT
| KRB5_TC_MATCH_AUTHDATA
|
194 KRB5_TC_MATCH_2ND_TKT
| KRB5_TC_MATCH_IS_SKEY
;
196 return krb5_compare_creds(context
, whichfields
, newcred
, &ccache
->creds
->cred
);
200 * Setup default events for a new credential
202 static krb5_error_code
203 kcm_ccache_make_default_event(krb5_context context
,
207 krb5_error_code ret
= 0;
208 kcm_ccache ccache
= event
->ccache
;
210 event
->fire_time
= 0;
211 event
->expire_time
= 0;
212 event
->backoff_time
= KCM_EVENT_DEFAULT_BACKOFF_TIME
;
214 if (newcred
== NULL
) {
215 /* no creds, must be acquire creds request */
216 if ((ccache
->flags
& KCM_MASK_KEY_PRESENT
) == 0) {
217 kcm_log(0, "Cannot acquire credentials without a key");
218 return KRB5_FCC_INTERNAL
;
221 event
->fire_time
= time(NULL
); /* right away */
222 event
->action
= KCM_EVENT_ACQUIRE_CREDS
;
223 } else if (is_primary_credential_p(context
, ccache
, newcred
)) {
224 if (newcred
->flags
.b
.renewable
) {
225 event
->action
= KCM_EVENT_RENEW_CREDS
;
226 ccache
->flags
|= KCM_FLAGS_RENEWABLE
;
228 if (ccache
->flags
& KCM_MASK_KEY_PRESENT
)
229 event
->action
= KCM_EVENT_ACQUIRE_CREDS
;
231 event
->action
= KCM_EVENT_NONE
;
232 ccache
->flags
&= ~(KCM_FLAGS_RENEWABLE
);
234 /* requeue with some slop factor */
235 event
->fire_time
= newcred
->times
.endtime
- KCM_EVENT_QUEUE_INTERVAL
;
237 event
->action
= KCM_EVENT_NONE
;
244 kcm_ccache_enqueue_default(krb5_context context
,
251 memset(&event
, 0, sizeof(event
));
252 event
.ccache
= ccache
;
254 ret
= kcm_ccache_make_default_event(context
, &event
, newcred
);
258 ret
= kcm_enqueue_event_internal(context
, &event
);
266 kcm_remove_event(krb5_context context
,
273 log_event(event
, "removing");
275 HEIMDAL_MUTEX_lock(&events_mutex
);
276 for (e
= &events_head
; *e
!= NULL
; e
= &(*e
)->next
) {
285 ret
= KRB5_CC_NOTFOUND
;
289 ret
= kcm_remove_event_internal(context
, &event
);
292 HEIMDAL_MUTEX_unlock(&events_mutex
);
298 kcm_cleanup_events(krb5_context context
,
303 KCM_ASSERT_VALID(ccache
);
305 HEIMDAL_MUTEX_lock(&events_mutex
);
307 for (e
= &events_head
; *e
!= NULL
; e
= &(*e
)->next
) {
308 if ((*e
)->valid
&& (*e
)->ccache
== ccache
) {
309 kcm_remove_event_internal(context
, e
);
315 HEIMDAL_MUTEX_unlock(&events_mutex
);
320 static krb5_error_code
321 kcm_fire_event(krb5_context context
,
326 krb5_creds
*credp
= NULL
;
331 switch (event
->action
) {
332 case KCM_EVENT_ACQUIRE_CREDS
:
333 ret
= kcm_ccache_acquire(context
, event
->ccache
, &credp
);
336 case KCM_EVENT_RENEW_CREDS
:
337 ret
= kcm_ccache_refresh(context
, event
->ccache
, &credp
);
338 if (ret
== KRB5KRB_AP_ERR_TKT_EXPIRED
) {
339 ret
= kcm_ccache_acquire(context
, event
->ccache
, &credp
);
343 case KCM_EVENT_DESTROY_CREDS
:
344 ret
= kcm_ccache_destroy(context
, event
->ccache
->name
);
346 case KCM_EVENT_DESTROY_EMPTY_CACHE
:
347 ret
= kcm_ccache_destroy_if_empty(context
, event
->ccache
);
350 ret
= KRB5_FCC_INTERNAL
;
357 /* Reschedule failed event for another time */
358 event
->fire_time
+= event
->backoff_time
;
359 if (event
->backoff_time
< KCM_EVENT_MAX_BACKOFF_TIME
)
360 event
->backoff_time
*= 2;
362 /* Remove it if it would never get executed */
363 if (event
->expire_time
&&
364 event
->fire_time
> event
->expire_time
)
365 kcm_remove_event_internal(context
, e
);
370 if (krb5_unparse_name(context
, event
->ccache
->client
,
374 kcm_log(0, "%s credentials in cache %s for principal %s",
375 (event
->action
== KCM_EVENT_ACQUIRE_CREDS
) ?
376 "Acquired" : "Renewed",
378 (cpn
!= NULL
) ? cpn
: "<none>");
383 /* Succeeded, but possibly replaced with another event */
384 ret
= kcm_ccache_make_default_event(context
, event
, credp
);
385 if (ret
|| event
->action
== KCM_EVENT_NONE
)
388 log_event(event
, "requeuing");
391 kcm_remove_event_internal(context
, e
);
398 kcm_run_events(krb5_context context
,
404 HEIMDAL_MUTEX_lock(&events_mutex
);
406 /* Only run event queue every N seconds */
407 if (now
< last_run
+ KCM_EVENT_QUEUE_INTERVAL
) {
408 HEIMDAL_MUTEX_unlock(&events_mutex
);
412 /* go through events list, fire and expire */
413 for (e
= &events_head
; *e
!= NULL
; e
= &(*e
)->next
) {
414 if ((*e
)->valid
== 0)
417 if (now
>= (*e
)->fire_time
) {
418 ret
= kcm_fire_event(context
, e
);
420 kcm_log(1, "Could not fire event for cache %s: %s",
421 (*e
)->ccache
->name
, krb5_get_err_text(context
, ret
));
423 } else if ((*e
)->expire_time
&& now
>= (*e
)->expire_time
) {
424 ret
= kcm_remove_event_internal(context
, e
);
426 kcm_log(1, "Could not expire event for cache %s: %s",
427 (*e
)->ccache
->name
, krb5_get_err_text(context
, ret
));
437 HEIMDAL_MUTEX_unlock(&events_mutex
);