1 /* $NetBSD: events.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $ */
4 * Copyright (c) 2005, PADL Software Pty Ltd.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of PADL Software nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 /* thread-safe in case we multi-thread later */
40 static HEIMDAL_MUTEX events_mutex
= HEIMDAL_MUTEX_INITIALIZER
;
41 static kcm_event
*events_head
= NULL
;
42 static time_t last_run
= 0;
44 static char *action_strings
[] = {
45 "NONE", "ACQUIRE_CREDS", "RENEW_CREDS",
46 "DESTROY_CREDS", "DESTROY_EMPTY_CACHE" };
49 kcm_enqueue_event(krb5_context context
,
54 if (event
->action
== KCM_EVENT_NONE
) {
58 HEIMDAL_MUTEX_lock(&events_mutex
);
59 ret
= kcm_enqueue_event_internal(context
, event
);
60 HEIMDAL_MUTEX_unlock(&events_mutex
);
66 print_times(time_t time
, char buf
[64])
69 strftime(buf
, 64, "%m-%dT%H:%M", gmtime(&time
));
71 strlcpy(buf
, "never", 64);
75 log_event(kcm_event
*event
, char *msg
)
77 char fire_time
[64], expire_time
[64];
79 print_times(event
->fire_time
, fire_time
);
80 print_times(event
->expire_time
, expire_time
);
82 kcm_log(7, "%s event %08x: fire_time %s fire_count %d expire_time %s "
83 "backoff_time %d action %s cache %s",
84 msg
, event
, fire_time
, event
->fire_count
, expire_time
,
85 event
->backoff_time
, action_strings
[event
->action
],
90 kcm_enqueue_event_internal(krb5_context context
,
95 if (event
->action
== KCM_EVENT_NONE
)
98 for (e
= &events_head
; *e
!= NULL
; e
= &(*e
)->next
)
101 *e
= (kcm_event
*)malloc(sizeof(kcm_event
));
103 return KRB5_CC_NOMEM
;
107 (*e
)->fire_time
= event
->fire_time
;
108 (*e
)->fire_count
= 0;
109 (*e
)->expire_time
= event
->expire_time
;
110 (*e
)->backoff_time
= event
->backoff_time
;
112 (*e
)->action
= event
->action
;
114 kcm_retain_ccache(context
, event
->ccache
);
115 (*e
)->ccache
= event
->ccache
;
118 log_event(*e
, "enqueuing");
124 * Dump events list on SIGUSR2
127 kcm_debug_events(krb5_context context
)
131 for (e
= events_head
; e
!= NULL
; e
= e
->next
)
132 log_event(e
, "debug");
138 kcm_enqueue_event_relative(krb5_context context
,
145 e
.backoff_time
= e
.fire_time
;
146 e
.fire_time
+= time(NULL
);
148 ret
= kcm_enqueue_event(context
, &e
);
153 static krb5_error_code
154 kcm_remove_event_internal(krb5_context context
,
163 (*e
)->fire_count
= 0;
164 (*e
)->expire_time
= 0;
165 (*e
)->backoff_time
= 0;
166 kcm_release_ccache(context
, (*e
)->ccache
);
176 is_primary_credential_p(krb5_context context
,
180 krb5_flags whichfields
;
182 if (ccache
->client
== NULL
)
185 if (newcred
->client
== NULL
||
186 !krb5_principal_compare(context
, ccache
->client
, newcred
->client
))
189 /* XXX just checks whether it's the first credential in the cache */
190 if (ccache
->creds
== NULL
)
193 whichfields
= KRB5_TC_MATCH_KEYTYPE
| KRB5_TC_MATCH_FLAGS_EXACT
|
194 KRB5_TC_MATCH_TIMES_EXACT
| KRB5_TC_MATCH_AUTHDATA
|
195 KRB5_TC_MATCH_2ND_TKT
| KRB5_TC_MATCH_IS_SKEY
;
197 return krb5_compare_creds(context
, whichfields
, newcred
, &ccache
->creds
->cred
);
201 * Setup default events for a new credential
203 static krb5_error_code
204 kcm_ccache_make_default_event(krb5_context context
,
208 krb5_error_code ret
= 0;
209 kcm_ccache ccache
= event
->ccache
;
211 event
->fire_time
= 0;
212 event
->expire_time
= 0;
213 event
->backoff_time
= KCM_EVENT_DEFAULT_BACKOFF_TIME
;
215 if (newcred
== NULL
) {
216 /* no creds, must be acquire creds request */
217 if ((ccache
->flags
& KCM_MASK_KEY_PRESENT
) == 0) {
218 kcm_log(0, "Cannot acquire credentials without a key");
219 return KRB5_FCC_INTERNAL
;
222 event
->fire_time
= time(NULL
); /* right away */
223 event
->action
= KCM_EVENT_ACQUIRE_CREDS
;
224 } else if (is_primary_credential_p(context
, ccache
, newcred
)) {
225 if (newcred
->flags
.b
.renewable
) {
226 event
->action
= KCM_EVENT_RENEW_CREDS
;
227 ccache
->flags
|= KCM_FLAGS_RENEWABLE
;
229 if (ccache
->flags
& KCM_MASK_KEY_PRESENT
)
230 event
->action
= KCM_EVENT_ACQUIRE_CREDS
;
232 event
->action
= KCM_EVENT_NONE
;
233 ccache
->flags
&= ~(KCM_FLAGS_RENEWABLE
);
235 /* requeue with some slop factor */
236 event
->fire_time
= newcred
->times
.endtime
- KCM_EVENT_QUEUE_INTERVAL
;
238 event
->action
= KCM_EVENT_NONE
;
245 kcm_ccache_enqueue_default(krb5_context context
,
252 memset(&event
, 0, sizeof(event
));
253 event
.ccache
= ccache
;
255 ret
= kcm_ccache_make_default_event(context
, &event
, newcred
);
259 ret
= kcm_enqueue_event_internal(context
, &event
);
267 kcm_remove_event(krb5_context context
,
274 log_event(event
, "removing");
276 HEIMDAL_MUTEX_lock(&events_mutex
);
277 for (e
= &events_head
; *e
!= NULL
; e
= &(*e
)->next
) {
286 ret
= KRB5_CC_NOTFOUND
;
290 ret
= kcm_remove_event_internal(context
, &event
);
293 HEIMDAL_MUTEX_unlock(&events_mutex
);
299 kcm_cleanup_events(krb5_context context
,
304 KCM_ASSERT_VALID(ccache
);
306 HEIMDAL_MUTEX_lock(&events_mutex
);
308 for (e
= &events_head
; *e
!= NULL
; e
= &(*e
)->next
) {
309 if ((*e
)->valid
&& (*e
)->ccache
== ccache
) {
310 kcm_remove_event_internal(context
, e
);
316 HEIMDAL_MUTEX_unlock(&events_mutex
);
321 static krb5_error_code
322 kcm_fire_event(krb5_context context
,
327 krb5_creds
*credp
= NULL
;
332 switch (event
->action
) {
333 case KCM_EVENT_ACQUIRE_CREDS
:
334 ret
= kcm_ccache_acquire(context
, event
->ccache
, &credp
);
337 case KCM_EVENT_RENEW_CREDS
:
338 ret
= kcm_ccache_refresh(context
, event
->ccache
, &credp
);
339 if (ret
== KRB5KRB_AP_ERR_TKT_EXPIRED
) {
340 ret
= kcm_ccache_acquire(context
, event
->ccache
, &credp
);
344 case KCM_EVENT_DESTROY_CREDS
:
345 ret
= kcm_ccache_destroy(context
, event
->ccache
->name
);
347 case KCM_EVENT_DESTROY_EMPTY_CACHE
:
348 ret
= kcm_ccache_destroy_if_empty(context
, event
->ccache
);
351 ret
= KRB5_FCC_INTERNAL
;
358 /* Reschedule failed event for another time */
359 event
->fire_time
+= event
->backoff_time
;
360 if (event
->backoff_time
< KCM_EVENT_MAX_BACKOFF_TIME
)
361 event
->backoff_time
*= 2;
363 /* Remove it if it would never get executed */
364 if (event
->expire_time
&&
365 event
->fire_time
> event
->expire_time
)
366 kcm_remove_event_internal(context
, e
);
371 if (krb5_unparse_name(context
, event
->ccache
->client
,
375 kcm_log(0, "%s credentials in cache %s for principal %s",
376 (event
->action
== KCM_EVENT_ACQUIRE_CREDS
) ?
377 "Acquired" : "Renewed",
379 (cpn
!= NULL
) ? cpn
: "<none>");
384 /* Succeeded, but possibly replaced with another event */
385 ret
= kcm_ccache_make_default_event(context
, event
, credp
);
386 if (ret
|| event
->action
== KCM_EVENT_NONE
)
389 log_event(event
, "requeuing");
392 kcm_remove_event_internal(context
, e
);
399 kcm_run_events(krb5_context context
, time_t now
)
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
);