Re: kerberos in the linux kernel??

Marcus Watts (mdw@umich.edu)
Tue, 30 Nov 1999 01:33:50 -0500


You wrote:
> Sender: curtisb@workspot.com
> Message-Id: <384226DD.297A7AA8@workspot.com>
> Date: Sun, 28 Nov 1999 23:10:21 -0800
> From: "Curtis M. Brune" <curtis_brune@workspot.com>
> Organization: WorkSpot, Inc.
> X-Mailer: Mozilla 4.61 [en] (X11; U; Linux 2.2.12 i686)
> X-Accept-Language: en
> Mime-Version: 1.0
> To: kerberos@MIT.EDU, linux-kernel@vger.rutgers.edu
> Subject: kerberos in the linux kernel??
> Content-Type: text/plain; charset=us-ascii
> Content-Transfer-Encoding: 7bit
>
>
> Hello--
>
> I was wondering if it's possible (or has been done) to "kerberize" the
> linux kernel so that as every process is instantiated the user is first
> authenticated by a kerberos KDC. I want to authenticate thousands of
> users before they launch any program, even programs like /bin/ls,
> without recompiling/relinking or manually "kerberizing" a single
> existing program. is this possible?
>
> I can see generating a default principal for each user based on their
> UID and a default realm. I envision giving a module a list of
> directories that contain executable programs that require authentication
> to run. Seems possible, has anybody done this?
>
> I don't know anything about writing a kernel module or other kernel
> hacking, but i'm up for it!
>
> Also I think this might violate some "crypto in the kernel" rules -- I
> poked around the international kernel patch for a while, but didn't find
> anything.
>
> Cheers,
> Curt
>

I think the best way to look at this is to look at other implementations
of "kerberos in the kernel". It *is* possible to do more or less what
you're asking for, with some important differences.

The first important difference is you say "every process"..."authenticated
by a kerberos KDC". This is the wrong way to think about things, because
it grossly violates Unix semantics. The way to think of it is:
kerberos credentials are like the Unix UID.
When you fork (& create a new process), the child process
inherits its parent's UID.
when you fork, the child process should *also* inherit its
parent's kerberos tickets.

The second important difference has to do with "launching any program, even ls".
I believe what you are asking for here is an *authorization* issue, and it's
very important to distinguish between *authentication* and *authorization*.
Authorization = I have the right to do X.
Authentication = I can prove who I am.
In real life, suppose you were going to a *very* fancy and exclusive party.
In order to gain admission to the party, the bouncers at the door check
your ID. They then check a list to see if you've been admitted to the party.
Checking your ID is authentication. Checking your name on the list
is authorization. A similar process happens with boarding an airplane.
The ticket is your authorization - but it's not authentication. Many
airlines are now paranoid enough to check your ID as well -- that's
the authentication step. An interesting difference between the party
and the plane is that on the party, *they* keep the authorization
information, and for the plane, *you* carry the authorization information.
Both schemes are possible, and have different properties.

You also talked about a need to deal with "thousands of users". That
brings up some other less major points -- basically, the Unix security
model was designed to deal with a small to medium sized computer with
fairly lax security needs -- say, the needs a small department might
need. The Unix security model doesn't scale so well into the many
thoussands of users size.

Probably the *best* example of "kerberos in the kernel" is AFS, so it's
worth explaining.

The first step with AFS basically the initial sign-on. There are 2
parts that implement this on the client workstation. The first is
the login program, and the 2nd is the kernel. The login program has
to talk to a KDC (instead of a local password database) to acquire
the TGT. The login program then has to acquire a ticket for "afs",
and stuff this into the kernel. The kernel, for its part, provides
2 calls. The first is "setpag", and the 2nd is "settoken". By
default, whenver a process forks, it belongs to the same "protection
access group (pag)" as its parent. setpag breaks this association
and creates a new pag that this process, and its descendents, will
belong to. The 2nd call, "settoken", stashes the afs ticket into
the pag structure. It's possible to have multiple "tokens" or afs tickets,
for different realms, in the same pag structure.
( For versions of login that support "pam", it's
easy to teach the pam stuff to talk to kerberos
and do the setpag/settoken calls. )

The next step with AFS is to go talk to a file out in AFS-land.
The AFS cache manager (with implements the "afs" filesystem) does
this on your behalf, automatically, as part of accessing AFS.
To access AFS, the cache manager uses a special flavour of rpc,
called "rx". Rx does a lot of other stuff, but the important
thing rx does here is that it has the ability to checksum the
request and to pass an encrypted copy of the checksum along
with the ticket to the fileserver. The fileserver, which also
knows key of afs, can decrypt the ticket, extract the session
key, and verify that the encrypted hashes match. Once the
file server has done this, it has "authenticated" you. It still
has *no idea* though, if you're allowed to access that file.

In order to check authorization, the file server has to do some
interesting fun and games. From the ticket, it knows your
principal (user.instancer@realm), but nothing more. This
is obviously unwieldly, and it's not what's stored in the
filesystem. Instead, what's stored in the filesystem
is just numbers -- "viceIDs". A viceID is to a kerberos principal
as a UID is to a loginid. For AFS, there is another database ("pt"),
which is logically parallel to the kerberos database
and contains principal<->viceID mappings. The "pt" database
also contains one other important set of mappings, "groups".
( "Groups" is one place where the Unix security model is weak.
If you're familiar with Unix groups, then you're probably
also familiar with their limits -- only the system administrator
can create or change groups, and users can only belong to
a small number of groups, and groups can't contain very many
people. AFS changes all that, it's easy for people to create
their own groups and add largish numbers of people to them,
and one person can belong to a fairly large # of groups. )
So, the fileserver first has to make an RPC call to map the kerberos
principal into a vicedID, and then it has to make another call
to map the viceID into a list of all the groups that users
belongs to - "getcps".

Doing a nametoID and getcps call on every filesystem RPC would
obviously be unwieldly, so the AFS fileserver also has the ability
to cache lookup results. This is kept on a per-connection basis,
so the fileserver is keeping state information around. It's
all disposable however -- the fileserver is more or less free to
throw this information away anytime it wants, and to regenerate
it on demand as needed. Users can also force the filesystem to
do this, by doing another "klog" -- this stores new tokens into
the kernel ("settoken"), which causes the cache manager to make
new connections to the fileserver, which causes the fileserver
to do a new nametoID and getcps call. Users might do this if
they're added to a group after they've already logged in, and
want to get immediate access as a member of that group.
( With K5, it is also possible to store authorization
information directly in the K5 ticket. OSF DCE DFS does
this, as does microsoft. Doing this does require
modifying the KDC which means you're much less portable.
If we're talking MIT K5 code, there are some other messy
issues here having to do with non-threaded design and
its limitations. The simple answer is, don't do this. )

The fileserver's next step, once it has the results from getcps, is to
check the file's ACL. Physically, the ACL is just a list of some 40
longs, but logically, it contains 2 lists of who & what. The who is a
viceid (which could be either a user or a group), the what is a bitmap
of rights, such as read, lookup, insert, delete, write, etc. The
rights you have are the intersection of the union of positive rights
for each group or user that matches your CPS on the positive list, and
the inverse of the union of the negative rights for each group or user
that matches your CPS.
( ACLs are another area which most versions of Unix are
very deficient in. IBM's version of Unix, "AIX",
does actually have ACLs. An important limitation of
AFS 3 ACLs is that they apply per directory, not per-file.
OSF DCE DFS does implement per-file ACLs. Novell netware
implements ACLs, with a richer notion of inheritance than
in AFS 3. )

The result of checking the CPS against the ACL is to compute
authorization data. Once the fileserver has that, it knows whether
you're permitted to read the file and will return it if so. So, the
end result of all this is that the fileserver is using kerberos as the
*authentication* part of the authentication & authorization decision it
makes in granting access to a file.

There are some important limitations on what AFS can do. The first has
to do with SUID programs. Basically, the notion of SUID programs makes
no sense in a networked environment. I, the owner of an executable
file, can't stop you, joe random user, from modifying your workstation
such that you can trap into the rom monitor in the middle of my
program, change a critical register's contents, and continue
execution. That means I can't trust you to run my program unmodified,
and that means the fundemental assumption of SUID, that the kernel can
be trusted, is all wrong. So in the context of AFS, a SUID program in
AFS-land *may* operate with enhanced rights on the user's workstation
(if the owner of the machine so chooses), but does *not* confer any
additional temporary access privileges to AFS. If you want to write a
secure application, you need to break it into "secure" and "insecure"
parts, and run the "secure" part on a trusted server that you own. In
Unix, files come with separate "execute" and "read" permissions. In
AFS, the two are equivalent -- just as I can't trust your workstation
with the SUID application, I can't trust whether it's fetching a file
to run it, or to read it. AFS still does store the Unix file
permissions, and your workstation probably will refuse to run a file
that doesn't have the execute bit set, it's just that most of the
interpretation of these bits is done by the workstation, not by AFS,
and therefore can't be trusted.

AFS 3 is, of course, proprietary code which is owned by Transarc, which
is part of IBM. There are at least 2 freeware clones of AFS, which do
a fairly decent job with the client workstation tools, including the
cache manager, and I believe they are working on the server end of
things as well. Arla is the name of one of these efforts, and I'm
afraid I don't have any URLs handy. NFS is evolving in the direction
of looking more like AFS, so may someday evolve some of the semantics
of AFS including cache callbacks, ACLs and groups. I don't know how
far it's gone in this direction yet. I believe I heard a rumour that
some part of OSF DCE source was released to the public, including (I
believe) DFS. I'm sure the part that's released isn't fully functional
without the rest of DCE, which is a pretty scary monster, but it could
be an interesting project to simplify and re-implement the missing
pieces. DFS does have one feature worthy of note: it has the ability
to export a Unix filesystem into DFS-land. The filesystem that's
exported doesn't, of course, support DFS acls, so it is limited
compared to real DFS space.

If you only care about single-workstation semantics, then you might
not care about all the things AFS and DFS can do. You can ignore
all the caching issues, and you might be content to implement Unix
file permissions as is. You wouldn't need to worry about RPC's,
secure or otherwise. Functionally, the Unix kernel would look like
"a server" to kerberos, and have its own key (which you'd probably
stuff in the kernel at boot time), and decode service tickets
directly. If you want to be really cheezy about it, you could
actually remove the kernel from most of this picture. Simply implement
a set of filesystem stubs that call out to a user process (like
"xfs" in OpenBSD), and have the user process implement all
the hard parts of the filesystem code (including decoding user service
tickets.) It won't perform as well as having the bits in the kernel,
but it will be a *lot* easier to debug.

Hope this helps, or at least suggests some additional avenues
for research.

-Marcus Watts
UM ITD PD&D Umich Systems Group

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/