Page MenuHome GnuPG

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/common/iobuf.c b/common/iobuf.c
index dee3b46b1..627d33900 100644
--- a/common/iobuf.c
+++ b/common/iobuf.c
@@ -1,3070 +1,3029 @@
/* iobuf.c - File Handling for OpenPGP.
* Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008,
* 2009, 2010, 2011 Free Software Foundation, Inc.
* Copyright (C) 2015, 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 <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef HAVE_W32_SYSTEM
# ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
# endif
# include <windows.h>
#endif
#ifdef __riscos__
# include <kernel.h>
# include <swis.h>
#endif /* __riscos__ */
#include <assuan.h>
#include "util.h"
#include "sysutils.h"
#include "iobuf.h"
/*-- Begin configurable part. --*/
/* The standard size of the internal buffers. */
#define DEFAULT_IOBUF_BUFFER_SIZE (64*1024)
/* To avoid a potential DoS with compression packets we better limit
the number of filters in a chain. */
#define MAX_NESTING_FILTER 64
/* The threshold for switching to use external buffers directly
instead of the internal buffers. */
#define IOBUF_ZEROCOPY_THRESHOLD_SIZE 1024
/*-- End configurable part. --*/
/* The size of the iobuffers. This can be changed using the
* iobuf_set_buffer_size function. */
static unsigned int iobuf_buffer_size = DEFAULT_IOBUF_BUFFER_SIZE;
#ifdef HAVE_W32_SYSTEM
# define FD_FOR_STDIN (GetStdHandle (STD_INPUT_HANDLE))
# define FD_FOR_STDOUT (GetStdHandle (STD_OUTPUT_HANDLE))
#else /*!HAVE_W32_SYSTEM*/
# define FD_FOR_STDIN (0)
# define FD_FOR_STDOUT (1)
#endif /*!HAVE_W32_SYSTEM*/
/* The context used by the file filter. */
typedef struct
{
gnupg_fd_t fp; /* Open file pointer or handle. */
int keep_open;
int no_cache;
int eof_seen;
int delayed_rc;
int print_only_name; /* Flags indicating that fname is not a real file. */
char peeked[32]; /* Read ahead buffer. */
byte npeeked; /* Number of bytes valid in peeked. */
byte upeeked; /* Number of bytes used from peeked. */
char fname[1]; /* Name of the file. */
} file_filter_ctx_t;
/* The context used by the estream filter. */
typedef struct
{
estream_t fp; /* Open estream handle. */
int keep_open;
int no_cache;
int eof_seen;
int use_readlimit; /* Take care of the readlimit. */
size_t readlimit; /* Number of bytes left to read. */
int print_only_name; /* Flags indicating that fname is not a real file. */
char fname[1]; /* Name of the file. */
} file_es_filter_ctx_t;
/* Object to control the "close cache". */
struct close_cache_s
{
struct close_cache_s *next;
gnupg_fd_t fp;
char fname[1];
};
typedef struct close_cache_s *close_cache_t;
static close_cache_t close_cache;
int iobuf_debug_mode;
#ifdef HAVE_W32_SYSTEM
typedef struct
{
int sock;
int keep_open;
int no_cache;
int eof_seen;
int print_only_name; /* Flag indicating that fname is not a real file. */
char fname[1]; /* Name of the file */
} sock_filter_ctx_t;
#endif /*HAVE_W32_SYSTEM*/
/* The first partial length header block must be of size 512 to make
* it easier (and more efficient) we use a min. block size of 512 for
* all chunks (but the last one) */
#define OP_MIN_PARTIAL_CHUNK 512
#define OP_MIN_PARTIAL_CHUNK_2POW 9
/* The context we use for the block filter (used to handle OpenPGP
length information header). */
typedef struct
{
int use;
size_t size;
size_t count;
int partial; /* 1 = partial header, 2 in last partial packet. */
char *buffer; /* Used for partial header. */
size_t buflen; /* Used size of buffer. */
int first_c; /* First character of a partial header (which is > 0). */
int eof;
}
block_filter_ctx_t;
/* Local prototypes. */
static int underflow (iobuf_t a, int clear_pending_eof);
static int underflow_target (iobuf_t a, int clear_pending_eof, size_t target);
static int translate_file_handle (int fd, int for_write);
/* Sends any pending data to the filter's FILTER function. Note: this
works on the filter and not on the whole pipeline. That is,
iobuf_flush doesn't necessarily cause data to be written to any
underlying file; it just causes any data buffered at the filter A
to be sent to A's filter function.
If A is a IOBUF_OUTPUT_TEMP filter, then this also enlarges the
buffer by iobuf_buffer_size.
May only be called on an IOBUF_OUTPUT or IOBUF_OUTPUT_TEMP filters. */
static int filter_flush (iobuf_t a);
/* This is a replacement for strcmp. Under W32 it does not
distinguish between backslash and slash. */
static int
fd_cache_strcmp (const char *a, const char *b)
{
#ifdef HAVE_DOSISH_SYSTEM
for (; *a && *b; a++, b++)
{
if (*a != *b && !((*a == '/' && *b == '\\')
|| (*a == '\\' && *b == '/')) )
break;
}
return *(const unsigned char *)a - *(const unsigned char *)b;
#else
return strcmp (a, b);
#endif
}
/*
* Invalidate (i.e. close) a cached iobuf
*/
static int
fd_cache_invalidate (const char *fname)
{
close_cache_t cc;
int rc = 0;
log_assert (fname);
if (DBG_IOBUF)
log_debug ("fd_cache_invalidate (%s)\n", fname);
for (cc = close_cache; cc; cc = cc->next)
{
if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname))
{
if (DBG_IOBUF)
log_debug (" did (%s)\n", cc->fname);
#ifdef HAVE_W32_SYSTEM
if (!CloseHandle (cc->fp))
rc = -1;
#else
rc = close (cc->fp);
#endif
cc->fp = GNUPG_INVALID_FD;
}
}
return rc;
}
/* Try to sync changes to the disk. This is to avoid data loss during
a system crash in write/close/rename cycle on some file
systems. */
static int
fd_cache_synchronize (const char *fname)
{
int err = 0;
#ifdef HAVE_FSYNC
close_cache_t cc;
if (DBG_IOBUF)
log_debug ("fd_cache_synchronize (%s)\n", fname);
for (cc=close_cache; cc; cc = cc->next )
{
if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname))
{
if (DBG_IOBUF)
log_debug (" did (%s)\n", cc->fname);
err = fsync (cc->fp);
}
}
#else
(void)fname;
#endif /*HAVE_FSYNC*/
return err;
}
static gnupg_fd_t
direct_open (const char *fname, const char *mode, int mode700)
{
#ifdef HAVE_W32_SYSTEM
unsigned long da, cd, sm;
HANDLE hfile;
(void)mode700;
/* Note, that we do not handle all mode combinations */
/* According to the ReactOS source it seems that open() of the
* standard MSW32 crt does open the file in shared mode which is
* something new for MS applications ;-)
*/
if (strchr (mode, '+'))
{
if (fd_cache_invalidate (fname))
return GNUPG_INVALID_FD;
da = GENERIC_READ | GENERIC_WRITE;
cd = OPEN_EXISTING;
sm = FILE_SHARE_READ | FILE_SHARE_WRITE;
}
else if (strchr (mode, 'w'))
{
if (fd_cache_invalidate (fname))
return GNUPG_INVALID_FD;
da = GENERIC_WRITE;
cd = CREATE_ALWAYS;
sm = FILE_SHARE_WRITE;
}
else
{
da = GENERIC_READ;
cd = OPEN_EXISTING;
sm = FILE_SHARE_READ;
}
/* We always use the Unicode version because it supports file names
* longer than MAX_PATH. (requires gpgrt 1.45) */
if (1)
{
wchar_t *wfname = gpgrt_fname_to_wchar (fname);
if (wfname)
{
hfile = CreateFileW (wfname, da, sm, NULL, cd,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hfile == INVALID_HANDLE_VALUE)
{
gnupg_w32_set_errno (-1);
if (DBG_IOBUF)
log_debug ("iobuf:direct_open '%s' CreateFile failed: %s\n",
fname, gpg_strerror (gpg_error_from_syserror()));
}
xfree (wfname);
}
else
hfile = INVALID_HANDLE_VALUE;
}
return hfile;
#else /*!HAVE_W32_SYSTEM*/
int oflag;
int cflag = S_IRUSR | S_IWUSR;
if (!mode700)
cflag |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
/* Note, that we do not handle all mode combinations */
if (strchr (mode, '+'))
{
if (fd_cache_invalidate (fname))
return GNUPG_INVALID_FD;
oflag = O_RDWR;
}
else if (strchr (mode, 'w'))
{
if (fd_cache_invalidate (fname))
return GNUPG_INVALID_FD;
oflag = O_WRONLY | O_CREAT | O_TRUNC;
}
else
{
oflag = O_RDONLY;
}
#ifdef O_BINARY
if (strchr (mode, 'b'))
oflag |= O_BINARY;
#endif
#ifdef __riscos__
{
struct stat buf;
/* Don't allow iobufs on directories */
if (!stat (fname, &buf) && S_ISDIR (buf.st_mode) && !S_ISREG (buf.st_mode))
return __set_errno (EISDIR);
}
#endif
return open (fname, oflag, cflag);
#endif /*!HAVE_W32_SYSTEM*/
}
/*
* Instead of closing an FD we keep it open and cache it for later reuse
* Note that this caching strategy only works if the process does not chdir.
*/
static void
fd_cache_close (const char *fname, gnupg_fd_t fp)
{
close_cache_t cc;
log_assert (fp);
if (!fname || !*fname)
{
#ifdef HAVE_W32_SYSTEM
CloseHandle (fp);
#else
close (fp);
#endif
if (DBG_IOBUF)
log_debug ("fd_cache_close (%d) real\n", (int)fp);
return;
}
/* try to reuse a slot */
for (cc = close_cache; cc; cc = cc->next)
{
if (cc->fp == GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname))
{
cc->fp = fp;
if (DBG_IOBUF)
log_debug ("fd_cache_close (%s) used existing slot\n", fname);
return;
}
}
/* add a new one */
if (DBG_IOBUF)
log_debug ("fd_cache_close (%s) new slot created\n", fname);
cc = xcalloc (1, sizeof *cc + strlen (fname));
strcpy (cc->fname, fname);
cc->fp = fp;
cc->next = close_cache;
close_cache = cc;
}
/*
* Do a direct_open on FNAME but first try to reuse one from the fd_cache
*/
static gnupg_fd_t
fd_cache_open (const char *fname, const char *mode)
{
close_cache_t cc;
log_assert (fname);
for (cc = close_cache; cc; cc = cc->next)
{
if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname))
{
gnupg_fd_t fp = cc->fp;
cc->fp = GNUPG_INVALID_FD;
if (DBG_IOBUF)
log_debug ("fd_cache_open (%s) using cached fp\n", fname);
#ifdef HAVE_W32_SYSTEM
if (SetFilePointer (fp, 0, NULL, FILE_BEGIN) == 0xffffffff)
{
int ec = (int) GetLastError ();
log_error ("rewind file failed on handle %p: ec=%d\n", fp, ec);
gnupg_w32_set_errno (ec);
fp = GNUPG_INVALID_FD;
}
#else
if (lseek (fp, 0, SEEK_SET) == (off_t) - 1)
{
log_error ("can't rewind fd %d: %s\n", fp, strerror (errno));
fp = GNUPG_INVALID_FD;
}
#endif
return fp;
}
}
if (DBG_IOBUF)
log_debug ("fd_cache_open (%s) not cached\n", fname);
return direct_open (fname, mode, 0);
}
static int
file_filter (void *opaque, int control, iobuf_t chain, byte * buf,
size_t * ret_len)
{
file_filter_ctx_t *a = opaque;
gnupg_fd_t f = a->fp;
size_t size = *ret_len;
size_t nbytes = 0;
int rc = 0;
(void)chain; /* Not used. */
if (control == IOBUFCTRL_UNDERFLOW)
{
log_assert (size); /* We need a buffer. */
if (a->npeeked > a->upeeked)
{
nbytes = a->npeeked - a->upeeked;
if (nbytes > size)
nbytes = size;
memcpy (buf, a->peeked + a->upeeked, nbytes);
a->upeeked += nbytes;
*ret_len = nbytes;
}
else if (a->eof_seen)
{
rc = -1;
*ret_len = 0;
}
else if (a->delayed_rc)
{
rc = a->delayed_rc;
a->delayed_rc = 0;
if (rc == -1)
a->eof_seen = -1;
*ret_len = 0;
}
else
{
#ifdef HAVE_W32_SYSTEM
unsigned long nread;
nbytes = 0;
if (!ReadFile (f, buf, size, &nread, NULL))
{
int ec = (int) GetLastError ();
if (ec != ERROR_BROKEN_PIPE)
{
rc = gpg_error_from_errno (ec);
log_error ("%s: read error: %s (ec=%d)\n",
a->fname, gpg_strerror (rc), ec);
}
}
else if (!nread)
{
a->eof_seen = 1;
rc = -1;
}
else
{
nbytes = nread;
}
#else
int n;
nbytes = 0;
read_more:
do
{
n = read (f, buf + nbytes, size - nbytes);
}
while (n == -1 && errno == EINTR);
if (n > 0)
{
nbytes += n;
if (nbytes < size)
goto read_more;
}
else if (!n) /* eof */
{
if (nbytes)
a->delayed_rc = -1;
else
{
a->eof_seen = 1;
rc = -1;
}
}
else /* error */
{
rc = gpg_error_from_syserror ();
if (gpg_err_code (rc) != GPG_ERR_EPIPE)
log_error ("%s: read error: %s\n", a->fname, gpg_strerror (rc));
if (nbytes)
{
a->delayed_rc = rc;
rc = 0;
}
}
#endif
*ret_len = nbytes;
}
}
else if (control == IOBUFCTRL_FLUSH)
{
if (size)
{
#ifdef HAVE_W32_SYSTEM
byte *p = buf;
unsigned long n;
nbytes = size;
do
{
if (size && !WriteFile (f, p, nbytes, &n, NULL))
{
int ec = (int) GetLastError ();
rc = gpg_error_from_errno (ec);
log_error ("%s: write error: %s (ec=%d)\n",
a->fname, gpg_strerror (rc), ec);
break;
}
p += n;
nbytes -= n;
}
while (nbytes);
nbytes = p - buf;
#else
byte *p = buf;
int n;
nbytes = size;
do
{
do
{
n = write (f, p, nbytes);
}
while (n == -1 && errno == EINTR);
if (n > 0)
{
p += n;
nbytes -= n;
}
}
while (n != -1 && nbytes);
if (n == -1)
{
rc = gpg_error_from_syserror ();
log_error ("%s: write error: %s\n", a->fname, strerror (errno));
}
nbytes = p - buf;
#endif
}
*ret_len = nbytes;
}
else if (control == IOBUFCTRL_INIT)
{
a->eof_seen = 0;
a->delayed_rc = 0;
a->keep_open = 0;
a->no_cache = 0;
a->npeeked = 0;
a->upeeked = 0;
}
else if (control == IOBUFCTRL_PEEK)
{
/* Peek on the input. */
#ifdef HAVE_W32_SYSTEM
unsigned long nread;
nbytes = 0;
if (!ReadFile (f, a->peeked, sizeof a->peeked, &nread, NULL))
{
int ec = (int) GetLastError ();
if (ec != ERROR_BROKEN_PIPE)
{
rc = gpg_error_from_errno (ec);
log_error ("%s: read error: %s (ec=%d)\n",
a->fname, gpg_strerror (rc), ec);
}
a->npeeked = 0;
}
else if (!nread)
{
a->eof_seen = 1;
a->npeeked = 0;
}
else
{
a->npeeked = nread;
}
#else /* Unix */
int n;
peek_more:
do
{
n = read (f, a->peeked + a->npeeked, sizeof a->peeked - a->npeeked);
}
while (n == -1 && errno == EINTR);
if (n > 0)
{
a->npeeked += n;
if (a->npeeked < sizeof a->peeked)
goto peek_more;
}
else if (!n) /* eof */
{
if (a->npeeked)
a->delayed_rc = -1;
else
a->eof_seen = 1;
}
else /* error */
{
rc = gpg_error_from_syserror ();
if (gpg_err_code (rc) != GPG_ERR_EPIPE)
log_error ("%s: read error: %s\n", a->fname, gpg_strerror (rc));
if (a->npeeked)
a->delayed_rc = rc;
}
#endif /* Unix */
size = a->npeeked < size? a->npeeked : size;
memcpy (buf, a->peeked, size);
*ret_len = size;
rc = 0; /* Return success - the user needs to check ret_len. */
}
else if (control == IOBUFCTRL_DESC)
{
mem2str (buf, "file_filter(fd)", *ret_len);
}
else if (control == IOBUFCTRL_FREE)
{
if (f != FD_FOR_STDIN && f != FD_FOR_STDOUT)
{
if (DBG_IOBUF)
log_debug ("%s: close fd/handle %d\n", a->fname, FD2INT (f));
if (!a->keep_open)
fd_cache_close (a->no_cache ? NULL : a->fname, f);
}
xfree (a); /* We can free our context now. */
}
return rc;
}
/* Similar to file_filter but using the estream system. */
static int
file_es_filter (void *opaque, int control, iobuf_t chain, byte * buf,
size_t * ret_len)
{
file_es_filter_ctx_t *a = opaque;
estream_t f = a->fp;
size_t size = *ret_len;
size_t nbytes = 0;
int rc = 0;
(void)chain; /* Not used. */
if (control == IOBUFCTRL_UNDERFLOW)
{
log_assert (size); /* We need a buffer. */
if (a->eof_seen)
{
rc = -1;
*ret_len = 0;
}
else if (a->use_readlimit)
{
nbytes = 0;
if (!a->readlimit)
{ /* eof */
a->eof_seen = 1;
rc = -1;
}
else
{
if (size > a->readlimit)
size = a->readlimit;
rc = es_read (f, buf, size, &nbytes);
if (rc == -1)
{ /* error */
rc = gpg_error_from_syserror ();
log_error ("%s: read error: %s\n", a->fname,strerror (errno));
}
else if (!nbytes)
{ /* eof */
a->eof_seen = 1;
rc = -1;
}
else
a->readlimit -= nbytes;
}
*ret_len = nbytes;
}
else
{
nbytes = 0;
rc = es_read (f, buf, size, &nbytes);
if (rc == -1)
{ /* error */
rc = gpg_error_from_syserror ();
log_error ("%s: read error: %s\n", a->fname, strerror (errno));
}
else if (!nbytes)
{ /* eof */
a->eof_seen = 1;
rc = -1;
}
*ret_len = nbytes;
}
}
else if (control == IOBUFCTRL_FLUSH)
{
if (size)
{
byte *p = buf;
size_t nwritten;
nbytes = size;
do
{
nwritten = 0;
if (es_write (f, p, nbytes, &nwritten))
{
rc = gpg_error_from_syserror ();
log_error ("%s: write error: %s\n",
a->fname, strerror (errno));
break;
}
p += nwritten;
nbytes -= nwritten;
}
while (nbytes);
nbytes = p - buf;
}
*ret_len = nbytes;
}
else if (control == IOBUFCTRL_INIT)
{
a->eof_seen = 0;
a->no_cache = 0;
}
else if (control == IOBUFCTRL_DESC)
{
mem2str (buf, "estream_filter", *ret_len);
}
else if (control == IOBUFCTRL_FREE)
{
if (f != es_stdin && f != es_stdout)
{
if (DBG_IOBUF)
log_debug ("%s: es_fclose %p\n", a->fname, f);
if (!a->keep_open)
es_fclose (f);
}
f = NULL;
xfree (a); /* We can free our context now. */
}
return rc;
}
#ifdef HAVE_W32_SYSTEM
/* Because network sockets are special objects under Lose32 we have to
use a dedicated filter for them. */
static int
sock_filter (void *opaque, int control, iobuf_t chain, byte * buf,
size_t * ret_len)
{
sock_filter_ctx_t *a = opaque;
size_t size = *ret_len;
size_t nbytes = 0;
int rc = 0;
(void)chain;
if (control == IOBUFCTRL_UNDERFLOW)
{
log_assert (size); /* need a buffer */
if (a->eof_seen)
{
rc = -1;
*ret_len = 0;
}
else
{
int nread;
nread = recv (a->sock, buf, size, 0);
if (nread == SOCKET_ERROR)
{
int ec = (int) WSAGetLastError ();
rc = gpg_error_from_errno (ec);
log_error ("socket read error: ec=%d\n", ec);
}
else if (!nread)
{
a->eof_seen = 1;
rc = -1;
}
else
{
nbytes = nread;
}
*ret_len = nbytes;
}
}
else if (control == IOBUFCTRL_FLUSH)
{
if (size)
{
byte *p = buf;
int n;
nbytes = size;
do
{
n = send (a->sock, p, nbytes, 0);
if (n == SOCKET_ERROR)
{
int ec = (int) WSAGetLastError ();
rc = gpg_error_from_errno (ec);
log_error ("socket write error: ec=%d\n", ec);
break;
}
p += n;
nbytes -= n;
}
while (nbytes);
nbytes = p - buf;
}
*ret_len = nbytes;
}
else if (control == IOBUFCTRL_INIT)
{
a->eof_seen = 0;
a->keep_open = 0;
a->no_cache = 0;
}
else if (control == IOBUFCTRL_DESC)
{
mem2str (buf, "sock_filter", *ret_len);
}
else if (control == IOBUFCTRL_FREE)
{
if (!a->keep_open)
closesocket (a->sock);
xfree (a); /* we can free our context now */
}
return rc;
}
#endif /*HAVE_W32_SYSTEM*/
/****************
* This is used to implement the block write mode.
* Block reading is done on a byte by byte basis in readbyte(),
* without a filter
*/
static int
block_filter (void *opaque, int control, iobuf_t chain, byte * buffer,
size_t * ret_len)
{
block_filter_ctx_t *a = opaque;
char *buf = (char *)buffer;
size_t size = *ret_len;
int c, needed, rc = 0;
char *p;
if (control == IOBUFCTRL_UNDERFLOW)
{
size_t n = 0;
p = buf;
log_assert (size); /* need a buffer */
if (a->eof) /* don't read any further */
rc = -1;
while (!rc && size)
{
if (!a->size)
{ /* get the length bytes */
if (a->partial == 2)
{
a->eof = 1;
if (!n)
rc = -1;
break;
}
else if (a->partial)
{
/* These OpenPGP introduced huffman like encoded length
* bytes are really a mess :-( */
if (a->first_c)
{
c = a->first_c;
a->first_c = 0;
}
else if ((c = iobuf_get (chain)) == -1)
{
log_error ("block_filter: 1st length byte missing\n");
rc = GPG_ERR_BAD_DATA;
break;
}
if (c < 192)
{
a->size = c;
a->partial = 2;
if (!a->size)
{
a->eof = 1;
if (!n)
rc = -1;
break;
}
}
else if (c < 224)
{
a->size = (c - 192) * 256;
if ((c = iobuf_get (chain)) == -1)
{
log_error
("block_filter: 2nd length byte missing\n");
rc = GPG_ERR_BAD_DATA;
break;
}
a->size += c + 192;
a->partial = 2;
if (!a->size)
{
a->eof = 1;
if (!n)
rc = -1;
break;
}
}
else if (c == 255)
{
size_t len = 0;
int i;
for (i = 0; i < 4; i++)
if ((c = iobuf_get (chain)) == -1)
break;
else
len = ((len << 8) | c);
if (i < 4)
{
log_error ("block_filter: invalid 4 byte length\n");
rc = GPG_ERR_BAD_DATA;
break;
}
a->size = len;
a->partial = 2;
if (!a->size)
{
a->eof = 1;
if (!n)
rc = -1;
break;
}
}
else
{ /* Next partial body length. */
a->size = 1 << (c & 0x1f);
}
/* log_debug("partial: ctx=%p c=%02x size=%u\n", a, c, a->size); */
}
else
BUG ();
}
while (!rc && size && a->size)
{
needed = size < a->size ? size : a->size;
c = iobuf_read (chain, p, needed);
if (c < needed)
{
if (c == -1)
c = 0;
log_error
("block_filter %p: read error (size=%lu,a->size=%lu)\n",
a, (ulong) size + c, (ulong) a->size + c);
rc = GPG_ERR_BAD_DATA;
}
else
{
size -= c;
a->size -= c;
p += c;
n += c;
}
}
}
*ret_len = n;
}
else if (control == IOBUFCTRL_FLUSH)
{
if (a->partial)
{ /* the complicated openpgp scheme */
size_t blen, n, nbytes = size + a->buflen;
log_assert (a->buflen <= OP_MIN_PARTIAL_CHUNK);
if (nbytes < OP_MIN_PARTIAL_CHUNK)
{
/* not enough to write a partial block out; so we store it */
if (!a->buffer)
a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK);
memcpy (a->buffer + a->buflen, buf, size);
a->buflen += size;
}
else
{ /* okay, we can write out something */
/* do this in a loop to use the most efficient block lengths */
p = buf;
do
{
/* find the best matching block length - this is limited
* by the size of the internal buffering */
for (blen = OP_MIN_PARTIAL_CHUNK * 2,
c = OP_MIN_PARTIAL_CHUNK_2POW + 1; blen <= nbytes;
blen *= 2, c++)
;
blen /= 2;
c--;
/* write the partial length header */
log_assert (c <= 0x1f); /*;-) */
c |= 0xe0;
iobuf_put (chain, c);
if ((n = a->buflen))
{ /* write stuff from the buffer */
log_assert (n == OP_MIN_PARTIAL_CHUNK);
if (iobuf_write (chain, a->buffer, n))
rc = gpg_error_from_syserror ();
a->buflen = 0;
nbytes -= n;
}
if ((n = nbytes) > blen)
n = blen;
if (n && iobuf_write (chain, p, n))
rc = gpg_error_from_syserror ();
p += n;
nbytes -= n;
}
while (!rc && nbytes >= OP_MIN_PARTIAL_CHUNK);
/* store the rest in the buffer */
if (!rc && nbytes)
{
log_assert (!a->buflen);
log_assert (nbytes < OP_MIN_PARTIAL_CHUNK);
if (!a->buffer)
a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK);
memcpy (a->buffer, p, nbytes);
a->buflen = nbytes;
}
}
}
else
BUG ();
}
else if (control == IOBUFCTRL_INIT)
{
if (DBG_IOBUF)
log_debug ("init block_filter %p\n", a);
if (a->partial)
a->count = 0;
else if (a->use == IOBUF_INPUT)
a->count = a->size = 0;
else
a->count = a->size; /* force first length bytes */
a->eof = 0;
a->buffer = NULL;
a->buflen = 0;
}
else if (control == IOBUFCTRL_DESC)
{
mem2str (buf, "block_filter", *ret_len);
}
else if (control == IOBUFCTRL_FREE)
{
if (a->use == IOBUF_OUTPUT)
{ /* write the end markers */
if (a->partial)
{
u32 len;
/* write out the remaining bytes without a partial header
* the length of this header may be 0 - but if it is
* the first block we are not allowed to use a partial header
* and frankly we can't do so, because this length must be
* a power of 2. This is _really_ complicated because we
* have to check the possible length of a packet prior
* to it's creation: a chain of filters becomes complicated
* and we need a lot of code to handle compressed packets etc.
* :-(((((((
*/
/* construct header */
len = a->buflen;
/*log_debug("partial: remaining length=%u\n", len ); */
if (len < 192)
rc = iobuf_put (chain, len);
else if (len < 8384)
{
if (!(rc = iobuf_put (chain, ((len - 192) / 256) + 192)))
rc = iobuf_put (chain, ((len - 192) % 256));
}
else
{ /* use a 4 byte header */
if (!(rc = iobuf_put (chain, 0xff)))
if (!(rc = iobuf_put (chain, (len >> 24) & 0xff)))
if (!(rc = iobuf_put (chain, (len >> 16) & 0xff)))
if (!(rc = iobuf_put (chain, (len >> 8) & 0xff)))
rc = iobuf_put (chain, len & 0xff);
}
if (!rc && len)
rc = iobuf_write (chain, a->buffer, len);
if (rc)
{
log_error ("block_filter: write error: %s\n",
strerror (errno));
rc = gpg_error_from_syserror ();
}
xfree (a->buffer);
a->buffer = NULL;
a->buflen = 0;
}
else
BUG ();
}
else if (a->size)
{
log_error ("block_filter: pending bytes!\n");
}
if (DBG_IOBUF)
log_debug ("free block_filter %p\n", a);
xfree (a); /* we can free our context now */
}
return rc;
}
/* Change the default size for all IOBUFs to KILOBYTE. This needs to
* be called before any iobufs are used and can only be used once.
* Returns the current value. Using 0 has no effect except for
* returning the current value. */
unsigned int
iobuf_set_buffer_size (unsigned int kilobyte)
{
static int used;
if (!used && kilobyte)
{
if (kilobyte < 4)
kilobyte = 4;
else if (kilobyte > 16*1024)
kilobyte = 16*1024;
iobuf_buffer_size = kilobyte * 1024;
used = 1;
}
return iobuf_buffer_size / 1024;
}
#define MAX_IOBUF_DESC 32
/*
* Fill the buffer by the description of iobuf A.
* The buffer size should be MAX_IOBUF_DESC (or larger).
* Returns BUF as (const char *).
*/
static const char *
iobuf_desc (iobuf_t a, byte *buf)
{
size_t len = MAX_IOBUF_DESC;
if (! a || ! a->filter)
memcpy (buf, "?", 2);
else
a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL, buf, &len);
return buf;
}
static void
print_chain (iobuf_t a)
{
if (!DBG_IOBUF)
return;
for (; a; a = a->chain)
{
byte desc[MAX_IOBUF_DESC];
log_debug ("iobuf chain: %d.%d '%s' filter_eof=%d start=%d len=%d\n",
a->no, a->subno, iobuf_desc (a, desc), a->filter_eof,
(int) a->d.start, (int) a->d.len);
}
}
int
iobuf_print_chain (iobuf_t a)
{
print_chain (a);
return 0;
}
iobuf_t
iobuf_alloc (int use, size_t bufsize)
{
iobuf_t a;
static int number = 0;
log_assert (use == IOBUF_INPUT || use == IOBUF_INPUT_TEMP
|| use == IOBUF_OUTPUT || use == IOBUF_OUTPUT_TEMP);
if (bufsize == 0)
{
log_bug ("iobuf_alloc() passed a bufsize of 0!\n");
bufsize = iobuf_buffer_size;
}
a = xcalloc (1, sizeof *a);
a->use = use;
a->d.buf = xmalloc (bufsize);
a->d.size = bufsize;
a->e_d.buf = NULL;
a->e_d.len = 0;
a->e_d.used = 0;
a->e_d.preferred = 0;
a->no = ++number;
a->subno = 0;
a->real_fname = NULL;
return a;
}
int
iobuf_close (iobuf_t a)
{
iobuf_t a_chain;
size_t dummy_len = 0;
int rc = 0;
for (; a; a = a_chain)
{
byte desc[MAX_IOBUF_DESC];
int rc2 = 0;
a_chain = a->chain;
if (a->use == IOBUF_OUTPUT && (rc = filter_flush (a)))
log_error ("filter_flush failed on close: %s\n", gpg_strerror (rc));
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: close '%s'\n",
a->no, a->subno, iobuf_desc (a, desc));
if (a->filter && (rc2 = a->filter (a->filter_ov, IOBUFCTRL_FREE,
a->chain, NULL, &dummy_len)))
log_error ("IOBUFCTRL_FREE failed on close: %s\n", gpg_strerror (rc));
if (! rc && rc2)
/* Whoops! An error occurred. Save it in RC if we haven't
already recorded an error. */
rc = rc2;
xfree (a->real_fname);
if (a->d.buf)
{
memset (a->d.buf, 0, a->d.size); /* erase the buffer */
xfree (a->d.buf);
}
xfree (a);
}
return rc;
}
int
iobuf_cancel (iobuf_t a)
{
const char *s;
iobuf_t a2;
int rc;
#if defined(HAVE_W32_SYSTEM) || defined(__riscos__)
char *remove_name = NULL;
#endif
if (a && a->use == IOBUF_OUTPUT)
{
s = iobuf_get_real_fname (a);
if (s && *s)
{
#if defined(HAVE_W32_SYSTEM) || defined(__riscos__)
remove_name = xstrdup (s);
#else
remove (s);
#endif
}
}
/* send a cancel message to all filters */
for (a2 = a; a2; a2 = a2->chain)
{
size_t dummy = 0;
if (a2->filter)
a2->filter (a2->filter_ov, IOBUFCTRL_CANCEL, a2->chain, NULL, &dummy);
}
rc = iobuf_close (a);
#if defined(HAVE_W32_SYSTEM) || defined(__riscos__)
if (remove_name)
{
/* Argg, MSDOS does not allow removing open files. So
* we have to do it here */
gnupg_remove (remove_name);
xfree (remove_name);
}
#endif
return rc;
}
iobuf_t
iobuf_temp (void)
{
return iobuf_alloc (IOBUF_OUTPUT_TEMP, iobuf_buffer_size);
}
iobuf_t
iobuf_temp_with_content (const char *buffer, size_t length)
{
iobuf_t a;
int i;
a = iobuf_alloc (IOBUF_INPUT_TEMP, length);
log_assert (length == a->d.size);
/* memcpy (a->d.buf, buffer, length); */
for (i=0; i < length; i++)
a->d.buf[i] = buffer[i];
a->d.len = length;
return a;
}
int
iobuf_is_pipe_filename (const char *fname)
{
if (!fname || (*fname=='-' && !fname[1]) )
return 1;
return check_special_filename (fname, 0, 1) != -1;
}
static iobuf_t
do_open (const char *fname, int special_filenames,
int use, const char *opentype, int mode700)
{
iobuf_t a;
gnupg_fd_t fp;
file_filter_ctx_t *fcx;
size_t len = 0;
int print_only = 0;
int fd;
byte desc[MAX_IOBUF_DESC];
log_assert (use == IOBUF_INPUT || use == IOBUF_OUTPUT);
if (special_filenames
/* NULL or '-'. */
&& (!fname || (*fname == '-' && !fname[1])))
{
if (use == IOBUF_INPUT)
{
fp = FD_FOR_STDIN;
fname = "[stdin]";
}
else
{
fp = FD_FOR_STDOUT;
fname = "[stdout]";
}
print_only = 1;
}
else if (!fname)
return NULL;
else if (special_filenames
&& (fd = check_special_filename (fname, 0, 1)) != -1)
return iobuf_fdopen (translate_file_handle (fd, use == IOBUF_INPUT ? 0 : 1),
opentype);
else
{
if (use == IOBUF_INPUT)
fp = fd_cache_open (fname, opentype);
else
fp = direct_open (fname, opentype, mode700);
if (fp == GNUPG_INVALID_FD)
return NULL;
}
a = iobuf_alloc (use, iobuf_buffer_size);
fcx = xmalloc (sizeof *fcx + strlen (fname));
fcx->fp = fp;
fcx->print_only_name = print_only;
strcpy (fcx->fname, fname);
if (!print_only)
a->real_fname = xstrdup (fname);
a->filter = file_filter;
a->filter_ov = fcx;
file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: open '%s' desc=%s fd=%d\n",
a->no, a->subno, fname, iobuf_desc (a, desc), FD2INT (fcx->fp));
return a;
}
iobuf_t
iobuf_open (const char *fname)
{
return do_open (fname, 1, IOBUF_INPUT, "rb", 0);
}
iobuf_t
iobuf_create (const char *fname, int mode700)
{
return do_open (fname, 1, IOBUF_OUTPUT, "wb", mode700);
}
iobuf_t
iobuf_openrw (const char *fname)
{
return do_open (fname, 0, IOBUF_OUTPUT, "r+b", 0);
}
static iobuf_t
do_iobuf_fdopen (int fd, const char *mode, int keep_open)
{
iobuf_t a;
gnupg_fd_t fp;
file_filter_ctx_t *fcx;
size_t len = 0;
fp = INT2FD (fd);
a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT,
iobuf_buffer_size);
fcx = xmalloc (sizeof *fcx + 20);
fcx->fp = fp;
fcx->print_only_name = 1;
fcx->keep_open = keep_open;
sprintf (fcx->fname, "[fd %d]", fd);
a->filter = file_filter;
a->filter_ov = fcx;
file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: fdopen%s '%s'\n",
a->no, a->subno, keep_open? "_nc":"", fcx->fname);
iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL);
return a;
}
iobuf_t
iobuf_fdopen (int fd, const char *mode)
{
return do_iobuf_fdopen (fd, mode, 0);
}
iobuf_t
iobuf_fdopen_nc (int fd, const char *mode)
{
return do_iobuf_fdopen (fd, mode, 1);
}
iobuf_t
iobuf_esopen (estream_t estream, const char *mode, int keep_open,
size_t readlimit)
{
iobuf_t a;
file_es_filter_ctx_t *fcx;
size_t len = 0;
a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT,
iobuf_buffer_size);
fcx = xtrymalloc (sizeof *fcx + 30);
fcx->fp = estream;
fcx->print_only_name = 1;
fcx->keep_open = keep_open;
fcx->readlimit = readlimit;
fcx->use_readlimit = !!readlimit;
snprintf (fcx->fname, 30, "[fd %p]", estream);
a->filter = file_es_filter;
a->filter_ov = fcx;
file_es_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: esopen%s '%s'\n",
a->no, a->subno, keep_open? "_nc":"", fcx->fname);
return a;
}
iobuf_t
iobuf_sockopen (int fd, const char *mode)
{
iobuf_t a;
#ifdef HAVE_W32_SYSTEM
sock_filter_ctx_t *scx;
size_t len;
a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT,
iobuf_buffer_size);
scx = xmalloc (sizeof *scx + 25);
scx->sock = fd;
scx->print_only_name = 1;
sprintf (scx->fname, "[sock %d]", fd);
a->filter = sock_filter;
a->filter_ov = scx;
sock_filter (scx, IOBUFCTRL_INIT, NULL, NULL, &len);
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: sockopen '%s'\n", a->no, a->subno, scx->fname);
iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL);
#else
a = iobuf_fdopen (fd, mode);
#endif
return a;
}
int
iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval)
{
byte desc[MAX_IOBUF_DESC];
if (cmd == IOBUF_IOCTL_KEEP_OPEN)
{
/* Keep system filepointer/descriptor open. This was used in
the past by http.c; this ioctl is not directly used
anymore. */
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: ioctl '%s' keep_open=%d\n",
a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a, desc),
intval);
for (; a; a = a->chain)
if (!a->chain && a->filter == file_filter)
{
file_filter_ctx_t *b = a->filter_ov;
b->keep_open = intval;
return 0;
}
#ifdef HAVE_W32_SYSTEM
else if (!a->chain && a->filter == sock_filter)
{
sock_filter_ctx_t *b = a->filter_ov;
b->keep_open = intval;
return 0;
}
#endif
}
else if (cmd == IOBUF_IOCTL_INVALIDATE_CACHE)
{
if (DBG_IOBUF)
log_debug ("iobuf-*.*: ioctl '%s' invalidate\n",
ptrval ? (char *) ptrval : "?");
if (!a && !intval && ptrval)
{
if (fd_cache_invalidate (ptrval))
return -1;
return 0;
}
}
else if (cmd == IOBUF_IOCTL_NO_CACHE)
{
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: ioctl '%s' no_cache=%d\n",
a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a, desc),
intval);
for (; a; a = a->chain)
if (!a->chain && a->filter == file_filter)
{
file_filter_ctx_t *b = a->filter_ov;
b->no_cache = intval;
return 0;
}
#ifdef HAVE_W32_SYSTEM
else if (!a->chain && a->filter == sock_filter)
{
sock_filter_ctx_t *b = a->filter_ov;
b->no_cache = intval;
return 0;
}
#endif
}
else if (cmd == IOBUF_IOCTL_FSYNC)
{
/* Do a fsync on the open fd and return any errors to the caller
of iobuf_ioctl. Note that we work on a file name here. */
if (DBG_IOBUF)
log_debug ("iobuf-*.*: ioctl '%s' fsync\n",
ptrval? (const char*)ptrval:"<null>");
if (!a && !intval && ptrval)
{
return fd_cache_synchronize (ptrval);
}
}
else if (cmd == IOBUF_IOCTL_PEEK)
{
/* Peek at a justed opened file. Use this only directly after a
* file has been opened for reading. Don't use it after you did
* a seek. This works only if just file filter has been
* pushed. Expects a buffer wit size INTVAL at PTRVAL and returns
* the number of bytes put into the buffer. */
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: ioctl '%s' peek\n",
a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a, desc));
if (a->filter == file_filter && ptrval && intval)
{
file_filter_ctx_t *fcx = a->filter_ov;
size_t len = intval;
if (!file_filter (fcx, IOBUFCTRL_PEEK, NULL, ptrval, &len))
return (int)len;
}
}
return -1;
}
/****************
* Register an i/o filter.
*/
int
iobuf_push_filter (iobuf_t a,
int (*f) (void *opaque, int control,
iobuf_t chain, byte * buf, size_t * len),
void *ov)
{
return iobuf_push_filter2 (a, f, ov, 0);
}
int
iobuf_push_filter2 (iobuf_t a,
int (*f) (void *opaque, int control,
iobuf_t chain, byte * buf, size_t * len),
void *ov, int rel_ov)
{
iobuf_t b;
size_t dummy_len = 0;
int rc = 0;
if (a->use == IOBUF_OUTPUT && (rc = filter_flush (a)))
return rc;
if (a->subno >= MAX_NESTING_FILTER)
{
log_error ("i/o filter too deeply nested - corrupted data?\n");
return GPG_ERR_BAD_DATA;
}
/* We want to create a new filter and put it in front of A. A
simple implementation would do:
b = iobuf_alloc (...);
b->chain = a;
return a;
This is a bit problematic: A is the head of the pipeline and
there are potentially many pointers to it. Requiring the caller
to update all of these pointers is a burden.
An alternative implementation would add a level of indirection.
For instance, we could use a pipeline object, which contains a
pointer to the first filter in the pipeline. This is not what we
do either.
Instead, we allocate a new buffer (B) and copy the first filter's
state into that and use the initial buffer (A) for the new
filter. One limitation of this approach is that it is not
practical to maintain a pointer to a specific filter's state.
Before:
A
|
v 0x100 0x200
+----------+ +----------+
| filter x |--------->| filter y |---->....
+----------+ +----------+
After: B
|
v 0x300
+----------+
A | filter x |
| +----------+
v 0x100 ^ v 0x200
+----------+ +----------+
| filter w | | filter y |---->....
+----------+ +----------+
Note: filter x's address changed from 0x100 to 0x300, but A still
points to the head of the pipeline.
*/
b = xmalloc (sizeof *b);
memcpy (b, a, sizeof *b);
/* fixme: it is stupid to keep a copy of the name at every level
* but we need the name somewhere because the name known by file_filter
* may have been released when we need the name of the file */
b->real_fname = a->real_fname ? xstrdup (a->real_fname) : NULL;
/* remove the filter stuff from the new stream */
a->filter = NULL;
a->filter_ov = NULL;
a->filter_ov_owner = 0;
a->filter_eof = 0;
if (a->use == IOBUF_OUTPUT_TEMP)
/* A TEMP filter buffers any data sent to it; it does not forward
any data down the pipeline. If we add a new filter to the
pipeline, it shouldn't also buffer data. It should send it
downstream to be buffered. Thus, the correct type for a filter
added in front of an IOBUF_OUTPUT_TEMP filter is IOBUF_OUPUT, not
IOBUF_OUTPUT_TEMP. */
{
a->use = IOBUF_OUTPUT;
/* When pipeline is written to, the temp buffer's size is
increased accordingly. We don't need to allocate a 10 MB
buffer for a non-terminal filter. Just use the default
size. */
a->d.size = iobuf_buffer_size;
}
else if (a->use == IOBUF_INPUT_TEMP)
/* Same idea as above. */
{
a->use = IOBUF_INPUT;
a->d.size = iobuf_buffer_size;
}
/* The new filter (A) gets a new buffer.
If the pipeline is an output or temp pipeline, then giving the
buffer to the new filter means that data that was written before
the filter was pushed gets sent to the filter. That's clearly
wrong.
If the pipeline is an input pipeline, then giving the buffer to
the new filter (A) means that data that has read from (B), but
not yet read from the pipeline won't be processed by the new
filter (A)! That's certainly not what we want. */
a->d.buf = xmalloc (a->d.size);
a->d.len = 0;
a->d.start = 0;
/* disable nlimit for the new stream */
a->ntotal = b->ntotal + b->nbytes;
a->nlimit = a->nbytes = 0;
a->nofast = 0;
/* make a link from the new stream to the original stream */
a->chain = b;
/* setup the function on the new stream */
a->filter = f;
a->filter_ov = ov;
a->filter_ov_owner = rel_ov;
a->subno = b->subno + 1;
if (DBG_IOBUF)
{
byte desc[MAX_IOBUF_DESC];
log_debug ("iobuf-%d.%d: push '%s'\n",
a->no, a->subno, iobuf_desc (a, desc));
print_chain (a);
}
/* now we can initialize the new function if we have one */
if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_INIT, a->chain,
NULL, &dummy_len)))
log_error ("IOBUFCTRL_INIT failed: %s\n", gpg_strerror (rc));
return rc;
}
/****************
* Remove an i/o filter.
*/
int
iobuf_pop_filter (iobuf_t a, int (*f) (void *opaque, int control,
iobuf_t chain, byte * buf, size_t * len),
void *ov)
{
iobuf_t b;
size_t dummy_len = 0;
int rc = 0;
byte desc[MAX_IOBUF_DESC];
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: pop '%s'\n",
a->no, a->subno, iobuf_desc (a, desc));
if (a->use == IOBUF_INPUT_TEMP || a->use == IOBUF_OUTPUT_TEMP)
{
/* This should be the last filter in the pipeline. */
log_assert (! a->chain);
return 0;
}
if (!a->filter)
{ /* this is simple */
b = a->chain;
log_assert (b);
xfree (a->d.buf);
xfree (a->real_fname);
memcpy (a, b, sizeof *a);
xfree (b);
return 0;
}
for (b = a; b; b = b->chain)
if (b->filter == f && (!ov || b->filter_ov == ov))
break;
if (!b)
log_bug ("iobuf_pop_filter(): filter function not found\n");
/* flush this stream if it is an output stream */
if (a->use == IOBUF_OUTPUT && (rc = filter_flush (b)))
{
log_error ("filter_flush failed in iobuf_pop_filter: %s\n",
gpg_strerror (rc));
return rc;
}
/* and tell the filter to free it self */
if (b->filter && (rc = b->filter (b->filter_ov, IOBUFCTRL_FREE, b->chain,
NULL, &dummy_len)))
{
log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc));
return rc;
}
if (b->filter_ov && b->filter_ov_owner)
{
xfree (b->filter_ov);
b->filter_ov = NULL;
}
/* and see how to remove it */
if (a == b && !b->chain)
log_bug ("can't remove the last filter from the chain\n");
else if (a == b)
{ /* remove the first iobuf from the chain */
/* everything from b is copied to a. This is save because
* a flush has been done on the to be removed entry
*/
b = a->chain;
xfree (a->d.buf);
xfree (a->real_fname);
memcpy (a, b, sizeof *a);
xfree (b);
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: popped filter\n", a->no, a->subno);
}
else if (!b->chain)
{ /* remove the last iobuf from the chain */
log_bug ("Ohh jeee, trying to remove a head filter\n");
}
else
{ /* remove an intermediate iobuf from the chain */
log_bug ("Ohh jeee, trying to remove an intermediate filter\n");
}
return rc;
}
/****************
* read underflow: read at least one byte into the buffer and return
* the first byte or -1 on EOF.
*/
static int
underflow (iobuf_t a, int clear_pending_eof)
{
return underflow_target (a, clear_pending_eof, 1);
}
/****************
* read underflow: read TARGET bytes into the buffer and return
* the first byte or -1 on EOF.
*/
static int
underflow_target (iobuf_t a, int clear_pending_eof, size_t target)
{
size_t len;
int rc;
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: underflow: buffer size: %d; still buffered: %d => space for %d bytes\n",
a->no, a->subno,
(int) a->d.size, (int) (a->d.len - a->d.start),
(int) (a->d.size - (a->d.len - a->d.start)));
if (a->use == IOBUF_INPUT_TEMP)
/* By definition, there isn't more data to read into the
buffer. */
return -1;
log_assert (a->use == IOBUF_INPUT);
a->e_d.used = 0;
/* If there is still some buffered data, then move it to the start
of the buffer and try to fill the end of the buffer. (This is
useful if we are called from iobuf_peek().) */
log_assert (a->d.start <= a->d.len);
a->d.len -= a->d.start;
if (a->d.len)
memmove (a->d.buf, &a->d.buf[a->d.start], a->d.len);
a->d.start = 0;
if (a->d.len < target && a->filter_eof)
/* The last time we tried to read from this filter, we got an EOF.
We couldn't return the EOF, because there was buffered data.
Since there is no longer any buffered data, return the
error. */
{
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: underflow: eof (pending eof)\n",
a->no, a->subno);
if (! clear_pending_eof)
return -1;
if (a->chain)
/* A filter follows this one. Free this filter. */
{
iobuf_t b = a->chain;
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: filter popped (pending EOF returned)\n",
a->no, a->subno);
xfree (a->d.buf);
xfree (a->real_fname);
memcpy (a, b, sizeof *a);
xfree (b);
print_chain (a);
}
else
a->filter_eof = 0; /* for the top level filter */
return -1; /* return one(!) EOF */
}
if (a->d.len == 0 && a->error)
/* The last time we tried to read from this filter, we got an
error. We couldn't return the error, because there was
buffered data. Since there is no longer any buffered data,
return the error. */
{
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: pending error (%s) returned\n",
a->no, a->subno, gpg_strerror (a->error));
return -1;
}
if (a->filter && ! a->filter_eof && ! a->error)
/* We have a filter function and the last time we tried to read we
didn't get an EOF or an error. Try to fill the buffer. */
{
/* Be careful to account for any buffered data. */
len = a->d.size - a->d.len;
if (a->e_d.preferred && a->d.len < IOBUF_ZEROCOPY_THRESHOLD_SIZE
&& (IOBUF_ZEROCOPY_THRESHOLD_SIZE - a->d.len) < len)
{
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: limit buffering as external drain is "
"preferred\n", a->no, a->subno);
len = IOBUF_ZEROCOPY_THRESHOLD_SIZE - a->d.len;
}
if (len == 0)
/* There is no space for more data. Don't bother calling
A->FILTER. */
rc = 0;
else
{
/* If no buffered data and drain buffer has been setup, and drain
* buffer is largish, read data directly to drain buffer. */
if (a->d.len == 0
&& a->e_d.buf
&& a->e_d.len >= IOBUF_ZEROCOPY_THRESHOLD_SIZE)
{
len = a->e_d.len;
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: underflow: A->FILTER (%lu bytes, to external drain)\n",
a->no, a->subno, (ulong)len);
rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
a->e_d.buf, &len);
a->e_d.used = len;
len = 0;
}
else
{
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: underflow: A->FILTER (%lu bytes)\n",
a->no, a->subno, (ulong)len);
rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
&a->d.buf[a->d.len], &len);
}
}
a->d.len += len;
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: A->FILTER() returned rc=%d (%s), read %lu bytes%s\n",
a->no, a->subno,
rc, rc == 0 ? "ok" : rc == -1 ? "EOF" : gpg_strerror (rc),
(ulong)(a->e_d.used ? a->e_d.used : len),
a->e_d.used ? " (to external buffer)" : "");
/* if( a->no == 1 ) */
/* log_hexdump (" data:", a->d.buf, len); */
if (rc == -1)
/* EOF. */
{
size_t dummy_len = 0;
/* Tell the filter to free itself */
if ((rc = a->filter (a->filter_ov, IOBUFCTRL_FREE, a->chain,
NULL, &dummy_len)))
log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc));
/* Free everything except for the internal buffer. */
if (a->filter_ov && a->filter_ov_owner)
xfree (a->filter_ov);
a->filter_ov = NULL;
a->filter = NULL;
a->filter_eof = 1;
if (clear_pending_eof && a->d.len == 0 && a->e_d.used == 0
&& a->chain)
/* We don't need to keep this filter around at all:
- we got an EOF
- we have no buffered data
- a filter follows this one.
Unlink this filter. */
{
iobuf_t b = a->chain;
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: pop in underflow (nothing buffered, got EOF)\n",
a->no, a->subno);
xfree (a->d.buf);
xfree (a->real_fname);
memcpy (a, b, sizeof *a);
xfree (b);
print_chain (a);
return -1;
}
else if (a->d.len == 0 && a->e_d.used == 0)
/* We can't unlink this filter (it is the only one in the
pipeline), but we can immediately return EOF. */
return -1;
}
else if (rc)
/* Record the error. */
{
a->error = rc;
if (a->d.len == 0 && a->e_d.used == 0)
/* There is no buffered data. Immediately return EOF. */
return -1;
}
}
log_assert (a->d.start <= a->d.len);
if (a->e_d.used > 0)
return 0;
if (a->d.start < a->d.len)
return a->d.buf[a->d.start++];
/* EOF. */
return -1;
}
static int
filter_flush (iobuf_t a)
{
int external_used = 0;
byte *src_buf;
size_t src_len;
size_t len;
int rc;
a->e_d.used = 0;
if (a->use == IOBUF_OUTPUT_TEMP)
{ /* increase the temp buffer */
size_t newsize = a->d.size + iobuf_buffer_size;
if (DBG_IOBUF)
log_debug ("increasing temp iobuf from %lu to %lu\n",
(ulong) a->d.size, (ulong) newsize);
a->d.buf = xrealloc (a->d.buf, newsize);
a->d.size = newsize;
return 0;
}
else if (a->use != IOBUF_OUTPUT)
log_bug ("flush on non-output iobuf\n");
else if (!a->filter)
log_bug ("filter_flush: no filter\n");
if (a->d.len == 0 && a->e_d.buf && a->e_d.len > 0)
{
src_buf = a->e_d.buf;
src_len = a->e_d.len;
external_used = 1;
}
else
{
src_buf = a->d.buf;
src_len = a->d.len;
external_used = 0;
}
len = src_len;
rc = a->filter (a->filter_ov, IOBUFCTRL_FLUSH, a->chain, src_buf, &len);
if (!rc && len != src_len)
{
log_info ("filter_flush did not write all!\n");
rc = GPG_ERR_INTERNAL;
}
else if (rc)
a->error = rc;
a->d.len = 0;
if (external_used)
a->e_d.used = len;
return rc;
}
int
iobuf_readbyte (iobuf_t a)
{
int c;
if (a->use == IOBUF_OUTPUT || a->use == IOBUF_OUTPUT_TEMP)
{
log_bug ("iobuf_readbyte called on a non-INPUT pipeline!\n");
return -1;
}
log_assert (a->d.start <= a->d.len);
if (a->nlimit && a->nbytes >= a->nlimit)
return -1; /* forced EOF */
if (a->d.start < a->d.len)
{
c = a->d.buf[a->d.start++];
}
else if ((c = underflow (a, 1)) == -1)
return -1; /* EOF */
log_assert (a->d.start <= a->d.len);
/* Note: if underflow doesn't return EOF, then it returns the first
byte that was read and advances a->d.start appropriately. */
a->nbytes++;
return c;
}
int
iobuf_read (iobuf_t a, void *buffer, unsigned int buflen)
{
unsigned char *buf = (unsigned char *)buffer;
int c, n;
if (a->use == IOBUF_OUTPUT || a->use == IOBUF_OUTPUT_TEMP)
{
log_bug ("iobuf_read called on a non-INPUT pipeline!\n");
return -1;
}
if (a->nlimit)
{
/* Handle special cases. */
for (n = 0; n < buflen; n++)
{
if ((c = iobuf_readbyte (a)) == -1)
{
if (!n)
return -1; /* eof */
break;
}
if (buf)
{
*buf = c;
buf++;
}
}
return n;
}
a->e_d.buf = NULL;
a->e_d.len = 0;
/* Hint for how full to fill iobuf internal drain buffer. */
a->e_d.preferred = (a->use != IOBUF_INPUT_TEMP)
&& (buf && buflen >= IOBUF_ZEROCOPY_THRESHOLD_SIZE);
n = 0;
do
{
if (n < buflen && a->d.start < a->d.len)
/* Drain the buffer. */
{
unsigned size = a->d.len - a->d.start;
if (size > buflen - n)
size = buflen - n;
if (buf)
memcpy (buf, a->d.buf + a->d.start, size);
n += size;
a->d.start += size;
if (buf)
buf += size;
}
if (n < buflen)
/* Draining the internal buffer didn't fill BUFFER. Call
underflow to read more data into the filter's internal
buffer. */
{
if (a->use != IOBUF_INPUT_TEMP && buf && n < buflen)
{
/* Setup external drain buffer for faster moving of data
* (avoid memcpy). */
a->e_d.buf = buf;
a->e_d.len = (buflen - n) / IOBUF_ZEROCOPY_THRESHOLD_SIZE
* IOBUF_ZEROCOPY_THRESHOLD_SIZE;
if (a->e_d.len == 0)
a->e_d.buf = NULL;
if (a->e_d.buf && DBG_IOBUF)
log_debug ("iobuf-%d.%d: reading to external buffer, %lu bytes\n",
a->no, a->subno, (ulong)a->e_d.len);
}
if ((c = underflow (a, 1)) == -1)
/* EOF. If we managed to read something, don't return EOF
now. */
{
a->e_d.buf = NULL;
a->e_d.len = 0;
a->nbytes += n;
return n ? n : -1 /*EOF*/;
}
if (a->e_d.buf && a->e_d.used > 0)
{
/* Drain buffer was used, 'c' only contains return code
* 0 or -1. */
n += a->e_d.used;
buf += a->e_d.used;
}
else
{
if (buf)
*buf++ = c;
n++;
}
a->e_d.buf = NULL;
a->e_d.len = 0;
}
}
while (n < buflen);
a->nbytes += n;
return n;
}
int
iobuf_peek (iobuf_t a, byte * buf, unsigned buflen)
{
int n = 0;
log_assert (buflen > 0);
log_assert (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP);
if (buflen > a->d.size)
/* We can't peek more than we can buffer. */
buflen = a->d.size;
/* Try to fill the internal buffer with enough data to satisfy the
request. */
while (buflen > a->d.len - a->d.start)
{
if (underflow_target (a, 0, buflen) == -1)
/* EOF. We can't read any more. */
break;
/* Underflow consumes the first character (it's the return
value). unget() it by resetting the "file position". */
log_assert (a->d.start == 1);
a->d.start = 0;
}
n = a->d.len - a->d.start;
if (n > buflen)
n = buflen;
if (n == 0)
/* EOF. */
return -1;
memcpy (buf, &a->d.buf[a->d.start], n);
return n;
}
int
iobuf_writebyte (iobuf_t a, unsigned int c)
{
int rc;
if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP)
{
log_bug ("iobuf_writebyte called on an input pipeline!\n");
return -1;
}
if (a->d.len == a->d.size)
if ((rc=filter_flush (a)))
return rc;
log_assert (a->d.len < a->d.size);
a->d.buf[a->d.len++] = c;
return 0;
}
int
iobuf_write (iobuf_t a, const void *buffer, unsigned int buflen)
{
const unsigned char *buf = (const unsigned char *)buffer;
int rc;
if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP)
{
log_bug ("iobuf_write called on an input pipeline!\n");
return -1;
}
a->e_d.buf = NULL;
a->e_d.len = 0;
/* Hint for how full to fill iobuf internal drain buffer. */
a->e_d.preferred = (a->use != IOBUF_OUTPUT_TEMP)
&& (buflen >= IOBUF_ZEROCOPY_THRESHOLD_SIZE);
do
{
if ((a->use != IOBUF_OUTPUT_TEMP)
&& a->d.len == 0 && buflen >= IOBUF_ZEROCOPY_THRESHOLD_SIZE)
{
/* Setup external drain buffer for faster moving of data
* (avoid memcpy). */
a->e_d.buf = (byte *)buf;
a->e_d.len = buflen / IOBUF_ZEROCOPY_THRESHOLD_SIZE
* IOBUF_ZEROCOPY_THRESHOLD_SIZE;
if (a->e_d.len == 0)
a->e_d.buf = NULL;
if (a->e_d.buf && DBG_IOBUF)
log_debug ("iobuf-%d.%d: writing from external buffer, %lu bytes\n",
a->no, a->subno, (ulong)a->e_d.len);
}
if (a->e_d.buf == NULL && buflen && a->d.len < a->d.size)
{
unsigned size;
if (a->e_d.preferred && a->d.len < IOBUF_ZEROCOPY_THRESHOLD_SIZE)
size = IOBUF_ZEROCOPY_THRESHOLD_SIZE - a->d.len;
else
size = a->d.size - a->d.len;
if (size > buflen)
size = buflen;
memcpy (a->d.buf + a->d.len, buf, size);
buflen -= size;
buf += size;
a->d.len += size;
}
if (buflen)
{
rc = filter_flush (a);
if (rc)
{
a->e_d.buf = NULL;
a->e_d.len = 0;
return rc;
}
}
if (a->e_d.buf && a->e_d.used > 0)
{
buf += a->e_d.used;
buflen -= a->e_d.used;
}
a->e_d.buf = NULL;
a->e_d.len = 0;
}
while (buflen);
return 0;
}
int
iobuf_writestr (iobuf_t a, const char *buf)
{
if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP)
{
log_bug ("iobuf_writestr called on an input pipeline!\n");
return -1;
}
return iobuf_write (a, buf, strlen (buf));
}
int
iobuf_write_temp (iobuf_t dest, iobuf_t source)
{
log_assert (source->use == IOBUF_OUTPUT || source->use == IOBUF_OUTPUT_TEMP);
log_assert (dest->use == IOBUF_OUTPUT || dest->use == IOBUF_OUTPUT_TEMP);
iobuf_flush_temp (source);
return iobuf_write (dest, source->d.buf, source->d.len);
}
size_t
iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen)
{
byte desc[MAX_IOBUF_DESC];
size_t n;
while (1)
{
int rc = filter_flush (a);
if (rc)
log_bug ("Flushing iobuf %d.%d (%s) from iobuf_temp_to_buffer failed. Ignoring.\n",
a->no, a->subno, iobuf_desc (a, desc));
if (! a->chain)
break;
a = a->chain;
}
n = a->d.len;
if (n > buflen)
n = buflen;
memcpy (buffer, a->d.buf, n);
return n;
}
/* Copies the data from the input iobuf SOURCE to the output iobuf
DEST until either an error is encountered or EOF is reached.
Returns the number of bytes copies or (size_t)(-1) on error. */
size_t
iobuf_copy (iobuf_t dest, iobuf_t source)
{
char *temp;
size_t temp_size;
size_t nread;
size_t nwrote = 0;
size_t max_read = 0;
int err;
log_assert (source->use == IOBUF_INPUT || source->use == IOBUF_INPUT_TEMP);
log_assert (dest->use == IOBUF_OUTPUT || source->use == IOBUF_OUTPUT_TEMP);
if (iobuf_error (dest))
return (size_t)(-1);
/* Use iobuf buffer size for temporary buffer. */
temp_size = iobuf_set_buffer_size(0) * 1024;
temp = xmalloc (temp_size);
while (1)
{
nread = iobuf_read (source, temp, temp_size);
if (nread == -1)
/* EOF. */
break;
if (nread > max_read)
max_read = nread;
err = iobuf_write (dest, temp, nread);
if (err)
break;
nwrote += nread;
}
/* Burn the buffer. */
if (max_read)
wipememory (temp, max_read);
xfree (temp);
return nwrote;
}
void
iobuf_flush_temp (iobuf_t temp)
{
if (temp->use == IOBUF_INPUT || temp->use == IOBUF_INPUT_TEMP)
log_bug ("iobuf_flush_temp called on an input pipeline!\n");
while (temp->chain)
iobuf_pop_filter (temp, temp->filter, NULL);
}
void
iobuf_set_limit (iobuf_t a, off_t nlimit)
{
if (nlimit)
a->nofast = 1;
else
a->nofast = 0;
a->nlimit = nlimit;
a->ntotal += a->nbytes;
a->nbytes = 0;
}
-
-off_t
-iobuf_get_filelength (iobuf_t a, int *overflow)
+/* Return the length of the file behind A. If there is no file, return 0. */
+uint64_t
+iobuf_get_filelength (iobuf_t a)
{
- if (overflow)
- *overflow = 0;
-
/* Hmmm: file_filter may have already been removed */
for ( ; a->chain; a = a->chain )
;
if (a->filter != file_filter)
return 0;
{
file_filter_ctx_t *b = a->filter_ov;
gnupg_fd_t fp = b->fp;
#if defined(HAVE_W32_SYSTEM)
- ulong size;
- static int (* __stdcall get_file_size_ex) (void *handle,
- LARGE_INTEGER *r_size);
- static int get_file_size_ex_initialized;
-
- if (!get_file_size_ex_initialized)
- {
- void *handle;
-
- handle = dlopen ("kernel32.dll", RTLD_LAZY);
- if (handle)
- {
- get_file_size_ex = dlsym (handle, "GetFileSizeEx");
- if (!get_file_size_ex)
- dlclose (handle);
- }
- get_file_size_ex_initialized = 1;
- }
-
- if (get_file_size_ex)
- {
- /* This is a newer system with GetFileSizeEx; we use this
- then because it seem that GetFileSize won't return a
- proper error in case a file is larger than 4GB. */
- LARGE_INTEGER exsize;
+ LARGE_INTEGER exsize;
- if (get_file_size_ex (fp, &exsize))
- {
- if (!exsize.u.HighPart)
- return exsize.u.LowPart;
- if (overflow)
- *overflow = 1;
- return 0;
- }
- }
- else
- {
- if ((size=GetFileSize (fp, NULL)) != 0xffffffff)
- return size;
- }
+ if (GetFileSizeEx (fp, &exsize))
+ return exsize.QuadPart;
log_error ("GetFileSize for handle %p failed: %s\n",
fp, w32_strerror (-1));
#else /*!HAVE_W32_SYSTEM*/
- {
- struct stat st;
+ struct stat st;
- if ( !fstat (fp, &st) )
- return st.st_size;
- log_error("fstat() failed: %s\n", strerror(errno) );
- }
+ if ( !fstat (fp, &st) )
+ return st.st_size;
+ log_error("fstat() failed: %s\n", strerror(errno) );
#endif /*!HAVE_W32_SYSTEM*/
}
return 0;
}
int
iobuf_get_fd (iobuf_t a)
{
for (; a->chain; a = a->chain)
;
if (a->filter != file_filter)
return -1;
{
file_filter_ctx_t *b = a->filter_ov;
gnupg_fd_t fp = b->fp;
return FD2INT (fp);
}
}
off_t
iobuf_tell (iobuf_t a)
{
return a->ntotal + a->nbytes;
}
#if !defined(HAVE_FSEEKO) && !defined(fseeko)
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif
#ifndef LONG_MAX
# define LONG_MAX ((long) ((unsigned long) -1 >> 1))
#endif
#ifndef LONG_MIN
# define LONG_MIN (-1 - LONG_MAX)
#endif
/****************
* A substitute for fseeko, for hosts that don't have it.
*/
static int
fseeko (FILE * stream, off_t newpos, int whence)
{
while (newpos != (long) newpos)
{
long pos = newpos < 0 ? LONG_MIN : LONG_MAX;
if (fseek (stream, pos, whence) != 0)
return -1;
newpos -= pos;
whence = SEEK_CUR;
}
return fseek (stream, (long) newpos, whence);
}
#endif
int
iobuf_seek (iobuf_t a, off_t newpos)
{
file_filter_ctx_t *b = NULL;
if (a->use == IOBUF_OUTPUT || a->use == IOBUF_INPUT)
{
/* Find the last filter in the pipeline. */
for (; a->chain; a = a->chain)
;
if (a->filter != file_filter)
return -1;
b = a->filter_ov;
#ifdef HAVE_W32_SYSTEM
if (SetFilePointer (b->fp, newpos, NULL, FILE_BEGIN) == 0xffffffff)
{
log_error ("SetFilePointer failed on handle %p: ec=%d\n",
b->fp, (int) GetLastError ());
return -1;
}
#else
if (lseek (b->fp, newpos, SEEK_SET) == (off_t) - 1)
{
log_error ("can't lseek: %s\n", strerror (errno));
return -1;
}
#endif
/* Discard the buffer it is not a temp stream. */
a->d.len = 0;
}
a->d.start = 0;
a->nbytes = 0;
a->nlimit = 0;
a->nofast = 0;
a->ntotal = newpos;
a->error = 0;
/* It is impossible for A->CHAIN to be non-NULL. If A is an INPUT
or OUTPUT buffer, then we find the last filter, which is defined
as A->CHAIN being NULL. If A is a TEMP filter, then A must be
the only filter in the pipe: when iobuf_push_filter adds a filter
to the front of a pipeline, it sets the new filter to be an
OUTPUT filter if the pipeline is an OUTPUT or TEMP pipeline and
to be an INPUT filter if the pipeline is an INPUT pipeline.
Thus, only the last filter in a TEMP pipeline can be a */
/* remove filters, but the last */
if (a->chain)
log_debug ("iobuf_pop_filter called in iobuf_seek - please report\n");
while (a->chain)
iobuf_pop_filter (a, a->filter, NULL);
return 0;
}
const char *
iobuf_get_real_fname (iobuf_t a)
{
if (a->real_fname)
return a->real_fname;
/* the old solution */
for (; a; a = a->chain)
if (!a->chain && a->filter == file_filter)
{
file_filter_ctx_t *b = a->filter_ov;
return b->print_only_name ? NULL : b->fname;
}
return NULL;
}
const char *
iobuf_get_fname (iobuf_t a)
{
for (; a; a = a->chain)
if (!a->chain && a->filter == file_filter)
{
file_filter_ctx_t *b = a->filter_ov;
return b->fname;
}
return NULL;
}
const char *
iobuf_get_fname_nonnull (iobuf_t a)
{
const char *fname;
fname = iobuf_get_fname (a);
return fname? fname : "[?]";
}
/****************
* Enable or disable partial body length mode (RFC 4880 4.2.2.4).
*
* If LEN is 0, this disables partial block mode by popping the
* partial body length filter, which must be the most recently
* added filter.
*
* If LEN is non-zero, it pushes a partial body length filter. If
* this is a read filter, LEN must be the length byte from the first
* chunk and A should be position just after this first partial body
* length header.
*/
void
iobuf_set_partial_body_length_mode (iobuf_t a, size_t len)
{
if (!len)
/* Disable partial body length mode. */
{
if (a->use == IOBUF_INPUT)
log_debug ("iobuf_pop_filter called in set_partial_block_mode"
" - please report\n");
log_assert (a->filter == block_filter);
iobuf_pop_filter (a, block_filter, NULL);
}
else
/* Enabled partial body length mode. */
{
block_filter_ctx_t *ctx = xcalloc (1, sizeof *ctx);
ctx->use = a->use;
ctx->partial = 1;
ctx->size = 0;
ctx->first_c = len;
iobuf_push_filter (a, block_filter, ctx);
}
}
unsigned int
iobuf_read_line (iobuf_t a, byte ** addr_of_buffer,
unsigned *length_of_buffer, unsigned *max_length)
{
int c;
char *buffer = (char *)*addr_of_buffer;
unsigned length = *length_of_buffer;
unsigned nbytes = 0;
unsigned maxlen = *max_length;
char *p;
/* The code assumes that we have space for at least a newline and a
NUL character in the buffer. This requires at least 2 bytes. We
don't complicate the code by handling the stupid corner case, but
simply assert that it can't happen. */
log_assert (!buffer || length >= 2 || maxlen >= 2);
if (!buffer || length <= 1)
/* must allocate a new buffer */
{
length = 256 <= maxlen ? 256 : maxlen;
buffer = xrealloc (buffer, length);
*addr_of_buffer = (unsigned char *)buffer;
*length_of_buffer = length;
}
p = buffer;
while (1)
{
if (!a->nofast && a->d.start < a->d.len && nbytes < length - 1)
/* Fast path for finding '\n' by using standard C library's optimized
memchr. */
{
unsigned size = a->d.len - a->d.start;
byte *newline_pos;
if (size > length - 1 - nbytes)
size = length - 1 - nbytes;
newline_pos = memchr (a->d.buf + a->d.start, '\n', size);
if (newline_pos)
{
/* Found newline, copy buffer and return. */
size = (newline_pos - (a->d.buf + a->d.start)) + 1;
memcpy (p, a->d.buf + a->d.start, size);
p += size;
nbytes += size;
a->d.start += size;
a->nbytes += size;
break;
}
else
{
/* No newline, copy buffer and continue. */
memcpy (p, a->d.buf + a->d.start, size);
p += size;
nbytes += size;
a->d.start += size;
a->nbytes += size;
}
}
else
{
c = iobuf_readbyte (a);
if (c == -1)
break;
*p++ = c;
nbytes++;
if (c == '\n')
break;
}
if (nbytes == length - 1)
/* We don't have enough space to add a \n and a \0. Increase
the buffer size. */
{
if (length == maxlen)
/* We reached the buffer's size limit! */
{
/* Skip the rest of the line. */
while ((c = iobuf_get (a)) != -1 && c != '\n')
;
/* p is pointing at the last byte in the buffer. We
always terminate the line with "\n\0" so overwrite
the previous byte with a \n. */
log_assert (p > buffer);
p[-1] = '\n';
/* Indicate truncation. */
*max_length = 0;
break;
}
length += length < 1024 ? 256 : 1024;
if (length > maxlen)
length = maxlen;
buffer = xrealloc (buffer, length);
*addr_of_buffer = (unsigned char *)buffer;
*length_of_buffer = length;
p = buffer + nbytes;
}
}
/* Add the terminating NUL. */
*p = 0;
/* Return the number of characters written to the buffer including
the newline, but not including the terminating NUL. */
return nbytes;
}
static int
translate_file_handle (int fd, int for_write)
{
#if defined(HAVE_W32_SYSTEM)
{
int x;
(void)for_write;
if (fd == 0)
x = (int) GetStdHandle (STD_INPUT_HANDLE);
else if (fd == 1)
x = (int) GetStdHandle (STD_OUTPUT_HANDLE);
else if (fd == 2)
x = (int) GetStdHandle (STD_ERROR_HANDLE);
else
x = fd;
if (x == -1)
log_debug ("GetStdHandle(%d) failed: ec=%d\n",
fd, (int) GetLastError ());
fd = x;
}
#else
(void)for_write;
#endif
return fd;
}
void
iobuf_skip_rest (iobuf_t a, unsigned long n, int partial)
{
if ( partial )
{
for (;;)
{
if (a->nofast || a->d.start >= a->d.len)
{
if (iobuf_readbyte (a) == -1)
{
break;
}
}
else
{
unsigned long count = a->d.len - a->d.start;
a->nbytes += count;
a->d.start = a->d.len;
}
}
}
else
{
unsigned long remaining = n;
while (remaining > 0)
{
if (a->nofast || a->d.start >= a->d.len)
{
if (iobuf_readbyte (a) == -1)
{
break;
}
--remaining;
}
else
{
unsigned long count = a->d.len - a->d.start;
if (count > remaining)
{
count = remaining;
}
a->nbytes += count;
a->d.start += count;
remaining -= count;
}
}
}
}
diff --git a/common/iobuf.h b/common/iobuf.h
index c132c2f3c..751ae73c3 100644
--- a/common/iobuf.h
+++ b/common/iobuf.h
@@ -1,645 +1,641 @@
/* iobuf.h - I/O buffer
* Copyright (C) 1998, 1999, 2000, 2001, 2003,
* 2010 Free Software Foundation, Inc.
*
* 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/>.
*/
#ifndef GNUPG_COMMON_IOBUF_H
#define GNUPG_COMMON_IOBUF_H
/* An iobuf is basically a filter in a pipeline.
Consider the following command, which consists of three filters
that are chained together:
$ cat file | base64 --decode | gunzip
The first filter reads the file from the file system and sends that
data to the second filter. The second filter decodes
base64-encoded data and sends the data to the third and last
filter. The last filter decompresses the data and the result is
displayed on the terminal. The iobuf system works in the same way
where each iobuf is a filter and the individual iobufs can be
chained together.
There are number of predefined filters. iobuf_open(), for
instance, creates a filter that reads from a specified file. And,
iobuf_temp_with_content() creates a filter that returns some
specified contents. There are also filters for writing content.
iobuf_openrw opens a file for writing. iobuf_temp creates a filter
that writes data to a fixed-sized buffer.
To chain filters together, you use the iobuf_push_filter()
function. The filters are chained together using the chain field
in the iobuf_t.
A pipeline can only be used for reading (IOBUF_INPUT) or for
writing (IOBUF_OUTPUT / IOBUF_OUTPUT_TEMP). When reading, data
flows from the last filter towards the first. That is, the user
calls iobuf_read(), the module reads from the first filter, which
gets its input from the second filter, etc. When writing, data
flows from the first filter towards the last. In this case, when
the user calls iobuf_write(), the data is written to the first
filter, which writes the transformed data to the second filter,
etc.
An iobuf_t contains some state about the filter. For instance, it
indicates if the filter has already returned EOF (filter_eof) and
the next filter in the pipeline, if any (chain). It also contains
a function pointer, filter. This is a generic function. It is
called when input is needed or output is available. In this case
it is passed a pointer to some filter-specific persistent state
(filter_ov), the actual operation, the next filter in the chain, if
any, and a buffer that either contains the contents to write, if
the pipeline is setup to write data, or is the place to store data,
if the pipeline is setup to read data.
Unlike a Unix pipeline, an IOBUF pipeline can return EOF multiple
times. This is similar to the following:
{ cat file1; cat file2; } | grep foo
However, instead of grep seeing a single stream, grep would see
each byte stream followed by an EOF marker. (When a filter returns
EOF, the EOF is returned to the user exactly once and then the
filter is removed from the pipeline.) */
/* For estream_t. */
#include <gpg-error.h>
#include "../common/types.h"
#include "../common/sysutils.h"
#define DBG_IOBUF iobuf_debug_mode
/* Filter control modes. */
enum
{
IOBUFCTRL_INIT = 1,
IOBUFCTRL_FREE = 2,
IOBUFCTRL_UNDERFLOW = 3,
IOBUFCTRL_FLUSH = 4,
IOBUFCTRL_DESC = 5,
IOBUFCTRL_CANCEL = 6,
IOBUFCTRL_PEEK = 7,
IOBUFCTRL_USER = 16
};
/* Command codes for iobuf_ioctl. */
typedef enum
{
IOBUF_IOCTL_KEEP_OPEN = 1, /* Uses intval. */
IOBUF_IOCTL_INVALIDATE_CACHE = 2, /* Uses ptrval. */
IOBUF_IOCTL_NO_CACHE = 3, /* Uses intval. */
IOBUF_IOCTL_FSYNC = 4, /* Uses ptrval. */
IOBUF_IOCTL_PEEK = 5 /* Uses intval and ptrval. */
} iobuf_ioctl_t;
enum iobuf_use
{
/* Pipeline is in input mode. The data flows from the end to the
beginning. That is, when reading from the pipeline, the first
filter gets its input from the second filter, etc. */
IOBUF_INPUT,
/* Pipeline is in input mode. The last filter in the pipeline is
a temporary buffer from which the data is "read". */
IOBUF_INPUT_TEMP,
/* Pipeline is in output mode. The data flows from the beginning
to the end. That is, when writing to the pipeline, the user
writes to the first filter, which transforms the data and sends
it to the second filter, etc. */
IOBUF_OUTPUT,
/* Pipeline is in output mode. The last filter in the pipeline is
a temporary buffer that grows as necessary. */
IOBUF_OUTPUT_TEMP
};
typedef struct iobuf_struct *iobuf_t;
typedef struct iobuf_struct *IOBUF; /* Compatibility with gpg 1.4. */
/* fixme: we should hide most of this stuff */
struct iobuf_struct
{
/* The type of filter. Either IOBUF_INPUT, IOBUF_OUTPUT or
IOBUF_OUTPUT_TEMP. */
enum iobuf_use use;
/* nlimit can be changed using iobuf_set_limit. If non-zero, it is
the number of additional bytes that can be read from the filter
before EOF is forcefully returned. */
off_t nlimit;
/* nbytes if the number of bytes that have been read (using
iobuf_get / iobuf_readbyte / iobuf_read) since the last call to
iobuf_set_limit. */
off_t nbytes;
/* The number of bytes read prior to the last call to
iobuf_set_limit. Thus, the total bytes read (i.e., the position
of stream) is ntotal + nbytes. */
off_t ntotal;
/* Whether we need to read from the filter one byte at a time or
whether we can do bulk reads. We need to read one byte at a time
if a limit (set via iobuf_set_limit) is active. */
int nofast;
/* A buffer for unread/unwritten data.
For an output pipeline (IOBUF_OUTPUT), this is the data that has
not yet been written to the filter. Consider a simple pipeline
consisting of a single stage, which writes to a file. When you
write to the pipeline (iobuf_writebyte or iobuf_write), the data
is first stored in this buffer. Only when the buffer is full or
you call iobuf_flush() is FILTER actually called and the data
written to the file.
For an input pipeline (IOBUF_INPUT), this is the data that has
been read from this filter, but not yet been read from the
preceding filter (or the user, if this filter is the head of the
pipeline). Again, consider a simple pipeline consisting of a
single stage. This stage reads from a file. If you read a
single byte (iobuf_get) and the buffer is empty, then FILTER is
called to fill the buffer. In this case, a single byte is not
requested, but the whole buffer is filled (if possible). */
struct
{
/* Size of the buffer. */
size_t size;
/* Number of bytes at the beginning of the buffer that have
already been consumed. (In other words: the index of the first
byte that hasn't been consumed.) This is only non-zero for
input filters. */
size_t start;
/* The number of bytes in the buffer including any bytes that have
been consumed. */
size_t len;
/* The buffer itself. */
byte *buf;
} d;
/* A external drain buffer for reading/writting data skipping internal
draint buffer D.BUF. This allows zerocopy operation reducing
processing overhead across filter stack.
Used when by iobuf_read/iobuf_write when internal buffer has been
depleted and remaining external buffer length is large enough.
*/
struct
{
/* The external buffer provided by iobuf_read/iobuf_write caller. */
byte *buf;
/* The number of bytes in the external buffer. */
size_t len;
/* The number of bytes that were consumed from the external buffer. */
size_t used;
/* Gives hint for processing that the external buffer is preferred and
that internal buffer should be consumed early. */
int preferred;
} e_d;
/* When FILTER is called to read some data, it may read some data
and then return EOF. We can't return the EOF immediately.
Instead, we note that we observed the EOF and when the buffer is
finally empty, we return the EOF. */
int filter_eof;
/* Like filter_eof, when FILTER is called to read some data, it may
read some data and then return an error. We can't return the
error (in the form of an EOF) immediately. Instead, we note that
we observed the error and when the buffer is finally empty, we
return the EOF. */
int error;
/* The callback function to read data from the filter, etc. See
iobuf_filter_push for details. */
int (*filter) (void *opaque, int control,
iobuf_t chain, byte * buf, size_t * len);
/* An opaque pointer that can be used for local filter state. This
is passed as the first parameter to FILTER. */
void *filter_ov;
/* Whether the iobuf code should free(filter_ov) when destroying the
filter. */
int filter_ov_owner;
/* When using iobuf_open, iobuf_create, iobuf_openrw to open a file,
the file's name is saved here. This is used to delete the file
when an output pipeline (IOBUF_OUPUT) is canceled
(iobuf_cancel). */
char *real_fname;
/* The next filter in the pipeline. */
iobuf_t chain;
/* This field is for debugging. Each time a filter is allocated
(via iobuf_alloc()), a monotonically increasing counter is
incremented and this field is set to the new value. This field
should only be accessed via the iobuf_io macro. */
int no;
/* The number of filters in the pipeline following (not including)
this one. When you call iobuf_push_filter or iobuf_push_filter2,
this value is used to check the length of the pipeline if the
pipeline already contains 65 stages then these functions fail.
This amount of nesting typically indicates corrupted data or an
active denial of service attack. */
int subno;
};
extern int iobuf_debug_mode;
/* Change the default size for all IOBUFs to KILOBYTE. This needs to
* be called before any iobufs are used and can only be used once.
* Returns the current value. Using 0 has no effect except for
* returning the current value. */
unsigned int iobuf_set_buffer_size (unsigned int kilobyte);
/* Returns whether the specified filename corresponds to a pipe. In
particular, this function checks if FNAME is "-" and, if special
filenames are enabled (see check_special_filename), whether
FNAME is a special filename. */
int iobuf_is_pipe_filename (const char *fname);
/* Allocate a new filter. This filter doesn't have a function
assigned to it. Thus you need to manually set IOBUF->FILTER and
IOBUF->FILTER_OV, if required. This function is intended to help
create a new primary source or primary sink, i.e., the last filter
in the pipeline.
USE is IOBUF_INPUT, IOBUF_INPUT_TEMP, IOBUF_OUTPUT or
IOBUF_OUTPUT_TEMP.
BUFSIZE is the desired internal buffer size (that is, the size of
the typical read / write request). */
iobuf_t iobuf_alloc (int use, size_t bufsize);
/* Create an output filter that simply buffers data written to it.
This is useful for collecting data for later processing. The
buffer can be written to in the usual way (iobuf_write, etc.). The
data can later be extracted using iobuf_write_temp() or
iobuf_temp_to_buffer(). */
iobuf_t iobuf_temp (void);
/* Create an input filter that contains some data for reading. */
iobuf_t iobuf_temp_with_content (const char *buffer, size_t length);
/* Create an input file filter that reads from a file. If FNAME is
'-', reads from stdin. If special filenames are enabled
(iobuf_enable_special_filenames), then interprets special
filenames. */
iobuf_t iobuf_open (const char *fname);
/* Create an output file filter that writes to a file. If FNAME is
NULL or '-', writes to stdout. If special filenames are enabled
(iobuf_enable_special_filenames), then interprets special
filenames. If FNAME is not NULL, '-' or a special filename, the
file is opened for writing. If the file exists, it is truncated.
If MODE700 is TRUE, the file is created with mode 600. Otherwise,
mode 666 is used. */
iobuf_t iobuf_create (const char *fname, int mode700);
/* Create an output file filter that writes to a specified file.
Neither '-' nor special file names are recognized. */
iobuf_t iobuf_openrw (const char *fname);
/* Create a file filter using an existing file descriptor. If MODE
contains the letter 'w', creates an output filter. Otherwise,
creates an input filter. Note: MODE must reflect the file
descriptors actual mode! When the filter is destroyed, the file
descriptor is closed. */
iobuf_t iobuf_fdopen (int fd, const char *mode);
/* Like iobuf_fdopen, but doesn't close the file descriptor when the
filter is destroyed. */
iobuf_t iobuf_fdopen_nc (int fd, const char *mode);
/* Create a filter using an existing estream. If MODE contains the
letter 'w', creates an output filter. Otherwise, creates an input
filter. If KEEP_OPEN is TRUE, then the stream is not closed when
the filter is destroyed. Otherwise, the stream is closed when the
filter is destroyed. If READLIMIT is not 0 this gives a limit on
the number of bytes to read from estream. */
iobuf_t iobuf_esopen (estream_t estream, const char *mode, int keep_open,
size_t readlimit);
/* Create a filter using an existing socket. On Windows creates a
special socket filter. On non-Windows systems simply, this simply
calls iobuf_fdopen. */
iobuf_t iobuf_sockopen (int fd, const char *mode);
/* Set various options / perform different actions on a PIPELINE. See
the IOBUF_IOCTL_* macros above. */
int iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval);
/* Close a pipeline. The filters in the pipeline are first flushed
using iobuf_flush, if they are output filters, and then
IOBUFCTRL_FREE is called on each filter.
If any filter returns a non-zero value in response to the
IOBUFCTRL_FREE, that first such non-zero value is returned. Note:
processing is not aborted in this case. If all filters are freed
successfully, 0 is returned. */
int iobuf_close (iobuf_t iobuf);
/* Calls IOBUFCTRL_CANCEL on each filter in the pipeline. Then calls
io_close() on the pipeline. Finally, if the pipeline is an output
pipeline, deletes the file. Returns the result of calling
iobuf_close on the pipeline. */
int iobuf_cancel (iobuf_t iobuf);
/* Add a new filter to the front of a pipeline. A is the head of the
pipeline. F is the filter implementation. OV is an opaque pointer
that is passed to F and is normally used to hold any internal
state, such as a file pointer.
Note: you may only maintain a reference to an iobuf_t as a
reference to the head of the pipeline. That is, don't think about
setting a pointer in OV to point to the filter's iobuf_t. This is
because when we add a new filter to a pipeline, we memcpy the state
in A into new buffer. This has the advantage that there is no need
to update any references to the pipeline when a filter is added or
removed, but it also means that a filter's state moves around in
memory.
The behavior of the filter function is determined by the value of
the control parameter:
IOBUFCTRL_INIT: Called this value just before the filter is
linked into the pipeline. This can be used to initialize
internal data structures.
IOBUFCTRL_FREE: Called with this value just before the filter is
removed from the pipeline. Normally used to release internal
data structures, close a file handle, etc.
IOBUFCTRL_UNDERFLOW: Called with this value to fill the passed
buffer with more data. *LEN is the size of the buffer. Before
returning, it should be set to the number of bytes which were
written into the buffer. The function must return 0 to
indicate success, -1 on EOF and a GPG_ERR_xxxxx code for any
error.
Note: this function may both return data and indicate an error
or EOF. In this case, it simply writes the data to BUF, sets
*LEN and returns the appropriate return code. The implication
is that if an error occurs and no data has yet been written, it
is essential that *LEN be set to 0!
IOBUFCTRL_FLUSH: Called with this value to write out any
collected data. *LEN is the number of bytes in BUF that need
to be written out. Returns 0 on success and a GPG_ERR_* code
otherwise. *LEN must be set to the number of bytes that were
written out.
IOBUFCTRL_CANCEL: Called with this value when iobuf_cancel() is
called on the pipeline.
IOBUFCTRL_DESC: Called with this value to get a human-readable
description of the filter. *LEN is the size of the buffer.
The description is filled into BUF, NUL-terminated. Always
returns 0.
*/
int iobuf_push_filter (iobuf_t a, int (*f) (void *opaque, int control,
iobuf_t chain, byte * buf,
size_t * len), void *ov);
/* This variant of iobuf_push_filter allows the called to indicate
that OV should be freed when this filter is freed. That is, if
REL_OV is TRUE, then when the filter is popped or freed OV will be
freed after the filter function is called with control set to
IOBUFCTRL_FREE. */
int iobuf_push_filter2 (iobuf_t a,
int (*f) (void *opaque, int control, iobuf_t chain,
byte * buf, size_t * len), void *ov,
int rel_ov);
/* Pop the top filter. The top filter must have the filter function F
and the cookie OV. The cookie check is ignored if OV is NULL. */
int iobuf_pop_filter (iobuf_t a,
int (*f) (void *opaque, int control,
iobuf_t chain, byte * buf, size_t * len),
void *ov);
/* Used for debugging. Prints out the chain using log_debug if
IOBUF_DEBUG_MODE is not 0. */
int iobuf_print_chain (iobuf_t a);
/* Indicate that some error occurred on the specified filter. */
#define iobuf_set_error(a) do { (a)->error = 1; } while(0)
/* Return any pending error on filter A. */
#define iobuf_error(a) ((a)->error)
/* Limit the amount of additional data that may be read from the
filter. That is, if you've already read 100 bytes from A and you
set the limit to 50, then you can read up to an additional 50 bytes
(i.e., a total of 150 bytes) before EOF is forcefully returned.
Setting NLIMIT to 0 removes any active limit.
Note: using iobuf_seek removes any currently enforced limit! */
void iobuf_set_limit (iobuf_t a, off_t nlimit);
/* Returns the number of bytes that have been read from the pipeline.
Note: the result is undefined for IOBUF_OUTPUT and IOBUF_OUTPUT_TEMP
pipelines! */
off_t iobuf_tell (iobuf_t a);
/* There are two cases:
- If A is an INPUT or OUTPUT pipeline, then the last filter in the
pipeline is found. If that is not a file filter, -1 is returned.
Otherwise, an fseek(..., SEEK_SET) is performed on the file
descriptor.
- If A is a TEMP pipeline and the *first* (and thus only filter) is
a TEMP filter, then the "file position" is effectively unchanged.
That is, data is appended to the buffer and the seek does not
cause the size of the buffer to grow.
If no error occurred, then any limit previous set by
iobuf_set_limit() is cleared. Further, any error on the filter
(the file filter or the temp filter) is cleared.
Returns 0 on success and -1 if an error occurs. */
int iobuf_seek (iobuf_t a, off_t newpos);
/* Read a single byte. If a filter has no more data, returns -1 to
indicate the EOF. Generally, you don't want to use this function,
but instead prefer the iobuf_get macro, which is faster if there is
data in the internal buffer. */
int iobuf_readbyte (iobuf_t a);
/* Get a byte from the iobuf; must check for eof prior to this
function. This function returns values in the range 0 .. 255 or -1
to indicate EOF. iobuf_get_noeof() does not return -1 to indicate
EOF, but masks the returned value to be in the range 0 .. 255. */
#define iobuf_get(a) \
( ((a)->nofast || (a)->d.start >= (a)->d.len )? \
iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) )
#define iobuf_get_noeof(a) (iobuf_get((a))&0xff)
/* Fill BUF with up to BUFLEN bytes. If a filter has no more data,
returns -1 to indicate the EOF. Otherwise returns the number of
bytes read. */
int iobuf_read (iobuf_t a, void *buf, unsigned buflen);
/* Read a line of input (including the '\n') from the pipeline.
The semantics are the same as for fgets(), but if the buffer is too
short a larger one will be allocated up to *MAX_LENGTH and the end
of the line except the trailing '\n' discarded. (Thus,
*ADDR_OF_BUFFER must be allocated using malloc().) If the buffer
is enlarged, then *LENGTH_OF_BUFFER will be updated to reflect the
new size. If the line is truncated, then *MAX_LENGTH will be set
to 0. If *ADDR_OF_BUFFER is NULL, a buffer is allocated using
malloc().
A line is considered a byte stream ending in a '\n'. Returns the
number of characters written to the buffer (i.e., excluding any
discarded characters due to truncation). Thus, use this instead of
strlen(buffer) to determine the length of the string as this is
unreliable if the input contains NUL characters.
EOF is indicated by a line of length zero.
The last LF may be missing due to an EOF. */
unsigned iobuf_read_line (iobuf_t a, byte ** addr_of_buffer,
unsigned *length_of_buffer, unsigned *max_length);
/* Read up to BUFLEN bytes from pipeline A. Note: this function can't
return more than the pipeline's internal buffer size. The return
value is the number of bytes actually written to BUF. If the
filter returns EOF, then this function returns -1.
This function does not clear any pending EOF. That is, if the
pipeline consists of two filters and the first one returns EOF
during the peek, then the subsequent iobuf_read* will still return
EOF before returning the data from the second filter. */
int iobuf_peek (iobuf_t a, byte * buf, unsigned buflen);
/* Write a byte to the pipeline. Returns 0 on success and an error
code otherwise. */
int iobuf_writebyte (iobuf_t a, unsigned c);
/* Alias for iobuf_writebyte. */
#define iobuf_put(a,c) iobuf_writebyte(a,c)
/* Write a sequence of bytes to the pipeline. Returns 0 on success
and an error code otherwise. */
int iobuf_write (iobuf_t a, const void *buf, unsigned buflen);
/* Write a string (not including the NUL terminator) to the pipeline.
Returns 0 on success and an error code otherwise. */
int iobuf_writestr (iobuf_t a, const char *buf);
/* Flushes the pipeline removing all filters but the sink (the last
filter) in the process. */
void iobuf_flush_temp (iobuf_t temp);
/* Flushes the pipeline SOURCE removing all filters but the sink (the
last filter) in the process (i.e., it calls
iobuf_flush_temp(source)) and then writes the data to the pipeline
DEST. Note: this doesn't free (iobuf_close()) SOURCE. Both SOURCE
and DEST must be output pipelines. */
int iobuf_write_temp (iobuf_t dest, iobuf_t source);
/* Flushes each filter in the pipeline (i.e., sends any buffered data
to the filter by calling IOBUFCTRL_FLUSH). Then, copies up to the
first BUFLEN bytes from the last filter's internal buffer (which
will only be non-empty if it is a temp filter) to the buffer
BUFFER. Returns the number of bytes actually copied. */
size_t iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen);
/* Copies the data from the input iobuf SOURCE to the output iobuf
DEST until either an error is encountered or EOF is reached.
Returns the number of bytes successfully written. If an error
occurred, then any buffered bytes are not returned to SOURCE and are
effectively lost. To check if an error occurred, use
iobuf_error. */
size_t iobuf_copy (iobuf_t dest, iobuf_t source);
/* Return the size of any underlying file. This only works with
- file_filter based pipelines.
-
- On Win32, it is sometimes not possible to determine the size of
- files larger than 4GB. In this case, *OVERFLOW (if not NULL) is
- set to 1. Otherwise, *OVERFLOW is set to 0. */
-off_t iobuf_get_filelength (iobuf_t a, int *overflow);
+ file_filter based pipelines. */
+uint64_t iobuf_get_filelength (iobuf_t a);
#define IOBUF_FILELENGTH_LIMIT 0xffffffff
/* Return the file descriptor designating the underlying file. This
only works with file_filter based pipelines. */
int iobuf_get_fd (iobuf_t a);
/* Return the real filename, if available. This only supports
pipelines that end in file filters. Returns NULL if not
available. */
const char *iobuf_get_real_fname (iobuf_t a);
/* Return the filename or a description thereof. For instance, for
iobuf_open("-"), this will return "[stdin]". This only supports
pipelines that end in file filters. Returns NULL if not
available. */
const char *iobuf_get_fname (iobuf_t a);
/* Like iobuf_getfname, but instead of returning NULL if no
description is available, return "[?]". */
const char *iobuf_get_fname_nonnull (iobuf_t a);
/* Pushes a filter on the pipeline that interprets the datastream as
an OpenPGP data block whose length is encoded using partial body
length headers (see Section 4.2.2.4 of RFC 4880). Concretely, it
just returns / writes the data and finishes the packet with an
EOF. */
void iobuf_set_partial_body_length_mode (iobuf_t a, size_t len);
/* If PARTIAL is set, then read from the pipeline until the first EOF
is returned.
If PARTIAL is 0, then read up to N bytes or until the first EOF is
returned.
Recall: a filter can return EOF. In this case, it and all
preceding filters are popped from the pipeline and the next read is
from the following filter (which may or may not return EOF). */
void iobuf_skip_rest (iobuf_t a, unsigned long n, int partial);
#define iobuf_where(a) "[don't know]"
/* Each time a filter is allocated (via iobuf_alloc()), a
monotonically increasing counter is incremented and this field is
set to the new value. This macro returns that number. */
#define iobuf_id(a) ((a)->no)
#define iobuf_get_temp_buffer(a) ( (a)->d.buf )
#define iobuf_get_temp_length(a) ( (a)->d.len )
/* Whether the filter uses an in-memory buffer. */
#define iobuf_is_temp(a) ( (a)->use == IOBUF_OUTPUT_TEMP )
#endif /*GNUPG_COMMON_IOBUF_H*/
diff --git a/g10/encrypt.c b/g10/encrypt.c
index ff1c6be85..9aeafa292 100644
--- a/g10/encrypt.c
+++ b/g10/encrypt.c
@@ -1,1272 +1,1271 @@
/* encrypt.c - Main encryption driver
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
* 2006, 2009 Free Software Foundation, Inc.
* Copyright (C) 2016, 2023 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG 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: GPL-3.0-or-later
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "gpg.h"
#include "options.h"
#include "packet.h"
#include "../common/status.h"
#include "../common/iobuf.h"
#include "keydb.h"
#include "../common/util.h"
#include "main.h"
#include "filter.h"
#include "trustdb.h"
#include "../common/i18n.h"
#include "../common/status.h"
#include "pkglue.h"
#include "../common/compliance.h"
static int encrypt_simple( const char *filename, int mode, int use_seskey );
static int write_pubkey_enc_from_list (ctrl_t ctrl,
PK_LIST pk_list, DEK *dek, iobuf_t out);
/****************
* Encrypt FILENAME with only the symmetric cipher. Take input from
* stdin if FILENAME is NULL. If --force-aead is used we use an SKESK.
*/
int
encrypt_symmetric (const char *filename)
{
return encrypt_simple( filename, 1, opt.force_aead);
}
/****************
* Encrypt FILENAME as a literal data packet only. Take input from
* stdin if FILENAME is NULL.
*/
int
encrypt_store (const char *filename)
{
return encrypt_simple( filename, 0, 0 );
}
/* Create and setup a DEK structure and print approriate warnings.
* PK_LIST gives the list of public keys. Always returns a DEK. The
* actual session needs to be added later. */
static DEK *
create_dek_with_warnings (pk_list_t pk_list)
{
DEK *dek;
dek = xmalloc_secure_clear (sizeof *dek);
if (!opt.def_cipher_algo)
{
/* Try to get it from the prefs. */
dek->algo = select_algo_from_prefs (pk_list, PREFTYPE_SYM, -1, NULL);
if (dek->algo == -1)
{
/* If does not make sense to fallback to the rfc4880
* required 3DES if we will reject that algo later. Thus we
* fallback to AES anticipating RFC4880bis rules. */
if (opt.flags.allow_old_cipher_algos)
dek->algo = CIPHER_ALGO_3DES;
else
dek->algo = CIPHER_ALGO_AES;
}
/* In case 3DES has been selected, print a warning if any key
* does not have a preference for AES. This should help to
* indentify why encrypting to several recipients falls back to
* 3DES. */
if (opt.verbose && dek->algo == CIPHER_ALGO_3DES)
warn_missing_aes_from_pklist (pk_list);
}
else
{
if (!opt.expert
&& (select_algo_from_prefs (pk_list, PREFTYPE_SYM,
opt.def_cipher_algo, NULL)
!= opt.def_cipher_algo))
{
log_info(_("WARNING: forcing symmetric cipher %s (%d)"
" violates recipient preferences\n"),
openpgp_cipher_algo_name (opt.def_cipher_algo),
opt.def_cipher_algo);
}
dek->algo = opt.def_cipher_algo;
}
return dek;
}
/* Check whether all encryption keys are compliant with the current
* mode and issue respective status lines. DEK has the info about the
* session key and PK_LIST the list of public keys. */
static gpg_error_t
check_encryption_compliance (DEK *dek, pk_list_t pk_list)
{
gpg_error_t err = 0;
pk_list_t pkr;
int compliant;
/* First check whether we should use the algo at all. */
if (openpgp_cipher_blocklen (dek->algo) < 16
&& !opt.flags.allow_old_cipher_algos)
{
log_error (_("cipher algorithm '%s' may not be used for encryption\n"),
openpgp_cipher_algo_name (dek->algo));
if (!opt.quiet)
log_info (_("(use option \"%s\" to override)\n"),
"--allow-old-cipher-algos");
err = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
/* Now check the compliance. */
if (! gnupg_cipher_is_allowed (opt.compliance, 1, dek->algo,
GCRY_CIPHER_MODE_CFB))
{
log_error (_("cipher algorithm '%s' may not be used in %s mode\n"),
openpgp_cipher_algo_name (dek->algo),
gnupg_compliance_option_string (opt.compliance));
err = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
if (!gnupg_rng_is_compliant (opt.compliance))
{
err = gpg_error (GPG_ERR_FORBIDDEN);
log_error (_("%s is not compliant with %s mode\n"),
"RNG",
gnupg_compliance_option_string (opt.compliance));
write_status_error ("random-compliance", err);
goto leave;
}
/* From here on we only test for CO_DE_VS - if we ever want to
* return other compliance mode values we need to change this to
* loop over all those values. */
compliant = gnupg_gcrypt_is_compliant (CO_DE_VS);
if (!gnupg_cipher_is_compliant (CO_DE_VS, dek->algo, GCRY_CIPHER_MODE_CFB))
compliant = 0;
for (pkr = pk_list; pkr; pkr = pkr->next)
{
PKT_public_key *pk = pkr->pk;
unsigned int nbits = nbits_from_pk (pk);
if (!gnupg_pk_is_compliant (opt.compliance, pk->pubkey_algo, 0,
pk->pkey, nbits, NULL))
log_info (_("WARNING: key %s is not suitable for encryption"
" in %s mode\n"),
keystr_from_pk (pk),
gnupg_compliance_option_string (opt.compliance));
if (compliant
&& !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey,
nbits, NULL))
compliant = 0; /* Not compliant - reset flag. */
}
/* If we are compliant print the status for de-vs compliance. */
if (compliant)
write_status_strings (STATUS_ENCRYPTION_COMPLIANCE_MODE,
gnupg_status_compliance_flag (CO_DE_VS),
NULL);
/* Check whether we should fail the operation. */
if (opt.flags.require_compliance
&& opt.compliance == CO_DE_VS
&& !compliant)
{
compliance_failure ();
err = gpg_error (GPG_ERR_FORBIDDEN);
goto leave;
}
leave:
return err;
}
/* Encrypt a session key using DEK and store a pointer to the result
* at R_ENCKEY and its length at R_ENCKEYLEN.
*
* R_SESKEY points to the unencrypted session key (.KEY, .KEYLEN) and
* the algorithm that will be used to encrypt the contents of the
* SKESK packet (.ALGO). If R_SESKEY points to NULL, then a random
* session key that is appropriate for DEK->ALGO is generated and
* stored at R_SESKEY. If AEAD_ALGO is not 0 the given AEAD algorithm
* is used for encryption.
*/
static gpg_error_t
encrypt_seskey (DEK *dek, aead_algo_t aead_algo,
DEK **r_seskey, void **r_enckey, size_t *r_enckeylen)
{
gpg_error_t err;
gcry_cipher_hd_t hd = NULL;
byte *buf = NULL;
DEK *seskey;
*r_enckey = NULL;
*r_enckeylen = 0;
if (*r_seskey)
seskey = *r_seskey;
else
{
seskey = xtrycalloc (1, sizeof(DEK));
if (!seskey)
{
err = gpg_error_from_syserror ();
goto leave;
}
seskey->algo = dek->algo;
make_session_key (seskey);
/*log_hexdump( "thekey", c->key, c->keylen );*/
}
if (aead_algo)
{
unsigned int noncelen;
enum gcry_cipher_modes ciphermode;
byte ad[4];
err = openpgp_aead_algo_info (aead_algo, &ciphermode, &noncelen);
if (err)
goto leave;
/* Allocate space for the nonce, the key, and the authentication
* tag (16). */
buf = xtrymalloc_secure (noncelen + seskey->keylen + 16);
if (!buf)
{
err = gpg_error_from_syserror ();
goto leave;
}
gcry_randomize (buf, noncelen, GCRY_STRONG_RANDOM);
err = openpgp_cipher_open (&hd, dek->algo,
ciphermode, GCRY_CIPHER_SECURE);
if (!err)
err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
if (!err)
err = gcry_cipher_setiv (hd, buf, noncelen);
if (err)
goto leave;
ad[0] = (0xc0 | PKT_SYMKEY_ENC);
ad[1] = 5;
ad[2] = dek->algo;
ad[3] = aead_algo;
err = gcry_cipher_authenticate (hd, ad, 4);
if (err)
goto leave;
memcpy (buf + noncelen, seskey->key, seskey->keylen);
gcry_cipher_final (hd);
err = gcry_cipher_encrypt (hd, buf + noncelen, seskey->keylen, NULL,0);
if (err)
goto leave;
err = gcry_cipher_gettag (hd, buf + noncelen + seskey->keylen, 16);
if (err)
goto leave;
*r_enckeylen = noncelen + seskey->keylen + 16;
*r_enckey = buf;
buf = NULL;
}
else
{
/* In the old version 4 SKESK the encrypted session key is
* prefixed with a one-octet algorithm id. */
buf = xtrymalloc_secure (1 + seskey->keylen);
if (!buf)
{
err = gpg_error_from_syserror ();
goto leave;
}
buf[0] = seskey->algo;
memcpy (buf + 1, seskey->key, seskey->keylen);
err = openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1);
if (!err)
err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
if (!err)
err = gcry_cipher_setiv (hd, NULL, 0);
if (!err)
err = gcry_cipher_encrypt (hd, buf, seskey->keylen + 1, NULL, 0);
if (err)
goto leave;
*r_enckeylen = seskey->keylen + 1;
*r_enckey = buf;
buf = NULL;
}
/* Return the session key in case we allocated it. */
*r_seskey = seskey;
seskey = NULL;
leave:
gcry_cipher_close (hd);
if (seskey != *r_seskey)
xfree (seskey);
xfree (buf);
return err;
}
/* Return the AEAD algo if we shall use AEAD mode. Returns 0 if AEAD
* shall not be used. */
aead_algo_t
use_aead (pk_list_t pk_list, int algo)
{
int can_use;
can_use = openpgp_cipher_get_algo_blklen (algo) == 16;
/* With --force-aead we want AEAD. */
if (opt.force_aead)
{
if (!can_use)
{
log_info ("Warning: request to use OCB ignored for cipher '%s'\n",
openpgp_cipher_algo_name (algo));
return 0;
}
return AEAD_ALGO_OCB;
}
/* AEAD does only work with 128 bit cipher blocklength. */
if (!can_use)
return 0;
/* Note the user which keys have no AEAD feature flag set. */
if (opt.verbose)
warn_missing_aead_from_pklist (pk_list);
/* If all keys support AEAD we can use it. */
return select_aead_from_pklist (pk_list);
}
/* Shall we use the MDC? Yes - unless rfc-2440 compatibility is
* requested. */
int
use_mdc (pk_list_t pk_list,int algo)
{
(void)pk_list;
(void)algo;
/* RFC-2440 don't has MDC - this is the only way to create a legacy
* non-MDC encryption packet. */
if (RFC2440)
return 0;
return 1; /* In all other cases we use the MDC */
}
/* We don't want to use use_seskey yet because older gnupg versions
can't handle it, and there isn't really any point unless we're
making a message that can be decrypted by a public key or
passphrase. */
static int
encrypt_simple (const char *filename, int mode, int use_seskey)
{
iobuf_t inp, out;
PACKET pkt;
PKT_plaintext *pt = NULL;
STRING2KEY *s2k = NULL;
void *enckey = NULL;
size_t enckeylen = 0;
int rc = 0;
u32 filesize;
cipher_filter_context_t cfx;
armor_filter_context_t *afx = NULL;
compress_filter_context_t zfx;
text_filter_context_t tfx;
progress_filter_context_t *pfx;
int do_compress = !!default_compress_algo();
char peekbuf[32];
int peekbuflen;
if (!gnupg_rng_is_compliant (opt.compliance))
{
rc = gpg_error (GPG_ERR_FORBIDDEN);
log_error (_("%s is not compliant with %s mode\n"),
"RNG",
gnupg_compliance_option_string (opt.compliance));
write_status_error ("random-compliance", rc);
return rc;
}
pfx = new_progress_context ();
memset( &cfx, 0, sizeof cfx);
memset( &zfx, 0, sizeof zfx);
memset( &tfx, 0, sizeof tfx);
init_packet(&pkt);
/* Prepare iobufs. */
inp = iobuf_open(filename);
if (inp)
iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if (!inp)
{
rc = gpg_error_from_syserror ();
log_error(_("can't open '%s': %s\n"), filename? filename: "[stdin]",
strerror(errno) );
release_progress_context (pfx);
return rc;
}
peekbuflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof peekbuf, peekbuf);
if (peekbuflen < 0)
{
peekbuflen = 0;
if (DBG_FILTER)
log_debug ("peeking at input failed\n");
}
handle_progress (pfx, inp, filename);
if (opt.textmode)
iobuf_push_filter( inp, text_filter, &tfx );
cfx.dek = NULL;
if ( mode )
{
aead_algo_t aead_algo;
rc = setup_symkey (&s2k, &cfx.dek);
if (rc)
{
iobuf_close (inp);
if (gpg_err_code (rc) == GPG_ERR_CIPHER_ALGO
|| gpg_err_code (rc) == GPG_ERR_DIGEST_ALGO)
; /* Error has already been printed. */
else
log_error (_("error creating passphrase: %s\n"), gpg_strerror (rc));
release_progress_context (pfx);
return rc;
}
if (use_seskey && s2k->mode != 1 && s2k->mode != 3)
{
use_seskey = 0;
log_info (_("can't use a SKESK packet due to the S2K mode\n"));
}
/* See whether we want to use AEAD. */
aead_algo = use_aead (NULL, cfx.dek->algo);
if ( use_seskey )
{
DEK *dek = NULL;
rc = encrypt_seskey (cfx.dek, aead_algo, &dek, &enckey, &enckeylen);
if (rc)
{
xfree (cfx.dek);
xfree (s2k);
iobuf_close (inp);
release_progress_context (pfx);
return rc;
}
/* Replace key in DEK. */
xfree (cfx.dek);
cfx.dek = dek;
}
if (aead_algo)
cfx.dek->use_aead = aead_algo;
else
cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo);
if (opt.verbose)
log_info(_("using cipher %s.%s\n"),
openpgp_cipher_algo_name (cfx.dek->algo),
cfx.dek->use_aead? openpgp_aead_algo_name (cfx.dek->use_aead)
/**/ : "CFB");
}
if (do_compress
&& cfx.dek
&& (cfx.dek->use_mdc || cfx.dek->use_aead)
&& !opt.explicit_compress_option
&& is_file_compressed (peekbuf, peekbuflen))
{
if (opt.verbose)
log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]");
do_compress = 0;
}
if ( rc || (rc = open_outfile (-1, filename, opt.armor? 1:0, 0, &out )))
{
iobuf_cancel (inp);
xfree (cfx.dek);
xfree (s2k);
release_progress_context (pfx);
return rc;
}
if ( opt.armor )
{
afx = new_armor_context ();
push_armor_filter (afx, out);
}
if ( s2k )
{
/* Fixme: This is quite similar to write_symkey_enc. */
PKT_symkey_enc *enc = xmalloc_clear (sizeof *enc + enckeylen);
enc->version = cfx.dek->use_aead ? 5 : 4;
enc->cipher_algo = cfx.dek->algo;
enc->aead_algo = cfx.dek->use_aead;
enc->s2k = *s2k;
if (enckeylen)
{
enc->seskeylen = enckeylen;
memcpy (enc->seskey, enckey, enckeylen);
}
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
if ((rc = build_packet( out, &pkt )))
log_error("build symkey packet failed: %s\n", gpg_strerror (rc) );
xfree (enc);
xfree (enckey);
enckey = NULL;
}
if (!opt.no_literal)
pt = setup_plaintext_name (filename, inp);
/* Note that PGP 5 has problems decrypting symmetrically encrypted
data if the file length is in the inner packet. It works when
only partial length headers are use. In the past, we always used
partial body length here, but since PGP 2, PGP 6, and PGP 7 need
the file length, and nobody should be using PGP 5 nowadays
anyway, this is now set to the file length. Note also that this
only applies to the RFC-1991 style symmetric messages, and not
the RFC-2440 style. PGP 6 and 7 work with either partial length
or fixed length with the new style messages. */
if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
{
- off_t tmpsize;
- int overflow;
+ uint64_t tmpsize;
- if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
- && !overflow && opt.verbose)
+ tmpsize = iobuf_get_filelength(inp);
+ if (!tmpsize && opt.verbose)
log_info(_("WARNING: '%s' is an empty file\n"), filename );
+
/* We can't encode the length of very large files because
OpenPGP uses only 32 bit for file sizes. So if the
size of a file is larger than 2^32 minus some bytes for
packet headers, we switch to partial length encoding. */
if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
filesize = tmpsize;
else
filesize = 0;
}
else
filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
if (!opt.no_literal)
{
/* Note that PT has been initialized above in !no_literal mode. */
pt->timestamp = make_timestamp();
pt->mode = opt.mimemode? 'm' : opt.textmode? 't' : 'b';
pt->len = filesize;
pt->new_ctb = !pt->len;
pt->buf = inp;
pkt.pkttype = PKT_PLAINTEXT;
pkt.pkt.plaintext = pt;
cfx.datalen = filesize && !do_compress ? calc_packet_length( &pkt ) : 0;
}
else
{
cfx.datalen = filesize && !do_compress ? filesize : 0;
pkt.pkttype = 0;
pkt.pkt.generic = NULL;
}
/* Register the cipher filter. */
if (mode)
iobuf_push_filter (out,
cfx.dek->use_aead? cipher_filter_aead
/**/ : cipher_filter_cfb,
&cfx );
/* Register the compress filter. */
if ( do_compress )
{
if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead))
zfx.new_ctb = 1;
push_compress_filter (out, &zfx, default_compress_algo());
}
/* Do the work. */
if (!opt.no_literal)
{
if ( (rc = build_packet( out, &pkt )) )
log_error("build_packet failed: %s\n", gpg_strerror (rc) );
}
else
{
/* User requested not to create a literal packet, so we copy the
plain data. */
rc = iobuf_copy (out, inp);
if (rc)
log_error ("copying input to output failed: %s\n", gpg_strerror (rc));
}
/* Finish the stuff. */
iobuf_close (inp);
if (rc)
iobuf_cancel(out);
else
{
iobuf_close (out); /* fixme: check returncode */
if (mode)
write_status ( STATUS_END_ENCRYPTION );
}
if (pt)
pt->buf = NULL;
free_packet (&pkt, NULL);
xfree (enckey);
xfree (cfx.dek);
xfree (s2k);
release_armor_context (afx);
release_progress_context (pfx);
return rc;
}
gpg_error_t
setup_symkey (STRING2KEY **symkey_s2k, DEK **symkey_dek)
{
int canceled;
int defcipher;
int s2kdigest;
defcipher = default_cipher_algo ();
if (openpgp_cipher_blocklen (defcipher) < 16
&& !opt.flags.allow_old_cipher_algos)
{
log_error (_("cipher algorithm '%s' may not be used for encryption\n"),
openpgp_cipher_algo_name (defcipher));
if (!opt.quiet)
log_info (_("(use option \"%s\" to override)\n"),
"--allow-old-cipher-algos");
return gpg_error (GPG_ERR_CIPHER_ALGO);
}
if (!gnupg_cipher_is_allowed (opt.compliance, 1, defcipher,
GCRY_CIPHER_MODE_CFB))
{
log_error (_("cipher algorithm '%s' may not be used in %s mode\n"),
openpgp_cipher_algo_name (defcipher),
gnupg_compliance_option_string (opt.compliance));
return gpg_error (GPG_ERR_CIPHER_ALGO);
}
s2kdigest = S2K_DIGEST_ALGO;
if (!gnupg_digest_is_allowed (opt.compliance, 1, s2kdigest))
{
log_error (_("digest algorithm '%s' may not be used in %s mode\n"),
gcry_md_algo_name (s2kdigest),
gnupg_compliance_option_string (opt.compliance));
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
*symkey_s2k = xmalloc_clear (sizeof **symkey_s2k);
(*symkey_s2k)->mode = opt.s2k_mode;
(*symkey_s2k)->hash_algo = s2kdigest;
*symkey_dek = passphrase_to_dek (defcipher,
*symkey_s2k, 1, 0, NULL, 0, &canceled);
if (!*symkey_dek || !(*symkey_dek)->keylen)
{
xfree(*symkey_dek);
xfree(*symkey_s2k);
return gpg_error (canceled?GPG_ERR_CANCELED:GPG_ERR_INV_PASSPHRASE);
}
return 0;
}
static int
write_symkey_enc (STRING2KEY *symkey_s2k, aead_algo_t aead_algo,
DEK *symkey_dek, DEK *dek, iobuf_t out)
{
int rc;
void *enckey;
size_t enckeylen;
PKT_symkey_enc *enc;
PACKET pkt;
rc = encrypt_seskey (symkey_dek, aead_algo, &dek, &enckey, &enckeylen);
if (rc)
return rc;
enc = xtrycalloc (1, sizeof (PKT_symkey_enc) + enckeylen);
if (!enc)
{
rc = gpg_error_from_syserror ();
xfree (enckey);
return rc;
}
enc->version = aead_algo? 5 : 4;
enc->cipher_algo = opt.s2k_cipher_algo;
enc->aead_algo = aead_algo;
enc->s2k = *symkey_s2k;
enc->seskeylen = enckeylen;
memcpy (enc->seskey, enckey, enckeylen);
xfree (enckey);
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
if ((rc=build_packet(out,&pkt)))
log_error("build symkey_enc packet failed: %s\n",gpg_strerror (rc));
xfree (enc);
return rc;
}
/*
* Encrypt the file with the given userids (or ask if none is
* supplied). Either FILENAME or FILEFD must be given, but not both.
* The caller may provide a checked list of public keys in
* PROVIDED_PKS; if not the function builds a list of keys on its own.
*
* Note that FILEFD is currently only used by cmd_encrypt in the
* not yet finished server.c.
*/
int
encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
strlist_t remusr, int use_symkey, pk_list_t provided_keys,
int outputfd)
{
iobuf_t inp = NULL;
iobuf_t out = NULL;
PACKET pkt;
PKT_plaintext *pt = NULL;
DEK *symkey_dek = NULL;
STRING2KEY *symkey_s2k = NULL;
int rc = 0, rc2 = 0;
u32 filesize;
cipher_filter_context_t cfx;
armor_filter_context_t *afx = NULL;
compress_filter_context_t zfx;
text_filter_context_t tfx;
progress_filter_context_t *pfx;
PK_LIST pk_list;
int do_compress;
char peekbuf[32];
int peekbuflen;
if (filefd != -1 && filename)
return gpg_error (GPG_ERR_INV_ARG); /* Both given. */
do_compress = !!opt.compress_algo;
pfx = new_progress_context ();
memset( &cfx, 0, sizeof cfx);
memset( &zfx, 0, sizeof zfx);
memset( &tfx, 0, sizeof tfx);
init_packet(&pkt);
if (use_symkey
&& (rc=setup_symkey(&symkey_s2k,&symkey_dek)))
{
release_progress_context (pfx);
return rc;
}
if (provided_keys)
pk_list = provided_keys;
else
{
if ((rc = build_pk_list (ctrl, remusr, &pk_list)))
{
release_progress_context (pfx);
return rc;
}
}
/* Prepare iobufs. */
#ifdef HAVE_W32_SYSTEM
if (filefd == -1)
inp = iobuf_open (filename);
else
{
inp = NULL;
gpg_err_set_errno (ENOSYS);
}
#else
if (filefd == -1)
inp = iobuf_open (filename);
else
inp = iobuf_fdopen_nc (filefd, "rb");
#endif
if (inp)
iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if (!inp)
{
char xname[64];
rc = gpg_error_from_syserror ();
if (filefd != -1)
snprintf (xname, sizeof xname, "[fd %d]", filefd);
else if (!filename)
strcpy (xname, "[stdin]");
else
*xname = 0;
log_error (_("can't open '%s': %s\n"),
*xname? xname : filename, gpg_strerror (rc) );
goto leave;
}
if (opt.verbose)
log_info (_("reading from '%s'\n"), iobuf_get_fname_nonnull (inp));
peekbuflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof peekbuf, peekbuf);
if (peekbuflen < 0)
{
peekbuflen = 0;
if (DBG_FILTER)
log_debug ("peeking at input failed\n");
}
handle_progress (pfx, inp, filename);
if (opt.textmode)
iobuf_push_filter (inp, text_filter, &tfx);
rc = open_outfile (outputfd, filename, opt.armor? 1:0, 0, &out);
if (rc)
goto leave;
if (opt.armor)
{
afx = new_armor_context ();
push_armor_filter (afx, out);
}
/* Create a session key. */
cfx.dek = create_dek_with_warnings (pk_list);
rc = check_encryption_compliance (cfx.dek, pk_list);
if (rc)
goto leave;
cfx.dek->use_aead = use_aead (pk_list, cfx.dek->algo);
if (!cfx.dek->use_aead)
cfx.dek->use_mdc = !!use_mdc (pk_list, cfx.dek->algo);
/* Only do the is-file-already-compressed check if we are using a
* MDC or AEAD. This forces compressed files to be re-compressed if
* we do not have a MDC to give some protection against chosen
* ciphertext attacks. */
if (do_compress
&& (cfx.dek->use_mdc || cfx.dek->use_aead)
&& !opt.explicit_compress_option
&& is_file_compressed (peekbuf, peekbuflen))
{
if (opt.verbose)
log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]");
do_compress = 0;
}
if (rc2)
{
rc = rc2;
goto leave;
}
make_session_key (cfx.dek);
if (DBG_CRYPTO)
log_printhex (cfx.dek->key, cfx.dek->keylen, "DEK is: ");
rc = write_pubkey_enc_from_list (ctrl, pk_list, cfx.dek, out);
if (rc)
goto leave;
/* We put the passphrase (if any) after any public keys as this
* seems to be the most useful on the recipient side - there is no
* point in prompting a user for a passphrase if they have the
* secret key needed to decrypt. */
if (use_symkey && (rc = write_symkey_enc (symkey_s2k, cfx.dek->use_aead,
symkey_dek, cfx.dek, out)))
goto leave;
if (!opt.no_literal)
pt = setup_plaintext_name (filename, inp);
/* Get the size of the file if possible, i.e., if it is a real file. */
if (filename && *filename
&& !iobuf_is_pipe_filename (filename) && !opt.textmode )
{
- off_t tmpsize;
- int overflow;
+ uint64_t tmpsize;
- if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
- && !overflow && opt.verbose)
+ tmpsize = iobuf_get_filelength (inp);
+ if (!tmpsize && opt.verbose)
log_info(_("WARNING: '%s' is an empty file\n"), filename );
/* We can't encode the length of very large files because
OpenPGP uses only 32 bit for file sizes. So if the size
of a file is larger than 2^32 minus some bytes for packet
headers, we switch to partial length encoding. */
if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
filesize = tmpsize;
else
filesize = 0;
}
else
filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
if (!opt.no_literal)
{
pt->timestamp = make_timestamp();
pt->mode = opt.mimemode? 'm' : opt.textmode ? 't' : 'b';
pt->len = filesize;
pt->new_ctb = !pt->len;
pt->buf = inp;
pkt.pkttype = PKT_PLAINTEXT;
pkt.pkt.plaintext = pt;
cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0;
}
else
cfx.datalen = filesize && !do_compress ? filesize : 0;
/* Register the cipher filter. */
iobuf_push_filter (out,
cfx.dek->use_aead? cipher_filter_aead
/**/ : cipher_filter_cfb,
&cfx);
/* Register the compress filter. */
if (do_compress)
{
int compr_algo = opt.compress_algo;
if (compr_algo == -1)
{
compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP, -1, NULL);
if (compr_algo == -1)
compr_algo = DEFAULT_COMPRESS_ALGO;
/* Theoretically impossible to get here since uncompressed
is implicit. */
}
else if (!opt.expert
&& select_algo_from_prefs(pk_list, PREFTYPE_ZIP,
compr_algo, NULL) != compr_algo)
{
log_info (_("WARNING: forcing compression algorithm %s (%d)"
" violates recipient preferences\n"),
compress_algo_to_string(compr_algo), compr_algo);
}
/* Algo 0 means no compression. */
if (compr_algo)
{
if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead))
zfx.new_ctb = 1;
push_compress_filter (out,&zfx,compr_algo);
}
}
/* Do the work. */
if (!opt.no_literal)
{
if ((rc = build_packet( out, &pkt )))
log_error ("build_packet failed: %s\n", gpg_strerror (rc));
}
else
{
/* User requested not to create a literal packet, so we copy the
plain data. */
byte copy_buffer[4096];
int bytes_copied;
while ((bytes_copied = iobuf_read (inp, copy_buffer, 4096)) != -1)
{
rc = iobuf_write (out, copy_buffer, bytes_copied);
if (rc)
{
log_error ("copying input to output failed: %s\n",
gpg_strerror (rc));
break;
}
}
wipememory (copy_buffer, 4096); /* Burn the buffer. */
}
/* Finish the stuff. */
leave:
iobuf_close (inp);
if (rc)
iobuf_cancel (out);
else
{
iobuf_close (out); /* fixme: check returncode */
write_status (STATUS_END_ENCRYPTION);
}
if (pt)
pt->buf = NULL;
free_packet (&pkt, NULL);
xfree (cfx.dek);
xfree (symkey_dek);
xfree (symkey_s2k);
if (!provided_keys)
release_pk_list (pk_list);
release_armor_context (afx);
release_progress_context (pfx);
return rc;
}
/*
* Filter to do a complete public key encryption.
*/
int
encrypt_filter (void *opaque, int control,
iobuf_t a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
encrypt_filter_context_t *efx = opaque;
int rc = 0;
if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */
{
BUG(); /* not used */
}
else if ( control == IOBUFCTRL_FLUSH ) /* encrypt */
{
if ( !efx->header_okay )
{
efx->header_okay = 1;
efx->cfx.dek = create_dek_with_warnings (efx->pk_list);
rc = check_encryption_compliance (efx->cfx.dek, efx->pk_list);
if (rc)
return rc;
efx->cfx.dek->use_aead = use_aead (efx->pk_list, efx->cfx.dek->algo);
if (!efx->cfx.dek->use_aead)
efx->cfx.dek->use_mdc = !!use_mdc (efx->pk_list,efx->cfx.dek->algo);
make_session_key ( efx->cfx.dek );
if (DBG_CRYPTO)
log_printhex (efx->cfx.dek->key, efx->cfx.dek->keylen, "DEK is: ");
rc = write_pubkey_enc_from_list (efx->ctrl,
efx->pk_list, efx->cfx.dek, a);
if (rc)
return rc;
if(efx->symkey_s2k && efx->symkey_dek)
{
rc = write_symkey_enc (efx->symkey_s2k, efx->cfx.dek->use_aead,
efx->symkey_dek, efx->cfx.dek, a);
if (rc)
return rc;
}
iobuf_push_filter (a,
efx->cfx.dek->use_aead? cipher_filter_aead
/**/ : cipher_filter_cfb,
&efx->cfx);
}
rc = iobuf_write (a, buf, size);
}
else if (control == IOBUFCTRL_FREE)
{
xfree (efx->symkey_dek);
xfree (efx->symkey_s2k);
}
else if ( control == IOBUFCTRL_DESC )
{
mem2str (buf, "encrypt_filter", *ret_len);
}
return rc;
}
/*
* Write a pubkey-enc packet for the public key PK to OUT.
*/
int
write_pubkey_enc (ctrl_t ctrl,
PKT_public_key *pk, int throw_keyid, DEK *dek, iobuf_t out)
{
PACKET pkt;
PKT_pubkey_enc *enc;
int rc;
gcry_mpi_t frame;
print_pubkey_algo_note ( pk->pubkey_algo );
enc = xmalloc_clear ( sizeof *enc );
enc->pubkey_algo = pk->pubkey_algo;
keyid_from_pk( pk, enc->keyid );
enc->throw_keyid = throw_keyid;
/* Okay, what's going on: We have the session key somewhere in
* the structure DEK and want to encode this session key in an
* integer value of n bits. pubkey_nbits gives us the number of
* bits we have to use. We then encode the session key in some
* way and we get it back in the big intger value FRAME. Then
* we use FRAME, the public key PK->PKEY and the algorithm
* number PK->PUBKEY_ALGO and pass it to pubkey_encrypt which
* returns the encrypted value in the array ENC->DATA. This
* array has a size which depends on the used algorithm (e.g. 2
* for Elgamal). We don't need frame anymore because we have
* everything now in enc->data which is the passed to
* build_packet(). */
frame = encode_session_key (pk->pubkey_algo, dek,
pubkey_nbits (pk->pubkey_algo, pk->pkey));
rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk, pk->pkey);
gcry_mpi_release (frame);
if (rc)
log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) );
else
{
if ( opt.verbose )
{
char *ustr = get_user_id_string_native (ctrl, enc->keyid);
if ((pk->pubkey_usage & PUBKEY_USAGE_RENC))
{
char *tmpustr = xstrconcat (ustr, " [ADSK]", NULL);
xfree (ustr);
ustr = tmpustr;
}
log_info (_("%s/%s.%s encrypted for: \"%s\"\n"),
openpgp_pk_algo_name (enc->pubkey_algo),
openpgp_cipher_algo_name (dek->algo),
dek->use_aead? openpgp_aead_algo_name (dek->use_aead)
/**/ : "CFB",
ustr );
xfree (ustr);
}
/* And write it. */
init_packet (&pkt);
pkt.pkttype = PKT_PUBKEY_ENC;
pkt.pkt.pubkey_enc = enc;
rc = build_packet (out, &pkt);
if (rc)
log_error ("build_packet(pubkey_enc) failed: %s\n",
gpg_strerror (rc));
}
free_pubkey_enc(enc);
return rc;
}
/*
* Write pubkey-enc packets from the list of PKs to OUT.
*/
static int
write_pubkey_enc_from_list (ctrl_t ctrl, PK_LIST pk_list, DEK *dek, iobuf_t out)
{
if (opt.throw_keyids && (PGP7 || PGP8))
{
log_info(_("option '%s' may not be used in %s mode\n"),
"--throw-keyids",
gnupg_compliance_option_string (opt.compliance));
compliance_failure();
}
for ( ; pk_list; pk_list = pk_list->next )
{
PKT_public_key *pk = pk_list->pk;
int throw_keyid = (opt.throw_keyids || (pk_list->flags&1));
int rc = write_pubkey_enc (ctrl, pk, throw_keyid, dek, out);
if (rc)
return rc;
}
return 0;
}
void
encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr)
{
int rc = 0;
if (opt.outfile)
{
log_error(_("--output doesn't work for this command\n"));
return;
}
if (!nfiles)
{
char line[2048];
unsigned int lno = 0;
while ( fgets(line, DIM(line), stdin) )
{
lno++;
if (!*line || line[strlen(line)-1] != '\n')
{
log_error("input line %u too long or missing LF\n", lno);
return;
}
line[strlen(line)-1] = '\0';
print_file_status(STATUS_FILE_START, line, 2);
rc = encrypt_crypt (ctrl, -1, line, remusr, 0, NULL, -1);
if (rc)
log_error ("encryption of '%s' failed: %s\n",
print_fname_stdin(line), gpg_strerror (rc) );
write_status( STATUS_FILE_DONE );
}
}
else
{
while (nfiles--)
{
print_file_status(STATUS_FILE_START, *files, 2);
if ( (rc = encrypt_crypt (ctrl, -1, *files, remusr, 0, NULL, -1)) )
log_error("encryption of '%s' failed: %s\n",
print_fname_stdin(*files), gpg_strerror (rc) );
write_status( STATUS_FILE_DONE );
files++;
}
}
}
diff --git a/g10/filter.h b/g10/filter.h
index 46342d2ad..4b4fc55ff 100644
--- a/g10/filter.h
+++ b/g10/filter.h
@@ -1,202 +1,202 @@
/* filter.h
* Copyright (C) 1998, 1999, 2000, 2001, 2003,
* 2005 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG 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/>.
*/
#ifndef G10_FILTER_H
#define G10_FILTER_H
#include "../common/types.h"
#include "dek.h"
typedef struct {
gcry_md_hd_t md; /* catch all */
gcry_md_hd_t md2; /* if we want to calculate an alternate hash */
size_t maxbuf_size;
} md_filter_context_t;
typedef struct {
int refcount; /* Initialized to 1. */
/* these fields may be initialized */
int what; /* what kind of armor headers to write */
int only_keyblocks; /* skip all headers but ".... key block" */
int dearmor_mode; /* dearmor all kind of stuff. */
const char *hdrlines; /* write these headerlines */
/* these fields must be initialized to zero */
int no_openpgp_data; /* output flag: "No valid OpenPGP data found" */
/* the following fields must be initialized to zero */
int inp_checked; /* set if the input has been checked */
int inp_bypass; /* set if the input is not armored */
int in_cleartext; /* clear text message */
int not_dash_escaped; /* clear text is not dash escaped */
int hashes; /* detected hash algorithms */
int faked; /* we are faking a literal data packet */
int truncated; /* number of truncated lines */
int qp_detected;
int dearmor_state; /* helper for dearmor_mode. */
byte eol[3]; /* The end of line characters as a
zero-terminated string. Defaults
(eol[0]=='\0') to whatever the local
platform uses. */
byte *buffer; /* malloced buffer */
unsigned buffer_size; /* and size of this buffer */
unsigned buffer_len; /* used length of the buffer */
unsigned buffer_pos; /* read position */
byte radbuf[4];
int idx, idx2;
gcry_md_hd_t crc_md;
int status; /* an internal state flag */
int cancel;
int any_data; /* any valid armored data seen */
int pending_lf; /* used together with faked */
} armor_filter_context_t;
struct compress_filter_context_s {
int status;
void *opaque; /* (used for z_stream) */
byte *inbuf;
unsigned inbufsize;
byte *outbuf;
unsigned outbufsize;
int algo; /* compress algo */
int algo1hack;
int new_ctb;
void (*release)(struct compress_filter_context_s*);
};
typedef struct compress_filter_context_s compress_filter_context_t;
typedef struct
{
/* Object with the key and algo */
DEK *dek;
/* Length of the data to encrypt if known - 32 bit because OpenPGP
* requires partial encoding for a larger data size. */
u32 datalen;
/* The current cipher handle. */
gcry_cipher_hd_t cipher_hd;
/* Various processing flags. */
unsigned int wrote_header : 1;
unsigned int short_blklen_warn : 1;
unsigned long short_blklen_count;
/* The encoded chunk byte for AEAD. */
byte chunkbyte;
/* The decoded CHUNKBYTE. */
uint64_t chunksize;
/* The chunk index for AEAD. */
uint64_t chunkindex;
/* The number of bytes in the current chunk. */
uint64_t chunklen;
/* The total count of encrypted plaintext octets. Note that we
* don't care about encrypting more than 16 Exabyte. */
uint64_t total;
/* The hash context and a buffer used for MDC. */
gcry_md_hd_t mdc_hash;
byte enchash[20];
/* The start IV for AEAD encryption. */
byte startiv[16];
/* Using a large buffer for encryption makes processing easier and
* also makes sure the data is well aligned. */
char *buffer;
size_t bufsize; /* Allocated length. */
size_t buflen; /* Used length. */
} cipher_filter_context_t;
typedef struct {
byte *buffer; /* malloced buffer */
unsigned buffer_size; /* and size of this buffer */
unsigned buffer_len; /* used length of the buffer */
unsigned buffer_pos; /* read position */
int truncated; /* number of truncated lines */
int not_dash_escaped;
int escape_from;
gcry_md_hd_t md;
int pending_lf;
int pending_esc;
} text_filter_context_t;
typedef struct {
char *what; /* description */
u32 last_time; /* last time reported */
- unsigned long last; /* last amount reported */
- unsigned long offset; /* current amount */
- unsigned long total; /* total amount */
+ uint64_t last; /* last amount reported */
+ uint64_t offset; /* current amount */
+ uint64_t total; /* total amount */
int refcount;
} progress_filter_context_t;
/* encrypt_filter_context_t defined in main.h */
/*-- mdfilter.c --*/
int md_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len);
void free_md_filter_context( md_filter_context_t *mfx );
/*-- armor.c --*/
armor_filter_context_t *new_armor_context (void);
void release_armor_context (armor_filter_context_t *afx);
int push_armor_filter (armor_filter_context_t *afx, iobuf_t iobuf);
int use_armor_filter( iobuf_t a );
/*-- compress.c --*/
gpg_error_t push_compress_filter (iobuf_t out, compress_filter_context_t *zfx,
int algo);
gpg_error_t push_compress_filter2 (iobuf_t out,compress_filter_context_t *zfx,
int algo, int rel);
/*-- cipher.c --*/
int cipher_filter_cfb (void *opaque, int control,
iobuf_t chain, byte *buf, size_t *ret_len);
/*-- cipher-aead.c --*/
int cipher_filter_aead (void *opaque, int control,
iobuf_t chain, byte *buf, size_t *ret_len);
/*-- textfilter.c --*/
int text_filter( void *opaque, int control,
iobuf_t chain, byte *buf, size_t *ret_len);
int copy_clearsig_text (iobuf_t out, iobuf_t inp, gcry_md_hd_t md,
int escape_dash, int escape_from);
/*-- progress.c --*/
progress_filter_context_t *new_progress_context (void);
void release_progress_context (progress_filter_context_t *pfx);
void handle_progress (progress_filter_context_t *pfx,
iobuf_t inp, const char *name);
#endif /*G10_FILTER_H*/
diff --git a/g10/gpg.c b/g10/gpg.c
index 6e54aa763..39776c3d1 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -1,5826 +1,5832 @@
/* gpg.c - The GnuPG OpenPGP tool
* Copyright (C) 1998-2020 Free Software Foundation, Inc.
* Copyright (C) 1997-2019 Werner Koch
* Copyright (C) 2015-2022 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG 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: GPL-3.0-or-later
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#ifdef HAVE_STAT
#include <sys/stat.h> /* for stat() */
#endif
#include <fcntl.h>
#ifdef HAVE_W32_SYSTEM
# ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
# endif
# include <windows.h>
#endif
#include <npth.h>
#define INCLUDED_BY_MAIN_MODULE 1
#include "gpg.h"
#include <assuan.h>
#include "../common/iobuf.h"
#include "../common/util.h"
#include "packet.h"
#include "../common/membuf.h"
#include "main.h"
#include "options.h"
#include "keydb.h"
#include "trustdb.h"
#include "filter.h"
#include "../common/ttyio.h"
#include "../common/i18n.h"
#include "../common/sysutils.h"
#include "../common/status.h"
#include "keyserver-internal.h"
#include "exec.h"
#include "../common/gc-opt-flags.h"
#include "../common/asshelp.h"
#include "call-dirmngr.h"
#include "tofu.h"
#include "objcache.h"
#include "../common/init.h"
#include "../common/mbox-util.h"
#include "../common/zb32.h"
#include "../common/shareddefs.h"
#include "../common/compliance.h"
#include "../common/comopt.h"
#include "../kbx/keybox.h"
#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
#define MY_O_BINARY O_BINARY
#ifndef S_IRGRP
# define S_IRGRP 0
# define S_IWGRP 0
#endif
#else
#define MY_O_BINARY 0
#endif
enum cmd_and_opt_values
{
aNull = 0,
oArmor = 'a',
aDetachedSign = 'b',
aSym = 'c',
aDecrypt = 'd',
aEncr = 'e',
oRecipientFile = 'f',
oHiddenRecipientFile = 'F',
oInteractive = 'i',
aListKeys = 'k',
oDryRun = 'n',
oOutput = 'o',
oQuiet = 'q',
oRecipient = 'r',
oHiddenRecipient = 'R',
aSign = 's',
oTextmodeShort= 't',
oLocalUser = 'u',
oVerbose = 'v',
oCompress = 'z',
oSetNotation = 'N',
aListSecretKeys = 'K',
oBatch = 500,
oMaxOutput,
oInputSizeHint,
oChunkSize,
oSigNotation,
oCertNotation,
oShowNotation,
oNoShowNotation,
oKnownNotation,
aEncrFiles,
aEncrSym,
aDecryptFiles,
aClearsign,
aStore,
aQuickKeygen,
aFullKeygen,
aKeygen,
aSignEncr,
aSignEncrSym,
aSignSym,
aSignKey,
aLSignKey,
aQuickSignKey,
aQuickLSignKey,
aQuickRevSig,
aQuickAddUid,
aQuickAddKey,
aQuickAddADSK,
aQuickRevUid,
aQuickSetExpire,
aQuickSetPrimaryUid,
aQuickUpdatePref,
aListConfig,
aListGcryptConfig,
aGPGConfList,
aGPGConfTest,
aListPackets,
aEditKey,
aDeleteKeys,
aDeleteSecretKeys,
aDeleteSecretAndPublicKeys,
aImport,
aFastImport,
aVerify,
aVerifyFiles,
aListSigs,
aSendKeys,
aRecvKeys,
aLocateKeys,
aLocateExtKeys,
aSearchKeys,
aRefreshKeys,
aFetchKeys,
aShowKeys,
aExport,
aExportSecret,
aExportSecretSub,
aExportSshKey,
aExportSecretSshKey,
aCheckKeys,
aGenRevoke,
aDesigRevoke,
aPrimegen,
aPrintMD,
aPrintMDs,
aCheckTrustDB,
aUpdateTrustDB,
aFixTrustDB,
aListTrustDB,
aListTrustPath,
aExportOwnerTrust,
aImportOwnerTrust,
aDeArmor,
aEnArmor,
aGenRandom,
aRebuildKeydbCaches,
aCardStatus,
aCardEdit,
aChangePIN,
aPasswd,
aServer,
aTOFUPolicy,
oMimemode,
oTextmode,
oNoTextmode,
oExpert,
oNoExpert,
oDefSigExpire,
oAskSigExpire,
oNoAskSigExpire,
oDefCertExpire,
oAskCertExpire,
oNoAskCertExpire,
oDefCertLevel,
oMinCertLevel,
oAskCertLevel,
oNoAskCertLevel,
oFingerprint,
oWithFingerprint,
oWithSubkeyFingerprint,
oWithICAOSpelling,
oWithKeygrip,
oWithKeyScreening,
oWithSecret,
oWithWKDHash,
oWithColons,
oWithKeyData,
oWithKeyOrigin,
oWithTofuInfo,
oWithSigList,
oWithSigCheck,
oAnswerYes,
oAnswerNo,
oKeyring,
oPrimaryKeyring,
oSecretKeyring,
oShowKeyring,
oDefaultKey,
oDefRecipient,
oDefRecipientSelf,
oNoDefRecipient,
oTrySecretKey,
oOptions,
oDebug,
oDebugLevel,
oDebugAll,
oDebugIOLBF,
oDebugSetIobufSize,
oDebugAllowLargeChunks,
oDebugIgnoreExpiration,
oStatusFD,
oStatusFile,
oAttributeFD,
oAttributeFile,
oEmitVersion,
oNoEmitVersion,
oCompletesNeeded,
oMarginalsNeeded,
oMaxCertDepth,
oLoadExtension,
oCompliance,
oGnuPG,
oRFC2440,
oRFC4880,
oOpenPGP,
oPGP7,
oPGP8,
oDE_VS,
oMinRSALength,
oRFC2440Text,
oNoRFC2440Text,
oCipherAlgo,
oDigestAlgo,
oCertDigestAlgo,
oCompressAlgo,
oCompressLevel,
oBZ2CompressLevel,
oBZ2DecompressLowmem,
oPassphrase,
oPassphraseFD,
oPassphraseFile,
oPassphraseRepeat,
oPinentryMode,
oCommandFD,
oCommandFile,
oQuickRandom,
oNoVerbose,
oTrustDBName,
oNoSecmemWarn,
oRequireSecmem,
oNoRequireSecmem,
oNoPermissionWarn,
oNoArmor,
oNoDefKeyring,
oNoKeyring,
oNoGreeting,
oNoTTY,
oNoOptions,
oNoBatch,
oHomedir,
oSkipVerify,
oSkipHiddenRecipients,
oNoSkipHiddenRecipients,
oAlwaysTrust,
oTrustModel,
oForceOwnertrust,
oNoAutoTrustNewKey,
oSetFilename,
oForYourEyesOnly,
oNoForYourEyesOnly,
oSetPolicyURL,
oSigPolicyURL,
oCertPolicyURL,
oShowPolicyURL,
oNoShowPolicyURL,
oSigKeyserverURL,
oUseEmbeddedFilename,
oNoUseEmbeddedFilename,
oComment,
oDefaultComment,
oNoComments,
oThrowKeyids,
oNoThrowKeyids,
oShowPhotos,
oNoShowPhotos,
oPhotoViewer,
oForceAEAD,
oS2KMode,
oS2KDigest,
oS2KCipher,
oS2KCount,
oDisplayCharset,
oNotDashEscaped,
oEscapeFrom,
oNoEscapeFrom,
oLockOnce,
oLockMultiple,
oLockNever,
oKeyServer,
oKeyServerOptions,
oImportOptions,
oImportFilter,
oExportOptions,
oExportFilter,
oListOptions,
oListFilter,
oVerifyOptions,
oTempDir,
oExecPath,
oEncryptTo,
oHiddenEncryptTo,
oNoEncryptTo,
oEncryptToDefaultKey,
oLoggerFD,
oLoggerFile,
oLogTime,
oUtf8Strings,
oNoUtf8Strings,
oDisableCipherAlgo,
oDisablePubkeyAlgo,
oAllowNonSelfsignedUID,
oNoAllowNonSelfsignedUID,
oAllowFreeformUID,
oNoAllowFreeformUID,
oAllowSecretKeyImport,
oAllowOldCipherAlgos,
oEnableSpecialFilenames,
oNoLiteral,
oSetFilesize,
oHonorHttpProxy,
oFastListMode,
oListOnly,
oIgnoreTimeConflict,
oIgnoreValidFrom,
oIgnoreCrcError,
oIgnoreMDCError,
oShowSessionKey,
oOverrideSessionKey,
oOverrideSessionKeyFD,
oNoRandomSeedFile,
oAutoKeyRetrieve,
oNoAutoKeyRetrieve,
oAutoKeyImport,
oNoAutoKeyImport,
oUseAgent,
oNoUseAgent,
oGpgAgentInfo,
oUseKeyboxd,
oMergeOnly,
oTryAllSecrets,
oTrustedKey,
oNoExpensiveTrustChecks,
oFixedListMode,
oLegacyListMode,
oNoSigCache,
oAutoCheckTrustDB,
oNoAutoCheckTrustDB,
oPreservePermissions,
oDefaultPreferenceList,
oDefaultKeyserverURL,
oPersonalCipherPreferences,
oPersonalDigestPreferences,
oPersonalCompressPreferences,
oAgentProgram,
oKeyboxdProgram,
oDirmngrProgram,
oDisableDirmngr,
oDisplay,
oTTYname,
oTTYtype,
oLCctype,
oLCmessages,
oXauthority,
oGroup,
oUnGroup,
oNoGroups,
oStrict,
oNoStrict,
oMangleDosFilenames,
oNoMangleDosFilenames,
oEnableProgressFilter,
oMultifile,
oKeyidFormat,
oExitOnStatusWriteError,
oLimitCardInsertTries,
oReaderPort,
octapiDriver,
opcscDriver,
oDisableCCID,
oRequireCrossCert,
oNoRequireCrossCert,
oAutoKeyLocate,
oNoAutoKeyLocate,
oEnableLargeRSA,
oDisableLargeRSA,
oEnableDSA2,
oDisableDSA2,
oAllowWeakDigestAlgos,
oAllowWeakKeySignatures,
oFakedSystemTime,
oNoAutostart,
oPrintDANERecords,
oTOFUDefaultPolicy,
oTOFUDBFormat,
oDefaultNewKeyAlgo,
oWeakDigest,
oUnwrap,
oOnlySignTextIDs,
oDisableSignerUID,
oSender,
oKeyOrigin,
oRequestOrigin,
oNoSymkeyCache,
oUseOnlyOpenPGPCard,
oFullTimestrings,
oIncludeKeyBlock,
oNoIncludeKeyBlock,
oChUid,
oForceSignKey,
oForbidGenKey,
oRequireCompliance,
oCompatibilityFlags,
oAddDesigRevoker,
oAssertSigner,
oKbxBufferSize,
oNoop
};
static gpgrt_opt_t opts[] = {
ARGPARSE_group (300, N_("@Commands:\n ")),
ARGPARSE_c (aSign, "sign", N_("make a signature")),
ARGPARSE_c (aClearsign, "clear-sign", N_("make a clear text signature")),
ARGPARSE_c (aClearsign, "clearsign", "@"),
ARGPARSE_c (aDetachedSign, "detach-sign", N_("make a detached signature")),
ARGPARSE_c (aEncr, "encrypt", N_("encrypt data")),
ARGPARSE_c (aEncrFiles, "encrypt-files", "@"),
ARGPARSE_c (aSym, "symmetric", N_("encryption only with symmetric cipher")),
ARGPARSE_c (aStore, "store", "@"),
ARGPARSE_c (aDecrypt, "decrypt", N_("decrypt data (default)")),
ARGPARSE_c (aDecryptFiles, "decrypt-files", "@"),
ARGPARSE_c (aVerify, "verify" , N_("verify a signature")),
ARGPARSE_c (aVerifyFiles, "verify-files" , "@" ),
ARGPARSE_c (aListKeys, "list-keys", N_("list keys")),
ARGPARSE_c (aListKeys, "list-public-keys", "@" ),
ARGPARSE_c (aListSigs, "list-signatures", N_("list keys and signatures")),
ARGPARSE_c (aListSigs, "list-sigs", "@"),
ARGPARSE_c (aCheckKeys, "check-signatures",
N_("list and check key signatures")),
ARGPARSE_c (aCheckKeys, "check-sigs", "@"),
ARGPARSE_c (oFingerprint, "fingerprint", N_("list keys and fingerprints")),
ARGPARSE_c (aListSecretKeys, "list-secret-keys", N_("list secret keys")),
ARGPARSE_c (aKeygen, "generate-key",
N_("generate a new key pair")),
ARGPARSE_c (aKeygen, "gen-key", "@"),
ARGPARSE_c (aQuickKeygen, "quick-generate-key" ,
N_("quickly generate a new key pair")),
ARGPARSE_c (aQuickKeygen, "quick-gen-key", "@"),
ARGPARSE_c (aQuickAddUid, "quick-add-uid",
N_("quickly add a new user-id")),
ARGPARSE_c (aQuickAddUid, "quick-adduid", "@"),
ARGPARSE_c (aQuickAddKey, "quick-add-key", "@"),
ARGPARSE_c (aQuickAddKey, "quick-addkey", "@"),
ARGPARSE_c (aQuickAddADSK, "quick-add-adsk", "@"),
ARGPARSE_c (aQuickRevUid, "quick-revoke-uid",
N_("quickly revoke a user-id")),
ARGPARSE_c (aQuickRevUid, "quick-revuid", "@"),
ARGPARSE_c (aQuickSetExpire, "quick-set-expire",
N_("quickly set a new expiration date")),
ARGPARSE_c (aQuickSetPrimaryUid, "quick-set-primary-uid", "@"),
ARGPARSE_c (aQuickUpdatePref, "quick-update-pref", "@"),
ARGPARSE_c (aFullKeygen, "full-generate-key" ,
N_("full featured key pair generation")),
ARGPARSE_c (aFullKeygen, "full-gen-key", "@"),
ARGPARSE_c (aGenRevoke, "generate-revocation",
N_("generate a revocation certificate")),
ARGPARSE_c (aGenRevoke, "gen-revoke", "@"),
ARGPARSE_c (aDeleteKeys,"delete-keys",
N_("remove keys from the public keyring")),
ARGPARSE_c (aDeleteSecretKeys, "delete-secret-keys",
N_("remove keys from the secret keyring")),
ARGPARSE_c (aQuickSignKey, "quick-sign-key" ,
N_("quickly sign a key")),
ARGPARSE_c (aQuickLSignKey, "quick-lsign-key",
N_("quickly sign a key locally")),
ARGPARSE_c (aQuickRevSig, "quick-revoke-sig" ,
N_("quickly revoke a key signature")),
ARGPARSE_c (aSignKey, "sign-key" ,N_("sign a key")),
ARGPARSE_c (aLSignKey, "lsign-key" ,N_("sign a key locally")),
ARGPARSE_c (aEditKey, "edit-key" ,N_("sign or edit a key")),
ARGPARSE_c (aEditKey, "key-edit" ,"@"),
ARGPARSE_c (aPasswd, "change-passphrase", N_("change a passphrase")),
ARGPARSE_c (aPasswd, "passwd", "@"),
ARGPARSE_c (aDesigRevoke, "generate-designated-revocation", "@"),
ARGPARSE_c (aDesigRevoke, "desig-revoke","@" ),
ARGPARSE_c (aExport, "export" , N_("export keys") ),
ARGPARSE_c (aSendKeys, "send-keys" , N_("export keys to a keyserver") ),
ARGPARSE_c (aRecvKeys, "receive-keys" , N_("import keys from a keyserver") ),
ARGPARSE_c (aRecvKeys, "recv-keys" , "@"),
ARGPARSE_c (aSearchKeys, "search-keys" ,
N_("search for keys on a keyserver") ),
ARGPARSE_c (aRefreshKeys, "refresh-keys",
N_("update all keys from a keyserver")),
ARGPARSE_c (aLocateKeys, "locate-keys", "@"),
ARGPARSE_c (aLocateExtKeys, "locate-external-keys", "@"),
ARGPARSE_c (aFetchKeys, "fetch-keys" , "@" ),
ARGPARSE_c (aShowKeys, "show-keys" , "@" ),
ARGPARSE_c (aExportSecret, "export-secret-keys" , "@" ),
ARGPARSE_c (aExportSecretSub, "export-secret-subkeys" , "@" ),
ARGPARSE_c (aExportSshKey, "export-ssh-key", "@" ),
ARGPARSE_c (aExportSecretSshKey, "export-secret-ssh-key", "@" ),
ARGPARSE_c (aImport, "import", N_("import/merge keys")),
ARGPARSE_c (aFastImport, "fast-import", "@"),
#ifdef ENABLE_CARD_SUPPORT
ARGPARSE_c (aCardStatus, "card-status", N_("print the card status")),
ARGPARSE_c (aCardEdit, "edit-card", N_("change data on a card")),
ARGPARSE_c (aCardEdit, "card-edit", "@"),
ARGPARSE_c (aChangePIN, "change-pin", N_("change a card's PIN")),
#endif
ARGPARSE_c (aListConfig, "list-config", "@"),
ARGPARSE_c (aListGcryptConfig, "list-gcrypt-config", "@"),
ARGPARSE_c (aGPGConfList, "gpgconf-list", "@" ),
ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@" ),
ARGPARSE_c (aListPackets, "list-packets","@"),
#ifndef NO_TRUST_MODELS
ARGPARSE_c (aExportOwnerTrust, "export-ownertrust", "@"),
ARGPARSE_c (aImportOwnerTrust, "import-ownertrust", "@"),
ARGPARSE_c (aUpdateTrustDB,"update-trustdb",
N_("update the trust database")),
ARGPARSE_c (aCheckTrustDB, "check-trustdb", "@"),
ARGPARSE_c (aFixTrustDB, "fix-trustdb", "@"),
ARGPARSE_c (aListTrustDB, "list-trustdb", "@"),
#endif
ARGPARSE_c (aDeArmor, "dearmor", "@"),
ARGPARSE_c (aDeArmor, "dearmour", "@"),
ARGPARSE_c (aEnArmor, "enarmor", "@"),
ARGPARSE_c (aEnArmor, "enarmour", "@"),
ARGPARSE_c (aPrintMD, "print-md", N_("print message digests")),
ARGPARSE_c (aPrintMDs, "print-mds", "@"), /* old */
ARGPARSE_c (aPrimegen, "gen-prime", "@" ),
ARGPARSE_c (aGenRandom,"gen-random", "@" ),
ARGPARSE_c (aServer, "server", N_("run in server mode")),
ARGPARSE_c (aTOFUPolicy, "tofu-policy",
N_("|VALUE|set the TOFU policy for a key")),
/* Not yet used:
ARGPARSE_c (aListTrustPath, "list-trust-path", "@"), */
ARGPARSE_c (aDeleteSecretAndPublicKeys,
"delete-secret-and-public-keys", "@"),
ARGPARSE_c (aRebuildKeydbCaches, "rebuild-keydb-caches", "@"),
ARGPARSE_c (aListKeys, "list-key", "@"), /* alias */
ARGPARSE_c (aListSigs, "list-sig", "@"), /* alias */
ARGPARSE_c (aCheckKeys, "check-sig", "@"), /* alias */
ARGPARSE_c (aShowKeys, "show-key", "@"), /* alias */
ARGPARSE_header ("Monitor", N_("Options controlling the diagnostic output")),
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"),
ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
ARGPARSE_s_n (oNoTTY, "no-tty", "@"),
ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"),
ARGPARSE_s_s (oDebug, "debug", "@"),
ARGPARSE_s_s (oDebugLevel, "debug-level", "@"),
ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
ARGPARSE_s_n (oDebugIOLBF, "debug-iolbf", "@"),
ARGPARSE_s_u (oDebugSetIobufSize, "debug-set-iobuf-size", "@"),
ARGPARSE_s_u (oDebugAllowLargeChunks, "debug-allow-large-chunks", "@"),
ARGPARSE_s_s (oDisplayCharset, "display-charset", "@"),
ARGPARSE_s_s (oDisplayCharset, "charset", "@"),
ARGPARSE_conffile (oOptions, "options", N_("|FILE|read options from FILE")),
ARGPARSE_noconffile (oNoOptions, "no-options", "@"),
ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"),
ARGPARSE_s_s (oLoggerFile, "log-file",
N_("|FILE|write server mode logs to FILE")),
ARGPARSE_s_s (oLoggerFile, "logger-file", "@"), /* 1.4 compatibility. */
ARGPARSE_s_n (oLogTime, "log-time", "@"),
ARGPARSE_header ("Configuration",
N_("Options controlling the configuration")),
ARGPARSE_s_s (oHomedir, "homedir", "@"),
ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"),
ARGPARSE_s_s (oDefaultKey, "default-key",
N_("|NAME|use NAME as default secret key")),
ARGPARSE_s_s (oEncryptTo, "encrypt-to",
N_("|NAME|encrypt to user ID NAME as well")),
ARGPARSE_s_n (oNoEncryptTo, "no-encrypt-to", "@"),
ARGPARSE_s_s (oHiddenEncryptTo, "hidden-encrypt-to", "@"),
ARGPARSE_s_n (oEncryptToDefaultKey, "encrypt-to-default-key", "@"),
ARGPARSE_s_s (oDefRecipient, "default-recipient", "@"),
ARGPARSE_s_n (oDefRecipientSelf, "default-recipient-self", "@"),
ARGPARSE_s_n (oNoDefRecipient, "no-default-recipient", "@"),
ARGPARSE_s_s (oGroup, "group",
N_("|SPEC|set up email aliases")),
ARGPARSE_s_s (oUnGroup, "ungroup", "@"),
ARGPARSE_s_n (oNoGroups, "no-groups", "@"),
ARGPARSE_s_s (oCompliance, "compliance", "@"),
ARGPARSE_s_n (oGnuPG, "gnupg", "@"),
ARGPARSE_s_n (oGnuPG, "no-pgp2", "@"),
ARGPARSE_s_n (oGnuPG, "no-pgp6", "@"),
ARGPARSE_s_n (oGnuPG, "no-pgp7", "@"),
ARGPARSE_s_n (oGnuPG, "no-pgp8", "@"),
ARGPARSE_s_n (oRFC2440, "rfc2440", "@"),
ARGPARSE_s_n (oRFC4880, "rfc4880", "@"),
ARGPARSE_s_n (oOpenPGP, "openpgp", N_("use strict OpenPGP behavior")),
ARGPARSE_s_n (oPGP7, "pgp6", "@"),
ARGPARSE_s_n (oPGP7, "pgp7", "@"),
ARGPARSE_s_n (oPGP8, "pgp8", "@"),
ARGPARSE_s_s (oDefaultNewKeyAlgo, "default-new-key-algo", "@"),
ARGPARSE_p_u (oMinRSALength, "min-rsa-length", "@"),
#ifndef NO_TRUST_MODELS
ARGPARSE_s_n (oAlwaysTrust, "always-trust", "@"),
#endif
ARGPARSE_s_s (oTrustModel, "trust-model", "@"),
ARGPARSE_s_s (oPhotoViewer, "photo-viewer", "@"),
ARGPARSE_s_s (oKnownNotation, "known-notation", "@"),
ARGPARSE_s_s (oAgentProgram, "agent-program", "@"),
ARGPARSE_s_s (oKeyboxdProgram, "keyboxd-program", "@"),
ARGPARSE_s_s (oDirmngrProgram, "dirmngr-program", "@"),
ARGPARSE_s_n (oExitOnStatusWriteError, "exit-on-status-write-error", "@"),
ARGPARSE_s_i (oLimitCardInsertTries, "limit-card-insert-tries", "@"),
ARGPARSE_s_n (oEnableProgressFilter, "enable-progress-filter", "@"),
ARGPARSE_s_s (oTempDir, "temp-directory", "@"),
ARGPARSE_s_s (oExecPath, "exec-path", "@"),
ARGPARSE_s_n (oExpert, "expert", "@"),
ARGPARSE_s_n (oNoExpert, "no-expert", "@"),
ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"),
ARGPARSE_s_n (oRequireSecmem, "require-secmem", "@"),
ARGPARSE_s_n (oNoRequireSecmem, "no-require-secmem", "@"),
ARGPARSE_s_n (oNoPermissionWarn, "no-permission-warning", "@"),
ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
ARGPARSE_s_n (oInteractive, "interactive", N_("prompt before overwriting")),
ARGPARSE_s_s (oDefSigExpire, "default-sig-expire", "@"),
ARGPARSE_s_n (oAskSigExpire, "ask-sig-expire", "@"),
ARGPARSE_s_n (oNoAskSigExpire, "no-ask-sig-expire", "@"),
ARGPARSE_s_s (oDefCertExpire, "default-cert-expire", "@"),
ARGPARSE_s_n (oAskCertExpire, "ask-cert-expire", "@"),
ARGPARSE_s_n (oNoAskCertExpire, "no-ask-cert-expire", "@"),
ARGPARSE_s_i (oDefCertLevel, "default-cert-level", "@"),
ARGPARSE_s_i (oMinCertLevel, "min-cert-level", "@"),
ARGPARSE_s_n (oAskCertLevel, "ask-cert-level", "@"),
ARGPARSE_s_n (oNoAskCertLevel, "no-ask-cert-level", "@"),
ARGPARSE_s_n (oOnlySignTextIDs, "only-sign-text-ids", "@"),
ARGPARSE_s_n (oEnableLargeRSA, "enable-large-rsa", "@"),
ARGPARSE_s_n (oDisableLargeRSA, "disable-large-rsa", "@"),
ARGPARSE_s_n (oEnableDSA2, "enable-dsa2", "@"),
ARGPARSE_s_n (oDisableDSA2, "disable-dsa2", "@"),
ARGPARSE_s_s (oPersonalCipherPreferences, "personal-cipher-preferences","@"),
ARGPARSE_s_s (oPersonalDigestPreferences, "personal-digest-preferences","@"),
ARGPARSE_s_s (oPersonalCompressPreferences,
"personal-compress-preferences", "@"),
ARGPARSE_s_s (oDefaultPreferenceList, "default-preference-list", "@"),
ARGPARSE_s_s (oDefaultKeyserverURL, "default-keyserver-url", "@"),
ARGPARSE_s_n (oNoExpensiveTrustChecks, "no-expensive-trust-checks", "@"),
ARGPARSE_s_n (oAllowNonSelfsignedUID, "allow-non-selfsigned-uid", "@"),
ARGPARSE_s_n (oNoAllowNonSelfsignedUID, "no-allow-non-selfsigned-uid", "@"),
ARGPARSE_s_n (oAllowFreeformUID, "allow-freeform-uid", "@"),
ARGPARSE_s_n (oNoAllowFreeformUID, "no-allow-freeform-uid", "@"),
ARGPARSE_s_n (oPreservePermissions, "preserve-permissions", "@"),
ARGPARSE_s_i (oDefCertLevel, "default-cert-check-level", "@"), /* old */
ARGPARSE_s_s (oTOFUDefaultPolicy, "tofu-default-policy", "@"),
ARGPARSE_s_n (oLockOnce, "lock-once", "@"),
ARGPARSE_s_n (oLockMultiple, "lock-multiple", "@"),
ARGPARSE_s_n (oLockNever, "lock-never", "@"),
ARGPARSE_s_s (oCompressAlgo,"compress-algo", "@"),
ARGPARSE_s_s (oCompressAlgo, "compression-algo", "@"), /* Alias */
ARGPARSE_s_n (oBZ2DecompressLowmem, "bzip2-decompress-lowmem", "@"),
ARGPARSE_s_i (oCompletesNeeded, "completes-needed", "@"),
ARGPARSE_s_i (oMarginalsNeeded, "marginals-needed", "@"),
ARGPARSE_s_i (oMaxCertDepth, "max-cert-depth", "@" ),
#ifndef NO_TRUST_MODELS
ARGPARSE_s_s (oTrustDBName, "trustdb-name", "@"),
ARGPARSE_s_n (oAutoCheckTrustDB, "auto-check-trustdb", "@"),
ARGPARSE_s_n (oNoAutoCheckTrustDB, "no-auto-check-trustdb", "@"),
ARGPARSE_s_s (oForceOwnertrust, "force-ownertrust", "@"),
ARGPARSE_s_n (oNoAutoTrustNewKey, "no-auto-trust-new-key", "@"),
#endif
ARGPARSE_s_s (oAddDesigRevoker, "add-desig-revoker", "@"),
ARGPARSE_s_s (oAssertSigner, "assert-signer", "@"),
ARGPARSE_header ("Input", N_("Options controlling the input")),
ARGPARSE_s_n (oMultifile, "multifile", "@"),
ARGPARSE_s_s (oInputSizeHint, "input-size-hint", "@"),
ARGPARSE_s_n (oUtf8Strings, "utf8-strings", "@"),
ARGPARSE_s_n (oNoUtf8Strings, "no-utf8-strings", "@"),
ARGPARSE_p_u (oSetFilesize, "set-filesize", "@"),
ARGPARSE_s_n (oNoLiteral, "no-literal", "@"),
ARGPARSE_s_s (oSetNotation, "set-notation", "@"),
ARGPARSE_s_s (oSigNotation, "sig-notation", "@"),
ARGPARSE_s_s (oCertNotation, "cert-notation", "@"),
ARGPARSE_s_s (oSetPolicyURL, "set-policy-url", "@"),
ARGPARSE_s_s (oSigPolicyURL, "sig-policy-url", "@"),
ARGPARSE_s_s (oCertPolicyURL, "cert-policy-url", "@"),
ARGPARSE_s_s (oSigKeyserverURL, "sig-keyserver-url", "@"),
ARGPARSE_header ("Output", N_("Options controlling the output")),
ARGPARSE_s_n (oArmor, "armor", N_("create ascii armored output")),
ARGPARSE_s_n (oArmor, "armour", "@"),
ARGPARSE_s_n (oNoArmor, "no-armor", "@"),
ARGPARSE_s_n (oNoArmor, "no-armour", "@"),
ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
ARGPARSE_p_u (oMaxOutput, "max-output", "@"),
ARGPARSE_s_s (oComment, "comment", "@"),
ARGPARSE_s_n (oDefaultComment, "default-comment", "@"),
ARGPARSE_s_n (oNoComments, "no-comments", "@"),
ARGPARSE_s_n (oEmitVersion, "emit-version", "@"),
ARGPARSE_s_n (oNoEmitVersion, "no-emit-version", "@"),
ARGPARSE_s_n (oNoEmitVersion, "no-version", "@"), /* alias */
ARGPARSE_s_n (oNotDashEscaped, "not-dash-escaped", "@"),
ARGPARSE_s_n (oEscapeFrom, "escape-from-lines", "@"),
ARGPARSE_s_n (oNoEscapeFrom, "no-escape-from-lines", "@"),
ARGPARSE_s_n (oMimemode, "mimemode", "@"),
ARGPARSE_s_n (oTextmodeShort, NULL, "@"),
ARGPARSE_s_n (oTextmode, "textmode", N_("use canonical text mode")),
ARGPARSE_s_n (oNoTextmode, "no-textmode", "@"),
ARGPARSE_s_s (oSetFilename, "set-filename", "@"),
ARGPARSE_s_n (oForYourEyesOnly, "for-your-eyes-only", "@"),
ARGPARSE_s_n (oNoForYourEyesOnly, "no-for-your-eyes-only", "@"),
ARGPARSE_s_n (oShowNotation, "show-notation", "@"),
ARGPARSE_s_n (oNoShowNotation, "no-show-notation", "@"),
ARGPARSE_s_n (oShowSessionKey, "show-session-key", "@"),
ARGPARSE_s_n (oUseEmbeddedFilename, "use-embedded-filename", "@"),
ARGPARSE_s_n (oNoUseEmbeddedFilename, "no-use-embedded-filename", "@"),
ARGPARSE_s_n (oUnwrap, "unwrap", "@"),
ARGPARSE_s_n (oMangleDosFilenames, "mangle-dos-filenames", "@"),
ARGPARSE_s_n (oNoMangleDosFilenames, "no-mangle-dos-filenames", "@"),
ARGPARSE_s_i (oChunkSize, "chunk-size", "@"),
ARGPARSE_s_n (oNoSymkeyCache, "no-symkey-cache", "@"),
ARGPARSE_s_n (oSkipVerify, "skip-verify", "@"),
ARGPARSE_s_n (oListOnly, "list-only", "@"),
ARGPARSE_s_i (oCompress, NULL,
N_("|N|set compress level to N (0 disables)")),
ARGPARSE_s_i (oCompressLevel, "compress-level", "@"),
ARGPARSE_s_i (oBZ2CompressLevel, "bzip2-compress-level", "@"),
ARGPARSE_s_n (oDisableSignerUID, "disable-signer-uid", "@"),
ARGPARSE_header ("ImportExport",
N_("Options controlling key import and export")),
ARGPARSE_s_s (oAutoKeyLocate, "auto-key-locate",
N_("|MECHANISMS|use MECHANISMS to locate keys by mail address")),
ARGPARSE_s_n (oNoAutoKeyLocate, "no-auto-key-locate", "@"),
ARGPARSE_s_n (oAutoKeyImport, "auto-key-import",
N_("import missing key from a signature")),
ARGPARSE_s_n (oNoAutoKeyImport, "no-auto-key-import", "@"),
ARGPARSE_s_n (oAutoKeyRetrieve, "auto-key-retrieve", "@"),
ARGPARSE_s_n (oNoAutoKeyRetrieve, "no-auto-key-retrieve", "@"),
ARGPARSE_s_n (oIncludeKeyBlock, "include-key-block",
N_("include the public key in signatures")),
ARGPARSE_s_n (oNoIncludeKeyBlock, "no-include-key-block", "@"),
ARGPARSE_s_n (oDisableDirmngr, "disable-dirmngr",
N_("disable all access to the dirmngr")),
ARGPARSE_s_s (oKeyServer, "keyserver", "@"), /* Deprecated. */
ARGPARSE_s_s (oKeyServerOptions, "keyserver-options", "@"),
ARGPARSE_s_s (oKeyOrigin, "key-origin", "@"),
ARGPARSE_s_s (oImportOptions, "import-options", "@"),
ARGPARSE_s_s (oImportFilter, "import-filter", "@"),
ARGPARSE_s_s (oExportOptions, "export-options", "@"),
ARGPARSE_s_s (oExportFilter, "export-filter", "@"),
ARGPARSE_s_n (oMergeOnly, "merge-only", "@" ),
ARGPARSE_s_n (oAllowSecretKeyImport, "allow-secret-key-import", "@"),
ARGPARSE_header ("Keylist", N_("Options controlling key listings")),
ARGPARSE_s_s (oListOptions, "list-options", "@"),
ARGPARSE_s_s (oListFilter, "list-filter", "@"),
ARGPARSE_s_n (oFullTimestrings, "full-timestrings", "@"),
ARGPARSE_s_n (oShowPhotos, "show-photos", "@"),
ARGPARSE_s_n (oNoShowPhotos, "no-show-photos", "@"),
ARGPARSE_s_n (oShowPolicyURL, "show-policy-url", "@"),
ARGPARSE_s_n (oNoShowPolicyURL, "no-show-policy-url", "@"),
ARGPARSE_s_n (oWithColons, "with-colons", "@"),
ARGPARSE_s_n (oWithTofuInfo,"with-tofu-info", "@"),
ARGPARSE_s_n (oWithKeyData,"with-key-data", "@"),
ARGPARSE_s_n (oWithSigList,"with-sig-list", "@"),
ARGPARSE_s_n (oWithSigCheck,"with-sig-check", "@"),
ARGPARSE_s_n (oWithFingerprint, "with-fingerprint", "@"),
ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprint", "@"),
ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprints", "@"),
ARGPARSE_s_n (oWithICAOSpelling, "with-icao-spelling", "@"),
ARGPARSE_s_n (oWithKeygrip, "with-keygrip", "@"),
ARGPARSE_s_n (oWithKeyScreening,"with-key-screening", "@"),
ARGPARSE_s_n (oWithSecret, "with-secret", "@"),
ARGPARSE_s_n (oWithWKDHash, "with-wkd-hash", "@"),
ARGPARSE_s_n (oWithKeyOrigin, "with-key-origin", "@"),
ARGPARSE_s_n (oFastListMode, "fast-list-mode", "@"),
ARGPARSE_s_n (oFixedListMode, "fixed-list-mode", "@"),
ARGPARSE_s_n (oLegacyListMode, "legacy-list-mode", "@"),
ARGPARSE_s_n (oPrintDANERecords, "print-dane-records", "@"),
ARGPARSE_s_s (oKeyidFormat, "keyid-format", "@"),
ARGPARSE_s_n (oShowKeyring, "show-keyring", "@"),
ARGPARSE_header (NULL, N_("Options to specify keys")),
ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
ARGPARSE_s_s (oHiddenRecipient, "hidden-recipient", "@"),
ARGPARSE_s_s (oRecipientFile, "recipient-file", "@"),
ARGPARSE_s_s (oHiddenRecipientFile, "hidden-recipient-file", "@"),
ARGPARSE_s_s (oRecipient, "remote-user", "@"), /* (old option name) */
ARGPARSE_s_n (oThrowKeyids, "throw-keyids", "@"),
ARGPARSE_s_n (oNoThrowKeyids, "no-throw-keyids", "@"),
ARGPARSE_s_s (oLocalUser, "local-user",
N_("|USER-ID|use USER-ID to sign or decrypt")),
ARGPARSE_s_s (oTrustedKey, "trusted-key", "@"),
ARGPARSE_s_s (oSender, "sender", "@"),
ARGPARSE_s_s (oTrySecretKey, "try-secret-key", "@"),
ARGPARSE_s_n (oTryAllSecrets, "try-all-secrets", "@"),
ARGPARSE_s_n (oNoDefKeyring, "no-default-keyring", "@"),
ARGPARSE_s_n (oNoKeyring, "no-keyring", "@"),
ARGPARSE_s_s (oKeyring, "keyring", "@"),
ARGPARSE_s_s (oPrimaryKeyring, "primary-keyring", "@"),
ARGPARSE_s_s (oSecretKeyring, "secret-keyring", "@"),
ARGPARSE_s_n (oSkipHiddenRecipients, "skip-hidden-recipients", "@"),
ARGPARSE_s_n (oNoSkipHiddenRecipients, "no-skip-hidden-recipients", "@"),
ARGPARSE_s_s (oOverrideSessionKey, "override-session-key", "@"),
ARGPARSE_s_i (oOverrideSessionKeyFD, "override-session-key-fd", "@"),
ARGPARSE_header ("Security", N_("Options controlling the security")),
ARGPARSE_s_i (oS2KMode, "s2k-mode", "@"),
ARGPARSE_s_s (oS2KDigest, "s2k-digest-algo", "@"),
ARGPARSE_s_s (oS2KCipher, "s2k-cipher-algo", "@"),
ARGPARSE_s_i (oS2KCount, "s2k-count", "@"),
ARGPARSE_s_n (oForceAEAD, "force-ocb", "@"),
ARGPARSE_s_n (oForceAEAD, "force-aead", "@"), /*(old name)*/
ARGPARSE_s_n (oRequireCrossCert, "require-backsigs", "@"),
ARGPARSE_s_n (oRequireCrossCert, "require-cross-certification", "@"),
ARGPARSE_s_n (oNoRequireCrossCert, "no-require-backsigs", "@"),
ARGPARSE_s_n (oNoRequireCrossCert, "no-require-cross-certification", "@"),
/* Options to override new security defaults. */
ARGPARSE_s_n (oAllowWeakKeySignatures, "allow-weak-key-signatures", "@"),
ARGPARSE_s_n (oAllowWeakDigestAlgos, "allow-weak-digest-algos", "@"),
ARGPARSE_s_n (oAllowOldCipherAlgos, "allow-old-cipher-algos", "@"),
ARGPARSE_s_s (oWeakDigest, "weak-digest","@"),
ARGPARSE_s_s (oVerifyOptions, "verify-options", "@"),
ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"),
ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"),
ARGPARSE_s_n (oNoSigCache, "no-sig-cache", "@"),
ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", "@"),
ARGPARSE_s_n (oIgnoreValidFrom, "ignore-valid-from", "@"),
ARGPARSE_s_n (oIgnoreCrcError, "ignore-crc-error", "@"),
ARGPARSE_s_n (oIgnoreMDCError, "ignore-mdc-error", "@"),
ARGPARSE_s_s (oDisableCipherAlgo, "disable-cipher-algo", "@"),
ARGPARSE_s_s (oDisablePubkeyAlgo, "disable-pubkey-algo", "@"),
ARGPARSE_s_s (oCipherAlgo, "cipher-algo", "@"),
ARGPARSE_s_s (oDigestAlgo, "digest-algo", "@"),
ARGPARSE_s_s (oCertDigestAlgo, "cert-digest-algo", "@"),
ARGPARSE_header (NULL, N_("Options for unattended use")),
ARGPARSE_s_n (oBatch, "batch", "@"),
ARGPARSE_s_n (oNoBatch, "no-batch", "@"),
ARGPARSE_s_n (oAnswerYes, "yes", "@"),
ARGPARSE_s_n (oAnswerNo, "no", "@"),
ARGPARSE_s_i (oStatusFD, "status-fd", "@"),
ARGPARSE_s_s (oStatusFile, "status-file", "@"),
ARGPARSE_s_i (oAttributeFD, "attribute-fd", "@"),
ARGPARSE_s_s (oAttributeFile, "attribute-file", "@"),
ARGPARSE_s_i (oCommandFD, "command-fd", "@"),
ARGPARSE_s_s (oCommandFile, "command-file", "@"),
ARGPARSE_o_s (oPassphrase, "passphrase", "@"),
ARGPARSE_s_i (oPassphraseFD, "passphrase-fd", "@"),
ARGPARSE_s_s (oPassphraseFile, "passphrase-file", "@"),
ARGPARSE_s_i (oPassphraseRepeat,"passphrase-repeat", "@"),
ARGPARSE_s_s (oPinentryMode, "pinentry-mode", "@"),
ARGPARSE_s_n (oForceSignKey, "force-sign-key", "@"),
ARGPARSE_header (NULL, N_("Other options")),
ARGPARSE_s_s (oRequestOrigin, "request-origin", "@"),
ARGPARSE_s_s (oDisplay, "display", "@"),
ARGPARSE_s_s (oTTYname, "ttyname", "@"),
ARGPARSE_s_s (oTTYtype, "ttytype", "@"),
ARGPARSE_s_s (oLCctype, "lc-ctype", "@"),
ARGPARSE_s_s (oLCmessages, "lc-messages","@"),
ARGPARSE_s_s (oXauthority, "xauthority", "@"),
ARGPARSE_s_s (oChUid, "chuid", "@"),
ARGPARSE_s_n (oNoAutostart, "no-autostart", "@"),
ARGPARSE_s_n (oUseKeyboxd, "use-keyboxd", "@"),
ARGPARSE_s_n (oForbidGenKey, "forbid-gen-key", "@"),
ARGPARSE_s_n (oRequireCompliance, "require-compliance", "@"),
ARGPARSE_s_s (oCompatibilityFlags, "compatibility-flags", "@"),
/* Options which can be used in special circumstances. They are not
* published and we hope they are never required. */
ARGPARSE_s_n (oUseOnlyOpenPGPCard, "use-only-openpgp-card", "@"),
/* Esoteric compatibility options. */
ARGPARSE_s_n (oRFC2440Text, "rfc2440-text", "@"),
ARGPARSE_s_n (oNoRFC2440Text, "no-rfc2440-text", "@"),
ARGPARSE_p_u (oKbxBufferSize, "kbx-buffer-size", "@"),
ARGPARSE_s_n (oQuickRandom, "debug-quick-random", "@"),
ARGPARSE_s_n (oDebugIgnoreExpiration, "debug-ignore-expiration", "@"),
ARGPARSE_header (NULL, ""), /* Stop the header group. */
/* Aliases. I constantly mistype these, and assume other people do
as well. */
ARGPARSE_s_s (oPersonalCipherPreferences, "personal-cipher-prefs", "@"),
ARGPARSE_s_s (oPersonalCompressPreferences, "personal-compress-prefs", "@"),
/* These two are aliases to help users of the PGP command line
product use gpg with minimal pain. Many commands are common
already as they seem to have borrowed commands from us. Now I'm
returning the favor. */
ARGPARSE_s_s (oLocalUser, "sign-with", "@"),
ARGPARSE_s_s (oRecipient, "user", "@"),
/* Dummy options with warnings. */
ARGPARSE_s_n (oUseAgent, "use-agent", "@"),
ARGPARSE_s_n (oNoUseAgent, "no-use-agent", "@"),
ARGPARSE_s_s (oGpgAgentInfo, "gpg-agent-info", "@"),
ARGPARSE_s_s (oReaderPort, "reader-port", "@"),
ARGPARSE_s_s (octapiDriver, "ctapi-driver", "@"),
ARGPARSE_s_s (opcscDriver, "pcsc-driver", "@"),
ARGPARSE_s_n (oDisableCCID, "disable-ccid", "@"),
ARGPARSE_s_n (oHonorHttpProxy, "honor-http-proxy", "@"),
ARGPARSE_s_s (oTOFUDBFormat, "tofu-db-format", "@"),
/* Dummy options. */
ARGPARSE_ignore (oStrict, "strict"),
ARGPARSE_ignore (oNoStrict, "no-strict"),
ARGPARSE_ignore (oLoadExtension, "load-extension"), /* from 1.4. */
ARGPARSE_s_n (oNoop, "sk-comments", "@"),
ARGPARSE_s_n (oNoop, "no-sk-comments", "@"),
ARGPARSE_s_n (oNoop, "compress-keys", "@"),
ARGPARSE_s_n (oNoop, "compress-sigs", "@"),
ARGPARSE_s_n (oNoop, "force-v3-sigs", "@"),
ARGPARSE_s_n (oNoop, "no-force-v3-sigs", "@"),
ARGPARSE_s_n (oNoop, "force-v4-certs", "@"),
ARGPARSE_s_n (oNoop, "no-force-v4-certs", "@"),
ARGPARSE_s_n (oNoop, "no-mdc-warning", "@"),
ARGPARSE_s_n (oNoop, "force-mdc", "@"),
ARGPARSE_s_n (oNoop, "no-force-mdc", "@"),
ARGPARSE_s_n (oNoop, "disable-mdc", "@"),
ARGPARSE_s_n (oNoop, "no-disable-mdc", "@"),
ARGPARSE_s_n (oNoop, "allow-multisig-verification", "@"),
ARGPARSE_s_n (oNoop, "allow-multiple-messages", "@"),
ARGPARSE_s_n (oNoop, "no-allow-multiple-messages", "@"),
ARGPARSE_s_s (oNoop, "aead-algo", "@"),
ARGPARSE_s_s (oNoop, "personal-aead-preferences","@"),
ARGPARSE_s_n (oNoop, "rfc4880bis", "@"),
ARGPARSE_s_n (oNoop, "override-compliance-check", "@"),
ARGPARSE_group (302, N_(
"@\n(See the man page for a complete listing of all commands and options)\n"
)),
ARGPARSE_group (303, N_("@\nExamples:\n\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
" --clear-sign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n")),
ARGPARSE_end ()
};
/* The list of supported debug flags. */
static struct debug_flags_s debug_flags [] =
{
{ DBG_PACKET_VALUE , "packet" },
{ DBG_MPI_VALUE , "mpi" },
{ DBG_CRYPTO_VALUE , "crypto" },
{ DBG_FILTER_VALUE , "filter" },
{ DBG_IOBUF_VALUE , "iobuf" },
{ DBG_MEMORY_VALUE , "memory" },
{ DBG_CACHE_VALUE , "cache" },
{ DBG_MEMSTAT_VALUE, "memstat" },
{ DBG_TRUST_VALUE , "trust" },
{ DBG_HASHING_VALUE, "hashing" },
{ DBG_IPC_VALUE , "ipc" },
{ DBG_CLOCK_VALUE , "clock" },
{ DBG_LOOKUP_VALUE , "lookup" },
{ DBG_EXTPROG_VALUE, "extprog" },
{ 0, NULL }
};
/* The list of compatibility flags. */
static struct compatibility_flags_s compatibility_flags [] =
{
{ 0, NULL }
};
#ifdef ENABLE_SELINUX_HACKS
#define ALWAYS_ADD_KEYRINGS 1
#else
#define ALWAYS_ADD_KEYRINGS 0
#endif
/* The list of the default AKL methods. */
#define DEFAULT_AKL_LIST "local,wkd"
/* Can be set to true to force gpg to return with EXIT_FAILURE. */
int g10_errors_seen = 0;
/* If opt.assert_signer_list is used and this variabale is not true
* gpg will be forced to return EXIT_FAILURE. */
int assert_signer_true = 0;
static int utf8_strings =
#ifdef HAVE_W32_SYSTEM
1
#else
0
#endif
;
static int maybe_setuid = 1;
static unsigned int opt_set_iobuf_size;
static unsigned int opt_set_iobuf_size_used;
static int opt_log_time;
/* Collection of options used only in this module. */
static struct {
unsigned int forbid_gen_key;
} mopt;
static char *build_list( const char *text, char letter,
const char *(*mapf)(int), int (*chkf)(int) );
static void set_cmd( enum cmd_and_opt_values *ret_cmd,
enum cmd_and_opt_values new_cmd );
static void print_mds( const char *fname, int algo );
static void add_notation_data( const char *string, int which );
static void add_policy_url( const char *string, int which );
static void add_keyserver_url( const char *string, int which );
static void emergency_cleanup (void);
static void read_sessionkey_from_fd (int fd);
/* NPth wrapper function definitions. */
ASSUAN_SYSTEM_NPTH_IMPL;
static char *
make_libversion (const char *libname, const char *(*getfnc)(const char*))
{
const char *s;
char *result;
if (maybe_setuid)
{
gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */
maybe_setuid = 0;
}
s = getfnc (NULL);
result = xmalloc (strlen (libname) + 1 + strlen (s) + 1);
strcpy (stpcpy (stpcpy (result, libname), " "), s);
return result;
}
static int
build_list_pk_test_algo (int algo)
{
/* Show only one "RSA" string. If RSA_E or RSA_S is available RSA
is also available. */
if (algo == PUBKEY_ALGO_RSA_E
|| algo == PUBKEY_ALGO_RSA_S)
return GPG_ERR_DIGEST_ALGO;
return openpgp_pk_test_algo (algo);
}
static const char *
build_list_pk_algo_name (int algo)
{
return openpgp_pk_algo_name (algo);
}
static int
build_list_cipher_test_algo (int algo)
{
return openpgp_cipher_test_algo (algo);
}
static const char *
build_list_cipher_algo_name (int algo)
{
return openpgp_cipher_algo_name (algo);
}
static int
build_list_md_test_algo (int algo)
{
/* By default we do not accept MD5 based signatures. To avoid
confusion we do not announce support for it either. */
if (algo == DIGEST_ALGO_MD5)
return GPG_ERR_DIGEST_ALGO;
return openpgp_md_test_algo (algo);
}
static const char *
build_list_md_algo_name (int algo)
{
return openpgp_md_algo_name (algo);
}
static const char *
my_strusage( int level )
{
static char *digests, *pubkeys, *ciphers, *zips, *ver_gcry;
const char *p;
switch( level ) {
case 9: p = "GPL-3.0-or-later"; break;
case 11: p = "@GPG@ (@GNUPG@)";
break;
case 13: p = VERSION; break;
case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break;
case 17: p = PRINTABLE_OS_NAME; break;
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
case 20:
if (!ver_gcry)
ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
p = ver_gcry;
break;
#ifdef IS_DEVELOPMENT_VERSION
case 25:
p="NOTE: THIS IS A DEVELOPMENT VERSION!";
break;
case 26:
p="It is only intended for test purposes and should NOT be";
break;
case 27:
p="used in a production environment or with production keys!";
break;
#endif
case 1:
case 40: p =
_("Usage: @GPG@ [options] [files] (-h for help)");
break;
case 41: p =
_("Syntax: @GPG@ [options] [files]\n"
"Sign, check, encrypt or decrypt\n"
"Default operation depends on the input data\n");
break;
case 31: p = "\nHome: "; break;
#ifndef __riscos__
case 32: p = gnupg_homedir (); break;
#else /* __riscos__ */
case 32: p = make_filename(gnupg_homedir (), NULL); break;
#endif /* __riscos__ */
case 33: p = _("\nSupported algorithms:\n"); break;
case 34:
if (!pubkeys)
pubkeys = build_list (_("Pubkey: "), 1,
build_list_pk_algo_name,
build_list_pk_test_algo );
p = pubkeys;
break;
case 35:
if( !ciphers )
ciphers = build_list(_("Cipher: "), 'S',
build_list_cipher_algo_name,
build_list_cipher_test_algo );
p = ciphers;
break;
case 37:
if( !digests )
digests = build_list(_("Hash: "), 'H',
build_list_md_algo_name,
build_list_md_test_algo );
p = digests;
break;
case 38:
if( !zips )
zips = build_list(_("Compression: "),'Z',
compress_algo_to_string,
check_compress_algo);
p = zips;
break;
case 95:
p = "1"; /* <-- Enable globbing under Windows (see init.c) */
break;
default: p = NULL;
}
return p;
}
static char *
build_list (const char *text, char letter,
const char * (*mapf)(int), int (*chkf)(int))
{
membuf_t mb;
int indent;
int i, j, len;
int limit;
const char *s;
char *string;
if (maybe_setuid)
gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */
indent = utf8_charcount (text, -1);
len = 0;
init_membuf (&mb, 512);
limit = (letter == 'A')? 4 : 110;
for (i=0; i <= limit; i++ )
{
if (!chkf (i) && (s = mapf (i)))
{
if (mb.len - len > 60)
{
put_membuf_str (&mb, ",\n");
len = mb.len;
for (j=0; j < indent; j++)
put_membuf_str (&mb, " ");
}
else if (mb.len)
put_membuf_str (&mb, ", ");
else
put_membuf_str (&mb, text);
put_membuf_str (&mb, s);
if (opt.verbose && letter)
{
char num[20];
if (letter == 1)
snprintf (num, sizeof num, " (%d)", i);
else
snprintf (num, sizeof num, " (%c%d)", letter, i);
put_membuf_str (&mb, num);
}
}
}
if (mb.len)
put_membuf_str (&mb, "\n");
put_membuf (&mb, "", 1);
string = get_membuf (&mb, NULL);
return xrealloc (string, strlen (string)+1);
}
static void
wrong_args( const char *text)
{
es_fprintf (es_stderr, _("usage: %s [options] %s\n"), GPG_NAME, text);
log_inc_errorcount ();
g10_exit(2);
}
static char *
make_username( const char *string )
{
char *p;
if( utf8_strings )
p = xstrdup(string);
else
p = native_to_utf8( string );
return p;
}
static void
set_opt_session_env (const char *name, const char *value)
{
gpg_error_t err;
err = session_env_setenv (opt.session_env, name, value);
if (err)
log_fatal ("error setting session environment: %s\n",
gpg_strerror (err));
}
/* Setup the debugging. With a LEVEL of NULL only the active debug
flags are propagated to the subsystems. With LEVEL set, a specific
set of debug flags is set; thus overriding all flags already
set. */
static void
set_debug (const char *level)
{
int numok = (level && digitp (level));
int numlvl = numok? atoi (level) : 0;
if (!level)
;
else if (!strcmp (level, "none") || (numok && numlvl < 1))
opt.debug = 0;
else if (!strcmp (level, "basic") || (numok && numlvl <= 2))
opt.debug = DBG_MEMSTAT_VALUE;
else if (!strcmp (level, "advanced") || (numok && numlvl <= 5))
opt.debug = DBG_MEMSTAT_VALUE|DBG_TRUST_VALUE|DBG_EXTPROG_VALUE;
else if (!strcmp (level, "expert") || (numok && numlvl <= 8))
opt.debug = (DBG_MEMSTAT_VALUE|DBG_TRUST_VALUE|DBG_EXTPROG_VALUE
|DBG_CACHE_VALUE|DBG_LOOKUP|DBG_FILTER_VALUE|DBG_PACKET_VALUE);
else if (!strcmp (level, "guru") || numok)
{
opt.debug = ~0;
/* Unless the "guru" string has been used we don't want to allow
hashing debugging. The rationale is that people tend to
select the highest debug value and would then clutter their
disk with debug files which may reveal confidential data. */
if (numok)
opt.debug &= ~(DBG_HASHING_VALUE);
}
else
{
log_error (_("invalid debug-level '%s' given\n"), level);
g10_exit (2);
}
if ((opt.debug & DBG_MEMORY_VALUE))
memory_debug_mode = 1;
if ((opt.debug & DBG_MEMSTAT_VALUE))
memory_stat_debug_mode = 1;
if (DBG_MPI)
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2);
if (DBG_CRYPTO)
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
if ((opt.debug & DBG_IOBUF_VALUE))
iobuf_debug_mode = 1;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
if (opt.debug)
parse_debug_flag (NULL, &opt.debug, debug_flags);
/* Make sure that we are --verbose in debug mode. */
if (opt.debug && !opt.verbose)
opt.verbose = 1;
if (opt.debug && opt.quiet)
opt.quiet = 0;
if (opt_set_iobuf_size || opt_set_iobuf_size_used)
log_debug ("iobuf buffer size is %uk\n",
iobuf_set_buffer_size (opt_set_iobuf_size));
}
/* We set the screen dimensions for UI purposes. Do not allow screens
smaller than 80x24 for the sake of simplicity. */
static void
set_screen_dimensions(void)
{
#ifndef HAVE_W32_SYSTEM
char *str;
str=getenv("COLUMNS");
if(str)
opt.screen_columns=atoi(str);
str=getenv("LINES");
if(str)
opt.screen_lines=atoi(str);
#endif
if(opt.screen_columns<80 || opt.screen_columns>255)
opt.screen_columns=80;
if(opt.screen_lines<24 || opt.screen_lines>255)
opt.screen_lines=24;
}
/* Helper to open a file FNAME either for reading or writing to be
used with --status-file etc functions. Not generally useful but it
avoids the riscos specific functions and well some Windows people
might like it too. Prints an error message and returns -1 on
error. On success the file descriptor is returned. */
static int
open_info_file (const char *fname, int for_write, int binary)
{
#ifdef __riscos__
return riscos_fdopenfile (fname, for_write);
#elif defined (ENABLE_SELINUX_HACKS)
/* We can't allow these even when testing for a secured filename
because files to be secured might not yet been secured. This is
similar to the option file but in that case it is unlikely that
sensitive information may be retrieved by means of error
messages. */
(void)fname;
(void)for_write;
(void)binary;
return -1;
#else
int fd;
if (binary)
binary = MY_O_BINARY;
/* if (is_secured_filename (fname)) */
/* { */
/* fd = -1; */
/* gpg_err_set_errno (EPERM); */
/* } */
/* else */
/* { */
do
{
if (for_write)
fd = gnupg_open (fname, O_CREAT | O_TRUNC | O_WRONLY | binary,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
else
fd = gnupg_open (fname, O_RDONLY | binary, 0);
}
while (fd == -1 && errno == EINTR);
/* } */
if ( fd == -1)
log_error ( for_write? _("can't create '%s': %s\n")
: _("can't open '%s': %s\n"), fname, strerror(errno));
return fd;
#endif
}
static void
set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd )
{
enum cmd_and_opt_values cmd = *ret_cmd;
if( !cmd || cmd == new_cmd )
cmd = new_cmd;
else if( cmd == aSign && new_cmd == aEncr )
cmd = aSignEncr;
else if( cmd == aEncr && new_cmd == aSign )
cmd = aSignEncr;
else if( cmd == aSign && new_cmd == aSym )
cmd = aSignSym;
else if( cmd == aSym && new_cmd == aSign )
cmd = aSignSym;
else if( cmd == aSym && new_cmd == aEncr )
cmd = aEncrSym;
else if( cmd == aEncr && new_cmd == aSym )
cmd = aEncrSym;
else if (cmd == aSignEncr && new_cmd == aSym)
cmd = aSignEncrSym;
else if (cmd == aSignSym && new_cmd == aEncr)
cmd = aSignEncrSym;
else if (cmd == aEncrSym && new_cmd == aSign)
cmd = aSignEncrSym;
else if( ( cmd == aSign && new_cmd == aClearsign )
|| ( cmd == aClearsign && new_cmd == aSign ) )
cmd = aClearsign;
else {
log_error(_("conflicting commands\n"));
g10_exit(2);
}
*ret_cmd = cmd;
}
static void
add_group(char *string)
{
char *name,*value;
struct groupitem *item;
/* Break off the group name */
name=strsep(&string,"=");
if(string==NULL)
{
log_error(_("no = sign found in group definition '%s'\n"),name);
return;
}
trim_trailing_ws(name,strlen(name));
/* Does this group already exist? */
for(item=opt.grouplist;item;item=item->next)
if(strcasecmp(item->name,name)==0)
break;
if(!item)
{
item=xmalloc(sizeof(struct groupitem));
item->name=name;
item->next=opt.grouplist;
item->values=NULL;
opt.grouplist=item;
}
/* Break apart the values */
while ((value= strsep(&string," \t")))
{
if (*value)
add_to_strlist2(&item->values,value,utf8_strings);
}
}
static void
rm_group(char *name)
{
struct groupitem *item,*last=NULL;
trim_trailing_ws(name,strlen(name));
for(item=opt.grouplist;item;last=item,item=item->next)
{
if(strcasecmp(item->name,name)==0)
{
if(last)
last->next=item->next;
else
opt.grouplist=item->next;
free_strlist(item->values);
xfree(item);
break;
}
}
}
/* We need to check three things.
0) The homedir. It must be x00, a directory, and owned by the
user.
1) The options/gpg.conf file. Okay unless it or its containing
directory is group or other writable or not owned by us. Disable
exec in this case.
2) Extensions. Same as #1.
Returns true if the item is unsafe. */
static int
check_permissions (const char *path, int item)
{
#if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM)
static int homedir_cache=-1;
char *tmppath,*dir;
struct stat statbuf,dirbuf;
int homedir=0,ret=0,checkonly=0;
int perm=0,own=0,enc_dir_perm=0,enc_dir_own=0;
if(opt.no_perm_warn)
return 0;
log_assert(item==0 || item==1 || item==2);
/* extensions may attach a path */
if(item==2 && path[0]!=DIRSEP_C)
{
if(strchr(path,DIRSEP_C))
tmppath=make_filename(path,NULL);
else
tmppath=make_filename(gnupg_libdir (),path,NULL);
}
else
tmppath=xstrdup(path);
/* If the item is located in the homedir, but isn't the homedir,
don't continue if we already checked the homedir itself. This is
to avoid user confusion with an extra options file warning which
could be rectified if the homedir itself had proper
permissions. */
if(item!=0 && homedir_cache>-1
&& !ascii_strncasecmp (gnupg_homedir (), tmppath,
strlen (gnupg_homedir ())))
{
ret=homedir_cache;
goto end;
}
/* It's okay if the file or directory doesn't exist */
if (gnupg_stat (tmppath,&statbuf))
{
ret=0;
goto end;
}
/* Now check the enclosing directory. Theoretically, we could walk
this test up to the root directory /, but for the sake of sanity,
I'm stopping at one level down. */
dir=make_dirname(tmppath);
if (gnupg_stat (dir,&dirbuf) || !S_ISDIR (dirbuf.st_mode))
{
/* Weird error */
xfree(dir);
ret=1;
goto end;
}
xfree(dir);
/* Assume failure */
ret=1;
if(item==0)
{
/* The homedir must be x00, a directory, and owned by the user. */
if(S_ISDIR(statbuf.st_mode))
{
if(statbuf.st_uid==getuid())
{
if((statbuf.st_mode & (S_IRWXG|S_IRWXO))==0)
ret=0;
else
perm=1;
}
else
own=1;
homedir_cache=ret;
}
}
else if(item==1 || item==2)
{
/* The options or extension file. Okay unless it or its
containing directory is group or other writable or not owned
by us or root. */
if(S_ISREG(statbuf.st_mode))
{
if(statbuf.st_uid==getuid() || statbuf.st_uid==0)
{
if((statbuf.st_mode & (S_IWGRP|S_IWOTH))==0)
{
/* it's not writable, so make sure the enclosing
directory is also not writable */
if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0)
{
if((dirbuf.st_mode & (S_IWGRP|S_IWOTH))==0)
ret=0;
else
enc_dir_perm=1;
}
else
enc_dir_own=1;
}
else
{
/* it's writable, so the enclosing directory had
better not let people get to it. */
if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0)
{
if((dirbuf.st_mode & (S_IRWXG|S_IRWXO))==0)
ret=0;
else
perm=enc_dir_perm=1; /* unclear which one to fix! */
}
else
enc_dir_own=1;
}
}
else
own=1;
}
}
else
BUG();
if(!checkonly)
{
if(own)
{
if(item==0)
log_info(_("WARNING: unsafe ownership on"
" homedir '%s'\n"),tmppath);
else if(item==1)
log_info(_("WARNING: unsafe ownership on"
" configuration file '%s'\n"),tmppath);
else
log_info(_("WARNING: unsafe ownership on"
" extension '%s'\n"),tmppath);
}
if(perm)
{
if(item==0)
log_info(_("WARNING: unsafe permissions on"
" homedir '%s'\n"),tmppath);
else if(item==1)
log_info(_("WARNING: unsafe permissions on"
" configuration file '%s'\n"),tmppath);
else
log_info(_("WARNING: unsafe permissions on"
" extension '%s'\n"),tmppath);
}
if(enc_dir_own)
{
if(item==0)
log_info(_("WARNING: unsafe enclosing directory ownership on"
" homedir '%s'\n"),tmppath);
else if(item==1)
log_info(_("WARNING: unsafe enclosing directory ownership on"
" configuration file '%s'\n"),tmppath);
else
log_info(_("WARNING: unsafe enclosing directory ownership on"
" extension '%s'\n"),tmppath);
}
if(enc_dir_perm)
{
if(item==0)
log_info(_("WARNING: unsafe enclosing directory permissions on"
" homedir '%s'\n"),tmppath);
else if(item==1)
log_info(_("WARNING: unsafe enclosing directory permissions on"
" configuration file '%s'\n"),tmppath);
else
log_info(_("WARNING: unsafe enclosing directory permissions on"
" extension '%s'\n"),tmppath);
}
}
end:
xfree(tmppath);
if(homedir)
homedir_cache=ret;
return ret;
#else /*!(HAVE_STAT && !HAVE_DOSISH_SYSTEM)*/
(void)path;
(void)item;
return 0;
#endif /*!(HAVE_STAT && !HAVE_DOSISH_SYSTEM)*/
}
/* Print the OpenPGP defined algo numbers. */
static void
print_algo_numbers(int (*checker)(int))
{
int i,first=1;
for(i=0;i<=110;i++)
{
if(!checker(i))
{
if(first)
first=0;
else
es_printf (";");
es_printf ("%d",i);
}
}
}
static void
print_algo_names(int (*checker)(int),const char *(*mapper)(int))
{
int i,first=1;
for(i=0;i<=110;i++)
{
if(!checker(i))
{
if(first)
first=0;
else
es_printf (";");
es_printf ("%s",mapper(i));
}
}
}
/* In the future, we can do all sorts of interesting configuration
output here. For now, just give "group" as the Enigmail folks need
it, and pubkey, cipher, hash, and compress as they may be useful
for frontends. */
static void
list_config(char *items)
{
int show_all = !items;
char *name = NULL;
const char *s;
struct groupitem *giter;
int first, iter;
if(!opt.with_colons)
return;
while(show_all || (name=strsep(&items," ")))
{
int any=0;
if(show_all || ascii_strcasecmp(name,"group")==0)
{
for (giter = opt.grouplist; giter; giter = giter->next)
{
strlist_t sl;
es_fprintf (es_stdout, "cfg:group:");
es_write_sanitized (es_stdout, giter->name, strlen(giter->name),
":", NULL);
es_putc (':', es_stdout);
for(sl=giter->values; sl; sl=sl->next)
{
es_write_sanitized (es_stdout, sl->d, strlen (sl->d),
":;", NULL);
if(sl->next)
es_printf(";");
}
es_printf("\n");
}
any=1;
}
if(show_all || ascii_strcasecmp(name,"version")==0)
{
es_printf("cfg:version:");
es_write_sanitized (es_stdout, VERSION, strlen(VERSION), ":", NULL);
es_printf ("\n");
any=1;
}
if(show_all || ascii_strcasecmp(name,"pubkey")==0)
{
es_printf ("cfg:pubkey:");
print_algo_numbers (build_list_pk_test_algo);
es_printf ("\n");
any=1;
}
if(show_all || ascii_strcasecmp(name,"pubkeyname")==0)
{
es_printf ("cfg:pubkeyname:");
print_algo_names (build_list_pk_test_algo,
build_list_pk_algo_name);
es_printf ("\n");
any=1;
}
if(show_all || ascii_strcasecmp(name,"cipher")==0)
{
es_printf ("cfg:cipher:");
print_algo_numbers (build_list_cipher_test_algo);
es_printf ("\n");
any=1;
}
if (show_all || !ascii_strcasecmp (name,"ciphername"))
{
es_printf ("cfg:ciphername:");
print_algo_names (build_list_cipher_test_algo,
build_list_cipher_algo_name);
es_printf ("\n");
any = 1;
}
if(show_all
|| ascii_strcasecmp(name,"digest")==0
|| ascii_strcasecmp(name,"hash")==0)
{
es_printf ("cfg:digest:");
print_algo_numbers (build_list_md_test_algo);
es_printf ("\n");
any=1;
}
if (show_all
|| !ascii_strcasecmp(name,"digestname")
|| !ascii_strcasecmp(name,"hashname"))
{
es_printf ("cfg:digestname:");
print_algo_names (build_list_md_test_algo,
build_list_md_algo_name);
es_printf ("\n");
any=1;
}
if(show_all || ascii_strcasecmp(name,"compress")==0)
{
es_printf ("cfg:compress:");
print_algo_numbers(check_compress_algo);
es_printf ("\n");
any=1;
}
if(show_all || ascii_strcasecmp (name, "compressname") == 0)
{
es_printf ("cfg:compressname:");
print_algo_names (check_compress_algo,
compress_algo_to_string);
es_printf ("\n");
any=1;
}
if (show_all || !ascii_strcasecmp(name,"ccid-reader-id"))
{
/* We ignore this for GnuPG 1.4 backward compatibility. */
any=1;
}
if (show_all || !ascii_strcasecmp (name,"curve"))
{
es_printf ("cfg:curve:");
for (iter=0, first=1; (s = openpgp_enum_curves (&iter)); first=0)
es_printf ("%s%s", first?"":";", s);
es_printf ("\n");
any=1;
}
/* Curve OIDs are rarely useful and thus only printed if requested. */
if (name && !ascii_strcasecmp (name,"curveoid"))
{
es_printf ("cfg:curveoid:");
for (iter=0, first=1; (s = openpgp_enum_curves (&iter)); first = 0)
{
s = openpgp_curve_to_oid (s, NULL, NULL);
es_printf ("%s%s", first?"":";", s? s:"[?]");
}
es_printf ("\n");
any=1;
}
if(show_all)
break;
if(!any)
log_error(_("unknown configuration item '%s'\n"),name);
}
}
/* List default values for use by gpgconf. */
static void
gpgconf_list (void)
{
es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT);
es_printf ("compliance:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, "gnupg");
/* The next one is an info only item and should match the macros at
the top of keygen.c */
es_printf ("default_pubkey_algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT,
get_default_pubkey_algo ());
/* This info only mode tells whether the we are running in de-vs
* compliance mode. This does not test all parameters but the basic
* conditions like a proper RNG and Libgcrypt. AS of now we always
* return 0 because this version of gnupg has not yet received an
* appoval. */
es_printf ("compliance_de_vs:%lu:%d:\n", GC_OPT_FLAG_DEFAULT,
0 /*gnupg_rng_is_compliant (CO_DE_VS)*/);
es_printf ("use_keyboxd:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, opt.use_keyboxd);
}
static int
parse_subpacket_list(char *list)
{
char *tok;
byte subpackets[128],i;
int count=0;
if(!list)
{
/* No arguments means all subpackets */
memset(subpackets+1,1,sizeof(subpackets)-1);
count=127;
}
else
{
memset(subpackets,0,sizeof(subpackets));
/* Merge with earlier copy */
if(opt.show_subpackets)
{
byte *in;
for(in=opt.show_subpackets;*in;in++)
{
if(*in>127 || *in<1)
BUG();
if(!subpackets[*in])
count++;
subpackets[*in]=1;
}
}
while((tok=strsep(&list," ,")))
{
if(!*tok)
continue;
i=atoi(tok);
if(i>127 || i<1)
return 0;
if(!subpackets[i])
count++;
subpackets[i]=1;
}
}
xfree(opt.show_subpackets);
opt.show_subpackets=xmalloc(count+1);
opt.show_subpackets[count--]=0;
for(i=1;i<128 && count>=0;i++)
if(subpackets[i])
opt.show_subpackets[count--]=i;
return 1;
}
static int
parse_list_options(char *str)
{
char *subpackets=""; /* something that isn't NULL */
struct parse_options lopts[]=
{
{"show-sig-subpackets",LIST_SHOW_SIG_SUBPACKETS,NULL,
NULL},
{"show-photos",LIST_SHOW_PHOTOS,NULL,
N_("display photo IDs during key listings")},
{"show-usage",LIST_SHOW_USAGE,NULL,
N_("show key usage information during key listings")},
{"show-policy-urls",LIST_SHOW_POLICY_URLS,NULL,
N_("show policy URLs during signature listings")},
{"show-notations",LIST_SHOW_NOTATIONS,NULL,
N_("show all notations during signature listings")},
{"show-std-notations",LIST_SHOW_STD_NOTATIONS,NULL,
N_("show IETF standard notations during signature listings")},
{"show-standard-notations",LIST_SHOW_STD_NOTATIONS,NULL,
NULL},
{"show-user-notations",LIST_SHOW_USER_NOTATIONS,NULL,
N_("show user-supplied notations during signature listings")},
{"show-keyserver-urls",LIST_SHOW_KEYSERVER_URLS,NULL,
N_("show preferred keyserver URLs during signature listings")},
{"show-uid-validity",LIST_SHOW_UID_VALIDITY,NULL,
N_("show user ID validity during key listings")},
{"show-unusable-uids",LIST_SHOW_UNUSABLE_UIDS,NULL,
N_("show revoked and expired user IDs in key listings")},
{"show-unusable-subkeys",LIST_SHOW_UNUSABLE_SUBKEYS,NULL,
N_("show revoked and expired subkeys in key listings")},
{"show-unusable-sigs",LIST_SHOW_UNUSABLE_SIGS,NULL,
N_("show signatures with invalid algorithms during signature listings")},
{"show-keyring",LIST_SHOW_KEYRING,NULL,
N_("show the keyring name in key listings")},
{"show-sig-expire",LIST_SHOW_SIG_EXPIRE,NULL,
N_("show expiration dates during signature listings")},
{"show-pref", LIST_SHOW_PREF, NULL,
N_("show preferences")},
{"show-pref-verbose", LIST_SHOW_PREF_VERBOSE, NULL,
N_("show preferences")},
{"show-only-fpr-mbox",LIST_SHOW_ONLY_FPR_MBOX, NULL,
NULL},
{"sort-sigs", LIST_SORT_SIGS, NULL,
NULL},
{NULL,0,NULL,NULL}
};
int i;
/* C99 allows for non-constant initializers, but we'd like to
compile everywhere, so fill in the show-sig-subpackets argument
here. Note that if the parse_options array changes, we'll have
to change the subscript here. We use a loop here in case the
list above is reordered. */
for (i=0; lopts[i].name; i++)
if (lopts[i].bit == LIST_SHOW_SIG_SUBPACKETS)
{
lopts[i].value = &subpackets;
break;
}
if(parse_options(str,&opt.list_options,lopts,1))
{
if(opt.list_options&LIST_SHOW_SIG_SUBPACKETS)
{
/* Unset so users can pass multiple lists in. */
opt.list_options&=~LIST_SHOW_SIG_SUBPACKETS;
if(!parse_subpacket_list(subpackets))
return 0;
}
else if(subpackets==NULL && opt.show_subpackets)
{
/* User did 'no-show-subpackets' */
xfree(opt.show_subpackets);
opt.show_subpackets=NULL;
}
return 1;
}
else
return 0;
}
/* Collapses argc/argv into a single string that must be freed */
static char *
collapse_args(int argc,char *argv[])
{
char *str=NULL;
int i,first=1,len=0;
for(i=0;i<argc;i++)
{
len+=strlen(argv[i])+2;
str=xrealloc(str,len);
if(first)
{
str[0]='\0';
first=0;
}
else
strcat(str," ");
strcat(str,argv[i]);
}
return str;
}
#ifndef NO_TRUST_MODELS
static void
parse_trust_model(const char *model)
{
if(ascii_strcasecmp(model,"pgp")==0)
opt.trust_model=TM_PGP;
else if(ascii_strcasecmp(model,"classic")==0)
opt.trust_model=TM_CLASSIC;
else if(ascii_strcasecmp(model,"always")==0)
opt.trust_model=TM_ALWAYS;
else if(ascii_strcasecmp(model,"direct")==0)
opt.trust_model=TM_DIRECT;
#ifdef USE_TOFU
else if(ascii_strcasecmp(model,"tofu")==0)
opt.trust_model=TM_TOFU;
else if(ascii_strcasecmp(model,"tofu+pgp")==0)
opt.trust_model=TM_TOFU_PGP;
#endif /*USE_TOFU*/
else if(ascii_strcasecmp(model,"auto")==0)
opt.trust_model=TM_AUTO;
else
log_error("unknown trust model '%s'\n",model);
}
#endif /*NO_TRUST_MODELS*/
static int
parse_tofu_policy (const char *policystr)
{
#ifdef USE_TOFU
struct { const char *keyword; int policy; } list[] = {
{ "auto", TOFU_POLICY_AUTO },
{ "good", TOFU_POLICY_GOOD },
{ "unknown", TOFU_POLICY_UNKNOWN },
{ "bad", TOFU_POLICY_BAD },
{ "ask", TOFU_POLICY_ASK }
};
int i;
if (!ascii_strcasecmp (policystr, "help"))
{
log_info (_("valid values for option '%s':\n"), "--tofu-policy");
for (i=0; i < DIM (list); i++)
log_info (" %s\n", list[i].keyword);
g10_exit (1);
}
for (i=0; i < DIM (list); i++)
if (!ascii_strcasecmp (policystr, list[i].keyword))
return list[i].policy;
#endif /*USE_TOFU*/
log_error (_("unknown TOFU policy '%s'\n"), policystr);
if (!opt.quiet)
log_info (_("(use \"help\" to list choices)\n"));
g10_exit (1);
}
static struct gnupg_compliance_option compliance_options[] =
{
{ "gnupg", oGnuPG },
{ "openpgp", oOpenPGP },
{ "rfc4880bis", oGnuPG },
{ "rfc4880", oRFC4880 },
{ "rfc2440", oRFC2440 },
{ "pgp6", oPGP7 },
{ "pgp7", oPGP7 },
{ "pgp8", oPGP8 },
{ "de-vs", oDE_VS }
};
/* Helper to set compliance related options. This is a separate
* function so that it can also be used by the --compliance option
* parser. */
static void
set_compliance_option (enum cmd_and_opt_values option)
{
switch (option)
{
case oOpenPGP:
case oRFC4880:
/* This is effectively the same as RFC2440, but with
"--enable-dsa2 --no-rfc2440-text --escape-from-lines
--require-cross-certification". */
opt.compliance = CO_RFC4880;
opt.flags.dsa2 = 1;
opt.flags.require_cross_cert = 1;
opt.rfc2440_text = 0;
opt.allow_non_selfsigned_uid = 1;
opt.allow_freeform_uid = 1;
opt.escape_from = 1;
opt.not_dash_escaped = 0;
opt.def_cipher_algo = 0;
opt.def_digest_algo = 0;
opt.cert_digest_algo = 0;
opt.compress_algo = -1;
opt.s2k_mode = 3; /* iterated+salted */
opt.s2k_digest_algo = DIGEST_ALGO_SHA1;
opt.s2k_cipher_algo = CIPHER_ALGO_3DES;
opt.flags.allow_old_cipher_algos = 1;
break;
case oRFC2440:
opt.compliance = CO_RFC2440;
opt.flags.dsa2 = 0;
opt.rfc2440_text = 1;
opt.allow_non_selfsigned_uid = 1;
opt.allow_freeform_uid = 1;
opt.escape_from = 0;
opt.not_dash_escaped = 0;
opt.def_cipher_algo = 0;
opt.def_digest_algo = 0;
opt.cert_digest_algo = 0;
opt.compress_algo = -1;
opt.s2k_mode = 3; /* iterated+salted */
opt.s2k_digest_algo = DIGEST_ALGO_SHA1;
opt.s2k_cipher_algo = CIPHER_ALGO_3DES;
opt.flags.allow_old_cipher_algos = 1;
break;
case oPGP7: opt.compliance = CO_PGP7; break;
case oPGP8: opt.compliance = CO_PGP8; break;
case oGnuPG:
opt.compliance = CO_GNUPG;
break;
case oDE_VS:
set_compliance_option (oOpenPGP);
opt.compliance = CO_DE_VS;
/* We divert here from the backward compatible rfc4880 algos. */
opt.s2k_digest_algo = DIGEST_ALGO_SHA256;
opt.s2k_cipher_algo = CIPHER_ALGO_AES256;
break;
default:
BUG ();
}
}
static void
gen_key_forbidden (void)
{
write_status_failure ("gen-key", gpg_error (GPG_ERR_NOT_ENABLED));
log_error (_("This command is not allowed while in %s mode.\n"),
"forbid-gen-key");
}
/* This function called to initialized a new control object. It is
assumed that this object has been zeroed out before calling this
function. */
static void
gpg_init_default_ctrl (ctrl_t ctrl)
{
ctrl->magic = SERVER_CONTROL_MAGIC;
}
/* This function is called to deinitialize a control object. It is
not deallocated. */
static void
gpg_deinit_default_ctrl (ctrl_t ctrl)
{
#ifdef USE_TOFU
tofu_closedbs (ctrl);
#endif
gpg_dirmngr_deinit_session_data (ctrl);
keydb_release (ctrl->cached_getkey_kdb);
gpg_keyboxd_deinit_session_data (ctrl);
xfree (ctrl->secret_keygrips);
ctrl->secret_keygrips = NULL;
}
int
main (int argc, char **argv)
{
gpgrt_argparse_t pargs;
IOBUF a;
int rc=0;
int orig_argc;
char **orig_argv;
const char *fname;
char *username;
int may_coredump;
strlist_t sl;
strlist_t remusr = NULL;
strlist_t locusr = NULL;
strlist_t nrings = NULL;
armor_filter_context_t *afx = NULL;
int detached_sig = 0;
char *last_configname = NULL;
const char *configname = NULL; /* NULL or points to last_configname.
* NULL also indicates that we are
* processing options from the cmdline. */
int debug_argparser = 0;
int default_keyring = 1;
int greeting = 0;
int nogreeting = 0;
char *logfile = NULL;
int use_random_seed = 1;
enum cmd_and_opt_values cmd = 0;
const char *debug_level = NULL;
#ifndef NO_TRUST_MODELS
const char *trustdb_name = NULL;
#endif /*!NO_TRUST_MODELS*/
char *def_cipher_string = NULL;
char *def_digest_string = NULL;
char *compress_algo_string = NULL;
char *cert_digest_string = NULL;
char *s2k_cipher_string = NULL;
char *s2k_digest_string = NULL;
char *pers_cipher_list = NULL;
char *pers_digest_list = NULL;
char *pers_compress_list = NULL;
int eyes_only=0;
int multifile=0;
int pwfd = -1;
int ovrseskeyfd = -1;
int fpr_maybe_cmd = 0; /* --fingerprint maybe a command. */
int any_explicit_recipient = 0;
int default_akl = 1;
int require_secmem = 0;
int got_secmem = 0;
struct assuan_malloc_hooks malloc_hooks;
ctrl_t ctrl;
static int print_dane_records;
static int allow_large_chunks;
static const char *homedirvalue;
static const char *changeuser;
#ifdef __riscos__
opt.lock_once = 1;
#endif /* __riscos__ */
/* Please note that we may running SUID(ROOT), so be very CAREFUL
when adding any stuff between here and the call to
secmem_init() somewhere after the option parsing. */
early_system_init ();
gnupg_reopen_std (GPG_NAME);
trap_unaligned ();
gnupg_rl_initialize ();
gpgrt_set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
log_set_prefix (GPG_NAME, GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_NO_REGISTRY);
/* Make sure that our subsystems are ready. */
i18n_init();
init_common_subsystems (&argc, &argv);
/* Use our own logging handler for Libcgrypt. */
setup_libgcrypt_logging ();
/* Put random number into secure memory */
gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
may_coredump = disable_core_dumps();
gnupg_init_signals (0, emergency_cleanup);
dotlock_create (NULL, 0); /* Register lock file cleanup. */
/* Tell the compliance module who we are. */
gnupg_initialize_compliance (GNUPG_MODULE_NAME_GPG);
opt.autostart = 1;
opt.session_env = session_env_new ();
if (!opt.session_env)
log_fatal ("error allocating session environment block: %s\n",
strerror (errno));
opt.command_fd = -1; /* no command fd */
opt.compress_level = -1; /* defaults to standard compress level */
opt.bz2_compress_level = -1; /* defaults to standard compress level */
/* note: if you change these lines, look at oOpenPGP */
opt.def_cipher_algo = 0;
opt.def_digest_algo = 0;
opt.cert_digest_algo = 0;
opt.compress_algo = -1; /* defaults to DEFAULT_COMPRESS_ALGO */
opt.s2k_mode = 3; /* iterated+salted */
opt.s2k_count = 0; /* Auto-calibrate when needed. */
opt.s2k_cipher_algo = DEFAULT_CIPHER_ALGO;
opt.completes_needed = 1;
opt.marginals_needed = 3;
opt.max_cert_depth = 5;
opt.escape_from = 1;
opt.flags.require_cross_cert = 1;
opt.import_options = (IMPORT_REPAIR_KEYS
| IMPORT_COLLAPSE_UIDS
| IMPORT_COLLAPSE_SUBKEYS);
opt.export_options = EXPORT_ATTRIBUTES;
opt.keyserver_options.import_options = (IMPORT_REPAIR_KEYS
| IMPORT_REPAIR_PKS_SUBKEY_BUG
| IMPORT_SELF_SIGS_ONLY
| IMPORT_COLLAPSE_UIDS
| IMPORT_COLLAPSE_SUBKEYS
| IMPORT_CLEAN);
opt.keyserver_options.export_options = EXPORT_ATTRIBUTES;
opt.keyserver_options.options = 0;
opt.verify_options = (LIST_SHOW_UID_VALIDITY
| VERIFY_SHOW_POLICY_URLS
| VERIFY_SHOW_STD_NOTATIONS
| VERIFY_SHOW_KEYSERVER_URLS);
opt.list_options = (LIST_SHOW_UID_VALIDITY
| LIST_SORT_SIGS
| LIST_SHOW_USAGE);
#ifdef NO_TRUST_MODELS
opt.trust_model = TM_ALWAYS;
#else
opt.trust_model = TM_AUTO;
#endif
opt.tofu_default_policy = TOFU_POLICY_AUTO;
opt.mangle_dos_filenames = 0;
opt.min_cert_level = 2;
set_screen_dimensions ();
opt.keyid_format = KF_NONE;
opt.def_sig_expire = "0";
opt.def_cert_expire = "0";
opt.passphrase_repeat = 1;
opt.emit_version = 0;
opt.weak_digests = NULL;
opt.compliance = CO_GNUPG;
/* Check special options given on the command line. */
orig_argc = argc;
orig_argv = argv;
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION);
while (gpgrt_argparse (NULL, &pargs, opts))
{
switch (pargs.r_opt)
{
case oDebug:
case oDebugAll:
debug_argparser++;
break;
case oDebugIOLBF:
es_setvbuf (es_stdout, NULL, _IOLBF, 0);
break;
case oNoOptions:
/* Set here here because the homedir would otherwise be
* created before main option parsing starts. */
opt.no_homedir_creation = 1;
break;
case oHomedir:
homedirvalue = pargs.r.ret_str;
break;
case oChUid:
changeuser = pargs.r.ret_str;
break;
case oNoPermissionWarn:
opt.no_perm_warn = 1;
break;
}
}
/* Reset the flags. */
pargs.flags &= ~(ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION);
#ifdef HAVE_DOSISH_SYSTEM
/* FIXME: Do we still need this? No: gnupg_homedir calls
* make_filename which changes the slashed anyway. IsDBCSLeadByte still
* needed? See bug #561. */
if ( strchr (gnupg_homedir (), '\\') ) {
char *d, *buf = xmalloc (strlen (gnupg_homedir ())+1);
const char *s;
for (d=buf, s = gnupg_homedir (); *s; s++)
{
*d++ = *s == '\\'? '/': *s;
#ifdef HAVE_W32_SYSTEM
if (s[1] && IsDBCSLeadByte (*s))
*d++ = *++s;
#endif
}
*d = 0;
gnupg_set_homedir (buf);
}
#endif
/* Initialize the secure memory. */
if (!gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0))
got_secmem = 1;
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
/* There should be no way to get to this spot while still carrying
setuid privs. Just in case, bomb out if we are. */
if ( getuid () != geteuid () )
BUG ();
#endif
maybe_setuid = 0;
/* Okay, we are now working under our real uid */
/* malloc hooks go here ... */
malloc_hooks.malloc = gcry_malloc;
malloc_hooks.realloc = gcry_realloc;
malloc_hooks.free = gcry_free;
assuan_set_malloc_hooks (&malloc_hooks);
assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
setup_libassuan_logging (&opt.debug, NULL);
/* Change UID and then set the homedir. */
if (changeuser && gnupg_chuid (changeuser, 0))
log_inc_errorcount (); /* Force later termination. */
gnupg_set_homedir (homedirvalue);
/* Set default options which require that malloc stuff is ready. */
additional_weak_digest ("MD5");
parse_auto_key_locate (DEFAULT_AKL_LIST);
argc = orig_argc;
argv = orig_argv;
pargs.argc = &argc;
pargs.argv = &argv;
/* We are re-using the struct, thus the reset flag. We OR the
* flags so that the internal intialized flag won't be cleared. */
pargs.flags |= (ARGPARSE_FLAG_RESET
| ARGPARSE_FLAG_KEEP
| ARGPARSE_FLAG_SYS
| ARGPARSE_FLAG_USER
| ARGPARSE_FLAG_USERVERS);
/* By this point we have a homedir, and cannot change it. */
check_permissions (gnupg_homedir (), 0);
/* The configuraton directories for use by gpgrt_argparser. */
gpgrt_set_confdir (GPGRT_CONFDIR_SYS, gnupg_sysconfdir ());
gpgrt_set_confdir (GPGRT_CONFDIR_USER, gnupg_homedir ());
while (gpgrt_argparser (&pargs, opts, GPG_NAME EXTSEP_S "conf" ))
{
switch (pargs.r_opt)
{
case ARGPARSE_CONFFILE:
if (debug_argparser)
log_info (_("reading options from '%s'\n"),
pargs.r_type? pargs.r.ret_str: "[cmdline]");
if (pargs.r_type)
{
xfree (last_configname);
last_configname = xstrdup (pargs.r.ret_str);
configname = last_configname;
if (is_secured_filename (configname))
{
pargs.r_opt = ARGPARSE_PERMISSION_ERROR;
pargs.err = ARGPARSE_PRINT_ERROR;
}
else if (strncmp (configname, gnupg_sysconfdir (),
strlen (gnupg_sysconfdir ())))
{
/* This is not the global config file and thus we
* need to check the permissions: If the file is
* unsafe, then disable any external programs for
* keyserver calls or photo IDs. Since the
* external program to call is set in the options
* file, a unsafe options file can lead to an
* arbitrary program being run. */
if (check_permissions (configname, 1))
opt.exec_disable=1;
}
}
else
configname = NULL;
break;
/* case oOptions: */
/* case oNoOptions: */
/* We will never see these options here because
* gpgrt_argparse handles them for us. */
/* break */
case aListConfig:
case aListGcryptConfig:
case aGPGConfList:
case aGPGConfTest:
set_cmd (&cmd, pargs.r_opt);
/* Do not register a keyring for these commands. */
default_keyring = -1;
break;
case aCheckKeys:
case aListPackets:
case aImport:
case aFastImport:
case aSendKeys:
case aRecvKeys:
case aSearchKeys:
case aRefreshKeys:
case aFetchKeys:
case aExport:
#ifdef ENABLE_CARD_SUPPORT
case aCardStatus:
case aCardEdit:
case aChangePIN:
#endif /* ENABLE_CARD_SUPPORT*/
case aListKeys:
case aLocateKeys:
case aLocateExtKeys:
case aListSigs:
case aExportSecret:
case aExportSecretSub:
case aExportSshKey:
case aExportSecretSshKey:
case aSym:
case aClearsign:
case aGenRevoke:
case aDesigRevoke:
case aPrimegen:
case aGenRandom:
case aPrintMD:
case aPrintMDs:
case aListTrustDB:
case aCheckTrustDB:
case aUpdateTrustDB:
case aFixTrustDB:
case aListTrustPath:
case aDeArmor:
case aEnArmor:
case aSign:
case aQuickSignKey:
case aQuickLSignKey:
case aQuickRevSig:
case aSignKey:
case aLSignKey:
case aStore:
case aQuickKeygen:
case aQuickAddUid:
case aQuickAddKey:
case aQuickAddADSK:
case aQuickRevUid:
case aQuickSetExpire:
case aQuickSetPrimaryUid:
case aQuickUpdatePref:
case aExportOwnerTrust:
case aImportOwnerTrust:
case aRebuildKeydbCaches:
set_cmd (&cmd, pargs.r_opt);
break;
case aKeygen:
case aFullKeygen:
case aEditKey:
case aDeleteSecretKeys:
case aDeleteSecretAndPublicKeys:
case aDeleteKeys:
case aPasswd:
set_cmd (&cmd, pargs.r_opt);
greeting=1;
break;
case aShowKeys:
set_cmd (&cmd, pargs.r_opt);
opt.import_options |= IMPORT_SHOW;
opt.import_options |= IMPORT_DRY_RUN;
opt.import_options &= ~IMPORT_REPAIR_KEYS;
opt.list_options |= LIST_SHOW_UNUSABLE_UIDS;
opt.list_options |= LIST_SHOW_UNUSABLE_SUBKEYS;
opt.list_options |= LIST_SHOW_NOTATIONS;
opt.list_options |= LIST_SHOW_POLICY_URLS;
break;
case aDetachedSign: detached_sig = 1; set_cmd( &cmd, aSign ); break;
case aDecryptFiles: multifile=1; /* fall through */
case aDecrypt: set_cmd( &cmd, aDecrypt); break;
case aEncrFiles: multifile=1; /* fall through */
case aEncr: set_cmd( &cmd, aEncr); break;
case aVerifyFiles: multifile=1; /* fall through */
case aVerify: set_cmd( &cmd, aVerify); break;
case aServer:
set_cmd (&cmd, pargs.r_opt);
opt.batch = 1;
break;
case aTOFUPolicy:
set_cmd (&cmd, pargs.r_opt);
break;
case oArmor: opt.armor = 1; opt.no_armor=0; break;
case oOutput: opt.outfile = pargs.r.ret_str; break;
case oMaxOutput: opt.max_output = pargs.r.ret_ulong; break;
case oInputSizeHint:
opt.input_size_hint = string_to_u64 (pargs.r.ret_str);
break;
case oChunkSize:
opt.chunk_size = pargs.r.ret_int;
break;
case oQuiet: opt.quiet = 1; break;
case oNoTTY: tty_no_terminal(1); break;
case oDryRun: opt.dry_run = 1; break;
case oInteractive: opt.interactive = 1; break;
case oVerbose:
opt.verbose++;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
opt.list_options|=LIST_SHOW_UNUSABLE_UIDS;
opt.list_options|=LIST_SHOW_UNUSABLE_SUBKEYS;
break;
case oBatch:
opt.batch = 1;
nogreeting = 1;
break;
case oUseAgent: /* Dummy. */
break;
case oNoUseAgent:
obsolete_option (configname, pargs.lineno, "no-use-agent");
break;
case oGpgAgentInfo:
obsolete_option (configname, pargs.lineno, "gpg-agent-info");
break;
case oUseKeyboxd:
opt.use_keyboxd = 1;
break;
case oReaderPort:
obsolete_scdaemon_option (configname, pargs.lineno, "reader-port");
break;
case octapiDriver:
obsolete_scdaemon_option (configname, pargs.lineno, "ctapi-driver");
break;
case opcscDriver:
obsolete_scdaemon_option (configname, pargs.lineno, "pcsc-driver");
break;
case oDisableCCID:
obsolete_scdaemon_option (configname, pargs.lineno, "disable-ccid");
break;
case oHonorHttpProxy:
obsolete_option (configname, pargs.lineno, "honor-http-proxy");
break;
case oAnswerYes: opt.answer_yes = 1; break;
case oAnswerNo: opt.answer_no = 1; break;
case oForceSignKey: opt.flags.force_sign_key = 1; break;
case oKeyring: append_to_strlist( &nrings, pargs.r.ret_str); break;
case oPrimaryKeyring:
sl = append_to_strlist (&nrings, pargs.r.ret_str);
sl->flags = KEYDB_RESOURCE_FLAG_PRIMARY;
break;
case oShowKeyring:
deprecated_warning(configname,pargs.lineno,"--show-keyring",
"--list-options ","show-keyring");
opt.list_options|=LIST_SHOW_KEYRING;
break;
case oDebug:
if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags))
{
pargs.r_opt = ARGPARSE_INVALID_ARG;
pargs.err = ARGPARSE_PRINT_ERROR;
}
break;
case oDebugAll: opt.debug = ~0; break;
case oDebugLevel: debug_level = pargs.r.ret_str; break;
case oDebugIOLBF: break; /* Already set in pre-parse step. */
case oDebugSetIobufSize:
opt_set_iobuf_size = pargs.r.ret_ulong;
opt_set_iobuf_size_used = 1;
break;
case oDebugAllowLargeChunks:
allow_large_chunks = 1;
break;
case oDebugIgnoreExpiration:
opt.ignore_expiration = 1;
break;
case oCompatibilityFlags:
if (parse_compatibility_flags (pargs.r.ret_str, &opt.compat_flags,
compatibility_flags))
{
pargs.r_opt = ARGPARSE_INVALID_ARG;
pargs.err = ARGPARSE_PRINT_ERROR;
}
break;
case oStatusFD:
set_status_fd ( translate_sys2libc_fd_int (pargs.r.ret_int, 1) );
break;
case oStatusFile:
set_status_fd ( open_info_file (pargs.r.ret_str, 1, 0) );
break;
case oAttributeFD:
set_attrib_fd ( translate_sys2libc_fd_int (pargs.r.ret_int, 1) );
break;
case oAttributeFile:
set_attrib_fd ( open_info_file (pargs.r.ret_str, 1, 1) );
break;
case oLoggerFD:
log_set_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1));
break;
case oLoggerFile:
logfile = pargs.r.ret_str;
break;
case oLogTime:
opt_log_time = 1;
break;
case oWithFingerprint:
opt.with_fingerprint = 1;
opt.fingerprint++;
break;
case oWithSubkeyFingerprint:
opt.with_subkey_fingerprint = 1;
break;
case oWithICAOSpelling:
opt.with_icao_spelling = 1;
break;
case oFingerprint:
opt.fingerprint++;
fpr_maybe_cmd = 1;
break;
case oWithKeygrip:
opt.with_keygrip = 1;
break;
case oWithKeyScreening:
opt.with_key_screening = 1;
break;
case oWithSecret:
opt.with_secret = 1;
break;
case oWithWKDHash:
opt.with_wkd_hash = 1;
break;
case oWithKeyOrigin:
opt.with_key_origin = 1;
break;
case oSecretKeyring:
obsolete_option (configname, pargs.lineno, "secret-keyring");
break;
case oNoArmor: opt.no_armor=1; opt.armor=0; break;
case oNoDefKeyring:
if (default_keyring > 0)
default_keyring = 0;
break;
case oNoKeyring:
default_keyring = -1;
break;
case oNoGreeting: nogreeting = 1; break;
case oNoVerbose:
opt.verbose = 0;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
opt.list_sigs=0;
break;
case oQuickRandom:
gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
break;
case oEmitVersion: opt.emit_version++; break;
case oNoEmitVersion: opt.emit_version=0; break;
case oCompletesNeeded: opt.completes_needed = pargs.r.ret_int; break;
case oMarginalsNeeded: opt.marginals_needed = pargs.r.ret_int; break;
case oMaxCertDepth: opt.max_cert_depth = pargs.r.ret_int; break;
#ifndef NO_TRUST_MODELS
case oTrustDBName: trustdb_name = pargs.r.ret_str; break;
#endif /*!NO_TRUST_MODELS*/
case oDefaultKey:
sl = add_to_strlist (&opt.def_secret_key, pargs.r.ret_str);
sl->flags = (pargs.r_opt << PK_LIST_SHIFT);
if (configname)
sl->flags |= PK_LIST_CONFIG;
break;
case oDefRecipient:
if( *pargs.r.ret_str )
{
xfree (opt.def_recipient);
opt.def_recipient = make_username(pargs.r.ret_str);
}
break;
case oDefRecipientSelf:
xfree(opt.def_recipient); opt.def_recipient = NULL;
opt.def_recipient_self = 1;
break;
case oNoDefRecipient:
xfree(opt.def_recipient); opt.def_recipient = NULL;
opt.def_recipient_self = 0;
break;
case oHomedir: break;
case oChUid: break; /* Command line only (see above). */
case oNoBatch: opt.batch = 0; break;
case oWithTofuInfo: opt.with_tofu_info = 1; break;
case oWithKeyData: opt.with_key_data=1; /*FALLTHRU*/
case oWithColons: opt.with_colons=':'; break;
case oWithSigCheck: opt.check_sigs = 1; /*FALLTHRU*/
case oWithSigList: opt.list_sigs = 1; break;
case oSkipVerify: opt.skip_verify=1; break;
case oSkipHiddenRecipients: opt.skip_hidden_recipients = 1; break;
case oNoSkipHiddenRecipients: opt.skip_hidden_recipients = 0; break;
case aListSecretKeys: set_cmd( &cmd, aListSecretKeys); break;
#ifndef NO_TRUST_MODELS
/* There are many programs (like mutt) that call gpg with
--always-trust so keep this option around for a long
time. */
case oAlwaysTrust: opt.trust_model=TM_ALWAYS; break;
case oTrustModel:
parse_trust_model(pargs.r.ret_str);
break;
#endif /*!NO_TRUST_MODELS*/
case oTOFUDefaultPolicy:
opt.tofu_default_policy = parse_tofu_policy (pargs.r.ret_str);
break;
case oTOFUDBFormat:
obsolete_option (configname, pargs.lineno, "tofu-db-format");
break;
case oForceOwnertrust:
log_info(_("Note: %s is not for normal use!\n"),
"--force-ownertrust");
opt.force_ownertrust=string_to_trust_value(pargs.r.ret_str);
if(opt.force_ownertrust==-1)
{
log_error("invalid ownertrust '%s'\n",pargs.r.ret_str);
opt.force_ownertrust=0;
}
break;
case oNoAutoTrustNewKey: opt.flags.no_auto_trust_new_key = 1; break;
case oCompliance:
{
int compliance = gnupg_parse_compliance_option
(pargs.r.ret_str,
compliance_options, DIM (compliance_options),
opt.quiet);
if (compliance < 0)
g10_exit (1);
set_compliance_option (compliance);
}
break;
case oOpenPGP:
case oRFC2440:
case oRFC4880:
case oPGP7:
case oPGP8:
case oGnuPG:
set_compliance_option (pargs.r_opt);
break;
case oMinRSALength: opt.min_rsa_length = pargs.r.ret_ulong; break;
case oRFC2440Text: opt.rfc2440_text=1; break;
case oNoRFC2440Text: opt.rfc2440_text=0; break;
case oSetFilename:
if(utf8_strings)
opt.set_filename = pargs.r.ret_str;
else
opt.set_filename = native_to_utf8(pargs.r.ret_str);
break;
case oForYourEyesOnly: eyes_only = 1; break;
case oNoForYourEyesOnly: eyes_only = 0; break;
case oSetPolicyURL:
add_policy_url(pargs.r.ret_str,0);
add_policy_url(pargs.r.ret_str,1);
break;
case oSigPolicyURL: add_policy_url(pargs.r.ret_str,0); break;
case oCertPolicyURL: add_policy_url(pargs.r.ret_str,1); break;
case oShowPolicyURL:
deprecated_warning(configname,pargs.lineno,"--show-policy-url",
"--list-options ","show-policy-urls");
deprecated_warning(configname,pargs.lineno,"--show-policy-url",
"--verify-options ","show-policy-urls");
opt.list_options|=LIST_SHOW_POLICY_URLS;
opt.verify_options|=VERIFY_SHOW_POLICY_URLS;
break;
case oNoShowPolicyURL:
deprecated_warning(configname,pargs.lineno,"--no-show-policy-url",
"--list-options ","no-show-policy-urls");
deprecated_warning(configname,pargs.lineno,"--no-show-policy-url",
"--verify-options ","no-show-policy-urls");
opt.list_options&=~LIST_SHOW_POLICY_URLS;
opt.verify_options&=~VERIFY_SHOW_POLICY_URLS;
break;
case oSigKeyserverURL: add_keyserver_url(pargs.r.ret_str,0); break;
case oUseEmbeddedFilename:
opt.flags.use_embedded_filename=1;
break;
case oNoUseEmbeddedFilename:
opt.flags.use_embedded_filename=0;
break;
case oComment:
if(pargs.r.ret_str[0])
append_to_strlist(&opt.comments,pargs.r.ret_str);
break;
case oDefaultComment:
deprecated_warning(configname,pargs.lineno,
"--default-comment","--no-comments","");
/* fall through */
case oNoComments:
free_strlist(opt.comments);
opt.comments=NULL;
break;
case oThrowKeyids: opt.throw_keyids = 1; break;
case oNoThrowKeyids: opt.throw_keyids = 0; break;
case oShowPhotos:
deprecated_warning(configname,pargs.lineno,"--show-photos",
"--list-options ","show-photos");
deprecated_warning(configname,pargs.lineno,"--show-photos",
"--verify-options ","show-photos");
opt.list_options|=LIST_SHOW_PHOTOS;
opt.verify_options|=VERIFY_SHOW_PHOTOS;
break;
case oNoShowPhotos:
deprecated_warning(configname,pargs.lineno,"--no-show-photos",
"--list-options ","no-show-photos");
deprecated_warning(configname,pargs.lineno,"--no-show-photos",
"--verify-options ","no-show-photos");
opt.list_options&=~LIST_SHOW_PHOTOS;
opt.verify_options&=~VERIFY_SHOW_PHOTOS;
break;
case oPhotoViewer: opt.photo_viewer = pargs.r.ret_str; break;
case oForceAEAD: opt.force_aead = 1; break;
case oDisableSignerUID: opt.flags.disable_signer_uid = 1; break;
case oIncludeKeyBlock: opt.flags.include_key_block = 1; break;
case oNoIncludeKeyBlock: opt.flags.include_key_block = 0; break;
case oS2KMode: opt.s2k_mode = pargs.r.ret_int; break;
case oS2KDigest: s2k_digest_string = xstrdup(pargs.r.ret_str); break;
case oS2KCipher: s2k_cipher_string = xstrdup(pargs.r.ret_str); break;
case oS2KCount:
if (pargs.r.ret_int)
opt.s2k_count = encode_s2k_iterations (pargs.r.ret_int);
else
opt.s2k_count = 0; /* Auto-calibrate when needed. */
break;
case oRecipient:
case oHiddenRecipient:
case oRecipientFile:
case oHiddenRecipientFile:
/* Store the recipient. Note that we also store the
* option as private data in the flags. This is achieved
* by shifting the option value to the left so to keep
* enough space for the flags. */
sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
sl->flags = (pargs.r_opt << PK_LIST_SHIFT);
if (configname)
sl->flags |= PK_LIST_CONFIG;
if (pargs.r_opt == oHiddenRecipient
|| pargs.r_opt == oHiddenRecipientFile)
sl->flags |= PK_LIST_HIDDEN;
if (pargs.r_opt == oRecipientFile
|| pargs.r_opt == oHiddenRecipientFile)
sl->flags |= PK_LIST_FROM_FILE;
any_explicit_recipient = 1;
break;
case oEncryptTo:
case oHiddenEncryptTo:
/* Store an additional recipient. */
sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
sl->flags = ((pargs.r_opt << PK_LIST_SHIFT) | PK_LIST_ENCRYPT_TO);
if (configname)
sl->flags |= PK_LIST_CONFIG;
if (pargs.r_opt == oHiddenEncryptTo)
sl->flags |= PK_LIST_HIDDEN;
break;
case oNoEncryptTo:
opt.no_encrypt_to = 1;
break;
case oEncryptToDefaultKey:
opt.encrypt_to_default_key = configname ? 2 : 1;
break;
case oTrySecretKey:
add_to_strlist2 (&opt.secret_keys_to_try,
pargs.r.ret_str, utf8_strings);
break;
case oMimemode: opt.mimemode = opt.textmode = 1; break;
case oTextmodeShort: opt.textmode = 2; break;
case oTextmode: opt.textmode=1; break;
case oNoTextmode: opt.textmode=opt.mimemode=0; break;
case oExpert: opt.expert = 1; break;
case oNoExpert: opt.expert = 0; break;
case oDefSigExpire:
if(*pargs.r.ret_str!='\0')
{
if(parse_expire_string(pargs.r.ret_str)==(u32)-1)
log_error(_("'%s' is not a valid signature expiration\n"),
pargs.r.ret_str);
else
opt.def_sig_expire=pargs.r.ret_str;
}
break;
case oAskSigExpire: opt.ask_sig_expire = 1; break;
case oNoAskSigExpire: opt.ask_sig_expire = 0; break;
case oDefCertExpire:
if(*pargs.r.ret_str!='\0')
{
if(parse_expire_string(pargs.r.ret_str)==(u32)-1)
log_error(_("'%s' is not a valid signature expiration\n"),
pargs.r.ret_str);
else
opt.def_cert_expire=pargs.r.ret_str;
}
break;
case oAskCertExpire: opt.ask_cert_expire = 1; break;
case oNoAskCertExpire: opt.ask_cert_expire = 0; break;
case oDefCertLevel: opt.def_cert_level=pargs.r.ret_int; break;
case oMinCertLevel: opt.min_cert_level=pargs.r.ret_int; break;
case oAskCertLevel: opt.ask_cert_level = 1; break;
case oNoAskCertLevel: opt.ask_cert_level = 0; break;
case oLocalUser: /* store the local users */
sl = add_to_strlist2( &locusr, pargs.r.ret_str, utf8_strings );
sl->flags = (pargs.r_opt << PK_LIST_SHIFT);
if (configname)
sl->flags |= PK_LIST_CONFIG;
break;
case oSender:
{
char *mbox = mailbox_from_userid (pargs.r.ret_str, 0);
if (!mbox)
log_error (_("\"%s\" is not a proper mail address\n"),
pargs.r.ret_str);
else
{
add_to_strlist (&opt.sender_list, mbox);
xfree (mbox);
}
}
break;
case oCompress:
/* this is the -z command line option */
opt.compress_level = opt.bz2_compress_level = pargs.r.ret_int;
opt.explicit_compress_option = 1;
break;
case oCompressLevel: opt.compress_level = pargs.r.ret_int; break;
case oBZ2CompressLevel: opt.bz2_compress_level = pargs.r.ret_int; break;
case oBZ2DecompressLowmem: opt.bz2_decompress_lowmem=1; break;
case oPassphrase:
set_passphrase_from_string (pargs.r_type ? pargs.r.ret_str : "");
break;
case oPassphraseFD:
pwfd = translate_sys2libc_fd_int (pargs.r.ret_int, 0);
break;
case oPassphraseFile:
pwfd = open_info_file (pargs.r.ret_str, 0, 1);
break;
case oPassphraseRepeat:
opt.passphrase_repeat = pargs.r.ret_int;
break;
case oPinentryMode:
opt.pinentry_mode = parse_pinentry_mode (pargs.r.ret_str);
if (opt.pinentry_mode == -1)
log_error (_("invalid pinentry mode '%s'\n"), pargs.r.ret_str);
break;
case oRequestOrigin:
opt.request_origin = parse_request_origin (pargs.r.ret_str);
if (opt.request_origin == -1)
log_error (_("invalid request origin '%s'\n"), pargs.r.ret_str);
break;
case oCommandFD:
opt.command_fd = translate_sys2libc_fd_int (pargs.r.ret_int, 0);
if (! gnupg_fd_valid (opt.command_fd))
log_error ("command-fd is invalid: %s\n", strerror (errno));
break;
case oCommandFile:
opt.command_fd = open_info_file (pargs.r.ret_str, 0, 1);
break;
case oCipherAlgo:
def_cipher_string = xstrdup(pargs.r.ret_str);
break;
case oDigestAlgo:
def_digest_string = xstrdup(pargs.r.ret_str);
break;
case oCompressAlgo:
/* If it is all digits, stick a Z in front of it for
later. This is for backwards compatibility with
versions that took the compress algorithm number. */
{
char *pt=pargs.r.ret_str;
while(*pt)
{
if (!isascii (*pt) || !isdigit (*pt))
break;
pt++;
}
if(*pt=='\0')
{
compress_algo_string=xmalloc(strlen(pargs.r.ret_str)+2);
strcpy(compress_algo_string,"Z");
strcat(compress_algo_string,pargs.r.ret_str);
}
else
compress_algo_string = xstrdup(pargs.r.ret_str);
}
break;
case oCertDigestAlgo:
cert_digest_string = xstrdup(pargs.r.ret_str);
break;
case oNoSecmemWarn:
gcry_control (GCRYCTL_DISABLE_SECMEM_WARN);
break;
case oRequireSecmem: require_secmem=1; break;
case oNoRequireSecmem: require_secmem=0; break;
case oNoPermissionWarn: opt.no_perm_warn=1; break;
case oDisplayCharset:
if( set_native_charset( pargs.r.ret_str ) )
log_error(_("'%s' is not a valid character set\n"),
pargs.r.ret_str);
break;
case oNotDashEscaped: opt.not_dash_escaped = 1; break;
case oEscapeFrom: opt.escape_from = 1; break;
case oNoEscapeFrom: opt.escape_from = 0; break;
case oLockOnce: opt.lock_once = 1; break;
case oLockNever:
dotlock_disable ();
break;
case oLockMultiple:
#ifndef __riscos__
opt.lock_once = 0;
#else /* __riscos__ */
riscos_not_implemented("lock-multiple");
#endif /* __riscos__ */
break;
case oKeyServer:
{
keyserver_spec_t keyserver;
keyserver = parse_keyserver_uri (pargs.r.ret_str, 0);
if (!keyserver)
log_error (_("could not parse keyserver URL\n"));
else
{
/* We only support a single keyserver. Later ones
override earlier ones. (Since we parse the
config file first and then the command line
arguments, the command line takes
precedence.) */
if (opt.keyserver)
free_keyserver_spec (opt.keyserver);
opt.keyserver = keyserver;
}
}
break;
case oKeyServerOptions:
if(!parse_keyserver_options(pargs.r.ret_str))
{
if(configname)
log_error(_("%s:%d: invalid keyserver options\n"),
configname,pargs.lineno);
else
log_error(_("invalid keyserver options\n"));
}
break;
case oImportOptions:
if(!parse_import_options(pargs.r.ret_str,&opt.import_options,1))
{
if(configname)
log_error(_("%s:%d: invalid import options\n"),
configname,pargs.lineno);
else
log_error(_("invalid import options\n"));
}
break;
case oImportFilter:
rc = parse_and_set_import_filter (pargs.r.ret_str);
if (rc)
log_error (_("invalid filter option: %s\n"), gpg_strerror (rc));
break;
case oExportOptions:
if(!parse_export_options(pargs.r.ret_str,&opt.export_options,1))
{
if(configname)
log_error(_("%s:%d: invalid export options\n"),
configname,pargs.lineno);
else
log_error(_("invalid export options\n"));
}
break;
case oExportFilter:
rc = parse_and_set_export_filter (pargs.r.ret_str);
if (rc)
log_error (_("invalid filter option: %s\n"), gpg_strerror (rc));
break;
case oListFilter:
rc = parse_and_set_list_filter (pargs.r.ret_str);
if (rc)
log_error (_("invalid filter option: %s\n"), gpg_strerror (rc));
break;
case oListOptions:
if(!parse_list_options(pargs.r.ret_str))
{
if(configname)
log_error(_("%s:%d: invalid list options\n"),
configname,pargs.lineno);
else
log_error(_("invalid list options\n"));
}
break;
case oVerifyOptions:
{
struct parse_options vopts[]=
{
{"show-photos",VERIFY_SHOW_PHOTOS,NULL,
N_("display photo IDs during signature verification")},
{"show-policy-urls",VERIFY_SHOW_POLICY_URLS,NULL,
N_("show policy URLs during signature verification")},
{"show-notations",VERIFY_SHOW_NOTATIONS,NULL,
N_("show all notations during signature verification")},
{"show-std-notations",VERIFY_SHOW_STD_NOTATIONS,NULL,
N_("show IETF standard notations during signature verification")},
{"show-standard-notations",VERIFY_SHOW_STD_NOTATIONS,NULL,
NULL},
{"show-user-notations",VERIFY_SHOW_USER_NOTATIONS,NULL,
N_("show user-supplied notations during signature verification")},
{"show-keyserver-urls",VERIFY_SHOW_KEYSERVER_URLS,NULL,
N_("show preferred keyserver URLs during signature verification")},
{"show-uid-validity",VERIFY_SHOW_UID_VALIDITY,NULL,
N_("show user ID validity during signature verification")},
{"show-unusable-uids",VERIFY_SHOW_UNUSABLE_UIDS,NULL,
N_("show revoked and expired user IDs in signature verification")},
{"show-primary-uid-only",VERIFY_SHOW_PRIMARY_UID_ONLY,NULL,
N_("show only the primary user ID in signature verification")},
{NULL,0,NULL,NULL}
};
if(!parse_options(pargs.r.ret_str,&opt.verify_options,vopts,1))
{
if(configname)
log_error(_("%s:%d: invalid verify options\n"),
configname,pargs.lineno);
else
log_error(_("invalid verify options\n"));
}
}
break;
case oTempDir: opt.temp_dir=pargs.r.ret_str; break;
case oExecPath:
if(set_exec_path(pargs.r.ret_str))
log_error(_("unable to set exec-path to %s\n"),pargs.r.ret_str);
else
opt.exec_path_set=1;
break;
case oSetNotation:
add_notation_data( pargs.r.ret_str, 0 );
add_notation_data( pargs.r.ret_str, 1 );
break;
case oSigNotation: add_notation_data( pargs.r.ret_str, 0 ); break;
case oCertNotation: add_notation_data( pargs.r.ret_str, 1 ); break;
case oKnownNotation: register_known_notation (pargs.r.ret_str); break;
case oShowNotation:
deprecated_warning(configname,pargs.lineno,"--show-notation",
"--list-options ","show-notations");
deprecated_warning(configname,pargs.lineno,"--show-notation",
"--verify-options ","show-notations");
opt.list_options|=LIST_SHOW_NOTATIONS;
opt.verify_options|=VERIFY_SHOW_NOTATIONS;
break;
case oNoShowNotation:
deprecated_warning(configname,pargs.lineno,"--no-show-notation",
"--list-options ","no-show-notations");
deprecated_warning(configname,pargs.lineno,"--no-show-notation",
"--verify-options ","no-show-notations");
opt.list_options&=~LIST_SHOW_NOTATIONS;
opt.verify_options&=~VERIFY_SHOW_NOTATIONS;
break;
case oUtf8Strings: utf8_strings = 1; break;
case oNoUtf8Strings:
#ifdef HAVE_W32_SYSTEM
utf8_strings = 0;
#endif
break;
case oDisableCipherAlgo:
{
int algo = string_to_cipher_algo (pargs.r.ret_str);
gcry_cipher_ctl (NULL, GCRYCTL_DISABLE_ALGO, &algo, sizeof algo);
}
break;
case oDisablePubkeyAlgo:
{
int algo = gcry_pk_map_name (pargs.r.ret_str);
gcry_pk_ctl (GCRYCTL_DISABLE_ALGO, &algo, sizeof algo);
}
break;
case oNoSigCache: opt.no_sig_cache = 1; break;
case oAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid = 1; break;
case oNoAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid=0; break;
case oAllowFreeformUID: opt.allow_freeform_uid = 1; break;
case oNoAllowFreeformUID: opt.allow_freeform_uid = 0; break;
case oNoLiteral: opt.no_literal = 1; break;
- case oSetFilesize: opt.set_filesize = pargs.r.ret_ulong; break;
+
+ case oSetFilesize:
+ /* There are restricts on the value (e.g. < 2^32); you
+ * need to check the entire code to understand this. */
+ opt.set_filesize = pargs.r.ret_ulong;
+ break;
+
case oFastListMode: opt.fast_list_mode = 1; break;
case oFixedListMode: /* Dummy */ break;
case oLegacyListMode: opt.legacy_list_mode = 1; break;
case oPrintDANERecords: print_dane_records = 1; break;
case oListOnly: opt.list_only=1; break;
case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
case oIgnoreValidFrom: opt.ignore_valid_from = 1; break;
case oIgnoreCrcError: opt.ignore_crc_error = 1; break;
case oIgnoreMDCError: opt.ignore_mdc_error = 1; break;
case oNoRandomSeedFile: use_random_seed = 0; break;
case oAutoKeyImport: opt.flags.auto_key_import = 1; break;
case oNoAutoKeyImport: opt.flags.auto_key_import = 0; break;
case oAutoKeyRetrieve:
opt.keyserver_options.options |= KEYSERVER_AUTO_KEY_RETRIEVE;
break;
case oNoAutoKeyRetrieve:
opt.keyserver_options.options &= ~KEYSERVER_AUTO_KEY_RETRIEVE;
break;
case oShowSessionKey: opt.show_session_key = 1; break;
case oOverrideSessionKey:
opt.override_session_key = pargs.r.ret_str;
break;
case oOverrideSessionKeyFD:
ovrseskeyfd = translate_sys2libc_fd_int (pargs.r.ret_int, 0);
break;
case oMergeOnly:
deprecated_warning(configname,pargs.lineno,"--merge-only",
"--import-options ","merge-only");
opt.import_options|=IMPORT_MERGE_ONLY;
break;
case oAllowSecretKeyImport: /* obsolete */ break;
case oTryAllSecrets: opt.try_all_secrets = 1; break;
case oTrustedKey: register_trusted_key( pargs.r.ret_str ); break;
case oEnableSpecialFilenames:
enable_special_filenames ();
break;
case oNoExpensiveTrustChecks: opt.no_expensive_trust_checks=1; break;
case oAutoCheckTrustDB: opt.no_auto_check_trustdb=0; break;
case oNoAutoCheckTrustDB: opt.no_auto_check_trustdb=1; break;
case oPreservePermissions: opt.preserve_permissions=1; break;
case oDefaultPreferenceList:
opt.def_preference_list = pargs.r.ret_str;
break;
case oDefaultKeyserverURL:
{
keyserver_spec_t keyserver;
keyserver = parse_keyserver_uri (pargs.r.ret_str,1 );
if (!keyserver)
log_error (_("could not parse keyserver URL\n"));
else
free_keyserver_spec (keyserver);
opt.def_keyserver_url = pargs.r.ret_str;
}
break;
case oPersonalCipherPreferences:
pers_cipher_list=pargs.r.ret_str;
break;
case oPersonalDigestPreferences:
pers_digest_list=pargs.r.ret_str;
break;
case oPersonalCompressPreferences:
pers_compress_list=pargs.r.ret_str;
break;
case oAgentProgram: opt.agent_program = pargs.r.ret_str; break;
case oKeyboxdProgram: opt.keyboxd_program = pargs.r.ret_str; break;
case oDirmngrProgram: opt.dirmngr_program = pargs.r.ret_str; break;
case oDisableDirmngr: opt.disable_dirmngr = 1; break;
case oWeakDigest:
additional_weak_digest(pargs.r.ret_str);
break;
case oUnwrap:
opt.unwrap_encryption = 1;
break;
case oOnlySignTextIDs:
opt.only_sign_text_ids = 1;
break;
case oDisplay:
set_opt_session_env ("DISPLAY", pargs.r.ret_str);
break;
case oTTYname:
set_opt_session_env ("GPG_TTY", pargs.r.ret_str);
break;
case oTTYtype:
set_opt_session_env ("TERM", pargs.r.ret_str);
break;
case oXauthority:
set_opt_session_env ("XAUTHORITY", pargs.r.ret_str);
break;
case oLCctype: opt.lc_ctype = pargs.r.ret_str; break;
case oLCmessages: opt.lc_messages = pargs.r.ret_str; break;
case oGroup: add_group(pargs.r.ret_str); break;
case oUnGroup: rm_group(pargs.r.ret_str); break;
case oNoGroups:
while(opt.grouplist)
{
struct groupitem *iter=opt.grouplist;
free_strlist(iter->values);
opt.grouplist=opt.grouplist->next;
xfree(iter);
}
break;
case oMangleDosFilenames: opt.mangle_dos_filenames = 1; break;
case oNoMangleDosFilenames: opt.mangle_dos_filenames = 0; break;
case oEnableProgressFilter: opt.enable_progress_filter = 1; break;
case oMultifile: multifile=1; break;
case oKeyidFormat:
if(ascii_strcasecmp(pargs.r.ret_str,"short")==0)
opt.keyid_format=KF_SHORT;
else if(ascii_strcasecmp(pargs.r.ret_str,"long")==0)
opt.keyid_format=KF_LONG;
else if(ascii_strcasecmp(pargs.r.ret_str,"0xshort")==0)
opt.keyid_format=KF_0xSHORT;
else if(ascii_strcasecmp(pargs.r.ret_str,"0xlong")==0)
opt.keyid_format=KF_0xLONG;
else if(ascii_strcasecmp(pargs.r.ret_str,"none")==0)
opt.keyid_format = KF_NONE;
else
log_error("unknown keyid-format '%s'\n",pargs.r.ret_str);
break;
case oExitOnStatusWriteError:
opt.exit_on_status_write_error = 1;
break;
case oLimitCardInsertTries:
opt.limit_card_insert_tries = pargs.r.ret_int;
break;
case oRequireCrossCert: opt.flags.require_cross_cert=1; break;
case oNoRequireCrossCert: opt.flags.require_cross_cert=0; break;
case oAutoKeyLocate:
if (default_akl)
{
/* This is the first time --auto-key-locate is seen.
* We need to reset the default akl. */
default_akl = 0;
release_akl();
}
if(!parse_auto_key_locate(pargs.r.ret_str))
{
if(configname)
log_error(_("%s:%d: invalid auto-key-locate list\n"),
configname,pargs.lineno);
else
log_error(_("invalid auto-key-locate list\n"));
}
break;
case oNoAutoKeyLocate:
release_akl();
break;
case oKeyOrigin:
if(!parse_key_origin (pargs.r.ret_str))
log_error (_("invalid argument for option \"%.50s\"\n"),
"--key-origin");
break;
case oEnableLargeRSA:
#if SECMEM_BUFFER_SIZE >= 65536
opt.flags.large_rsa=1;
#else
if (configname)
log_info("%s:%d: WARNING: gpg not built with large secure "
"memory buffer. Ignoring enable-large-rsa\n",
configname,pargs.lineno);
else
log_info("WARNING: gpg not built with large secure "
"memory buffer. Ignoring --enable-large-rsa\n");
#endif /* SECMEM_BUFFER_SIZE >= 65536 */
break;
case oDisableLargeRSA: opt.flags.large_rsa=0;
break;
case oEnableDSA2: opt.flags.dsa2=1; break;
case oDisableDSA2: opt.flags.dsa2=0; break;
case oAllowWeakDigestAlgos:
opt.flags.allow_weak_digest_algos = 1;
break;
case oAllowWeakKeySignatures:
opt.flags.allow_weak_key_signatures = 1;
break;
case oAllowOldCipherAlgos:
opt.flags.allow_old_cipher_algos = 1;
break;
case oFakedSystemTime:
{
size_t len = strlen (pargs.r.ret_str);
int freeze = 0;
time_t faked_time;
if (len > 0 && pargs.r.ret_str[len-1] == '!')
{
freeze = 1;
pargs.r.ret_str[len-1] = '\0';
}
faked_time = isotime2epoch (pargs.r.ret_str);
if (faked_time == (time_t)(-1))
faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
gnupg_set_time (faked_time, freeze);
}
break;
case oNoAutostart: opt.autostart = 0; break;
case oNoSymkeyCache: opt.no_symkey_cache = 1; break;
case oDefaultNewKeyAlgo:
opt.def_new_key_algo = pargs.r.ret_str;
break;
case oUseOnlyOpenPGPCard:
opt.flags.use_only_openpgp_card = 1;
break;
case oFullTimestrings:
opt.flags.full_timestrings = 1;
break;
case oForbidGenKey:
mopt.forbid_gen_key = 1;
break;
case oRequireCompliance:
opt.flags.require_compliance = 1;
break;
case oAddDesigRevoker:
if (!strcmp (pargs.r.ret_str, "clear"))
FREE_STRLIST (opt.desig_revokers);
else
append_to_strlist (&opt.desig_revokers, pargs.r.ret_str);
break;
case oAssertSigner:
add_to_strlist (&opt.assert_signer_list, pargs.r.ret_str);
break;
case oKbxBufferSize:
keybox_set_buffersize (pargs.r.ret_ulong, 0);
break;
case oNoop: break;
default:
if (configname)
pargs.err = ARGPARSE_PRINT_WARNING;
else
{
pargs.err = ARGPARSE_PRINT_ERROR;
/* The argparse function calls a plain exit and thus
* we need to print a status here. */
write_status_failure ("option-parser",
gpg_error(GPG_ERR_GENERAL));
}
break;
}
}
gpgrt_argparse (NULL, &pargs, NULL); /* Release internal state. */
if (log_get_errorcount (0))
{
write_status_failure ("option-parser", gpg_error(GPG_ERR_GENERAL));
g10_exit(2);
}
/* Process common component options. */
if (parse_comopt (GNUPG_MODULE_NAME_GPG, debug_argparser))
{
write_status_failure ("option-parser", gpg_error(GPG_ERR_GENERAL));
g10_exit(2);
}
if (opt.use_keyboxd)
log_info ("Note: Please move option \"%s\" to \"common.conf\"\n",
"use-keyboxd");
opt.use_keyboxd = comopt.use_keyboxd; /* Override. */
if (opt.keyboxd_program)
log_info ("Note: Please move option \"%s\" to \"common.conf\"\n",
"keyboxd-program");
if (!opt.keyboxd_program && comopt.keyboxd_program)
{
opt.keyboxd_program = comopt.keyboxd_program;
comopt.keyboxd_program = NULL;
}
if (comopt.no_autostart)
opt.autostart = 0;
/* The command --gpgconf-list is pretty simple and may be called
directly after the option parsing. */
if (cmd == aGPGConfList)
{
gpgconf_list ();
g10_exit (0);
}
xfree (last_configname);
if (print_dane_records)
log_error ("invalid option \"%s\"; use \"%s\" instead\n",
"--print-dane-records",
"--export-options export-dane");
if (log_get_errorcount (0))
{
write_status_failure ("option-checking", gpg_error(GPG_ERR_GENERAL));
g10_exit(2);
}
if( nogreeting )
greeting = 0;
if( greeting )
{
es_fprintf (es_stderr, "%s %s; %s\n",
gpgrt_strusage(11), gpgrt_strusage(13), gpgrt_strusage(14));
es_fprintf (es_stderr, "%s\n", gpgrt_strusage(15) );
}
#ifdef IS_DEVELOPMENT_VERSION
if (!opt.batch)
{
const char *s;
if((s=gpgrt_strusage(25)))
log_info("%s\n",s);
if((s=gpgrt_strusage(26)))
log_info("%s\n",s);
if((s=gpgrt_strusage(27)))
log_info("%s\n",s);
}
#endif
/* Init threading which is used by some helper functions. */
npth_init ();
assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
if (logfile)
{
log_set_file (logfile);
log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX
| GPGRT_LOG_WITH_TIME
| GPGRT_LOG_WITH_PID ));
}
else if (opt_log_time)
log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_NO_REGISTRY
|GPGRT_LOG_WITH_TIME));
if (opt.verbose > 2)
log_info ("using character set '%s'\n", get_native_charset ());
if( may_coredump && !opt.quiet )
log_info(_("WARNING: program may create a core file!\n"));
if (eyes_only) {
if (opt.set_filename)
log_info(_("WARNING: %s overrides %s\n"),
"--for-your-eyes-only","--set-filename");
opt.set_filename="_CONSOLE";
}
if (opt.no_literal) {
log_info(_("Note: %s is not for normal use!\n"), "--no-literal");
if (opt.textmode)
log_error(_("%s not allowed with %s!\n"),
"--textmode", "--no-literal" );
if (opt.set_filename)
log_error(_("%s makes no sense with %s!\n"),
eyes_only?"--for-your-eyes-only":"--set-filename",
"--no-literal" );
}
if (opt.set_filesize)
log_info(_("Note: %s is not for normal use!\n"), "--set-filesize");
if( opt.batch )
tty_batchmode( 1 );
if (gnupg_faked_time_p ())
{
gnupg_isotime_t tbuf;
log_info (_("WARNING: running with faked system time: "));
gnupg_get_isotime (tbuf);
dump_isotime (tbuf);
log_printf ("\n");
}
/* Print a warning if an argument looks like an option. */
if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
{
int i;
for (i=0; i < argc; i++)
if (argv[i][0] == '-' && argv[i][1] == '-')
log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
}
gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
if(require_secmem && !got_secmem)
{
log_info(_("will not run with insecure memory due to %s\n"),
"--require-secmem");
write_status_failure ("option-checking", gpg_error(GPG_ERR_GENERAL));
g10_exit(2);
}
set_debug (debug_level);
if (opt.verbose) /* Print the compatibility flags. */
parse_compatibility_flags (NULL, &opt.compat_flags, compatibility_flags);
gnupg_set_compliance_extra_info (opt.min_rsa_length);
if (DBG_CLOCK)
log_clock ("start");
/* Do these after the switch(), so they can override settings. */
if (PGP7)
{
/* That does not anymore work because we have no more support
for v3 signatures. */
opt.escape_from=1;
opt.ask_sig_expire=0;
}
else if(PGP8)
{
opt.escape_from=1;
}
if( def_cipher_string ) {
opt.def_cipher_algo = string_to_cipher_algo (def_cipher_string);
xfree(def_cipher_string); def_cipher_string = NULL;
if ( openpgp_cipher_test_algo (opt.def_cipher_algo) )
log_error(_("selected cipher algorithm is invalid\n"));
}
if( def_digest_string ) {
opt.def_digest_algo = string_to_digest_algo (def_digest_string);
xfree(def_digest_string); def_digest_string = NULL;
if ( openpgp_md_test_algo (opt.def_digest_algo) )
log_error(_("selected digest algorithm is invalid\n"));
}
if( compress_algo_string ) {
opt.compress_algo = string_to_compress_algo(compress_algo_string);
xfree(compress_algo_string); compress_algo_string = NULL;
if( check_compress_algo(opt.compress_algo) )
log_error(_("selected compression algorithm is invalid\n"));
}
if( cert_digest_string ) {
opt.cert_digest_algo = string_to_digest_algo (cert_digest_string);
xfree(cert_digest_string); cert_digest_string = NULL;
if (openpgp_md_test_algo(opt.cert_digest_algo))
log_error(_("selected certification digest algorithm is invalid\n"));
}
if( s2k_cipher_string ) {
opt.s2k_cipher_algo = string_to_cipher_algo (s2k_cipher_string);
xfree(s2k_cipher_string); s2k_cipher_string = NULL;
if (openpgp_cipher_test_algo (opt.s2k_cipher_algo))
log_error(_("selected cipher algorithm is invalid\n"));
}
if( s2k_digest_string ) {
opt.s2k_digest_algo = string_to_digest_algo (s2k_digest_string);
xfree(s2k_digest_string); s2k_digest_string = NULL;
if (openpgp_md_test_algo(opt.s2k_digest_algo))
log_error(_("selected digest algorithm is invalid\n"));
}
if( opt.completes_needed < 1 )
log_error(_("completes-needed must be greater than 0\n"));
if( opt.marginals_needed < 2 )
log_error(_("marginals-needed must be greater than 1\n"));
if( opt.max_cert_depth < 1 || opt.max_cert_depth > 255 )
log_error(_("max-cert-depth must be in the range from 1 to 255\n"));
if(opt.def_cert_level<0 || opt.def_cert_level>3)
log_error(_("invalid default-cert-level; must be 0, 1, 2, or 3\n"));
if( opt.min_cert_level < 1 || opt.min_cert_level > 3 )
log_error(_("invalid min-cert-level; must be 1, 2, or 3\n"));
switch( opt.s2k_mode ) {
case 0:
if (!opt.quiet)
log_info(_("Note: simple S2K mode (0) is strongly discouraged\n"));
break;
case 1: case 3: break;
default:
log_error(_("invalid S2K mode; must be 0, 1 or 3\n"));
}
/* This isn't actually needed, but does serve to error out if the
string is invalid. */
if(opt.def_preference_list &&
keygen_set_std_prefs(opt.def_preference_list,0))
log_error(_("invalid default preferences\n"));
if(pers_cipher_list &&
keygen_set_std_prefs(pers_cipher_list,PREFTYPE_SYM))
log_error(_("invalid personal cipher preferences\n"));
if(pers_digest_list &&
keygen_set_std_prefs(pers_digest_list,PREFTYPE_HASH))
log_error(_("invalid personal digest preferences\n"));
if(pers_compress_list &&
keygen_set_std_prefs(pers_compress_list,PREFTYPE_ZIP))
log_error(_("invalid personal compress preferences\n"));
/* Check chunk size. Please fix also the man page if you change
* the default. The limits are given by the specs. */
if (!opt.chunk_size)
opt.chunk_size = 22; /* Default to the suggested max of 4 MiB. */
else if (opt.chunk_size < 6)
{
opt.chunk_size = 6;
log_info (_("chunk size invalid - using %d\n"), opt.chunk_size);
}
else if (opt.chunk_size > (allow_large_chunks? 62 : 22))
{
opt.chunk_size = (allow_large_chunks? 62 : 22);
log_info (_("chunk size invalid - using %d\n"), opt.chunk_size);
}
/* We don't support all possible commands with multifile yet */
if(multifile)
{
char *cmdname;
switch(cmd)
{
case aSign:
cmdname="--sign";
break;
case aSignEncr:
cmdname="--sign --encrypt";
break;
case aClearsign:
cmdname="--clear-sign";
break;
case aDetachedSign:
cmdname="--detach-sign";
break;
case aSym:
cmdname="--symmetric";
break;
case aEncrSym:
cmdname="--symmetric --encrypt";
break;
case aStore:
cmdname="--store";
break;
default:
cmdname=NULL;
break;
}
if(cmdname)
log_error(_("%s does not yet work with %s\n"),cmdname,"--multifile");
}
if( log_get_errorcount(0) )
{
write_status_failure ("option-postprocessing",
gpg_error(GPG_ERR_GENERAL));
g10_exit (2);
}
if(opt.compress_level==0)
opt.compress_algo=COMPRESS_ALGO_NONE;
/* Check our chosen algorithms against the list of legal
algorithms. */
if(!GNUPG)
{
const char *badalg=NULL;
preftype_t badtype=PREFTYPE_NONE;
if(opt.def_cipher_algo
&& !algo_available(PREFTYPE_SYM,opt.def_cipher_algo,NULL))
{
badalg = openpgp_cipher_algo_name (opt.def_cipher_algo);
badtype = PREFTYPE_SYM;
}
else if(opt.def_digest_algo
&& !algo_available(PREFTYPE_HASH,opt.def_digest_algo,NULL))
{
badalg = gcry_md_algo_name (opt.def_digest_algo);
badtype = PREFTYPE_HASH;
}
else if(opt.cert_digest_algo
&& !algo_available(PREFTYPE_HASH,opt.cert_digest_algo,NULL))
{
badalg = gcry_md_algo_name (opt.cert_digest_algo);
badtype = PREFTYPE_HASH;
}
else if(opt.compress_algo!=-1
&& !algo_available(PREFTYPE_ZIP,opt.compress_algo,NULL))
{
badalg = compress_algo_to_string(opt.compress_algo);
badtype = PREFTYPE_ZIP;
}
if(badalg)
{
switch(badtype)
{
case PREFTYPE_SYM:
log_info (_("cipher algorithm '%s'"
" may not be used in %s mode\n"),
badalg,
gnupg_compliance_option_string (opt.compliance));
break;
case PREFTYPE_HASH:
log_info (_("digest algorithm '%s'"
" may not be used in %s mode\n"),
badalg,
gnupg_compliance_option_string (opt.compliance));
break;
case PREFTYPE_ZIP:
log_info (_("compression algorithm '%s'"
" may not be used in %s mode\n"),
badalg,
gnupg_compliance_option_string (opt.compliance));
break;
default:
BUG();
}
compliance_failure();
}
}
/* Check our chosen algorithms against the list of allowed
* algorithms in the current compliance mode, and fail hard if it
* is not. This is us being nice to the user informing her early
* that the chosen algorithms are not available. We also check
* and enforce this right before the actual operation. */
if (opt.def_cipher_algo
&& ! gnupg_cipher_is_allowed (opt.compliance,
cmd == aEncr
|| cmd == aSignEncr
|| cmd == aEncrSym
|| cmd == aSym
|| cmd == aSignSym
|| cmd == aSignEncrSym,
opt.def_cipher_algo,
GCRY_CIPHER_MODE_NONE))
log_error (_("cipher algorithm '%s' may not be used in %s mode\n"),
openpgp_cipher_algo_name (opt.def_cipher_algo),
gnupg_compliance_option_string (opt.compliance));
if (opt.def_digest_algo
&& ! gnupg_digest_is_allowed (opt.compliance,
cmd == aSign
|| cmd == aSignEncr
|| cmd == aSignEncrSym
|| cmd == aSignSym
|| cmd == aClearsign,
opt.def_digest_algo))
log_error (_("digest algorithm '%s' may not be used in %s mode\n"),
gcry_md_algo_name (opt.def_digest_algo),
gnupg_compliance_option_string (opt.compliance));
/* Fail hard. */
if (log_get_errorcount (0))
{
write_status_failure ("option-checking", gpg_error(GPG_ERR_GENERAL));
g10_exit (2);
}
/* Set the random seed file. */
if (use_random_seed)
{
char *p = make_filename (gnupg_homedir (), "random_seed", NULL );
gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p);
if (!gnupg_access (p, F_OK))
register_secured_file (p);
xfree(p);
}
/* If there is no command but the --fingerprint is given, default
to the --list-keys command. */
if (!cmd && fpr_maybe_cmd)
{
set_cmd (&cmd, aListKeys);
}
if( opt.verbose > 1 )
set_packet_list_mode(1);
/* Add the keyrings, but not for some special commands. We always
* need to add the keyrings if we are running under SELinux, this
* is so that the rings are added to the list of secured files.
* We do not add any keyring if --no-keyring or --use-keyboxd has
* been used. Note that keydb_add_resource may create a new
* homedir and also tries to write a common.conf to enable the use
* of the keyboxd - in this case a special error code is returned
* and use_keyboxd is then also set. */
if (!opt.use_keyboxd
&& default_keyring >= 0
&& (ALWAYS_ADD_KEYRINGS
|| (cmd != aDeArmor && cmd != aEnArmor && cmd != aGPGConfTest)))
{
gpg_error_t tmperr = 0;
if (!nrings || default_keyring > 0) /* Add default ring. */
tmperr = keydb_add_resource ("pubring" EXTSEP_S GPGEXT_GPG,
KEYDB_RESOURCE_FLAG_DEFAULT);
if (gpg_err_code (tmperr) == GPG_ERR_TRUE && opt.use_keyboxd)
; /* The keyboxd has been enabled. */
else
{
for (sl = nrings; sl; sl = sl->next )
keydb_add_resource (sl->d, sl->flags);
}
}
FREE_STRLIST(nrings);
/* In loopback mode, never ask for the password multiple times. */
if (opt.pinentry_mode == PINENTRY_MODE_LOOPBACK)
{
opt.passphrase_repeat = 0;
}
/* If no pinentry is expected shunt
* gnupg_allow_set_foregound_window to avoid useless error
* messages on Windows. */
if (opt.pinentry_mode != PINENTRY_MODE_ASK)
{
gnupg_inhibit_set_foregound_window (1);
}
if (cmd == aGPGConfTest)
g10_exit(0);
if (pwfd != -1) /* Read the passphrase now. */
read_passphrase_from_fd (pwfd);
if (ovrseskeyfd != -1 ) /* Read the sessionkey now. */
read_sessionkey_from_fd (ovrseskeyfd);
fname = argc? *argv : NULL;
if(fname && utf8_strings)
opt.flags.utf8_filename=1;
ctrl = xcalloc (1, sizeof *ctrl);
gpg_init_default_ctrl (ctrl);
#ifndef NO_TRUST_MODELS
switch (cmd)
{
case aPrimegen:
case aPrintMD:
case aPrintMDs:
case aGenRandom:
case aDeArmor:
case aEnArmor:
case aListConfig:
case aListGcryptConfig:
break;
case aFixTrustDB:
case aExportOwnerTrust:
rc = setup_trustdb (0, trustdb_name);
break;
case aListTrustDB:
rc = setup_trustdb (argc? 1:0, trustdb_name);
break;
case aKeygen:
case aFullKeygen:
case aQuickKeygen:
rc = setup_trustdb (1, trustdb_name);
break;
default:
/* If we are using TM_ALWAYS, we do not need to create the
trustdb. */
rc = setup_trustdb (opt.trust_model != TM_ALWAYS, trustdb_name);
break;
}
if (rc)
log_error (_("failed to initialize the TrustDB: %s\n"),
gpg_strerror (rc));
#endif /*!NO_TRUST_MODELS*/
switch (cmd)
{
case aStore:
case aSym:
case aSign:
case aSignSym:
case aClearsign:
if (!opt.quiet && any_explicit_recipient)
log_info (_("WARNING: recipients (-r) given "
"without using public key encryption\n"));
break;
default:
break;
}
/* Check for certain command whether we need to migrate a
secring.gpg to the gpg-agent. */
switch (cmd)
{
case aListSecretKeys:
case aSign:
case aSignEncr:
case aSignEncrSym:
case aSignSym:
case aClearsign:
case aDecrypt:
case aSignKey:
case aLSignKey:
case aEditKey:
case aPasswd:
case aDeleteSecretKeys:
case aDeleteSecretAndPublicKeys:
case aQuickKeygen:
case aQuickAddUid:
case aQuickAddKey:
case aQuickAddADSK:
case aQuickRevUid:
case aQuickSetPrimaryUid:
case aQuickUpdatePref:
case aFullKeygen:
case aKeygen:
case aImport:
case aExportSecret:
case aExportSecretSub:
case aGenRevoke:
case aDesigRevoke:
case aCardEdit:
case aChangePIN:
migrate_secring (ctrl);
break;
case aListKeys:
if (opt.with_secret)
migrate_secring (ctrl);
break;
default:
break;
}
/* The command dispatcher. */
switch( cmd )
{
case aServer:
gpg_server (ctrl);
break;
case aStore: /* only store the file */
if( argc > 1 )
wrong_args("--store [filename]");
if( (rc = encrypt_store(fname)) )
{
write_status_failure ("store", rc);
log_error ("storing '%s' failed: %s\n",
print_fname_stdin(fname),gpg_strerror (rc) );
}
break;
case aSym: /* encrypt the given file only with the symmetric cipher */
if( argc > 1 )
wrong_args("--symmetric [filename]");
if( (rc = encrypt_symmetric(fname)) )
{
write_status_failure ("symencrypt", rc);
log_error (_("symmetric encryption of '%s' failed: %s\n"),
print_fname_stdin(fname),gpg_strerror (rc) );
}
break;
case aEncr: /* encrypt the given file */
if(multifile)
encrypt_crypt_files (ctrl, argc, argv, remusr);
else
{
if( argc > 1 )
wrong_args("--encrypt [filename]");
if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 0, NULL, -1)) )
{
write_status_failure ("encrypt", rc);
log_error("%s: encryption failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
}
break;
case aEncrSym:
/* This works with PGP 8 in the sense that it acts just like a
symmetric message. It doesn't work at all with 2 or 6. It
might work with 7, but alas, I don't have a copy to test
with right now. */
if( argc > 1 )
wrong_args("--symmetric --encrypt [filename]");
else if(opt.s2k_mode==0)
log_error(_("you cannot use --symmetric --encrypt"
" with --s2k-mode 0\n"));
else if (PGP7)
log_error(_("you cannot use --symmetric --encrypt"
" in %s mode\n"),
gnupg_compliance_option_string (opt.compliance));
else
{
if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 1, NULL, -1)) )
{
write_status_failure ("encrypt", rc);
log_error ("%s: encryption failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
}
break;
case aSign: /* sign the given file */
sl = NULL;
if( detached_sig ) { /* sign all files */
for( ; argc; argc--, argv++ )
add_to_strlist( &sl, *argv );
}
else {
if( argc > 1 )
wrong_args("--sign [filename]");
if( argc ) {
sl = xmalloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname);
}
}
if ((rc = sign_file (ctrl, sl, detached_sig, locusr, 0, NULL, NULL)))
{
write_status_failure ("sign", rc);
log_error ("signing failed: %s\n", gpg_strerror (rc) );
}
free_strlist(sl);
break;
case aSignEncr: /* sign and encrypt the given file */
if( argc > 1 )
wrong_args("--sign --encrypt [filename]");
if( argc ) {
sl = xmalloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname);
}
else
sl = NULL;
if ((rc = sign_file (ctrl, sl, detached_sig, locusr, 1, remusr, NULL)))
{
write_status_failure ("sign-encrypt", rc);
log_error("%s: sign+encrypt failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
free_strlist(sl);
break;
case aSignEncrSym: /* sign and encrypt the given file */
if( argc > 1 )
wrong_args("--symmetric --sign --encrypt [filename]");
else if(opt.s2k_mode==0)
log_error(_("you cannot use --symmetric --sign --encrypt"
" with --s2k-mode 0\n"));
else if (PGP7)
log_error(_("you cannot use --symmetric --sign --encrypt"
" in %s mode\n"),
gnupg_compliance_option_string (opt.compliance));
else
{
if( argc )
{
sl = xmalloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname);
}
else
sl = NULL;
if ((rc = sign_file (ctrl, sl, detached_sig, locusr,
2, remusr, NULL)))
{
write_status_failure ("sign-encrypt", rc);
log_error("%s: symmetric+sign+encrypt failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
free_strlist(sl);
}
break;
case aSignSym: /* sign and conventionally encrypt the given file */
if (argc > 1)
wrong_args("--sign --symmetric [filename]");
rc = sign_symencrypt_file (ctrl, fname, locusr);
if (rc)
{
write_status_failure ("sign-symencrypt", rc);
log_error("%s: sign+symmetric failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
break;
case aClearsign: /* make a clearsig */
if( argc > 1 )
wrong_args("--clear-sign [filename]");
if( (rc = clearsign_file (ctrl, fname, locusr, NULL)) )
{
write_status_failure ("sign", rc);
log_error("%s: clear-sign failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
break;
case aVerify:
if (multifile)
{
if ((rc = verify_files (ctrl, argc, argv)))
log_error("verify files failed: %s\n", gpg_strerror (rc) );
}
else
{
if ((rc = verify_signatures (ctrl, argc, argv)))
log_error("verify signatures failed: %s\n", gpg_strerror (rc) );
}
if (rc)
write_status_failure ("verify", rc);
break;
case aDecrypt:
if (multifile)
decrypt_messages (ctrl, argc, argv);
else
{
if( argc > 1 )
wrong_args("--decrypt [filename]");
if( (rc = decrypt_message (ctrl, fname) ))
{
write_status_failure ("decrypt", rc);
log_error("decrypt_message failed: %s\n", gpg_strerror (rc) );
}
}
break;
case aQuickSignKey:
case aQuickLSignKey:
{
const char *fpr;
if (argc < 1)
wrong_args ("--quick-[l]sign-key fingerprint [userids]");
fpr = *argv++; argc--;
sl = NULL;
for( ; argc; argc--, argv++)
append_to_strlist2 (&sl, *argv, utf8_strings);
keyedit_quick_sign (ctrl, fpr, sl, locusr, (cmd == aQuickLSignKey));
free_strlist (sl);
}
break;
case aQuickRevSig:
{
const char *userid, *siguserid;
if (argc < 2)
wrong_args ("--quick-revoke-sig USER-ID SIG-USER-ID [userids]");
userid = *argv++; argc--;
siguserid = *argv++; argc--;
sl = NULL;
for( ; argc; argc--, argv++)
append_to_strlist2 (&sl, *argv, utf8_strings);
keyedit_quick_revsig (ctrl, userid, siguserid, sl);
free_strlist (sl);
}
break;
case aSignKey:
if( argc != 1 )
wrong_args("--sign-key user-id");
/* fall through */
case aLSignKey:
if( argc != 1 )
wrong_args("--lsign-key user-id");
/* fall through */
sl=NULL;
if(cmd==aSignKey)
append_to_strlist(&sl,"sign");
else if(cmd==aLSignKey)
append_to_strlist(&sl,"lsign");
else
BUG();
append_to_strlist( &sl, "save" );
username = make_username( fname );
keyedit_menu (ctrl, username, locusr, sl, 0, 0 );
xfree(username);
free_strlist(sl);
break;
case aEditKey: /* Edit a key signature */
if( !argc )
wrong_args("--edit-key user-id [commands]");
username = make_username( fname );
if( argc > 1 ) {
sl = NULL;
for( argc--, argv++ ; argc; argc--, argv++ )
append_to_strlist( &sl, *argv );
keyedit_menu (ctrl, username, locusr, sl, 0, 1 );
free_strlist(sl);
}
else
keyedit_menu (ctrl, username, locusr, NULL, 0, 1 );
xfree(username);
break;
case aPasswd:
if (argc != 1)
wrong_args("--change-passphrase <user-id>");
else
{
username = make_username (fname);
keyedit_passwd (ctrl, username);
xfree (username);
}
break;
case aDeleteKeys:
case aDeleteSecretKeys:
case aDeleteSecretAndPublicKeys:
sl = NULL;
/* Print a note if the user did not specify any key. */
if (!argc && !opt.quiet)
log_info (_("Note: %s\n"), gpg_strerror (GPG_ERR_NO_KEY));
/* I'm adding these in reverse order as add_to_strlist2
reverses them again, and it's easier to understand in the
proper order :) */
for( ; argc; argc-- )
add_to_strlist2( &sl, argv[argc-1], utf8_strings );
delete_keys (ctrl, sl,
cmd==aDeleteSecretKeys, cmd==aDeleteSecretAndPublicKeys);
free_strlist(sl);
break;
case aCheckKeys:
opt.check_sigs = 1; /* fall through */
case aListSigs:
opt.list_sigs = 1; /* fall through */
case aListKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist2( &sl, *argv, utf8_strings );
public_key_list (ctrl, sl, 0, 0);
free_strlist(sl);
break;
case aListSecretKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist2( &sl, *argv, utf8_strings );
secret_key_list (ctrl, sl);
free_strlist(sl);
break;
case aLocateKeys:
case aLocateExtKeys:
sl = NULL;
for (; argc; argc--, argv++)
add_to_strlist2( &sl, *argv, utf8_strings );
if (cmd == aLocateExtKeys && akl_empty_or_only_local ())
{
/* This is a kludge to let --locate-external-keys even
* work if the config file has --no-auto-key-locate. This
* better matches the expectations of the user. */
release_akl ();
parse_auto_key_locate (DEFAULT_AKL_LIST);
}
public_key_list (ctrl, sl, 1, cmd == aLocateExtKeys);
free_strlist (sl);
break;
case aQuickKeygen:
{
const char *x_algo, *x_usage, *x_expire;
if (argc < 1 || argc > 4)
wrong_args("--quick-generate-key USER-ID [ALGO [USAGE [EXPIRE]]]");
username = make_username (fname);
argv++, argc--;
x_algo = "";
x_usage = "";
x_expire = "";
if (argc)
{
x_algo = *argv++; argc--;
if (argc)
{
x_usage = *argv++; argc--;
if (argc)
{
x_expire = *argv++; argc--;
}
}
}
if (mopt.forbid_gen_key)
gen_key_forbidden ();
else
quick_generate_keypair (ctrl, username, x_algo, x_usage, x_expire);
xfree (username);
}
break;
case aKeygen: /* generate a key */
if (mopt.forbid_gen_key)
gen_key_forbidden ();
else if( opt.batch )
{
if( argc > 1 )
wrong_args("--generate-key [parameterfile]");
generate_keypair (ctrl, 0, argc? *argv : NULL, NULL, 0);
}
else
{
if (opt.command_fd != -1 && argc)
{
if( argc > 1 )
wrong_args("--generate-key [parameterfile]");
opt.batch = 1;
generate_keypair (ctrl, 0, argc? *argv : NULL, NULL, 0);
}
else if (argc)
wrong_args ("--generate-key");
else
generate_keypair (ctrl, 0, NULL, NULL, 0);
}
break;
case aFullKeygen: /* Generate a key with all options. */
if (mopt.forbid_gen_key)
gen_key_forbidden ();
else if (opt.batch)
{
if (argc > 1)
wrong_args ("--full-generate-key [parameterfile]");
generate_keypair (ctrl, 1, argc? *argv : NULL, NULL, 0);
}
else
{
if (argc)
wrong_args("--full-generate-key");
generate_keypair (ctrl, 1, NULL, NULL, 0);
}
break;
case aQuickAddUid:
{
const char *uid, *newuid;
if (argc != 2)
wrong_args ("--quick-add-uid USER-ID NEW-USER-ID");
uid = *argv++; argc--;
newuid = *argv++; argc--;
keyedit_quick_adduid (ctrl, uid, newuid);
}
break;
case aQuickAddKey:
{
const char *x_fpr, *x_algo, *x_usage, *x_expire;
if (argc < 1 || argc > 4)
wrong_args ("--quick-add-key FINGERPRINT [ALGO [USAGE [EXPIRE]]]");
x_fpr = *argv++; argc--;
x_algo = "";
x_usage = "";
x_expire = "";
if (argc)
{
x_algo = *argv++; argc--;
if (argc)
{
x_usage = *argv++; argc--;
if (argc)
{
x_expire = *argv++; argc--;
}
}
}
if (mopt.forbid_gen_key)
gen_key_forbidden ();
else
keyedit_quick_addkey (ctrl, x_fpr, x_algo, x_usage, x_expire);
}
break;
case aQuickAddADSK:
{
if (argc != 2)
wrong_args ("--quick-add-adsk FINGERPRINT ADSK-FINGERPRINT");
if (mopt.forbid_gen_key)
gen_key_forbidden ();
else
keyedit_quick_addadsk (ctrl, argv[0], argv[1]);
}
break;
case aQuickRevUid:
{
const char *uid, *uidtorev;
if (argc != 2)
wrong_args ("--quick-revoke-uid USER-ID USER-ID-TO-REVOKE");
uid = *argv++; argc--;
uidtorev = *argv++; argc--;
keyedit_quick_revuid (ctrl, uid, uidtorev);
}
break;
case aQuickSetExpire:
{
const char *x_fpr, *x_expire;
if (argc < 2)
wrong_args ("--quick-set-exipre FINGERPRINT EXPIRE [SUBKEY-FPRS]");
x_fpr = *argv++; argc--;
x_expire = *argv++; argc--;
keyedit_quick_set_expire (ctrl, x_fpr, x_expire, argv);
}
break;
case aQuickSetPrimaryUid:
{
const char *uid, *primaryuid;
if (argc != 2)
wrong_args ("--quick-set-primary-uid USER-ID PRIMARY-USER-ID");
uid = *argv++; argc--;
primaryuid = *argv++; argc--;
keyedit_quick_set_primary (ctrl, uid, primaryuid);
}
break;
case aQuickUpdatePref:
{
if (argc != 1)
wrong_args ("--quick-update-pref USER-ID");
keyedit_quick_update_pref (ctrl, *argv);
}
break;
case aFastImport:
opt.import_options |= IMPORT_FAST; /* fall through */
case aImport:
case aShowKeys:
import_keys (ctrl, argc? argv:NULL, argc, NULL,
opt.import_options, opt.key_origin, opt.key_origin_url);
break;
/* TODO: There are a number of command that use this same
"make strlist, call function, report error, free strlist"
pattern. Join them together here and avoid all that
duplicated code. */
case aExport:
case aSendKeys:
case aRecvKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
append_to_strlist2( &sl, *argv, utf8_strings );
if( cmd == aSendKeys )
rc = keyserver_export (ctrl, sl );
else if( cmd == aRecvKeys )
rc = keyserver_import (ctrl, sl );
else
{
export_stats_t stats = export_new_stats ();
rc = export_pubkeys (ctrl, sl, opt.export_options, stats);
export_print_stats (stats);
export_release_stats (stats);
}
if(rc)
{
if(cmd==aSendKeys)
{
write_status_failure ("send-keys", rc);
log_error(_("keyserver send failed: %s\n"),gpg_strerror (rc));
}
else if(cmd==aRecvKeys)
{
write_status_failure ("recv-keys", rc);
log_error (_("keyserver receive failed: %s\n"),
gpg_strerror (rc));
}
else
{
write_status_failure ("export", rc);
log_error (_("key export failed: %s\n"), gpg_strerror (rc));
}
}
free_strlist(sl);
break;
case aExportSshKey:
if (argc != 1)
wrong_args ("--export-ssh-key <user-id>");
rc = export_ssh_key (ctrl, argv[0]);
if (rc)
{
write_status_failure ("export-ssh-key", rc);
log_error (_("export as ssh key failed: %s\n"), gpg_strerror (rc));
}
break;
case aExportSecretSshKey:
if (argc != 1)
wrong_args ("--export-secret-ssh-key <user-id>");
rc = export_secret_ssh_key (ctrl, argv[0]);
if (rc)
{
write_status_failure ("export-ssh-key", rc);
log_error (_("export as ssh key failed: %s\n"), gpg_strerror (rc));
}
break;
case aSearchKeys:
sl = NULL;
for (; argc; argc--, argv++)
append_to_strlist2 (&sl, *argv, utf8_strings);
rc = keyserver_search (ctrl, sl);
if (rc)
{
write_status_failure ("search-keys", rc);
log_error (_("keyserver search failed: %s\n"), gpg_strerror (rc));
}
free_strlist (sl);
break;
case aRefreshKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
append_to_strlist2( &sl, *argv, utf8_strings );
rc = keyserver_refresh (ctrl, sl);
if(rc)
{
write_status_failure ("refresh-keys", rc);
log_error (_("keyserver refresh failed: %s\n"),gpg_strerror (rc));
}
free_strlist(sl);
break;
case aFetchKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
append_to_strlist2( &sl, *argv, utf8_strings );
rc = keyserver_fetch (ctrl, sl, opt.key_origin);
free_strlist (sl);
if(rc)
{
write_status_failure ("fetch-keys", rc);
log_error ("key fetch failed: %s\n",gpg_strerror (rc));
if (gpg_err_code (rc) == GPG_ERR_NO_DATA)
g10_exit (1); /* In this case return 1 and not 2. */
}
break;
case aExportSecret:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist2( &sl, *argv, utf8_strings );
{
export_stats_t stats = export_new_stats ();
export_seckeys (ctrl, sl, opt.export_options, stats);
export_print_stats (stats);
export_release_stats (stats);
}
free_strlist(sl);
break;
case aExportSecretSub:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist2( &sl, *argv, utf8_strings );
{
export_stats_t stats = export_new_stats ();
export_secsubkeys (ctrl, sl, opt.export_options, stats);
export_print_stats (stats);
export_release_stats (stats);
}
free_strlist(sl);
break;
case aGenRevoke:
if( argc != 1 )
wrong_args("--generate-revocation user-id");
username = make_username(*argv);
gen_revoke (ctrl, username );
xfree( username );
break;
case aDesigRevoke:
if (argc != 1)
wrong_args ("--generate-designated-revocation user-id");
username = make_username (*argv);
gen_desig_revoke (ctrl, username, locusr);
xfree (username);
break;
case aDeArmor:
if( argc > 1 )
wrong_args("--dearmor [file]");
rc = dearmor_file( argc? *argv: NULL );
if( rc )
{
write_status_failure ("dearmor", rc);
log_error (_("dearmoring failed: %s\n"), gpg_strerror (rc));
}
break;
case aEnArmor:
if( argc > 1 )
wrong_args("--enarmor [file]");
rc = enarmor_file( argc? *argv: NULL );
if( rc )
{
write_status_failure ("enarmor", rc);
log_error (_("enarmoring failed: %s\n"), gpg_strerror (rc));
}
break;
case aPrimegen:
#if 0 /*FIXME*/
{ int mode = argc < 2 ? 0 : atoi(*argv);
if( mode == 1 && argc == 2 ) {
mpi_print (es_stdout,
generate_public_prime( atoi(argv[1]) ), 1);
}
else if( mode == 2 && argc == 3 ) {
mpi_print (es_stdout, generate_elg_prime(
0, atoi(argv[1]),
atoi(argv[2]), NULL,NULL ), 1);
}
else if( mode == 3 && argc == 3 ) {
MPI *factors;
mpi_print (es_stdout, generate_elg_prime(
1, atoi(argv[1]),
atoi(argv[2]), NULL,&factors ), 1);
es_putc ('\n', es_stdout);
mpi_print (es_stdout, factors[0], 1 ); /* print q */
}
else if( mode == 4 && argc == 3 ) {
MPI g = mpi_alloc(1);
mpi_print (es_stdout, generate_elg_prime(
0, atoi(argv[1]),
atoi(argv[2]), g, NULL ), 1);
es_putc ('\n', es_stdout);
mpi_print (es_stdout, g, 1 );
mpi_free (g);
}
else
wrong_args("--gen-prime mode bits [qbits] ");
es_putc ('\n', es_stdout);
}
#endif
wrong_args("--gen-prime not yet supported ");
break;
case aGenRandom:
{
int level = argc ? atoi(*argv):0;
int count = argc > 1 ? atoi(argv[1]): 0;
int endless = !count;
int hexhack = (level == 16);
if (hexhack)
level = 1;
/* Level 30 uses the same algorithm as our magic wand in
* pinentry/gpg-agent. */
if (level == 30)
{
unsigned int nbits = 150;
size_t nbytes = (nbits + 7) / 8;
void *rand;
char *generated;
rand = gcry_random_bytes_secure (nbytes, GCRY_STRONG_RANDOM);
if (!rand)
log_fatal ("failed to generate random password\n");
generated = zb32_encode (rand, nbits);
gcry_free (rand);
es_fputs (generated, es_stdout);
es_putc ('\n', es_stdout);
xfree (generated);
break;
}
if (argc < 1 || argc > 2 || level < 0 || level > 2 || count < 0)
wrong_args ("--gen-random 0|1|2|16|30 [count]");
while (endless || count)
{
byte *p;
/* We need a multiple of 3, so that in case of armored
* output we get a correct string. No linefolding is
* done, as it is best to leave this to other tools */
size_t n = !endless && count < 99? count : 99;
size_t nn;
p = gcry_random_bytes (n, level);
if (hexhack)
{
for (nn = 0; nn < n; nn++)
es_fprintf (es_stdout, "%02x", p[nn]);
}
else if (opt.armor)
{
char *tmp = make_radix64_string (p, n);
es_fputs (tmp, es_stdout);
xfree (tmp);
if (n%3 == 1)
es_putc ('=', es_stdout);
if (n%3)
es_putc ('=', es_stdout);
}
else
{
es_set_binary (es_stdout);
es_fwrite( p, n, 1, es_stdout );
}
xfree(p);
if (!endless)
count -= n;
}
if (opt.armor || hexhack)
es_putc ('\n', es_stdout);
}
break;
case aPrintMD:
if( argc < 1)
wrong_args("--print-md algo [files]");
{
int all_algos = (**argv=='*' && !(*argv)[1]);
int algo = all_algos? 0 : gcry_md_map_name (*argv);
if( !algo && !all_algos )
log_error(_("invalid hash algorithm '%s'\n"), *argv );
else {
argc--; argv++;
if( !argc )
print_mds(NULL, algo);
else {
for(; argc; argc--, argv++ )
print_mds(*argv, algo);
}
}
}
break;
case aPrintMDs: /* old option */
if( !argc )
print_mds(NULL,0);
else {
for(; argc; argc--, argv++ )
print_mds(*argv,0);
}
break;
#ifndef NO_TRUST_MODELS
case aListTrustDB:
if( !argc )
list_trustdb (ctrl, es_stdout, NULL);
else {
for( ; argc; argc--, argv++ )
list_trustdb (ctrl, es_stdout, *argv );
}
break;
case aUpdateTrustDB:
if( argc )
wrong_args("--update-trustdb");
update_trustdb (ctrl);
break;
case aCheckTrustDB:
/* Old versions allowed for arguments - ignore them */
check_trustdb (ctrl);
break;
case aFixTrustDB:
how_to_fix_the_trustdb ();
break;
case aListTrustPath:
if( !argc )
wrong_args("--list-trust-path <user-ids>");
for( ; argc; argc--, argv++ ) {
username = make_username( *argv );
list_trust_path( username );
xfree(username);
}
break;
case aExportOwnerTrust:
if( argc )
wrong_args("--export-ownertrust");
export_ownertrust (ctrl);
break;
case aImportOwnerTrust:
if( argc > 1 )
wrong_args("--import-ownertrust [file]");
import_ownertrust (ctrl, argc? *argv:NULL );
break;
#endif /*!NO_TRUST_MODELS*/
case aRebuildKeydbCaches:
if (argc)
wrong_args ("--rebuild-keydb-caches");
keydb_rebuild_caches (ctrl, 1);
break;
#ifdef ENABLE_CARD_SUPPORT
case aCardStatus:
if (argc == 0)
card_status (ctrl, es_stdout, NULL);
else if (argc == 1)
card_status (ctrl, es_stdout, *argv);
else
wrong_args ("--card-status [serialno]");
break;
case aCardEdit:
if (argc) {
sl = NULL;
for (argc--, argv++ ; argc; argc--, argv++)
append_to_strlist (&sl, *argv);
card_edit (ctrl, sl);
free_strlist (sl);
}
else
card_edit (ctrl, NULL);
break;
case aChangePIN:
if (!argc)
change_pin (0,1);
else if (argc == 1)
change_pin (atoi (*argv),1);
else
wrong_args ("--change-pin [no]");
break;
#endif /* ENABLE_CARD_SUPPORT*/
case aListConfig:
{
char *str=collapse_args(argc,argv);
list_config(str);
xfree(str);
}
break;
case aListGcryptConfig:
/* Fixme: It would be nice to integrate that with
--list-config but unfortunately there is no way yet to have
libgcrypt print it to an estream for further parsing. */
gcry_control (GCRYCTL_PRINT_CONFIG, stdout);
break;
case aTOFUPolicy:
#ifdef USE_TOFU
{
int policy;
int i;
KEYDB_HANDLE hd;
if (argc < 2)
wrong_args ("--tofu-policy POLICY KEYID [KEYID...]");
policy = parse_tofu_policy (argv[0]);
hd = keydb_new (ctrl);
if (! hd)
{
write_status_failure ("tofu-driver", gpg_error(GPG_ERR_GENERAL));
g10_exit (1);
}
tofu_begin_batch_update (ctrl);
for (i = 1; i < argc; i ++)
{
KEYDB_SEARCH_DESC desc;
kbnode_t kb;
rc = classify_user_id (argv[i], &desc, 0);
if (rc)
{
log_error (_("error parsing key specification '%s': %s\n"),
argv[i], gpg_strerror (rc));
write_status_failure ("tofu-driver", rc);
g10_exit (1);
}
if (! (desc.mode == KEYDB_SEARCH_MODE_SHORT_KID
|| desc.mode == KEYDB_SEARCH_MODE_LONG_KID
|| desc.mode == KEYDB_SEARCH_MODE_FPR
|| desc.mode == KEYDB_SEARCH_MODE_KEYGRIP))
{
log_error (_("'%s' does not appear to be a valid"
" key ID, fingerprint or keygrip\n"),
argv[i]);
write_status_failure ("tofu-driver",
gpg_error(GPG_ERR_GENERAL));
g10_exit (1);
}
rc = keydb_search_reset (hd);
if (rc)
{
/* This should not happen, thus no need to tranalate
the string. */
log_error ("keydb_search_reset failed: %s\n",
gpg_strerror (rc));
write_status_failure ("tofu-driver", rc);
g10_exit (1);
}
rc = keydb_search (hd, &desc, 1, NULL);
if (rc)
{
log_error (_("key \"%s\" not found: %s\n"), argv[i],
gpg_strerror (rc));
write_status_failure ("tofu-driver", rc);
g10_exit (1);
}
rc = keydb_get_keyblock (hd, &kb);
if (rc)
{
log_error (_("error reading keyblock: %s\n"),
gpg_strerror (rc));
write_status_failure ("tofu-driver", rc);
g10_exit (1);
}
merge_keys_and_selfsig (ctrl, kb);
if (tofu_set_policy (ctrl, kb, policy))
{
write_status_failure ("tofu-driver", rc);
g10_exit (1);
}
release_kbnode (kb);
}
tofu_end_batch_update (ctrl);
keydb_release (hd);
}
#endif /*USE_TOFU*/
break;
default:
if (!opt.quiet)
log_info (_("WARNING: no command supplied."
" Trying to guess what you mean ...\n"));
/*FALLTHRU*/
case aListPackets:
if( argc > 1 )
wrong_args("[filename]");
/* Issue some output for the unix newbie */
if (!fname && !opt.outfile
&& gnupg_isatty (fileno (stdin))
&& gnupg_isatty (fileno (stdout))
&& gnupg_isatty (fileno (stderr)))
log_info(_("Go ahead and type your message ...\n"));
a = iobuf_open(fname);
if (a && is_secured_file (iobuf_get_fd (a)))
{
iobuf_close (a);
a = NULL;
gpg_err_set_errno (EPERM);
}
if( !a )
log_error(_("can't open '%s'\n"), print_fname_stdin(fname));
else {
if( !opt.no_armor ) {
if( use_armor_filter( a ) ) {
afx = new_armor_context ();
push_armor_filter (afx, a);
}
}
if( cmd == aListPackets ) {
opt.list_packets=1;
set_packet_list_mode(1);
}
rc = proc_packets (ctrl, NULL, a );
if( rc )
{
write_status_failure ("-", rc);
log_error ("processing message failed: %s\n",
gpg_strerror (rc));
}
iobuf_close(a);
}
break;
}
/* cleanup */
gpg_deinit_default_ctrl (ctrl);
xfree (ctrl);
release_armor_context (afx);
FREE_STRLIST(remusr);
FREE_STRLIST(locusr);
g10_exit(0);
return 8; /*NEVER REACHED*/
}
/* Note: This function is used by signal handlers!. */
static void
emergency_cleanup (void)
{
gcry_control (GCRYCTL_TERM_SECMEM );
}
void
g10_exit( int rc )
{
/* If we had an error but not printed an error message, do it now.
* Note that write_status_failure will never print a second failure
* status line. */
if (rc)
write_status_failure ("gpg-exit", gpg_error (GPG_ERR_GENERAL));
gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE);
if (DBG_CLOCK)
log_clock ("stop");
if ( (opt.debug & DBG_MEMSTAT_VALUE) )
{
keydb_dump_stats ();
sig_check_dump_stats ();
objcache_dump_stats ();
gcry_control (GCRYCTL_DUMP_MEMORY_STATS);
gcry_control (GCRYCTL_DUMP_RANDOM_STATS);
}
if (opt.debug)
gcry_control (GCRYCTL_DUMP_SECMEM_STATS );
gnupg_block_all_signals ();
emergency_cleanup ();
if (rc)
;
else if (log_get_errorcount(0))
rc = 2;
else if (g10_errors_seen)
rc = 1;
else if (opt.assert_signer_list && !assert_signer_true)
rc = 1;
exit (rc);
}
/* Pretty-print hex hashes. This assumes at least an 80-character
display, but there are a few other similar assumptions in the
display code. */
static void
print_hex (gcry_md_hd_t md, int algo, const char *fname)
{
int i,n,count,indent=0;
const byte *p;
if (fname)
indent = es_printf("%s: ",fname);
if (indent>40)
{
es_printf ("\n");
indent=0;
}
if (algo==DIGEST_ALGO_RMD160)
indent += es_printf("RMD160 = ");
else if (algo>0)
indent += es_printf("%6s = ", gcry_md_algo_name (algo));
else
algo = abs(algo);
count = indent;
p = gcry_md_read (md, algo);
n = gcry_md_get_algo_dlen (algo);
count += es_printf ("%02X",*p++);
for(i=1;i<n;i++,p++)
{
if(n==16)
{
if(count+2>79)
{
es_printf ("\n%*s",indent,indent?" ":"");
count = indent;
}
else
count += es_printf(" ");
if (!(i%8))
count += es_printf(" ");
}
else if (n==20)
{
if(!(i%2))
{
if(count+4>79)
{
es_printf ("\n%*s",indent,indent?" ":"");
count=indent;
}
else
count += es_printf(" ");
}
if (!(i%10))
count += es_printf(" ");
}
else
{
if(!(i%4))
{
if (count+8>=79)
{
es_printf ("\n%*s",indent, indent?" ":"");
count=indent;
}
else
count += es_printf(" ");
}
}
count += es_printf("%02X",*p);
}
es_printf ("\n");
}
static void
print_hashline( gcry_md_hd_t md, int algo, const char *fname )
{
int i, n;
const byte *p;
if ( fname )
{
for (p = fname; *p; p++ )
{
if ( *p <= 32 || *p > 127 || *p == ':' || *p == '%' )
es_printf ("%%%02X", *p );
else
es_putc (*p, es_stdout);
}
}
es_putc (':', es_stdout);
es_printf ("%d:", algo);
p = gcry_md_read (md, algo);
n = gcry_md_get_algo_dlen (algo);
for(i=0; i < n ; i++, p++ )
es_printf ("%02X", *p);
es_fputs (":\n", es_stdout);
}
static void
print_mds( const char *fname, int algo )
{
estream_t fp;
char buf[1024];
size_t n;
gcry_md_hd_t md;
if (!fname)
{
fp = es_stdin;
es_set_binary (fp);
}
else
{
fp = es_fopen (fname, "rb" );
if (fp && is_secured_file (es_fileno (fp)))
{
es_fclose (fp);
fp = NULL;
gpg_err_set_errno (EPERM);
}
}
if (!fp)
{
log_error("%s: %s\n", fname?fname:"[stdin]", strerror(errno) );
return;
}
gcry_md_open (&md, 0, 0);
if (algo)
gcry_md_enable (md, algo);
else
{
if (!gcry_md_test_algo (GCRY_MD_MD5))
gcry_md_enable (md, GCRY_MD_MD5);
gcry_md_enable (md, GCRY_MD_SHA1);
if (!gcry_md_test_algo (GCRY_MD_RMD160))
gcry_md_enable (md, GCRY_MD_RMD160);
if (!gcry_md_test_algo (GCRY_MD_SHA224))
gcry_md_enable (md, GCRY_MD_SHA224);
if (!gcry_md_test_algo (GCRY_MD_SHA256))
gcry_md_enable (md, GCRY_MD_SHA256);
if (!gcry_md_test_algo (GCRY_MD_SHA384))
gcry_md_enable (md, GCRY_MD_SHA384);
if (!gcry_md_test_algo (GCRY_MD_SHA512))
gcry_md_enable (md, GCRY_MD_SHA512);
}
while ((n=es_fread (buf, 1, DIM(buf), fp)))
gcry_md_write (md, buf, n);
if (es_ferror(fp))
log_error ("%s: %s\n", fname?fname:"[stdin]", strerror(errno));
else
{
gcry_md_final (md);
if (opt.with_colons)
{
if ( algo )
print_hashline (md, algo, fname);
else
{
if (!gcry_md_test_algo (GCRY_MD_MD5))
print_hashline( md, GCRY_MD_MD5, fname );
print_hashline( md, GCRY_MD_SHA1, fname );
if (!gcry_md_test_algo (GCRY_MD_RMD160))
print_hashline( md, GCRY_MD_RMD160, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA224))
print_hashline (md, GCRY_MD_SHA224, fname);
if (!gcry_md_test_algo (GCRY_MD_SHA256))
print_hashline( md, GCRY_MD_SHA256, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA384))
print_hashline ( md, GCRY_MD_SHA384, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA512))
print_hashline ( md, GCRY_MD_SHA512, fname );
}
}
else
{
if (algo)
print_hex (md, -algo, fname);
else
{
if (!gcry_md_test_algo (GCRY_MD_MD5))
print_hex (md, GCRY_MD_MD5, fname);
print_hex (md, GCRY_MD_SHA1, fname );
if (!gcry_md_test_algo (GCRY_MD_RMD160))
print_hex (md, GCRY_MD_RMD160, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA224))
print_hex (md, GCRY_MD_SHA224, fname);
if (!gcry_md_test_algo (GCRY_MD_SHA256))
print_hex (md, GCRY_MD_SHA256, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA384))
print_hex (md, GCRY_MD_SHA384, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA512))
print_hex (md, GCRY_MD_SHA512, fname );
}
}
}
gcry_md_close (md);
if (fp != es_stdin)
es_fclose (fp);
}
/****************
* Check the supplied name,value string and add it to the notation
* data to be used for signatures. which==0 for sig notations, and 1
* for cert notations.
*/
static void
add_notation_data( const char *string, int which )
{
struct notation *notation;
notation=string_to_notation(string,utf8_strings);
if(notation)
{
if(which)
{
notation->next=opt.cert_notations;
opt.cert_notations=notation;
}
else
{
notation->next=opt.sig_notations;
opt.sig_notations=notation;
}
}
}
static void
add_policy_url( const char *string, int which )
{
unsigned int i,critical=0;
strlist_t sl;
if(*string=='!')
{
string++;
critical=1;
}
for(i=0;i<strlen(string);i++)
if( !isascii (string[i]) || iscntrl(string[i]))
break;
if(i==0 || i<strlen(string))
{
if(which)
log_error(_("the given certification policy URL is invalid\n"));
else
log_error(_("the given signature policy URL is invalid\n"));
}
if(which)
sl=add_to_strlist( &opt.cert_policy_url, string );
else
sl=add_to_strlist( &opt.sig_policy_url, string );
if(critical)
sl->flags |= 1;
}
static void
add_keyserver_url( const char *string, int which )
{
unsigned int i,critical=0;
strlist_t sl;
if(*string=='!')
{
string++;
critical=1;
}
for(i=0;i<strlen(string);i++)
if( !isascii (string[i]) || iscntrl(string[i]))
break;
if(i==0 || i<strlen(string))
{
if(which)
BUG();
else
log_error(_("the given preferred keyserver URL is invalid\n"));
}
if(which)
BUG();
else
sl=add_to_strlist( &opt.sig_keyserver_url, string );
if(critical)
sl->flags |= 1;
}
static void
read_sessionkey_from_fd (int fd)
{
int i, len;
char *line;
if (! gnupg_fd_valid (fd))
log_fatal ("override-session-key-fd is invalid: %s\n", strerror (errno));
for (line = NULL, i = len = 100; ; i++ )
{
if (i >= len-1 )
{
char *tmp = line;
len += 100;
line = xmalloc_secure (len);
if (tmp)
{
memcpy (line, tmp, i);
xfree (tmp);
}
else
i=0;
}
if (read (fd, line + i, 1) != 1 || line[i] == '\n')
break;
}
line[i] = 0;
log_debug ("seskey: %s\n", line);
gpgrt_annotate_leaked_object (line);
opt.override_session_key = line;
}
diff --git a/g10/photoid.c b/g10/photoid.c
index 72e6acf7d..fc8866121 100644
--- a/g10/photoid.c
+++ b/g10/photoid.c
@@ -1,824 +1,830 @@
/* photoid.c - photo ID handling code
* Copyright (C) 2001, 2002, 2005, 2006, 2008, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG 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/>.
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#ifdef _WIN32
# ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
# endif
# include <windows.h>
# ifndef VER_PLATFORM_WIN32_WINDOWS
# define VER_PLATFORM_WIN32_WINDOWS 1
# endif
#endif
#include "gpg.h"
#include "../common/util.h"
#include "packet.h"
#include "../common/status.h"
#include "keydb.h"
#include "../common/i18n.h"
#include "../common/iobuf.h"
#include "options.h"
#include "main.h"
#include "photoid.h"
#include "../common/ttyio.h"
#include "trustdb.h"
#if defined (_WIN32)
/* This is a nicer system() for windows that waits for programs to
return before returning control to the caller. I hate helpful
computers. */
static int
w32_system (const char *command)
{
if (!strncmp (command, "!ShellExecute ", 14))
{
SHELLEXECUTEINFOW see;
wchar_t *wname;
int waitms;
command = command + 14;
while (spacep (command))
command++;
waitms = atoi (command);
if (waitms < 0)
waitms = 0;
else if (waitms > 60*1000)
waitms = 60000;
while (*command && !spacep (command))
command++;
while (spacep (command))
command++;
wname = utf8_to_wchar (command);
if (!wname)
return -1;
memset (&see, 0, sizeof see);
see.cbSize = sizeof see;
see.fMask = (SEE_MASK_NOCLOSEPROCESS
| SEE_MASK_NOASYNC
| SEE_MASK_FLAG_NO_UI
| SEE_MASK_NO_CONSOLE);
see.lpVerb = L"open";
see.lpFile = (LPCWSTR)wname;
see.nShow = SW_SHOW;
if (DBG_EXTPROG)
log_debug ("running ShellExecuteEx(open,'%s')\n", command);
if (!ShellExecuteExW (&see))
{
if (DBG_EXTPROG)
log_debug ("ShellExecuteEx failed: rc=%d\n", (int)GetLastError ());
xfree (wname);
return -1;
}
if (DBG_EXTPROG)
log_debug ("ShellExecuteEx succeeded (hProcess=%p,hInstApp=%d)\n",
see.hProcess, (int)see.hInstApp);
if (!see.hProcess)
{
gnupg_usleep (waitms*1000);
if (DBG_EXTPROG)
log_debug ("ShellExecuteEx ready (wait=%dms)\n", waitms);
}
else
{
WaitForSingleObject (see.hProcess, INFINITE);
if (DBG_EXTPROG)
log_debug ("ShellExecuteEx ready\n");
}
CloseHandle (see.hProcess);
xfree (wname);
}
else
{
char *string;
wchar_t *wstring;
PROCESS_INFORMATION pi;
STARTUPINFOW si;
/* We must use a copy of the command as CreateProcess modifies
* this argument. */
string = xstrdup (command);
wstring = utf8_to_wchar (string);
xfree (string);
if (!wstring)
return -1;
memset (&pi, 0, sizeof(pi));
memset (&si, 0, sizeof(si));
si.cb = sizeof (si);
if (!CreateProcessW (NULL, wstring, NULL, NULL, FALSE,
DETACHED_PROCESS,
NULL, NULL, &si, &pi))
{
xfree (wstring);
return -1;
}
/* Wait for the child to exit */
WaitForSingleObject (pi.hProcess, INFINITE);
CloseHandle (pi.hProcess);
CloseHandle (pi.hThread);
xfree (wstring);
}
return 0;
}
#endif /*_W32*/
/* Generate a new photo id packet, or return NULL if canceled.
FIXME: Should we add a duplicates check similar to generate_user_id? */
PKT_user_id *
generate_photo_id (ctrl_t ctrl, PKT_public_key *pk,const char *photo_name)
{
PKT_user_id *uid;
int error=1,i;
- unsigned int len;
+ uint64_t len;
char *filename;
byte *photo=NULL;
byte header[16];
IOBUF file;
- int overflow;
header[0]=0x10; /* little side of photo header length */
header[1]=0; /* big side of photo header length */
header[2]=1; /* 1 == version of photo header */
header[3]=1; /* 1 == JPEG */
for(i=4;i<16;i++) /* The reserved bytes */
header[i]=0;
#define EXTRA_UID_NAME_SPACE 71
uid=xmalloc_clear(sizeof(*uid)+71);
if(photo_name && *photo_name)
filename=make_filename(photo_name,(void *)NULL);
else
{
tty_printf(_("\nPick an image to use for your photo ID."
" The image must be a JPEG file.\n"
"Remember that the image is stored within your public key."
" If you use a\n"
"very large picture, your key will become very large"
" as well!\n"
"Keeping the image close to 240x288 is a good size"
" to use.\n"));
filename=NULL;
}
while(photo==NULL)
{
if(filename==NULL)
{
char *tempname;
tty_printf("\n");
tty_enable_completion(NULL);
tempname=cpr_get("photoid.jpeg.add",
_("Enter JPEG filename for photo ID: "));
tty_disable_completion();
filename=make_filename(tempname,(void *)NULL);
xfree(tempname);
if(strlen(filename)==0)
goto scram;
}
file=iobuf_open(filename);
if (file && is_secured_file (iobuf_get_fd (file)))
{
iobuf_close (file);
file = NULL;
gpg_err_set_errno (EPERM);
}
if(!file)
{
log_error(_("unable to open JPEG file '%s': %s\n"),
filename,strerror(errno));
xfree(filename);
filename=NULL;
continue;
}
- len=iobuf_get_filelength(file, &overflow);
- if(len>6144 || overflow)
+ len = iobuf_get_filelength(file);
+ if(len>6144)
{
- tty_printf( _("This JPEG is really large (%d bytes) !\n"),len);
- if(!cpr_get_answer_is_yes("photoid.jpeg.size",
+ /* We silently skip JPEGs larger than 1MiB because we have a
+ * 2MiB limit on the user ID packets and we need some limit
+ * anyway because the returned u64 is larger than the u32 or
+ * OpenPGP. Note that the diagnostic may print a wrong
+ * value if the value is really large; we don't fix this to
+ * avoid a string change. */
+ tty_printf( _("This JPEG is really large (%d bytes) !\n"), (int)len);
+ if(len > 1024*1024
+ || !cpr_get_answer_is_yes("photoid.jpeg.size",
_("Are you sure you want to use it? (y/N) ")))
{
iobuf_close(file);
xfree(filename);
filename=NULL;
continue;
}
}
photo=xmalloc(len);
iobuf_read(file,photo,len);
iobuf_close(file);
/* Is it a JPEG? */
if(photo[0]!=0xFF || photo[1]!=0xD8)
{
log_error(_("'%s' is not a JPEG file\n"),filename);
xfree(photo);
photo=NULL;
xfree(filename);
filename=NULL;
continue;
}
/* Build the packet */
build_attribute_subpkt(uid,1,photo,len,header,16);
parse_attribute_subpkts(uid);
make_attribute_uidname(uid, EXTRA_UID_NAME_SPACE);
/* Showing the photo is not safe when noninteractive since the
"user" may not be able to dismiss a viewer window! */
if(opt.command_fd==-1)
{
show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid);
switch(cpr_get_answer_yes_no_quit("photoid.jpeg.okay",
_("Is this photo correct (y/N/q)? ")))
{
case -1:
goto scram;
case 0:
free_attributes(uid);
xfree(photo);
photo=NULL;
xfree(filename);
filename=NULL;
continue;
}
}
}
error=0;
uid->ref=1;
scram:
xfree(filename);
xfree(photo);
if(error)
{
free_attributes(uid);
xfree(uid);
return NULL;
}
return uid;
}
/* Returns 0 for error, 1 for valid */
int parse_image_header(const struct user_attribute *attr,byte *type,u32 *len)
{
u16 headerlen;
if(attr->len<3)
return 0;
/* For historical reasons (i.e. "oops!"), the header length is
little endian. */
headerlen=(attr->data[1]<<8) | attr->data[0];
if(headerlen>attr->len)
return 0;
if(type && attr->len>=4)
{
if(attr->data[2]==1) /* header version 1 */
*type=attr->data[3];
else
*type=0;
}
*len=attr->len-headerlen;
if(*len==0)
return 0;
return 1;
}
/* style==0 for extension, 1 for name, 2 for MIME type. Remember that
the "name" style string could be used in a user ID name field, so
make sure it is not too big (see parse-packet.c:parse_attribute).
Extensions should be 3 characters long for the best cross-platform
compatibility. */
const char *
image_type_to_string(byte type,int style)
{
const char *string;
switch(type)
{
case 1: /* jpeg */
if(style==0)
string="jpg";
else if(style==1)
string="jpeg";
else
string="image/jpeg";
break;
default:
if(style==0)
string="bin";
else if(style==1)
string="unknown";
else
string="image/x-unknown";
break;
}
return string;
}
#if !defined(FIXED_PHOTO_VIEWER) && !defined(DISABLE_PHOTO_VIEWER)
static const char *
get_default_photo_command(void)
{
#if defined(_WIN32)
OSVERSIONINFO osvi;
memset(&osvi,0,sizeof(osvi));
osvi.dwOSVersionInfoSize=sizeof(osvi);
GetVersionEx(&osvi);
if(osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS)
return "start /w %i";
else
return "!ShellExecute 400 %i";
#elif defined(__APPLE__)
/* OS X. This really needs more than just __APPLE__. */
return "open %I";
#else
if (!path_access ("xloadimage", X_OK))
return "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin";
else if (!path_access ("display",X_OK))
return "display -title 'KeyID 0x%k' %i";
else if (getuid () && !path_access ("xdg-open", X_OK))
{
/* xdg-open spawns the actual program and exits so we need to
* keep the temp file */
return "xdg-open %I";
}
else
return "/bin/true";
#endif
}
#endif
#ifndef DISABLE_PHOTO_VIEWER
struct spawn_info
{
unsigned int keep_temp_file;
char *command;
char *tempdir;
char *tempfile;
};
#ifdef NO_EXEC
static void
show_photo (const char *command, const char *name, const void *image, u32 len)
{
log_error(_("no remote program execution supported\n"));
return GPG_ERR_GENERAL;
}
#else /* ! NO_EXEC */
#include "../common/membuf.h"
#include "../common/exechelp.h"
/* Makes a temp directory and filenames */
static int
setup_input_file (struct spawn_info *info, const char *name)
{
char *tmp = opt.temp_dir;
int len;
#define TEMPLATE "gpg-XXXXXX"
/* Initialize by the length of last part in the path + 1 */
len = strlen (DIRSEP_S) + strlen (TEMPLATE) + 1;
/* Make up the temp dir and file in case we need them */
if (tmp)
{
len += strlen (tmp);
info->tempdir = xmalloc (len);
}
else
{
#if defined (_WIN32)
int ret;
tmp = xmalloc (MAX_PATH+1);
if (!tmp)
return -1;
ret = GetTempPath (MAX_PATH-len, tmp);
if (ret == 0 || ret >= MAX_PATH-len)
strcpy (tmp, "c:\\windows\\temp");
else
{
/* GetTempPath may return with \ on the end */
while (ret > 0 && tmp[ret-1] == '\\')
{
tmp[ret-1]='\0';
ret--;
}
}
len += ret;
info->tempdir = tmp;
#else /* More unixish systems */
if (!(tmp = getenv ("TMPDIR"))
&& !(tmp = getenv ("TMP")))
tmp = "/tmp";
len += strlen (tmp);
info->tempdir = xmalloc (len);
#endif
}
if (info->tempdir == NULL)
return -1;
sprintf (info->tempdir, "%s" DIRSEP_S TEMPLATE, tmp);
if (gnupg_mkdtemp (info->tempdir) == NULL)
{
log_error (_("can't create directory '%s': %s\n"),
info->tempdir, strerror (errno));
return -1;
}
info->tempfile = xmalloc (strlen (info->tempdir) + strlen (DIRSEP_S)
+ strlen (name) + 1);
if (info->tempfile == NULL)
{
xfree (info->tempdir);
info->tempdir = NULL;
return -1;
}
sprintf (info->tempfile, "%s" DIRSEP_S "%s", info->tempdir, name);
return 0;
}
/* Expands %i or %I in the args to the full temp file within the temp
directory. */
static int
expand_args (struct spawn_info *info, const char *args_in, const char *name)
{
const char *ch = args_in;
membuf_t command;
info->keep_temp_file = 0;
if (DBG_EXTPROG)
log_debug ("expanding string \"%s\"\n", args_in);
init_membuf (&command, 100);
while (*ch != '\0')
{
if (*ch == '%')
{
const char *append = NULL;
ch++;
switch (*ch)
{
case 'I':
info->keep_temp_file = 1;
/* fall through */
case 'i': /* in */
if (info->tempfile == NULL)
{
if (setup_input_file (info, name) < 0)
goto fail;
}
append = info->tempfile;
break;
case '%':
append = "%";
break;
}
if (append)
put_membuf_str (&command, append);
}
else
put_membuf (&command, ch, 1);
ch++;
}
put_membuf (&command, "", 1); /* Terminate string. */
info->command = get_membuf (&command, NULL);
if (!info->command)
return -1;
if(DBG_EXTPROG)
log_debug("args expanded to \"%s\", use %s, keep %u\n", info->command,
info->tempfile, info->keep_temp_file);
return 0;
fail:
xfree (get_membuf (&command, NULL));
return -1;
}
#ifndef EXEC_TEMPFILE_ONLY
static void
fill_command_argv (const char *argv[4], const char *command)
{
argv[0] = getenv ("SHELL");
if (argv[0] == NULL)
argv[0] = "/bin/sh";
argv[1] = "-c";
argv[2] = command;
argv[3] = NULL;
}
#endif
static void
run_with_pipe (struct spawn_info *info, const void *image, u32 len)
{
#ifdef EXEC_TEMPFILE_ONLY
(void)info;
(void)image;
(void)len;
log_error (_("this platform requires temporary files when calling"
" external programs\n"));
return;
#else /* !EXEC_TEMPFILE_ONLY */
int to[2];
pid_t pid;
gpg_error_t err;
const char *argv[4];
err = gnupg_create_pipe (to);
if (err)
return;
fill_command_argv (argv, info->command);
err = gnupg_spawn_process_fd (argv[0], argv+1, to[0], -1, -1, &pid);
close (to[0]);
if (err)
{
log_error (_("unable to execute shell '%s': %s\n"),
argv[0], gpg_strerror (err));
close (to[1]);
}
else
{
write (to[1], image, len);
close (to[1]);
err = gnupg_wait_process (argv[0], pid, 1, NULL);
if (err)
log_error (_("unnatural exit of external program\n"));
}
#endif /* !EXEC_TEMPFILE_ONLY */
}
static int
create_temp_file (struct spawn_info *info, const void *ptr, u32 len)
{
if (DBG_EXTPROG)
log_debug ("using temp file '%s'\n", info->tempfile);
/* It's not fork/exec/pipe, so create a temp file */
if ( is_secured_filename (info->tempfile) )
{
log_error (_("can't create '%s': %s\n"),
info->tempfile, strerror (EPERM));
gpg_err_set_errno (EPERM);
return -1;
}
else
{
estream_t fp = es_fopen (info->tempfile, "wb");
if (fp)
{
es_fwrite (ptr, len, 1, fp);
es_fclose (fp);
return 0;
}
else
{
int save = errno;
log_error (_("can't create '%s': %s\n"),
info->tempfile, strerror(errno));
gpg_err_set_errno (save);
return -1;
}
}
}
static void
show_photo (const char *command, const char *name, const void *image, u32 len)
{
struct spawn_info *spawn;
spawn = xmalloc_clear (sizeof (struct spawn_info));
if (!spawn)
return;
/* Expand the args */
if (expand_args (spawn, command, name) < 0)
{
xfree (spawn);
return;
}
if (DBG_EXTPROG)
log_debug ("running command: %s\n", spawn->command);
if (spawn->tempfile == NULL)
run_with_pipe (spawn, image, len);
else if (create_temp_file (spawn, image, len) == 0)
{
#if defined (_WIN32)
if (w32_system (spawn->command) < 0)
log_error (_("system error while calling external program: %s\n"),
strerror (errno));
#else
pid_t pid;
gpg_error_t err;
const char *argv[4];
fill_command_argv (argv, spawn->command);
err = gnupg_spawn_process_fd (argv[0], argv+1, -1, -1, -1, &pid);
if (!err)
err = gnupg_wait_process (argv[0], pid, 1, NULL);
if (err)
log_error (_("unnatural exit of external program\n"));
#endif
if (!spawn->keep_temp_file)
{
if (unlink (spawn->tempfile) < 0)
log_info (_("WARNING: unable to remove tempfile (%s) '%s': %s\n"),
"in", spawn->tempfile, strerror(errno));
if (rmdir (spawn->tempdir) < 0)
log_info (_("WARNING: unable to remove temp directory '%s': %s\n"),
spawn->tempdir, strerror(errno));
}
}
xfree(spawn->command);
xfree(spawn->tempdir);
xfree(spawn->tempfile);
xfree(spawn);
}
#endif
#endif
void
show_photos (ctrl_t ctrl, const struct user_attribute *attrs, int count,
PKT_public_key *pk, PKT_user_id *uid)
{
#ifdef DISABLE_PHOTO_VIEWER
(void)attrs;
(void)count;
(void)pk;
(void)uid;
#else /*!DISABLE_PHOTO_VIEWER*/
int i;
struct expando_args args;
u32 len;
u32 kid[2]={0,0};
if (opt.exec_disable && !opt.no_perm_warn)
{
log_info (_("external program calls are disabled due to unsafe "
"options file permissions\n"));
return;
}
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
/* There should be no way to get to this spot while still carrying
setuid privs. Just in case, bomb out if we are. */
if ( getuid () != geteuid ())
BUG ();
#endif
memset (&args, 0, sizeof(args));
args.pk = pk;
args.validity_info = get_validity_info (ctrl, NULL, pk, uid);
args.validity_string = get_validity_string (ctrl, pk, uid);
namehash_from_uid (uid);
args.namehash = uid->namehash;
if (pk)
keyid_from_pk (pk, kid);
es_fflush (es_stdout);
#ifdef FIXED_PHOTO_VIEWER
opt.photo_viewer = FIXED_PHOTO_VIEWER;
#else
if (!opt.photo_viewer)
opt.photo_viewer = get_default_photo_command ();
#endif
for (i=0; i<count; i++)
if (attrs[i].type == ATTRIB_IMAGE
&& parse_image_header (&attrs[i], &args.imagetype, &len))
{
char *command, *name;
int offset = attrs[i].len-len;
/* make command grow */
command = pct_expando (ctrl, opt.photo_viewer,&args);
if(!command)
goto fail;
if (!*command)
{
xfree (command);
goto fail;
}
name = xmalloc (1 + 16 + strlen(EXTSEP_S)
+ strlen (image_type_to_string (args.imagetype, 0)));
if (!name)
{
xfree (command);
goto fail;
}
/* Make the filename. Notice we are not using the image
encoding type for more than cosmetics. Most external image
viewers can handle a multitude of types, and even if one
cannot understand a particular type, we have no way to know
which. The spec permits this, by the way. -dms */
#ifdef USE_ONLY_8DOT3
sprintf (name,"%08lX" EXTSEP_S "%s", (ulong)kid[1],
image_type_to_string (args.imagetype, 0));
#else
sprintf (name, "%08lX%08lX" EXTSEP_S "%s",
(ulong)kid[0], (ulong)kid[1],
image_type_to_string (args.imagetype, 0));
#endif
show_photo (command, name, &attrs[i].data[offset], len);
xfree (name);
xfree (command);
}
return;
fail:
log_error(_("unable to display photo ID!\n"));
#endif /*!DISABLE_PHOTO_VIEWER*/
}
diff --git a/g10/progress.c b/g10/progress.c
index 7e777d4ab..7ee8b1e04 100644
--- a/g10/progress.c
+++ b/g10/progress.c
@@ -1,202 +1,203 @@
/* progress.c - emit progress status lines
* Copyright (C) 2003, 2006 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG 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/>.
*/
#include <config.h>
#include <stdio.h>
#include "gpg.h"
#include "../common/iobuf.h"
#include "filter.h"
#include "../common/status.h"
#include "../common/util.h"
#include "options.h"
/* Create a new context for use with the progress filter. We need to
allocate such contexts on the heap because there is no guarantee
that at the end of a function the filter has already been popped
off. In general this will happen but with malformed packets it is
possible that a filter has not yet reached the end-of-stream when
the function has done all processing. Checking in each function
that end-of-stream has been reached would be to cumbersome.
What we also do is to shortcut the progress handler by having this
function return NULL if progress information has not been
requested.
*/
progress_filter_context_t *
new_progress_context (void)
{
progress_filter_context_t *pfx;
if (!opt.enable_progress_filter)
return NULL;
if (!is_status_enabled ())
return NULL;
pfx = xcalloc (1, sizeof *pfx);
pfx->refcount = 1;
return pfx;
}
/* Release a progress filter context. Passing NULL is explicitly
allowed and a no-op. */
void
release_progress_context (progress_filter_context_t *pfx)
{
if (!pfx)
return;
log_assert (pfx->refcount);
if ( --pfx->refcount )
return;
xfree (pfx->what);
xfree (pfx);
}
static void
-write_status_progress (const char *what,
- unsigned long current, unsigned long total_arg)
+write_status_progress (const char *what, uint64_t current, uint64_t total)
{
char buffer[60];
char units[] = "BKMGTPEZY?";
int unitidx = 0;
- uint64_t total = total_arg;
/* Although we use an unsigned long for the values, 32 bit
* applications using GPGME will use an "int" and thus are limited
* in the total size which can be represented. On Windows, where
* sizeof(int)==sizeof(long), this is even worse and will lead to an
* integer overflow for all files larger than 2 GiB. Although, the
* allowed value range of TOTAL and CURRENT is nowhere specified, we
* better protect applications from the need to handle negative
* values. The common usage pattern of the progress information is
* to display how many percent of the operation has been done and
* thus scaling CURRENT and TOTAL down before they get to large,
* should not have a noticeable effect except for rounding
- * imprecision. */
+ * imprecision.
+ * Update 2023-06-13: We now use uint64_t but to keep the API stable
+ * we still do the scaling.
+ */
if (!total && opt.input_size_hint)
total = opt.input_size_hint;
if (total)
{
if (current > total)
current = total;
while (total > 1024*1024)
{
total /= 1024;
current /= 1024;
unitidx++;
}
}
else
{
while (current > 1024*1024)
{
current /= 1024;
unitidx++;
}
}
if (unitidx > 9)
unitidx = 9;
snprintf (buffer, sizeof buffer, "%.20s ? %lu %lu %c%s",
- what? what : "?", current, (unsigned long)total,
+ what? what : "?", (unsigned long)current, (unsigned long)total,
units[unitidx],
unitidx? "iB" : "");
write_status_text (STATUS_PROGRESS, buffer);
}
/****************
* The filter is used to report progress to the user.
*/
static int
progress_filter (void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
int rc = 0;
progress_filter_context_t *pfx = opaque;
if (control == IOBUFCTRL_INIT)
{
pfx->last = 0;
pfx->offset = 0;
pfx->last_time = make_timestamp ();
write_status_progress (pfx->what, pfx->offset, pfx->total);
}
else if (control == IOBUFCTRL_UNDERFLOW)
{
u32 timestamp = make_timestamp ();
int len = iobuf_read (a, buf, *ret_len);
if (len >= 0)
{
pfx->offset += len;
*ret_len = len;
}
else
{
*ret_len = 0;
rc = -1;
}
if ((len == -1 && pfx->offset != pfx->last)
|| timestamp - pfx->last_time > 0)
{
write_status_progress (pfx->what, pfx->offset, pfx->total);
pfx->last = pfx->offset;
pfx->last_time = timestamp;
}
}
else if (control == IOBUFCTRL_FREE)
{
release_progress_context (pfx);
}
else if (control == IOBUFCTRL_DESC)
mem2str (buf, "progress_filter", *ret_len);
return rc;
}
void
handle_progress (progress_filter_context_t *pfx, IOBUF inp, const char *name)
{
- off_t filesize = 0;
+ uint64_t filesize = 0;
if (!pfx)
return;
log_assert (opt.enable_progress_filter);
log_assert (is_status_enabled ());
if ( !iobuf_is_pipe_filename (name) && *name )
- filesize = iobuf_get_filelength (inp, NULL);
+ filesize = iobuf_get_filelength (inp);
else if (opt.set_filesize)
filesize = opt.set_filesize;
/* register the progress filter */
pfx->what = xstrdup (name ? name : "stdin");
pfx->total = filesize;
pfx->refcount++;
iobuf_push_filter (inp, progress_filter, pfx);
}
diff --git a/g10/sign.c b/g10/sign.c
index b5e9d422d..f9984f811 100644
--- a/g10/sign.c
+++ b/g10/sign.c
@@ -1,2012 +1,2011 @@
/* sign.c - sign data
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007, 2010, 2012 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG 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/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "gpg.h"
#include "options.h"
#include "packet.h"
#include "../common/status.h"
#include "../common/iobuf.h"
#include "keydb.h"
#include "../common/util.h"
#include "main.h"
#include "filter.h"
#include "../common/ttyio.h"
#include "trustdb.h"
#include "../common/status.h"
#include "../common/i18n.h"
#include "pkglue.h"
#include "../common/sysutils.h"
#include "call-agent.h"
#include "../common/mbox-util.h"
#include "../common/compliance.h"
#ifdef HAVE_DOSISH_SYSTEM
#define LF "\r\n"
#else
#define LF "\n"
#endif
/* Hack */
static int recipient_digest_algo;
/* A type for the extra data we hash into v5 signature packets. */
struct pt_extra_hash_data_s
{
unsigned char mode;
u32 timestamp;
unsigned char namelen;
char name[1];
};
typedef struct pt_extra_hash_data_s *pt_extra_hash_data_t;
/*
* Create notations and other stuff. It is assumed that the strings in
* STRLIST are already checked to contain only printable data and have
* a valid NAME=VALUE format.
*/
static void
mk_notation_policy_etc (ctrl_t ctrl, PKT_signature *sig,
PKT_public_key *pk, PKT_public_key *pksk)
{
const char *string;
char *p = NULL;
strlist_t pu = NULL;
struct notation *nd = NULL;
struct expando_args args;
log_assert (sig->version >= 4);
memset (&args, 0, sizeof(args));
args.pk = pk;
args.pksk = pksk;
/* Notation data. */
if (IS_ATTST_SIGS(sig))
;
else if (IS_SIG(sig) && opt.sig_notations)
nd = opt.sig_notations;
else if (IS_CERT(sig) && opt.cert_notations)
nd = opt.cert_notations;
if (nd)
{
struct notation *item;
for (item = nd; item; item = item->next)
{
item->altvalue = pct_expando (ctrl, item->value,&args);
if (!item->altvalue)
log_error (_("WARNING: unable to %%-expand notation "
"(too large). Using unexpanded.\n"));
}
keygen_add_notations (sig, nd);
for (item = nd; item; item = item->next)
{
xfree (item->altvalue);
item->altvalue = NULL;
}
}
/* Set policy URL. */
if (IS_ATTST_SIGS(sig))
;
else if (IS_SIG(sig) && opt.sig_policy_url)
pu = opt.sig_policy_url;
else if (IS_CERT(sig) && opt.cert_policy_url)
pu = opt.cert_policy_url;
for (; pu; pu = pu->next)
{
string = pu->d;
p = pct_expando (ctrl, string, &args);
if (!p)
{
log_error(_("WARNING: unable to %%-expand policy URL "
"(too large). Using unexpanded.\n"));
p = xstrdup(string);
}
build_sig_subpkt (sig, (SIGSUBPKT_POLICY
| ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0)),
p, strlen (p));
xfree (p);
}
/* Preferred keyserver URL. */
if (IS_SIG(sig) && opt.sig_keyserver_url)
pu = opt.sig_keyserver_url;
for (; pu; pu = pu->next)
{
string = pu->d;
p = pct_expando (ctrl, string, &args);
if (!p)
{
log_error (_("WARNING: unable to %%-expand preferred keyserver URL"
" (too large). Using unexpanded.\n"));
p = xstrdup (string);
}
build_sig_subpkt (sig, (SIGSUBPKT_PREF_KS
| ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0)),
p, strlen (p));
xfree (p);
}
/* Set signer's user id. */
if (IS_SIG (sig) && !opt.flags.disable_signer_uid)
{
char *mbox;
/* For now we use the uid which was used to locate the key. */
if (pksk->user_id
&& (mbox = mailbox_from_userid (pksk->user_id->name, 0)))
{
if (DBG_LOOKUP)
log_debug ("setting Signer's UID to '%s'\n", mbox);
build_sig_subpkt (sig, SIGSUBPKT_SIGNERS_UID, mbox, strlen (mbox));
xfree (mbox);
}
else if (opt.sender_list)
{
/* If a list of --sender was given we scan that list and use
* the first one matching a user id of the current key. */
/* FIXME: We need to get the list of user ids for the PKSK
* packet. That requires either a function to look it up
* again or we need to extend the key packet struct to link
* to the primary key which in turn could link to the user
* ids. Too much of a change right now. Let's take just
* one from the supplied list and hope that the caller
* passed a matching one. */
build_sig_subpkt (sig, SIGSUBPKT_SIGNERS_UID,
opt.sender_list->d, strlen (opt.sender_list->d));
}
}
}
/*
* Put the Key Block subpacket into SIG for key PKSK. Returns an
* error code on failure.
*/
static gpg_error_t
mk_sig_subpkt_key_block (ctrl_t ctrl, PKT_signature *sig, PKT_public_key *pksk)
{
gpg_error_t err;
char *mbox;
char *filterexp = NULL;
int save_opt_armor = opt.armor;
int save_opt_verbose = opt.verbose;
char hexfpr[2*MAX_FINGERPRINT_LEN + 1];
void *data = NULL;
size_t datalen;
kbnode_t keyblock = NULL;
push_export_filters ();
opt.armor = 0;
hexfingerprint (pksk, hexfpr, sizeof hexfpr);
/* Get the user id so that we know which one to insert into the
* key. */
if (pksk->user_id
&& (mbox = mailbox_from_userid (pksk->user_id->name, 0)))
{
if (DBG_LOOKUP)
log_debug ("including key with UID '%s' (specified)\n", mbox);
filterexp = xasprintf ("keep-uid= -- mbox = %s", mbox);
xfree (mbox);
}
else if (opt.sender_list)
{
/* If --sender was given we use the first one from that list. */
if (DBG_LOOKUP)
log_debug ("including key with UID '%s' (--sender)\n",
opt.sender_list->d);
filterexp = xasprintf ("keep-uid= -- mbox = %s", opt.sender_list->d);
}
else /* Use the primary user id. */
{
if (DBG_LOOKUP)
log_debug ("including key with primary UID\n");
filterexp = xstrdup ("keep-uid= primary -t");
}
if (DBG_LOOKUP)
log_debug ("export filter expression: %s\n", filterexp);
err = parse_and_set_export_filter (filterexp);
if (err)
goto leave;
xfree (filterexp);
filterexp = xasprintf ("drop-subkey= fpr <> %s && usage !~ e", hexfpr);
if (DBG_LOOKUP)
log_debug ("export filter expression: %s\n", filterexp);
err = parse_and_set_export_filter (filterexp);
if (err)
goto leave;
opt.verbose = 0;
err = export_pubkey_buffer (ctrl, hexfpr, EXPORT_MINIMAL|EXPORT_CLEAN,
"", 1, /* Prefix with the reserved byte. */
NULL, &keyblock, &data, &datalen);
opt.verbose = save_opt_verbose;
if (err)
{
log_error ("failed to get to be included key: %s\n", gpg_strerror (err));
goto leave;
}
build_sig_subpkt (sig, SIGSUBPKT_KEY_BLOCK, data, datalen);
leave:
xfree (data);
release_kbnode (keyblock);
xfree (filterexp);
opt.armor = save_opt_armor;
pop_export_filters ();
return err;
}
/*
* Helper to hash a user ID packet.
*/
static void
hash_uid (gcry_md_hd_t md, int sigversion, const PKT_user_id *uid)
{
byte buf[5];
(void)sigversion;
if (uid->attrib_data)
{
buf[0] = 0xd1; /* Indicates an attribute packet. */
buf[1] = uid->attrib_len >> 24; /* Always use 4 length bytes. */
buf[2] = uid->attrib_len >> 16;
buf[3] = uid->attrib_len >> 8;
buf[4] = uid->attrib_len;
}
else
{
buf[0] = 0xb4; /* Indicates a userid packet. */
buf[1] = uid->len >> 24; /* Always use 4 length bytes. */
buf[2] = uid->len >> 16;
buf[3] = uid->len >> 8;
buf[4] = uid->len;
}
gcry_md_write( md, buf, 5 );
if (uid->attrib_data)
gcry_md_write (md, uid->attrib_data, uid->attrib_len );
else
gcry_md_write (md, uid->name, uid->len );
}
/*
* Helper to hash some parts from the signature. EXTRAHASH gives the
* extra data to be hashed into v5 signatures; it may by NULL for
* detached signatures.
*/
static void
hash_sigversion_to_magic (gcry_md_hd_t md, const PKT_signature *sig,
pt_extra_hash_data_t extrahash)
{
byte buf[10];
int i;
size_t n;
gcry_md_putc (md, sig->version);
gcry_md_putc (md, sig->sig_class);
gcry_md_putc (md, sig->pubkey_algo);
gcry_md_putc (md, sig->digest_algo);
if (sig->hashed)
{
n = sig->hashed->len;
gcry_md_putc (md, (n >> 8) );
gcry_md_putc (md, n );
gcry_md_write (md, sig->hashed->data, n );
n += 6;
}
else
{
gcry_md_putc (md, 0); /* Always hash the length of the subpacket. */
gcry_md_putc (md, 0);
n = 6;
}
/* Hash data from the literal data packet. */
if (sig->version >= 5 && (sig->sig_class == 0x00 || sig->sig_class == 0x01))
{
/* - One octet content format
* - File name (one octet length followed by the name)
* - Four octet timestamp */
if (extrahash)
{
buf[0] = extrahash->mode;
buf[1] = extrahash->namelen;
gcry_md_write (md, buf, 2);
if (extrahash->namelen)
gcry_md_write (md, extrahash->name, extrahash->namelen);
buf[0] = extrahash->timestamp >> 24;
buf[1] = extrahash->timestamp >> 16;
buf[2] = extrahash->timestamp >> 8;
buf[3] = extrahash->timestamp;
gcry_md_write (md, buf, 4);
}
else /* Detached signatures */
{
memset (buf, 0, 6);
gcry_md_write (md, buf, 6);
}
}
/* Add some magic aka known as postscript. The idea was to make it
* impossible to make up a document with a v3 signature and then
* turn this into a v4 signature for another document. The last
* hashed 5 bytes of a v4 signature should never look like a the
* last 5 bytes of a v3 signature. The length can be used to parse
* from the end. */
i = 0;
buf[i++] = sig->version; /* Hash convention version. */
buf[i++] = 0xff; /* Not any sig type value. */
if (sig->version >= 5)
{
/* Note: We don't hashed any data larger than 2^32 and thus we
* can always use 0 here. See also note below. */
buf[i++] = 0;
buf[i++] = 0;
buf[i++] = 0;
buf[i++] = 0;
}
buf[i++] = n >> 24; /* (n is only 16 bit, so this is always 0) */
buf[i++] = n >> 16;
buf[i++] = n >> 8;
buf[i++] = n;
gcry_md_write (md, buf, i);
}
/* Perform the sign operation. If CACHE_NONCE is given the agent is
* advised to use that cached passphrase for the key. SIGNHINTS has
* hints so that we can do some additional checks. */
static int
do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig,
gcry_md_hd_t md, int mdalgo,
const char *cache_nonce, unsigned int signhints)
{
gpg_error_t err;
byte *dp;
char *hexgrip;
/* An ADSK key commonly has a creation date older than the primary
* key. For example because the ADSK is used as an archive key for
* a group of users. */
if (pksk->timestamp > sig->timestamp && !(signhints & SIGNHINT_ADSK))
{
ulong d = pksk->timestamp - sig->timestamp;
log_info (ngettext("key %s was created %lu second"
" in the future (time warp or clock problem)\n",
"key %s was created %lu seconds"
" in the future (time warp or clock problem)\n",
d), keystr_from_pk (pksk), d);
if (!opt.ignore_time_conflict)
return gpg_error (GPG_ERR_TIME_CONFLICT);
}
print_pubkey_algo_note (pksk->pubkey_algo);
if (!mdalgo)
mdalgo = gcry_md_get_algo (md);
if ((signhints & SIGNHINT_KEYSIG) && !(signhints & SIGNHINT_SELFSIG)
&& mdalgo == GCRY_MD_SHA1
&& !opt.flags.allow_weak_key_signatures)
{
/* We do not allow the creation of third-party key signatures
* using SHA-1 because we also reject them when verifying. Note
* that this will render dsa1024 keys unsuitable for such
* keysigs and in turn the WoT. */
print_sha1_keysig_rejected_note ();
err = gpg_error (GPG_ERR_DIGEST_ALGO);
goto leave;
}
/* Check compliance. */
if (! gnupg_digest_is_allowed (opt.compliance, 1, mdalgo))
{
log_error (_("digest algorithm '%s' may not be used in %s mode\n"),
gcry_md_algo_name (mdalgo),
gnupg_compliance_option_string (opt.compliance));
err = gpg_error (GPG_ERR_DIGEST_ALGO);
goto leave;
}
if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING,
pksk->pubkey_algo, 0,
pksk->pkey, nbits_from_pk (pksk), NULL))
{
log_error (_("key %s may not be used for signing in %s mode\n"),
keystr_from_pk (pksk),
gnupg_compliance_option_string (opt.compliance));
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
}
if (!gnupg_rng_is_compliant (opt.compliance))
{
err = gpg_error (GPG_ERR_FORBIDDEN);
log_error (_("%s is not compliant with %s mode\n"),
"RNG",
gnupg_compliance_option_string (opt.compliance));
write_status_error ("random-compliance", err);
goto leave;
}
print_digest_algo_note (mdalgo);
dp = gcry_md_read (md, mdalgo);
sig->digest_algo = mdalgo;
sig->digest_start[0] = dp[0];
sig->digest_start[1] = dp[1];
mpi_release (sig->data[0]);
sig->data[0] = NULL;
mpi_release (sig->data[1]);
sig->data[1] = NULL;
err = hexkeygrip_from_pk (pksk, &hexgrip);
if (!err)
{
char *desc;
gcry_sexp_t s_sigval;
desc = gpg_format_keydesc (ctrl, pksk, FORMAT_KEYDESC_NORMAL, 1);
err = agent_pksign (NULL/*ctrl*/, cache_nonce, hexgrip, desc,
pksk->keyid, pksk->main_keyid, pksk->pubkey_algo,
dp, gcry_md_get_algo_dlen (mdalgo), mdalgo,
&s_sigval);
xfree (desc);
if (err)
;
else if (pksk->pubkey_algo == GCRY_PK_RSA
|| pksk->pubkey_algo == GCRY_PK_RSA_S)
sig->data[0] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG);
else if (pksk->pubkey_algo == PUBKEY_ALGO_EDDSA
&& openpgp_oid_is_ed25519 (pksk->pkey[0]))
{
err = sexp_extract_param_sos_nlz (s_sigval, "r", &sig->data[0]);
if (!err)
err = sexp_extract_param_sos_nlz (s_sigval, "s", &sig->data[1]);
}
else if (pksk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pksk->pubkey_algo == PUBKEY_ALGO_EDDSA)
{
err = sexp_extract_param_sos (s_sigval, "r", &sig->data[0]);
if (!err)
err = sexp_extract_param_sos (s_sigval, "s", &sig->data[1]);
}
else
{
sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_USG);
sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG);
}
gcry_sexp_release (s_sigval);
}
xfree (hexgrip);
leave:
if (err)
log_error (_("signing failed: %s\n"), gpg_strerror (err));
else
{
if (opt.verbose)
{
char *ustr = get_user_id_string_native (ctrl, sig->keyid);
log_info (_("%s/%s signature from: \"%s\"\n"),
openpgp_pk_algo_name (pksk->pubkey_algo),
openpgp_md_algo_name (sig->digest_algo),
ustr);
xfree (ustr);
}
}
return err;
}
static int
complete_sig (ctrl_t ctrl,
PKT_signature *sig, PKT_public_key *pksk, gcry_md_hd_t md,
const char *cache_nonce, unsigned int signhints)
{
int rc;
/* if (!(rc = check_secret_key (pksk, 0))) */
rc = do_sign (ctrl, pksk, sig, md, 0, cache_nonce, signhints);
return rc;
}
/* Return true if the key seems to be on a version 1 OpenPGP card.
This works by asking the agent and may fail if the card has not yet
been used with the agent. */
static int
openpgp_card_v1_p (PKT_public_key *pk)
{
gpg_error_t err;
int result;
/* Shortcut if we are not using RSA: The v1 cards only support RSA
thus there is no point in looking any further. */
if (!is_RSA (pk->pubkey_algo))
return 0;
if (!pk->flags.serialno_valid)
{
char *hexgrip;
err = hexkeygrip_from_pk (pk, &hexgrip);
if (err)
{
log_error ("error computing a keygrip: %s\n", gpg_strerror (err));
return 0; /* Ooops. */
}
xfree (pk->serialno);
agent_get_keyinfo (NULL, hexgrip, &pk->serialno, NULL);
xfree (hexgrip);
pk->flags.serialno_valid = 1;
}
if (!pk->serialno)
result = 0; /* Error from a past agent_get_keyinfo or no card. */
else
{
/* The version number of the card is included in the serialno. */
result = !strncmp (pk->serialno, "D2760001240101", 14);
}
return result;
}
/* Get a matching hash algorithm for DSA and ECDSA. */
static int
match_dsa_hash (unsigned int qbytes)
{
if (qbytes <= 20)
return DIGEST_ALGO_SHA1;
if (qbytes <= 28)
return DIGEST_ALGO_SHA224;
if (qbytes <= 32)
return DIGEST_ALGO_SHA256;
if (qbytes <= 48)
return DIGEST_ALGO_SHA384;
if (qbytes <= 66 ) /* 66 corresponds to 521 (64 to 512) */
return DIGEST_ALGO_SHA512;
return DEFAULT_DIGEST_ALGO;
/* DEFAULT_DIGEST_ALGO will certainly fail, but it's the best wrong
answer we have if a digest larger than 512 bits is requested. */
}
/*
First try --digest-algo. If that isn't set, see if the recipient
has a preferred algorithm (which is also filtered through
--personal-digest-prefs). If we're making a signature without a
particular recipient (i.e. signing, rather than signing+encrypting)
then take the first algorithm in --personal-digest-prefs that is
usable for the pubkey algorithm. If --personal-digest-prefs isn't
set, then take the OpenPGP default (i.e. SHA-1).
Note that EdDSA takes an input of arbitrary length and thus
we don't enforce any particular algorithm like we do for standard
ECDSA. However, we use SHA256 as the default algorithm.
Possible improvement: Use the highest-ranked usable algorithm from
the signing key prefs either before or after using the personal
list?
*/
static int
hash_for (PKT_public_key *pk)
{
if (opt.def_digest_algo)
{
return opt.def_digest_algo;
}
else if (recipient_digest_algo && !is_weak_digest (recipient_digest_algo))
{
return recipient_digest_algo;
}
else if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA)
{
if (opt.personal_digest_prefs)
return opt.personal_digest_prefs[0].value;
else
if (gcry_mpi_get_nbits (pk->pkey[1]) > 256)
return DIGEST_ALGO_SHA512;
else
return DIGEST_ALGO_SHA256;
}
else if (pk->pubkey_algo == PUBKEY_ALGO_DSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
{
unsigned int qbytes = gcry_mpi_get_nbits (pk->pkey[1]);
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
qbytes = ecdsa_qbits_from_Q (qbytes);
qbytes = qbytes/8;
/* It's a DSA key, so find a hash that is the same size as q or
larger. If q is 160, assume it is an old DSA key and use a
160-bit hash unless --enable-dsa2 is set, in which case act
like a new DSA key that just happens to have a 160-bit q
(i.e. allow truncation). If q is not 160, by definition it
must be a new DSA key. We ignore the personal_digest_prefs
for ECDSA because they should always macth the curve and
truncated hashes are not useful either. Even worse,
smartcards may reject non matching hash lengths for curves
(e.g. using SHA-512 with brainpooolP385r1 on a Yubikey). */
if (pk->pubkey_algo == PUBKEY_ALGO_DSA && opt.personal_digest_prefs)
{
prefitem_t *prefs;
if (qbytes != 20 || opt.flags.dsa2)
{
for (prefs=opt.personal_digest_prefs; prefs->type; prefs++)
if (gcry_md_get_algo_dlen (prefs->value) >= qbytes)
return prefs->value;
}
else
{
for (prefs=opt.personal_digest_prefs; prefs->type; prefs++)
if (gcry_md_get_algo_dlen (prefs->value) == qbytes)
return prefs->value;
}
}
return match_dsa_hash(qbytes);
}
else if (openpgp_card_v1_p (pk))
{
/* The sk lives on a smartcard, and old smartcards only handle
SHA-1 and RIPEMD/160. Newer smartcards (v2.0) don't have
this restriction anymore. Fortunately the serial number
encodes the version of the card and thus we know that this
key is on a v1 card. */
if(opt.personal_digest_prefs)
{
prefitem_t *prefs;
for (prefs=opt.personal_digest_prefs;prefs->type;prefs++)
if (prefs->value==DIGEST_ALGO_SHA1
|| prefs->value==DIGEST_ALGO_RMD160)
return prefs->value;
}
return DIGEST_ALGO_SHA1;
}
else if (opt.personal_digest_prefs)
{
/* It's not DSA, so we can use whatever the first hash algorithm
is in the pref list */
return opt.personal_digest_prefs[0].value;
}
else
return DEFAULT_DIGEST_ALGO;
}
static void
print_status_sig_created (PKT_public_key *pk, PKT_signature *sig, int what)
{
byte array[MAX_FINGERPRINT_LEN];
char buf[100+MAX_FINGERPRINT_LEN*2];
size_t n;
snprintf (buf, sizeof buf - 2*MAX_FINGERPRINT_LEN, "%c %d %d %02x %lu ",
what, sig->pubkey_algo, sig->digest_algo, sig->sig_class,
(ulong)sig->timestamp );
fingerprint_from_pk (pk, array, &n);
bin2hex (array, n, buf + strlen (buf));
write_status_text( STATUS_SIG_CREATED, buf );
}
/*
* Loop over the secret certificates in SK_LIST and build the one pass
* signature packets. OpenPGP says that the data should be bracket by
* the onepass-sig and signature-packet; so we build these onepass
* packet here in reverse order.
*/
static int
write_onepass_sig_packets (SK_LIST sk_list, IOBUF out, int sigclass )
{
int skcount;
SK_LIST sk_rover;
for (skcount=0, sk_rover=sk_list; sk_rover; sk_rover = sk_rover->next)
skcount++;
for (; skcount; skcount--)
{
PKT_public_key *pk;
PKT_onepass_sig *ops;
PACKET pkt;
int i, rc;
for (i=0, sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
if (++i == skcount)
break;
pk = sk_rover->pk;
ops = xmalloc_clear (sizeof *ops);
ops->sig_class = sigclass;
ops->digest_algo = hash_for (pk);
ops->pubkey_algo = pk->pubkey_algo;
keyid_from_pk (pk, ops->keyid);
ops->last = (skcount == 1);
init_packet (&pkt);
pkt.pkttype = PKT_ONEPASS_SIG;
pkt.pkt.onepass_sig = ops;
rc = build_packet (out, &pkt);
free_packet (&pkt, NULL);
if (rc)
{
log_error ("build onepass_sig packet failed: %s\n",
gpg_strerror (rc));
return rc;
}
}
return 0;
}
/*
* Helper to write the plaintext (literal data) packet. At
* R_EXTRAHASH a malloced object with the with the extra data hashed
* into v5 signatures is stored.
*/
static int
write_plaintext_packet (iobuf_t out, iobuf_t inp,
const char *fname, int ptmode,
pt_extra_hash_data_t *r_extrahash)
{
PKT_plaintext *pt = NULL;
u32 filesize;
int rc = 0;
if (!opt.no_literal)
pt = setup_plaintext_name (fname, inp);
/* Try to calculate the length of the data. */
if ( !iobuf_is_pipe_filename (fname) && *fname)
{
- off_t tmpsize;
- int overflow;
+ uint64_t tmpsize;
- if (!(tmpsize = iobuf_get_filelength (inp, &overflow))
- && !overflow && opt.verbose)
+ tmpsize = iobuf_get_filelength (inp);
+ if (!tmpsize && opt.verbose)
log_info (_("WARNING: '%s' is an empty file\n"), fname);
/* We can't encode the length of very large files because
* OpenPGP uses only 32 bit for file sizes. So if the size of a
* file is larger than 2^32 minus some bytes for packet headers,
* we switch to partial length encoding. */
if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536))
filesize = tmpsize;
else
filesize = 0;
/* Because the text_filter modifies the length of the
* data, it is not possible to know the used length
* without a double read of the file - to avoid that
* we simple use partial length packets. */
if (ptmode == 't' || ptmode == 'u' || ptmode == 'm')
filesize = 0;
}
else
filesize = opt.set_filesize? opt.set_filesize : 0; /* stdin */
if (!opt.no_literal)
{
PACKET pkt;
/* Note that PT has been initialized above in no_literal mode. */
pt->timestamp = make_timestamp ();
pt->mode = ptmode;
pt->len = filesize;
pt->new_ctb = !pt->len;
pt->buf = inp;
init_packet (&pkt);
pkt.pkttype = PKT_PLAINTEXT;
pkt.pkt.plaintext = pt;
/*cfx.datalen = filesize? calc_packet_length( &pkt ) : 0;*/
if ((rc = build_packet (out, &pkt)))
log_error ("build_packet(PLAINTEXT) failed: %s\n",
gpg_strerror (rc) );
*r_extrahash = xtrymalloc (sizeof **r_extrahash + pt->namelen);
if (!*r_extrahash)
rc = gpg_error_from_syserror ();
else
{
(*r_extrahash)->mode = pt->mode;
(*r_extrahash)->timestamp = pt->timestamp;
(*r_extrahash)->namelen = pt->namelen;
/* Note that the last byte of NAME won't be initialized
* because we don't need it. */
memcpy ((*r_extrahash)->name, pt->name, pt->namelen);
}
pt->buf = NULL;
free_packet (&pkt, NULL);
}
else
{
byte copy_buffer[4096];
int bytes_copied;
*r_extrahash = xtrymalloc (sizeof **r_extrahash);
if (!*r_extrahash)
{
rc = gpg_error_from_syserror ();
goto leave;
}
/* FIXME: We need to parse INP to get the to be hashed data from
* it. */
(*r_extrahash)->mode = 0;
(*r_extrahash)->timestamp = 0;
(*r_extrahash)->namelen = 0;
while ((bytes_copied = iobuf_read (inp, copy_buffer, 4096)) != -1)
if ((rc = iobuf_write (out, copy_buffer, bytes_copied)))
{
log_error ("copying input to output failed: %s\n",
gpg_strerror (rc));
break;
}
wipememory (copy_buffer, 4096); /* burn buffer */
}
leave:
return rc;
}
/*
* Write the signatures from the SK_LIST to OUT. HASH must be a
* non-finalized hash which will not be changes here. EXTRAHASH is
* either NULL or the extra data tro be hashed into v5 signatures.
*/
static int
write_signature_packets (ctrl_t ctrl,
SK_LIST sk_list, IOBUF out, gcry_md_hd_t hash,
pt_extra_hash_data_t extrahash,
int sigclass, u32 timestamp, u32 duration,
int status_letter, const char *cache_nonce)
{
SK_LIST sk_rover;
/* Loop over the certificates with secret keys. */
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
{
PKT_public_key *pk;
PKT_signature *sig;
gcry_md_hd_t md;
gpg_error_t err;
pk = sk_rover->pk;
/* Build the signature packet. */
sig = xtrycalloc (1, sizeof *sig);
if (!sig)
return gpg_error_from_syserror ();
if (pk->version >= 5)
sig->version = 5; /* Required for v5 keys. */
else
sig->version = 4; /* Required. */
keyid_from_pk (pk, sig->keyid);
sig->digest_algo = hash_for (pk);
sig->pubkey_algo = pk->pubkey_algo;
if (timestamp)
sig->timestamp = timestamp;
else
sig->timestamp = make_timestamp();
if (duration)
sig->expiredate = sig->timestamp + duration;
sig->sig_class = sigclass;
if (gcry_md_copy (&md, hash))
BUG ();
build_sig_subpkt_from_sig (sig, pk, 0);
mk_notation_policy_etc (ctrl, sig, NULL, pk);
if (opt.flags.include_key_block && IS_SIG (sig))
err = mk_sig_subpkt_key_block (ctrl, sig, pk);
else
err = 0;
hash_sigversion_to_magic (md, sig, extrahash);
gcry_md_final (md);
if (!err)
err = do_sign (ctrl, pk, sig, md, hash_for (pk), cache_nonce, 0);
gcry_md_close (md);
if (!err)
{
/* Write the packet. */
PACKET pkt;
init_packet (&pkt);
pkt.pkttype = PKT_SIGNATURE;
pkt.pkt.signature = sig;
err = build_packet (out, &pkt);
if (!err && is_status_enabled())
print_status_sig_created (pk, sig, status_letter);
free_packet (&pkt, NULL);
if (err)
log_error ("build signature packet failed: %s\n",
gpg_strerror (err));
}
else
free_seckey_enc (sig);
if (err)
return err;
}
return 0;
}
/*
* Sign the files whose names are in FILENAME using all secret keys
* which can be taken from LOCUSR, if this is NULL, use the default
* secret key.
* If DETACHED has the value true, make a detached signature.
* If ENCRYPTFLAG is true, use REMUSER (or ask if it is NULL) to encrypt the
* signed data for these users. If ENCRYPTFLAG is 2 symmetric encryption
* is also used.
* If FILENAMES->d is NULL read from stdin and ignore the detached mode.
* If OUTFILE is not NULL; this file is used for output and the function
* does not ask for overwrite permission; output is then always
* uncompressed, non-armored and in binary mode.
*/
int
sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
int encryptflag, strlist_t remusr, const char *outfile )
{
const char *fname;
armor_filter_context_t *afx;
compress_filter_context_t zfx;
md_filter_context_t mfx;
text_filter_context_t tfx;
progress_filter_context_t *pfx;
encrypt_filter_context_t efx;
iobuf_t inp = NULL;
iobuf_t out = NULL;
PACKET pkt;
int rc = 0;
PK_LIST pk_list = NULL;
SK_LIST sk_list = NULL;
SK_LIST sk_rover = NULL;
int multifile = 0;
u32 duration=0;
pt_extra_hash_data_t extrahash = NULL;
char peekbuf[32];
int peekbuflen = 0;
pfx = new_progress_context ();
afx = new_armor_context ();
memset (&zfx, 0, sizeof zfx);
memset (&mfx, 0, sizeof mfx);
memset (&efx, 0, sizeof efx);
efx.ctrl = ctrl;
init_packet (&pkt);
if (filenames)
{
fname = filenames->d;
multifile = !!filenames->next;
}
else
fname = NULL;
if (fname && filenames->next && (!detached || encryptflag))
log_bug ("multiple files can only be detached signed");
if (encryptflag == 2
&& (rc = setup_symkey (&efx.symkey_s2k, &efx.symkey_dek)))
goto leave;
if (opt.ask_sig_expire && !opt.batch)
duration = ask_expire_interval(1,opt.def_sig_expire);
else
duration = parse_expire_string(opt.def_sig_expire);
/* Note: In the old non-agent version the following call used to
* unprotect the secret key. This is now done on demand by the agent. */
if ((rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG )))
goto leave;
if (encryptflag
&& (rc = build_pk_list (ctrl, remusr, &pk_list)))
goto leave;
/* Prepare iobufs. */
if (multifile) /* have list of filenames */
inp = NULL; /* we do it later */
else
{
inp = iobuf_open(fname);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if (!inp)
{
rc = gpg_error_from_syserror ();
log_error (_("can't open '%s': %s\n"), fname? fname: "[stdin]",
strerror (errno));
goto leave;
}
peekbuflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof peekbuf, peekbuf);
if (peekbuflen < 0)
{
peekbuflen = 0;
if (DBG_FILTER)
log_debug ("peeking at input failed\n");
}
handle_progress (pfx, inp, fname);
}
if (outfile)
{
if (is_secured_filename (outfile))
{
out = NULL;
gpg_err_set_errno (EPERM);
}
else
out = iobuf_create (outfile, 0);
if (!out)
{
rc = gpg_error_from_syserror ();
log_error (_("can't create '%s': %s\n"), outfile, gpg_strerror (rc));
goto leave;
}
else if (opt.verbose)
log_info (_("writing to '%s'\n"), outfile);
}
else if ((rc = open_outfile (-1, fname,
opt.armor? 1 : detached? 2 : 0, 0, &out)))
{
goto leave;
}
/* Prepare to calculate the MD over the input. */
if (opt.textmode && !outfile && !multifile)
{
memset (&tfx, 0, sizeof tfx);
iobuf_push_filter (inp, text_filter, &tfx);
}
if (gcry_md_open (&mfx.md, 0, 0))
BUG ();
if (DBG_HASHING)
gcry_md_debug (mfx.md, "sign");
/* If we're encrypting and signing, it is reasonable to pick the
* hash algorithm to use out of the recipient key prefs. This is
* best effort only, as in a DSA2 and smartcard world there are
* cases where we cannot please everyone with a single hash (DSA2
* wants >160 and smartcards want =160). In the future this could
* be more complex with different hashes for each sk, but the
* current design requires a single hash for all SKs. */
if (pk_list)
{
if (opt.def_digest_algo)
{
if (!opt.expert
&& select_algo_from_prefs (pk_list,PREFTYPE_HASH,
opt.def_digest_algo,
NULL) != opt.def_digest_algo)
{
log_info (_("WARNING: forcing digest algorithm %s (%d)"
" violates recipient preferences\n"),
gcry_md_algo_name (opt.def_digest_algo),
opt.def_digest_algo);
}
}
else
{
int algo;
int conflict = 0;
struct pref_hint hint = { 0 };
hint.digest_length = 0;
/* Of course, if the recipient asks for something
* unreasonable (like the wrong hash for a DSA key) then
* don't do it. Check all sk's - if any are DSA or live
* on a smartcard, then the hash has restrictions and we
* may not be able to give the recipient what they want.
* For DSA, pass a hint for the largest q we have. Note
* that this means that a q>160 key will override a q=160
* key and force the use of truncation for the q=160 key.
* The alternative would be to ignore the recipient prefs
* completely and get a different hash for each DSA key in
* hash_for(). The override behavior here is more or less
* reasonable as it is under the control of the user which
* keys they sign with for a given message and the fact
* that the message with multiple signatures won't be
* usable on an implementation that doesn't understand
* DSA2 anyway. */
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next )
{
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA
|| sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
{
int temp_hashlen = gcry_mpi_get_nbits (sk_rover->pk->pkey[1]);
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
{
temp_hashlen = ecdsa_qbits_from_Q (temp_hashlen);
if (!temp_hashlen)
conflict = 1; /* Better don't use the prefs. */
temp_hashlen = (temp_hashlen+7)/8;
/* Fixup for that funny nistp521 (yes, 521) were
* we need to use a 512 bit hash algo. */
if (temp_hashlen == 66)
temp_hashlen = 64;
}
else
temp_hashlen = (temp_hashlen+7)/8;
/* Pick a hash that is large enough for our largest
* Q or matches our Q. If there are several of them
* we run into a conflict and don't use the
* preferences. */
if (hint.digest_length < temp_hashlen)
{
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
{
if (hint.exact)
conflict = 1;
hint.exact = 1;
}
hint.digest_length = temp_hashlen;
}
}
}
if (!conflict
&& (algo = select_algo_from_prefs (pk_list, PREFTYPE_HASH,
-1, &hint)) > 0)
{
/* Note that we later check that the algo is not weak. */
recipient_digest_algo = algo;
}
}
}
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
gcry_md_enable (mfx.md, hash_for (sk_rover->pk));
if (!multifile)
iobuf_push_filter (inp, md_filter, &mfx);
if (detached && !encryptflag)
afx->what = 2;
if (opt.armor && !outfile)
push_armor_filter (afx, out);
if (encryptflag)
{
efx.pk_list = pk_list;
/* fixme: set efx.cfx.datalen if known */
iobuf_push_filter (out, encrypt_filter, &efx);
}
if (opt.compress_algo && !outfile && !detached)
{
int compr_algo = opt.compress_algo;
if (!opt.explicit_compress_option
&& is_file_compressed (peekbuf, peekbuflen))
{
if (opt.verbose)
log_info(_("'%s' already compressed\n"), fname? fname: "[stdin]");
compr_algo = 0;
}
else if (compr_algo==-1)
{
/* If we're not encrypting, then select_algo_from_prefs
* will fail and we'll end up with the default. If we are
* encrypting, select_algo_from_prefs cannot fail since
* there is an assumed preference for uncompressed data.
* Still, if it did fail, we'll also end up with the
* default. */
if ((compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP,
-1, NULL)) == -1)
{
compr_algo = default_compress_algo();
}
}
else if (!opt.expert && pk_list
&& select_algo_from_prefs (pk_list, PREFTYPE_ZIP,
compr_algo, NULL) != compr_algo)
{
log_info (_("WARNING: forcing compression algorithm %s (%d)"
" violates recipient preferences\n"),
compress_algo_to_string (compr_algo), compr_algo);
}
/* Algo 0 means no compression. */
if (compr_algo)
push_compress_filter (out, &zfx, compr_algo);
}
/* Write the one-pass signature packets if needed */
if (!detached)
{
rc = write_onepass_sig_packets (sk_list, out,
opt.textmode && !outfile ? 0x01:0x00);
if (rc)
goto leave;
}
write_status_begin_signing (mfx.md);
/* Setup the inner packet. */
if (detached)
{
size_t iobuf_size = iobuf_set_buffer_size(0) * 1024;
if (multifile)
{
strlist_t sl;
if (opt.verbose)
log_info (_("signing:") );
/* Must walk reverse trough this list. */
for (sl = strlist_last(filenames);
sl;
sl = strlist_prev( filenames, sl))
{
inp = iobuf_open (sl->d);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if (!inp)
{
rc = gpg_error_from_syserror ();
log_error (_("can't open '%s': %s\n"),
sl->d, gpg_strerror (rc));
goto leave;
}
handle_progress (pfx, inp, sl->d);
if (opt.verbose)
log_printf (" '%s'", sl->d );
if (opt.textmode)
{
memset (&tfx, 0, sizeof tfx);
iobuf_push_filter (inp, text_filter, &tfx);
}
iobuf_push_filter (inp, md_filter, &mfx);
while (iobuf_read (inp, NULL, iobuf_size) != -1)
;
iobuf_close (inp);
inp = NULL;
}
if (opt.verbose)
log_printf ("\n");
}
else
{
/* Read, so that the filter can calculate the digest. */
while (iobuf_read (inp, NULL, iobuf_size) != -1)
;
}
}
else
{
rc = write_plaintext_packet (out, inp, fname,
(opt.textmode && !outfile) ?
(opt.mimemode? 'm' : 't') : 'b',
&extrahash);
}
/* Catch errors from above. */
if (rc)
goto leave;
/* Write the signatures. */
rc = write_signature_packets (ctrl, sk_list, out, mfx.md, extrahash,
opt.textmode && !outfile? 0x01 : 0x00,
0, duration, detached ? 'D':'S', NULL);
if (rc)
goto leave;
leave:
if (rc)
iobuf_cancel (out);
else
{
iobuf_close (out);
if (encryptflag)
write_status (STATUS_END_ENCRYPTION);
}
iobuf_close (inp);
gcry_md_close (mfx.md);
release_sk_list (sk_list);
release_pk_list (pk_list);
recipient_digest_algo = 0;
release_progress_context (pfx);
release_armor_context (afx);
xfree (extrahash);
return rc;
}
/*
* Make a clear signature. Note that opt.armor is not needed.
*/
int
clearsign_file (ctrl_t ctrl,
const char *fname, strlist_t locusr, const char *outfile)
{
armor_filter_context_t *afx;
progress_filter_context_t *pfx;
gcry_md_hd_t textmd = NULL;
iobuf_t inp = NULL;
iobuf_t out = NULL;
PACKET pkt;
int rc = 0;
SK_LIST sk_list = NULL;
SK_LIST sk_rover = NULL;
u32 duration = 0;
pt_extra_hash_data_t extrahash = NULL;
pfx = new_progress_context ();
afx = new_armor_context ();
init_packet( &pkt );
if (opt.ask_sig_expire && !opt.batch)
duration = ask_expire_interval (1, opt.def_sig_expire);
else
duration = parse_expire_string (opt.def_sig_expire);
/* Note: In the old non-agent version the following call used to
* unprotect the secret key. This is now done on demand by the agent. */
if ((rc=build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG)))
goto leave;
/* Prepare iobufs. */
inp = iobuf_open (fname);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if (!inp)
{
rc = gpg_error_from_syserror ();
log_error (_("can't open '%s': %s\n"),
fname? fname: "[stdin]", gpg_strerror (rc));
goto leave;
}
handle_progress (pfx, inp, fname);
if (outfile)
{
if (is_secured_filename (outfile))
{
outfile = NULL;
gpg_err_set_errno (EPERM);
}
else
out = iobuf_create (outfile, 0);
if (!out)
{
rc = gpg_error_from_syserror ();
log_error (_("can't create '%s': %s\n"), outfile, gpg_strerror (rc));
goto leave;
}
else if (opt.verbose)
log_info (_("writing to '%s'\n"), outfile);
}
else if ((rc = open_outfile (-1, fname, 1, 0, &out)))
{
goto leave;
}
iobuf_writestr (out, "-----BEGIN PGP SIGNED MESSAGE-----" LF);
{
const char *s;
int any = 0;
byte hashs_seen[256];
memset (hashs_seen, 0, sizeof hashs_seen);
iobuf_writestr (out, "Hash: " );
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
{
int i = hash_for (sk_rover->pk);
if (!hashs_seen[ i & 0xff ])
{
s = gcry_md_algo_name (i);
if (s)
{
hashs_seen[ i & 0xff ] = 1;
if (any)
iobuf_put (out, ',');
iobuf_writestr (out, s);
any = 1;
}
}
}
log_assert (any);
iobuf_writestr (out, LF);
}
if (opt.not_dash_escaped)
iobuf_writestr (out,
"NotDashEscaped: You need "GPG_NAME
" to verify this message" LF);
iobuf_writestr (out, LF );
if (gcry_md_open (&textmd, 0, 0))
BUG ();
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
gcry_md_enable (textmd, hash_for(sk_rover->pk));
if (DBG_HASHING)
gcry_md_debug (textmd, "clearsign");
copy_clearsig_text (out, inp, textmd, !opt.not_dash_escaped, opt.escape_from);
/* fixme: check for read errors */
/* Now write the armor. */
afx->what = 2;
push_armor_filter (afx, out);
/* Prepare EXTRAHASH, so that it can be used for v5 signature. */
extrahash = xtrymalloc (sizeof *extrahash);
if (!extrahash)
{
rc = gpg_error_from_syserror ();
goto leave;
}
else
{
extrahash->mode = 't';
extrahash->timestamp = 0;
extrahash->namelen = 0;
}
/* Write the signatures. */
rc = write_signature_packets (ctrl, sk_list, out, textmd, extrahash,
0x01, 0, duration, 'C', NULL);
if (rc)
goto leave;
leave:
if (rc)
iobuf_cancel (out);
else
iobuf_close (out);
iobuf_close (inp);
gcry_md_close (textmd);
release_sk_list (sk_list);
release_progress_context (pfx);
release_armor_context (afx);
xfree (extrahash);
return rc;
}
/*
* Sign and conventionally encrypt the given file.
* FIXME: Far too much code is duplicated - revamp the whole file.
*/
int
sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr)
{
armor_filter_context_t *afx;
progress_filter_context_t *pfx;
compress_filter_context_t zfx;
md_filter_context_t mfx;
text_filter_context_t tfx;
cipher_filter_context_t cfx;
iobuf_t inp = NULL;
iobuf_t out = NULL;
PACKET pkt;
STRING2KEY *s2k = NULL;
int rc = 0;
SK_LIST sk_list = NULL;
SK_LIST sk_rover = NULL;
int algo;
u32 duration = 0;
int canceled;
pt_extra_hash_data_t extrahash = NULL;
pfx = new_progress_context ();
afx = new_armor_context ();
memset (&zfx, 0, sizeof zfx);
memset (&mfx, 0, sizeof mfx);
memset (&tfx, 0, sizeof tfx);
memset (&cfx, 0, sizeof cfx);
init_packet (&pkt);
if (opt.ask_sig_expire && !opt.batch)
duration = ask_expire_interval (1, opt.def_sig_expire);
else
duration = parse_expire_string (opt.def_sig_expire);
/* Note: In the old non-agent version the following call used to
* unprotect the secret key. This is now done on demand by the agent. */
rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG);
if (rc)
goto leave;
/* Prepare iobufs. */
inp = iobuf_open (fname);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if (!inp)
{
rc = gpg_error_from_syserror ();
log_error (_("can't open '%s': %s\n"),
fname? fname: "[stdin]", gpg_strerror (rc));
goto leave;
}
handle_progress (pfx, inp, fname);
/* Prepare key. */
s2k = xmalloc_clear (sizeof *s2k);
s2k->mode = opt.s2k_mode;
s2k->hash_algo = S2K_DIGEST_ALGO;
algo = default_cipher_algo ();
cfx.dek = passphrase_to_dek (algo, s2k, 1, 1, NULL, 0, &canceled);
if (!cfx.dek || !cfx.dek->keylen)
{
rc = gpg_error (canceled?GPG_ERR_CANCELED:GPG_ERR_BAD_PASSPHRASE);
log_error (_("error creating passphrase: %s\n"), gpg_strerror (rc));
goto leave;
}
cfx.dek->use_aead = use_aead (NULL, cfx.dek->algo);
if (!cfx.dek->use_aead)
cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo);
if (!opt.quiet || !opt.batch)
log_info (_("%s.%s encryption will be used\n"),
openpgp_cipher_algo_name (algo),
cfx.dek->use_aead? openpgp_aead_algo_name (cfx.dek->use_aead)
/**/ : "CFB");
/* Now create the outfile. */
rc = open_outfile (-1, fname, opt.armor? 1:0, 0, &out);
if (rc)
goto leave;
/* Prepare to calculate the MD over the input. */
if (opt.textmode)
iobuf_push_filter (inp, text_filter, &tfx);
if (gcry_md_open (&mfx.md, 0, 0))
BUG ();
if (DBG_HASHING)
gcry_md_debug (mfx.md, "symc-sign");
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
gcry_md_enable (mfx.md, hash_for (sk_rover->pk));
iobuf_push_filter (inp, md_filter, &mfx);
/* Push armor output filter */
if (opt.armor)
push_armor_filter (afx, out);
/* Write the symmetric key packet */
/* (current filters: armor)*/
{
PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc );
enc->version = cfx.dek->use_aead ? 5 : 4;
enc->cipher_algo = cfx.dek->algo;
enc->aead_algo = cfx.dek->use_aead;
enc->s2k = *s2k;
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
if ((rc = build_packet (out, &pkt)))
log_error ("build symkey packet failed: %s\n", gpg_strerror (rc));
xfree (enc);
}
/* Push the encryption filter */
iobuf_push_filter (out,
cfx.dek->use_aead? cipher_filter_aead
/**/ : cipher_filter_cfb,
&cfx);
/* Push the compress filter */
if (default_compress_algo())
{
if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead))
zfx.new_ctb = 1;
push_compress_filter (out, &zfx,default_compress_algo() );
}
/* Write the one-pass signature packets */
/* (current filters: zip - encrypt - armor) */
rc = write_onepass_sig_packets (sk_list, out, opt.textmode? 0x01:0x00);
if (rc)
goto leave;
write_status_begin_signing (mfx.md);
/* Pipe data through all filters; i.e. write the signed stuff. */
/* (current filters: zip - encrypt - armor) */
rc = write_plaintext_packet (out, inp, fname,
opt.textmode ? (opt.mimemode?'m':'t'):'b',
&extrahash);
if (rc)
goto leave;
/* Write the signatures. */
/* (current filters: zip - encrypt - armor) */
rc = write_signature_packets (ctrl, sk_list, out, mfx.md, extrahash,
opt.textmode? 0x01 : 0x00,
0, duration, 'S', NULL);
if (rc)
goto leave;
leave:
if (rc)
iobuf_cancel (out);
else
{
iobuf_close (out);
write_status (STATUS_END_ENCRYPTION);
}
iobuf_close (inp);
release_sk_list (sk_list);
gcry_md_close (mfx.md);
xfree (cfx.dek);
xfree (s2k);
release_progress_context (pfx);
release_armor_context (afx);
xfree (extrahash);
return rc;
}
/*
* Create a v4 signature in *RET_SIG.
*
* PK is the primary key to sign (required for all sigs)
* UID is the user id to sign (required for 0x10..0x13, 0x30)
* SUBPK is subkey to sign (required for 0x18, 0x19, 0x28)
*
* PKSK is the signing key
*
* SIGCLASS is the type of signature to create.
*
* TIMESTAMP is the timestamp to use for the signature. 0 means "now"
*
* DURATION is the amount of time (in seconds) until the signature
* expires.
*
* If CACHED_NONCE is not NULL the agent may use it to avoid
* additional pinnetry popups for the same keyblock.
*
* This function creates the following subpackets: issuer, created,
* and expire (if duration is not 0). Additional subpackets can be
* added using MKSUBPKT, which is called after these subpackets are
* added and before the signature is generated. OPAQUE is passed to
* MKSUBPKT.
*/
int
make_keysig_packet (ctrl_t ctrl,
PKT_signature **ret_sig, PKT_public_key *pk,
PKT_user_id *uid, PKT_public_key *subpk,
PKT_public_key *pksk,
int sigclass,
u32 timestamp, u32 duration,
int (*mksubpkt)(PKT_signature *, void *), void *opaque,
const char *cache_nonce)
{
PKT_signature *sig;
int rc = 0;
int sigversion;
int digest_algo;
gcry_md_hd_t md;
u32 pk_keyid[2], pksk_keyid[2];
unsigned int signhints;
log_assert ((sigclass >= 0x10 && sigclass <= 0x13) || sigclass == 0x1F
|| sigclass == 0x20 || sigclass == 0x18 || sigclass == 0x19
|| sigclass == 0x30 || sigclass == 0x28 );
if (pksk->version >= 5)
sigversion = 5;
else
sigversion = 4;
/* Select the digest algo to use. */
if (opt.cert_digest_algo) /* Forceful override by the user. */
digest_algo = opt.cert_digest_algo;
else if (pksk->pubkey_algo == PUBKEY_ALGO_DSA) /* Meet DSA requirements. */
digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8);
else if (pksk->pubkey_algo == PUBKEY_ALGO_ECDSA) /* Meet ECDSA requirements. */
digest_algo = match_dsa_hash
(ecdsa_qbits_from_Q (gcry_mpi_get_nbits (pksk->pkey[1]))/8);
else if (pksk->pubkey_algo == PUBKEY_ALGO_EDDSA)
{
if (gcry_mpi_get_nbits (pksk->pkey[1]) > 256)
digest_algo = DIGEST_ALGO_SHA512;
else
digest_algo = DIGEST_ALGO_SHA256;
}
else /* Use the default. */
digest_algo = DEFAULT_DIGEST_ALGO;
signhints = SIGNHINT_KEYSIG;
keyid_from_pk (pk, pk_keyid);
keyid_from_pk (pksk, pksk_keyid);
if (pk_keyid[0] == pksk_keyid[0] && pk_keyid[1] == pksk_keyid[1])
signhints |= SIGNHINT_SELFSIG;
if (gcry_md_open (&md, digest_algo, 0))
BUG ();
/* Hash the public key certificate. */
hash_public_key (md, pk);
if (sigclass == 0x18 || sigclass == 0x19 || sigclass == 0x28)
{
/* Hash the subkey binding/backsig/revocation. */
hash_public_key (md, subpk);
if ((subpk->pubkey_usage & PUBKEY_USAGE_RENC))
signhints |= SIGNHINT_ADSK;
}
else if (sigclass != 0x1F && sigclass != 0x20)
{
/* Hash the user id. */
hash_uid (md, sigversion, uid);
}
/* Make the signature packet. */
sig = xmalloc_clear (sizeof *sig);
sig->version = sigversion;
sig->flags.exportable = 1;
sig->flags.revocable = 1;
keyid_from_pk (pksk, sig->keyid);
sig->pubkey_algo = pksk->pubkey_algo;
sig->digest_algo = digest_algo;
sig->timestamp = timestamp? timestamp : make_timestamp ();
if (duration)
sig->expiredate = sig->timestamp + duration;
sig->sig_class = sigclass;
build_sig_subpkt_from_sig (sig, pksk, signhints);
mk_notation_policy_etc (ctrl, sig, pk, pksk);
/* Crucial that the call to mksubpkt comes LAST before the calls
* to finalize the sig as that makes it possible for the mksubpkt
* function to get a reliable pointer to the subpacket area. */
if (mksubpkt)
rc = (*mksubpkt)(sig, opaque);
if (!rc)
{
hash_sigversion_to_magic (md, sig, NULL);
gcry_md_final (md);
rc = complete_sig (ctrl, sig, pksk, md, cache_nonce, signhints);
}
gcry_md_close (md);
if (rc)
free_seckey_enc (sig);
else
*ret_sig = sig;
return rc;
}
/*
* Create a new signature packet based on an existing one.
* Only user ID signatures are supported for now.
* PK is the public key to work on.
* PKSK is the key used to make the signature.
*
* TODO: Merge this with make_keysig_packet.
*/
gpg_error_t
update_keysig_packet (ctrl_t ctrl,
PKT_signature **ret_sig,
PKT_signature *orig_sig,
PKT_public_key *pk,
PKT_user_id *uid,
PKT_public_key *subpk,
PKT_public_key *pksk,
int (*mksubpkt)(PKT_signature *, void *),
void *opaque)
{
PKT_signature *sig;
gpg_error_t rc = 0;
int digest_algo;
gcry_md_hd_t md;
u32 pk_keyid[2], pksk_keyid[2];
unsigned int signhints = 0;
if ((!orig_sig || !pk || !pksk)
|| (orig_sig->sig_class >= 0x10 && orig_sig->sig_class <= 0x13 && !uid)
|| (orig_sig->sig_class == 0x18 && !subpk))
return GPG_ERR_GENERAL;
/* Either use the override digest algo or in the normal case the
* original digest algorithm. However, iff the original digest
* algorithms is SHA-1 and we are in gnupg or de-vs compliance mode
* we switch to SHA-256 (done by the macro). */
if (opt.cert_digest_algo)
digest_algo = opt.cert_digest_algo;
else if (pksk->pubkey_algo == PUBKEY_ALGO_DSA
|| pksk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pksk->pubkey_algo == PUBKEY_ALGO_EDDSA)
digest_algo = orig_sig->digest_algo;
else if (orig_sig->digest_algo == DIGEST_ALGO_SHA1
|| orig_sig->digest_algo == DIGEST_ALGO_RMD160)
digest_algo = DEFAULT_DIGEST_ALGO;
else
digest_algo = orig_sig->digest_algo;
signhints = SIGNHINT_KEYSIG;
keyid_from_pk (pk, pk_keyid);
keyid_from_pk (pksk, pksk_keyid);
if (pk_keyid[0] == pksk_keyid[0] && pk_keyid[1] == pksk_keyid[1])
signhints |= SIGNHINT_SELFSIG;
if (gcry_md_open (&md, digest_algo, 0))
BUG ();
/* Hash the public key certificate and the user id. */
hash_public_key (md, pk);
if (orig_sig->sig_class == 0x18)
hash_public_key (md, subpk);
else
hash_uid (md, orig_sig->version, uid);
/* Create a new signature packet. */
sig = copy_signature (NULL, orig_sig);
/* Don't generate version 3 signature, but newer. */
if (sig->version == 3)
{
if (pk->version > 3)
sig->version = pk->version;
else
sig->version = 4;
}
sig->digest_algo = digest_algo;
/* We need to create a new timestamp so that new sig expiration
* calculations are done correctly... */
sig->timestamp = make_timestamp();
/* ... but we won't make a timestamp earlier than the existing
* one. */
{
int tmout = 0;
while (sig->timestamp <= orig_sig->timestamp)
{
if (++tmout > 5 && !opt.ignore_time_conflict)
{
rc = gpg_error (GPG_ERR_TIME_CONFLICT);
goto leave;
}
gnupg_sleep (1);
sig->timestamp = make_timestamp();
}
}
/* Detect an ADSK key binding signature. */
if ((sig->sig_class == 0x18
|| sig->sig_class == 0x19 || sig->sig_class == 0x28)
&& (pk->pubkey_usage & PUBKEY_USAGE_RENC))
signhints |= SIGNHINT_ADSK;
/* Note that already expired sigs will remain expired (with a
* duration of 1) since build-packet.c:build_sig_subpkt_from_sig
* detects this case. */
/* Put the updated timestamp into the sig. Note that this will
* automagically lower any sig expiration dates to correctly
* correspond to the differences in the timestamps (i.e. the
* duration will shrink). */
build_sig_subpkt_from_sig (sig, pksk, signhints);
if (mksubpkt)
rc = (*mksubpkt)(sig, opaque);
if (!rc)
{
hash_sigversion_to_magic (md, sig, NULL);
gcry_md_final (md);
rc = complete_sig (ctrl, sig, pksk, md, NULL, signhints);
}
leave:
gcry_md_close (md);
if (rc)
free_seckey_enc (sig);
else
*ret_sig = sig;
return rc;
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Apr 25, 3:40 PM (1 d, 20 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
66/f2/b664413a83cfbc93613837cf9180

Event Timeline