システムログインのテストを作成するにはどうすればよいですか?


9

bashコマンドを呼び出すPython CGIスクリプトを作成しました。ホストでのログインが成功するかどうかをテストする必要があります。

どうすればそのためのテストを書けますか?

たとえばbash、ホストに登録されているユーザーに対して特定のユーザー名とパスワードの組み合わせをテストするスクリプトを作成できますか?


1
おそらく、loginプログラムの背後にあるコードを見ることができます。
ケビン

質問とは関係ありませんが、Webサーバーへのトラフィックを暗号化して、ユーザーのログインがネットワーク上で傍受されないようにしてください。
jw013

回答:


8

PAMを使用するのが最適なソリューションです。小さなCコードを書くか、python-pamパッケージをインストールして、python-pamパッケージに付属するpythonスクリプトを使用できます。見る/usr/share/doc/python-pam/examples/pamtest.py


PAMを試しましたが、うまくいきませんでした。しかし、この例をもう一度試してみると、うまくいきます。
jcubic 2012年

1
OpenSUSE 12.3(python-pam 0.5.0-84.1.1)および13.1(0.5.0-87.1.2)では、pamtest.pyへの絶対パスは次のとおりです/usr/share/doc/packages/python-pam/examples/pamtest.py。pamtest.pyスクリプトを使用して、PAMを使用するシステムで資格情報をテストできます。認証用にpython-pamパッケージ(pythonが必要)に含まれており、一部のディストリビューションではフルパスは/usr/share/doc/python-pam/examples/pamtest.pyです。
ShadSterling 2014年

5

ユーザーがログインできるかどうかをテストする正しい方法は、実際にそのユーザーとしてログインすることです。

したがって、CGIスクリプトを使用expectsuてパスワードを渡し、実行する必要があるコマンドを実行することをお勧めします。これを実行するexpectスクリプトのドラフトは次のとおりです(警告:完全にテストされておらず、私は期待に堪能ではありません)。ユーザー名、パスワード、およびコマンド(私が書いたbobswordfishおよび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であることを確認します。

別のアプローチは、PythonのPAMバインディングを通じて、アプリケーションで直接PAMを使用することです。


それは素晴らしいです、ルートアクセスなしの唯一のソリューション。
jcubic 2012年

これは動作su -c true bob && echo successしますsuが引数としてパスワードを受け入れないのは残念です
jcubic

suはCGIスクリプトからテストしましたが、それが機能するには端末が必要です。
jcubic 2012年

3
@jcubicコマンドライン引数はUnixシステムで公開されているため、コマンドライン引数にパスワードを入れるのは本当に愚かな考えです。パスワードを削除すると、コマンドラインに入力した場合と同じレベルのセキュリティが提供されます。そして、それはチェックするのがはるかに簡単です:/bin/true十分でしょう。
2015年

2

具体的には、「指定されたユーザー名とパスワードの組み合わせをホスト上の登録ユーザーに対してテストする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

2
これでうまくいきましたか?既存のシャドウパスワードをシャドウパスワードと照合しているようですが、ハッシュはどこに関係していますか?
Nikhil Mulley、2012年

私がテストしましたが動作しません
jcubic

3
悪いアイデア!プログラムがrootまたは少なくともshadowグループとして実行されていることを想定しているため、CGIには強く推奨されていません。別の権限昇格層が必要です。また、特定のパスワードハッシュアルゴリズム(opensslでサポートされているもの)とパスワードの保存場所(/etc/shadowNISやLDAPなどではなく)を想定しているため、その特定のユーザーが実際に使用している場合とそうでない場合があります。
Gilles「SO-邪悪なことをやめよう」

2

ここに引用されている「C」、「Python」のPAMソリューションがあります。perlも1つ入れます:-)

出典:http : //search.cpan.org/~nikip/Authen-PAM-0.16/PAM/FAQ.pod#1._Can_I_authenticate_a_user_non_interactively

#!/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();

はい、でも問題はPythonで書かれたCGIスクリプトに関するものでした。
Gilles「SO-邪悪なことをやめよう」

1

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";
}

2
簡潔でシンプル :-)。これは、/ etc / passwdがmd5ハッシュを使用している限り機能します。システムで認証(nsswitch)が異なる場合は、pamモジュールを使用するのが最適です。
Nikhil Mulley、2012年

2
悪いアイデア!プログラムがrootまたは少なくともshadowグループとして実行されていることを想定しているため、CGIには強く推奨されていません。別の権限昇格層が必要です。また、特定のパスワードハッシュアルゴリズム(bcryptやその他の推奨アルゴリズムではなくMD5)とパスワードの保存場所(/etc/shadowNISやLDAPなどではなく)を想定しているため、その特定のユーザーが実際に使用する場所としない場合があります。
ジル 'SO-邪悪なことをやめよ'

0

いくつか検索した後、スクリプトから使用できるこの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"

1
悪いアイデア!プログラムがrootまたは少なくともshadowグループとして実行されていることを想定しているため、CGIには強く推奨されていません。別の権限昇格層が必要です。また、特定のパスワードハッシュアルゴリズム(opensslでサポートされているもの)とパスワードの保存場所(/etc/shadowNISやLDAPなどではなく)を想定しているため、その特定のユーザーが実際に使用している場合とそうでない場合があります。PAMを使用してください、それはその仕事を知っています。
Gilles「SO-邪悪なことをやめよう」

はい私は知っていますが、これはルートなしでは不可能だと思いました。他のすべてのソリューションも同様にrootを使用します。
jcubic 2012年

0

PythonでCGIを使用していると述べたので、httpdサーバーとしてApacheを使用していると想定するのがおそらく適切です。その場合は、プログラムの認証プロセスを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_ubuntu

別のアプローチは、認証済みユーザーに代わってプロセス(cgiプログラム)を実行するApacheのSuEXEcモジュールを使用することです。


このCGIスクリプトはAjax経由で呼び出されるJSON-RPCサービスであり、トークンを返すメソッドloginが必要です。ログインが成功した場合、トークンが返されます。基本的に、すべてのユーザーがそのスクリプトを実行できる必要があります。
jcubic 2012年

0

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; 
}

コンテキスト情報を追加して、回答のように「感じる」ことができますか?
Volker Siegel 14

-3

ホストにログインするためのスクリプトが必要な場合にできる最善のことは、ホスト間にsshキーを構成することです。

リンク: http //pkeck.myweb.uga.edu/ssh/

私はこれをページからかなり持ち上げました


まず、2つのUNIXマシンにhurlyとburlyのOpenSSHをインストールします。これは、私が知る限り、デフォルトでDSAキーとSSH2を使用して最適に機能します。私が見た他のすべてのHOWTOは、RSA鍵とSSH1を扱っているようであり、その指示は、SSH2で動作しないことは驚くに値しません。各マシンでssh somemachine.example.comと入力し、通常のパスワードで接続します。これにより、ホームディレクトリに適切な権限を持つ.sshディレクトリが作成されます。秘密鍵を有効にしておく(プライマリーマシンで)、次のように入力します。

ssh-keygen -t dsa

これにより、秘密のパスフレーズの入力が求められます。これがプライマリIDキーである場合は、適切なパスフレーズを使用してください。これが正しく機能する場合、.sshディレクトリにid_dsaおよびid_dsa.pubという2つのファイルが作成されます。注:パスフレーズの入力を求められたら、Enterキーを押すだけで、パスフレーズのないキーが作成されます。これはIDキーのBad Idea™であるため、行わないでください。パスフレーズなしのキーの使用については、以下を参照してください。

scp ~/.ssh/id_dsa.pub burly:.ssh/authorized_keys2

id_dsa.pubファイルを、authorized_keys2という名前で他のホストの.sshディレクトリにコピーします。これで、burlyはsshキーを受け入れる準備ができました。どのキーを使用するかをどのように伝えるか?ssh-addコマンドがそれを行います。テストの場合は、次のように入力します

ssh-agent sh -c 'ssh-add < /dev/null && bash'

これにより、ssh-agentが起動し、デフォルトのIDが追加され(パスフレーズの入力を求められます)、bashシェルが生成されます。この新しいシェルから、次のことができるはずです。

ssh burly

ログインできるはずです


これは事実ですが、Webブラウザーを介してアクセスされるアプリケーションに関する質問には関係がないようです。
Gilles「SO-邪悪なことをやめよう」
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.