新山の発表内容
set no_fork
set stdout
基本的には、あるツールを daemontools-aware にするのは さほどむずかしくない。
新山の註釈つきソースを使用:
djb 特有の関数:
研究室の学生に好き勝手に CGI をつかわせたい。 でも publicfile/shttpd がいい。shttpd はいつも決めうちで 同じ cgi しか実行してくれないという欠点がある。
しかし shttpd が環境変数 PATH_INFO をセットするので、これを見て apache の suexec のようなことをやってみた。psuexec という setuid root されたプログラムを実行させ、各ユーザの cgi に分岐させるのだ。
けれどもこれは注意する必要がある。 それにリソースを使われすぎないようリミットをかけねばならない。
#!/bin/perl -T
#!/bin/jperl -T
にしてください (taint check)。
int main(int argc, char* argv[], char* envp[])
{
// 変数定義は略
http = 0;
proto = getenv("SERVER_PROTOCOL");
if (proto) {
if (!strncasecmp(proto, "http/1.0", 8))
http = 1;
if (!strncasecmp(proto, "http/1.1", 8))
http = 2;
}
// パスを取得・ながすぎたらもうダメ
p1 = getenv("PATH_INFO");
if (!p1)
{ fprintf(stderr, PROGNAME "PATH_INFO isn't set.\n"); ab(-1); }
if (BUFFSIZE < strlen(p1)) {
fprintf(stderr, PROGNAME "PATH_INFO too long: %d\n", strlen(p1));
ab(-2);
}
// プレフィクスが違ってらダメ
if (strncmp(p1, PATH_PREFIX, STRLEN(PATH_PREFIX))) {
fprintf(stderr, PROGNAME "PATH_INFO malformed.\n");
ab(-3);
}
if (strlen(p1) < 4 ||
strncmp(p1+strlen(p1)-STRLEN(CGI_SUFFIX),
CGI_SUFFIX, STRLEN(CGI_SUFFIX))) {
fprintf(stderr, PROGNAME "illegal extension: %s\n", p1);
ab(-4);
}
p1 += STRLEN(PATH_PREFIX);
for(i = 0; i < 8 && *p1 && *p1 != '/'; i++)
username[i] = *(p1++);
username[i] = '\0';
sprintf(s, HOME_DIR "/%s", username);
// そのユーザのディレクトリがなかったらダメ
if (i == 0 || stat(s, &buf)) {
fprintf(stderr, PROGNAME "not found: %s\n", s);
ab(-5);
}
// そのディレクトリがそのユーザの所有でかつ uid,gid が 1000以上
if (buf.st_uid < UID_MIN || buf.st_gid < GID_MIN) {
fprintf(stderr, PROGNAME "illegal uid or gid.\n");
ab(-6);
}
// 他人から見えてたらダメ
if ((buf.st_mode & 0777) != (S_IRUSR | S_IWUSR | S_IXUSR)) {
fprintf(stderr, PROGNAME "illegal directory permission: %x\n",
buf.st_mode);
ab(-7);
}
// そのディレクトリのuid,gidになる
if (setregid(buf.st_gid, buf.st_gid) ||
setreuid(buf.st_uid, buf.st_uid)) {
fprintf(stderr, PROGNAME "can't change user/group.\n");
ab(-8);
}
(中略)
// そのディレクトリに chdir する
if (chdir(s)) {
fprintf(stderr, PROGNAME "can't chdir to %s\n", s);
ab(-9);
}
// プログラムがあって、所有者か?
base++;
if (stat(base, &buf)) {
fprintf(stderr, PROGNAME "not found: %s\n", base);
ab(-10);
}
if (buf.st_uid != getuid() ||
buf.st_gid != getgid()) {
fprintf(stderr, PROGNAME "not owner: %s\n", base);
ab(-11);
}
// ふつうのファイルか?
if (! S_ISREG(buf.st_mode)) {
fprintf(stderr, PROGNAME "illegal file type: %s\n", base);
ab(-12);
}
setenv("PATH", CONST_PATH, 1);
execv(base, argv);
i = errno; ab(i);
}
あるいは ディレクトリ は cdb?
djb がネットワークプロトコルを作ったら?