Python bash
コマンドを呼び出すCGIスクリプトを作成しました。ホストでのログインが成功するかどうかをテストする必要があります。
どうすればそのためのテストを書けますか?
たとえば、ホストに登録されているユーザーに対して特定のユーザー名とパスワードの組み合わせをテストするbash
スクリプトを作成できますか?
PAMを使用するのが最適なソリューションです。小さなCコードを書くか、python-pamパッケージをインストールして、python python-pamパッケージに付属するスクリプトを使用できます。/usr/share/doc/python-pam/examples/pamtest.py
を参照してください。
ユーザーがログインできるかどうかをテストする正しい方法は、実際にそのユーザーとしてログインすることです。
したがって、CGIスクリプトで expect
を使用してsu
を実行し、パスワードを渡して、実行する必要があるコマンドを実行することをお勧めします。これを実行するexpectスクリプトのドラフトは次のとおりです(警告:完全にテストされておらず、私は期待に堪能ではありません)。ユーザー名、パスワード、およびコマンドを置き換えます(ここでbob
、swordfish
およびsomecommand
と書きました)。正しく引用してください。
spawn "/bin/su" "bob"
expect "Password:"
send "swordfish\r"
expect "^\\$"
send "somecommand"
expect -re "^\\$"
send "exit\r"
expect eof
su
のレイヤーを介してコマンドを実行したくない場合は(たとえば、CGIプロセス自体が実行する必要があるため)、expectを使用してコマンドtrue
を返し、戻りステータスが0であることを確認します。
別のアプローチは、アプリケーションで [〜#〜] pam [〜#〜] を PythonのPAMバインディング を介して直接使用することです。
具体的には、「指定されたユーザー名とパスワードの組み合わせをホスト上の登録ユーザーに対してテストするbashスクリプトを作成することは可能ですか?」
はい。
#!/bin/bash
uid=`id -u`
if [ $uid -ne 0 ]; then
echo "You must be root to run this"
exit 1
fi
if [ $# -lt 1 ]; then
echo "You must supply a username to check - ($# supplied)"
exit 1
fi
username=$1
salt=`grep $username /etc/shadow | awk -F: ' {print substr($2,4,8)}'`
if [ "$salt" != "" ]; then
newpass=`openssl passwd -1 -salt $salt`
grep $username /etc/shadow | grep -q $newpass && echo "Success" || echo "Failure"
fi
ここに引用されている「C」、「Python」のPAMソリューションがあります。Perlも使用させてください:-)
#!/usr/bin/Perl
use Authen::PAM;
use POSIX qw(ttyname);
$service = "login";
$username = "foo";
$password = "bar";
$tty_name = ttyname(fileno(STDIN));
sub my_conv_func {
my @res;
while ( @_ ) {
my $code = shift;
my $msg = shift;
my $ans = "";
$ans = $username if ($code == PAM_Prompt_ECHO_ON() );
$ans = $password if ($code == PAM_Prompt_ECHO_OFF() );
Push @res, (PAM_SUCCESS(),$ans);
}
Push @res, PAM_SUCCESS();
return @res;
}
ref($pamh = new Authen::PAM($service, $username, \&my_conv_func)) ||
die "Error code $pamh during PAM init!";
$res = $pamh->pam_set_item(PAM_TTY(), $tty_name);
$res = $pamh->pam_authenticate;
print $pamh->pam_strerror($res),"\n" unless $res == PAM_SUCCESS();
Rootアクセスがあり、md5パスワードを使用していて、パスワードを比較するだけの場合は、Perl Crypt :: PasswdMD5 モジュールを使用できます。/etc/shadowからMD5ハッシュを取得し、$ 1 $を取り除き、残りの$で分割します。フィールド1 = Salt、フィールド2 =暗号化されたテキスト。次に、テキスト入力をCGIにハッシュし、それを暗号化されたテキストと比較して、ボブはあなたの叔父です。
#!/usr/bin/env Perl
use Crypt::PasswdMD5;
my $user = $ARGV[0];
my $plain = $ARGV[1];
my $check = qx{ grep $user /etc/shadow | cut -d: -f2 };
chomp($check);
my($salt,$md5txt) = $check =~ m/\$1\$([^\$].+)\$(.*)$/;
my $pass = unix_md5_crypt($plain, $salt);
if ( "$check" eq "$pass" ) {
print "OK","\n";
} else {
print "ERR","\n";
}
PythonでCGIを使用していると述べたので、Apacheをhttpdサーバーとして使用していると想定するのがおそらく適切です。その場合は、プログラムの認証プロセスをApacheに任せて、認証された人だけがcgiスクリプト/プログラムを実行できるようにします。
Apacheで認証を実行できる十分なモジュールがありますが、それは実際にはどのような認証メカニズムを探しているかによって異なります。質問で引用した方法は、/ etc/passwd、shadowファイルに基づくローカルホストアカウント認証に関連しているようです。これに関する私のクイック検索に来るモジュールはmod_auth_shadow
。利点は、権限のあるユーザー(特権ポート80で実行)がユーザー/パスワードを認証できるようにし、必要に応じてユーザーの代わりにコマンドを実行するためにユーザーの認証情報に依存できることです。
開始するための良いリンク:
http://adam.shand.net/archives/2008/Apache_tips_and_tricks/#index5h2
http://mod-auth-shadow.sourceforge.net/
http://www.howtoforge.com/Apache_mod_auth_shadow_debian_ubunt
別のアプローチは、認証されたユーザーに代わってプロセス(cgiプログラム)を実行するApacheのSuEXEcモジュールを使用することです。
いくつか検索した後、スクリプトから使用できるこのCプログラムを作成しました
#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#include <shadow.h>
#include <string.h>
#include <crypt.h>
#include <unistd.h>
#include <libgen.h>
int main(int argc, char **argv) {
struct spwd *pwd;
if (argc != 3) {
printf("usage:\n\t%s [username] [password]\n", basename(argv[0]));
return 1;
} else if (getuid() == 0) {
pwd = getspnam(argv[1]);
return strcmp(crypt(argv[2], pwd->sp_pwdp), pwd->sp_pwdp);
} else {
printf("You need to be root\n");
return 1;
}
}
あなたはそれをコンパイルします:
gcc -Wall password_check.c /usr/lib/libcrypt.a -o check_passwd
次のように使用できます
Sudo ./check_passwd <user> <password> && echo "success" || echo "failure"
PAMを使用するこのコードは私のために働きました:
#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <stdio.h>
#include <string.h>
// Define custom PAM conversation function
int custom_converation(int num_msg, const struct pam_message** msg, struct pam_response** resp, void* appdata_ptr)
{
// Provide password for the PAM conversation response that was passed into appdata_ptr
struct pam_response* reply = (struct pam_response* )malloc(sizeof(struct pam_response));
reply[0].resp = (char*)appdata_ptr;
reply[0].resp_retcode = 0;
*resp = reply;
return PAM_SUCCESS;
}
int main (int argc, char* argv[])
{
if (argc > 2)
{
// Set up a custom PAM conversation passing in authentication password
char* password = (char*)malloc(strlen(argv[2]) + 1);
strcpy(password, argv[2]);
struct pam_conv pamc = { custom_converation, password };
pam_handle_t* pamh;
int retval;
// Start PAM - just associate with something simple like the "whoami" command
if ((retval = pam_start("whoami", argv[1], &pamc, &pamh)) == PAM_SUCCESS)
{
// Authenticate the user
if ((retval = pam_authenticate(pamh, 0)) == PAM_SUCCESS)
fprintf(stdout, "OK\n");
else
fprintf(stderr, "FAIL: pam_authentication failed.\n");
// All done
pam_end(pamh, 0);
return retval;
}
else
{
fprintf(stderr, "FAIL: pam_start failed.\n");
return retval;
}
}
fprintf(stderr, "FAIL: expected two arguments for user name and password.\n");
return 1;
}