Perl系统调用无法正确解释变量

时间:2016-05-19 03:39:34

标签: perl system

我使用Perl将字符串($ password)传递给要解释的系统行。它似乎没有被正确解释,因为当我去附加稀疏捆绑时,它说认证失败了。作为一个注释,管道后的一切工作正常。

$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

一切似乎都运行良好,但密码显然是错误的。

2 个答案:

答案 0 :(得分:6)

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

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

答案 1 :(得分:1)

hdiutil实用程序需要以空值终止的密码。

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

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

可以试试这个,因为它可以在linux下运行:

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

但...... AFAICT,回声的OSX版本没有-e,因此您可能需要进行试验。

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

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

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

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

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

<强>更新

  

我非常确定,因为stdpass必须将printf传送到命令。我不知道我是否完全正确。

echoprintf将通过管道输入命令。但是,还有更多方法可以做到这一点,我会推荐这些方法。

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

但是,默认情况下可能不会在您的系统[或其他OSX系统]上安装它。说不上 - 因人而异。您可能需要对此进行调查。所以,你可能必须自己安装它[来自CPAN,我推测(?)]

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

所以,你必须平衡易用性和无处不在。那将是你的选择。

Run3在stdin命令上设置 stdouthdiutil,这是您需要的,因为您要为其提供密码捕获其输出。

如果你只需要一个,你可以在&#34; pipe&#34;中使用perl的open功能。模式。有关详细信息,请参阅man perlipc。它使用起来非常简单。我有一个例子 - 见下文。

或者,你可以自己推动自己的&#34;使用perl的内在run3调用等同于pipe所做的事情。您需要两个管道。这是一项工作,所以也许是未来的事情。

echoprintf,[和passme]方法的最大问题是[正如其他人所指出的],是因为他们使用argv来获取密码,他们& #34;流血&#34;它简要地说到其他过程。

所以,我们希望有一种安全的方法[例如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脚本方法,除了它将密码作为环境变量传递下去。这样可以避免通过命令行参数泄露密码。

这是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);