The Trusted Solaris remote procedure call (RPC) mechanism is built on Berkeley internet sockets and the Trusted Security Information Exchange (TSIX) library, and supports Transport Layer Interface (TLI). Trusted Solaris modifications to RPC enable a server process to receive security attribute information on incoming client requests, and change security attribute information on an outgoing response to a client. Chapter 12, Trusted Security Information Exchange Library describes the privileges required to change security attribute information on messages.
In addition, mappings have been extended to include sensitivity labels to separate and protect mappings according to their sensitivity label.
Mapping is a relationship maintained by the RPC binder service between an ordered triple (program number, version number, and network ID) and a service address on a machine serviced by the RPC binder. The current set of mappings represents the available registered RPC services on a host. The Trusted Solaris environment supports single-level mapping and multilevel mapping.
Program number - A number assigned to identify the remote procedure.
Version number - The version number of the remote procedure.
Network ID - The network transport to which the network address refers.
A single-level mapping is a mapping the RPC binder service advertises only to clients that have the same sensitivity label as the server that created the mapping.
A multilevel mapping (MLM) is a mapping the RPC binder service advertises to all clients regardless of their sensitivity label. A multilevel mapping is created when a server has the net_mac_read privilege in its effective set when it makes the RPC library call to register the service with the RPC binder service.
A multilevel port is created when a server has the net_mac_read privilege in its effective set when it makes the RPC library call to create the port. See "Multilevel Ports" in Chapter 10, Interprocess Communications for a discussion of multilevel ports.
The server handle for RPC library calls is a pointer to an SVCXPRT data structure, and the client handle for RPC library calls is a pointer to a CLIENT data structure. In the Trusted Solaris environment, both structures have additional fields that point to security attribute information.
The security attributes pointed to by the server and client handles are based on the TSIX library. See Chapter 12, Trusted Security Information Exchange Library for information on the library routines and privileges required to change security attributes.
The caller must free all memory blocks allocated for security attribute pointers.
The following security attribute fields of the SVCXPRT structure can be accessed directly by the server process:
t6attr_t xp_tsol_incoming_attrsp t6attr_t xp_tsol_outgoing_attrsp t6mask_t xp_tsol_incoming_new_attrs |
A server can receive one or more security attributes of incoming client requests by using t6alloc_blk(3NSL) to allocate an opaque structure with space for the security attributes and setting xp_tsol_incoming_attrsp field in the SVCXPRT structure to point to the security attribute structure.
A privileged server can set security attributes on a request to the server by using t6alloc_blk(3NSL) to allocate an opaque structure with space for the security attributes and setting xp_tsol_outgoing_attrsp field in the SVCXPRT to point to the security attribute structure. The RPC library routines pick up the attributes and send them as the attributes for the response.
A server can examine the security attributes on the next and last bytes of data by using the xp_tsol_incoming_new_attrs field in the SVCXPRT structure to point to specific incoming attributes to be examined.
The following security attribute fields of the CLIENT structure can be accessed directly by the client process:
t6attr_t cl_tsol_incoming_attrsp t6attr_t cl_tsol_outgoing_attrsp |
A client can receive one or more security attributes of incoming server responses by using t6alloc_blk(3NSL) to allocate an opaque structure with space for the attributes and setting cl_tsol_incoming_attrsp field in the CLIENT structure to point to the security attribute structure.
A privileged client can set security attributes on a request to the server by using t6alloc_blk(3NSL) to allocate an opaque structure with space for the security attributes and setting xp_tsol_outgoing_attrsp field in the CLIENT structure to point to the security attribute structure. The RPC library routines pick up the attributes and send them as the attributes for the response.
The following header file is necessary to use the RPC programming interfaces.
#include <rpc/rpc.h> |
The examples in this chapter compile with the following libraries:
-DTSOL -lt6 -lnsl -lsocket -ltsol |
The Trusted Solaris environment introduces the rpcb_getallmaps() routine in the rpcbind() interface. This section lists the RPC man pages that have information specific to the Trusted Solaris environment added.
This is a simple client-server application to show how security attributes are sent and received with RPC library routines. Command line arguments supply the server name and a user ID, and the server process retrieves the user ID sent by the client, multiplies the input by 2, and sends the result to the client. To run the programs, compile them with the libraries listed in "Header Files and Libraries".
The following header file rpc_test.h is required for the example application to compile.
#include <rpc/rpc.h> #include <rpc/types.h> #define RPC_TEST_PROG ((u_long)1234567890) #define RPC_TEST_VERS ((u_long)1) #define RPC_TEST_DOUBLE1 ((u_long)1) #define RPC_TEST_EXIT1 ((u_long)2)
This part of the client program accepts command line inputs and creates a client handle.
#include <stdio.h> #include <stdlib.h> #include <rpc/rpc.h> #include <netdb.h> #include <tsix/t6attrs.h> #include "rpc_test.h" extern int main(int argc, char *argv[]) { struct timeval time_out; CLIENT *handlep; enum clnt_stat stat; int input, output; uid_t uid; if (argc < 2 || argc > 3) { fprintf(stderr, "Usage: simple_rpc_clnt_test HOSTNAME [UID]\n"); exit(1); } handlep = clnt_create(argv[1], RPC_TEST_PROG, RPC_TEST_VERS, "udp"); if (handlep == (CLIENT *) NULL) { fprintf(stderr, "Couldn't create client%s.\n", clnt_spcreateerror("")); exit(1); }
This part of the client program sets the client handle to point to the space allocated for the user ID to be input from the command line, sets the user ID value, sends the value to the server process, and waits for the server response. The client prints out the server response before it exits.
The client program needs the net_setid privilege in its effective set to send a changed outgoing user ID. The code comments indicate where privilege bracketing should occur.
if (argc == 3) { handlep->cl_tsol_outgoing_attrsp = t6alloc_blk(T6M_UID); if (handlep->cl_tsol_outgoing_attrsp == NULL) { fprintf(stderr, "Can't create attr buffer\n"); exit(1); } printf ("Sending UID %s\n", argv[2]); uid = atoi(argv[2]); if (t6set_attr(T6_UID, &uid, handlep->cl_tsol_outgoing_attrsp) != 0) { fprintf(stderr, "Error returned by t6set_attr.\n"); exit(1); } } time_out.tv_sec = 30; time_out.tv_usec = 0; input = 3; /* Turn net_uid on in the effective set */ stat = clnt_call(handlep, RPC_TEST_DOUBLE1, xdr_int, (caddr_t) &input, xdr_int, (caddr_t) &output, time_out); if (stat != RPC_SUCCESS) { fprintf(stderr, "Call failed. %s.\n", clnt_sperror(handlep, "")); exit(1); } /* Turn off the net_uid privilege */ printf("Response received: %d\n", output); (void) clnt_destroy(handlep); return (0); }
The server program sets the server handle to point to the space allocated space for all security attributes.
#include <stdio.h> #include <stdlib.h> #include <rpc/rpc.h> #include <tsix/t6attrs.h> #include "rpc_test.h" static void proc_1(struct svc_req *rqstp, SVCXPRT *transp); extern int main(int argc, char *argv[]) { SVCXPRT *handlep; struct netconfig *netconfigp; netconfigp = getnetconfigent("udp"); if (netconfigp == NULL) { fprintf(stderr, "Cannot find netconfig entry for udp.\n"); exit(1); } handlep = svc_tp_create(proc_1, RPC_TEST_PROG, RPC_TEST_VERS, netconfigp); if (handlep == NULL) { fprintf(stderr, "Cannot create service.\n"); exit(1); } freenetconfigent(netconfigp); handlep->xp_tsol_incoming_attrsp = t6alloc_blk(T6M_ALL_ATTRS); if (handlep->xp_tsol_incoming_attrsp == NULL) { fprintf(stderr, "Can't create attr buffer\n"); exit(1); } svc_run(); return (0); }
The remote procedure receives the user ID from command line arguments, and multiplies the input by 2, sends the result to the client and prints the response before exiting.
static void proc_1(struct svc_req *rqstp, SVCXPRT *handlep) { int input; int result; uid_t *uidp; switch(rqstp->rq_proc) { case NULLPROC: svc_sendreply(handlep, xdr_void, NULL); break; case RPC_TEST_DOUBLE1: if (!svc_getargs(handlep, xdr_int, (caddr_t) &input)) { fprintf(stderr, "Error from svc_getargs\n"); svcerr_systemerr(handlep); } uidp = (uid_t *) t6get_attr(T6_UID, handlep->xp_tsol_incoming_attrsp); if (uidp == NULL) fprintf(stderr, "Error from t6get_attr.\n"); else printf("Client's UID is %d\n", *uidp); result = 2 * input; if (!svc_sendreply(handlep, xdr_int, (caddr_t) &result)) { fprintf(stderr, "Error from sendreply\n"); svcerr_systemerr(handlep); } svc_freeargs(handlep, xdr_int, (caddr_t) &input); break; default: fprintf(stderr, "Call to unexpected procedure number %d\n", rqstp->rq_proc); svcerr_noproc(handlep); break; } }
The client process takes the server host name and a user ID as input parameters and prints that it is sending the specified user ID:
owl% phoenix phoenix% owl 2570 Sending UID 2570 |
The server retrieves the user ID and prints it out as follows:
Client's UID is 2570 |
The client process prints the server response and then exits:
Response received: 6 phoenix% |