NetBSD でも YubiKey を使って二要素認証したい
これは NetBSD Advent Calendar 2015 の 24 日目の記事です。
はじめに
Github ユーザなら 20% off というキャンペーンを見かけて IYH した YubiKey 4 が届いたので NetBSD で使えるか試してみました。
Attn @github users! Get 20% off every #U2F #YubiKey including the YubiKey 4! https://t.co/G3TbHe9GOa #security pic.twitter.com/db3Lhr53zW
— Yubico (@Yubico) 2015, 11月 24
今回はコンソールログイン時の認証を既存のパスワードに加え YubiKey の OTP を使った認証を使えるようにしてみようと思います。
前提条件
- 使用しているシステムは 2015/12/20 あたりのソースがベースの独自パッチがあたっている NetBSD/amd64 7.99.24 です。
- pam_yubico で使用される認証サーバーには Yubico の提供しているサーバー YubiCloud を使用します。
- 上記条件より pam_yubico を使用するシステムはインターネットに接続されている必要があります。
YubiKey 設定確認
YubiKey 4 の初期設定では OTP が使えるようになっているはずなので、とりあえず NetBSD マシンに YubiKey 4 を接続してみます。
uhidev2 at uhub2 port 1 configuration 1 interface 0 uhidev2: Yubico Yubikey 4 OTP+U2F+CCID, rev 2.00/4.27, addr 6, iclass 3/1 ukbd1 at uhidev2: 8 modifier keys, 6 key codes wskbd2 at ukbd1 mux 1 wskbd2: connecting to wsdisplay0 uhidev3 at uhub2 port 1 configuration 1 interface 1 uhidev3: Yubico Yubikey 4 OTP+U2F+CCID, rev 2.00/4.27, addr 6, iclass 3/0 uhid0 at uhidev3: input=64, output=64, feature=0
接続後の dmesg に上記の様に「Yubico Yubikey 4 OTP+U2F+CCID
」と「OTP
」が含まれている、かつ ukbd(4) が attach されていて YubiKey 4 の「y」マークをタッチして 44 文字入力されれば問題ないようです。
ここで ukbd(4) が attach されていないと動作モードの変更が必要になるのですが、素の NetBSD カーネルだと動作モードの変更が難しいので後述することにします、しないかもしれません。*1
NetBSD 設定
pam_yubico インストール
NetBSD では PAM を使ってログイン時の認証などを行っています。 調べてみたところ YubiKey 開発元の Yubico が pam_yubico という PAM のモジュールを提供しているのでこれを使えば YubiKey を使った認証ができるようになりそうです。
幸いにして pkgsrc に security/pam-yubico が存在しているのでこれを使えば苦労することなく目的を達することができそうです。
というわけで pam_yubico を pkgsrc を使ってインストールします。
$ cd /usr/pkgsrc/security/pam-yubico $ sudo make install $ pkg_info | grep pam-yubico pam-yubico-2.17 Pluggable Authentication Module for Yubikey validation
Client ID 取得
PAM 設定ファイルを編集する前に pam_yubico の Configuration を見ると Client ID というものが出てきますが、これは Yubico Get API Key で取得可能なようです。
「Your e-mail address」には適当に自分のメールアドレスを、「YubiKey one-time password」には右側の入力欄にフォーカスさせてから YubiKey を指でタッチして文字列を入力します。「Get API Key」ボタンをクリック後に Client ID と Secret Key が表示されるので忘れないようにメモしておきます。
PAM 設定
コンソールログイン時の login プロンプトは /usr/bin/login が表示しています。これに対応する PAM の設定ファイルはどうマッピングされているのかまでは調べていませんが /etc/pam.d/login になるようです。
2015/12/23 時点の HEAD の /etc/pam.d/login の内容はこんな感じです。
# $NetBSD: login,v 1.4 2005/02/27 03:40:14 thorpej Exp $ # # PAM configuration for the "login" service # # auth auth sufficient pam_self.so no_warn auth required pam_nologin.so no_warn auth include system # account account requisite pam_securetty.so account required pam_login_access.so account include system # session session include system # password password include system
今回注目するのは auth です。
auth の最後の行で system を include しているので、こっちも見てみます。
# $NetBSD: system,v 1.8 2008/03/26 11:31:17 lukem Exp $ # # System-wide defaults # # auth auth sufficient pam_skey.so no_warn try_first_pass auth sufficient pam_krb5.so no_warn try_first_pass auth optional pam_afslog.so no_warn try_first_pass auth required pam_unix.so no_warn try_first_pass nullok # account account required pam_krb5.so account required pam_unix.so # session session required pam_lastlog.so no_fail no_nested # password password sufficient pam_krb5.so no_warn try_first_pass password required pam_unix.so no_warn try_first_pass
/etc/pam.d/system にはシステムデフォルトが記述されているようです。このファイルを編集すると別のプログラムでも影響があるので、/etc/pam.d/login の方を編集することにします。
※PAM 設定ファイル中の auth 以外の箇所は変更しないので、これ以降は auth 部分のみを例示します。
pam_yubico の Configuration を参考に /etc/pam.d/login に pam_yubico の設定を追加します。
auth sufficient pam_self.so no_warn auth required pam_nologin.so no_warn auth sufficient /usr/pkg/lib/security/pam_yubico.so id=XXX debug auth include system
NetBSD では特定のディレクトリに PAM モジュールを配置する必要は無く、モジュールのフルパスを指定すれば良いようです。
id には取得した Client ID を設定します。
また、この時点では設定ミスなどがあった場合にわかりやすくなるので debug を指定しておきます。
認可用マッピングファイル作成
pam_yubico の Authorization Mapping Files に記載されているように、ユーザと YubiKey の対応付けを行うファイルを作成します。 今回はユーザのホームディレクトリに認可用マッピングファイルを作成します。
$ cd $ pwd /home/nonaka $ mkdir .yubico $ chmod 700 .yubico $ ls -ld .yubico drwx------ 2 nonaka nonaka 512 Dec 23 09:22 .yubico $ cd .yubico $ pwd /home/nonaka/.yubico $ echo "nonaka:OTPfirst12char" > authorized_yubikeys $ chmod 600 authorized_yubikeys $ ls -l authorized_yubikeys -rw------- 1 nonaka nonaka 20 Dec 23 09:22 authorized_yubikeys
$HOME/.yubico/authorized_yubikeys
の OTPfirst12char
には YubiKey をタッチして入力される文字列の先頭 12 文字を設定します。
最初は Modhex を指定する必要があるのかと思ったのですがそういう訳では無いようです。
さあ試してみよう
ここまで設定すると YubiKey を使ったログインができるようになっている筈なので実際にログインしてみます。
system の前に pam_yubico を追加したのでパスワードの前に YubiKey の入力が求められる筈です。
NetBSD/amd64 (koharu.myhome.nonakap.org) (ttyE1) login: nonaka Password: Last login: Wed Dec 23 14:24:22 2015 on ttyE1 Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 The NetBSD Foundation, Inc. All rights reserved. Copyright (c) 1982, 1986, 1989, 1991, 1993 The Regents of the University of California. All rights reserved. NetBSD 7.99.24 (KOHARU) #3367: Sun Dec 20 14:07:44 JST 2015 nonaka@koaru$
おいィ?
YubiKey のゆの字すら出てきません。
慌てず騒がず /var/log 配下のログファイルを見てみます。認証認可関連なので /var/log/authlog でしょう。
Dec 23 14:34:19 koharu login: in openpam_dispatch(): /usr/pkg/lib/security/pam_yubico.so: no pam_sm_authenticate() Dec 23 14:34:21 koharu login: nonaka on tty ttyE1 Dec 23 14:34:21 koharu login: in openpam_dispatch(): /usr/pkg/lib/security/pam_yubico.so: no pam_sm_setcred()
おいィ?(二回目
慌てず騒がず /usr/pkg/lib/security/pam_yubico.so のシンボル一覧を見ます。
$ nm /usr/pkg/lib/security/pam_yubico.so 0000000000203028 d _DYNAMIC 0000000000203388 d _GLOBAL_OFFSET_TABLE_ w _Jv_RegisterClasses 0000000000203008 d __CTOR_LIST_END__ 0000000000002870 r __FUNCTION__.4014 0000000000002850 r __FUNCTION__.4029 0000000000002990 r __FUNCTION__.4254 0000000000002970 r __FUNCTION__.4260 000000000020355c D __bss_start w __cxa_finalize w __deregister_frame_info@@GCC_3.0 0000000000203510 d __dso_handle U __errno U __getpwnam50 w __register_frame_info@@GCC_3.0 U __sF 000000000020355c D _edata 0000000000203560 D _end 0000000000002580 T _fini 00000000000013b0 T _init 00000000000019f3 T challenge_response 00000000000018b9 T check_firmware_version U fclose U fflush U fileno U fopen U fprintf U fread U free U fscanf U fsync U ftruncate U fwrite 0000000000001863 T generate_random 0000000000001770 T get_user_cfgfile_path 0000000000001b19 T get_user_challenge_file U getegid U geteuid U getgroups 00000000000019ce T init_yubikey U initgroups 0000000000001c6d T load_chalresp_state U malloc U memset 00000000000021f0 T pam_modutil_drop_priv 0000000000002422 T pam_modutil_regain_priv U printf U putchar U rewind U setegid U seteuid U setgroups U snprintf U strerror 0000000000001ffe T write_chalresp_state U yk_challenge_response@@LIBYKPERS_1.8 U yk_get_serial@@LIBYKPERS_1.5 U yk_get_status@@LIBYKPERS_1.0 U yk_hmac_sha1@@LIBYKPERS_1.9 U yk_init@@LIBYKPERS_1.0 U yk_open_first_key@@LIBYKPERS_1.0 U yk_pbkdf2@@LIBYKPERS_1.0 U ykds_alloc@@LIBYKPERS_1.0 U ykds_version_build@@LIBYKPERS_1.0 U ykds_version_major@@LIBYKPERS_1.0 U ykds_version_minor@@LIBYKPERS_1.0 U yubikey_hex_decode@@YUBIKEY_1.0 U yubikey_hex_encode@@YUBIKEY_1.0 U yubikey_hex_p@@YUBIKEY_1.5
確かに pam_sm_* なんてシンボルは存在していません。
pam_yubico 訪問
pam_yubico のソースを確認してみましょう。
$ cd /usr/pkgsrc/security/pam-yubico $ make patch $ cd /usr/obj.pkgsrc/security/pam-yubico/work.koharu/pam_yubico-2.17 $ grep -n pam_sm_authenticate *.[ch] pam_yubico.c:792:pam_sm_authenticate (pam_handle_t * pamh, pam_yubico.c:1107: pam_sm_authenticate, test.c:50: rc = pam_sm_authenticate (pamh, 0, 1, (const char **) argv);
ありました。pam_yubico.c の 792 行目付近を見てます。
PAM_EXTERN int pam_sm_authenticate (pam_handle_t * pamh, int flags, int argc, const char **argv)
PAM_EXTERN
というのが怪しそうです。
$ grep -n PAM_EXTERN *.[ch] | grep define pam_yubico.c:81:#ifndef PAM_EXTERN pam_yubico.c:83:#define PAM_EXTERN static pam_yubico.c:85:#define PAM_EXTERN extern pam_yubico.c:791:PAM_EXTERN int pam_yubico.c:1097:PAM_EXTERN int
pam_yubico.c の 81 行目付近を見てます。
#ifndef PAM_EXTERN #ifdef PAM_STATIC #define PAM_EXTERN static #else #define PAM_EXTERN extern #endif #endif
PAM_STATIC
というのはどこで define されるのでしょうか。
$ grep -n PAM_STATIC *.[ch] pam_yubico.c:56:#define PAM_STATIC pam_yubico.c:82:#ifdef PAM_STATIC pam_yubico.c:1103:#ifdef PAM_STATIC
pam_yubico.c の 56 行目ですね。
/* Libtool defines PIC for shared objects */ #ifndef PIC #define PAM_STATIC #endif
この PIC
は共有ライブラリを作成する際に指定されるやつですかね?
では実際に pkgsrc/security/pam-yubico をビルドしてみましょう。
$ cd /usr/pkgsrc/security/pam-yubico $ make V=1 2>&1 | tee make.log $ grep pam_yubico.c make.log /bin/sh ./libtool --tag=CC --mode=compile gcc -DDEBUG_PAM -DPAM_DEBUG -DPACKAGE_NAME=\"pam_yubico\" -DPACKAGE_TARNAME=\"pam_yubico\" -DPACKAGE_VERSION=\"2.17\" -DPACKAGE_STRING=\"pam_yubico\ 2.17\" -DPACKAGE_BUGREPORT=\"yubico-devel@googlegroups.com\" -DPACKAGE_URL=\"\" -DPACKAGE=\"pam_yubico\" -DVERSION=\"2.17\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=1 -DLT_OBJDIR=\".libs/\" -DHAVE_SECURITY_PAM_APPL_H=1 -DHAVE_SECURITY_PAM_MODULES_H=1 -DHAVE_LIBPAM=1 -DHAVE_LIBLDAP=1 -DHAVE_LIBYKCLIENT=1 -DHAVE_LIBYUBIKEY=1 -DHAVE_CR=1 -I. -pthread -I/usr/pkg/include/ykpers-1 -I/usr/pkg/include -I/usr/include/krb5 -I/usr/include -O2 -I/usr/pkg/include -I/usr/include/krb5 -I/usr/include -MT pam_yubico_la-pam_yubico.lo -MD -MP -MF .deps/pam_yubico_la-pam_yubico.Tpo -c -o pam_yubico_la-pam_yubico.lo `test -f 'pam_yubico.c' || echo './'`pam_yubico.c libtool: compile: gcc -DDEBUG_PAM -DPAM_DEBUG -DPACKAGE_NAME=\"pam_yubico\" -DPACKAGE_TARNAME=\"pam_yubico\" -DPACKAGE_VERSION=\"2.17\" "-DPACKAGE_STRING=\"pam_yubico 2.17\"" -DPACKAGE_BUGREPORT=\"yubico-devel@googlegroups.com\" -DPACKAGE_URL=\"\" -DPACKAGE=\"pam_yubico\" -DVERSION=\"2.17\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=1 -DLT_OBJDIR=\".libs/\" -DHAVE_SECURITY_PAM_APPL_H=1 -DHAVE_SECURITY_PAM_MODULES_H=1 -DHAVE_LIBPAM=1 -DHAVE_LIBLDAP=1 -DHAVE_LIBYKCLIENT=1 -DHAVE_LIBYUBIKEY=1 -DHAVE_CR=1 -I. -pthread -I/usr/obj.pkgsrc/security/pam-yubico/work.koharu/.buildlink/include/ykpers-1 -I/usr/obj.pkgsrc/security/pam-yubico/work.koharu/.buildlink/include -I/usr/include/krb5 -O2 -MT pam_yubico_la-pam_yubico.lo -MD -MP -MF .deps/pam_yubico_la-pam_yubico.Tpo -c pam_yubico.c -fPIC -DPIC -o .libs/pam_yubico_la-pam_yubico.o pam_yubico.c: In function 'display_error': pam_yubico.c:452:14: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] libtool: compile: gcc -DDEBUG_PAM -DPAM_DEBUG -DPACKAGE_NAME=\"pam_yubico\" -DPACKAGE_TARNAME=\"pam_yubico\" -DPACKAGE_VERSION=\"2.17\" "-DPACKAGE_STRING=\"pam_yubico 2.17\"" -DPACKAGE_BUGREPORT=\"yubico-devel@googlegroups.com\" -DPACKAGE_URL=\"\" -DPACKAGE=\"pam_yubico\" -DVERSION=\"2.17\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=1 -DLT_OBJDIR=\".libs/\" -DHAVE_SECURITY_PAM_APPL_H=1 -DHAVE_SECURITY_PAM_MODULES_H=1 -DHAVE_LIBPAM=1 -DHAVE_LIBLDAP=1 -DHAVE_LIBYKCLIENT=1 -DHAVE_LIBYUBIKEY=1 -DHAVE_CR=1 -I. -pthread -I/usr/obj.pkgsrc/security/pam-yubico/work.koharu/.buildlink/include/ykpers-1 -I/usr/obj.pkgsrc/security/pam-yubico/work.koharu/.buildlink/include -I/usr/include/krb5 -O2 -MT pam_yubico_la-pam_yubico.lo -MD -MP -MF .deps/pam_yubico_la-pam_yubico.Tpo -c pam_yubico.c -o pam_yubico_la-pam_yubico.o >/dev/null 2>&1
ちなみに make に V=1
を指定しないとコンパイル時に実行されるコマンドが表示されません。
少なくとも共有ライブラリ向けにコンパイルしている .libs/pam_yubico_la-pam_yubico.o
では -fPIC -DPIC
が指定されているので問題なさそうです。
一応 .libs/pam_yubico_la-pam_yubico.o
のシンボルも見てみましょう。
$ nm /usr/obj.pkgsrc/security/pam-yubico/work.koharu/pam_yubico-2.17/.libs/pam_yubico_la-pam_yubico.o
おいィ?(三回目
何も表示されませんでした。
何かの間違いかもしれないので別のファイル .libs/pam_yubico_la-util.o
のシンボルも見てみましょう。
$ nm /usr/obj.pkgsrc/security/pam-yubico/work.koharu/pam_yubico-2.17/.libs/pam_yubico_la-util.o 0000000000000000 r .LC0 0000000000000006 r .LC1 0000000000000054 r .LC10 000000000000005b r .LC11 0000000000000068 r .LC12 000000000000006f r .LC13 00000000000000a8 r .LC14 0000000000000075 r .LC15 00000000000000d8 r .LC16 0000000000000120 r .LC17 0000000000000140 r .LC18 0000000000000178 r .LC19 0000000000000014 r .LC2 00000000000001a8 r .LC20 00000000000001d0 r .LC21 0000000000000091 r .LC22 00000000000000a9 r .LC23 0000000000000016 r .LC3 0000000000000023 r .LC4 0000000000000000 r .LC5 000000000000003e r .LC6 0000000000000043 r .LC7 0000000000000038 r .LC8 000000000000004a r .LC9 U _GLOBAL_OFFSET_TABLE_ 0000000000000020 r __FUNCTION__.4014 0000000000000000 r __FUNCTION__.4029 U __getpwnam50 U __sF 0000000000000283 T challenge_response 0000000000000149 T check_firmware_version U fclose U fflush U fileno U fopen U fprintf U fread U free U fscanf U fsync U ftruncate U fwrite 00000000000000f3 T generate_random 0000000000000000 T get_user_cfgfile_path 00000000000003a9 T get_user_challenge_file 000000000000025e T init_yubikey 00000000000004fd T load_chalresp_state U malloc U memset U printf U putchar U rewind U snprintf 000000000000088e T write_chalresp_state U yk_challenge_response U yk_get_serial U yk_get_status U yk_hmac_sha1 U yk_init U yk_open_first_key U yk_pbkdf2 U ykds_alloc U ykds_version_build U ykds_version_major U ykds_version_minor U yubikey_hex_decode U yubikey_hex_encode U yubikey_hex_p
おいィ?(四回目
そんなバカな…
$ ls -l /usr/obj.pkgsrc/security/pam-yubico/work.koharu/pam_yubico-2.17/.libs/pam_yubico_la-* -rw-r--r-- 1 nonaka wsrc 5704 Dec 23 15:09 /usr/obj.pkgsrc/security/pam-yubico/work.koharu/pam_yubico-2.17/.libs/pam_yubico_la-drop_privs.o -rw-r--r-- 1 nonaka wsrc 814 Dec 23 15:09 /usr/obj.pkgsrc/security/pam-yubico/work.koharu/pam_yubico-2.17/.libs/pam_yubico_la-pam_yubico.o -rw-r--r-- 1 nonaka wsrc 11048 Dec 23 15:09 /usr/obj.pkgsrc/security/pam-yubico/work.koharu/pam_yubico-2.17/.libs/pam_yubico_la-util.o
おいィ?(五回目
オブジェクトファイルのサイズが妙に小さい上にシンボルが一つも表示されないのってあれですかね、ファイル内に public なシンボルが無かったから全部消されたとかいう事なんでしょうか。
それって PAM_EXTERN として static が指定されているって事じゃないすか!やだー
という事で PIC 定義の有無に関係無く PAM_EXTERN に static が指定されているという事に。
$ cd /usr/obj.pkgsrc/security/pam-yubico/work.koharu/pam_yubico-2.17 $ find . -type f | xargs grep -n PAM_EXTERN /dev/null ./pam_yubico.c:81:#ifndef PAM_EXTERN ./pam_yubico.c:83:#define PAM_EXTERN static ./pam_yubico.c:85:#define PAM_EXTERN extern ./pam_yubico.c:791:PAM_EXTERN int ./pam_yubico.c:1097:PAM_EXTERN int
という事で pam_yubico は無罪
OpenPAM 訪問
NetBSD では PAM 関連のヘッダファイルは /usr/include/security にあるようです。
$ cd /usr/include/security/ $ grep -n PAM_EXTERN * openpam.h:281:PAM_EXTERN int \ openpam.h:372:# define PAM_EXTERN static openpam.h:389:# define PAM_EXTERN pam_modules.h:56:PAM_EXTERN int pam_modules.h:64:PAM_EXTERN int pam_modules.h:72:PAM_EXTERN int pam_modules.h:80:PAM_EXTERN int pam_modules.h:88:PAM_EXTERN int pam_modules.h:96:PAM_EXTERN int pam_modules.h:107:PAM_EXTERN int pam_modules.h:118:PAM_EXTERN int pam_modules.h:128:PAM_EXTERN int pam_modules.h:139:PAM_EXTERN int pam_modules.h:149:PAM_EXTERN int
おいィ?(六回目
PAM_EXTERN
は openpam.h で定義されているようです。
#if (defined(__GNUC__) || defined(__PCC__)) && !defined(NO_STATIC_MODULES) # include <sys/cdefs.h> # ifdef __FreeBSD__ # include <linker_set.h> # endif # ifdef __NetBSD__ # define DATA_SET(a, b) __link_set_add_data(a, b) # define SET_DECLARE(a, b) __link_set_decl(a, b) # define SET_FOREACH(a, b) __link_set_foreach(a, b) # endif # define OPENPAM_STATIC_MODULES # define PAM_EXTERN static # define PAM_MODULE_ENTRY(name) \ static char _pam_name[] = name PAM_SOEXT; \ static struct pam_module _pam_module = { \ .path = _pam_name, \ .func = { \ [PAM_SM_AUTHENTICATE] = _PAM_SM_AUTHENTICATE, \ [PAM_SM_SETCRED] = _PAM_SM_SETCRED, \ [PAM_SM_ACCT_MGMT] = _PAM_SM_ACCT_MGMT, \ [PAM_SM_OPEN_SESSION] = _PAM_SM_OPEN_SESSION, \ [PAM_SM_CLOSE_SESSION] = _PAM_SM_CLOSE_SESSION, \ [PAM_SM_CHAUTHTOK] = _PAM_SM_CHAUTHTOK \ }, \ }; \ DATA_SET(openpam_static_modules, _pam_module) #else /* normal case */ # define PAM_EXTERN # define PAM_MODULE_ENTRY(name) #endif
あー
どうやら NO_STATIC_MODULES
が定義されていないと PAM_EXTERN に static が自動定義されるスバラシイ(褒めてない)コードになっていますね。
どうしてやろうか…
システム側のヘッダファイルがおかしいのであれば他の PAM 関連 pkgsrc でも問題が発生するような気がするんだけど、実は何か対処しているのだろうか
$ cd /usr/pkgsrc/security $ grep -n NO_STATIC_MODULES */Makefile gnome-keyring/Makefile:26:CFLAGS.NetBSD+= -DNO_STATIC_MODULES google-authenticator/Makefile:17:CFLAGS+= -DNO_STATIC_MODULES pam-dbm/Makefile:27:CFLAGS+= -DNO_STATIC_MODULES pam-fprint/Makefile:24:CFLAGS+= -DNO_STATIC_MODULES pam-krb5/Makefile:17:CPPFLAGS.NetBSD+= -DNO_STATIC_MODULES pam-ldap/Makefile:31:CFLAGS+= -DNO_STATIC_MODULES pam-mkhomedir/Makefile:38:CFLAGS+= -DNO_STATIC_MODULES pam-passwdqc/Makefile:16:CFLAGS+= -DNO_STATIC_MODULES pam-pwauth_suid/Makefile:13:PAMDEF+= -DNO_STATIC_MODULES pam-yubico.new/Makefile:21:CFLAGS.NetBSD+= -DNO_STATIC_MODULES pam-yubico/Makefile:24:CFLAGS+= -DNO_STATIC_MODULES
あー(二回目
とりあえず一番上の gnome-keyring/Makefile を見てましょう。
# $NetBSD: Makefile,v 1.83 2015/04/25 14:24:48 tnn Exp $ DISTNAME= gnome-keyring-2.32.1 PKGREVISION= 19 CATEGORIES= security gnome MASTER_SITES= ${MASTER_SITE_GNOME:=sources/gnome-keyring/2.32/} EXTRACT_SUFX= .tar.bz2 MAINTAINER= pkgsrc-users@NetBSD.org HOMEPAGE= http://www.gnome.org/ COMMENT= GNOME password and secret manager USE_TOOLS+= gmake intltool msgfmt pkg-config GNU_CONFIGURE= YES USE_LIBTOOL= YES USE_PKGLOCALEDIR= YES PKGCONFIG_OVERRIDE+= gcr/gcr.pc.in PKGCONFIG_OVERRIDE+= gp11/gp11.pc.in CFLAGS.HPUX+= -DMAP_ANON=MAP_ANONYMOUS # Workaround a bug with NetBSD's openpam # The bug is described in PR security/39313 # CFLAGS.NetBSD+= -DNO_STATIC_MODULES .include "../../mk/bsd.prefs.mk" .if ${OPSYS} == "SunOS" CONFIGURE_ENV+= ac_cv_header_security_pam_modules_h=yes .endif .include "../../mk/dlopen.buildlink3.mk" BUILDLINK_TRANSFORM+= opt:-ldl:${BUILDLINK_LDADD.dl:M*} BUILDLINK_API_DEPENDS.glib2+= glib2>=2.26.0 .include "../../devel/glib2/schemas.mk" .include "../../devel/gettext-lib/buildlink3.mk" .include "../../devel/glib2/buildlink3.mk" .include "../../security/libgcrypt/buildlink3.mk" .include "../../security/libtasn1/buildlink3.mk" .include "../../sysutils/dbus/buildlink3.mk" .include "../../x11/gtk2/buildlink3.mk" .include "../../mk/pam.buildlink3.mk" .include "../../mk/bsd.pkg.mk"
あー(三回目
lib/39313、そういう事のようです
workaround をパチってきました。
Index: security/pam-yubico/Makefile =================================================================== RCS file: /cvsroot/pkgsrc/security/pam-yubico/Makefile,v retrieving revision 1.10 diff -u -p -r1.10 Makefile --- security/pam-yubico/Makefile 10 Nov 2014 21:55:31 -0000 1.10 +++ security/pam-yubico/Makefile 23 Dec 2015 06:59:08 -0000 @@ -15,6 +15,11 @@ GNU_CONFIGURE= yes USE_TOOLS+= pkg-config USE_LIBTOOL= yes +# Workaround a bug with NetBSD's openpam +# The bug is described in PR security/39313 +# +CFLAGS.NetBSD+= -DNO_STATIC_MODULES + .include "../../security/libyubikey/buildlink3.mk" .include "../../security/ykclient/buildlink3.mk" .include "../../security/ykpers/buildlink3.mk"
Makefile にパッチをあてて pam_yubico パッケージを再作成します。
$ cd /usr/pkgsrc/security/pam-yubico $ make clean $ make update
新しい pam_yubico モジュールのシンボルを確認しておきます。
$ nm /usr/pkg/lib/security/pam_yubico.so 0000000000208620 d _DYNAMIC 0000000000208980 d _GLOBAL_OFFSET_TABLE_ w _Jv_RegisterClasses 0000000000208600 d __CTOR_LIST_END__ 0000000000008050 r __FUNCTION__.4014 0000000000008030 r __FUNCTION__.4029 0000000000008170 r __FUNCTION__.4254 0000000000008150 r __FUNCTION__.4260 0000000000007ce0 r __FUNCTION__.6198 0000000000007d00 r __FUNCTION__.6211 0000000000007d20 r __FUNCTION__.6235 0000000000007d3a r __FUNCTION__.6252 0000000000007d50 r __FUNCTION__.6275 0000000000007d66 r __FUNCTION__.6289 0000000000007d70 r __FUNCTION__.6319 0000000000208d34 D __bss_start w __cxa_finalize w __deregister_frame_info@@GCC_3.0 0000000000208ce8 d __dso_handle U __errno U __fstat50 U __getpwnam50 w __register_frame_info@@GCC_3.0 U __sF 0000000000208d34 D _edata 0000000000208d38 D _end 0000000000006f60 T _fini 00000000000023b0 T _init U _yk_errno_location@@LIBYKPERS_1.0 U ber_free 0000000000006033 T challenge_response 0000000000005ef9 T check_firmware_version 0000000000006bc0 t check_user_token.isra.1 U close U fclose U fdopen U fflush U fgets U fileno U fopen U fprintf U fread U free U fscanf U fsync U ftruncate U fwrite 0000000000005ea3 T generate_random 0000000000005db0 T get_user_cfgfile_path 0000000000006159 T get_user_challenge_file U getegid U geteuid U getgroups 000000000000600e T init_yubikey U initgroups U ldap_count_values_len U ldap_err2string U ldap_first_attribute U ldap_first_entry U ldap_get_values_len U ldap_init U ldap_initialize U ldap_memfree U ldap_msgfree U ldap_next_attribute U ldap_search_ext_s U ldap_set_option U ldap_simple_bind_s U ldap_unbind U ldap_value_free_len 00000000000062ad T load_chalresp_state U malloc U memcmp U memcpy U memset U open U pam_get_item U pam_get_user 0000000000006830 T pam_modutil_drop_priv 0000000000006a62 T pam_modutil_regain_priv U pam_set_data U pam_set_item 0000000000002b30 T pam_sm_authenticate 0000000000005dac T pam_sm_setcred U pam_strerror U printf U putchar U rename U rewind U setegid U seteuid U setgroups U snprintf U sprintf U sscanf U strcmp U strdup U strerror U strncmp U strncpy U strtok U strtok_r U syslog 000000000000663e T write_chalresp_state U yk_challenge_response@@LIBYKPERS_1.8 U yk_close_key@@LIBYKPERS_1.0 U yk_get_serial@@LIBYKPERS_1.5 U yk_get_status@@LIBYKPERS_1.0 U yk_hmac_sha1@@LIBYKPERS_1.9 U yk_init@@LIBYKPERS_1.0 U yk_open_first_key@@LIBYKPERS_1.0 U yk_pbkdf2@@LIBYKPERS_1.0 U yk_release@@LIBYKPERS_1.0 U yk_strerror@@LIBYKPERS_1.0 U yk_usb_strerror@@LIBYKPERS_1.0 U ykclient_done@@Base U ykclient_init@@Base U ykclient_request@@Base U ykclient_set_ca_path@@Base U ykclient_set_client_b64@@Base U ykclient_set_url_bases@@YKCLIENT_2.12 U ykclient_set_url_template@@Base U ykclient_set_verify_signature@@Base U ykclient_strerror@@Base U ykds_alloc@@LIBYKPERS_1.0 U ykds_version_build@@LIBYKPERS_1.0 U ykds_version_major@@LIBYKPERS_1.0 U ykds_version_minor@@LIBYKPERS_1.0 U yubikey_hex_decode@@YUBIKEY_1.0 U yubikey_hex_encode@@YUBIKEY_1.0 U yubikey_hex_p@@YUBIKEY_1.5
ヤッター
再度試してみよう
気を取り直して再度ログインしてみましょう。
NetBSD/amd64 (koharu.myhome.nonakap.org) (ttyE1) login: nonaka debug: pam_yubico.c:764 (parse_cfg): called. (snip...) YubiKey for `nonaka': (ここで YubiKey にタッチする) debug: pam_yubico.c:972 (pam_sm_authenticate): conv returned 44 bytes (snip...) Last login: Wed Dec 23 14:59:11 2015 on 192.168.0.103 Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 The NetBSD Foundation, Inc. All rights reserved. Copyright (c) 1982, 1986, 1989, 1991, 1993 The Regents of the University of California. All rights reserved. NetBSD 7.99.24 (KOHARU) #3367: Sun Dec 20 14:07:44 JST 2015 nonaka@koaru$
ヤッター(二回目
pam_yubico モジュールのフラグに debug を指定しているのでデバッグログがちょっとうるさいですが、YubiKey 認証が実行されました。
ってあれ? そういえばパスワード入力無しでログインできてしまいました。
これはこれで大変便利なのですが今回は二要素認証したいという事なのでちょっと困ります。
再度 PAM 設定
これは YubiKey 関係無しに PAM の設定の話になります。
/etc/pam.d/login に設定で二要素認証するようになっていなかったという事です。
auth sufficient pam_self.so no_warn auth required pam_nologin.so no_warn auth sufficient /usr/pkg/lib/security/pam_yubico.so id=XXX debug auth include system
認証のバリエーションとしては以下の三つですかね
YubiKey→パスワード、YubiKey が成功したらログイン成功
auth sufficient pam_self.so no_warn auth required pam_nologin.so no_warn auth sufficient /usr/pkg/lib/security/pam_yubico.so id=XXX auth include system
YubiKey→パスワード、両方成功したらログイン成功
auth sufficient pam_self.so no_warn auth required pam_nologin.so no_warn auth required /usr/pkg/lib/security/pam_yubico.so id=XXX auth include system
パスワード→YubiKey、両方成功したらログイン成功
auth sufficient pam_self.so no_warn auth required pam_nologin.so no_warn auth include system auth required /usr/pkg/lib/security/pam_yubico.so id=XXX
再々度試してみよう
「パスワード→YubiKey、両方成功したらログイン成功」の設定を使ってログインしてみます。
NetBSD/amd64 (koharu.myhome.nonakap.org) (ttyE1) login: nonaka Password: YubiKey for `nonaka': (ここで YubiKey にタッチする) Last login: Wed Dec 23 16:22:58 2015 on ttyE1 Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 The NetBSD Foundation, Inc. All rights reserved. Copyright (c) 1982, 1986, 1989, 1991, 1993 The Regents of the University of California. All rights reserved. NetBSD 7.99.24 (KOHARU) #3367: Sun Dec 20 14:07:44 JST 2015 nonaka@koaru$
ヤッター(三回目
やっと当初の目的をとりあえず達成できました。
まとめ
コンソールログインだと二要素認証はそんなに便利でもないというか、その前にやる事が沢山あるだろうという気持ちでいっぱいです。
pam-u2f を使った U2F も試そうと思ったのですが、pkgsrc にパッケージが無かったので早々に諦めました。
pam_yubico を使った ssh 接続時の認証は多分 NetBSD 固有の設定は無いと思うのでググれ。
Authentication Using Challenge-Response も NetBSD 固有の設定は無いと思うのでググれ。
しかし pkgsrc にもちゃんと使えないパッケージがあるんですね(棒
*1:書きました。nonakap.hatenablog.com
NetBSD で USB デバイスを強制的に ugen(4) として attach したい
通常は uftdi(4) とか uhid(4) として認識される USB デバイスを ugen(4) として認識させたい事ってありませんか?
そうですか、ありませんか…。
はじめに
これは「NetBSD でも YubiKey を使って二要素認証したい」の以下の文章を説明するための記事です。
ここで ukbd(4) が attach されていないと動作モードの変更が必要になるのですが、素の NetBSD カーネルだと動作モードの変更が難しいので後述することにします、しないかもしれません。
なんで ugen(4) にしたいの?
まずは libusb のソースを見てもらおうか。話はそれからだ。
int netbsd_get_device_list(struct libusb_context * ctx, struct discovered_devs **discdevs) { struct libusb_device *dev; struct device_priv *dpriv; struct usb_device_info di; unsigned long session_id; char devnode[16]; int fd, err, i; usbi_dbg(""); /* Only ugen(4) is supported */ for (i = 0; i < USB_MAX_DEVICES; i++) { /* Control endpoint is always .00 */ snprintf(devnode, sizeof(devnode), "/dev/ugen%d.00", i);
libusb を使えばデバイスドライバを書かなくてもユーザーランドプログラムから USB デバイスを制御できるのですが、残念ながら NetBSD 上では libusb は上記ソースに記載されている通り ugen(4) として認識されている USB デバイスしか扱えないのです。
そんな訳で uhid(4) や ukbd(4) として認識されている YubiKey 4 を libusb 経由で YubiKey を制御する ykpersonalize や ykinfo といったプログラムは制御する以前にそもそも YubiKey デバイスを見つけられません。
nonaka@koharu$ dmesg (snip...) uhidev2 at uhub2 port 1 configuration 1 interface 0 uhidev2: Yubico Yubikey NEO OTP+U2F, rev 2.00/3.43, addr 6, iclass 3/1 ukbd1 at uhidev2: 8 modifier keys, 6 key codes wskbd2 at ukbd1 mux 1 wskbd2: connecting to wsdisplay0 uhidev3 at uhub2 port 1 configuration 1 interface 1 uhidev3: Yubico Yubikey NEO OTP+U2F, rev 2.00/3.43, addr 6, iclass 3/0 uhid0 at uhidev3: input=64, output=64, feature=0 nonaka@koharu$ sudo ykinfo -v Yubikey core error: no yubikey present
本来ならば ukbd(4) などの USB デバイスドライバはすべからく ugen(4) として振る舞えるべき、もしくはインタフェースを持つべきだとは思うのですが、それを実現するにはちょっと長い道のりとなります。
なのでここではデバイスドライバの match/attach 処理に小細工をして、USB デバイスを接続した際に正しいデバイスドライバではなく ugen(4) として認識させようとします。
デバイスドライバ match/attach 処理
もの凄くざっくりとした説明
NetBSD ではあるバス(PCI とか USB とか)に接続されているデバイスドライバを attach する際には、該当バスに接続できるすべてのデバイスドライバの match 関数を呼び出します。
match 関数は PCI や USB であれば Vendor ID, Product ID といったバス固有の情報を受け取り、その情報から該当デバイスドライバで取り扱うデバイスであると判断したら match 関数の戻り値として 1 以上の整数値を返します。
全てのデバイスドライバの match 関数を呼び終わった後で 0 では無い一番大きな値を返したデバイスドライバの attach 関数を呼び出してデバイスドライバを attach します。
まあ、要するに ugen(4) として attach してほしいデバイスには ugen(4) の match 関数で他のデバイスドライバよりも大きな値を戻り値として返せば良いだけです。
USB スタック固有事情
実は USB スタックでは この match 関数で返すべき値が用意されています。
/* Match codes. */ #define UMATCH_HIGHEST 15 /* First five codes is for a whole device. */ #define UMATCH_VENDOR_PRODUCT_REV 14 #define UMATCH_VENDOR_PRODUCT 13 #define UMATCH_VENDOR_DEVCLASS_DEVPROTO 12 #define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO 11 #define UMATCH_DEVCLASS_DEVSUBCLASS 10 /* Next six codes are for interfaces. */ #define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE 9 #define UMATCH_VENDOR_PRODUCT_CONF_IFACE 8 #define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO 7 #define UMATCH_VENDOR_IFACESUBCLASS 6 #define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO 5 #define UMATCH_IFACECLASS_IFACESUBCLASS 4 #define UMATCH_IFACECLASS 3 #define UMATCH_IFACECLASS_GENERIC 2 /* Generic driver */ #define UMATCH_GENERIC 1 /* No match */ #define UMATCH_NONE 0
という訳で他のデバイスとして attach して貰いたくない場合には ugen(4) で該当デバイスの場合に UMATCH_HIGHEST
を返すようにすれば良いという事になります。
ugen(4) match 関数
それでは ugen(4) の match 関数である ugen_match() 関数を見てみましょう。
/* toggle to control attach priority. -1 means "let autoconf decide" */ int ugen_override = -1; int ugen_match(device_t parent, cfdata_t match, void *aux) { struct usb_attach_arg *uaa = aux; int override; if (ugen_override != -1) override = ugen_override; else override = match->cf_flags & 1; if (override) return (UMATCH_HIGHEST); else if (uaa->usegeneric) return (UMATCH_GENERIC); else return (UMATCH_NONE); }
既に UMATCH_HIGHEST
を返すコードがありますね。
struct usb_attach_arg
が先ほど説明したバス固有のデバイス情報になります。
struct usb_attach_arg { int port; int vendor; int product; int release; usbd_device_handle device; /* current device */ int class, subclass, proto; int usegeneric; };
struct usb_attach_arg
に接続されたデバイスの Vendor ID と Product ID があるので、この二つが ugen(4) として attach したいデバイスの値と一致したら UMATCH_HIGHEST
を返すコードを追加すれば良いですかね。
後は ugen(4) として attach したいデバイスの Vendor ID と Product ID をどうやって指定するかという事になります。
Linux であれば /proc 経由で良いのでしょうが、NetBSD では何年前かはまで知りませんが今時は sysctl(8) 経由でユーザランドから設定できるようにするらしいです。
完成
良くあるように実際の処理よりもユーザインタフェース部分の方が処理が多い差分になりました。仕方ないね
USB デバイスの Vendor ID と Product ID はデバイスを接続した状態で usbdevs -v
で表示されます。
nonaka@koharu$ sudo usbdevs -v Controller /dev/usb0: addr 0: high speed, self powered, config 1, xHCI Root Hub(0x0000), NetBSD(0x0000), rev 1.00 port 1 powered port 2 powered port 3 powered port 4 powered port 5 powered port 6 powered port 7 powered port 8 powered port 9 powered Controller /dev/usb1: addr 1: high speed, self powered, config 1, EHCI root hub(0x0000), Intel(0x8086), rev 1.00 port 1 addr 2: high speed, self powered, config 1, product 8000(0x8000), Intel(0x8087), rev 0.04 port 1 addr 6: full speed, power 30 mA, config 1, Yubikey NEO OTP+U2F(0x0114), Yubico(0x1050), rev 3.43 port 2 powered port 3 powered port 4 powered port 5 addr 3: full speed, self powered, config 1, product 0a2a(0x0a2a), Intel(0x8087), rev 0.01 port 6 addr 4: high speed, power 200 mA, config 1, BisonCam, NB Pro(0x024b), vendor 5986(0x5986), rev 6.08 port 7 addr 5: full speed, power 100 mA, config 1, ThinkPad Compact USB Keyboard with TrackPoint(0x6047), Lenovo(0x17ef), rev 3.00 port 8 powered port 2 powered
実際に使ってみた
$ dmesg (snip...) uhidev2 at uhub2 port 1 configuration 1 interface 0 uhidev2: Yubico Yubikey NEO OTP+U2F, rev 2.00/3.43, addr 6, iclass 3/1 ukbd1 at uhidev2: 8 modifier keys, 6 key codes wskbd2 at ukbd1 mux 1 wskbd2: connecting to wsdisplay0 uhidev3 at uhub2 port 1 configuration 1 interface 1 uhidev3: Yubico Yubikey NEO OTP+U2F, rev 2.00/3.43, addr 6, iclass 3/0 uhid0 at uhidev3: input=64, output=64, feature=0 $ sysctl hw.ugen hw.ugen.override = -1 hw.ugen.forced_attach.vendor = 65535 hw.ugen.forced_attach.product = 65535 hw.ugen.forced_attach.addr = -1 hw.ugen.forced_attach.add = 0 hw.ugen.forced_attach.delete = 0 hw.ugen.forced_attach.clear = 0 hw.ugen.forced_attach.list = $ sudo sysctl -w hw.ugen.forced_attach.vendor=0x1050 hw.ugen.forced_attach.vendor: 65535 -> 4176 $ sudo sysctl -w hw.ugen.forced_attach.product=0x114 hw.ugen.forced_attach.product: 65535 -> 276 $ sudo sysctl -w hw.ugen.forced_attach.add=1 hw.ugen.forced_attach.add: 0 -> 1 $ sysctl hw.ugen hw.ugen.override = -1 hw.ugen.forced_attach.vendor = 65535 hw.ugen.forced_attach.product = 65535 hw.ugen.forced_attach.addr = -1 hw.ugen.forced_attach.add = 0 hw.ugen.forced_attach.delete = 0 hw.ugen.forced_attach.clear = 0 hw.ugen.forced_attach.list = [0x1050 0x0114 -1] (ここでデバイスを一旦抜いてから接続しなおす) $ dmesg (snip...) wskbd2: disconnecting from wsdisplay0 wskbd2: detached ukbd1: detached uhidev2: detached uhidev2: at uhub2 port 1 (addr 6) disconnected uhid0: detached uhidev3: detached uhidev3: at uhub2 port 1 (addr 6) disconnected ugen0 at uhub2 port 1 ugen0: Yubico Yubikey NEO OTP+U2F, rev 2.00/3.43, addr 6 $ sudo ykinfo -v version: 3.4.3
使い方
デバイス追加
$ sudo sysctl -w hw.ugen.forced_attach.vendor=0x1050 hw.ugen.forced_attach.vendor: 65535 -> 4176 $ sudo sysctl -w hw.ugen.forced_attach.product=0x114 hw.ugen.forced_attach.product: 65535 -> 276 $ sudo sysctl -w hw.ugen.forced_attach.add=1 hw.ugen.forced_attach.add: 0 -> 1 $ sysctl hw.ugen hw.ugen.override = -1 hw.ugen.forced_attach.vendor = 65535 hw.ugen.forced_attach.product = 65535 hw.ugen.forced_attach.addr = -1 hw.ugen.forced_attach.add = 0 hw.ugen.forced_attach.delete = 0 hw.ugen.forced_attach.clear = 0 hw.ugen.forced_attach.list = [0x1050 0x0114 -1]
デバイス削除
$ sudo sysctl -w hw.ugen.forced_attach.vendor=0x1050 hw.ugen.forced_attach.vendor: 65535 -> 4176 $ sudo sysctl -w hw.ugen.forced_attach.product=0x114 hw.ugen.forced_attach.product: 65535 -> 276 $ sudo sysctl -w hw.ugen.forced_attach.delete=1 hw.ugen.forced_attach.delete: 0 -> 1
デバイス全削除
$ sudo sysctl -w hw.ugen.forced_attach.clear=1 hw.ugen.forced_attach.clear: 0 -> 1
課題など
USB スタックにも設定がおかしな USB デバイスが接続された場合の振る舞いを変更するための quirk を決定する処理が実は既に存在しています。
Cross Reference: /src/sys/dev/usb/usb_quirks.c
何故これを使わなかったのかと言えば、動的にデバイスの追加・削除をするのが面倒だったのと Vendor ID と Product ID 以外でも指定したかったからです。現在のパッチでは Vendor ID と Product ID の他に addr が指定できます。これは同じ Vendor ID と Product ID を持っているけれど特定のデバイスだけ ugen(4) として使いたいという要求があったからです。
後で usbdevs -v の出力を見て気付いたのですが、addr だけでは controller だか bus が違う場合にも適用されてしまうのでもう細かく少し指定できる必要がありそうです。それを修正するのは後の課題としたいと思います。
パッチ
GitHub の netbsd-src リポジトリの nonakap-catfood ブランチでパッチは保守しているのでそちらを参照して貰った方が良いかもしれません。
nonakap/netbsd-src at nonakap-catfood · GitHub
続きを読む