Solaris Trusted Extensions 開発ガイド

第 5 章 プロセス間通信

Solaris Trusted Extensions が構成されているシステム は、必須アクセス制御 (MAC) および任意アクセス制御 (DAC) を実行します。アクセス制御は、同じホストでのプロセス間通信、およびネットワークをまたがるプロセス間通信で実行されます。この章では、Trusted Extensions が設定されているシステムで使用できるプロセス間通信 (IPC) のメカニズムについて概説します。さらに、アクセス制御がどのように適用されるかについても説明します。

Trusted Extensions API を Solaris OS で使用する方法の例は、Solaris ソースコードを参照してください。OpenSolaris の Web サイト の左のナビゲーションバーにある「Source Browser」をクリックします。Source Browser を使用して Solaris のソースコードを検索します。

この章の内容は次のとおりです。

マルチレベルポートについて

Solaris Trusted Extensions が構成されているシステム は、単一レベルポートおよびマルチレベルポートに対応します。これらのポートは、アプリケーション間を接続するために使用されます。マルチレベルポートは、ポートに定義されている機密ラベルの範囲内のデータを受信できます。単一レベルポートは、指定された機密ラベルのデータのみを受信できます。


注意 – 注意 –

接続がマルチレベルである場合、アプリケーションの接続が 1 つの機密ラベルで行われ、データの送信または受信が別の機密ラベルで行われるのではないようにしてください。このような構成では、承認されていない宛先にデータが到達することがあります。


トラステッドネットワークライブラリは、パケットからラベルを取得するインタフェースを提供します。ネットワークパケットのプログラム化された操作は必要ありません。ただし、メッセージを送信する前に、メッセージのセキュリティー属性を変更することはできません。また、メッセージが送信される通信終端でもセキュリティー属性を変更できません。パケットのラベルの読み取りは、パケットのその他のセキュリティー情報と同様に行えます。ucred_getlabel() 関数を使用してラベル情報を取得します。

アプリケーションがマルチレベルポートの使用を必要とする場合、そのポートはプログラムでは作成できません。アプリケーション用のマルチレベルポートの作成は、システム管理者に依頼する必要があります。

マルチレベルポートについての詳細は、次を参照してください。

通信終端

Trusted Extensions ソフトウェアは、次のソケットベースのメカニズムを使用することによって、通信終端間の IPC に対応します。

この節では、ソケット通信メカニズムとそれに関連するセキュリティーポリシーについて概説します。セキュリティーポリシーと適用可能な特権については、該当するマニュアルページを参照してください。

これらのメカニズムのほかに、Trusted Extensions はマルチレベルポートにも対応します。「マルチレベルポートについて」を参照してください。

バークレーソケットと TLI

Trusted Extensions ソフトウェアは、単一ポートおよびマルチレベルポートでバークレーソケットおよび TLI を使用することによって、ネットワーク通信に対応します。システムコールの AF_UNIX ファミリは、完全解決のパス名で指定される特別なファイルを使用して、同じラベル付けされたゾーンでプロセス間接続を確立します。システムコールの AF_INET ファミリは、IP アドレスとポート番号を使用して、ネットワークにまたがるプロセス間接続を確立します。

AF_UNIX ファミリ

インタフェースの AF_UNIX ファミリでは、UNIX® ドメインソケットである特別な 1 つのファイルに対してサーバーバインドを 1 つだけ確立できます。AF_UNIX ファミリはマルチレベルポートに対応しません。

UNIX ドメインソケット同様に、ドアおよび名前付きパイプは、認識し合う目的で特別なファイルを使用します。

すべての Trusted Extensions IPC メカニズムのデフォルトポリシーは、すべてのメカニズムが 1 つのラベル付けされたゾーン内で機能するように制約されることです。このポリシーの例外は次のとおりです。

AF_INET ファミリ

AF_INET ファミリでは、プロセスは、特権または非特権のポート番号に対して単一ラベル接続またはマルチラベル接続を確立できます。特権ポート番号に接続するには、net_priv_addr 特権が必要です。マルチレベルポート接続にするには、net_bindmlp 特権も必要です。

サーバープロセスには、マルチレベルポート接続のために、その実効セットに net_bindmlp 特権が必要です。それに対して単一レベルポート接続を行う場合、サーバープロセスにはソケットに対する必須同等読み取りアクセスが必要であり、クライアントプロセスには必須同等書き込みアクセスが必要です。両方のプロセスには、ファイルに対する必須と任意のアクセスが必要です。ファイルへのアクセスが拒否される場合、アクセスが拒否されるプロセスには、アクセスを得るために、適切なファイル特権がその実効セットに必要です。

次のコーディング例は、接続されたクライアントのラベルをマルチレベルサーバーがどのように取得するかを示します。標準 C ライブラリ関数 getpeerucred() が、接続されたソケットまたは STREAM ピアの資格を取得します。Trusted Extensions において、マルチレベルポートサーバーの待機ソケットが接続要求を受け入れると、通常、最初の引数がクライアントソケットファイル記述子になります。Trusted Extensions アプリケーションは、標準のアプリケーションプログラムとまったく同じに getpeerucred() 関数を使用します。Trusted Extensions には ucred_getlabel() が追加され、ラベルを返します。詳細は、ucred_get(3C) のマニュアルページを参照してください。

/*
 * この例は、接続されたクライアントのラベルをマルチレベルサーバーが 
 * どのように取得するかを示す。
 */
void
remote_client_label(int svr_fd)
{
	ucred_t *uc = NULL;
	m_label_t *sl;
	struct sockaddr_in6 remote_addr;

	bzero((void *)&remote_addr, sizeof (struct sockaddr_in6));

	while (1) {
		int clnt_fd;
		clnt_fd = accept(svr_fd, (struct sockaddr *)&remote_addr,
				&sizeof (struct sockaddr_in6));

		/*
		 * ソケットからクライアント属性を取得する
		 */
		if (getpeerucred(clnt_fd, &uc) == -1) {
			return;
		}

		/*
		 * ucred 構造体から各フィールドを抽出する
		 */

		sl = ucred_getlabel(uc);

		/*
		 * セキュリティーラベルの使用法
		 * .....
		 */

		ucred_free(uc);
		close(clnt_fd);
	}
}

RPC メカニズム

Trusted Extensions ソフトウェアは、遠隔手続き呼び出し (RPC) に対するマルチレベルポートに対応します。クライアントアプリケーションは、特定サービスが使用可能かどうかの問い合わせをサーバーの PORTMAPPER サービス (ポート 111) に対して送信できます。要求されたサービスがサーバーの PORTMAPPER に登録されている場合、サーバーは匿名ポートを動的に割り当て、そのポートをクライアントに返します。

Solaris Trusted Extensions システムでは、管理者が PORTMAPPER ポートをマルチレベルポートとして構成できるので、複数の単一レベルアプリケーションでこのサービスを使用できます。PORTMAPPER ポートをマルチレベルポートにすると、PORTMAPPER サービスによって割り当てられるすべての匿名ポートもマルチレベルポートになります。これ以外の、匿名のマルチレベルポートを制御するための、プログラム可能なインタフェースも管理インタフェースもありません。

UDP とマルチレベルポートの使用

前の節で説明した PORTMAPPER サービスは、UDP を使用して実装されます。TCP と異なり、UDP ソケットは接続指向ではないので、マルチレベルポートでクライアントに応答する場合にどの資格を使用するかに関してあいまいになることがあります。したがって、クライアントの要求ソケットは、サーバーの応答ソケットに明示的に関連付けられなければなりません。これを関連付けるには、SO_RECVUCRED ソケットオプションを使用します。

SO_RECVUCRED が UDP ソケットで設定されると、カーネル UDP モジュールは ucred 構造体のラベルを補助データとしてアプリケーションに渡します。ucredleveltype の値がそれぞれ、SOL_SOCKETSCM_UCRED です。

アプリケーションは次のいずれかの方法でこの ucred 構造体を処理します。

次のコード抜粋は再利用の場合を示します。

/*
 * src で SCM_UCRED を検索し、dest のみのオプションに
 * ポインタを指定する。これら 2 つの 'netbuf' 構造体が同じ
 * である場合もある。そのため、コードで dest の変更後に
 * src を照会するときに注意が必要。
 */
static void
extract_cred(const struct netbuf *src, struct netbuf *dest)
{
    char *cp = src->buf;
    unsigned int len = src->len;
    const struct T_opthdr *opt;
    unsigned int olen;

    while (len >= sizeof (*opt)) {
        /* LINTED: pointer alignment */
        opt = (const struct T_opthdr *)cp;
        olen = opt->len;
        if (olen > len || olen < sizeof (*opt) ||
            !IS_P2ALIGNED(olen, sizeof (t_uscalar_t)))
            break;
        if (opt->level == SOL_SOCKET &&
            opt->name == SCM_UCRED) {
            dest->buf = cp;
            dest->len = olen;
            return;
        }
        cp += olen;
        len -= olen;
    }
    dest->len = 0;
}

次のコード抜粋は、受信バッファーからユーザー資格にアクセスする方法を示します。

void
examine_udp_label()
{
    struct msghdr   recv_msg;
    struct cmsghdr  *cmsgp;
    char message[MAX_MSGLEN+1];
    char inmsg[MAX_MSGLEN+1];
    int on = 1;

    setsockopt(sockfd, SOL_SOCKET, SO_RECVUCRED, (void *)&on,
        sizeof (int));

    [...]

    while (1) {
       if (recvmsg(sockfd, &recv_msg, 0) < 0) {
           (void) fprintf(stderr, "recvmsg_errno:   %d\n", errno);
           exit(1);
           }

           /*
            * 補助データで ucred を検査する
            */
           ucred = NULL;

           for (cmsgp = CMSG_FIRSTHDR(&recv_msg); cmsgp;
               cmsgp = CMSG_NXTHDR(&recv_msg, cmsgp)) {
               if (cmsgp->cmsg_level == SOL_SOCKET &&
                  cmsgp->cmsg_type == SCM_UCRED) {
                  ucred = (ucred_t *)CMSG_DATA(cmsgp);
                     break;
                     }

               if (ucred == NULL) {
                   (void) sprintf(&message[0],
                       "No ucred info in ancillary data with UDP");
               } else {
                   /*
                    * ここで、ucred_getlabel(3C) を使用して、ucred から
                    * ラベルを抽出することもできる。
                    */
               }

           }

           [...]

           if (message != NULL)
               (void) strlcpy(&inmsg[0], message, MAX_MSGLEN);
           /*
            * 正しいラベルが含まれるように、受信したメッセージを
            * 使用する
            */
           iov.iov_len = strlen(inmsg);
           ret = sendmsg(sockfd, &recv_msg, 0);
       }
}