如何使用Linux检查密码?


22

我想从linux命令行检查给定的明文密码是否与/ etc / shadow上的加密密码相同

(我需要使用它来验证Web用户。我正在运行嵌入式Linux。)

我可以访问/ etc / shadow文件本身。


使用密码以用户身份登录?
库沙兰丹

测试必须自动完成,我无法从Web服务器手动键入密码
michelemarcon 2011年

Answers:


17

您可以使用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 

由于无法在纯Shell工具中完成此操作,因此,如果有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。实际上,如果可以的话,最好安排Web应用程序通过执行其所需的任何操作su -c somecommand username。这里的困难是将密码提供给su;这需要一个终端。通常的工具来模拟一个终端是期望,但它是一个嵌入式系统一个很大的依赖性。另外,虽然su在BusyBox中,但由于许多用途要求将BusyBox二进制文件设置为setuid根,因此通常将其省略。尽管如此,从安全角度来看,这是最可靠的方法。


1
我喜欢这种su方法。
约翰

6

看看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程序,则可以按照此处的建议使用它。(如果不明显,您可以从影子文件中获取。)例如,使用md5密码:

 mkpasswd -5 <the_salt> <the_password>

将生成应与/etc/shadow条目匹配的字符串。


1
在我的Debian Wheezy上,该命令的语法完全不同mkpasswd,必须使用来安装apt-get install whois。阴影线的命令行<user>:$6$<salt>$<pwd>:mkpasswd -msha-512 <password> <salt>
Daniel Alder 2014年

1

在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

0

请记住,假设系统配置正确,则该程序将需要以root用户身份运行。

与直接读取影子文件并围绕crypt编写自己的代码相比,更好的解决方案是仅使用pam绑定。

鱿鱼压缩包用于配备使用标准输入输出验证用户名/密码,一个简单的CLI工具-这么简单,以适应使用参数-虽然我砍死以前的版本几乎没有对结构化编程一个迷人的海报。一个快速的谷歌,看起来较新的版本已被大大清理,但那里仍然有一些“ goto”。


0
#! /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 ${@}

出现错误line 61: :: syntax error: operand expected (error token is ":")
蹒跚

终端仿真器应为Bash 5,并且必须同时指定用户名和密码作为参数。我已经测试过了。
Alberto Salvia Novella
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.