Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13806] ファイルの概要

このコミットは、Go言語のsyscallパッケージにLinux固有のGetsockoptUcred関数を追加するものです。これは、UnixドメインソケットにおいてSO_PEERCREDオプションを使用して接続元プロセスの認証情報(UID, GID, PID)を取得する際に必要となるUcred構造体を適切に処理するために導入されました。これにより、GoプログラムがLinux上でUnixドメインソケットのピア認証情報を正確に取得できるようになります。

コミット

  • コミットハッシュ: 452d6b46a390290a350593b3756f6511b8441e74
  • 作者: Andreas Jellinghaus andreas@ionisiert.de
  • コミット日時: 2012年9月12日 06:38:21 -0700

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/452d6b46a390290a350593b3756f6511b8441e74

元コミット内容

syscall: add linux specific Getsockopt for Ucred struct

SO_PEERCRED on unix domain socket will fill a Ucred struct,
thus linux needs a custom Getsockopt variant.
Fixes #3836.

R=golang-dev, rsc, iant
CC=golang-dev
https://golang.org/cl/6445104

変更の背景

この変更の背景には、Go言語がLinuxシステム上でUnixドメインソケットを扱う際の特定の要件があります。Unixドメインソケットは、同じホスト上のプロセス間通信(IPC)に利用されるソケットの一種です。セキュリティや認証の目的で、サーバープロセスが接続してきたクライアントプロセスの認証情報(ユーザーID、グループID、プロセスIDなど)を取得したい場合があります。

Linuxでは、この目的のためにSO_PEERCREDソケットオプションが提供されています。このオプションをgetsockoptシステムコールと共に使用すると、接続元のプロセスの認証情報がstruct ucredという構造体に格納されて返されます。しかし、Goの既存のsyscall.Getsockopt関数は、このstruct ucredのサイズや構造に特化しておらず、汎用的な処理では正しく情報を取得できませんでした。

具体的には、Goのsyscallパッケージは、様々なソケットオプションに対応するために汎用的なGetsockopt関数を提供していますが、SO_PEERCREDが返すUcred構造体は、他の多くのソケットオプションが返す単純な整数値やブール値とは異なり、複数のフィールドを持つ複雑な構造体です。このため、Linux固有のUcred構造体のサイズとアラインメントを考慮した専用のGetsockoptラッパー関数が必要とされました。

この問題はGoのIssue #3836として報告されており、このコミットはその問題を解決するために行われました。

前提知識の解説

Unixドメインソケット (Unix Domain Sockets, UDS)

Unixドメインソケットは、同じオペレーティングシステムインスタンス上で動作するプロセス間での通信(IPC)メカニズムです。TCP/IPソケットがネットワークを介した通信に使用されるのに対し、Unixドメインソケットはファイルシステム上のパス(例: /tmp/my_socket)をアドレスとして使用し、カーネルを介して効率的な通信を提供します。ネットワークオーバーヘッドがないため、ローカルプロセス間通信においてTCP/IPソケットよりも高速で低レイテンシな場合があります。

getsockopt システムコール

getsockoptは、ソケットのオプションを取得するためのシステムコールです。 そのプロトタイプは通常以下のようになります(C言語の場合):

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
  • sockfd: オプションを取得するソケットのファイルディスクリプタ。
  • level: オプションが定義されているプロトコルレベル(例: SOL_SOCKETはソケットレベルのオプション、IPPROTO_TCPはTCPレベルのオプション)。
  • optname: 取得するオプションの名前(例: SO_REUSEADDR, SO_KEEPALIVE, SO_PEERCRED)。
  • optval: 取得したオプションの値が格納されるバッファへのポインタ。
  • optlen: optvalバッファのサイズを指すポインタ。getsockopt呼び出し後には、実際に格納されたデータのサイズが設定されます。

SO_PEERCRED ソケットオプション

SO_PEERCREDはLinux固有のソケットオプションで、Unixドメインソケットに接続しているピア(相手側)プロセスの認証情報(credentials)を取得するために使用されます。このオプションをgetsockoptと共に使用すると、ピアのユーザーID (UID)、グループID (GID)、およびプロセスID (PID) がstruct ucred構造体に格納されて返されます。これは、サーバーが接続してきたクライアントの身元を確認する際に非常に有用です。

struct ucred (Linux)

struct ucredは、Linuxカーネルがプロセス認証情報を表現するために使用する構造体です。通常、以下のようなフィールドを持ちます(定義はカーネルバージョンやアーキテクチャによって異なる場合がありますが、概念は同じです):

struct ucred {
    pid_t pid;    /* PID of the peer process */
    uid_t uid;    /* Real UID of the peer process */
    gid_t gid;    /* Real GID of the peer process */
};

この構造体は、SO_PEERCREDオプションを通じてユーザー空間に公開され、アプリケーションがピアの認証情報を取得できるようにします。

Go言語の syscall パッケージ

Go言語のsyscallパッケージは、オペレーティングシステムの低レベルなプリミティブ(システムコール)へのアクセスを提供します。これにより、ファイル操作、プロセス管理、ネットワーク通信など、OSレベルの機能に直接アクセスできます。このパッケージはOSに依存する部分が多く、各OS(Linux, Windows, macOSなど)ごとに異なる実装が提供されます。syscall_linux.goのようなファイルは、Linux固有のシステムコールラッパーや定数を定義しています。

unsafe.Pointer (Go言語)

Go言語のunsafeパッケージは、Goの型安全性をバイパスする低レベルな操作を可能にします。unsafe.Pointerは、任意の型のポインタを保持できる特別なポインタ型です。これはC言語のvoid*に似ており、異なる型のポインタ間で変換を行うことができます。しかし、その名の通り「安全ではない」操作であり、誤用するとメモリ破壊や未定義の動作を引き起こす可能性があるため、非常に慎重に使用する必要があります。システムコールのように、OSのAPIが特定のメモリレイアウトを期待する場合に、Goの構造体をそのレイアウトに合わせてポインタを渡すために使用されることがあります。

_Socklen (Go言語)

Goのsyscallパッケージ内で定義されている_Socklenは、socklen_t型(C言語のソケットAPIで使用されるサイズ指定型)に対応するGoの型エイリアスまたは定義です。これは、getsockoptsetsockoptのようなシステムコールに渡すバッファのサイズを表現するために使用されます。

技術的詳細

このコミットは、LinuxシステムコールgetsockoptSO_PEERCREDオプションと共に使用された際に、Ucred構造体を正しく取得するためのGo言語のラッパー関数GetsockoptUcredsrc/pkg/syscall/syscall_linux.goに追加します。

Goのsyscallパッケージには、すでに汎用的なgetsockopt関数(Goの内部関数で、Cのgetsockoptシステムコールをラップしたもの)が存在します。しかし、この汎用関数はoptval引数としてuintptrを受け取り、そのサイズを_Socklen型のポインタで渡す必要があります。

SO_PEERCREDの場合、optvalにはUcred構造体へのポインタを渡し、optlenにはUcred構造体のサイズを渡す必要があります。GoのGetsockoptUcred関数は、以下の手順でこれを実現します。

  1. Ucred型の変数を宣言し、この変数に取得したピア認証情報を格納します。
  2. Ucred構造体のサイズを_Socklen型にキャストしたvallen変数を初期化します。SizeofUcredUcred構造体のバイトサイズを表す定数です。
  3. Goの内部getsockopt関数を呼び出します。
    • fd, level, optは引数として渡された値をそのまま使用します。
    • optvalには、unsafe.Pointer(&value)を使用してUcred構造体valueのアドレスをuintptrに変換して渡します。これにより、Goの型システムを一時的にバイパスし、getsockoptが期待するvoid*のようなポインタを渡すことができます。
    • optlenには、&vallen_Socklen型のポインタ)を渡します。getsockoptは、このポインタが指すメモリ位置に実際に読み込まれたデータのサイズを書き込みます。

この専用関数を導入することで、GoプログラムはLinuxのSO_PEERCRED機能を安全かつ正確に利用できるようになり、Unixドメインソケットを介したプロセス間通信における認証情報の取得が可能になります。

コアとなるコードの変更箇所

src/pkg/syscall/syscall_linux.go ファイルに以下の7行が追加されました。

--- a/src/pkg/syscall/syscall_linux.go
+++ b/src/pkg/syscall/syscall_linux.go
@@ -477,6 +477,13 @@ func GetsockoptIPv6Mreq(fd, level, opt int) (*IPv6Mreq, error) {
 	return &value, err
 }
 
+func GetsockoptUcred(fd, level, opt int) (*Ucred, error) {
+	var value Ucred
+	vallen := _Socklen(SizeofUcred)
+	err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
+	return &value, err
+}
+
 func SetsockoptInt(fd, level, opt int, value int) (err error) {
 	var n = int32(value)
 	return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), 4)

コアとなるコードの解説

追加されたGetsockoptUcred関数は以下の通りです。

func GetsockoptUcred(fd, level, opt int) (*Ucred, error) {
	var value Ucred
	vallen := _Socklen(SizeofUcred)
	err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
	return &value, err
}
  • func GetsockoptUcred(fd, level, opt int) (*Ucred, error):
    • fd: ソケットのファイルディスクリプタ。
    • level: ソケットオプションのレベル(例: SOL_SOCKET)。
    • opt: ソケットオプションの名前(この場合はSO_PEERCRED)。
    • 戻り値: *Ucred(取得したUcred構造体へのポインタ)とerror
  • var value Ucred: Ucred型の変数を宣言します。getsockoptシステムコールが取得したピア認証情報はこのvalueに格納されます。
  • vallen := _Socklen(SizeofUcred): Ucred構造体のサイズ(SizeofUcred定数で定義されている)を_Socklen型にキャストしてvallenに代入します。これはgetsockoptに渡すバッファの初期サイズを示します。
  • err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen):
    • Goの内部getsockopt関数を呼び出します。
    • uintptr(unsafe.Pointer(&value)): value変数のメモリアドレスをunsafe.Pointerに変換し、さらにuintptrに変換してgetsockoptに渡します。これにより、getsockoptは指定されたメモリアドレスにUcred構造体のデータを直接書き込むことができます。
    • &vallen: vallen変数のアドレスを渡します。getsockoptは、実際に読み込まれたデータのサイズをこのアドレスに書き込みます。
  • return &value, err: 取得したUcred構造体へのポインタと、システムコールが返したエラーを返します。

この関数は、LinuxのSO_PEERCREDオプションが返すUcred構造体の特殊な性質(固定サイズで複数のフィールドを持つ)に対応するために、Goの型システムとCのシステムコールインターフェースの間の橋渡しをしています。

関連リンク

参考にした情報源リンク