By Wolfgang Jährling
Introduction
In this text, which mostly resembles the talk I gave at Libre Software Meeting 2002 in Bordeaux, I will describe what the auth server does, why it is so important and which cool things you can do with it, both on the programming and the user side. I will also describe related programs like the password and fakeauth servers. Note that this text is targeted at programmers who want to understand the auth mechanism in detail and are already familiar with concepts like Remote Procedure Calls (RPCs) as well as the way User- and Group-IDs are used in the POSIX world.
The auth server is a very small server, therefore
it gives a useful example when you want to learn how a server
typically looks like. One reason why it is so small is that the auth
interface, which it implements, consists of only four RPCs. You can
find the interface in hurd/hurd/auth.defs and the server itself in
hurd/auth/.
How IDs are represented and used
Each process holds (usually) one port to auth (an auth_t in C source,
which actually is a mach_port_t, of course). The purpose of auth is
to manage User-IDs and Group-IDs, which is the reason why users often
will have no choice but to make use of the systems main auth server,
which does not listen on /servers/auth; instead you inherit a port to
auth from your parent process. Each such port is (internally in the
auth server) associated with a set of effective User- and Group-IDs as
well as a set of available User- and Group-IDs. So we have four sets
of IDs in total. The available IDs can be turned into corresponding
effective IDs at any time.
When you send an auth_getids RPC on the port you hold, you will get
information about which IDs are associated with it, so you can figure
out which permissions you have. But how will a server know that you
have these permissions and therefore know which actions (e.g. writing
into file "foo") it is supposed to do on your behalf and which not?
The establishing of a trusted connection to a server works as follows:
- A user wants a server to know its IDs
- The user requests a reauthentication from the server
- In this request the user will include a port
- Both will hand this port to auth
- The user uses
auth_user_authenticate - The server uses
auth_server_authenticate - The server also passes a new port to auth
- auth matches these two requests
- The user gets the new port from auth
- The server learns about the IDs of the user
- The user uses the new port for further communication
We have different RPCs for users and servers because what we pass and what we get back differs for them: Users get a port, and servers get the sets of IDs, and have to specify the port which the user will get.
It is interesting to note that auth can match the requests by
comparing two integers, because when you get the same port from two
people, you will have the same mach_port_t (which is nothing but an
integer).
All of this of course only works if they use the same auth server, which is why I said often you have no choice other than to use the one main auth server. But this is no serious restriction, as the auth server has almost no functionality one might want to replace. In fact, there is one replacement for the default auth implementation, but more on that later.
POSIX and beyond
Before we examine what is possible with this design, let us take a short look at how the POSIX semantics are implemented on top of this design. When a program that comes out of POSIX-land asks for its own effective User- or Group-ID, we will tell it about the first of the effective IDs. In the same sense, the POSIX real User- or Group-ID is the first available ID and the POSIX saved User- or Group-ID is the second available ID, which is why you have the same ID two times in the available IDs when you log into your GNU/Hurd machine (you can figure out which IDs you have with the program "ids", that basically just does an auth_getauth RPC). When you lack one of those IDs (for example when you have no effective Group-ID), a POSIX program asking for this particular information will get "-1" as the ID.
But as you can imagine, we can do more than what POSIX specifies. Fox
example, we can modify our permissions. This is always done with the
auth_makeauth RPC. In this RPC, you specify the IDs that should be
associated with the new port. All of these IDs must be associated
with either the port where the RPC is sent to or one of the additional
ports you can specify; an exception is the superuser root, which is
allowed to creat ports that are associated with arbitrary IDs.
Hereby you can convert available into effective IDs.
This opens the door to a bunch of nice features. For example, we have the addauth program in the Hurd, which makes it possible to add an ID to either a single process or a group of processes if you hold the ID or know the appropriate password, and there is a corresponding rmauth program that removes an ID. So when you are working on your computer with GNU Emacs and want to edit a system configuration file, you switch to Emacs' shell-mode, do an "addauth root", enter the password, edit the file, and when you are done switch back to shell-mode and do "rmauth root". These programs have some interesting options, and there are various other programs, for setting the complete list of IDs (setauth) and so on.
Related servers
Finally, I want to explain two servers which are related to auth. The
first is the password server, which listens on
/servers/password. If you pass to it a User- or Group-ID and the
correct password for it, it will return a port to auth to you which is
associated with the ID you passed to it. It can create such a port
because it is running as root. So let us assume you are an FTP server
process. You will start as root, because you want to use port 21 (in
this case, "port" does not refer to a mach_port_t, of course). But
then, you can drop all your permissions so that you run without any
ID. This makes it far less dangerous to communicate with yet unknown
users over the network. But when someone now hands a username and
password to you, you can ask the password server for a new auth port.
The password server will check the data you pass to it, for example by
looking into /etc/shadow, and if it is valid, it will ask the auth
server for a new port. It receives this port from auth and then
passes it on to you. So you have raised your permissions. (And for
the very curious: Yes, we are well aware of the differences between
this concept and capabilities; and we also do have some kinds of
capabilities in various parts of the Hurd.)
My second example is the fakeauth server. It also implements the auth
protocol. It is the part of the fakeroot implementation that gives a
process the impression that it runs as root, even if it doesn't. So
when the process asks fakeauth about its own IDs, fakeauth will tell
the process that it runs as root. But when the process wants to make
use of the authentication protocol described earlier in this text,
fakeauth will forward the request to its own auth server, which will
usually be the systems main auth server, which will then be able to
match the auth_*_authenticate requests. So what fakeauth does is
acting as a proxy auth server that gives someone the impression to run
as root, while not modifying what that one is allowed to do.
At this point, I have said at least most of what can be said about the auth server and the protocol it implements, so I will finish by saying that it might be an interesting task (for you) to modify some existing software to take advantage of the features I described here.
