[linux] abstract socket address

netstatUNIXドメインソケットを見ると、先頭に '@' が付いてるパスとそうでないものがある。

```
# netstat -ax
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ACC ] STREAM LISTENING 5826 @/com/ubuntu/upstart
unix 6 [ ] DGRAM 7364 /dev/log
```

の二つ

## 正体は 抽象ソケットアドレス

抽象ソケットアドレス と呼ぶらしい

```
Hi all, the attached path adds support for unix socket in abstract namespace.
They are special sockets without filesystem correspondence (so you can use
them without thinking about permissions or in chroot). In netstat they are
reported with a '@' prefix.
```

抽象ソケットアドレスは、netstatで参照すると'@'がprefixとしてついている

ファイルシステムに関係無くファイルパスで扱えるので、パーミッションの制限や chroot 環境からも使える

```
abstract (抽象): 抽象ソケットアドレスは、 sun_path[0] がヌルバイト ('\0') であることから区別できる。 sun_path の残りの全バイトによりソケットの「名前」が定義される (名前中のヌルバイトには特別な意味はない)。 この名前はファイルシステムのパス名とは何の関係もない。 この名前空間におけるソケットのアドレスは、 sun_path の残りのバイトで表される。 getsockname(2), getpeername(2), accept(2) が抽象ソケットのアドレスを返す際には、その長さは sizeof(struct sockaddr_un) であり、 sun_path に抽象名前空間の名前が格納される。 ソケットの抽象名前空間Linux による拡張であり、移植性はない。
```

## 誰が @ 文字列をつけるのか?

`@` の文字を調べる

`/proc/net/unix` を見ると `@` が付いてるので、カーネルが `@` を付けていると推測できる

# cat /proc/net/unix
Num       RefCount Protocol Flags    Type St Inode Path
ffff88001cc2c680: 00000002 00000000 00010000 0001 01  5826 @/com/ubuntu/upstart
ffff88001cc2d380: 00000002 00000000 00010000 0001 01 53603 @/var/lib/lxc/001/command
ffff88001cc2e3c0: 00000002 00000000 00010000 0005 01  6091 @/org/kernel/udev/udevd
ffff88001eb80d00: 00000002 00000000 00010000 0001 01  7343 /var/run/dbus/system_bus_socket
ffff88001eb81a00: 00000006 00000000 00000000 0002 01  7364 /dev/log

linux-3.0.4/net/unix/af_unix.c 調べると...

```
static int unix_seq_show(struct seq_file *seq, void *v)
{

/* 略 */

if (!UNIX_ABSTRACT(s))
len--;
else {
seq_putc(seq, '@');
i++;
}
```

`@` を出力するコードがあったあった。これで謎が解決した。


## 抽象ソケットアドレス を perlで試す

  • 検証用コード

```perl
#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket::UNIX;

# 先頭に \0 入れれば 抽象ソケットを生成する
my $sock = IO::Socket::UNIX->new( Local => "\0/path/not/exists") or die $!;
sleep(100);
```


これを実行して、別セッションで netstat の出力を見る

```
$ netstat -x | grep path
unix 2 [ ] STREAM 55077 @/path/not/exists
```

確認できました!

蛇足

UNIXドメインソケットは ネットワークの名前空間 (≒ CLONE_NEWNET) に属する

unshare --net で 名前空間を切り離してから netstat すると見えなくなる

```
# unshare --net -- netstat -x
Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags Type State I-Node Path
```

CLONE_NEWNET は TCP/Iソケットだけが対象だと思ってたなぁ

## 2020年12月追記

containerd で abrstract socket address / abrstracet UNIX domain socket を用いてコンテナをエスケープできる脆弱性が見つかった

research.nccgroup.com