November 2019

S M T W T F S
      12
34 5 678 9
10111213141516
17181920212223
24252627282930

Style Credit

Expand Cut Tags

No cut tags
Saturday, March 3rd, 2012 03:29 pm

Возникла у меня тут необходимость запускать gpg в пакетном режиме, без взаимодействия с пользователем. Точнее, пользователь должен один раз ввести пароль от ключа, и больше не вмешиваться.

Нашел модуль GnuPG, но оказалось, что он полагается на shared memory, а в нынешнем gpg2 эта фича не поддерживается. Теперь надо а gpg все дополнительные данные скармливать через файловые дескрипторы. Пробую запустить из комнадной строки:

/usr/bin/gpg --decrypt --batch --passphrase-fd 3 encrypted.file 3<password.file

Работает. Ну ладно, будем запускать  то же самое из перла, только без файла, а напрямую скармливая пароль через пайп.

В целом вроде бы тоже работает, gpg запускается, зашифрованный файл находит, даже показывает, каким ключом он зашифрован, но… расшифровать не может, поскольку не видит пароля:

can't connect to `/home/dil/.gnupg/S.gpg-agent': No such file or directory
gpg: encrypted with 2048-bit RSA key, ID 673056FF, created 2012-02-17
"юзер <почтовый адрес>"
gpg: public key decryption failed: Bad passphrase
gpg: decryption failed: No secret key

Пробую напечатать пароль  непосредственно в перле из дочернего процесса, прочитав его из пайпа. Нормально читается и печатается. А gpg из того же пайпа этот пароль в упор не видит.

Вопрос для продвинутых программистов и сисадминов: где засада?

Подсказка: проблема решилась после добавления в скрипт пары строчек. Угадайте, каких.

Исходник на перле под катом.

#!/usr/bin/perl -w
my ($pwread, $pwwrite);
pipe($pwread, $pwwrite);

my $dataread;
my $pid = open $dataread, "-|";
die "Cannot fork: $!\n" unless defined($pid); # fork error

if($pid) { # parent process
  close($pwread);
  print $pwwrite 'password';
  close($pwwrite);
  $DATA = <$dataread>;
  unless(defined($DATA) && length($DATA) > 0) {
    die "Failed to read data\n";
  }
  chomp $DATA;
  print "data='$DATA'\n";
  close($dataread);
  waitpid($pid, 0);
  exit 0;
}

# child
close($pwwrite);
my $pwfd = fileno($pwread); # узнаём номер файлового дескриптора
print STDERR "pwfd=$pwfd\n";

# my $d = <$pwread>; # вот если читать пароль тут, то он нормально читается
# print STDERR "d='$d'\n"; # и пишется
# print STDOUT "some fake data here\n"; # потом можно подсунуть родительскому процессу данные, якобы от расшифрованного файла, и он их тоже нормально получает
# exit 0;

# а вот если запустить gpg, то пароль из указанного дексриптора почему-то не виден
exec('/usr/bin/gpg',
  '--decrypt',
  '--batch',
  '--passphrase-fd', $pwfd,
  'encrypted.file',
) or die "Cannot exec gpg: $!\n";
exit 0; # child exits anyhow;

Оригинал этой записи в личном блоге.
Любые материалы из этого блога запрещается использовать на сайте livejournal.ru в любой форме и любом объёме.

Saturday, March 3rd, 2012 04:22 pm (UTC)
что-то я не уверен, что можно так попросту номер дескриптора передавать. как-нибудь так:
my $d = <$pwread>
echo $d | gpg --passphrase-fd 0
Saturday, March 3rd, 2012 07:19 pm (UTC)
а разве кто-то обещал, что номер файлового дескриптора в пределах ОС уникален?
ну и "FD_CLOEXEC, the close-on-exec flag. If the FD_CLOEXEC bit is 0, the file descriptor will remain open across an execve(2), otherwise it will be closed", так что не удивлюсь если оно тупо делает fcloseall при exec.
Saturday, March 3rd, 2012 09:07 pm (UTC)
для процесса - да, для форка - да, для процесса, который порождает exec - мне неочевидно. где такое сказано?
Sunday, March 4th, 2012 12:26 pm (UTC)
stdin,stdout,stderr прибиты гвоздями к каждому процессу:)
да, забыл про особенности exec. давно ничего такого не писал:(
Saturday, March 3rd, 2012 04:51 pm (UTC)
На подобные вопросы я давно уже отвечаю только за деньги...
Saturday, March 3rd, 2012 06:45 pm (UTC)
Хех. Размягчение мозгов для меня - не цель. ;)
Saturday, March 3rd, 2012 08:35 pm (UTC)
Разминание иногда размягчает.