/*
* reserved.
*/
/*
*
* This file of the Kerberos V5 software is derived from public-domain code
* contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
*
*/
/*
* An implementation for the default replay cache type.
*/
/* Solaris Kerberos */
#include "rc_common.h"
#include "rc_file.h"
/* Solaris Kerberos */
#include <kstat.h>
#include <atomic.h>
#include <assert.h>
#include <syslog.h>
/*
* Solaris: The NOIOSTUFF macro has been taken out for the Solaris version
* of this module, because this has been split into a separate mem rcache.
*/
/* of course, list is backwards from file */
/* hash could be forwards since we have to search on match, but naaaah */
static int
{
int rephash;
/* Solaris: calling krb_timeofday() here, once for better perf. */
/* Solaris: calling alive() on rep since it doesn't make sense to store an
* expired replay.
*/
return CMP_EXPIRED;
}
{
case CMP_REPLAY:
return CMP_REPLAY;
case CMP_HOHUM:
t->nummisses++;
else
t->numhits++;
break;
default:
; /* wtf? */
}
}
return CMP_MALLOC;
return CMP_MALLOC;
}
return CMP_MALLOC;
}
return CMP_HOHUM;
}
/*ARGSUSED*/
char * KRB5_CALLCONV
{
}
/*ARGSUSED*/
{
struct file_data *t;
if (err)
return err;
return 0;
}
static krb5_error_code KRB5_CALLCONV
{
/* default to clockskew from the context */
return retval;
}
if ((krb5_rc_io_write(context, &t->d,
|| krb5_rc_io_sync(context, &t->d))) {
return KRB5_RC_IO;
}
return 0;
}
{
if (retval)
return retval;
return retval;
}
/* Called with the mutex already locked. */
{
struct authlist *q;
if (t->h)
FREE_RC(t->h);
if (t->name)
while ((q = t->a))
{
t->a = q->na;
FREE_RC(q);
}
if (t->d.fd >= 0)
(void) krb5_rc_io_close(context, &t->d);
FREE_RC(t);
return 0;
}
{
if (retval)
return retval;
return 0;
}
{
return KRB5_RC_IO;
}
/*ARGSUSED*/
{
struct file_data *t = 0;
/* allocate id? no */
return KRB5_RC_MALLOC;
if (name) {
if (!t->name) {
goto cleanup;
}
} else
t->name = 0;
if (!t->h) {
goto cleanup;
}
t->a = (struct authlist *) 0;
t->d.fd = -1;
t->recovering = 0;
return 0;
if (t) {
if (t->name)
krb5_xfree(t->name);
if (t->h)
krb5_xfree(t->h);
krb5_xfree(t);
}
return retval;
}
/*ARGSUSED*/
void
{
if (rp)
{
}
}
static krb5_error_code
{
unsigned int len;
sizeof(len));
if (retval)
return retval;
return KRB5_RC_IO_EOF;
return KRB5_RC_MALLOC;
if (retval)
goto errout;
sizeof(len));
if (retval)
goto errout;
goto errout;
}
goto errout;
}
if (retval)
goto errout;
if (retval)
goto errout;
if (retval)
goto errout;
return 0;
return retval;
}
static krb5_error_code
static krb5_error_code
{
long max_size;
int expired_entries = 0;
return retval;
}
t->recovering = 1;
sizeof(t->lifespan))) {
retval = KRB5_RC_IO;
goto io_fail;
}
goto io_fail;
}
/* now read in each auth_replay and insert into table */
for (;;) {
if (krb5_rc_io_mark(context, &t->d)) {
retval = KRB5_RC_IO;
goto io_fail;
}
if (retval == KRB5_RC_IO_EOF)
break;
else if (retval != 0)
goto io_fail;
/* Solaris: made the change below for better perf. */
case CMP_EXPIRED:
break;
case CMP_MALLOC:
goto io_fail;
break;
}
/*
* free fields allocated by rc_io_fetch
*/
}
retval = 0;
krb5_rc_io_unmark(context, &t->d);
/*
* An automatic expunge here could remove the need for
*/
if (retval)
krb5_rc_io_close(context, &t->d);
else if (expired_entries > EXCESSREPS)
t->recovering = 0;
return retval;
}
{
if (ret)
return ret;
return ret;
}
{
if (retval)
return retval;
if (retval)
return retval;
}
static krb5_error_code
{
if (buf == 0)
return KRB5_RC_MALLOC;
return ret;
}
/*
* Solaris Kerberos
*
* Get time of boot. This is needed for fsync()-less operation. See below.
*
* Cstyle note: MIT style used here.
*/
static
{
kstat_t *k;
/*
* We use the boot_time kstat from the "unix" module.
*
* It's hard to determine the interface stability of kstats. To be safe
* we treat boot_time with extra care: if it disappears or is renamed,
* or if its type changes, or if its value appears to be in the future,
* then we fail to get boot time and the rcache falls back on slow
* behavior (fsync()ing at every write). If this kstat should produce a
* time less than the actual boot time then this increases the chance of
* post-crash replays of Authenticators whose rcache entries were not
* fsync()ed and were lost.
*
* We consider it extremely unlikely that this kstat will ever change at
* all however, much less to change in such a way that it will return
* the wrong boot time as an unsigned 32-bit integer. If we fail to
* find the kstat we expect we log loudly even though the rcache remains
* functional.
*/
/* check that the kstat's type hasn't changed */
/* boot_time value sanity check */
/* krb5_timestamp is int32_t, this kstat is uint32_t; 2038 problem! */
/* Return boot time to 1 to indicate failure to get actual boot time */
bt = 1;
"kstat removed or changed?); rcache will be functional, but slow");
} else {
}
(void) kstat_close(kc);
return (bt);
}
/*
* Solaris Kerberos
*
* We optimize the rcache by foregoing fsync() in the most common cases.
* Foregoing fsync() requires an early boot procedure to ensure that we
* never accept an authenticator that could be a replay of one whose
* rcache entry we've lost.
*
* We do this by picking an arbitrary, small time delta such that
* storing any krb5_donot_replays whose ctime is further into the future
* than now + that small delta causes an fsync() of the rcache. Early
* after booting we must reject all krb5_donot_replays whose ctime falls
* before time of boot + that delta.
*
* This works well as long as client clocks are reasonably synchronized
* or as long as they use kdc_timesync. Clients with clocks faster than
* this delta will find their AP exchanges are slower than clients with
* good or slow clocks. Clients with very slow clocks will find that
* their AP-REQs are rejected by servers that have just booted. In all
* other cases clients will notice only that AP exchanges are much
* faster as a result of the missing fsync()s.
*
* KRB5_RC_FSYNCLESS_FAST_SKEW is that time delta, in seconds. Five
* seconds seems like a reasonable delta. If it takes more than five
* seconds from the time the kernel initializes itself to the time when
* a kerberized system starts, and clients have good clocks or use
* kdc_timesync, then no authenticators will be rejected.
*/
{
struct file_data *t;
/* No time of day -> broken rcache */
return KRB5KRB_AP_ERR_REPEAT;
/*
* Solaris Kerberos
*
* if boot_time <= 1 -> we always fsync() (see below)
* if boot_time == 1 -> don't bother trying to get it again (as it could be
* a slow operation)
*/
if (boot_time == 0) {
}
/*
* Solaris Kerberos
*
* fsync()-less-ness requires safety. If we just booted then we want to
* reject all Authenticators whose timestamps are old enough that we might
* not have fsync()ed rcache entries for them prior to booting. See
* comment above where KRB5_RC_FSYNCLESS_FAST_SKEW is defined. See
* also below, where krb5_rc_io_sync() is called.
*
* If we could tell here the time of the last system crash then we
* could do better because we could know that the rcache has been
* synced to disk. But there's no reliable way to detect past
* crashes in this code; getting the time of boot is hard enough.
*/
if (boot_time > 1 &&
/*
* A better error code would be nice; clients might then know
* that nothing's necessarily wrong with their (or our) clocks
* and that they should just wait a while (or even set their
* clock offset slow so that their timestamps then appear into
* the future, where we'd accept them.
*
* KRB5KRB_AP_ERR_SKEW will just have to do.
*/
return KRB5KRB_AP_ERR_SKEW;
if (ret)
return ret;
case CMP_MALLOC:
return KRB5_RC_MALLOC;
case CMP_REPLAY:
return KRB5KRB_AP_ERR_REPEAT;
case CMP_EXPIRED:
return KRB5KRB_AP_ERR_SKEW;
case CMP_HOHUM: break;
default: /* wtf? */ ;
}
if (ret) {
return ret;
}
/* Shall we automatically expunge? */
{
/* Expunge calls krb5_rc_io_sync() */
return ret;
}
/* Solaris Kerberos */
{
/*
* fsync() only when necessary:
*
* - on expunge (see above)
* - if we don't know when we booted
* - if rep->ctime is too far into the future
*/
if (krb5_rc_io_sync(context, &t->d)) {
return KRB5_RC_IO;
}
}
return 0;
}
static krb5_error_code
{
struct authlist *q;
char *name;
if (! t->recovering) {
t->name = 0; /* Clear name so it isn't freed */
if (retval)
return retval;
if (retval)
return retval;
}
if (!tmp)
return ENOMEM;
if (retval) {
return retval;
}
goto out;
goto out;
for (q = t->a;q;q = q->na) {
retval = KRB5_RC_IO;
goto out;
}
}
if (krb5_rc_io_sync(context, &t->d)) {
retval = KRB5_RC_IO;
goto out;
}
retval = KRB5_RC_IO;
out:
/*
* krb5_rc_file_close() will free the tmp struct and it's members that the
* previous functions had allocated.
*/
return (retval);
}
{
if (ret)
return ret;
return ret;
}