mirror of
https://github.com/qpdf/qpdf.git
synced 2025-01-05 08:02:11 +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>
|
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
|
* 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
|
with an empty owner password or an owner password that is the same
|
||||||
as the user password. Such files are insecure. Most viewers either
|
as the user password. Such files are insecure. Most viewers either
|
||||||
|
@ -571,13 +571,17 @@ make
|
|||||||
linkend="ref.page-selection"/>.
|
linkend="ref.page-selection"/>.
|
||||||
</para>
|
</para>
|
||||||
<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
|
command-line, it will be read line by line, and each line will be
|
||||||
treated as a command-line argument. The <option>@-</option> option
|
treated as a command-line argument. The <option>@-</option> option
|
||||||
allows arguments to be read from standard input. This allows qpdf
|
allows arguments to be read from standard input. This allows qpdf
|
||||||
to be invoked with an arbitrary number of arbitrarily long
|
to be invoked with an arbitrary number of arbitrarily long
|
||||||
arguments. It is also very useful for avoiding having to pass
|
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>
|
||||||
<para>
|
<para>
|
||||||
<option>outfilename</option> does not have to be seekable, even
|
<option>outfilename</option> does not have to be seekable, even
|
||||||
@ -714,14 +718,34 @@ make
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>--password=password</option></term>
|
<term><option>--password=<replaceable>password</replaceable></option></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Specifies a password for accessing encrypted files. Note that
|
Specifies a password for accessing encrypted files. To read
|
||||||
you can use <option>@filename</option> or <option>@-</option>
|
the password from a file or standard input, you can use
|
||||||
as described above to put the password in a file or pass it
|
<option>--password-file</option>, added in qpdf 10.2. Note
|
||||||
via standard input so you can avoid specifying it on the
|
that you can also use <option>@filename</option> or
|
||||||
command line.
|
<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>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -4884,6 +4908,24 @@ print "\n";
|
|||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</listitem>
|
</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>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Library Enhancements
|
Library Enhancements
|
||||||
|
41
qpdf/qpdf.cc
41
qpdf/qpdf.cc
@ -4,6 +4,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <qpdf/QUtil.hh>
|
#include <qpdf/QUtil.hh>
|
||||||
#include <qpdf/QTC.hh>
|
#include <qpdf/QTC.hh>
|
||||||
@ -199,6 +200,7 @@ struct Options
|
|||||||
}
|
}
|
||||||
|
|
||||||
char const* password;
|
char const* password;
|
||||||
|
std::shared_ptr<char> password_alloc;
|
||||||
bool linearize;
|
bool linearize;
|
||||||
bool decrypt;
|
bool decrypt;
|
||||||
int split_pages;
|
int split_pages;
|
||||||
@ -739,6 +741,7 @@ class ArgParser
|
|||||||
void argShowCrypto();
|
void argShowCrypto();
|
||||||
void argPositional(char* arg);
|
void argPositional(char* arg);
|
||||||
void argPassword(char* parameter);
|
void argPassword(char* parameter);
|
||||||
|
void argPasswordFile(char* paramter);
|
||||||
void argEmpty();
|
void argEmpty();
|
||||||
void argLinearize();
|
void argLinearize();
|
||||||
void argEncrypt();
|
void argEncrypt();
|
||||||
@ -955,6 +958,8 @@ ArgParser::initOptionTable()
|
|||||||
(*t)[""] = oe_positional(&ArgParser::argPositional);
|
(*t)[""] = oe_positional(&ArgParser::argPositional);
|
||||||
(*t)["password"] = oe_requiredParameter(
|
(*t)["password"] = oe_requiredParameter(
|
||||||
&ArgParser::argPassword, "password");
|
&ArgParser::argPassword, "password");
|
||||||
|
(*t)["password-file"] = oe_requiredParameter(
|
||||||
|
&ArgParser::argPasswordFile, "password-file");
|
||||||
(*t)["empty"] = oe_bare(&ArgParser::argEmpty);
|
(*t)["empty"] = oe_bare(&ArgParser::argEmpty);
|
||||||
(*t)["linearize"] = oe_bare(&ArgParser::argLinearize);
|
(*t)["linearize"] = oe_bare(&ArgParser::argLinearize);
|
||||||
(*t)["encrypt"] = oe_bare(&ArgParser::argEncrypt);
|
(*t)["encrypt"] = oe_bare(&ArgParser::argEncrypt);
|
||||||
@ -1235,6 +1240,9 @@ ArgParser::argHelp()
|
|||||||
<< "--completion-bash output a bash complete command you can eval\n"
|
<< "--completion-bash output a bash complete command you can eval\n"
|
||||||
<< "--completion-zsh output a zsh 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=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"
|
<< "--is-encrypted silently exit 0 if the file is encrypted or 2\n"
|
||||||
<< " if not; useful for shell scripts\n"
|
<< " if not; useful for shell scripts\n"
|
||||||
<< "--requires-password silently exit 0 if a password (other than as\n"
|
<< "--requires-password silently exit 0 if a password (other than as\n"
|
||||||
@ -1273,7 +1281,8 @@ ArgParser::argHelp()
|
|||||||
<< "\n"
|
<< "\n"
|
||||||
<< "Note that you can use the @filename or @- syntax for any argument at any\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"
|
<< "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"
|
<< "\n"
|
||||||
<< "If none of --copy-encryption, --encrypt or --decrypt are given, qpdf will\n"
|
<< "If none of --copy-encryption, --encrypt or --decrypt are given, qpdf will\n"
|
||||||
<< "preserve any encryption data associated with a file.\n"
|
<< "preserve any encryption data associated with a file.\n"
|
||||||
@ -1749,6 +1758,36 @@ ArgParser::argPassword(char* parameter)
|
|||||||
o.password = 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
|
void
|
||||||
ArgParser::argEmpty()
|
ArgParser::argEmpty()
|
||||||
{
|
{
|
||||||
|
@ -568,3 +568,5 @@ NNTree erased last item in tree 0
|
|||||||
NNTree remove limits from root 0
|
NNTree remove limits from root 0
|
||||||
QPDFPageObjectHelper unresolved names 0
|
QPDFPageObjectHelper unresolved names 0
|
||||||
QPDFPageObjectHelper resolving unresolved 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", "", 0, 0],
|
||||||
["20-pages.pdf", "user", 0, 3],
|
["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)
|
foreach my $d (@check_encryption_password)
|
||||||
{
|
{
|
||||||
my ($file, $pass, $is_encrypted, $requires_password) = @$d;
|
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});
|
{$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();
|
show_ntests();
|
||||||
# ----------
|
# ----------
|
||||||
$td->notify("--- Dangling Refs ---");
|
$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