Solaris Trusted Extensions が構成されているシステム は、必須アクセス制御 (MAC) および任意アクセス制御 (DAC) を実行します。アクセス制御は、同じホストでのプロセス間通信、およびネットワークをまたがるプロセス間通信で実行されます。この章では、Trusted Extensions が設定されているシステムで使用できるプロセス間通信 (IPC) のメカニズムについて概説します。さらに、アクセス制御がどのように適用されるかについても説明します。
Trusted Extensions API を Solaris OS で使用する方法の例は、Solaris ソースコードを参照してください。OpenSolaris の Web サイト の左のナビゲーションバーにある「Source Browser」をクリックします。Source Browser を使用して Solaris のソースコードを検索します。
この章の内容は次のとおりです。
Solaris Trusted Extensions が構成されているシステム は、単一レベルポートおよびマルチレベルポートに対応します。これらのポートは、アプリケーション間を接続するために使用されます。マルチレベルポートは、ポートに定義されている機密ラベルの範囲内のデータを受信できます。単一レベルポートは、指定された機密ラベルのデータのみを受信できます。
単一レベルポート – 通信チャネルは、2 つの非特権アプリケーション間に確立されます。通信終端の機密ラベルは等位でなければなりません。
マルチレベルポート – 通信チャネルは、net_bindmlp 特権が実効セットであるアプリケーションと、異なる機密ラベルで実行される不定数の非特権アプリケーションとの間に確立されます。プロセスの実効セットに net_bindmlp 特権を持つアプリケーションは、受信側アプリケーションの機密ラベルに関係なく、アプリケーションからのすべてのデータを受信できます。
マルチレベルポートは、異なるラベルで実行されている 2 つの Trusted Extensions アプリケーション間で接続を確立するためのサーバー側のメカニズムです。Trusted Extensions クライアントアプリケーションで、ラベルの異なる信頼できないオペレーティングシステムで実行されるサービスと通信する場合、SO_MAC_EXEMPT ソケットオプションを使用することもできます。詳細は、「MAC 適用外ソケット」を参照してください。
接続がマルチレベルである場合、アプリケーションの接続が 1 つの機密ラベルで行われ、データの送信または受信が別の機密ラベルで行われるのではないようにしてください。このような構成では、承認されていない宛先にデータが到達することがあります。
トラステッドネットワークライブラリは、パケットからラベルを取得するインタフェースを提供します。ネットワークパケットのプログラム化された操作は必要ありません。ただし、メッセージを送信する前に、メッセージのセキュリティー属性を変更することはできません。また、メッセージが送信される通信終端でもセキュリティー属性を変更できません。パケットのラベルの読み取りは、パケットのその他のセキュリティー情報と同様に行えます。ucred_getlabel() 関数を使用してラベル情報を取得します。
アプリケーションがマルチレベルポートの使用を必要とする場合、そのポートはプログラムでは作成できません。アプリケーション用のマルチレベルポートの作成は、システム管理者に依頼する必要があります。
マルチレベルポートについての詳細は、次を参照してください。
Trusted Extensions ソフトウェアは、次のソケットベースのメカニズムを使用することによって、通信終端間の IPC に対応します。
バークレーソケット
トランスポートレイヤーインタフェース (TLI)
遠隔手続き呼び出し (RPC)
この節では、ソケット通信メカニズムとそれに関連するセキュリティーポリシーについて概説します。セキュリティーポリシーと適用可能な特権については、該当するマニュアルページを参照してください。
これらのメカニズムのほかに、Trusted Extensions はマルチレベルポートにも対応します。「マルチレベルポートについて」を参照してください。
Trusted Extensions ソフトウェアは、単一ポートおよびマルチレベルポートでバークレーソケットおよび TLI を使用することによって、ネットワーク通信に対応します。システムコールの AF_UNIX ファミリは、完全解決のパス名で指定される特別なファイルを使用して、同じラベル付けされたゾーンでプロセス間接続を確立します。システムコールの AF_INET ファミリは、IP アドレスとポート番号を使用して、ネットワークにまたがるプロセス間接続を確立します。
インタフェースの AF_UNIX ファミリでは、UNIX® ドメインソケットである特別な 1 つのファイルに対してサーバーバインドを 1 つだけ確立できます。AF_UNIX ファミリはマルチレベルポートに対応しません。
UNIX ドメインソケット同様に、ドアおよび名前付きパイプは、認識し合う目的で特別なファイルを使用します。
すべての Trusted Extensions IPC メカニズムのデフォルトポリシーは、すべてのメカニズムが 1 つのラベル付けされたゾーン内で機能するように制約されることです。このポリシーの例外は次のとおりです。
大域ゾーン管理者は、ラベルが所有ゾーンより優位であるゾーンに対して名前付きパイプ (FIFO) を使用可能にできます。管理者は、FIFO を含むディレクトリをループバックマウントすることによってこれを行います。
より高いレベルのゾーンで実行されるプロセスは、読み取り専用モードで FIFO を開くことができます。プロセスは下位書き込みのために FIFO を使用できません。
大域ゾーンランデブファイルがラベル付けされたゾーンにループバックマウントされる場合、ラベル付けされたゾーンは大域ゾーンドアサーバーにアクセスできます。
Trusted Extensions ソフトウェアは、labeld および nscd のドアベースサービスをサポートするドアポリシーに従います。デフォルトの zonecfg テンプレートは、大域ゾーンの /var/tsol/doors ディレクトリが、ラベル付けされたゾーンのそれぞれにループバックマウントされることを指定します。
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); } }
Trusted Extensions ソフトウェアは、遠隔手続き呼び出し (RPC) に対するマルチレベルポートに対応します。クライアントアプリケーションは、特定サービスが使用可能かどうかの問い合わせをサーバーの PORTMAPPER サービス (ポート 111) に対して送信できます。要求されたサービスがサーバーの PORTMAPPER に登録されている場合、サーバーは匿名ポートを動的に割り当て、そのポートをクライアントに返します。
Solaris Trusted Extensions システムでは、管理者が PORTMAPPER ポートをマルチレベルポートとして構成できるので、複数の単一レベルアプリケーションでこのサービスを使用できます。PORTMAPPER ポートをマルチレベルポートにすると、PORTMAPPER サービスによって割り当てられるすべての匿名ポートもマルチレベルポートになります。これ以外の、匿名のマルチレベルポートを制御するための、プログラム可能なインタフェースも管理インタフェースもありません。
前の節で説明した PORTMAPPER サービスは、UDP を使用して実装されます。TCP と異なり、UDP ソケットは接続指向ではないので、マルチレベルポートでクライアントに応答する場合にどの資格を使用するかに関してあいまいになることがあります。したがって、クライアントの要求ソケットは、サーバーの応答ソケットに明示的に関連付けられなければなりません。これを関連付けるには、SO_RECVUCRED ソケットオプションを使用します。
SO_RECVUCRED が UDP ソケットで設定されると、カーネル UDP モジュールは ucred 構造体のラベルを補助データとしてアプリケーションに渡します。ucred の level と type の値がそれぞれ、SOL_SOCKET と SCM_UCRED です。
アプリケーションは次のいずれかの方法でこの ucred 構造体を処理します。
この 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); } }