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
37 /* thread-safe in case we multi-thread later */
38 static HEIMDAL_MUTEX events_mutex
= HEIMDAL_MUTEX_INITIALIZER
;
39 static kcm_event
*events_head
= NULL
;
40 static time_t last_run
= 0;
42 static char *action_strings
[] = {
43 "NONE", "ACQUIRE_CREDS", "RENEW_CREDS",
44 "DESTROY_CREDS", "DESTROY_EMPTY_CACHE" };
47 kcm_enqueue_event(krb5_context context
,
52 if (event
->action
== KCM_EVENT_NONE
) {
56 HEIMDAL_MUTEX_lock(&events_mutex
);
57 ret
= kcm_enqueue_event_internal(context
, event
);
58 HEIMDAL_MUTEX_unlock(&events_mutex
);
64 print_times(time_t time
, char buf
[64])
67 strftime(buf
, 64, "%m-%dT%H:%M", gmtime(&time
));
69 strlcpy(buf
, "never", 64);
73 log_event(kcm_event
*event
, char *msg
)
75 char fire_time
[64], expire_time
[64];
77 print_times(event
->fire_time
, fire_time
);
78 print_times(event
->expire_time
, expire_time
);
80 kcm_log(7, "%s event %08x: fire_time %s fire_count %d expire_time %s "
81 "backoff_time %d action %s cache %s",
82 msg
, event
, fire_time
, event
->fire_count
, expire_time
,
83 event
->backoff_time
, action_strings
[event
->action
],
88 kcm_enqueue_event_internal(krb5_context context
,
93 if (event
->action
== KCM_EVENT_NONE
)
96 for (e
= &events_head
; *e
!= NULL
; e
= &(*e
)->next
)
99 *e
= (kcm_event
*)malloc(sizeof(kcm_event
));
101 return KRB5_CC_NOMEM
;
105 (*e
)->fire_time
= event
->fire_time
;
106 (*e
)->fire_count
= 0;
107 (*e
)->expire_time
= event
->expire_time
;
108 (*e
)->backoff_time
= event
->backoff_time
;
110 (*e
)->action
= event
->action
;
112 kcm_retain_ccache(context
, event
->ccache
);
113 (*e
)->ccache
= event
->ccache
;
116 log_event(*e
, "enqueuing");
122 * Dump events list on SIGUSR2
125 kcm_debug_events(krb5_context context
)
129 for (e
= events_head
; e
!= NULL
; e
= e
->next
)
130 log_event(e
, "debug");
136 kcm_enqueue_event_relative(krb5_context context
,
143 e
.backoff_time
= e
.fire_time
;
144 e
.fire_time
+= time(NULL
);
146 ret
= kcm_enqueue_event(context
, &e
);
151 static krb5_error_code
152 kcm_remove_event_internal(krb5_context context
,
161 (*e
)->fire_count
= 0;
162 (*e
)->expire_time
= 0;
163 (*e
)->backoff_time
= 0;
164 kcm_release_ccache(context
, &(*e
)->ccache
);
174 is_primary_credential_p(krb5_context context
,
178 krb5_flags whichfields
;
180 if (ccache
->client
== NULL
)
183 if (newcred
->client
== NULL
||
184 !krb5_principal_compare(context
, ccache
->client
, newcred
->client
))
187 /* XXX just checks whether it's the first credential in the cache */
188 if (ccache
->creds
== NULL
)
191 whichfields
= KRB5_TC_MATCH_KEYTYPE
| KRB5_TC_MATCH_FLAGS_EXACT
|
192 KRB5_TC_MATCH_TIMES_EXACT
| KRB5_TC_MATCH_AUTHDATA
|
193 KRB5_TC_MATCH_2ND_TKT
| KRB5_TC_MATCH_IS_SKEY
;
195 return krb5_compare_creds(context
, whichfields
, newcred
, &ccache
->creds
->cred
);
199 * Setup default events for a new credential
201 static krb5_error_code
202 kcm_ccache_make_default_event(krb5_context context
,
206 krb5_error_code ret
= 0;
207 kcm_ccache ccache
= event
->ccache
;
209 event
->fire_time
= 0;
210 event
->expire_time
= 0;
211 event
->backoff_time
= KCM_EVENT_DEFAULT_BACKOFF_TIME
;
213 if (newcred
== NULL
) {
214 /* no creds, must be acquire creds request */
215 if ((ccache
->flags
& KCM_MASK_KEY_PRESENT
) == 0) {
216 kcm_log(0, "Cannot acquire credentials without a key");
217 return KRB5_FCC_INTERNAL
;
220 event
->fire_time
= time(NULL
); /* right away */
221 event
->action
= KCM_EVENT_ACQUIRE_CREDS
;
222 } else if (is_primary_credential_p(context
, ccache
, newcred
)) {
223 if (newcred
->flags
.b
.renewable
) {
224 event
->action
= KCM_EVENT_RENEW_CREDS
;
225 ccache
->flags
|= KCM_FLAGS_RENEWABLE
;
227 if (ccache
->flags
& KCM_MASK_KEY_PRESENT
)
228 event
->action
= KCM_EVENT_ACQUIRE_CREDS
;
230 event
->action
= KCM_EVENT_NONE
;
231 ccache
->flags
&= ~(KCM_FLAGS_RENEWABLE
);
233 /* requeue with some slop factor */
234 event
->fire_time
= newcred
->times
.endtime
- KCM_EVENT_QUEUE_INTERVAL
;
236 event
->action
= KCM_EVENT_NONE
;
243 kcm_ccache_enqueue_default(krb5_context context
,
250 memset(&event
, 0, sizeof(event
));
251 event
.ccache
= ccache
;
253 ret
= kcm_ccache_make_default_event(context
, &event
, newcred
);
257 ret
= kcm_enqueue_event_internal(context
, &event
);
265 kcm_remove_event(krb5_context context
,
272 log_event(event
, "removing");
274 HEIMDAL_MUTEX_lock(&events_mutex
);
275 for (e
= &events_head
; *e
!= NULL
; e
= &(*e
)->next
) {
284 ret
= KRB5_CC_NOTFOUND
;
288 ret
= kcm_remove_event_internal(context
, &event
);
291 HEIMDAL_MUTEX_unlock(&events_mutex
);
297 kcm_cleanup_events(krb5_context context
,
302 KCM_ASSERT_VALID(ccache
);
304 HEIMDAL_MUTEX_lock(&events_mutex
);
306 for (e
= &events_head
; *e
!= NULL
; e
= &(*e
)->next
) {
307 if ((*e
)->valid
&& (*e
)->ccache
== ccache
) {
308 kcm_remove_event_internal(context
, e
);
314 HEIMDAL_MUTEX_unlock(&events_mutex
);
319 static krb5_error_code
320 kcm_fire_event(krb5_context context
,
325 krb5_creds
*credp
= NULL
;
330 switch (event
->action
) {
331 case KCM_EVENT_ACQUIRE_CREDS
:
332 ret
= kcm_ccache_acquire(context
, event
->ccache
, &credp
);
335 case KCM_EVENT_RENEW_CREDS
:
336 ret
= kcm_ccache_refresh(context
, event
->ccache
, &credp
);
337 if (ret
== KRB5KRB_AP_ERR_TKT_EXPIRED
) {
338 ret
= kcm_ccache_acquire(context
, event
->ccache
, &credp
);
342 case KCM_EVENT_DESTROY_CREDS
:
343 ret
= kcm_ccache_destroy(context
, event
->ccache
->name
);
345 case KCM_EVENT_DESTROY_EMPTY_CACHE
:
346 ret
= kcm_ccache_destroy_if_empty(context
, event
->ccache
);
349 ret
= KRB5_FCC_INTERNAL
;
356 /* Reschedule failed event for another time */
357 event
->fire_time
+= event
->backoff_time
;
358 if (event
->backoff_time
< KCM_EVENT_MAX_BACKOFF_TIME
)
359 event
->backoff_time
*= 2;
361 /* Remove it if it would never get executed */
362 if (event
->expire_time
&&
363 event
->fire_time
> event
->expire_time
)
364 kcm_remove_event_internal(context
, e
);
369 if (krb5_unparse_name(context
, event
->ccache
->client
,
373 kcm_log(0, "%s credentials in cache %s for principal %s",
374 (event
->action
== KCM_EVENT_ACQUIRE_CREDS
) ?
375 "Acquired" : "Renewed",
377 (cpn
!= NULL
) ? cpn
: "<none>");
382 /* Succeeded, but possibly replaced with another event */
383 ret
= kcm_ccache_make_default_event(context
, event
, credp
);
384 if (ret
|| event
->action
== KCM_EVENT_NONE
)
387 log_event(event
, "requeuing");
390 kcm_remove_event_internal(context
, e
);
397 kcm_run_events(krb5_context context
, time_t now
)
402 HEIMDAL_MUTEX_lock(&events_mutex
);
404 /* Only run event queue every N seconds */
405 if (now
< last_run
+ KCM_EVENT_QUEUE_INTERVAL
) {
406 HEIMDAL_MUTEX_unlock(&events_mutex
);
410 /* go through events list, fire and expire */
411 for (e
= &events_head
; *e
!= NULL
; e
= &(*e
)->next
) {
412 if ((*e
)->valid
== 0)
415 if (now
>= (*e
)->fire_time
) {
416 ret
= kcm_fire_event(context
, e
);
418 kcm_log(1, "Could not fire event for cache %s: %s",
419 (*e
)->ccache
->name
, krb5_get_err_text(context
, ret
));
421 } else if ((*e
)->expire_time
&& now
>= (*e
)->expire_time
) {
422 ret
= kcm_remove_event_internal(context
, e
);
424 kcm_log(1, "Could not expire event for cache %s: %s",
425 (*e
)->ccache
->name
, krb5_get_err_text(context
, ret
));
435 HEIMDAL_MUTEX_unlock(&events_mutex
);