UNIX ドメインソケットを使っている場合、INET ドメインや INET6 ドメインなどの場合と違って、通信相手(正確には通信を開始した相手)の PID, UID, GID を取得する方法があります。通信相手が名乗るのではなく、カーネルから情報を取ることになるので、取得した情報は信頼できます。

ちょっと必要になったので、SOCK_STREAM なソケットの場合を調べてみました。SOCK_DGRAM の場合でも取得できますが、その場合だと同一のソケットであってもパケットごとに違う相手である可能性があるため、取得がめんどくさくなります。

ソケットに関する情報なので、当然 getsockopt() で取得するのですが、このあたりは POSIX などでは標準化されておらず、OS によってまちまちです。そのまちまちなのを調べたのが以下の表です。

OS getsockopt() の引数 <sys/socket.h> と <sys/un.h> 以外に必要なヘッダファイル 先に定義する必要のあるマクロ
第2引数 (level) 第3引数 (optname) 第4引数の型
Linux SOL_SOCKET SO_PEERCRED struct ucred 不要 _GNU_SOURCE
FreeBSD 0 LOCAL_PEERCRED struct xucred
PID は取れない
GID はプライマリ以外のものも取れる
<sys/ucred.h> なし
OpenBSD SOL_SOCKET SO_PEERCRED struct sockpeercred 不要 _BSD_SOURCE
NetBSD 5 以降 0 LOCAL_PEEREID struct unpcbid 不要 _NETBSD_SOURCE
Mac OS X 0 LOCAL_PEERCRED struct xucred
PID は取れない
GID はプライマリ以外のものも取れる
<sys/ucred.h> なし

ちなみに、「通信相手 の PID 等」が確定するタイミングは、厳密に言うと実装によって微妙に異なります。socketpair() で作ったソケットの場合は socketpair() を呼んだタイミングのものが取れ、accept() で返ってきたソケットの場合は通信相手(クライアント)のソケットに関して connect() を呼んだタイミングのものが取れます(ここまではどの実装も同じ)。一方、connect() が完了したソケットの場合は、通信相手(サーバ)のソケットの元となったソケットに関して bind() を呼んだタイミングであったり、listen() を呼んだタイミングであったりします。もっとも、bind() 時と listen() 時とで PID や EUID が違うということは普通ないので、気にするようなことではないでしょう。privilege separation していると別ですが。ていうか、クライアントがサーバの UID を取得する必要がある場面もなかなか無いと思います。


ところで、途中まで調べて、MySQLのコマンドラインクライアントにはコマンドを実行したユーザを元にユーザ名やパスワード等を聞かずに認証する仕組みがあったのを思い出したので、なーんだ MySQL のソース見たら(自分で調べなくても)解決じゃーんと思ったんですが、ソースをダウンロードしてよく見たら Linux の実装しか入ってませんでした。ぐっすし。んで自分で調べた結果が上の表です。

Trackback

no comment untill now

Add your comment now