Beginning to address recryption properly

This commit is contained in:
John Whitington 2014-10-27 16:48:08 +00:00
parent f689fa8f20
commit 88ad70ff80
1 changed files with 70 additions and 42 deletions

View File

@ -3,7 +3,7 @@ let demo = false
let noncomp = false let noncomp = false
let major_version = 1 let major_version = 1
let minor_version = 8 let minor_version = 8
let version_date = "(unreleased, 16th September 2014)" let version_date = "(unreleased, 27th October 2014)"
open Pdfutil open Pdfutil
open Pdfio open Pdfio
@ -355,7 +355,8 @@ type args =
mutable original_filename : string; mutable original_filename : string;
mutable was_encrypted : bool; mutable was_encrypted : bool;
mutable cpdflin : string option; mutable cpdflin : string option;
mutable recrypt : bool} mutable recrypt : bool;
mutable was_decrypted_with_owner : bool}
(* List of all filenames in any AND stage - this is used to check that we don't (* List of all filenames in any AND stage - this is used to check that we don't
overwrite any input file when -dont-overwrite-existing-files is used. *) overwrite any input file when -dont-overwrite-existing-files is used. *)
@ -436,7 +437,8 @@ let args =
original_filename = ""; original_filename = "";
was_encrypted = false; was_encrypted = false;
cpdflin = None; cpdflin = None;
recrypt = false} recrypt = false;
was_decrypted_with_owner = false}
let reset_arguments () = let reset_arguments () =
args.op <- None; args.op <- None;
@ -508,9 +510,25 @@ let reset_arguments () =
args.labelstyle <- Pdfpagelabels.DecimalArabic; args.labelstyle <- Pdfpagelabels.DecimalArabic;
args.labelprefix <- None; args.labelprefix <- None;
args.labelstartval <- 1; args.labelstartval <- 1;
args.squeeze <- false; args.squeeze <- false
args.recrypt <- false (* Do not reset original_filename or cpdflin or was_encrypted or
(* Do not reset original_filename or cpdflin or was_encrypted, since we want it to work across ANDs. *) * was_decrypted_with_owner or recrypt, since we want it to work across ANDs. *)
let string_of_permission = function
| Pdfcrypt.NoEdit -> "No edit"
| Pdfcrypt.NoPrint -> "No print"
| Pdfcrypt.NoCopy -> "No copy"
| Pdfcrypt.NoAnnot -> "No annotate"
| Pdfcrypt.NoForms -> "No edit forms"
| Pdfcrypt.NoExtract -> "No extract"
| Pdfcrypt.NoAssemble -> "No assemble"
| Pdfcrypt.NoHqPrint -> "No high-quality print"
let getpermissions pdf =
fold_left
(fun x y -> if x = "" then x ^ y else x ^ ", " ^ y)
""
(map string_of_permission (Pdfread.permissions pdf))
let banlist_of_args () = let banlist_of_args () =
let l = ref [] in let l = ref [] in
@ -532,14 +550,31 @@ performed is checked to see if it's allowable under the permissions regime. *)
bans list in the input file, the operation cannot proceed. Other operations bans list in the input file, the operation cannot proceed. Other operations
cannot proceed at all without owner password. *) cannot proceed at all without owner password. *)
let banned banlist = function let banned banlist = function
| Fonts | Info | Metadata | PageInfo | CountPages -> false (* Always allowed *) | Fonts | Info | Metadata | PageInfo | CountPages
| ListAttachedFiles | ListAnnotationsMore | ListAnnotations
| ListBookmarks | ImageResolution _ | MissingFonts
| PrintPageLabels | Clean | Compress | Decompress
| RemoveUnusedResources -> false (* Always allowed *)
| Decrypt | Encrypt -> true (* Never allowed *) | Decrypt | Encrypt -> true (* Never allowed *)
| ExtractText | ExtractImages | ExtractFontFile -> mem Pdfcrypt.NoExtract banlist
| AddBookmarks _ | PadBefore | PadAfter | PadEvery _ | PadMultiple _
| Merge | Split | SplitOnBookmarks _ | RotateContents _ | Rotate _
| Rotateby _ | Upright | VFlip | HFlip | SetPageLayout _
| SetPageMode _ | HideToolbar _ | HideMenubar _ | HideWindowUI _
| FitWindow _ | CenterWindow _ | DisplayDocTitle _ | ChangeId
| RemoveId | CopyId _ | OpenAtPageFit _ | OpenAtPage _
| AddPageLabels | RemovePageLabels -> mem Pdfcrypt.NoAssemble banlist
| _ -> mem Pdfcrypt.NoEdit banlist | _ -> mem Pdfcrypt.NoEdit banlist
let operation_allowed banlist op = let operation_allowed pdf banlist op =
match op with match op with
| None -> true (* Merge *) (* changed to allow it *) | None ->
| Some op -> not (banned banlist op) Printf.printf "operation is None, so allowed!\n";
true (* Merge *) (* changed to allow it *)
| Some op ->
if args.debugcrypt then Printf.printf "operation_allowed: op = %s\n" (string_of_op op);
if args.debugcrypt then Printf.printf "Permissions: %s\n" (getpermissions pdf);
not (banned banlist op)
let rec decrypt_if_necessary (_, _, _, user_pw, owner_pw) op pdf = let rec decrypt_if_necessary (_, _, _, user_pw, owner_pw) op pdf =
if args.debugcrypt then if args.debugcrypt then
@ -550,13 +585,15 @@ let rec decrypt_if_necessary (_, _, _, user_pw, owner_pw) op pdf =
if not (Pdfcrypt.is_encrypted pdf) then pdf else if not (Pdfcrypt.is_encrypted pdf) then pdf else
match Pdfcrypt.decrypt_pdf_owner owner_pw pdf with match Pdfcrypt.decrypt_pdf_owner owner_pw pdf with
| Some pdf -> | Some pdf ->
if args.debugcrypt then Printf.printf "Managed to decrypt with owner password\n"; pdf args.was_decrypted_with_owner <- true;
if args.debugcrypt then Printf.printf "Managed to decrypt with owner password\n";
pdf
| _ -> | _ ->
if args.debugcrypt then Printf.printf "Couldn't decrypt with owner password %s\n" owner_pw; if args.debugcrypt then Printf.printf "Couldn't decrypt with owner password %s\n" owner_pw;
match Pdfcrypt.decrypt_pdf user_pw pdf with match Pdfcrypt.decrypt_pdf user_pw pdf with
| Some pdf, permissions -> | Some pdf, permissions ->
if args.debugcrypt then Printf.printf "Managed to decrypt with user password\n"; if args.debugcrypt then Printf.printf "Managed to decrypt with user password\n";
if operation_allowed permissions op if operation_allowed pdf permissions op
then pdf then pdf
else soft_error "User password cannot give permission for this operation" else soft_error "User password cannot give permission for this operation"
| _ -> | _ ->
@ -1851,7 +1888,9 @@ let get_pdf_from_input_kind ((_, _, _, u, o) as input) op = function
end; end;
begin try Hashtbl.find filenames s with begin try Hashtbl.find filenames s with
Not_found -> Not_found ->
let pdf = decrypt_if_necessary input op (pdfread_pdf_of_file (optstring u) (optstring o) s) in let pdf = pdfread_pdf_of_file (optstring u) (optstring o) s in
args.was_encrypted <- Pdfcrypt.is_encrypted pdf;
let pdf = decrypt_if_necessary input op pdf in
Hashtbl.add filenames s pdf; pdf Hashtbl.add filenames s pdf; pdf
end end
| StdIn -> | StdIn ->
@ -1891,11 +1930,14 @@ let get_single_pdf_nodecrypt read_lazy =
raise (Arg.Bad "cpdf: No input specified.\n") raise (Arg.Bad "cpdf: No input specified.\n")
let really_write_pdf ?(encryption = None) mk_id pdf outname = let really_write_pdf ?(encryption = None) mk_id pdf outname =
if args.debugcrypt then Printf.printf "really_write_pdf\n";
let outname' = let outname' =
if args.linearize if args.linearize
then Filename.temp_file "cpdflin" ".pdf" then Filename.temp_file "cpdflin" ".pdf"
else outname else outname
in in
if args.debugcrypt then
Printf.printf "args.recrypt = %b, args.was_encrypted = %b\n" args.recrypt args.was_encrypted;
begin begin
if args.recrypt && args.was_encrypted then if args.recrypt && args.was_encrypted then
begin begin
@ -1904,6 +1946,8 @@ let really_write_pdf ?(encryption = None) mk_id pdf outname =
(get_single_pdf_nodecrypt false) pdf args.user outname' (get_single_pdf_nodecrypt false) pdf args.user outname'
end end
else else
begin
if not args.was_encrypted || args.was_decrypted_with_owner && args.was_encrypted then
begin begin
if args.debugcrypt then Printf.printf "Pdf to file in really_write_pdf\n"; if args.debugcrypt then Printf.printf "Pdf to file in really_write_pdf\n";
Pdfwrite.pdf_to_file_options Pdfwrite.pdf_to_file_options
@ -1911,6 +1955,9 @@ let really_write_pdf ?(encryption = None) mk_id pdf outname =
~generate_objstm:args.create_objstm ~generate_objstm:args.create_objstm
false encryption mk_id pdf outname' false encryption mk_id pdf outname'
end end
else
soft_error "You must supply -recrypt here, or provide the owner password."
end
end; end;
begin begin
if args.linearize then if args.linearize then
@ -1936,6 +1983,7 @@ let really_write_pdf ?(encryption = None) mk_id pdf outname =
((float s /. float !initial_file_size) *. 100.) ((float s /. float !initial_file_size) *. 100.)
let write_pdf ?(encryption = None) ?(is_decompress=false) mk_id pdf = let write_pdf ?(encryption = None) ?(is_decompress=false) mk_id pdf =
if args.debugcrypt then Printf.printf "write_pdf\n";
if args.create_objstm && not args.keepversion if args.create_objstm && not args.keepversion
then pdf.Pdf.minor <- max pdf.Pdf.minor 5; then pdf.Pdf.minor <- max pdf.Pdf.minor 5;
let mk_id = args.makenewid || mk_id in let mk_id = args.makenewid || mk_id in
@ -2138,21 +2186,6 @@ let getencryption pdf =
| Some (Pdfwrite.AES256bitISO true) -> "256bit AES ISO, Metadata encrypted" | Some (Pdfwrite.AES256bitISO true) -> "256bit AES ISO, Metadata encrypted"
| Some (Pdfwrite.AES256bitISO false) -> "256bit AES ISO, Metadata not encrypted" | Some (Pdfwrite.AES256bitISO false) -> "256bit AES ISO, Metadata not encrypted"
let string_of_permission = function
| Pdfcrypt.NoEdit -> "No edit"
| Pdfcrypt.NoPrint -> "No print"
| Pdfcrypt.NoCopy -> "No copy"
| Pdfcrypt.NoAnnot -> "No Annotate"
| Pdfcrypt.NoForms -> "No edit forms"
| Pdfcrypt.NoExtract -> "No extract"
| Pdfcrypt.NoAssemble -> "No assemble"
| Pdfcrypt.NoHqPrint -> "No high-quality print"
let getpermissions pdf =
fold_left
(fun x y -> if x = "" then x ^ y else x ^ ", " ^ y)
""
(map string_of_permission (Pdfread.permissions pdf))
(* If a cropbox exists, make it the mediabox. If not, change nothing. *) (* If a cropbox exists, make it the mediabox. If not, change nothing. *)
let copy_cropbox_to_mediabox pdf range = let copy_cropbox_to_mediabox pdf range =
@ -2707,17 +2740,12 @@ let go () =
in in
match namewiths with match namewiths with
| (namewiths, _, _, _, _) as input::t -> | (namewiths, _, _, _, _) as input::t ->
let spdf = let spdf = get_pdf_from_input_kind input (Some Merge) namewiths in
get_pdf_from_input_kind
input
(Some Decrypt)
namewiths
in
write_pdf x (Cpdf.copy_id true spdf pdf) write_pdf x (Cpdf.copy_id true spdf pdf)
| _ -> write_pdf x pdf | _ -> write_pdf x pdf
in in
let names, ranges, rotations, _, _ = split5 inputs in let names, ranges, rotations, _, _ = split5 inputs in
let pdfs = map2 (fun i -> get_pdf_from_input_kind i (Some Decrypt)) inputs names in let pdfs = map2 (fun i -> get_pdf_from_input_kind i (Some Merge)) inputs names in
(* If at least one file had object streams and args.preserve_objstm is true, set -objstm-create *) (* If at least one file had object streams and args.preserve_objstm is true, set -objstm-create *)
if args.preserve_objstm then if args.preserve_objstm then
iter iter
@ -2805,7 +2833,7 @@ let go () =
| (AlreadyInMemory pdf, _, _, _, _) as input::_ -> pdf, "", input | (AlreadyInMemory pdf, _, _, _, _) as input::_ -> pdf, "", input
| _ -> raise (Arg.Bad "cpdf: No input specified.\n") | _ -> raise (Arg.Bad "cpdf: No input specified.\n")
in in
let pdf = decrypt_if_necessary input (Some Info) pdf in let pdf = decrypt_if_necessary input (Some CountPages) pdf in
output_page_count pdf output_page_count pdf
| Some Clean -> | Some Clean ->
begin match args.out with begin match args.out with