Skip Navigation Links | |
Exit Print View | |
ONC+ Developer's Guide Oracle Solaris 11.1 Information Library |
1. Introduction to ONC+ Technologies
4. Programmer's Interface to RPC
5. Advanced RPC Programming Techniques
Authentication Using RPCSEC_GSS
Changing Values and Destroying a Context
Setting Server Principal Names
Generating Client Principal Names
Receiving Credentials at the Server
/etc/gss/qop and /etc/gss/mech
Using Transient RPC Program Numbers
6. Porting From TS-RPC to TI-RPC
7. Multithreaded RPC Programming
8. Extensions to the Oracle Solaris RPC Library
Just as you can use different transports when creating RPC clients and servers, you can associate different “flavors” of authentication with RPC clients. The authentication subsystem of RPC is open ended. So, RPC can support many flavors of authentication. Appendix B, RPC Protocol and Language Specification further defines the authentication protocols.
Oracle RPC currently supports the authentication flavors shown in the following table.
Table 5-1 Authentication Methods Supported by Oracle RPC
|
When a caller creates a new RPC client handle as in:
clnt = clnt_create(host, prognum, versnum, nettype);
the appropriate client-creation routine sets the associated authentication handle to:
clnt->cl_auth = authnone_create();
If you create a new instance of authentication, you must destroy it with auth_destroy(clnt->cl_auth). This destruction conserves memory.
On the server side, the RPC package passes a request that has an arbitrary authentication style associated with it to the service-dispatch routine. The request handle passed to a service-dispatch routine contains the structure rq_cred. This structure is opaque, except for one field: the flavor of the authentication credentials.
/* * Authentication data */ 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 structural requirements to the service-dispatch routine:
The rq_cred field in the svc_req structure is well formed. You can check rq_cred.oa_flavor to get the flavor of authentication. You can also check the other fields of rq_cred if RPC does not support the flavor.
The rq_clntcred field that is passed to service procedures is either NULL or points to a well-formed structure that corresponds to a supported flavor of authentication credential. No authentication data exists for the AUTH_NONE flavor. rq_clntcred can be cast only as a pointer to an authsys_parms, short_hand_verf, authkerb_cred, or authdes_cred structure.
The client can use AUTH_SYS style authentication (called AUTH_UNIX in previous releases) by setting clnt–>cl_auth after creating the RPC client handle:
clnt->cl_auth = authsys_create_default();
This setting causes each RPC call associated with clnt to carry with it the following credentials-authentication structure shown in the following example.
Example 5-7 AUTH_SYS Credential Structure
/* * AUTH_SYS flavor credentials. */ struct authsys_parms { u_long aup_time; /* credentials creation time */ char *aup_machname; /* client's host name */ 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 */ };
rpc.broadcast defaults to AUTH_SYS authentication.
The following example shows a server, with procedure RUSERPROC_1(), that returns the number of users on the network. As an example of authentication, the server checks AUTH_SYS credentials and does not service requests from callers with a uid of 16.
Example 5-8 Authentication Server
nuser(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { struct authsys_parms *sys_cred; uid_t uid; unsigned int nusers; /* NULLPROC should never be authenticated */ if (rqstp->rq_proc == NULLPROC) { if (!svc_sendreply( transp, xdr_void, (caddr_t) NULL)) fprintf(stderr, "can't reply to RPC call\n"); 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; default: svcerr_weakauth(transp); return; } switch(rqstp->rq_proc) { case RUSERSPROC_1: /* 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_int, &nusers)) fprintf(stderr, "can't reply to RPC call\n"); return; default: svcerr_noproc(transp); return; } }
Note the following points about the example:
The authentication parameters associated with the NULLPROC (procedure number zero) are usually not checked.
The server calls svcerr_weakauth() if the authentication parameter's flavor is too weak. In this case, there is no way to get the list of authentication flavors the server requires.
The service protocol should return status for access denied. In the examples, the protocol instead calls the service primitive svcerr_systemerr().
The last point underscores the relation between the RPC authentication package and the services: RPC deals only with authentication and not with an individual service's access control. The services must establish access-control policies and reflect these policies as return statuses in their protocols.
Use AUTH_DES authentication for programs that require more security than AUTH_SYS provides. AUTH_SYS authentication is easy to defeat. For example, instead of using authsys_create_default(), a program can call authsys_create() and change the RPC authentication handle to give itself any desired user ID and host name.
AUTH_DES authentication requires keyserv() daemons to be running on both the server and client hosts. The NIS naming service must also be running. Users on these hosts need public/secret key pairs assigned by the network administrator in the publickey() database. The users must also have decrypted their secret keys with the keylogin() command. This decryption is normally done by login() unless the login password and secure-RPC password differ.
To use AUTH_DES authentication, a client must set its authentication handle appropriately, as shown in the following example.
cl->cl_auth = authdes_seccreate(servername, 60, server, (char *)NULL);
The first argument is the network name or “net name” of the owner of the server process. Server processes are usually root processes, and you can get their net names with the following call;
char servername[MAXNETNAMELEN]; host2netname(servername, server, (char *)NULL);
servername points to the receiving string and server is the name of the host the server process is running on. If the server process was run by a non-root user, use the call user2netname() as follows:
char servername[MAXNETNAMELEN]; user2netname(servername, serveruid(), (char *)NULL);
serveruid() is the user ID of the server process. The last argument of both functions is the name of the domain that contains the server. NULL means “use the local domain name.”
The second argument of authdes_seccreate() is the lifetime (known also as the “window”) of the client's credential. In this example, a credential expires 60 seconds after the client makes an RPC call. If a program tries to reuse the credential, the server RPC subsystem recognizes that the credential has expired and does not service the request carrying the expired credential. If any program tries to reuse a credential within its lifetime, the process is rejected, because the server RPC subsystem saves credentials it has seen in the near past and does not serve duplicates.
The third argument of authdes_seccreate() is the name of the timehost used to synchronize clocks. AUTH_DES authentication requires that server and client agree on the time. Example 5-8 specifies synchronization with the server. A (char *)NULL says not to synchronize. Use this syntax only when you are sure that the client and server are already synchronized.
The fourth argument of authdes_seccreate() points to a DES encryption key to encrypt timestamps and data. If this argument is (char *)NULL, as it is in Example 5-8, a random key is chosen. The ah_key field of the authentication handle contains the key.
The server side is simpler than the client. The following example shows the server in Example 5-8 changed to use AUTH_DES.
Example 5-9 AUTH_DES Server
#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]; /* NULLPROC should never be authenticated */ 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; default: svcerr_weakauth(transp); return; } /* The rest is the same as before */
The routine netname2user() converts a network name, or “net name” of a user, to a local system ID. It also supplies group IDs, which are not used in this example.
Recent releases of the Oracle Solaris OS include support for most client-side features of Kerberos 5 except klogin. AUTH_KERB is conceptually similar to AUTH_DES. The essential difference is that DES passes a network name and a DES-encrypted session key, while Kerberos passes the encrypted service ticket. This section describes other factors that affect implementation and interoperability.
Kerberos uses the concept of a time window in which its credentials are valid. It does not place restrictions on the clocks of the client or server. Specifically, the window is passed as an argument to authkerb_seccreate(). The window does not change. If a timehost is specified as an argument, the client side gets the time from the timehost and alters its timestamp by the difference in time. Various methods of time synchronization are available. See the kerberos_rpc man page for more information.
Kerberos users are identified by a primary name, instance, and realm. The RPC authentication code ignores the realm and instance, while the Kerberos library code does not. The assumption is that user names are the same between client and server. This enables a server to translate a primary name into user identification information. Two forms of well-known names are used (omitting the realm):
root.host represents a privileged user on client host.
user.ignored represents the user whose user name is user. The instance is ignored.
Kerberos uses cipher block chaining (CBC) mode when sending a full name credential, one that includes the ticket and window, and electronic code book (ECB) mode otherwise. CBC and ECB are two methods of DES encryption. The session key is used as the initial input vector for CBC mode. The following notation means that XDR is used on object as a type.
xdr_type(object)
The length in the next code section is the size, in bytes of the credential or verifier, rounded up to 4-byte units. The full name credential and verifier are obtained as follows:
xdr_long(timestamp.seconds) xdr_long(timestamp.useconds) xdr_long(window) xdr_long(window - 1)
After encryption with CBC with input vector equal to the session key, the output is two DES cipher blocks:
CB0 CB1.low CB1.high
xdr_long(AUTH_KERB) xdr_long(length) xdr_enum(AKN_FULLNAME) xdr_bytes(ticket) xdr_opaque(CB1.high)
xdr_long(AUTH_KERB) xdr_long(length) xdr_opaque(CB0) xdr_opaque(CB1.low)
xdr_long(timestamp.seconds) xdr_long(timestamp.useconds)
The nickname is encrypted with ECB to obtain ECB0, and the credential is:
xdr_long(AUTH_KERB) xdr_long(length) xdr_enum(AKN_NICKNAME) xdr_opaque(akc_nickname)
xdr_long(AUTH_KERB) xdr_long(length) xdr_opaque(ECB0) xdr_opaque(0)