筑波大学 システム情報工学研究科 コンピュータサイエンス専攻, 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2007/No10.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2007/
http://www.coins.tsukuba.ac.jp/~yas/
f() { char buf[BUFFERSIZE]; snprintf(buf,sizeof(buf),"hello"); return( buf ); // 誤り }ローカル変数用のメモリは、関数から return したら、別の関数が利用する。
main() { f(); g(); }
図? ローカル変数用のメモリ
関数から文字列を return する方法f() { auto int x;// auto は省略可能。 ... }前半の「バッファオーバーフロー」の説明参照。
図? アクセス制御における主体、オブジェクト、および、操作
Unixは、マルチユーザのシステムなので、ユーザ1人ひとりを認識するような アクセス制御の仕組みを持っている。たとえば、Unix では、共同プロジェク トに関連したファイルは、他のユーザにも見せてもよいが、個人の電子メール は、他の人には見せないといった制御ができる。
これに対して、古いパソコン用のOS(Windows 95/98/ME, MacOS 9以下) では、このような複数のユーザを識別したアクセス制御は、できない。 Windows NT, Windows 2000, Windows XP, MacOS X は、可能である。
ファイルの「内容」のアクセス3段階であるが、ファイルの「属性」次の2段 階である。
WWW ページのアクセス制御、
アクセス制御の方針(ポリシー)は、サーバ側で既述される。 この時、次のような情報がよく使われる。
アクセス制御は、基本的には、WWWサーバの管理者の仕事である。
WWWサーバ (httpd) の設定ファイル
(
/usr/local/etc/httpd/conf/httpd.conf
,
/var/www/conf/httpd.conf
など
)
に記述される。
個人のページについては、
.htaccess
というファイルを作れば、個人で変更できるようになっている場合がある。
order deny,allow deny from all allow from 130.158.0.0/16 133.51.0.0/16 192.50.17.0/24
例:パスワード・ファイル
/home/lab/Denjo/yas/etc/passwd-doc1
に登録されているユーザだけが、
.htaccess
があるディレクトリ以下にあるファイルをアクセスできる。
AuthType Basic AuthName "Members Only" AuthUserFile /home/lab/Denjo/yas/etc/passwd-doc1 require valid-userこのパスワード・ファイルは、htpasswd というプログ ラムで作る。
% ls /home/lab/Denjo/yas/etc/passwd-doc1一番最初は、ls: /home/lab/Denjo/yas/etc/passwd-doc1: No such file or directory % htpasswd -c /home/lab/Denjo/yas/etc/passwd-doc1 user1
Adding password for user1. New password:user1のパスワードを打ち込む
Re-type new password:user1のパスワードを打ち込む
% htpasswd /home/lab/Denjo/yas/etc/passwd-doc1 user2
Adding user user2 New password:user2のパスワードを打ち込む
Re-type new password:user2のパスワードを打ち込む
% cat /home/lab/Denjo/yas/etc/passwd-doc1
user1:1fjr1tHIgoG7U user2:qXaeA9Zge7Yqc %
![]()
-c
オプション付で実行する。passwd
コマ
ンドと同様に、打ち込んだパスワードは、画面には表示されず、また、確
認のために2回打つ必要がある。
htpasswd コマンドの結果、次のようなファイルが作られる。
ユーザ名と暗号化されたパスワード(ハッシュ値)から構成される。 このファイルは、~/public_html 以外の場所に置くと GET で盗まれることはない。user1:1fjr1tHIgoG7U user2:qXaeA9Zge7Yqc
このアクセス制御が有効なファイルをクライアントが GET したとする。
すると、サーバから次のようなエラーが返される。GET /dir1/file1.html HTTP/1.0![]()
![]()
HTTP/1.1 401 Authorization Required WWW-Authenticate: Basic realm="Members Only" Content-Type: text/html <HTML><HEAD> <TITLE>401 Authorization Required</TITLE> </HEAD><BODY> ... </BODY></HTML>
.htaccess
の AuthType
と AuthName
の内容が、HTTP の応答の WWW-Authenticate:
に現れる。
このページをアクセスするには、クライアント(WWWブラウザ)は、ウインドウ を開いてユーザにユーザ名とパスワードを要求する。そして、次のような GET 要求をもう一度送る。
GET /dir1/file1.html HTTP/1.0Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
![]()
![]()
Authorization:
には、ユーザが打ち込んだユーザ名とパスワードが
Base64 (64進数)で符合化されて含まれている。
これを受け取ったサーバは、
AuthUserFile
で指定されたファイルを開いて、ユーザ名とパスワー
ドパスワードを照合して、正しければアクセスを許する。
Base64 は、「暗号ではない」ので、簡単に元にもどせる。
% echo QWxhZGRpbjpvcGVuIHNlc2FtZQ== | openssl base64 -dAuthType Basic を使うアクセス制御の方法では、パスワードがそのままネッ トワークを流れてしまうので、盗聴に弱い。Aladdin:open sesame%
![]()
情報学類コンピューティング環境/web環境
https://www.coins.tsukuba.ac.jp/ce/index.php?web%B4%C4%B6%AD
この機能を使う時には、SSL 付きのサーバ (http: ではなく https:) を使うこと。
プロセスはインタプリタの実行形式(機械語)から作られ、インタプリタのソー ス・プログラムは、そのプロセスが読み込む単なるデータとなる。
Unix には、スクリプトを簡単に実行する仕組みとして #!がある。
% cat > run-cat「catインタプリタ」用のプログラムを実行する。#!/bin/cat
hello
^D % chmod +x run-cat
% ls -l run-cat
-rwxr-xr-x 1 yas 20 Sep 13 04:11 run-cat %
![]()
% ./run-catこれは次のように実行したものと同じになる。#!/bin/cat hello %
![]()
% /bin/cat ./run-cat#!/bin/cat hello %
![]()
インタプリタの実行形式の絶対パス名: /dir/interpreter
そのソース・プログラムのファイル名: run
run
の内容:
#!/dir/interpreter arg-1 arg-2 arg-3 <以下、プログラム>このファイルに実行可能属性を付ける(
chmod +x
)と、
ファイル名を入力して実行することができる。
% chmod +x ./runこれは、次のようにインタプリタを起動したものと同じ結果になる。% ./run arg-a arg-b arg-c
<実行結果>
% /dir/interpreter ./run 'arg-1 arg-2 arg-3' arg-a arg-b argc![]()
./run
ファイルに書いた引数は、シェルから実行する時場合には、
''で囲まれた時と同じような形式でインタプリタのプロ
セスへの引数として渡される。
#!
」行は、Unix のカーネルが解釈する。
シェルが、ファイルの先頭を読み、指定されたインタプリタを起動するのでは
ない。「#!
」行では、シェル変数、環境変数、エイリアスはつ
かえない。
スクリプト言語のプログラムでは
「#
」から始まる行がコメントであると都合がよい。
awk
や sed
のように、プログラムが含まれたファイルを指定
する時に -f
(program file) オプションが必要なものは、次の
ように、この行に -f
を付ける。
#!/bin/awk -f { print }
シェル・スクリプトの先頭の
#!/bin/sh
や
#!/bin/csh
は、
そのインタプリタを起動するという意味である。
tcsh は、csh に terminal での編集機能や補完機能を付けたもの。シェル・ スクリプトを書く時には、多くのシステムで備わっている /bin/csh を使うこ とが多い。
% gcc -I/usr/local/include/ file1.c -llib1 -llib2 -o progtcsh の機能で、^p (Control+P) で1行戻して、echo でファイルに落とす。 「|」があれば、'' でくくる。csh の引数にファイル名を与えて実行できる。(^p で1行もどす。^a で、行頭に移動して echo と打ち、^e して > run と打つ) % echo gcc -I/usr/local/include/ file1.c -llib1 -llib2 -o prog > run
% csh run
%
![]()
いちいち csh と打たないでもいいようにするには、chmod する。
% chmod +x run#! がなければ、execve() システム・コールがエラーになる。その場合、tcsh が sh か csh (先頭が#の時)を実行する。% ./run
%
![]()
必要なら、エディタで「#!/bin/csh」か「#!/bin/sh」を入れる。
数行にわたるものの場合、history コマンドを使う。
% history% history | tail -5 > run
% emacs run
![]()
「#!/bin/csh -f」と、-f を付けた方が、~/.cshrc を読み込まないので起動 が速い。ただし、~/.cshrc での設定(aliasなど)は効かないことがある。環境 変数は、今の状態が引き継がれる(~/.cshrc を読み込ませない方が都合がよい ことが多い)。
% csh -f -x run![]()
-n オプションを付けて実行して、構文のチェックだけ行う。
シェルに1行ずつ与えて実行してみる。
~/.cshrc
などを設定して、~/bin
をpath シェル変数(PATH
環境変数)に含まれるようにすることを奨める。そして自分で作成したプログ
ラムやスクリプトを、~/bin
に置くと./
などで実行する必
要はない。
ただし、ファイルを作成し、chmod +x した後で、1度だけ rehash コマンド
を打つ必要がある。
% emacs ~/bin/newcommandchmod も rehash も、シェルごとに1度だけやればよい。端末をたくさん開い ていた時には、作成したスクリプトをすぐに使いたい時にはそれぞれのシェル でrehash コマンドを実行する。% chmod +x ~/bin/newcommand
% rehash
% newcommand
% emacs ~/bin/newcommand
% newcommand
% emacs ~/bin/newcommand
% newcommand
%
![]()
rehash の意味は、ハッシュ表を作り直すことである。path にあるコマンドは、 csh は、コマンドを打つたびに探すのではなくてハッシュ表に入れてそれを検 索している。
rehash は、新しいシェルが実行される時には自動的に行われている。次にロ グインした時、chmod +x した後に開いた端末ではrehash を実行する必要はな い。
% ls強制的に縦に並べるオプション ls -1 や横に並べるオプション ls -C もある。file1 file2 file3 % ls | cat
file1 file2 file3 %
![]()
UTMP_FILE
」
または、「WTMP_FILE
」という文字列を含む行を、ファイル
/usr/include/utmp.h
から探し、結果を表示している。
% egrep '_PATH_[UW]TMP' /usr/include/utmp.h一般的には、次のようになる。#define _PATH_UTMP "/var/run/utmp" #define _PATH_WTMP "/var/log/wtmp" %
![]()
% egrep pattern file1 file2 ...![]()
pattern
としては、次のような正規表現(regular
expressions)が使える。
-v オプションを付けると、マッチしなかった行が表示される。
-s オプションを付けると、パタンを含むか含まないかの判定だけを行い、画 面に結果を表示しない。シェル・スクリプトの if 文の中で使う。
「*」、「$」、「[]」など、シェルが展開してしまう文字をパタンとして指定 する時には、シングル・クォート「' '」で括る。
egrep は、機能が拡張され、アルゴリズムも改良されていて速い。
1: #!/usr/bin/ruby 2: # cgi-printarg-ruby.cgi -- CGI プログラムに対する引数を表示するプログラム 3: # ~yas/syspro/www/cgi-printarg-ruby.cgi 4: # Created on 2005/06/27 01:52:36 5: 6: require "cgi" 7: 8: def main() 9: $SAFE = 1 10: @cgi = CGI.new() 11: print_header() 12: print_content() 13: exit( 0 ) 14: end 15: 16: def print_header() 17: printf("Content-Type: text/html\n") 18: printf("\n") 19: end 20: 21: def print_content() 22: printf("<HTML><HEAD></HEAD><BODY><PRE>\n") 23: printf("request_method: %s\n",e(@cgi.request_method)) 24: printf("script_name: %s\n",e(@cgi.script_name)) 25: printf("query_string: %s\n",e(@cgi.query_string)) 26: printf("content_length: %d\n",@cgi.content_length) 27: qh = @cgi.keys 28: i = 0 29: qh.each { |name| 30: val = @cgi[name] 31: printf("qv[%d]: %s=%s \n",i,e(name),e(val) ) 32: i = i + 1 33: } 34: printf("</PRE></BODY></HTML>\n") 35: end 36: 37: def e( str ) 38: return( str == nil ? "" : CGI::escapeHTML(str) ) 39: end 40: 41: main()
Ruby では、require で、必要なライブラリを読み込む。 def から end までがメソッドの定義である。
main() では、CGI.new() により、CGI クラスのインスタンスを生成している。 その結果を @cgi という変数(インスタンス変数、mainの外でも使える)に保存 している。
このプログラムでは、main() という名前のメソッドを定義しているが、、 main() というメソッドから実行が開始させるわけではない。メソッド定義で はないものは、即座に実行される。このプログラムは最後に main() を呼び出 す文がある。これを忘れると何も実行されない。
$SAFE は、グローバル変数である。Ruby では、ファイルからの入力や環境変 数は汚染されたものとして扱われる。$SAFE を 1 にすると、汚染された文字 列でファイルを開くとエラーになる。標準では、0 。安全を確認したら、 obj.untaint() メソッドで汚染を解除する。明示的に obj.taint() で汚染さ せることもできる。
print_header()では、HTTP のヘッダのうち、Content-Type: 行だけを 出力している。
print_content() では、本文を出力している。
関数(メソッド) e() では、CGI ライブラリ (CGIクラス)の CGI::escapeHTML() を呼び出して、安全なものにして表示する。 たとえば、「<」は、「<」と変換している。こ れで、<SCRIPT> のような危険なスクリプトが送り込まれたとし ても「<SCRIPT>」と表示と表示されるだけで、スクリプト は実行されない。
@cgi に保存されたCGI クラスのインスタンスの request_method() メソッド を呼び出すと、"GET" か "POST" が返される。環境変数は、ハッシュ表 ENV に対して ENV['REQUEST_METHOD'] のようにしてもアクセスできるが、環境変 数を CGI クラスで変更してしまうこともあるようである。
@cgi.keys により、パラメタの一覧が配列の形で得られる。配列の各要素につ いて(qh.each)、パラメタ名を得て表示している。この例では、どんなパラメ タでも表示しているので、このようなループになっているが、通常の CGI プ ログラムでは、@cgi['パラメタ名'] のようにして、パラメタの値を文字列と して取り出すだけでよい。
表示例:
実行例:
request_method: GET script_name: /~yas/coins/syspro-2007/No10_files/cgi-printarg-ruby.cgi query_string: lastname=arg1&firstname=arg2&sex=Male&email=arg3 CONTENT_LENGTH: qv[0]: firstname=arg2 qv[1]: lastname=arg1 qv[2]: sex=Male qv[3]: email=arg3
request_method: POST script_name: /~yas/coins/syspro-2007/No10_files/cgi-printarg-ruby.cgi query_string: CONTENT_LENGTH: 48 qv[0]: firstname=arg2 qv[1]: lastname=arg1 qv[2]: sex=Male qv[3]: email=arg3
% echo "a=b&c=d" | ./cgi-printarg-ruby.cgiContent-Type: text/html <HTML><HEAD></HEAD><BODY><PRE> request_method: script_name: query_string: CONTENT_LENGTH: qv[0]: a=b qv[1]: c=d </PRE></BODY></HTML> %
![]()
% idここで、uid は、getuid()、 gid は、getgid()、groups は、getgroups() シ ステムコールの結果である。getgroups() では、複数の GID が返される。uid=1013(yas) gid=40(lab) groups=40(lab),510(softadm),500(jikken3) %
![]()
% mychmod 755 filenameモードは、8進数で与えるものとする。引数として取ることができるファイル 名は、1つだけでよい。![]()
% mytouch filenameこの結果、ファイルの最終更新時刻が現在の時刻になる。![]()
ヒント:time(2) と utime(2) を使う。
ヒント:stat(2)システムコールで、コピー元ファイルとコピー先ファイル最 終更新時刻を調べる。もし、前者が新しければ、コピーする。後者が新しけれ ば、なにもしない。
この課題では、次のような属性を保存しなさい。
コピーする時に、内容をコピーした後に、コピー元のファイルに stat() を実 行する。こうして得られた属性を、chmod() や utime() でコピー先のファイ ルに設定する。
特定のホスト(たとえば、azalea10)からだけしかアクセスできないような .htaccess を定義しなさい。
注意:ユーザ名は、長くてもよい。Unix や Windows のユーザ名とは別のもの でよい。
注意:ここで設定したパスワードは、ネットワーク上を暗号化されずに流れる。 非SSLのWebサーバで 実験する時には、決して Unix や Windows のログインのパスワードと 同じものを使ってはならない。
% ls-c ~/syspro/file/ヒント:ls の結果から、egrep で .c や .h で終わるものを抜き出す。ある いは、シェルのパタン・マッチの機能を使って、*.c や *.h だけを取り出す。fd-print.c file-copy.c mmap-head.c stdio-thru.c utmp-print.c wtmp-last10.c ystat.c ystat.h
余裕があれば、-l などのオプションが付けられるようにしなさい。
% ls-dot ~ヒント:ls に -a オプションを付けると、全てのファイルを表示する。 先頭が「.」のものを抜き出す。. .. .cshrc .emacs .login %
![]()
csh, tcsh, sh で 「.*」と指定して探す方法もある。
余裕があれば、-l などのオプションが付けられるようにしなさい。
% ls-dir ~ヒント:ls -l で、d から始まる行だけを抜き出す。あるいは、csh の -d や test の -d でディレクトリだけを選びだす。Mail lib syspro tmp %
![]()
できるだけ上のようにディレクトリの名前だけ表示するようにしなさい。 モードや日付は表示しないようにしなさい。
余裕があれば、-l などのオプションが付けられるようにしなさい。
% ls-size ~yas/syspro/wwwヒント:ls -l の出力を sort コマンドでソートする。 ソートは、第5フィールドで行う。-rwxr-xr-x 1 yas prof 27056 Jun 12 11:54 cgi-printarg.cgi -rwxr-xr-x 1 yas prof 18376 Jun 19 01:29 cgi-hello.cgi -rw-r--r-- 1 yas prof 5287 Jun 12 11:54 cgi-printarg.c -rw-r--r-- 1 yas prof 1579 Sep 26 2002 get.c -rwxr-xr-x 1 yas prof 985 May 20 17:17 cgi-printarg-ruby.cgi -rw-r--r-- 1 yas prof 486 Jun 11 21:20 cgi-hello.c -rw-r--r-- 1 yas prof 297 Sep 22 2003 Makefile drwxr-xr-x 5 yas prof 170 Jun 19 01:27 CVS -rw-r--r-- 1 yas prof 54 Feb 14 2004 data total 128 %
![]()
% wc-lines *.cこの課題では、合計(Total)は表示されなくてもよい。85 228 1836 proc-uid-print.c 75 187 1156 pipe-rw-dup.c 50 152 1141 vaddr-print.c 46 140 1071 proc-create.c 67 161 1014 pipe-rw-nodup.c 38 93 819 signal-int.c 50 98 802 setjmp-longjmp.c 32 90 561 run-n.c 27 66 535 home-print.c 20 50 424 cont-1.c 20 50 424 cont-0.c 25 60 419 t-system.c 20 40 384 exec-date.c 14 49 370 arg-print.c 15 44 355 env-print.c 12 32 305 cont-2.c 13 16 174 fork-hello.c 4 10 60 main-return.c %
![]()
ヒント:wc の出力を sort コマンドでソートする。foreach か for で1つず つ wc コマンドを実行して、全体の結果を sort するか、引数 $* で wc した 後、sort して、合計(Total)の行を削る。
% ps auxそのうち、メモリのサイズ(RSS)が大きいプロセスを 10 個だけ表示するシェル・ スクリプトを作りなさい。USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND root 1 0.0 -0.0 28340 540 ?? S<s 6Jun06 0:22.58 /sbin/laun root 52 0.0 -0.0 27256 156 ?? Ss 6Jun06 0:00.00 /sbin/dyna root 58 0.0 -0.2 30632 3536 ?? Ss 6Jun06 0:08.21 kextd root 91 0.0 -0.0 28240 624 ?? Ss 6Jun06 0:00.03 /usr/sbin/ ... root 52 0.0 -0.0 27256 156 ?? Ss 6Jun06 0:00.00 /sbin/dyna root 58 0.0 -0.2 30632 3536 ?? Ss 6Jun06 0:08.21 kextd root 91 0.0 -0.0 28240 624 ?? Ss 6Jun06 0:00.03 /usr/sbin/ root 92 0.0 -0.2 28360 3624 ?? Ss 6Jun06 9:41.29 /usr/sbin/ %
![]()
% ps-rss-top10ヒント:1行目は、そのまま表示する。 RSS の順(第6フィールド)に sort して head する。USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND root 239 0.0 -0.9 62428 18316 ?? Ss 6Jun06 2:16.88 /System/Li security 21102 0.1 -0.8 247056 15876 ?? S Sat04PM 5:17.79 /System/Li windowse 21096 0.1 -0.5 203808 11108 ?? Ss Sat04PM 4:32.14 /System/Li daemon 242 0.0 -0.4 73932 9280 ?? Ss 6Jun06 1:41.18 /System/Li root 115 0.0 -0.4 39260 7920 ?? Ss 6Jun06 1:50.21 /System/Li root 104 0.0 -0.2 34128 5100 ?? Ss 6Jun06 2:47.36 /usr/sbin/ root 92 0.0 -0.2 28360 3624 ?? Ss 6Jun06 9:41.81 /usr/sbin/ root 58 0.0 -0.2 30632 3536 ?? Ss 6Jun06 0:08.21 kextd root 132 0.0 -0.2 31460 3372 ?? Ss 6Jun06 1:21.59 /usr/sbin/ root 21094 0.0 -0.1 205696 2924 ?? Ss Sat04PM 0:00.19 /System/Li
余裕があれば、VSZ の順、CPU 時間の順に表示するスクリプトを作りなさい。
類似のことを実行するプログラムとして top がある。
% diff-backup *.cヒント:foreach または for 文で、引数のファイルについて、 "$file"~ のような名前のファイルが存在するかを調べる。 存在すれば、diff コマンドで表示する。![]()
余裕があれば、比較しているファイルの名前を表示したり、ファイルごとに停 止する、引数を取る、diff に対するオプションを取る、などの工夫をしなさ い。
% mv-lower [A-Z]*![]()
ヒント:ファイル名を echo して、tr で小文字にして、それを `` でシェル変数に入れる。元の名前から小文 字の名前に mv で変える。
余裕があれば、大文字と小文字を変換することで、ファイルが上書きされる時 には警告を出したり、ユーザに問い合わせたりするようにしなさい。
% countdown 5ヒント:sleep 1 で、1ごとに止める。5 4 3 2 1 0 %
![]()
% show-n-m 10 20 filename![]()
余裕があれば、-n オプションを付けなさい。これは、ファイルに行番号を振 るものである。nl コマンドを使うとよい。(cat -n が使えるシステムもある。)
ヒント:strtol() のように、文字列から整数値を得るには、Ruby では、 Integer() を使って次のように行う方法がある。
s = "100" i = Integer(s)