|
|
In the examples presented so far, the caller never identified itself to the server, and the server never required an ID from the caller. Some network services, such as a network filesystem, require stronger security than what has been presented so far.
Every RPC call is subjected to a style of authentication by the RPC package on the server. Similarly, the RPC client package generates and sends authentication parameters suitable for the style of authentication in effect. The default authentication style is AUTH_NONE (none).
Just as different transports can be used when creating RPC clients and servers, different forms of authentication can be associated with RPC clients.
The authentication subsystem of the RPC package is open ended. That is, numerous styles of authentication are easy to support; programmers can design their own authentication style and easily configure the RPC package to support it.
In addition to AUTH_NONE, the RPC package already supports the following authentication styles:
When a caller creates a new RPC client handle as in:
clnt = clnt_create(host, prognum, versnum, nettype)the appropriate transport instance defaults the associated authentication handle to be
clnt->cl_auth = authnone_create();
Service implementors have a harder time dealing with authentication issues because the RPC package passes the service dispatch routine a request that has an arbitrary authentication style associated with it. Consider the fields of a request handle passed to a service dispatch routine:
/* * An RPC Service request */ struct svc_req { u_long rq_prog; /* service program number */ u_long rq_vers; /* service protocol vers num */ u_long rq_proc; /* desired procedure number */ struct opaque_auth rq_cred; /* raw credentials from wire */ caddr_t rq_clntcred; /* credentials (read only) */ SVCXPRT *rq_xprt; /* associated transport */ };The
rq_cred
is mostly opaque, except for one field of interest:
the style or flavor of authentication credentials:
/* * Authentication info. Mostly opaque to the programmer. */ struct opaque_auth { enum_t oa_flavor; /* style of credentials */ caddr_t oa_base; /* address of more auth stuff */ u_int oa_length; /* not to exceed MAX_AUTH_BYTES */ };The RPC package guarantees the following to the service dispatch routine:
rq_cred
is well formed.
Thus the service implementor may inspect the request's
rq_cred.oa_flavor
to determine the style of authentication the caller used.
The service implementor may also wish to inspect the other fields of
rq_cred
if the style is not one supported by the RPC package.
rq_clntcred
field is either
NULL
or points to a well formed structure
that corresponds to a supported style of authentication credentials.
Remember that only
AUTH_NONE,
AUTH_SYS,
AUTH_SHORT
and
AUTH_DES
styles are currently supported, so (currently)
rq_clntcred
could be cast only as a pointer to an
authsys_parms,
short_hand_verf,
or
authdes_cred
structure.
If
rq_clntcred
is
NULL,
the service implementor may wish to inspect the other (opaque) fields of
rq_cred
if the service knows about a new type of authentication
that the RPC package does not know about.
The RPC client can choose to use
AUTH_SYS
style authentication by setting
clnt->cl_auth
after creating the RPC client handle:
clnt->cl_auth = authsys_create_default();This causes each RPC call associated with
clnt
to carry with it the following authentication credentials structure:
/* * AUTH_SYS style credentials. */ struct authsys_parms { u_long aup_time; /* credentials creation time */ char *aup_machname; /* host name where client is */ uid_t aup_uid; /* client's effective uid */ gid_t aup_gid; /* client's current group id */ u_int aup_len; /* element length of aup_gids */ gid_t *aup_gids; /* array of groups user is in */ };These fields are set by authsys_create_default by invoking the appropriate system calls.
The following shows the server for a remote procedure, RUSERPROC_n, that computes the number of users on the network. As a trivial demonstration of authentication usage, this server checks AUTH_SYS credentials and does not service requests from callers whose user ID is 16:
nuser(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { struct authsys_parms *SYS_cred; uid_t uid; unsigned long nusers;A few things should be noted here:/* * we don't care about authentication for null proc */ if (rqstp->rq_proc == NULLPROC) { if (!svc_sendreply(transp, xdr_void, 0)) { fprintf(stderr, "can't reply to RPC call\n"); return (1); } return; } /* * now get the uid */ switch (rqstp->rq_cred.oa_flavor) { case AUTH_SYS: SYS_cred = (struct authsys_parms *)rqstp->rq_clntcred; uid = SYS_cred->aup_uid; break; case AUTH_NONE: default: svcerr_weakauth(transp); return; } switch (rqstp->rq_proc) { case RUSERSPROC_n: /* * make sure caller is allowed to call this proc */ if (uid == 16) { svcerr_systemerr(transp); return; } /* * Code here to compute the number of users * and assign it to the variable nusers */ if (!svc_sendreply(transp, xdr_u_long, &nusers)) { fprintf(stderr, "can't reply to RPC call\n"); return (1); } return; default: svcerr_noproc(transp); return; } }
AUTH_DES authentication is recommended for programs that require more security than that offered by the AUTH_SYS style of authentication.
AUTH_SYS authentication is easy to defeat. For example, instead of using authsys_create_default, a program could call authsys_create, and then change the RPC authentication handle to give itself any desired user ID and hostname.
The details of the AUTH_DES authentication protocol are complicated and are not explained here. See the ``AUTH_DES authentication'' for more information.
For AUTH_DES authentication to work, the keyserv(1Mbnu) daemon must be running on both the server and client machines. The users on these machines need public/secret key pairs assigned by the RPC administrator in the publickey(4bnu) database. And, they need to have decrypted their secret keys using the keylogin(1bnu) command.
If a client wishes to use AUTH_DES authentication, it must set its authentication handle appropriately. This is an example:
cl->cl_auth = authdes_seccreate(servername, 60, server, NULL);The first argument is the network name or ``netname'' of the owner of the server process. Typically, server processes are root processes and their netname can be derived using the following call:
char servername[MAXNETNAMELEN];rhostname is the hostname of the machine on which the server process is running. host2netname populates servername to contain this root process's netname. If the server process was run by a regular user, you could use the call user2netname instead. This is an example for a server process with the same user ID as the client:host2netname(servername, rhostname, NULL);
char servername[MAXNETNAMELEN];The last argument to both of these calls, user2netname and host2netname, is the name of the naming domain where the server is located. The NULL used here means ``use the local domain name.''user2netname(servername, getuid(), NULL);
The second argument to authdes_seccreate is a lifetime for the credential. Here it is set to sixty seconds which means that the credential will expire 60 seconds from now. If some mischievous program tries to reuse the credential, the server RPC subsystem will recognize that it has expired and will not grant any requests. If the same mischievous program tries to reuse the credential within the sixty second lifetime, it will still be rejected, because the server RPC subsystem remembers credentials it has seen in the near past, and will not grant requests to duplicates.
The third argument to authdes_seccreate is the name of the host to synchronize with. For AUTH_DES authentication to work, the server and client must agree on the time. Here we pass the hostname of the server itself, so the client and server will both be using the same time: the server's time. The argument can be NULL, which means ``don't bother synchronizing.'' A program should pass NULL only if sure the client and server are already synchronized.
The final argument to authdes_seccreate
is the address of a DES encryption key to use for encrypting
timestamps and data.
If this argument is NULL,
as it is in this example, a random key will be chosen.
The client
may find out the encryption key being used by consulting the
ah_key
field of the authentication handle.
The server side is simpler than the client side. This is the previous example rewritten to use the AUTH_DES style instead of AUTH_SYS:
#include <rpc/rpc.h> . . . . . . nuser(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { struct authdes_cred *DES_cred; uid_t uid; gid_t gid; int gidlen; gid_t gidlist[10]; /* * we don't care about authentication for null proc */Note the use of the routine netname2user, the inverse of user2netname: it takes a network ID and converts to a local system ID. netname2user also supplies the group IDs, not used in this example, but which may be useful to other programs.if (rqstp->rq_proc == NULLPROC) { /* same as before */ }
/* * now get the uid */ switch (rqstp->rq_cred.oa_flavor) { case AUTH_DES: DES_cred = (struct authdes_cred *) rqstp->rq_clntcred; if (! netname2user(DES_cred->adc_fullname.name, &uid, &gid, &gidlen, gidlist)) { fprintf(stderr, "unknown user: %s\n", DES_cred->adc_fullname.name); svcerr_systemerr(transp); return; } break; case AUTH_NONE: default: svcerr_weakauth(transp); return; }
/* * The rest is the same as before */