月別アーカイブ: 2012年10月

シェルスクリプト

昔 /bin/sh (NetBSD) 用に書いた CGI プログラムに GET リクエストのパラメータで引数を与えらるようにしようと思った。

Perl とかで書けば書くのは簡単だけど、実行環境が Perl 必須になってしまう。まあ、記述がややこしくなってメンテナンスの手間が増えてしまうようだと本末転倒なのでその時は割りきって Perl で書くことにしてしまおう。

外部コマンドの呼び出しがやたらと増えてしまう場合も同様だ。それなら Perl の方がましだろう。標準コマンドかそうでないかの違いはあるが。

(もちろん、これは閉じた環境で、外部からアクセス可能なところに置くものではない。なので、脆弱性の話は除外して考えていることをお断りしておく。)

シェルスクリプトを書くのも久しぶりだ。だいぶ忘れている。

まずは仕様の整理。

  1. パラメータ文字列は環境変数 QUERY_STRING で渡される。
  2. パラメータ文字列は ‘&’ で区切られており、複数のパラメータが指定可能。
  3. 各パラメータは名前と値が ‘=’ で連結されている。
  4. 今回作成する CGI はパラメータ名 ‘target’ のみを受け付け、それ以外は無視する。
  5. パラメータ ‘target’ は複数指定でき、値は CGI 内部から実行する別のコマンドの引数として渡す。
  6. パラメータの値はデコードしない。空の場合は無視する。

ということで、最初に ‘&’ を区切り文字として分解する部分。

区切り文字といえば、シェル変数 IFS がどーたらという話があった記憶があるが、どう使うのだったか覚えていない。
標準入力を分割するのに使えるのだった記憶はあるのだが…。

ググると、for 文と組み合わせる例が見つかった。それを参考に記述。

IFS='&'
for i in $QUERY_STRING; do
(変数 $i には & で切り分けられた内容が順に入る)
done

次は ‘=’ で名前と値に分割する部分。これはパラメータ展開でプレフィックスやサフィックスを削除する ‘%’ や ‘##’ が使える。

key=${i%%=*}
value=${i#*=}

‘%%’ は後方から最長一致でマッチした部分を取り除く。つまり、’%%=*’ で最初に出現した ‘=’ 以降の文字を取り除くことになる。’#=’ は前方から最短一致でマッチした部分を取り除く。つまり、’#*=’ で最初に出現した ‘=’ までの文字を取り除くことになる。

結果は位置パラメータ ’$@’ に格納して、外部コマンド呼び出し時に展開するのが良いだろう。位置パラメータ ‘$@’ の初期化は “shift $#” のようにするらしい(’$#’ は位置パラメータの個数)。

shift $#
for i in ...; do
...
value=${i#*=}
set "$@" "$value"
done

これで for ループが終了したあとには、位置パラメータに値がセットされている。

まとめると、下のように。

OLDIFS=$IFS
IFS='&'
shift $#
for i in $QUERY_STRING; do
key=${i%%=*}
value=${i#*=}
case "$key" in
target)
case "$value" in
-* | *=* | '')
;;
*)
set "$@" "$value"
;;
;;
esac
done
IFS=$OLDIFS
sh exec-make.sh "$@"

必要な処理が終わったあとは IFS は元の値に戻しておく。間違えるとややこしいので。

重ねて書くが、このスクリプトは内部の閉じた環境からのみアクセスする前提で書いているので、脆弱性への対策は行なっていない。シェルスクリプトの記述の事例として読んでもらうのは良いが、CGI の事例としては読まないことをおすすめする。入力を解析する必要のある CGI なら、シェルスクリプトではなく、ちゃんとしたライブラリとかの支援がある言語環境で書いたほうが良いに決まっている。

今回のスクリプトでは使わなかったが、if 文で変数の内容を比較する際に、変数と比較対象の頭にそれぞれ文字 ‘x’ をつけて比較する、という記述を見かける。

if [ x"$hoge" = xyes ]; then ...

なんでだったか忘れたので、これもググった。

$hoge が ‘-f’ とかだった場合に test(1) のオプションとみなされることを防ぐためらしい。

手元の環境 (NetBSD 5.1.2) で試してみたら、”test -f = -f” の “-f” はオプションとは解釈されないようだ(test(1) も シェル組み込みも)。引数の解釈の順序が先頭から順じゃないのかな?

Apache2.4

pkgsrc から Apache-2.4.3 (pkgsrc/www/apache24) をインストールした。

Apache-2.2.x といろいろ変わっていて戸惑う。必要なときにしかいじらないので、ちょっとしたデフォルト設定の変更だけでもつまづきの元だ。解説してくれてるページがあると良いのだが。

とりあえず、気づいたところだけでもメモしておく。

  • NameVitualHost が不要になった。<VirtualHost> ディレクティブだけで良い、ってことかな?このあたりの解説が見当たらないのでよくわからない。
  • デフォルトの httpd.conf で / に “Require all denied” が設定された。いくつかのディレクトリには “Require all granted” が設定されているものの、独自のディレクトリを追加する場合は “Require all granted” を設定しなければならない。
  • デフォルトの httpd.conf で mod_rewrite の読み込みがコメントアウトされた。
  • mod_cgi がなくなった(NetBSD の pkgsrc でデフォルト設定でインストールした場合)。mod_cgid をつかうようだ。
  • 起動時に “Failed to enable the ‘httpready’ Accept Filter” という warning が出る。FreeBSD ではカーネルモジュール accf_http.ko を読み込むと Accept Filter とやらが動作するそうだけど、NetBSD にはないよね?

    Apache 2.4 のドキュメントには Linux と FreeBSD のみサポートするとなっているのに、なんで組み込まれてしまっているのか。

    設定ファイル群を grep -i accept しても関連しそうな設定が見当たらないし、どうしたら無効にできるのだろう?

Astec-X と Emacs

Emacs-24.2 を Astec-X 7.0 で表示させようとすると Bad Request で異常終了する。

Astec-X のコントロールパネルで、拡張機能 RENDER のチェックを外したら異常終了しなくなった。

XSLT2.0 で XHTML をコピーしたときに余計な名前空間が出ないようにする

久しぶりに NetBSD に Java 処理系をインストールしようとしたら、OpenJDK なるものが(pkgsrcにも)存在することを知った。

このあたりの事情には全く疎かったのだが、今やSunJDKの正式な後継で、かつオープンな Java 処理系という理解で良いのかな?

オープンになったということで、NetBSD でもネイティブに動作するようになっているようだ。ということはLinux エミュレーションは不要ということか。それはめでたい。

そもそも、なんで Java をインストールしようとしていたか(Java のプログラムなど書かないのに)というと、単に XSLT の処理系を動かしたくて、それが Java で書かれているってだけの理由だ。

これまで XSLT の処理系は Xalan というのを
Exslt という XSLT の拡張と組合せて使っていた。Xalan はApache Software Foundation の処理系ということで、なんとなく標準に近そうなイメージで選んでいたのだが、近年は開発が止まっているように見える。同様に、Exslt の開発も止まっている。

今時は何を使うのだろうと思って調べてみると、Saxon とかいうのがよく使われているらしい。

で、OpenJDK とともに Saxon を使ってみた。使用するスタイルシートは Xalan + Exlt 用に書いたもの。

結果は、Exlt は不要、というか、Saxon では Exlt はエラーが出て動かなかった。まあ、Exslt で拡張した関数は、同等のものが組み込みで提供されるので必要なくなっている。

多少の書き換えが必要だったものの、昔のスタイルシートを Saxon で動作させることができた。印象としては、機能が大幅に拡張され、厳密性のチェックは少し厳しくなったような感じ。

ちょっと困ったのが、XML ソースにある XHTML のノードツリーをコピーすると、空の名前空間宣言がついてしまうこと。

<div>
<p>これは<a href="#">サンプルテキスト</a>です。<br />
改行しました。</p>
</div>
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output
method="xhtml" version="1.0" encoding="utf-8"
indent="yes" omit-xml-declaration="yes" />
<xsl:template match="/">
<html>
<head><title>タイトル</title></head>
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="@*|text()">
<xsl:copy />
</xsl:template>
</xsl:stylesheet>
$ openjdk7-java -jar saxon9he.jar -xsl:test.xsl -s:test.xml -o:test.html
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>タイトル</title>
</head>
<body>
<div xmlns="">
<p>これは
<a href="#">サンプルテキスト</a>です。
<br></br>
改行しました。
</p>
</div>
</body>
</html>

見ての通り、ソースからコピーしたノードツリーのトップ div 要素の属性に空のデフォルト名前空間が出力されてしまっている。また、本来短縮形であるはずの br 要素が長形式 “<br></br>” になってしまっている。

IBM のページによれば、こういう時は xsl:copy ではなく xsl:element を使うのが正しいらしい(XSLT2.0の場合)。

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output
method="xhtml" version="1.0" encoding="utf-8"
indent="yes" omit-xml-declaration="yes" />
<xsl:template match="/">
<html>
<head><title>タイトル</title></head>
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:apply-templates select="@*" />
<xsl:apply-templates select="node()" />
</xsl:element>
</xsl:template>
<xsl:template match="@*|text()">
<xsl:copy />
</xsl:template>
</xsl:stylesheet>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>タイトル</title>
</head>
<body>
<div>
<p>これは<a href="#">サンプルテキスト</a>です。<br />
改行しました。
</p>
</div>
</body>
</html>

不要な名前空間はなくなり、br 要素も正しく短縮形 “<br />” となった。

pkgsrc/inputmethod/anthy-elisp

pkgsrc から emacs-24.2 (pkgsrc/editors/emacs24) をインストールしたら anthy-elisp (pkgsrc/inputmethod/anthy-elisp) がインストールできなかった。

インストール時に leim-list.elc がないとかなんとか。

ググってみると最近の emacs では leim-list.elc を(少なくとも pkgsrc では)作らないように なっているらしい。

仕組みはよくわからないが、PLISTを修正してインストール

--- PLIST       5 Aug 2009 07:01:05 -0000       1.6
+++ PLIST       23 Oct 2012 06:22:52 -0000
@@ -12,4 +12,4 @@
${EMACS_LISPPREFIX}/anthy/anthy.el
${EMACS_LISPPREFIX}/anthy/anthy.elc
${EMACS_LISPPREFIX}/anthy/leim-list.el
-${NOTFOR_emacs23}${NOTFOR_emacs22}${NOTFOR_emacs22nox}${EMACS_LISPPREFIX}/anthy/leim-list.elc
+${NOTFOR_emacs24}${NOTFOR_emacs23}${NOTFOR_emacs22}${NOTFOR_emacs22nox}${EMACS_LISPPREFIX}/anthy/leim-list.elc

Winbind (4)

winbind が遅い件。条件を変えて色々と試しているうちに、グループの取得に時間がかかっているような気がしたので ‘winbind expand groups’ を 0 にしてみた(デフォルトは 1)。

[global]
...
winbind expand groups = 0
...
$ time id (ADドメイン)_(ユーザ)
uid=10003((ADドメイン)_(ユーザ)) gid=10006((ADドメイン)_domain users) groups=10006((ADドメイン)_domain users),...(略)...,10001(BUILTIN_users)
real    0m0.056s
user    0m0.010s
sys     0m0.000s

うん、早くなった。実用速度だ。ADグループを展開しないことで何か不都合があるかどうかはわからないけれども、これで良いことにしよう。

Winbind (3)

testparm で warning が出たので、’winbind separator = _’ に修正。

'winbind separator = +' might cause problems with group membership.

man によると glibc などを使ってるシステムの NIS で問題が出るそうだ。

Please note that setting this parameter to + causes problems with
group membership at least on glibc systems, as the character + is
used as a special character for NIS in /etc/group.

症状には変化なし。

現時点のまとめ

  • id (ユーザ) がすごく遅い。10分以上かかる。または検索に失敗する。
  • getent passwd (ユーザ) も遅かった。
  • Windows から Samba への接続は一度成功すると普通に使えてる。
  • ls -l とかもすごく遅いが、一旦成功するとしばらくは普通に使える。
  • login (ユーザ) は即座に Segmentation fault。ADドメイン以外のユーザでは正常。

Winbind (2)

id が通る場合と失敗する場合。どちらも同じユーザに対する検索。winbind separater = + に設定。

$ time id (ADドメイン)+(ユーザ)
uid=10000((ADドメイン)+(ユーザ)) gid=10006((ADドメイン)+domain users) groups=10006((ADドメイン)+domain users),10007,10008,10009,10010,10001(BUILTIN+users)
real    11m41.455s
user    0m0.000s
sys     0m0.000s
$ time id (ADドメイン)+(ユーザ)
id: (ADドメイン)+(ユーザ): No such user
real    1m45.208s
user    0m0.001s
sys     0m0.001s

成功する場合で11分、失敗するときはもっと短い。

Winbind

Windows Server (Active Directory, AD) との連携を試してみた。

pkgsrc (2012Q2) から Samba (net/samba, samba-3.6.5) をインストール。

$ sudo net ads join -U (ユーザ)
...
Failed to join domain: failed to set machine spn: Constraint violation

エラー。

ドメインに参加させようとしているホストと、AD のドメインが一致していないとダメらしい。

AD の DNS への登録権限は持ってないので、/etc/hosts に適当に追加。

今度は

Client not found in Kerberos database

適当に /etc/krb5.conf に AD の realm の設定をして kinit の実行したら net ads join も成功した。こんな手順なのか?ネットで調べたと若干違うような気がするけど、再現できないからよくわからない。AD に登録されたホストを消す方法も不明(ADへのログオン権限ないので)。

winbindd を起動して、まずは動作確認。

$ wbinfo -t
checking the trust secret for domain (ADドメイン) via RPC calls succeeded
$ wbinfo -a (ADドメインユーザ)
Enter (ADドメインユーザ)'s password:
plaintext password authentication succeeded
Enter (ADドメインユーザ)'s password:
challenge/response password authentication failed
error code was NT_STATUS_WRONG_PASSWORD (0xc000006a)
error message was: Wrong Password
Could not authenticate user (ADドメインユーザ) with challenge/response

AD への接続は O.K.、plain text のパスワード認証は O.K.、challenge の認証がこけている。調べたけれど類似の事例が見つからず。とりあえず先に進んでみる。

/etc/nsswitch.conf に winbind を追加して接続…失敗。

$ id (ADドメインユーザ)
id: (ADドメインユーザ): No such user

winbind のログを見ても、どうなっているのかさっぱり不明。ユーザを検索しているように見えない。

ktrace しろとのお告げに従って、

$ ktrace id (ADドメインユーザ)
...
$ kdump
...
804      1 id       CALL  open(0xbfbfe038,0,0)
804      1 id       NAMI  "/usr/lib/nss_winbind.so.0"
804      1 id       RET   open -1 errno 2 No such file or directory
...

あれれ、失敗しているよ。

$ ls -l /usr/lib/nss_winbind.so.0
lrwxr-xr-x  1 root  wheel  29 Oct  2 11:08 /usr/lib/nss_winbind.so.0 -> /usr/pkg/lib/nss_winbind.so.0
$ file /usr/lib/nss_winbind.so.0
/usr/lib/nss_winbind.so.0: broken symbolic link to `/usr/pkg/lib/nss_winbind.so.0'

???

$ ls -l /usr/pkg/lib/nss_*
-rwxr-xr-x  1 root  wheel    39022 Oct  2 11:07 /usr/pkg/lib/nss_winbind.so
-rwxr-xr-x  1 root  wheel  1437639 Oct  2 11:07 /usr/pkg/lib/nss_wins.so

おや、まあ。

$ cd /usr/pkg/lib
$ sudo ln -s nss_winbind.so nss_winbind.so.0
$ getent passwd (ADドメインユーザ)
(ADドメインユーザ):*:10000:10006::/home/(ADドメイン)/(ユーザ):/bin/false

まずは一段階クリア。

$ id (ADドメインユーザ)
...反応なし

問合せに時間がかかっているのか、ハングアップしているのかどっちかわからん。

放置していたら、成功してた。でも時間を計ろうと time id を実行したら 2 分ぐらいして No such user を返してきた。

どっちにしてもこのままでは使い物にならないな。設定で何とかなるんだろうか。