AIX: サービスの起動 ― 2007/02/14 10:37
ひきつづき AIX 5.3 であそんでいる。
SunOS 4.1.3 -> Solaris 2.x -> Debian Gnu/Linux と、ある意味UNIX としてメジャーな街道を歩いてきた身には、「これも UNIX?」と思うことが多い。
/etc/init.d や /etc/rc?.d がないので、はて、どこからサービスを起動したものかしらん?、と悩んでいたら、AIX は SRC (System Resource Controller)というのを使うんだそうな。
# startsrc -s sshd # stopsrc -s sshd
のようにすると、sshd の起動と停止が行える。
で、この startsrc / stopsrc がどの設定ファイルを読んでいるのかというと、どうも /var/adm/SRC/active_list であるそうな。sshd のエントリは下記のようになっている。
sshd _ -D /usr/sbin/sshd 0 0 /dev/console /dev/console /dev/console 1 0 2 0 0 20 15 9 1 20 ssh
/dev/console のあとの数字はなにゃらよくわからないけれど、ふーん。勉強になるなぁ。
AIX: ユーザー名の長さ ― 2007/02/13 18:50
ちょっと用事があって SSH on AIX で遊んでいる。ま、SSH 経由で AIX にユーザーアカウントを作りたいのだけれど、やっててはまったことに、AIX 5.2 ではユーザー名の長さはどうあがいても最大8文字まで。AIX 5.3 はデフォルトで 8文字まで。8文字以上255文字まで使いたい場合は設定を変更しなければならないということ。いきなり長いユーザー名を流し込んで「ユーザー名が長すぎます」と怒られてびびった。
で、どうせこのあたりの設定は /etc/security あたりにあるんだろう、と思って探し回ったが、ない。
しょうがないのでネットでいろいろ調べると、コマンドで操作するんですなぁ...
# lsattr -El sys0 -a max_logname max_logname 9 Maximum login name length at boot time True
lsattr というコマンドを使うらしい。上記例では、9文字がユーザー名の最大長になっている。
ちなみに変更するには chdev というコマンドを使うらしいのだが、遊んでいるのは借り物の環境なので、今回は試してない。
# chdev -l sys0 -a max_logname=21
しかし、設定するのと確認するのと、違うコマンドを使わなきゃならんなんて、なんていう設計するんだろう...
AIX のユーザー名に関する制限などは、このへんを参考にした。
コメント・スパムの切りかた? ― 2007/02/11 21:46
購入した本・CD・DVD -2007年2月 ― 2007/02/10 20:59
購入した本
- 入門OpenSSH
新山 祐介(著)
秀和システム, ISBN-13: 978-4798013480, ASIN: 479801348X, 2,415円
- 実用SSH-第2版-セキュアシェル徹底活用ガイド
Daniel J. Barrett (著), Richard E. Silverman (著), Robert G. Byrnes (著)
オライリー・ジャパン, ISBN-13: 978-4873112879, ASIN: 4873112877, 5,040円
SSH に関する本をふたつ読んだけれど,フツーに使うなら入門OpenSSHで十分。
購入した DVD
- Emergency First Response
PADIのEFRの教材。ゆえに一般には手に入りません。2月末に熱海で講習をすることになったので購入。ビデオ版を既に持っているけれど,持ち運びや頭だしや劣化のことを考えると DVD のほうが便利で安心。
公開鍵の秘密 -追補- 3 ― 2007/02/09 15:32
次に ssh-keygen が実際に秘密鍵を作成する部分のコードを見てみる。
ssh-keygen.c をたどっていくと、
1338 private = key_generate(type, bits); 1339 if (private == NULL) { 1340 fprintf(stderr, "key_generate failed"); 1341 exit(1); 1342 }
となっている。private は Key 型へのポインタである。key_generate() は key.c で定義されている。
594 Key * 595 key_generate(int type, u_int bits) 596 { 597 Key *k = key_new(KEY_UNSPEC); 598 switch (type) { 599 case KEY_DSA: 600 k->dsa = dsa_generate_private_key(bits); 601 break; 602 case KEY_RSA: 603 case KEY_RSA1: 604 k->rsa = rsa_generate_private_key(bits); 605 break; 606 default: 607 fatal("key_generate: unknown type %d", type); 608 } 609 k->type = type; 610 return k; 611 }
key_generate() はとても短い。key->type が KEY_RSA のときには、rsa_generate_private_key() を呼び出す。この関数は key.c で定義されている。
569 static RSA * 570 rsa_generate_private_key(u_int bits) 571 { 572 RSA *private; 573 574 private = RSA_generate_key(bits, 35, NULL, NULL); 575 if (private == NULL) 576 fatal("rsa_generate_private_key: key generation failed."); 577 return private; 578 }
鍵が RSA 形式の時には、rsa_generate_private_key() の中で RSA_generate_key() を呼んでいる。紛らわしいが、大文字で始まるほうの RSA_generate_key() は、openssl のソースに含まれているので、これ以上 OpenSSH のソースコードを漁っても何も得られない。
openssl-0.9.8d を展開したあと、crypto ディレクトリに移動するとそこに rsa_depr.c というソースコードがある。RSA_generate_key() はそこに定義されている。
71 RSA *RSA_generate_key(int bits, unsigned long e_value, 72 void (*callback)(int,int,void *), void *cb_arg) 73 { ...中略 92 if(RSA_generate_key_ex(rsa, bits, e, &cb)) { 93 BN_free(e); 94 return rsa; 95 }
とまぁ、RSA_generate_key() のなかでは今度は RSA_generate_key_ex() を呼び出している。_ex つきの関数は rsa_gen.c の定義されている。これで何度目のリダイレクトだ?
rsa_gen.c では、
78 int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb) 79 { 80 if(rsa->meth->rsa_keygen) 81 return rsa->meth->rsa_keygen(rsa, bits, e_value, cb); 82 return rsa_builtin_keygen(rsa, bits, e_value, cb); 83 }
RSA_generate_key_ex() の関数は、これがすべてである。
本当の鍵の生成は rsa_builtin_keygen() を呼び出しておこなっており、rsa_builtin_keygen() は RSA_generate_key() の直後から定義されている。この関数は長いので割愛。
さて、話を元に戻すと、key_generate() の結果を private (Key 型へのポインタ)に保存すると、その先の処理は大きく分けて二つある。一つは private から公開鍵を生成すること、そしてもうひとつは private を秘密鍵として保存することである。
もう一度 key_generate() の部分を ssh-keygen.c から抜き出すと下記のとおりで、これは今回の冒頭に挙げているものと同じだ。
1338 private = key_generate(type, bits); 1339 if (private == NULL) { 1340 fprintf(stderr, "key_generate failed"); 1341 exit(1); 1342 }
private = key_generate(type, bits); の処理で、private には RSA の鍵情報が入っている。この直後に、
1343 public = key_from_private(private);
として、private の内容から公開鍵の情報を取り出して、やはり Key 型へのポインタである public へ代入している。
そして、最終的に、
1405 if (!key_save_private(private, identity_file, passphrase1, comment)) { 1406 printf("Saving the key failed: %s.\n", identity_file); 1407 memset(passphrase1, 0, strlen(passphrase1)); 1408 xfree(passphrase1); 1409 exit(1); 1410 }
と、key_save_private() で identity_file に設定されているファイルへ書き出すのである。
最後に見るのは、key_save_private() だ。この関数は OpenSSH の authfile.c で定義されている。
204 switch (key->type) { 205 case KEY_DSA: 206 success = PEM_write_DSAPrivateKey(fp, key->dsa, 207 cipher, passphrase, len, NULL, NULL); 208 break; 209 case KEY_RSA: 210 success = PEM_write_RSAPrivateKey(fp, key->rsa, 211 cipher, passphrase, len, NULL, NULL); 212 break; 213 }
と、key->type が KEY_RSA なら PEM_write_RSAPrivateKey() を呼び出す。この PEM_write_RSAPrivateKey() はマクロで、openssl-0.9.8d/crypto/pem/pem.h に次のように定義されている。
409 #define PEM_write_RSAPrivateKey(fp,x,enc,kstr,klen,cb,u) \ 410 PEM_ASN1_write((int (*)())i2d_RSAPrivateKey,PEM_STRING_RSA,fp,\ 411 (char *)x,enc,kstr,klen,cb,u)
つまり、PEM_ASN1_write である、と。
openssl-0.8.9d/crypto/pem/pem_lib.c に実際の PEM_ASN1_write() が定義されている。
結果から言うと、PEM_ASN1_write() からは更に PEM_ASN1_write_bio() を呼び出しており、正真正銘、これが最終最後の場所なのだが、はっきりいって、中身をみても良くわからん... 結局、ssh-keygen で id_rsa を生成するときに、p、q、d、n、e の情報を格納していると確信できる部分までだ取り付けていない。今後の宿題かなぁ...
暗号関連、嫌いです。
公開鍵の秘密 -追補- 2 ― 2007/02/09 13:52
さて、昨日までの勉強で秘密鍵ファイルには、秘密鍵のほかにも公開鍵や、それらを生成する材料となったふたつの素数までもが格納されているらしいことがわかった。
実際に OpenSSH の ssh-keygen のコードを見てみよう。下は OpenSSH 4.5-p1 に含まれる ssh-keygen.c からの抜粋だ。do_convert_private_ssh2_from_blob() という関数が実際に -y オプションを与えられたときに RSA の秘密鍵の処理を行っている部分のようだ。 訂正: この関数は、秘密鍵を SSH2 形式へ変更する -X オプションを指定したときに呼ばれる関数である。
この関数は、一連のデータのかたまり(鍵の元となる)とその長さを受け取り、Key 型へのポインタを返す。この関数は結構な長さがあるので該当部分だけ抜き出すと、次のようになる。
278 switch (key->type) { 279 case KEY_DSA: 280 buffer_get_bignum_bits(&b, key->dsa->p); 281 buffer_get_bignum_bits(&b, key->dsa->g); 282 buffer_get_bignum_bits(&b, key->dsa->q); 283 buffer_get_bignum_bits(&b, key->dsa->pub_key); 284 buffer_get_bignum_bits(&b, key->dsa->priv_key); 285 break; 286 case KEY_RSA: 287 e = buffer_get_char(&b); 288 debug("e %lx", e); 289 if (e < 30) { 290 e <<= 8; 291 e += buffer_get_char(&b); 292 debug("e %lx", e); 293 e <<= 8; 294 e += buffer_get_char(&b); 295 debug("e %lx", e); 296 } 297 if (!BN_set_word(key->rsa->e, e)) { 298 buffer_free(&b); 299 key_free(key); 300 return NULL; 301 } 302 buffer_get_bignum_bits(&b, key->rsa->d); 303 buffer_get_bignum_bits(&b, key->rsa->n); 304 buffer_get_bignum_bits(&b, key->rsa->iqmp); 305 buffer_get_bignum_bits(&b, key->rsa->q); 306 buffer_get_bignum_bits(&b, key->rsa->p); 307 rsa_generate_additional_parameters(key->rsa); 308 break; 309 }
key->type が KEY_RSA の場合には、はやり p, q, n, d, e を格納取り出している。
公開鍵の秘密 -追補- ― 2007/02/09 01:26
つまりは,秘密鍵から公開鍵を生成できないというのは,僕の単なる思い込みなのであった。
... とひとつ前に書いているが,厳密に言うと思い込みではない。
暗号化/復号化のために使う鍵のペアは,次のように導きだす。
まず,大きな素数をふたつ用意する。p と q とする。p と q を掛けて n を導く。n = p*q である。つぎに p と q からそれぞれ 1 を引いた数を求める。(p - 1) と (q - 1)である。この二つをかける。(p - 1)*(q - 1)。3よりも大きな数字で,この (p - 1)*(q - 1) と 1以外の公約数を持たない(つまり,たがいに素である)数を求める。これが e となる。つぎが複雑だ。「ある数に e をかけ(p - 1)*(q - 1)で割ると,あまりが 1になる」数を探す。その数が d となる。
暗号化と復号化に使うペアは,(d, n)と(e, n)だ。n を導きだした p と q は登場しない。n は十分大きな数字なので n から p と q を求めることはむずかしく,e と d はそれぞれ p と q から直接的に導きだされるから,p と q を知らなければ,e から d を導くことは難しく,また d から e を導くこともきわめて難しい。
つまり,厳密な意味では秘密鍵(d, n)から公開鍵(e, n)を求めることは計算上非常に難しく,逆もまたしかり,である。
秘密鍵から公開鍵を生成できるというのは,秘密鍵を納めた秘密鍵ファイル(OpenSSH では id_rsa)に厳密な意味での秘密鍵(d, n)以外に,計算の元となった p と q,そして (p - 1) と (q - 1),くわえて e (公開鍵の片割れ) までもが記録されているからに過ぎない。
公開鍵の秘密 -5- 完結編 ― 2007/02/09 00:19
完結編。
やはり,まずは標準にあたるべきである。
RFC 2313 - PKCS #1: RSA Encryption Version 1.5のセクション 7.2 にこうある。
7.2 Private-key syntax An RSA private key shall have ASN.1 type RSAPrivateKey: RSAPrivateKey ::= SEQUENCE { version Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- d mod (p-1) exponent2 INTEGER, -- d mod (q-1) coefficient INTEGER -- (inverse of q) mod p } Version ::= INTEGER The fields of type RSAPrivateKey have the following meanings: o version is the version number, for compatibility with future revisions of this document. It shall be 0 for this version of the document. o modulus is the modulus n. o publicExponent is the public exponent e. o privateExponent is the private exponent d. o prime1 is the prime factor p of n. o prime2 is the prime factor q of n. o exponent1 is d mod (p-1). o exponent2 is d mod (q-1). o coefficient is the Chinese Remainder Theorem coefficient q-1 mod p.
つまり,秘密鍵には encryption exponent も decryption exponent も両方格納されている。ご丁寧に,直後の note には,
2. The presence of the public exponent e is intended to make it straightforward to derive a public key from the private key.
ここで public exponent といっているのは,encryption exponent と同義だ。つまりは,public exponent(公開指数,暗号化指数)がここにあるのは,秘密鍵から公開鍵を容易に生成できるようにするためであると明解に書かれている。
あー。すっきりした。
つまりは,秘密鍵から公開鍵を生成できないというのは,僕の単なる思い込みなのであった。
正確には decryption exponent (d) と moduli (n) の組み合わせ(これが厳密な意味での秘密鍵だ)からは,encryption exponent (e) と moduli (n) の組み合わせ -- 公開鍵 -- は推測できない。だが,秘密鍵を格納しているファイルには,公開鍵を生成するための encryption exponent (public exponent)が仕込まれていたんである。
ソースを追いかけること 3時間。無駄だったのか,楽しかったのか...
公開鍵の秘密 -4- ― 2007/02/08 23:07
転生編。
普段職場で RTM とというと,それは Release To Manifacturer である。製品の CD / DVD をプレスする工場へゴールデンマスター(製品のマスターコピー)を送る作業のことをいう。転じて,製品の開発が終了した状態を指すようになった。「次期バージョンの RTM が二ヶ月遅れる」というのは,開発期間が伸びて市場に投入する時期が二ヶ月遅れるという悪い知らせだ。
もちろん,今日の RTM はもうひとつの RTM: Read The Manualである。
そもそもの疑問は,「秘密鍵から公開鍵は計算できないはず」というものであった。であるのに,SSH-AUTH の段階でクライアントは平然と id_rsa (秘密鍵を納めたファイル)から公開鍵を生成しているんである。
OpenSSH のソースをあちこち這いずり回ったあげく,load-identity-file() 関数のその先は,openssl の rsa.c を見ないと答えがわからないことがわかった。でもって openssl のソースはいま手元にない。
ところで,ひょんなことから ssh-keygen(1)のマニュアルを読んでいたら,次のオプションがあることに気がついた。
-y This option will read a private OpenSSH format file and print an OpenSSH public key to stdout.
ここにははっきりと秘密鍵を納めたファイルから公開鍵を生成できることが書いてある。
ためしてみると,
$ ssh-keygen -y Enter file in which the key is (/Users/ats/.ssh/id_rsa): Enter passphrase: ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAuSlWJTJpZ8pKPCbOyYyVhmQaTSvmfz1r8uZ5cnakts8K x1Vgsruu9bfSqC7CF2zVs2QNSOo8BotkjVBEg3npf8RaPRit4i/f2MYihveUustg9HT56bD4ZMQq/+0W SZpYedzL2AMTeqzGgj2Z6CoY3ypMOEmvMpArTcwAapv9esxUpeikjggcY+99c50gCGjf0Gb6MJT8eSqA yL5oh5chcYOS1PevCihoo5qoESr7ZAdVvzplsjsRwmKhHDjpo6jxGpqqGaf/5jz2OE78tUV2uhd2FHts NtZw7ewRiDLSdEO1z2iPYXyXyu+M+ZwuzY37u6ZoFkuKCCKs3/l+db6Isw== $ cat id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAuSlWJTJpZ8pKPCbOyYyVhmQaTSvmfz1r8uZ5cnakts8K x1Vgsruu9bfSqC7CF2zVs2QNSOo8BotkjVBEg3npf8RaPRit4i/f2MYihveUustg9HT56bD4ZMQq/+0W SZpYedzL2AMTeqzGgj2Z6CoY3ypMOEmvMpArTcwAapv9esxUpeikjggcY+99c50gCGjf0Gb6MJT8eSqA yL5oh5chcYOS1PevCihoo5qoESr7ZAdVvzplsjsRwmKhHDjpo6jxGpqqGaf/5jz2OE78tUV2uhd2FHts NtZw7ewRiDLSdEO1z2iPYXyXyu+M+ZwuzY37u6ZoFkuKCCKs3/l+db6Isw== ats@mika.local
まさしく,id_rsa から id_rsa.pub と同一の内容を生成している。
... となると,秘密は id_rsa のファイル形式にある。
まだまだ続く。
公開鍵の秘密 -3- ― 2007/02/08 17:58
... さらにつづく。
早速 OpenSSH 4.5p1 のソースを落としてきて眺めてみた。
sshconnect2.c の userauth_pubkey() 関数あたりが、実際に公開鍵認証を実行しているクライアント側のコードのようである。
userauth_pubkey() の中盤あたりはこうなっている。
1121 if (id->key && id->key->type != KEY_RSA1) { 1122 debug("Offering public key: %s", id->filename); 1123 sent = send_pubkey_test(authctxt, id); 1124 } else if (id->key == NULL) { 1125 debug("Trying private key: %s", id->filename); 1126 id->key = load_identity_file(id->filename); 1127 if (id->key != NULL) { 1128 id->isprivate = 1; 1129 sent = sign_and_send_pubkey(authctxt, id); 1130 key_free(id->key); 1131 id->key = NULL; 1132 } 1133 }
つまり、ここまでの流れの中で id->key に値が設定されていないと、公開鍵が見つからなかったと判断して、load_identity_file()関数を呼んで秘密鍵を得る。(1126行目)。引数の id->filename は ssh コマンドを実行したときに -i オプションで渡したファイル名がセットされている。
id->key に秘密鍵を読み込んだところで、sign_and_send_pubkey() を呼び出す。この関数は同じ sshconnect2.c に定義されていて、サーバーへの接続はこの関数の中で行われる。sign_and_send_pubkey() では、
869 if (key_to_blob(id->key, &blob, &bloblen) == 0) { 870 /* we cannot handle this key */ 871 debug3("sign_and_send_pubkey: cannot handle key"); 872 return 0; 873 }
と、869行目で key_to_blob() 関数に読み込んだ秘密鍵を渡している。
key_to_blob() 関数は id->key に格納されている秘密鍵から "blob"へと変換する。blob とは「ひとかたまり」を意味する英単語で、データベースの Binary Large Object のことではない。
その key_to_blob() は key.c で定義されている。実際に秘密鍵から blob に変換する部分のコードは次の通りだ。
757 switch (key->type) { 758 case KEY_DSA: 759 buffer_put_cstring(&b, key_ssh_name(key)); 760 buffer_put_bignum2(&b, key->dsa->p); 761 buffer_put_bignum2(&b, key->dsa->q); 762 buffer_put_bignum2(&b, key->dsa->g); 763 buffer_put_bignum2(&b, key->dsa->pub_key); 764 break; 765 case KEY_RSA: 766 buffer_put_cstring(&b, key_ssh_name(key)); 767 buffer_put_bignum2(&b, key->rsa->e); 768 buffer_put_bignum2(&b, key->rsa->n); 769 break;
ここで問題となるのが 767行目と 768行目の key->rsa->e と key->rsa->n だ。RSA とはつまりは秘密鍵と公開鍵の二つの非対称鍵を使うしくみなのだが、rsa->e は encryption exponent (暗号化指数)、rsa->n は RSA moduli (OpenSSH では public moduli: 係数) を意味する。rsa->e と rsa->n の組み合わせが公開鍵となり、秘密鍵は rsa->d (decryption exponent: 復号化指数) と rsa->n の組み合わせになる。
上記の流れで行くと、userauth_pubkey() で秘密鍵を id->key にロードし、さらに key_to_blob() で id->key にセットされている秘密鍵から暗号化指数 rsa->e と係数 rsa->n を読み出している。
さて。困った。
秘密鍵がもっているのは、復号化指数 (rsa->d)と係数(rsa->n)ではないのか?
userauth_pubkey() で秘密鍵を読み出しておきながら、つづく key_to_blob() でその秘密鍵から暗号化指数 (rsa->e)を読み出せているリクツはなんだろう。
load_identity_file() で id->key になにをロードしているのか調べる必要がある。
まだつづく... が...
どうも、RSA の基本からやりなおさなければだめなようだ...
最近のコメント