【问题标题】:Perl System Call not Interpretting Variable CorrectlyPerl 系统调用未正确解释变量
【发布时间】:2016-05-19 03:39:34
【问题描述】:

我正在使用 Perl 将字符串 ($password) 传递给系统行以进行解释。不过,它似乎没有被正确解释,因为当我附加 Sparse Bundle 时,它​​说身份验证失败。请注意,管道之后的一切都可以正常工作。

$password = chomp($password);

### Create the bash system call to create the sparse bundle with the password
my $cmd = `echo $password | hdiutil create -size 200g -type SPARSEBUNDLE -encryption -stdinpass -volname \"Encrypted Storage for Matt\" -fs \"Case-sensitive Journaled HFS+\" -verbose ~/Desktop/SparseBundle`;

一些示例输出:

SLC1087-Matt:backups matt$ ./create_sparsebundle.pl 
DIDiskImageCreatorProbe: interface  1, score     1000, CSparseBundleDiskImage
DIDiskImageCreatorProbe: interface  2, score    -1000, CSparseDiskImage
DIDiskImageCreatorProbe: interface  3, score    -1000, CRawDiskImage
DIDiskImageCreatorProbe: interface  7, score    -1000, CWOUDIFDiskImage
DIDiskImageCreatorProbe: interface  9, score    -1000, CCFPlugInDiskImage
DIDiskImageCreateWithCFURL: CSparseBundleDiskImage
CBSDBackingStore::createProbe directory, not a valid image file.
DIBackingStoreCreatorProbe: interface  0, score    -1000, CBSDBackingStore
DIBackingStoreCreatorProbe: interface  1, score     1000, CBundleBackingStore
DIBackingStoreCreatorProbe: interface  2, score        0, CRAMBackingStore
DIBackingStoreCreatorProbe: interface  3, score      100, CCarbonBackingStore
DIBackingStoreCreatorProbe: interface  5, score     -100, CCURLBackingStore
DIBackingStoreCreateWithCFURL: CBundleBackingStore
DIFileEncodingCreatorProbe: interface  2, score     1000, CEncryptedEncoding
DIFileEncodingCreateWithCFURL: CEncryptedEncoding
DIFileEncodingCreatorProbe: interface  2, score    -1000, CEncryptedEncoding
DIBackingStoreCreatorProbe: interface  0, score      100, CBSDBackingStore
DIBackingStoreCreatorProbe: interface  1, score    -1000, CBundleBackingStore
DIBackingStoreCreatorProbe: interface  2, score        0, CRAMBackingStore
DIBackingStoreCreatorProbe: interface  3, score      100, CCarbonBackingStore
DIBackingStoreCreatorProbe: interface  5, score     -100, CCURLBackingStore
DIBackingStoreCreateWithCFURL: CBSDBackingStore
DIBackingStoreCreateWithCFURL: creator returned 0
DIFileEncodingCreateWithCFURL: creator returned 0
DIBackingStoreCreateWithCFURL: creator returned 0
DIDiskImageCreateWithCFURL: creator returned 0
DI_kextWaitQuiet: about to call IOServiceWaitQuiet...
DI_kextWaitQuiet: IOServiceWaitQuiet took 0.000003 seconds
2016-05-18 20:59:09.627 diskimages-helper[68122:1796245] *** -[NSMachPort handlePortMessage:]: dropping incoming DO message because the connection is invalid
hdiutil: create: returning 0
SLC1087-Matt:backups matt$ hdiutil attach ~/Desktop/SparseBundle.sparsebundle
Enter password to access "SparseBundle.sparsebundle": 
hdiutil: attach failed - Authentication error

似乎一切正常,但密码显然错误。

【问题讨论】:

  • 只打印密码会发生什么? system("echo $password")?
  • 您是否验证过这在 bash 中确实有效? - 即没有 perl。我遇到的一些应用程序使用密码做一些时髦的事情,并且不会使用那种格式来获取东西。所以打开 bash 并运行 echo TheRealPassword | hdiutil create -size 200g -type SPARSEBUNDLE -encryption -stdinpass -volname \"Encrypted Storage for Matt\" -fs \"Case-sensitive Journaled HFS+\" -verbose ~/Desktop/SparseBundle
  • @GregHNZ,为什么?这不是他使用 Perl 传递给 shell 的内容。
  • @Matt O.,它通过 STDIN 而不是作为参数获取密码的原因是机器上的其他用户可以看到命令。使用 IPC::Run3 的run3 来做正确的事情
  • 在你做$password = chomp($password);之后马上放print $password;是你期望的吗?

标签: perl system


【解决方案1】:

了解chomp 返回的内容。您的代码至少用整数可能01 重写了$password 变量。这引起了我的注意。


chomp 返回从其所有参数中删除的字符总数。

【讨论】:

  • 我删除了chomp,但我仍然遇到问题。我会更新我在顶部显示的内容。
【解决方案2】:

hdiutil 实用程序需要一个以空字符结尾的密码。

也就是说,"mypass" 不够好。它需要看到"mypass\0",因为密码允许包含有趣的字符,例如嵌入的换行符。

所以,它看到的是"mypass\n" 而不是"mypass\0"

可以试试这个,因为它可以在 linux 下工作:

echo -n -e "mypass\x00" | hdiutil ...

但是……AFAICT,OSX 版本的 echo 没有-e,所以你可能需要试验一下。

如果所有其他方法都失败,请将echo 替换为:

echo "mypass" | perl -e '$_ = <STDIN>; chomp ; print $_,"\x00"'

可能有更简单的方法来做/表达这一点。您可以创建第二个 perl 脚本(称为 passme):

#!/usr/bin/perl
print $ARGV[0],"\x00";

然后将echo 替换为passme。我在参数两边加上双引号。


更新:

我很确定由于标准输入密码,printf 必须通过管道传输到命令。我不知道我是否完全正确。

echoprintf 将通过管道传送到命令。但是,我会推荐更多的方法来做到这一点。

正如其他人所提到的,最干净的方法可能是使用IPC::Run3。这是一站式购物。

但是,它可能不会默认安装在您的系统 [或其他 OSX 系统] 上。不知道--YMMV。您可能需要对此进行调查。因此,您可能必须自己安装它 [来自 CPAN,我猜是(?)]

如果您的脚本供您自己使用,上述可能没问题。但是,如果您正在创建供其他人使用的脚本,它可能最终会出现在没有 Run3 的系统上,并且 sysadm 不会安装它。

因此,您必须在易用性与普遍性之间取得平衡。这将是您的选择。

Run3 bothhdiutil 命令上设置stdinstdout,这是你需要的,因为你想给它密码 捕获它的输出。

如果你只需要一个,你可以在“管道”模式下使用 perl 的open 函数。有关详细信息,请参阅man perlipc。使用起来非常简单。我有一个例子——见下文。

或者,您可以通过使用 perl 的内在 pipe 调用“滚动自己的”等效于 run3 所做的事情。您将需要 两个 管道。这有点工作,所以也许是未来的事情。

echoprintf、[和passme] 方法的最大问题是[正如其他人指出的那样],因为他们使用 argv 来获取密码,他们将其“泄露”到其他进程[简要]。

所以,我们希望有一种安全的方法 [e.g. Run3 将是]。这就是为什么hdiutilstdin 上使用密码而不是命令行参数的原因。


(1) 实际上,最简单的方法可能是将密码转储到临时文件中:

use Fcntl;

my $fd;
my $tmp = "/tmp/pwfile";

my $password = "whatever";

# NOTE: by using sysopen and setting the 600 permission atomically, we avoid a
# race condition that may allow another user to open this file
unlink($tmp);
sysopen($fd,$tmp,O_WRONLY | O_CREAT | O_EXCL,0600) or
    die("unable to open '$tmp' -- $!\n");
syswrite($fd,$password . "\x00");
close($fd)

my $cmd = `hdiutil ... < $tmp`;
unlink($tmp);

(2) 如果您不想将密码写入临时文件,不管它可能会在多长时间内徘徊,还有另一种方法。它类似于上面的passme 脚本方法,except 它将密码作为环境变量传递。这样可以避免通过命令行参数暴露密码。

这是passme 变体:

#!/usr/bin/perl
print($ENV{"PASSME"},"\x00");

这也可以通过内联来完成:

perl -e 'print($ENV{"PASSME"},"\x00")'

然后,你的脚本变成:

my $password = "whatever";

$ENV{"PASSME"} = $password;
my $cmd = `passme | hdiutil ...`;
delete($ENV{"PASSME"});

(3) 还有一种方法是在管道模式下使用[上述] perl 的open。将其设置为hdiutil 的输入管道,并将输出转移到您在命令执行后读回的临时文件。

my $fdout;
my $fdin;
my $tmp = "/tmp/results";
my $buf;

my $password = "whatever";

open($fdout,"| hdiutil ... > $tmp") ||
    die("unable to open hdiutil pope -- $!\n");
print($fdout $password,"\x00");
close($fdout);

open($fdin,"<$tmp") ||
    die("unable to open '$tmp' -- $!\n");
while ($buf = <$fdin>) {
    chomp($buf);
    # do whatever ...
}
close($fdin);

【讨论】:

  • 也可以不用三个无关的进程,不用向机器上的每个人显示密码,使用use IPC::Run3 qw( run3 ); run3([ 'hdutil', ... ], \"$password\0", \my $stdout);
  • 不,它看到的是"1\n",因为他将$password 指定为chomp 的返回值。
  • 为了便携性:使用printf而不是echo -e
  • 我很确定由于 stdinpass 必须将 printf 传送到命令。我不知道我是否完全正确。
  • 那么大的并发症是什么? hdutil 可以通过检测 \0 结束字符或通过检测标准输入上的 EOF 来接受字符串的结尾。我们对密码一无所知,可能其中包含一些由 bash 解释的特殊序列或字符。然后我建议用引号将密码变量括起来。这就是我们所需要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-06
  • 1970-01-01
  • 2018-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多