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

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

このコミットは、Go言語のsyscallパッケージにおいて、OpenBSD、FreeBSD、およびDragonflyBSD向けにsetresuid(2)およびsetresgid(2)システムコールの実装を追加するものです。これにより、これらのBSD系OS上でGoプログラムがプロセスの実効ユーザーID(EUID)、実ユーザーID(RUID)、および保存設定ユーザーID(SUID)をより柔軟かつ安全に管理できるようになります。同様に、グループID(GID)についても同様の機能が提供されます。

コミット

commit 63e3763af88e758815187bd58c342b7ccfcd3aa2
Author: William Orr <will@worrbase.com>
Date:   Tue Jun 24 13:30:30 2014 -0700

    syscall: implement setresuid(2) and setresgid(2) on OpenBSD/FreeBSD/DragonflyBSD
    
    Fixes #8218.
    
    LGTM=iant
    R=golang-codereviews, iant, minux
    CC=golang-codereviews
    https://golang.org/cl/107150043

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

https://github.com/golang/go/commit/63e3763af88e758815187bd58c342b7ccfcd3aa2

元コミット内容

このコミットは、Goのsyscallパッケージにsetresuid(2)setresgid(2)システムコールをOpenBSD、FreeBSD、およびDragonflyBSD向けに実装したものです。これにより、GoプログラムがこれらのOS上でユーザーIDとグループIDをより細かく制御できるようになります。コミットメッセージには「Fixes #8218」と記載されていますが、現在のGoのGitHubリポジトリではこの番号のIssueは見つかりませんでした。これは、当時の内部トラッカーの参照であるか、あるいはIssue番号が変更された可能性があります。

変更の背景

Unix系システムにおいて、プロセスの権限管理はセキュリティ上非常に重要です。従来のsetuid()setgid()といったシステムコールは、プロセスのユーザーIDやグループIDを変更するために使用されてきましたが、これらは実効IDと実IDを同時に変更するなど、柔軟性に欠ける点がありました。特に、特権を一時的に放棄し、必要に応じて再度取得するといった高度な権限管理を行う際には不十分でした。

setresuid(2)setresgid(2)は、実ユーザーID(RUID)、実効ユーザーID(EUID)、および保存設定ユーザーID(SUID)を個別に設定できるため、よりきめ細やかな権限管理を可能にします。これにより、例えばroot権限で起動したプロセスが、特定の処理を行う際に一時的に非特権ユーザーの権限に切り替え、処理完了後に再度root権限に戻るといった安全な運用が可能になります。

このコミットは、Go言語がこれらのBSD系OS上でより堅牢なセキュリティ機能を提供し、特権を扱うアプリケーションの開発を容易にすることを目的としています。GoのsyscallパッケージはOSの低レベルな機能へのインターフェースを提供するため、これらのシステムコールをGoから直接呼び出せるようにすることは、Goプログラムの機能性とセキュリティを向上させる上で不可欠でした。

前提知識の解説

Unix系システムのユーザーIDとグループID

Unix系OSでは、各プロセスは以下の3種類のユーザーID(UID)とグループID(GID)を持ちます。

  1. 実ユーザーID (Real User ID, RUID) / 実グループID (Real Group ID, RGID): プロセスを起動したユーザーまたはグループのIDです。通常、プロセスのライフサイクルを通じて変更されません。これはプロセスの「所有者」を示します。

  2. 実効ユーザーID (Effective User ID, EUID) / 実効グループID (Effective Group ID, EGID): プロセスがファイルやその他のシステムリソースにアクセスする際の権限を決定するIDです。特権プログラム(例: sudoコマンド)は、この実効IDを一時的に変更することで、通常はアクセスできないリソースにアクセスできるようになります。

  3. 保存設定ユーザーID (Saved Set-User-ID, SUID) / 保存設定グループID (Saved Set-Group-ID, SGID): execve()システムコールによって新しいプログラムが実行される際に、その時点の実効IDのコピーが保存されます。これにより、プロセスは一時的に実効IDを非特権ユーザーに変更した後でも、保存された特権IDを使用して元の特権を回復することが可能になります。これは、特権を必要としない処理中は特権を放棄し、必要な時だけ特権を再取得するというセキュリティプラクティスを可能にします。

setresuid(2)setresgid(2)システムコール

これらのシステムコールは、プロセスのRUID、EUID、SUID(またはRGID、EGID、SGID)を個別に設定するために使用されます。

  • setresuid(uid_t ruid, uid_t euid, uid_t suid):

    • ruid: 設定したい実ユーザーID
    • euid: 設定したい実効ユーザーID
    • suid: 設定したい保存設定ユーザーID
    • 引数に-1を指定すると、対応するIDは変更されません。
    • 特権プロセス(rootなど)は任意のIDを設定できます。
    • 非特権プロセスは、現在のRUID、EUID、SUIDのいずれかにのみIDを変更できます。
  • setresgid(gid_t rgid, gid_t egid, gid_t sgid):

    • rgid: 設定したい実グループID
    • egid: 設定したい実効グループID
    • sgid: 設定したい保存設定グループID
    • setresuidと同様の挙動を示します。

これらのシステムコールは、特にセキュリティが重視されるデーモンやネットワークサービスにおいて、最小権限の原則を実装するために不可欠です。

Go言語のsyscallパッケージ

Go言語の標準ライブラリに含まれるsyscallパッケージは、オペレーティングシステムの低レベルなプリミティブ(システムコール)へのインターフェースを提供します。これにより、GoプログラムはOS固有の機能に直接アクセスし、ファイルシステム操作、プロセス管理、ネットワーク通信など、OSレベルの操作を実行できます。syscallパッケージはOSごとに異なる実装を持ち、各OSのシステムコールをGoの関数としてラップしています。

zsyscall_*.goファイル

Goのsyscallパッケージには、zsyscall_*.goという命名規則のファイルが多数存在します。これらのファイルは通常、go tool cgo -godefsのようなツールによって自動生成されます。このツールは、C言語のヘッダーファイルからGoの構造体や関数定義を生成し、GoプログラムがCのシステムコールを呼び出すためのバインディングを提供します。したがって、これらのファイルへの変更は、新しいシステムコールがGoから利用可能になったことを意味します。

技術的詳細

このコミットの技術的詳細は、主に以下の2点に集約されます。

  1. システムコール定義の追加: src/pkg/syscall/syscall_dragonfly.gosrc/pkg/syscall/syscall_freebsd.gosrc/pkg/syscall/syscall_openbsd.goといった各OS固有のファイルに、setresuidsetresgidのGo言語でのシステムコール定義が追加されています。これらは//sysnbディレクティブ(//sysの非ブロックバージョン)を用いて、Goの関数シグネチャと対応するシステムコールを紐付けています。

    例:

    //sysnb	Setresgid(rgid int, egid int, sgid int) (err error)
    //sysnb	Setresuid(ruid int, euid int, suid int) (err error)
    

    この行は、GoのSetresgid関数が、対応するOSのsetresgidシステムコールを呼び出すことをGoツールチェインに指示します。

  2. 自動生成されるシステムコールラッパーの実装: src/pkg/syscall/zsyscall_dragonfly_386.gosrc/pkg/syscall/zsyscall_freebsd_amd64.goなどのzsyscall_*.goファイルには、実際にシステムコールを呼び出すためのGo関数が追加されています。これらの関数は、RawSyscall関数を使用して低レベルなシステムコールを実行します。

    例:

    func Setresgid(rgid int, egid int, sgid int) (err error) {
    	_, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
    	if e1 != 0 {
    		err = e1
    	}
    	return
    }
    

    ここで、SYS_SETRESGIDは対応するOSのsetresgidシステムコールの番号を表す定数です。RawSyscallは、引数をuintptrに変換してOSのカーネルに渡し、システムコールを実行します。返されたエラーコードe1は、Goのエラー型に変換されて返されます。

OpenBSDのsrc/pkg/syscall/syscall_openbsd.goでは、以前のコメントアウトされていたsetresgidsetresuidの記述が削除されています。これは、これらのシステムコールが正式に実装されたため、もはや「未実装」としてリストする必要がなくなったことを示しています。

この変更により、GoプログラムはこれらのBSD系OS上で、より高度な権限管理を必要とするアプリケーション(例: セキュアなサーバー、サンドボックス環境)を開発する際に、ネイティブなシステムコールを活用できるようになります。

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

このコミットで変更された主要なファイルとコードスニペットは以下の通りです。

1. src/pkg/syscall/syscall_dragonfly.go, src/pkg/syscall/syscall_freebsd.go, src/pkg/syscall/syscall_openbsd.go: これらのファイルに、setresgidsetresuidのシステムコール定義が追加されました。

例 (src/pkg/syscall/syscall_dragonfly.go):

--- a/src/pkg/syscall/syscall_dragonfly.go
+++ b/src/pkg/syscall/syscall_dragonfly.go
@@ -183,6 +183,8 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
 //sys	Setpriority(which int, who int, prio int) (err error)
 //sysnb	Setregid(rgid int, egid int) (err error)
 //sysnb	Setreuid(ruid int, euid int) (err error)
+//sysnb	Setresgid(rgid int, egid int, sgid int) (err error)
+//sysnb	Setresuid(ruid int, euid int, suid int) (err error)
 //sysnb	Setrlimit(which int, lim *Rlimit) (err error)
 //sysnb	Setsid() (pid int, err error)
 //sysnb	Settimeofday(tp *Timeval) (err error)

2. src/pkg/syscall/syscall_openbsd.go: 既存のコメントからsetresgidsetresuidの記述が削除されました。

--- a/src/pkg/syscall/syscall_openbsd.go
+++ b/src/pkg/syscall/syscall_openbsd.go
@@ -272,8 +274,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
 // semop
 // setgroups
 // setitimer
-// setresgid
-// setresuid
 // setrtable
 // setsockopt
 // shmat

3. src/pkg/syscall/zsyscall_dragonfly_386.go (および他のzsyscall_*.goファイル): これらの自動生成ファイルに、SetresgidSetresuidのGo関数実装が追加されました。

例 (src/pkg/syscall/zsyscall_dragonfly_386.go):

--- a/src/pkg/syscall/zsyscall_dragonfly_386.go
+++ b/src/pkg/syscall/zsyscall_dragonfly_386.go
@@ -1059,6 +1059,26 @@ func Setreuid(ruid int, euid int) (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func Setresgid(rgid int, egid int, sgid int) (err error) {
+\t_, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
+\tif e1 != 0 {
+\t\terr = e1
+\t}\n+\treturn
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setresuid(ruid int, euid int, suid int) (err error) {
+\t_, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
+\tif e1 != 0 {
+\t\terr = e1
+\t}\n+\treturn
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func Setrlimit(which int, lim *Rlimit) (err error) {
 \t_, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
 \tif e1 != 0 {

コアとなるコードの解説

このコミットの核となる変更は、Goのsyscallパッケージが特定のBSD系OS(OpenBSD、FreeBSD、DragonflyBSD)上でsetresuid(2)setresgid(2)システムコールを呼び出せるようにするためのものです。

  1. //sysnbディレクティブの追加: syscall_*.goファイルに追加された//sysnbコメントは、Goのツールチェイン(特にgo tool cgo -godefs)に対する指示です。これは、GoのSetresgidおよびSetresuid関数が、それぞれOSのsetresgidおよびsetresuidシステムコールに対応することを宣言しています。nbは「non-blocking」を意味し、このシステムコールがブロックしない性質を持つことを示唆しています。このディレクティブがあることで、ツールチェインは対応するGoの関数シグネチャから、適切なシステムコール呼び出しコードを自動生成できるようになります。

  2. zsyscall_*.goファイルでの実装: zsyscall_*.goファイルは、前述の//sysnbディレクティブに基づいて自動生成される部分です。ここに、実際にOSのシステムコールを呼び出すためのGo関数が追加されています。

    • RawSyscall関数は、Goから直接OSのシステムコールを呼び出すための低レベルなインターフェースです。第一引数にはシステムコール番号(例: SYS_SETRESGID)、続く引数にはシステムコールに渡すパラメータがuintptr型で渡されます。
    • システムコールが成功した場合、RawSyscallはエラーコード0を返します。エラーが発生した場合は、非ゼロの値が返され、それがGoのエラー型に変換されて呼び出し元に返されます。
    • これらの関数は、Goプログラムがsyscall.Setresuid(ruid, euid, suid)syscall.Setresgid(rgid, egid, sgid)といった形で、OSの権限管理機能を直接利用できるようにします。

この変更により、Goで書かれたアプリケーションは、これらのBSD系OSのセキュリティモデルに深く統合され、より高度な権限分離や特権の管理をプログラム的に行えるようになります。これは、特にセキュリティが要求されるシステムサービスやデーモンをGoで開発する際に、非常に重要な機能となります。

関連リンク

参考にした情報源リンク