From bcce8e5dc38509c5aa43174a0d6e0341444f1d87 Mon Sep 17 00:00:00 2001 From: Paul Gardiner Date: Thu, 4 Jan 2018 15:11:24 +0000 Subject: Perform document signing via fz_stream and fz_output This change achieves two goals. It allows signing to be performed even when the document is obtained other than from a disk file. It also reestablishes to a working state signing of file-based documents, a feature that was broken due to complete_signatures being called after certain tables, avaialble via the output options object, had been destroyed. --- include/mupdf/pdf/crypt.h | 2 +- source/pdf/pdf-pkcs7.c | 112 ++++++++++++++++++++++++++++++++----- source/pdf/pdf-write.c | 138 ++++++++++++++++++++++++---------------------- 3 files changed, 172 insertions(+), 80 deletions(-) diff --git a/include/mupdf/pdf/crypt.h b/include/mupdf/pdf/crypt.h index f66fe0d2..46970870 100644 --- a/include/mupdf/pdf/crypt.h +++ b/include/mupdf/pdf/crypt.h @@ -37,7 +37,7 @@ pdf_signer *pdf_read_pfx(fz_context *ctx, const char *sigfile, const char *passw pdf_signer *pdf_keep_signer(fz_context *ctx, pdf_signer *signer); void pdf_drop_signer(fz_context *ctx, pdf_signer *signer); pdf_designated_name *pdf_signer_designated_name(fz_context *ctx, pdf_signer *signer); -void pdf_write_digest(fz_context *ctx, pdf_document *doc, const char *filename, pdf_obj *byte_range, int digest_offset, int digest_length, pdf_signer *signer); +void pdf_write_digest(fz_context *ctx, fz_output *out, pdf_obj *byte_range, int digest_offset, int digest_length, pdf_signer *signer); /* pdf_signature_widget_byte_range: retrieve the byte range for a signature widget diff --git a/source/pdf/pdf-pkcs7.c b/source/pdf/pdf-pkcs7.c index 7139156f..864e3039 100644 --- a/source/pdf/pdf-pkcs7.c +++ b/source/pdf/pdf-pkcs7.c @@ -80,6 +80,92 @@ static const char AdobeCA_p7c[] = { #warning detect version of openssl at compile time #endif +typedef struct +{ + fz_context *ctx; + fz_stream *stm; +} BIO_stream_data; + +static int stream_read(BIO *b, char *buf, int size) +{ + BIO_stream_data *data = (BIO_stream_data *)BIO_get_data(b); + return fz_read(data->ctx, data->stm, buf, size); +} + +static long stream_ctrl(BIO *b, int cmd, long arg1, void *arg2) +{ + BIO_stream_data *data = (BIO_stream_data *)BIO_get_data(b); + switch (cmd) + { + case BIO_C_FILE_SEEK: + fz_seek(data->ctx, data->stm, arg1, SEEK_SET); + return 0; + default: + return 1; + } +} + +static int stream_new(BIO *b) +{ + BIO_stream_data *data = (BIO_stream_data *)malloc(sizeof(BIO_stream_data)); + if (!data) + return 0; + + data->ctx = NULL; + data->stm = NULL; + + BIO_set_init(b, 1); + BIO_set_data(b, data); + BIO_clear_flags(b, INT_MAX); + + return 1; +} + +static int stream_free(BIO *b) +{ + if (b == NULL) + return 0; + + free(BIO_get_data(b)); + BIO_set_data(b, NULL); + BIO_set_init(b, 0); + BIO_clear_flags(b, INT_MAX); + + return 1; +} + +static long stream_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp) +{ + return 1; +} + +static BIO *BIO_new_stream(fz_context *ctx, fz_stream *stm) +{ + static BIO_METHOD *methods = NULL; + BIO *bio; + BIO_stream_data *data; + + if (!methods) + { + methods = BIO_meth_new(BIO_TYPE_NONE, "segment reader"); + if (!methods) + return NULL; + + BIO_meth_set_read(methods, stream_read); + BIO_meth_set_ctrl(methods, stream_ctrl); + BIO_meth_set_create(methods, stream_new); + BIO_meth_set_destroy(methods, stream_free); + BIO_meth_set_callback_ctrl(methods, stream_callback_ctrl); + } + + bio = BIO_new(methods); + data = BIO_get_data(bio); + data->ctx = ctx; + data->stm = stm; + + return bio; +} + enum { SEG_START = 0, @@ -649,25 +735,25 @@ pdf_designated_name *pdf_signer_designated_name(fz_context *ctx, pdf_signer *sig return (pdf_designated_name *)dn; } -void pdf_write_digest(fz_context *ctx, pdf_document *doc, const char *filename, pdf_obj *byte_range, int digest_offset, int digest_length, pdf_signer *signer) +void pdf_write_digest(fz_context *ctx, fz_output *out, pdf_obj *byte_range, int digest_offset, int digest_length, pdf_signer *signer) { + fz_stream *in = NULL; BIO *bdata = NULL; BIO *bsegs = NULL; BIO *bp7in = NULL; BIO *bp7 = NULL; PKCS7 *p7 = NULL; PKCS7_SIGNER_INFO *si; - FILE *f = NULL; int (*brange)[2] = NULL; int brange_len = pdf_array_len(ctx, byte_range)/2; + fz_var(in); fz_var(bdata); fz_var(bsegs); fz_var(bp7in); fz_var(bp7); fz_var(p7); - fz_var(f); fz_try(ctx) { @@ -675,6 +761,8 @@ void pdf_write_digest(fz_context *ctx, pdf_document *doc, const char *filename, int p7_len; int i; + in = fz_stream_from_output(ctx, out); + brange = fz_calloc(ctx, brange_len, sizeof(*brange)); for (i = 0; i < brange_len; i++) { @@ -682,10 +770,9 @@ void pdf_write_digest(fz_context *ctx, pdf_document *doc, const char *filename, brange[i][1] = pdf_to_int(ctx, pdf_array_get(ctx, byte_range, 2*i+1)); } - bdata = BIO_new(BIO_s_file()); + bdata = BIO_new_stream(ctx, in); if (bdata == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to create file BIO"); - BIO_read_filename(bdata, filename); bsegs = BIO_new(BIO_f_segments()); if (bsegs == NULL) @@ -729,6 +816,8 @@ void pdf_write_digest(fz_context *ctx, pdf_document *doc, const char *filename, bsegs = NULL; BIO_free(bdata); bdata = NULL; + fz_drop_stream(ctx, in); + in = NULL; bp7 = BIO_new(BIO_s_mem()); if (bp7 == NULL || !i2d_PKCS7_bio(bp7, p7)) @@ -738,24 +827,19 @@ void pdf_write_digest(fz_context *ctx, pdf_document *doc, const char *filename, if (p7_len*2 + 2 > digest_length) fz_throw(ctx, FZ_ERROR_GENERIC, "Insufficient space for digest"); - f = fopen(filename, "rb+"); - if (f == NULL) - fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to write digest"); - - fseek(f, digest_offset+1, SEEK_SET); + fz_seek_output(ctx, out, digest_offset+1, SEEK_SET); for (i = 0; i < p7_len; i++) - fprintf(f, "%02x", p7_ptr[i]); + fz_write_printf(ctx, out, "%02x", p7_ptr[i]); } fz_always(ctx) { + fz_drop_stream(ctx, in); PKCS7_free(p7); BIO_free(bsegs); BIO_free(bdata); BIO_free(bp7in); BIO_free(bp7); - if (f) - fclose(f); } fz_catch(ctx) { @@ -896,7 +980,7 @@ void pdf_drop_signer(fz_context *ctx, pdf_signer *signer) { } -void pdf_write_digest(fz_context *ctx, pdf_document *doc, const char *filename, pdf_obj *byte_range, int digest_offset, int digest_length, pdf_signer *signer) +void pdf_write_digest(fz_context *ctx, fz_output *out, pdf_obj *byte_range, int digest_offset, int digest_length, pdf_signer *signer) { } diff --git a/source/pdf/pdf-write.c b/source/pdf/pdf-write.c index b13970b9..28a505f9 100644 --- a/source/pdf/pdf-write.c +++ b/source/pdf/pdf-write.c @@ -2624,95 +2624,102 @@ static void presize_unsaved_signature_byteranges(fz_context *ctx, pdf_document * } } -static void complete_signatures(fz_context *ctx, pdf_document *doc, pdf_write_state *opts, const char *filename) +static void complete_signatures(fz_context *ctx, pdf_document *doc, pdf_write_state *opts) { pdf_unsaved_sig *usig; char buf[5120]; int s; int i; int last_end; - FILE *f; + fz_stream *stm = NULL; + fz_var(stm); - for (s = 0; s < doc->num_incremental_sections; s++) + fz_try(ctx) { - pdf_xref *xref = &doc->xref_sections[doc->num_incremental_sections - s - 1]; - - if (xref->unsaved_sigs) + for (s = 0; s < doc->num_incremental_sections; s++) { - pdf_obj *byte_range; + pdf_xref *xref = &doc->xref_sections[doc->num_incremental_sections - s - 1]; - f = fopen(filename, "rb+"); - if (!f) - fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to open %s to complete signatures", filename); - - /* Locate the byte ranges and contents in the saved file */ - for (usig = xref->unsaved_sigs; usig; usig = usig->next) + if (xref->unsaved_sigs) { - char *bstr, *cstr, *fstr; - int pnum = pdf_obj_parent_num(ctx, pdf_dict_getl(ctx, usig->field, PDF_NAME_V, PDF_NAME_ByteRange, NULL)); - fseek(f, opts->ofs_list[pnum], SEEK_SET); - (void)fread(buf, 1, sizeof(buf), f); - buf[sizeof(buf)-1] = 0; + pdf_obj *byte_range; - bstr = strstr(buf, "/ByteRange"); - cstr = strstr(buf, "/Contents"); - fstr = strstr(buf, "/Filter"); - - if (bstr && cstr && fstr && bstr < cstr && cstr < fstr) + stm = fz_stream_from_output(ctx, opts->out); + /* Locate the byte ranges and contents in the saved file */ + for (usig = xref->unsaved_sigs; usig; usig = usig->next) { - usig->byte_range_start = bstr - buf + 10 + opts->ofs_list[pnum]; - usig->byte_range_end = cstr - buf + opts->ofs_list[pnum]; - usig->contents_start = cstr - buf + 9 + opts->ofs_list[pnum]; - usig->contents_end = fstr - buf + opts->ofs_list[pnum]; + char *bstr, *cstr, *fstr; + int pnum = pdf_obj_parent_num(ctx, pdf_dict_getl(ctx, usig->field, PDF_NAME_V, PDF_NAME_ByteRange, NULL)); + fz_seek(ctx, stm, opts->ofs_list[pnum], SEEK_SET); + (void)fz_read(ctx, stm, (unsigned char *)buf, sizeof(buf)); + buf[sizeof(buf)-1] = 0; + + bstr = strstr(buf, "/ByteRange"); + cstr = strstr(buf, "/Contents"); + fstr = strstr(buf, "/Filter"); + + if (bstr && cstr && fstr && bstr < cstr && cstr < fstr) + { + usig->byte_range_start = bstr - buf + 10 + opts->ofs_list[pnum]; + usig->byte_range_end = cstr - buf + opts->ofs_list[pnum]; + usig->contents_start = cstr - buf + 9 + opts->ofs_list[pnum]; + usig->contents_end = fstr - buf + opts->ofs_list[pnum]; + } } - } - /* Recreate ByteRange with correct values. Initially store the - * recreated object in the first of the unsaved signatures */ - byte_range = pdf_new_array(ctx, doc, 4); - pdf_dict_putl_drop(ctx, xref->unsaved_sigs->field, byte_range, PDF_NAME_V, PDF_NAME_ByteRange, NULL); + fz_drop_stream(ctx, stm); + stm = NULL; - last_end = 0; - for (usig = xref->unsaved_sigs; usig; usig = usig->next) - { - pdf_array_push_drop(ctx, byte_range, pdf_new_int(ctx, doc, last_end)); - pdf_array_push_drop(ctx, byte_range, pdf_new_int(ctx, doc, usig->contents_start - last_end)); - last_end = usig->contents_end; - } - pdf_array_push_drop(ctx, byte_range, pdf_new_int(ctx, doc, last_end)); - pdf_array_push_drop(ctx, byte_range, pdf_new_int(ctx, doc, xref->end_ofs - last_end)); + /* Recreate ByteRange with correct values. Initially store the + * recreated object in the first of the unsaved signatures */ + byte_range = pdf_new_array(ctx, doc, 4); + pdf_dict_putl_drop(ctx, xref->unsaved_sigs->field, byte_range, PDF_NAME_V, PDF_NAME_ByteRange, NULL); - /* Copy the new ByteRange to the other unsaved signatures */ - for (usig = xref->unsaved_sigs->next; usig; usig = usig->next) - pdf_dict_putl_drop(ctx, usig->field, pdf_copy_array(ctx, byte_range), PDF_NAME_V, PDF_NAME_ByteRange, NULL); + last_end = 0; + for (usig = xref->unsaved_sigs; usig; usig = usig->next) + { + pdf_array_push_drop(ctx, byte_range, pdf_new_int(ctx, doc, last_end)); + pdf_array_push_drop(ctx, byte_range, pdf_new_int(ctx, doc, usig->contents_start - last_end)); + last_end = usig->contents_end; + } + pdf_array_push_drop(ctx, byte_range, pdf_new_int(ctx, doc, last_end)); + pdf_array_push_drop(ctx, byte_range, pdf_new_int(ctx, doc, xref->end_ofs - last_end)); - /* Write the byte range into buf, padding with spaces*/ - i = pdf_sprint_obj(ctx, buf, sizeof(buf), byte_range, 1); - memset(buf+i, ' ', sizeof(buf)-i); + /* Copy the new ByteRange to the other unsaved signatures */ + for (usig = xref->unsaved_sigs->next; usig; usig = usig->next) + pdf_dict_putl_drop(ctx, usig->field, pdf_copy_array(ctx, byte_range), PDF_NAME_V, PDF_NAME_ByteRange, NULL); - /* Write the byte range to the file */ - for (usig = xref->unsaved_sigs; usig; usig = usig->next) - { - fseek(f, usig->byte_range_start, SEEK_SET); - fwrite(buf, 1, usig->byte_range_end - usig->byte_range_start, f); - } + /* Write the byte range into buf, padding with spaces*/ + i = pdf_sprint_obj(ctx, buf, sizeof(buf), byte_range, 1); + memset(buf+i, ' ', sizeof(buf)-i); - fclose(f); + /* Write the byte range to the file */ + for (usig = xref->unsaved_sigs; usig; usig = usig->next) + { + fz_seek_output(ctx, opts->out, usig->byte_range_start, SEEK_SET); + fz_write_data(ctx, opts->out, buf, usig->byte_range_end - usig->byte_range_start); + } - /* Write the digests into the file */ - for (usig = xref->unsaved_sigs; usig; usig = usig->next) - pdf_write_digest(ctx, doc, filename, byte_range, usig->contents_start, usig->contents_end - usig->contents_start, usig->signer); + /* Write the digests into the file */ + for (usig = xref->unsaved_sigs; usig; usig = usig->next) + pdf_write_digest(ctx, opts->out, byte_range, usig->contents_start, usig->contents_end - usig->contents_start, usig->signer); - /* delete the unsaved_sigs records */ - while ((usig = xref->unsaved_sigs) != NULL) - { - xref->unsaved_sigs = usig->next; - pdf_drop_obj(ctx, usig->field); - pdf_drop_signer(ctx, usig->signer); - fz_free(ctx, usig); + /* delete the unsaved_sigs records */ + while ((usig = xref->unsaved_sigs) != NULL) + { + xref->unsaved_sigs = usig->next; + pdf_drop_obj(ctx, usig->field); + pdf_drop_signer(ctx, usig->signer); + fz_free(ctx, usig); + } } } } + fz_catch(ctx) + { + fz_drop_stream(ctx, stm); + fz_rethrow(ctx); + } } static void clean_content_streams(fz_context *ctx, pdf_document *doc, int sanitize, int ascii) @@ -3054,6 +3061,8 @@ do_pdf_save_document(fz_context *ctx, pdf_document *doc, pdf_write_state *opts, doc->xref_sections[0].end_ofs = fz_tell_output(ctx, opts->out); } + complete_signatures(ctx, doc, opts); + doc->dirty = 0; } fz_always(ctx) @@ -3149,7 +3158,6 @@ void pdf_save_document(fz_context *ctx, pdf_document *doc, const char *filename, { do_pdf_save_document(ctx, doc, &opts, in_opts); fz_close_output(ctx, opts.out); - complete_signatures(ctx, doc, &opts, filename); } fz_always(ctx) { -- cgit v1.2.3