mirror of
https://github.com/qpdf/qpdf.git
synced 2025-01-02 22:50:20 +00:00
Add --password-file=filename option (fixes #499)
This commit is contained in:
parent
21b0f4acfc
commit
63158cf546
@ -1,5 +1,9 @@
|
||||
2021-02-04 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* Add new option --pasword-file=file for reading the decryption
|
||||
password from a file. file may be "-" to read from standard input.
|
||||
Fixes #499.
|
||||
|
||||
* By default, give an error if a user attempts to encrypt a file
|
||||
with an empty owner password or an owner password that is the same
|
||||
as the user password. Such files are insecure. Most viewers either
|
||||
|
@ -571,13 +571,17 @@ make
|
||||
linkend="ref.page-selection"/>.
|
||||
</para>
|
||||
<para>
|
||||
If <option>@filename</option> appears anywhere in the
|
||||
If <option>@filename</option> appears as a word anywhere in the
|
||||
command-line, it will be read line by line, and each line will be
|
||||
treated as a command-line argument. The <option>@-</option> option
|
||||
allows arguments to be read from standard input. This allows qpdf
|
||||
to be invoked with an arbitrary number of arbitrarily long
|
||||
arguments. It is also very useful for avoiding having to pass
|
||||
passwords on the command line.
|
||||
passwords on the command line. Note that the
|
||||
<option>@filename</option> can't appear in the middle of an
|
||||
argument, so constructs such as <option>--arg=@option</option>
|
||||
will not work. You would have to include the argument and its
|
||||
options together in the arguments file.
|
||||
</para>
|
||||
<para>
|
||||
<option>outfilename</option> does not have to be seekable, even
|
||||
@ -714,14 +718,34 @@ make
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--password=password</option></term>
|
||||
<term><option>--password=<replaceable>password</replaceable></option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Specifies a password for accessing encrypted files. Note that
|
||||
you can use <option>@filename</option> or <option>@-</option>
|
||||
as described above to put the password in a file or pass it
|
||||
via standard input so you can avoid specifying it on the
|
||||
command line.
|
||||
Specifies a password for accessing encrypted files. To read
|
||||
the password from a file or standard input, you can use
|
||||
<option>--password-file</option>, added in qpdf 10.2. Note
|
||||
that you can also use <option>@filename</option> or
|
||||
<option>@-</option> as described above to put the password in
|
||||
a file or pass it via standard input, but you would do so by
|
||||
specifying the entire
|
||||
<option>--password=<replaceable>password</replaceable></option>
|
||||
option in the file. Syntax such as
|
||||
<option>--password=@filename</option> won't work since
|
||||
<option>@filename</option> is not recognized in the middle of
|
||||
an argument.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--password-file=<replaceable>filename</replaceable></option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Reads the first line from the specified file and uses it as
|
||||
the password for accessing encrypted files.
|
||||
<option><replaceable>filename</replaceable></option> may be
|
||||
<literal>-</literal> to read the password from standard input.
|
||||
Note that, in this case, the password is echoed and there is
|
||||
no prompt, so use with caution.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -4884,6 +4908,24 @@ print "\n";
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
CLI Enhancements
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
The option
|
||||
<option>--password-file=<replaceable>filename</replaceable></option>
|
||||
can now be used to read the decryption password from a file.
|
||||
You can use <literal>-</literal> as the file name to read
|
||||
the password from standard input. This is an easier/more
|
||||
obvious way to read passwords from files or standard input
|
||||
than using <option>@file</option> for this purpose.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Library Enhancements
|
||||
|
41
qpdf/qpdf.cc
41
qpdf/qpdf.cc
@ -4,6 +4,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <memory>
|
||||
|
||||
#include <qpdf/QUtil.hh>
|
||||
#include <qpdf/QTC.hh>
|
||||
@ -199,6 +200,7 @@ struct Options
|
||||
}
|
||||
|
||||
char const* password;
|
||||
std::shared_ptr<char> password_alloc;
|
||||
bool linearize;
|
||||
bool decrypt;
|
||||
int split_pages;
|
||||
@ -739,6 +741,7 @@ class ArgParser
|
||||
void argShowCrypto();
|
||||
void argPositional(char* arg);
|
||||
void argPassword(char* parameter);
|
||||
void argPasswordFile(char* paramter);
|
||||
void argEmpty();
|
||||
void argLinearize();
|
||||
void argEncrypt();
|
||||
@ -955,6 +958,8 @@ ArgParser::initOptionTable()
|
||||
(*t)[""] = oe_positional(&ArgParser::argPositional);
|
||||
(*t)["password"] = oe_requiredParameter(
|
||||
&ArgParser::argPassword, "password");
|
||||
(*t)["password-file"] = oe_requiredParameter(
|
||||
&ArgParser::argPasswordFile, "password-file");
|
||||
(*t)["empty"] = oe_bare(&ArgParser::argEmpty);
|
||||
(*t)["linearize"] = oe_bare(&ArgParser::argLinearize);
|
||||
(*t)["encrypt"] = oe_bare(&ArgParser::argEncrypt);
|
||||
@ -1235,6 +1240,9 @@ ArgParser::argHelp()
|
||||
<< "--completion-bash output a bash complete command you can eval\n"
|
||||
<< "--completion-zsh output a zsh complete command you can eval\n"
|
||||
<< "--password=password specify a password for accessing encrypted files\n"
|
||||
<< "--password-file=file get the password the first line \"file\"; use \"-\"\n"
|
||||
<< " to read the password from stdin (without prompt or\n"
|
||||
<< " disabling echo, so use with caution)\n"
|
||||
<< "--is-encrypted silently exit 0 if the file is encrypted or 2\n"
|
||||
<< " if not; useful for shell scripts\n"
|
||||
<< "--requires-password silently exit 0 if a password (other than as\n"
|
||||
@ -1273,7 +1281,8 @@ ArgParser::argHelp()
|
||||
<< "\n"
|
||||
<< "Note that you can use the @filename or @- syntax for any argument at any\n"
|
||||
<< "point in the command. This provides a good way to specify a password without\n"
|
||||
<< "having to explicitly put it on the command line.\n"
|
||||
<< "having to explicitly put it on the command line. @filename or @- must be a\n"
|
||||
<< "word by itself. Syntax such as --arg=@filename doesn't work.\n"
|
||||
<< "\n"
|
||||
<< "If none of --copy-encryption, --encrypt or --decrypt are given, qpdf will\n"
|
||||
<< "preserve any encryption data associated with a file.\n"
|
||||
@ -1749,6 +1758,36 @@ ArgParser::argPassword(char* parameter)
|
||||
o.password = parameter;
|
||||
}
|
||||
|
||||
void
|
||||
ArgParser::argPasswordFile(char* parameter)
|
||||
{
|
||||
std::list<std::string> lines;
|
||||
if (strcmp(parameter, "-") == 0)
|
||||
{
|
||||
QTC::TC("qpdf", "qpdf password stdin");
|
||||
lines = QUtil::read_lines_from_file(std::cin);
|
||||
}
|
||||
else
|
||||
{
|
||||
QTC::TC("qpdf", "qpdf password file");
|
||||
lines = QUtil::read_lines_from_file(parameter);
|
||||
}
|
||||
if (lines.size() >= 1)
|
||||
{
|
||||
// Make sure the memory for this stays in scope.
|
||||
o.password_alloc = std::shared_ptr<char>(
|
||||
QUtil::copy_string(lines.front().c_str()),
|
||||
std::default_delete<char[]>());
|
||||
o.password = o.password_alloc.get();
|
||||
|
||||
if (lines.size() > 1)
|
||||
{
|
||||
std::cerr << whoami << ": WARNING: all but the first line of"
|
||||
<< " the password file are ignored" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ArgParser::argEmpty()
|
||||
{
|
||||
|
@ -568,3 +568,5 @@ NNTree erased last item in tree 0
|
||||
NNTree remove limits from root 0
|
||||
QPDFPageObjectHelper unresolved names 0
|
||||
QPDFPageObjectHelper resolving unresolved 0
|
||||
qpdf password stdin 0
|
||||
qpdf password file 0
|
||||
|
@ -270,7 +270,7 @@ my @check_encryption_password = (
|
||||
["20-pages.pdf", "", 0, 0],
|
||||
["20-pages.pdf", "user", 0, 3],
|
||||
);
|
||||
$n_tests += 2 * scalar(@check_encryption_password);
|
||||
$n_tests += 3 * scalar(@check_encryption_password);
|
||||
foreach my $d (@check_encryption_password)
|
||||
{
|
||||
my ($file, $pass, $is_encrypted, $requires_password) = @$d;
|
||||
@ -283,6 +283,29 @@ foreach my $d (@check_encryption_password)
|
||||
{$td->STRING => "", $td->EXIT_STATUS => $requires_password});
|
||||
}
|
||||
|
||||
# Exercise reading password from file
|
||||
open(F, ">args") or die;
|
||||
print F "user\n";
|
||||
close(F);
|
||||
$td->runtest("password from file)",
|
||||
{$td->COMMAND => "qpdf --check --password-file=args 20-pages.pdf"},
|
||||
{$td->FILE => "20-pages-check.out", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
open(F, ">>args") or die;
|
||||
print F "ignored\n";
|
||||
close(F);
|
||||
$td->runtest("ignore extra args from file)",
|
||||
{$td->COMMAND => "qpdf --check --password-file=args 20-pages.pdf"},
|
||||
{$td->FILE => "20-pages-check-password-warning.out",
|
||||
$td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
unlink "args";
|
||||
$td->runtest("password from stdin)",
|
||||
{$td->COMMAND => "echo user |" .
|
||||
" qpdf --check --password-file=- 20-pages.pdf"},
|
||||
{$td->FILE => "20-pages-check.out", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- Dangling Refs ---");
|
||||
|
19
qpdf/qtest/qpdf/20-pages-check-password-warning.out
Normal file
19
qpdf/qtest/qpdf/20-pages-check-password-warning.out
Normal file
@ -0,0 +1,19 @@
|
||||
qpdf: WARNING: all but the first line of the password file are ignored
|
||||
checking 20-pages.pdf
|
||||
PDF Version: 1.4
|
||||
R = 3
|
||||
P = -4
|
||||
User password = user
|
||||
Supplied password is user password
|
||||
extract for accessibility: allowed
|
||||
extract for any purpose: allowed
|
||||
print low resolution: allowed
|
||||
print high resolution: allowed
|
||||
modify document assembly: allowed
|
||||
modify forms: allowed
|
||||
modify annotations: allowed
|
||||
modify other: allowed
|
||||
modify anything: allowed
|
||||
File is not linearized
|
||||
No syntax or stream encoding errors found; the file may still contain
|
||||
errors that qpdf cannot detect
|
18
qpdf/qtest/qpdf/20-pages-check.out
Normal file
18
qpdf/qtest/qpdf/20-pages-check.out
Normal file
@ -0,0 +1,18 @@
|
||||
checking 20-pages.pdf
|
||||
PDF Version: 1.4
|
||||
R = 3
|
||||
P = -4
|
||||
User password = user
|
||||
Supplied password is user password
|
||||
extract for accessibility: allowed
|
||||
extract for any purpose: allowed
|
||||
print low resolution: allowed
|
||||
print high resolution: allowed
|
||||
modify document assembly: allowed
|
||||
modify forms: allowed
|
||||
modify annotations: allowed
|
||||
modify other: allowed
|
||||
modify anything: allowed
|
||||
File is not linearized
|
||||
No syntax or stream encoding errors found; the file may still contain
|
||||
errors that qpdf cannot detect
|
Loading…
Reference in New Issue
Block a user