1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-05 23:07:49 +02:00
gnupg/common/ksba-io-support.c
Werner Koch 6bdf11f671
gpgsm: Strip trailing zeroes from detached signatures.
* common/ksba-io-support.c: Include tlv.h
(struct reader_cb_parm_s): Add new fields.
(starts_with_sequence): New.
(simple_reader_cb): Handle stripping.
* common/ksba-io-support.h (GNUPG_KSBA_IO_STRIP): New.
(gnupg_ksba_create_reader): Handle the new flag.
* sm/verify.c (gpgsm_verify): Use the new flag for detached
signatures.
--

Note that this works only if --assume-binary is given.  The use case
for the feature is PDF signature checking where the PDF specs require
that the detached signature is padded with zeroes.

(cherry picked from commit 2a13f7f9dc)
2023-09-07 16:38:35 +02:00

954 lines
26 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* kska-io-support.c - Supporting functions for ksba reader and writer
* Copyright (C) 2001-2005, 2007, 2010-2011, 2017 Werner Koch
* Copyright (C) 2006, 2023 g10 Code GmbH
*
* This file is part of GnuPG.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later)
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <assert.h>
#include <ksba.h>
#include "util.h"
#include "i18n.h"
#include "tlv.h"
#include "ksba-io-support.h"
#ifdef HAVE_DOSISH_SYSTEM
#define LF "\r\n"
#else
#define LF "\n"
#endif
/* Data used by the reader callbacks. */
struct reader_cb_parm_s
{
estream_t fp;
unsigned char line[1024];
int linelen;
int readpos;
int have_lf;
unsigned long line_counter;
int allow_multi_pem; /* Allow processing of multiple PEM objects. */
int autodetect; /* Try to detect the input encoding. */
int assume_pem; /* Assume input encoding is PEM. */
int assume_base64; /* Assume input is base64 encoded. */
int strip_zeroes; /* Expect a SEQUENCE followed by zero padding. */
/* 1 = check state; 2 = reading; 3 = checking */
/* for zeroes. */
int use_maxread; /* If true read not more than MAXREAD. */
unsigned int maxread; /* # of bytes left to read. */
off_t nzeroes; /* Number of padding zeroes red. */
int identified;
int is_pem;
int is_base64;
int stop_seen;
int might_be_smime;
int eof_seen;
struct {
int idx;
unsigned char val;
int stop_seen;
} base64;
};
/* Data used by the writer callbacks. */
struct writer_cb_parm_s
{
estream_t stream; /* Output stream. */
char *pem_name; /* Malloced. */
struct {
gnupg_ksba_progress_cb_t cb;
ctrl_t ctrl;
u32 last_time; /* last time reported */
uint64_t last; /* last amount reported */
uint64_t current; /* current amount */
uint64_t total; /* total amount */
} progress;
int wrote_begin;
int did_finish;
struct {
int idx;
int quad_count;
unsigned char radbuf[4];
} base64;
};
/* Context for this module's functions. */
struct gnupg_ksba_io_s {
int is_writer; /* True if this context refers a writer object. */
union {
struct reader_cb_parm_s rparm;
struct writer_cb_parm_s wparm;
} u;
union {
ksba_reader_t reader;
ksba_writer_t writer;
} u2;
};
/* The base-64 character list */
static char bintoasc[64] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
/* The reverse base-64 list */
static unsigned char asctobin[256] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff
};
static int
has_only_base64 (const unsigned char *line, int linelen)
{
if (linelen < 20)
return 0;
for (; linelen; line++, linelen--)
{
if (*line == '\n' || (linelen > 1 && *line == '\r' && line[1] == '\n'))
break;
if ( !strchr (bintoasc, *line) )
return 0;
}
return 1; /* yes */
}
static int
is_empty_line (const unsigned char *line, int linelen)
{
if (linelen >= 2 && *line == '\r' && line[1] == '\n')
return 1;
if (linelen >= 1 && *line == '\n')
return 1;
return 0;
}
static int
base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
{
struct reader_cb_parm_s *parm = cb_value;
size_t n;
int c, c2;
*nread = 0;
if (!buffer)
return -1; /* not supported */
next:
if (!parm->linelen)
{
/* read an entire line or up to the size of the buffer */
parm->line_counter++;
parm->have_lf = 0;
for (n=0; n < DIM(parm->line);)
{
c = es_getc (parm->fp);
if (c == EOF)
{
parm->eof_seen = 1;
if (es_ferror (parm->fp))
return -1;
break;
}
parm->line[n++] = c;
if (c == '\n')
{
parm->have_lf = 1;
/* Fixme: we need to skip overlong lines while detecting
the dashed lines */
break;
}
}
parm->linelen = n;
if (!n)
return -1; /* eof */
parm->readpos = 0;
}
if (!parm->identified)
{
if (!parm->autodetect)
{
if (parm->assume_pem)
{
/* wait for the header line */
parm->linelen = parm->readpos = 0;
if (!parm->have_lf
|| strncmp ((char*)parm->line, "-----BEGIN ", 11)
|| !strncmp ((char*)parm->line+11, "PGP ", 4))
goto next;
parm->is_pem = 1;
}
else if (parm->assume_base64)
parm->is_base64 = 1;
}
else if (parm->line_counter == 1 && !parm->have_lf)
{
/* first line too long - assume DER encoding */
parm->is_pem = 0;
}
else if (parm->line_counter == 1 && parm->linelen && *parm->line == 0x30)
{
/* the very first byte does pretty much look like a SEQUENCE tag*/
parm->is_pem = 0;
}
else if ( parm->have_lf
&& !strncmp ((char*)parm->line, "-----BEGIN ", 11)
&& strncmp ((char *)parm->line+11, "PGP ", 4) )
{
/* Fixme: we must only compare if the line really starts at
the beginning */
parm->is_pem = 1;
parm->linelen = parm->readpos = 0;
}
else if ( parm->have_lf && parm->line_counter == 1
&& parm->linelen >= 13
&& !ascii_memcasecmp (parm->line, "Content-Type:", 13))
{ /* might be a S/MIME body */
parm->might_be_smime = 1;
parm->linelen = parm->readpos = 0;
goto next;
}
else if (parm->might_be_smime == 1
&& is_empty_line (parm->line, parm->linelen))
{
parm->might_be_smime = 2;
parm->linelen = parm->readpos = 0;
goto next;
}
else if (parm->might_be_smime == 2)
{
parm->might_be_smime = 0;
if ( !has_only_base64 (parm->line, parm->linelen))
{
parm->linelen = parm->readpos = 0;
goto next;
}
parm->is_pem = 1;
}
else
{
parm->linelen = parm->readpos = 0;
goto next;
}
parm->identified = 1;
parm->base64.stop_seen = 0;
parm->base64.idx = 0;
}
n = 0;
if (parm->is_pem || parm->is_base64)
{
if (parm->is_pem && parm->have_lf
&& !strncmp ((char*)parm->line, "-----END ", 9))
{
parm->identified = 0;
parm->linelen = parm->readpos = 0;
/* If the caller want to read multiple PEM objects from one
file, we have to reset our internal state and return a
EOF immediately. The caller is the expected to use
ksba_reader_clear to clear the EOF condition and continue
to read. If we don't want to do that we just return 0
bytes which will force the ksba_reader to skip until
EOF. */
if (parm->allow_multi_pem)
{
parm->identified = 0;
parm->autodetect = 0;
parm->assume_pem = 1;
parm->stop_seen = 0;
return -1; /* Send EOF now. */
}
}
else if (parm->stop_seen)
{ /* skip the rest of the line */
parm->linelen = parm->readpos = 0;
}
else
{
int idx = parm->base64.idx;
unsigned char val = parm->base64.val;
while (n < count && parm->readpos < parm->linelen )
{
c = parm->line[parm->readpos++];
if (c == '\n' || c == ' ' || c == '\r' || c == '\t')
continue;
if ((c = asctobin[(c2=c)]) == 255)
{
if (c2 == '=')
{ /* pad character: stop */
if (idx == 1)
buffer[n++] = val;
parm->stop_seen = 1;
break;
}
else if (c2 == '-'
&& parm->readpos == 1
&& parm->readpos-1+9 < parm->linelen
&& !strncmp ((char*)parm->line + parm->readpos-1,
"-----END ", 9))
{ /* END line seen (padding was not needed). */
parm->stop_seen = 1;
break;
}
log_error (_("invalid radix64 character %02x skipped\n"),
c2);
continue;
}
switch (idx)
{
case 0:
val = c << 2;
break;
case 1:
val |= (c>>4)&3;
buffer[n++] = val;
val = (c<<4)&0xf0;
break;
case 2:
val |= (c>>2)&15;
buffer[n++] = val;
val = (c<<6)&0xc0;
break;
case 3:
val |= c&0x3f;
buffer[n++] = val;
break;
}
idx = (idx+1) % 4;
}
if (parm->readpos == parm->linelen)
parm->linelen = parm->readpos = 0;
parm->base64.idx = idx;
parm->base64.val = val;
}
}
else
{ /* DER encoded */
while (n < count && parm->readpos < parm->linelen)
buffer[n++] = parm->line[parm->readpos++];
if (parm->readpos == parm->linelen)
parm->linelen = parm->readpos = 0;
}
*nread = n;
return 0;
}
/* Read up to 10 bytes to test whether the data consist of a sequence;
* if that is true, set the limited flag and record the length of the
* entire sequence in PARM. Unget everything then. Return true if we
* have a sequence with a fixed length. */
static int
starts_with_sequence (struct reader_cb_parm_s *parm)
{
gpg_error_t err;
unsigned char peekbuf[10];
int npeeked, c;
int found = 0;
const unsigned char *p;
size_t n, objlen, hdrlen;
int class, tag, constructed, ndef;
for (npeeked=0; npeeked < sizeof peekbuf; npeeked++)
{
c = es_getc (parm->fp);
if (c == EOF)
goto leave;
peekbuf[npeeked] = c;
}
/* Enough to check for a sequence. */
p = peekbuf;
n = npeeked;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (err)
{
log_debug ("%s: error parsing data: %s\n", __func__, gpg_strerror (err));
goto leave;
}
if (class == CLASS_UNIVERSAL && constructed && tag == TAG_SEQUENCE && !ndef)
{
/* We need to add 1 due to the way we implement the limit. */
parm->maxread = objlen + hdrlen + 1;
if (!(parm->maxread < objlen + hdrlen) && parm->maxread)
parm->use_maxread = 1;
found = 1;
}
leave:
while (npeeked)
es_ungetc (peekbuf[--npeeked], parm->fp);
return found;
}
static int
simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
{
struct reader_cb_parm_s *parm = cb_value;
size_t n;
int c = 0;
*nread = 0;
if (!buffer)
return -1; /* not supported */
restart:
if (parm->strip_zeroes)
{
if (parm->strip_zeroes == 1)
{
if (starts_with_sequence (parm))
parm->strip_zeroes = 2; /* Found fixed length sequence. */
else
parm->strip_zeroes = 0; /* Disable zero padding check. */
}
else if (parm->strip_zeroes == 3)
{
/* Limit reached - check that only zeroes follow. */
while (!(c = es_getc (parm->fp)))
parm->nzeroes++;
if (c == EOF)
{ /* only zeroes found. Reset zero padding engine and
* return EOF. */
parm->strip_zeroes = 0;
parm->eof_seen = 1;
return -1;
}
/* Not only zeroes. Reset engine and continue. */
parm->strip_zeroes = 0;
}
}
for (n=0; n < count; n++)
{
if (parm->use_maxread && !--parm->maxread)
{
parm->use_maxread = 0;
if (parm->strip_zeroes)
{
parm->strip_zeroes = 3;
parm->nzeroes = 0;
if (n)
goto leave; /* Return what we already got. */
goto restart; /* Immediately check for trailing zeroes. */
}
}
if (parm->nzeroes)
{
parm->nzeroes--;
c = 0;
}
else
c = es_getc (parm->fp);
if (c == EOF)
{
parm->eof_seen = 1;
if (es_ferror (parm->fp))
return -1;
if (n)
break; /* Return what we have before an EOF. */
return -1;
}
*(byte *)buffer++ = c;
}
leave:
*nread = n;
return 0;
}
/* Call the progress callback if its time. We do this very 2 seconds
* or if FORCE is set. However, we also require that at least 64KiB
* have been written to avoid unnecessary progress lines for small
* files. */
static gpg_error_t
update_write_progress (struct writer_cb_parm_s *parm, size_t count, int force)
{
gpg_error_t err = 0;
u32 timestamp;
parm->progress.current += count;
if (parm->progress.current >= (64*1024))
{
timestamp = make_timestamp ();
if (force || (timestamp - parm->progress.last_time > 1))
{
parm->progress.last = parm->progress.current;
parm->progress.last_time = timestamp;
err = parm->progress.cb (parm->progress.ctrl,
parm->progress.current,
parm->progress.total);
}
}
return err;
}
static int
base64_writer_cb (void *cb_value, const void *buffer, size_t count)
{
struct writer_cb_parm_s *parm = cb_value;
unsigned char radbuf[4];
int i, c, idx, quad_count;
const unsigned char *p;
estream_t stream = parm->stream;
int rc;
size_t nleft;
if (!count)
return 0;
if (!parm->wrote_begin)
{
if (parm->pem_name)
{
es_fputs ("-----BEGIN ", stream);
es_fputs (parm->pem_name, stream);
es_fputs ("-----\n", stream);
}
parm->wrote_begin = 1;
parm->base64.idx = 0;
parm->base64.quad_count = 0;
}
idx = parm->base64.idx;
quad_count = parm->base64.quad_count;
for (i=0; i < idx; i++)
radbuf[i] = parm->base64.radbuf[i];
for (p=buffer, nleft = count; nleft; p++, nleft--)
{
radbuf[idx++] = *p;
if (idx > 2)
{
idx = 0;
c = bintoasc[(*radbuf >> 2) & 077];
es_putc (c, stream);
c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
es_putc (c, stream);
c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
es_putc (c, stream);
c = bintoasc[radbuf[2]&077];
es_putc (c, stream);
if (++quad_count >= (64/4))
{
es_fputs (LF, stream);
quad_count = 0;
}
}
}
for (i=0; i < idx; i++)
parm->base64.radbuf[i] = radbuf[i];
parm->base64.idx = idx;
parm->base64.quad_count = quad_count;
rc = es_ferror (stream)? gpg_error_from_syserror () : 0;
/* Note that we use the unencoded count for the progress. */
if (!rc && parm->progress.cb)
rc = update_write_progress (parm, count, 0);
return rc;
}
/* This callback is only used in stream mode. However, we don't
restrict it to this. */
static int
plain_writer_cb (void *cb_value, const void *buffer, size_t count)
{
struct writer_cb_parm_s *parm = cb_value;
estream_t stream = parm->stream;
int rc;
if (!count)
return 0;
es_write (stream, buffer, count, NULL);
rc = es_ferror (stream)? gpg_error_from_syserror () : 0;
if (!rc && parm->progress.cb)
rc = update_write_progress (parm, count, 0);
return rc;
}
static int
base64_finish_write (struct writer_cb_parm_s *parm)
{
unsigned char *radbuf;
int c, idx, quad_count;
estream_t stream = parm->stream;
int rc;
if (!parm->wrote_begin)
return 0; /* Nothing written or we are not called in base-64 mode. */
/* flush the base64 encoding */
idx = parm->base64.idx;
quad_count = parm->base64.quad_count;
if (idx)
{
radbuf = parm->base64.radbuf;
c = bintoasc[(*radbuf>>2)&077];
es_putc (c, stream);
if (idx == 1)
{
c = bintoasc[((*radbuf << 4) & 060) & 077];
es_putc (c, stream);
es_putc ('=', stream);
es_putc ('=', stream);
}
else
{
c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
es_putc (c, stream);
c = bintoasc[((radbuf[1] << 2) & 074) & 077];
es_putc (c, stream);
es_putc ('=', stream);
}
if (++quad_count >= (64/4))
{
es_fputs (LF, stream);
quad_count = 0;
}
}
if (quad_count)
es_fputs (LF, stream);
if (parm->pem_name)
{
es_fputs ("-----END ", stream);
es_fputs (parm->pem_name, stream);
es_fputs ("-----\n", stream);
}
rc = es_ferror (stream)? gpg_error_from_syserror () : 0;
if (!rc && parm->progress.cb)
rc = update_write_progress (parm, 0, 1);
return rc;
}
/* Create a reader for the stream FP. FLAGS can be used to specify
* the expected input encoding.
*
* The function returns a gnupg_ksba_io_t object which must be passed to
* the gpgme_destroy_reader function. The created ksba_reader_t
* object is stored at R_READER - the caller must not call the
* ksba_reader_release function on.
*
* The supported flags are:
*
* GNUPG_KSBA_IO_PEM - Assume the input is PEM encoded
* GNUPG_KSBA_IO_BASE64 - Assume the input is Base64 encoded.
* GNUPG_KSBA_IO_AUTODETECT - The reader tries to detect the encoding.
* GNUPG_KSBA_IO_MULTIPEM - The reader expects that the caller uses
* ksba_reader_clear after EOF until no more
* objects were found.
* GNUPG_KSBA_IO_STRIP - Strip zero padding from some CMS objects.
*
* Note that the PEM flag has a higher priority than the BASE64 flag
* which in turn has a gight priority than the AUTODETECT flag.
*/
gpg_error_t
gnupg_ksba_create_reader (gnupg_ksba_io_t *ctx,
unsigned int flags, estream_t fp,
ksba_reader_t *r_reader)
{
int rc;
ksba_reader_t r;
*r_reader = NULL;
*ctx = xtrycalloc (1, sizeof **ctx);
if (!*ctx)
return out_of_core ();
(*ctx)->u.rparm.allow_multi_pem = !!(flags & GNUPG_KSBA_IO_MULTIPEM);
(*ctx)->u.rparm.strip_zeroes = !!(flags & GNUPG_KSBA_IO_STRIP);
rc = ksba_reader_new (&r);
if (rc)
{
xfree (*ctx); *ctx = NULL;
return rc;
}
(*ctx)->u.rparm.fp = fp;
if ((flags & GNUPG_KSBA_IO_PEM))
{
(*ctx)->u.rparm.assume_pem = 1;
(*ctx)->u.rparm.assume_base64 = 1;
rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
}
else if ((flags & GNUPG_KSBA_IO_BASE64))
{
(*ctx)->u.rparm.assume_base64 = 1;
rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
}
else if ((flags & GNUPG_KSBA_IO_AUTODETECT))
{
(*ctx)->u.rparm.autodetect = 1;
rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
}
else
rc = ksba_reader_set_cb (r, simple_reader_cb, &(*ctx)->u.rparm);
if (rc)
{
ksba_reader_release (r);
xfree (*ctx); *ctx = NULL;
return rc;
}
(*ctx)->u2.reader = r;
*r_reader = r;
return 0;
}
/* Return True if an EOF as been seen. */
int
gnupg_ksba_reader_eof_seen (gnupg_ksba_io_t ctx)
{
return ctx && ctx->u.rparm.eof_seen;
}
/* Destroy a reader object. */
void
gnupg_ksba_destroy_reader (gnupg_ksba_io_t ctx)
{
if (!ctx)
return;
ksba_reader_release (ctx->u2.reader);
xfree (ctx);
}
/* Create a writer for the given STREAM. Depending on FLAGS an output
* encoding is chosen. In PEM mode PEM_NAME is used for the header
* and footer lines; if PEM_NAME is NULL the string "CMS OBJECT" is
* used.
*
* The function returns a gnupg_ksba_io_t object which must be passed to
* the gpgme_destroy_writer function. The created ksba_writer_t
* object is stored at R_WRITER - the caller must not call the
* ksba_reader_release function on it.
*
* The supported flags are:
*
* GNUPG_KSBA_IO_PEM - Write output as PEM
* GNUPG_KSBA_IO_BASE64 - Write output as plain Base64; note that the PEM
* flag overrides this flag.
*
*/
gpg_error_t
gnupg_ksba_create_writer (gnupg_ksba_io_t *ctx, unsigned int flags,
const char *pem_name, estream_t stream,
ksba_writer_t *r_writer)
{
int rc;
ksba_writer_t w;
*r_writer = NULL;
*ctx = xtrycalloc (1, sizeof **ctx);
if (!*ctx)
return gpg_error_from_syserror ();
(*ctx)->is_writer = 1;
rc = ksba_writer_new (&w);
if (rc)
{
xfree (*ctx); *ctx = NULL;
return rc;
}
if ((flags & GNUPG_KSBA_IO_PEM) || (flags & GNUPG_KSBA_IO_BASE64))
{
(*ctx)->u.wparm.stream = stream;
if ((flags & GNUPG_KSBA_IO_PEM))
{
(*ctx)->u.wparm.pem_name = xtrystrdup (pem_name
? pem_name
: "CMS OBJECT");
if (!(*ctx)->u.wparm.pem_name)
{
rc = gpg_error_from_syserror ();
ksba_writer_release (w);
xfree (*ctx); *ctx = NULL;
return rc;
}
}
rc = ksba_writer_set_cb (w, base64_writer_cb, &(*ctx)->u.wparm);
}
else if (stream)
{
(*ctx)->u.wparm.stream = stream;
rc = ksba_writer_set_cb (w, plain_writer_cb, &(*ctx)->u.wparm);
}
else
rc = gpg_error (GPG_ERR_INV_ARG);
if (rc)
{
ksba_writer_release (w);
xfree (*ctx); *ctx = NULL;
return rc;
}
(*ctx)->u2.writer = w;
*r_writer = w;
return 0;
}
/* Flush a writer. This is for example required to write the padding
* or the PEM footer. */
gpg_error_t
gnupg_ksba_finish_writer (gnupg_ksba_io_t ctx)
{
struct writer_cb_parm_s *parm;
if (!ctx)
return gpg_error (GPG_ERR_INV_VALUE);
parm = &ctx->u.wparm;
if (parm->did_finish)
return 0; /* Already done. */
parm->did_finish = 1;
if (!parm->stream)
return 0; /* Callback was not used. */
return base64_finish_write (parm);
}
/* Destroy a writer object. */
void
gnupg_ksba_destroy_writer (gnupg_ksba_io_t ctx)
{
if (!ctx)
return;
ksba_writer_release (ctx->u2.writer);
xfree (ctx->u.wparm.pem_name);
xfree (ctx);
}
/* Set a callback to the writer object. CTRL will be bassed to the
* callback. */
void
gnupg_ksba_set_progress_cb (gnupg_ksba_io_t ctx,
gnupg_ksba_progress_cb_t cb, ctrl_t ctrl)
{
struct writer_cb_parm_s *parm;
if (!ctx || !ctx->is_writer)
return; /* Currently only supported for writer objects. */
parm = &ctx->u.wparm;
parm->progress.cb = cb;
parm->progress.ctrl = ctrl;
parm->progress.last_time = 0;
parm->progress.last = 0;
parm->progress.current = 0;
parm->progress.total = 0;
}
/* Update the total count for the progress thingy. */
void
gnupg_ksba_set_total (gnupg_ksba_io_t ctx, uint64_t total)
{
struct writer_cb_parm_s *parm;
if (!ctx || !ctx->is_writer)
return; /* Currently only supported for writer objects. */
parm = &ctx->u.wparm;
parm->progress.total = total;
}