web-dev-qa-db-ja.com

Linuxでパスワードを確認する方法は?

Linuxコマンドラインから、指定されたクリアテキストパスワードが/ etc/shadowの暗号化されたパスワードと同じかどうかを確認したい

(Webユーザーを認証するためにこれが必要です。組み込みLinuxを実行しています。)

/ etc/shadowファイル自体にアクセスできます。

23
michelemarcon

暗号化されたパスワードは、awkを使用して簡単に抽出できます。次に、接頭辞$algorithm$salt$を抽出する必要があります(このシステムが従来のDESを使用していないことを前提としています。これは、ブルートフォース攻撃が可能なため、非推奨です)。

correct=$(</etc/shadow awk -v user=bob -F : 'user == $1 {print $2}')
prefix=${correct%"${correct#\$*\$*\$}"}

パスワードチェックの場合、基になるC関数は crypt ですが、それにアクセスするための標準のShellコマンドはありません。

コマンドラインで、Perlワンライナーを使用して、パスワードでcryptを呼び出すことができます。

supplied=$(echo "$password" |
           Perl -e '$_ = <STDIN>; chomp; print crypt($_, $ARGV[0])' "$prefix")
if [ "$supplied" = "$correct" ]; then …

これは純粋なシェルツールでは実行できないため、Perlを使用できる場合は、すべてPerlで実行することもできます。 (または、Python、Rubyなど、crypt関数を呼び出すことができるものはすべて使用可能です。)警告、テストされていないコード。

#!/usr/bin/env Perl
use warnings;
use strict;
my @pwent = getpwnam($ARGV[0]);
if (!@pwent) {die "Invalid username: $ARGV[0]\n";}
my $supplied = <STDIN>;
chomp($supplied);
if (crypt($supplied, $pwent[1]) eq $pwent[1]) {
    exit(0);
} else {
    print STDERR "Invalid password for $ARGV[0]\n";
    exit(1);
}

Perlのない組み込みシステムでは、小さな専用のCプログラムを使用します。警告、ブラウザに直接入力しました。コンパイルすることすらしていません。これは、堅牢な実装としてではなく、必要な手順を説明するためのものです!

/* Usage: echo password | check_password username */
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <shadow.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
    char password[100];
    struct spwd shadow_entry;
    char *p, *correct, *supplied, *salt;
    if (argc < 2) return 2;
    /* Read the password from stdin */
    p = fgets(password, sizeof(password), stdin);
    if (p == NULL) return 2;
    *p = 0;
    /* Read the correct hash from the shadow entry */
    shadow_entry = getspnam(username);
    if (shadow_entry == NULL) return 1;
    correct = shadow_entry->sp_pwdp;
    /* Extract the salt. Remember to free the memory. */
    salt = strdup(correct);
    if (salt == NULL) return 2;
    p = strchr(salt + 1, '$');
    if (p == NULL) return 2;
    p = strchr(p + 1, '$');
    if (p == NULL) return 2;
    p[1] = 0;
    /*Encrypt the supplied password with the salt and compare the results*/
    supplied = crypt(password, salt);
    if (supplied == NULL) return 2;
    return !!strcmp(supplied, correct);
}

別のアプローチは、suloginなどの既存のプログラムを使用することです。実際、可能であれば、su -c somecommand usernameを介してWebアプリケーションが必要なことをすべて実行できるように調整するのが理想的です。ここでの問題は、パスワードをsuに送ることです。これには端末が必要です。端末をエミュレートする通常のツールは expect ですが、組み込みシステムにとっては大きな依存関係です。また、suはBusyBoxにありますが、多くの場合、BusyBoxバイナリをsetuidルートにする必要があるため、省略されます。それでも、それが可能であれば、これはセキュリティの観点から最も堅牢なアプローチです。

man 5 shadowman 3 cryptをご覧ください。後者から、/etc/shadowのパスワードハッシュは次の形式であることがわかります。

 $id$salt$encrypted

ここで、idは暗号化のタイプを定義し、さらに読むと、次のいずれかになります。

          ID  | Method
          ---------------------------------------------------------
          1   | MD5
          2a  | Blowfish (not in mainline glibc; added in some
              | Linux distributions)
          5   | SHA-256 (since glibc 2.7)
          6   | SHA-512 (since glibc 2.7)

ハッシュのタイプに応じて、「手動」でパスワードを生成および検証するための適切な関数/ツールを使用する必要があります。システムにmkpasswdプログラムが含まれている場合は、それを使用できます ここで推奨 。 (それが明らかでない場合は、シャドウファイルからsaltを取得します。)たとえば、md5 passwordsの場合:

 mkpasswd -5 <the_salt> <the_password>

/etc/shadowエントリに一致する必要がある文字列を生成します。

6
rozcietrzewiacz

Stack Overflowで同様の質問 がありました。 cluelessCoderは、expectを使用してスクリプトを提供しました 、これは、組み込みシステムにある場合とない場合があります。

#!/bin/bash
#
# login.sh $USERNAME $PASSWORD

#this script doesn't work if it is run as root, since then we don't have to specify a pw for 'su'
if [ $(id -u) -eq 0 ]; then
        echo "This script can't be run as root." 1>&2
        exit 1
fi

if [ ! $# -eq 2 ]; then
        echo "Wrong Number of Arguments (expected 2, got $#)" 1>&2
        exit 1
fi

USERNAME=$1
PASSWORD=$2

#since we use expect inside a bash-script, we have to escape tcl-$.
expect << EOF
spawn su $USERNAME -c "exit" 
expect "Password:"
send "$PASSWORD\r"
#expect eof

set wait_result  [wait]

# check if it is an OS error or a return code from our command
#   index 2 should be -1 for OS erro, 0 for command return code
if {[lindex \$wait_result 2] == 0} {
        exit [lindex \$wait_result 3]
} 
else {
        exit 1 
}
EOF
1
mr.Shu

システムが適切に構成されている場合、プログラムはrootとして実行する必要があることに注意してください。

シャドウファイルを直接読み取り、cryptの周りに独自のコードを記述するよりも優れたソリューションは、pamバインディングを使用することです。

squid tarballは、stdioを使用してユーザー名/パスワードを検証するためのシンプルなCLIツールに付属していました-引数の使用に適応するのはとても簡単です-以前ハッキングしたバージョンは、構造化プログラミングのピンナップポスターではありませんでした。簡単なグーグルで、 最近のバージョン のように見えますが、大幅にクリーンアップされていますが、まだいくつかの 'goto'があります。

0
symcbean
#! /bin/bash
#  (GPL3+) Alberto Salvia Novella (es20490446e)


passwordHash () {
    password=${1}
    salt=${2}
    encryption=${3}

    hashes=$(echo ${password} | openssl passwd -${encryption} -salt ${salt} -stdin)
    echo $(substring ${hashes} "$" "3")
}


passwordIsValid () {
    user=${1}
    password=${2}

    encryption=$(secret "encryption" ${user})
    salt=$(secret "salt" ${user})
    salted=$(secret "salted" ${user})
    hash=$(passwordHash ${password} ${salt} ${encryption})

    [ ${salted} = ${hash} ] && echo "true" || echo "false"
}


secret () {
    secret=${1}
    user=${2}
    shadow=$(shadow ${user})

    if [ ${secret} = "encryption" ]; then
        position=1
    Elif [ ${secret} = "salt" ]; then
        position=2
    Elif [ ${secret} = "salted" ]; then
        position=3
    fi

    echo $(substring ${shadow} "$" ${position})
}


shadow () {
    user=${1}
    shadow=$(cat /etc/shadow | grep ${user})
    shadow=$(substring ${shadow} ":" "1")
    echo ${shadow}
}


substring () {
    string=${1}
    separator=${2}
    position=${3}

    substring=${string//"${separator}"/$'\2'}
    IFS=$'\2' read -a substring <<< "${substring}"
    echo ${substring[${position}]}
}


passwordIsValid ${@}