Trusted Solaris 開発ガイド

第 14 章 遠隔手続き呼び出し

Trusted Solaris 7 の遠隔手続き呼び出し (RPC) 機構は、Berkeley インターネットソケットとトラステッドセキュリティ情報交換 (TSIX) ライブラリで構築されます。この機構は、トランスポートレイヤーインタフェース (TLI) をサポートします。RPC に対する Trusted Solaris 7 の変更により、サーバープロセスは、着信クライアント要求のセキュリティ属性情報を受信し、クライアントに対する送信応答のセキュリティ属性情報を変更することが可能になりました。メッセージのセキュリティ属性情報の変更に必要な特権の詳細は、第 13 章「トラステッドセキュリティ情報交換ライブラリ」を参照してください。

また、マップは、その機密ラベルに応じて分けて保護するために、機密ラベルを含むように拡張されました。

マップ

マップとは、RPC バインダサービスによって維持されるマシン上の順序付けられた 3 要素 (プログラム番号、バージョン番号、ネットワーク ID) と RPC バインダがサービスを提供するマシンのサービスアドレスとの関係です。現在のマップセットは、ホスト上の利用可能な登録済み RPC サービスを示します。Trusted Solaris 7 は、シングルレベルマップとマルチレベルマップをサポートします。

シングルレベルマップ

シングルレベルマップは、マップを作成したサーバーと同じ機密ラベルのクライアントだけに RPC バインダサービスが通知するマップです。

マルチレベルマップ

マルチレベルマップ (MLM) は、機密ラベルにかかわらずすべてのクライアントに RPC バインダサービスが通知するマップです。マルチレベルマップは、有効セットに net_mac_read 特権があるサーバーが RPC ライブラリコールを作成して RPC バインダサービスにサービスを登録する場合に作成されます。

マルチレベルポート

マルチレベルポートは、有効セットに net_mac_read 特権があるサーバーが RPC ライブラリコールを作成してポートを作成する場合に作成されます。マルチレベルポートの詳細は、第 11 章「プロセス間通信」「マルチレベルポート」を参照してください。

セキュリティ属性

RPC ライブラリコールのサーバーハンドルは、SVCXPRT データ構造体に対するポインタです。また、RPC ライブラリコールのクライアントハンドルは、CLIENT データ構造体に対するポインタです。Trusted Solaris 7 では、これら両構造体にセキュリティ属性情報を指す追加フィールドがあります。

サーバーハンドルとクライアントハンドルが指すセキュリティ属性は、TSIX ライブラリに基づいています。セキュリティ属性の変更に必要な TSIX ライブラリルーチンと特権の詳細は、第 13 章「トラステッドセキュリティ情報交換ライブラリ」を参照してください。


注 -

呼び出し元は、セキュリティ属性ポインタに割り当てられたすべてのメモリーブロックを解放する必要があります。


サーバー

次に示す、SVCXPRT 構造体のセキュリティ属性フィールドは、サーバープロセスを使用して直接アクセスできます。

t6attr_t xp_tsol_incoming_attrsp

t6attr_t xp_tsol_outgoing_attrsp

t6mask_t xp_tsol_incoming_new_attrs

サーバーは、t6alloc_blk(3N) を使用してセキュリティ属性の領域を持つ不透明な構造体を割り当て、SVCXPRT 構造体内の xp_tsol_incoming_attrsp フィールドを設定してそのセキュリティ属性構造体を指すことにより、着信クライアント要求の 1 つ以上のセキュリティ属性を受信できます。

特権を持つサーバーは、t6alloc_blk(3N) を使用してセキュリティ属性の領域を持つ不透明な構造体を割り当て、SVCXPRT 構造体内の xp_tsol_outgoing_attrsp フィールドを設定してセキュリティ属性構造体を指すことにより、サーバーへの要求にセキュリティ属性を設定できます。RPC ライブラリルーチンは、属性をピックアップし、それらを応答の属性として送信します。

サーバーは、SVCXPRT 構造体内の xp_tsol_incoming_new_attrs フィールドを使用して、確認される特定の着信属性を指すことにより、データの次のバイトと最後のバイトのセキュリティ属性を確認できます。

クライアント

次に示す CLIENT 構造体のセキュリティ属性フィールドは、クライアントプロセスを使用して直接アクセスできます。

t6attr_t cl_tsol_incoming_attrsp

t6attr_t cl_tsol_outgoing_attrsp

クライアントは、t6alloc_blk(3N) を使用して属性の領域を持つ不透明な構造を割り当て、CLIENT 構造体内の cl_tsol_incoming_attrsp フィールドを設定してセキュリティ属性構造を指すことにより、着信サーバー応答の 1 つ以上のセキュリティ属性を受信できます。

特権を持つクライアントは、t6alloc_blk(3N) を使用してセキュリティ属性の領域を持つ不透明な構造体を割り当て、CLIENT 構造体内の xp_tsol_outgoing_attrsp フィールドを設定してセキュリティ属性構造体を指すことにより、サーバーへの要求にセキュリティ属性を設定できます。RPC ライブラリルーチンは、属性を取り出し、それらを応答の属性として送信します。

ヘッダーファイルとライブラリ

RPC プログラミングインタフェースを使用するには、次のヘッダーファイルが必要です。

#include <rpc/rpc.h>

この章の例は、次のライブラリを使用してコンパイルしています。

-DTSOL -lt6 -lnsl -lsocket -ltsol

プログラミングインタフェース

Trusted Solaris 7 には、基本となる Solaris 7 に導入されているもの以外の新しい RPC インタフェースは、導入されていません。次に、Trusted Solaris 7 固有の情報を追加した RPC のマニュアルページを示します。

クライアントサーバーアプリケーション

この節では、RPC ライブラリルーチンによってセキュリティ属性がどのように送受信されるかを示す簡単なクライアントサーバーアプリケーションを示します。コマンド行引数には、サーバー名とユーザー ID を指定します。サーバープロセスは、クライアントが送信するユーザー ID を取得し、その入力を 2 倍にした結果をクライアントに送信します。このプログラムを実行するには、「ヘッダーファイルとライブラリ」に示されているライブラリを使用してコンパイルしてください。

ヘッダーファイル

このアプリケーション例をコンパイルするには、次のヘッダーファイル rpc_test.h が必要です。

#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)

クライアントプログラム

クライアントプログラムのこの部分は、コマンド行入力を受け入れて、クライアントハンドルを作成します。

#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);
 	}

クライアントプログラムのこの部分は、コマンド行から入力されるユーザー ID に割り当てられる領域を指すようにクライアントハンドルを設定し、ユーザー ID 値を設定し、その値をサーバープロセスに送信し、サーバー応答を待機します。クライアントは、サーバー応答を出力して終了します。

クライアントプログラムは、変更された発信ユーザー ID を送信するために有効セットに net_setid 特権を必要とします。コード内のコメントは、特権のブラケットが必要な位置を示します。

	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;

/* 有効セット内で net_uid をオン (有効) にする */
 	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);
 	}
/* net_uid をオフ (無効) にする */

 	printf("Response received: %d¥n", output);
 	(void) clnt_destroy(handlep);

 	return (0);
 }

サーバープログラム

サーバープログラムは、すべてのセキュリティ属性に割り当てられた領域を指すようにサーバーハンドルを設定します。

#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);
 }

遠隔手続き

遠隔手続きは、コマンド行引数からユーザー ID を受け取り、その入力を 2 倍にした結果をクライアントに送信し、応答を出力して終了します。

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;
 	}
 }

簡単なアプリケーションの実行例

クライアントプロセスは、サーバーホスト名とユーザー ID を入力パラメータとして受け取り、指定されたユーザー ID を送信中であることを出力します。

%owl phoenix

%phoenix owl 2570

Sending UID 2570

サーバーは、ユーザー ID を取得し、それを次のように出力します。

Client's UID is 2570

クライアントプロセスは、サーバー応答を出力して終了します。

Response received: 6

%phoenix