[190] in Kerberos_V5_Development
comments on librc from an implementer
jtkohl@ATHENA.MIT.EDU (jtkohl@ATHENA.MIT.EDU)
Mon Feb 19 16:11:14 1990
Date: Sat, 17 Feb 90 15:23:19 -0500
From: brnstnd@KRAMDEN.ACF.NYU.EDU (Dan Bernstein)
To: brnstnd@KRAMDEN.ACF.NYU.EDU, jtkohl@ATHENA.MIT.EDU
Subject: Re: volunteer stuff
Sorry about the delay; I've been busy assuming management responsibility
for stealth.
If ``the administrative types'' are having conniptions about the phrase
I crossed out on that volunteer contract, remind them that a contract
without consideration for both sides is unenforceable. The law recognizes
volunteer work as such. In the meantime, I'll finish coding.
I just sat down and did a blind first implementation, completely mucking
around with the spec as I went along. The default ("dfl") cache functions
are in their own file. Type and name are cleanly separated; they can be
stuck back together if necessary. I don't like the idea of copying names,
so all names and types must be constant strings. All of this can be
changed; I just coded the way that I like.
There's one static structure, the linked list of registered ops. For
amusement I put in a semaphore and appropriate down()s and up()s.
The tkt_authents in a cache are stored in a linked list, cross-linked
into a thousand open hash lists. So store() is hellishly fast for any
cache size.
I used v7 file I/O plus fsync() plus atomic rename(). Sorry, but without
fsync() there's no way to guarantee cache integrity through a crash.
Atomic rename() isn't necessary but greatly simplifies the design since
the cache supports only writes.
I'll need some .h files to run tests, or I can send you the code now and
give you the trouble. (I haven't yet run the code through gcc -Wall -ansi
-pedantic with some dummy definitions of tkt_authent and deltat, so were
it not for a programmer's overconfidence I wouldn't be sure that it even
compiles.)
My philosophy on .h files is to have each recognize double loading and
then to have each include all files that it depends on. This means that
the compiler has to work a bit harder but the programmer doesn't have to
worry about multiple inclusions or the order of inclusions.
> Some (at least one) rc type(s) will be pre-registered (through some
> linker magic).
A normal initialization works. The rc_dfl library has an initialized ops
with all its functions; the main rc.c includes rc_dfl.h and initializes
the registry to use it.
> get_name() needs to be in the rcache, since the info needed to construct
> that name (probably) resides in private storage in the rcache.
Yeah. Now it's separated into get_type() and get_name().
> The thought was (but not made explicit) that the "store" routine would
> do the removal of outdated entries, assuming that "expunge" was
> requested; the expunge routine does the work of cleaning up old entries.
Okay, I get the idea. Right now I assume a function cmp(aold,anew,deltat)
that compares two tkt_authents for a replay within interval deltat.
store() calls cmp() on every old tkt_authent in the appropriate hash
list; meanwhile it keeps a tally of valid and expired tkt_authents, and
uses two simple heuristics (cache hash hits and time since last) to
decide when expunging is necessary. The flag is gone: if the caller
wants to force an expunge, he can call expunge himself.
> > Why does store have an expunge flag? Should expunge do anything visible?
> Expunge removes all expired entries from the rcache.
In other words, it doesn't do anything visible to the callers. It just
conserves disk space and memory. Right?
> > Can get_name be eliminated?
> No, because it might be necessary for a routine to generate a replay
> cache, then retrieve its name for use by future unrelated processes
> (e.g. something run out of inetd).
Okay. I meant whether it could be eliminated in favor of having fixed
fields inside the rcache structure. Oh, I've combined generate_new()
and resolve(), since they do the same thing; the work of generating a
unique cache name is init()'s problem. A to-be-generated name is denoted
by NULL: in other words, get_name() is only guaranteed after init().
I've also split resolve() and moved the type-independent part outside
the ops structure.
Am I correct in taking init() to mean create and resolve() to mean open?
> Remember, what is being cached here is authenticators & server/client
> name pairs, not tickets!
Sorry, my mistake.
> there isn't really any per-cache standard
> info.
Lifespan, name.
> > What should the cache know about tickets
> > anyway?
> not much, except to store the server name, client name, and time &
> mstime fields from the authenticator.
Right now it knows nothing absolutely nothing about tkt_authents; it
just stores the whole thing and lets cmp() worry about the comparison
details. Probably I should change it to convert tkt_authents to, say,
tkt_authreps, which will be defined as containing exactly that
information which must not be replayed.
> For the default and generated names, I would suggest /usr/tmp/ and/or /tmp/
Yeah. Now it goes through KRB5RCACHEDIR, TMPDIR, and P_tmpdir (if it is
defined) or /tmp. Tough luck if the program wants to use lots of caches
in different starting directories.
> The only real reliability that will be necessary is
> over reboots, assuming machines can reboot in <5mins.
A reasonable assumption. If certain network buffers fill up, network
programs will die like fleas. An alert sysadmin at the console can get
the machine going again in seconds. (I speak from experience: most of
stealth's crashes seem to be caused by an elusive bug that fills up all
the dbufs, whatever those are.) Even after a normal crash, many machines
can boot in under ten minutes. I'd be surprised if a Cray took longer
than five.
> > Can I make restrictions like no . / : in names?
> Any particular reason to do so?
Well, when the type and name are combined (in, say, filenames) as
type:name, the type should not contain a colon. The restrictions on
/ . come from my systems-programmer mentality: always try to reduce
the damage an error can do to just a single directory, and always
make sure that files are visible.
You'll have to explain the error system to me. Right now the functions
return Kerror_success, Kerror_malloc, Kerror_io, and a few others. (Oh,
yeah, that should be krb5_error_success, krb5_error_malloc, etc. Sigh.)
---Dan