mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-23 10:29:58 +01:00
6bdf11f671
* 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 2a13f7f9dc75265ece649e30fecd3dc694b1240e)
954 lines
26 KiB
C
954 lines
26 KiB
C
/* 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;
|
||
}
|