Messages in this thread | | | Subject | Re: kerberos in the linux kernel?? | Date | Tue, 30 Nov 1999 01:33:50 -0500 | From | Marcus Watts <> |
| |
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/
| |