[インデックス 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の型エイリアスまたは定義です。これは、getsockopt
やsetsockopt
のようなシステムコールに渡すバッファのサイズを表現するために使用されます。
技術的詳細
このコミットは、Linuxシステムコールgetsockopt
がSO_PEERCRED
オプションと共に使用された際に、Ucred
構造体を正しく取得するためのGo言語のラッパー関数GetsockoptUcred
をsrc/pkg/syscall/syscall_linux.go
に追加します。
Goのsyscall
パッケージには、すでに汎用的なgetsockopt
関数(Goの内部関数で、Cのgetsockopt
システムコールをラップしたもの)が存在します。しかし、この汎用関数はoptval
引数としてuintptr
を受け取り、そのサイズを_Socklen
型のポインタで渡す必要があります。
SO_PEERCRED
の場合、optval
にはUcred
構造体へのポインタを渡し、optlen
にはUcred
構造体のサイズを渡す必要があります。GoのGetsockoptUcred
関数は、以下の手順でこれを実現します。
Ucred
型の変数を宣言し、この変数に取得したピア認証情報を格納します。Ucred
構造体のサイズを_Socklen
型にキャストしたvallen
変数を初期化します。SizeofUcred
はUcred
構造体のバイトサイズを表す定数です。- 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
は、実際に読み込まれたデータのサイズをこのアドレスに書き込みます。
- Goの内部
return &value, err
: 取得したUcred
構造体へのポインタと、システムコールが返したエラーを返します。
この関数は、LinuxのSO_PEERCRED
オプションが返すUcred
構造体の特殊な性質(固定サイズで複数のフィールドを持つ)に対応するために、Goの型システムとCのシステムコールインターフェースの間の橋渡しをしています。
関連リンク
- Go CL 6445104: https://golang.org/cl/6445104
- Go Issue #3836: https://github.com/golang/go/issues/3836
参考にした情報源リンク
getsockopt(2)
man page: https://man7.org/linux/man-pages/man2/getsockopt.2.htmlunix(7)
man page (forSO_PEERCRED
andstruct ucred
): https://man7.org/linux/man-pages/man7/unix.7.html- Go
unsafe
package documentation: https://pkg.go.dev/unsafe - Go
syscall
package documentation: https://pkg.go.dev/syscall - Unix Domain Sockets (Wikipedia): https://en.wikipedia.org/wiki/Unix_domain_socket
- Linux
ucred
struct definition (example from kernel source): https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/socket.h#L209