Page MenuHome GnuPG

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/g10/armor.c b/g10/armor.c
index ed5ef5d2e..948b8c802 100644
--- a/g10/armor.c
+++ b/g10/armor.c
@@ -1,1604 +1,1612 @@
/* armor.c - Armor filter
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007 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 <ctype.h>
#include "gpg.h"
#include "../common/status.h"
#include "../common/iobuf.h"
#include "../common/util.h"
#include "filter.h"
#include "packet.h"
#include "options.h"
#include "main.h"
#include "../common/i18n.h"
#define MAX_LINELEN 20000
static const byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static u32 asctobin[4][256]; /* runtime initialized */
static int is_initialized;
typedef enum {
fhdrHASArmor = 0,
fhdrNOArmor,
fhdrINIT,
fhdrINITCont,
fhdrINITSkip,
fhdrCHECKBegin,
fhdrWAITHeader,
fhdrWAITClearsig,
fhdrSKIPHeader,
fhdrCLEARSIG,
fhdrREADClearsig,
fhdrNullClearsig,
fhdrEMPTYClearsig,
fhdrCHECKClearsig,
fhdrCHECKClearsig2,
fhdrCHECKDashEscaped,
fhdrCHECKDashEscaped2,
fhdrCHECKDashEscaped3,
fhdrREADClearsigNext,
fhdrENDClearsig,
fhdrENDClearsigHelp,
fhdrTESTSpaces,
fhdrCLEARSIGSimple,
fhdrCLEARSIGSimpleNext,
fhdrTEXT,
fhdrTEXTSimple,
fhdrERROR,
fhdrERRORShow,
fhdrEOF
} fhdr_state_t;
/* if we encounter this armor string with this index, go
* into a mode which fakes packets and wait for the next armor */
#define BEGIN_SIGNATURE 2
#define BEGIN_SIGNED_MSG_IDX 3
static char *head_strings[] = {
"BEGIN PGP MESSAGE",
"BEGIN PGP PUBLIC KEY BLOCK",
"BEGIN PGP SIGNATURE",
"BEGIN PGP SIGNED MESSAGE",
"BEGIN PGP ARMORED FILE", /* gnupg extension */
"BEGIN PGP PRIVATE KEY BLOCK",
"BEGIN PGP SECRET KEY BLOCK", /* only used by pgp2 */
NULL
};
static char *tail_strings[] = {
"END PGP MESSAGE",
"END PGP PUBLIC KEY BLOCK",
"END PGP SIGNATURE",
"END dummy",
"END PGP ARMORED FILE",
"END PGP PRIVATE KEY BLOCK",
"END PGP SECRET KEY BLOCK",
NULL
};
static int armor_filter ( void *opaque, int control,
iobuf_t chain, byte *buf, size_t *ret_len);
/* Create a new context for armor filters. */
armor_filter_context_t *
new_armor_context (void)
{
armor_filter_context_t *afx;
gpg_error_t err;
afx = xcalloc (1, sizeof *afx);
if (afx)
{
err = gcry_md_open (&afx->crc_md, GCRY_MD_CRC24_RFC2440, 0);
if (err != 0)
{
log_error ("gcry_md_open failed for GCRY_MD_CRC24_RFC2440: %s",
gpg_strerror (err));
xfree (afx);
return NULL;
}
afx->refcount = 1;
}
return afx;
}
/* Release an armor filter context. Passing NULL is explicitly
allowed and a no-op. */
void
release_armor_context (armor_filter_context_t *afx)
{
if (!afx)
return;
log_assert (afx->refcount);
if ( --afx->refcount )
return;
gcry_md_close (afx->crc_md);
xfree (afx);
}
/* Push the armor filter onto the iobuf stream IOBUF. */
int
push_armor_filter (armor_filter_context_t *afx, iobuf_t iobuf)
{
int rc;
afx->refcount++;
rc = iobuf_push_filter (iobuf, armor_filter, afx);
if (rc)
afx->refcount--;
return rc;
}
+/* This function returns true if the armor filter detected that the
+ * input was indeed armored. Gives a valid result only after the
+ * first PGP packet has been read. */
+int
+was_armored (armor_filter_context_t *afx)
+{
+ return (afx && !afx->inp_bypass);
+}
static void
initialize(void)
{
u32 i;
const byte *s;
/* Build the helptable for radix64 to bin conversion. Value 0xffffffff is
used to detect invalid characters. */
memset (asctobin, 0xff, sizeof(asctobin));
for(s=bintoasc,i=0; *s; s++,i++ )
{
asctobin[0][*s] = i << (0 * 6);
asctobin[1][*s] = i << (1 * 6);
asctobin[2][*s] = i << (2 * 6);
asctobin[3][*s] = i << (3 * 6);
}
is_initialized=1;
}
static inline u32
get_afx_crc (armor_filter_context_t *afx)
{
const byte *crc_buf;
u32 crc;
crc_buf = gcry_md_read (afx->crc_md, GCRY_MD_CRC24_RFC2440);
crc = crc_buf[0];
crc <<= 8;
crc |= crc_buf[1];
crc <<= 8;
crc |= crc_buf[2];
return crc;
}
/*
* Check whether this is an armored file. See also
* parse-packet.c for details on this code.
*
* Note that the buffer BUF needs to be at least 2 bytes long. If in
* doubt that the second byte to 0.
*
* Returns: True if it seems to be armored
*/
static int
is_armored (const byte *buf)
{
int ctb, pkttype;
int indeterminate_length_allowed;
ctb = *buf;
if( !(ctb & 0x80) )
/* The most significant bit of the CTB must be set. Since it is
cleared, this is not a binary OpenPGP message. Assume it is
armored. */
return 1;
pkttype = ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
switch( pkttype ) {
case PKT_PUBKEY_ENC:
case PKT_SIGNATURE:
case PKT_SYMKEY_ENC:
case PKT_ONEPASS_SIG:
case PKT_SECRET_KEY:
case PKT_PUBLIC_KEY:
case PKT_SECRET_SUBKEY:
case PKT_MARKER:
case PKT_RING_TRUST:
case PKT_USER_ID:
case PKT_PUBLIC_SUBKEY:
case PKT_ATTRIBUTE:
case PKT_MDC:
indeterminate_length_allowed = 0;
break;
case PKT_COMPRESSED:
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD:
case PKT_PLAINTEXT:
case PKT_OLD_COMMENT:
case PKT_COMMENT:
case PKT_GPG_CONTROL:
indeterminate_length_allowed = 1;
break;
default:
/* Invalid packet type. */
return 1;
}
if (! indeterminate_length_allowed)
/* It is only legal to use an indeterminate length with a few
packet types. If a packet uses an indeterminate length, but
that is not allowed, then the data is not valid binary
OpenPGP data. */
{
int new_format;
int indeterminate_length;
new_format = !! (ctb & (1 << 6));
if (new_format)
indeterminate_length = (buf[1] >= 224 && buf[1] < 255);
else
indeterminate_length = (ctb & 3) == 3;
if (indeterminate_length)
return 1;
}
/* The first CTB seems legit. It is probably not armored
data. */
return 0;
}
/****************
* Try to check whether the iobuf is armored
* Returns true if this may be the case; the caller should use the
* filter to do further processing.
*/
int
use_armor_filter( IOBUF a )
{
byte buf[2];
int n;
/* fixme: there might be a problem with iobuf_peek */
n = iobuf_peek (a, buf, 2);
if( n == -1 )
return 0; /* EOF, doesn't matter whether armored or not */
if( !n )
return 1; /* can't check it: try armored */
if (n != 2)
return 0; /* short buffer */
return is_armored(buf);
}
static void
invalid_armor(void)
{
write_status(STATUS_BADARMOR);
g10_exit(1); /* stop here */
}
/****************
* check whether the armor header is valid on a signed message.
* this is for security reasons: the header lines are not included in the
* hash and by using some creative formatting rules, Mallory could fake
* any text at the beginning of a document; assuming it is read with
* a simple viewer. We only allow the Hash Header.
*/
static int
parse_hash_header( const char *line )
{
const char *s, *s2;
unsigned found = 0;
if( strlen(line) < 6 || strlen(line) > 60 )
return 0; /* too short or too long */
if( memcmp( line, "Hash:", 5 ) )
return 0; /* invalid header */
for(s=line+5;;s=s2) {
for(; *s && (*s==' ' || *s == '\t'); s++ )
;
if( !*s )
break;
for(s2=s+1; *s2 && *s2!=' ' && *s2 != '\t' && *s2 != ','; s2++ )
;
if( !strncmp( s, "RIPEMD160", s2-s ) )
found |= 1;
else if( !strncmp( s, "SHA1", s2-s ) )
found |= 2;
else if( !strncmp( s, "SHA224", s2-s ) )
found |= 8;
else if( !strncmp( s, "SHA256", s2-s ) )
found |= 16;
else if( !strncmp( s, "SHA384", s2-s ) )
found |= 32;
else if( !strncmp( s, "SHA512", s2-s ) )
found |= 64;
else
return 0;
for(; *s2 && (*s2==' ' || *s2 == '\t'); s2++ )
;
if( *s2 && *s2 != ',' )
return 0;
if( *s2 )
s2++;
}
return found;
}
/* Returns true if this is a valid armor tag as per RFC-2440bis-21. */
static int
is_armor_tag(const char *line)
{
if(strncmp(line,"Version",7)==0
|| strncmp(line,"Comment",7)==0
|| strncmp(line,"MessageID",9)==0
|| strncmp(line,"Hash",4)==0
|| strncmp(line,"Charset",7)==0)
return 1;
return 0;
}
/****************
* Check whether this is a armor line. Returns: -1 if it is not a
* armor header, 42 if it is a generic header, or the index number of
* the armor header.
*/
static int
is_armor_header( byte *line, unsigned len )
{
const char *s;
byte *save_p, *p;
int save_c;
int i;
if( len < 15 )
return -1; /* too short */
if( memcmp( line, "-----", 5 ) )
return -1; /* no */
p = strstr( line+5, "-----");
if( !p )
return -1;
save_p = p;
p += 5;
/* Some Windows environments seem to add whitespace to the end of
the line, so we strip it here. This becomes strict if
--rfc2440 is set since 2440 reads "The header lines, therefore,
MUST start at the beginning of a line, and MUST NOT have text
following them on the same line." It is unclear whether "text"
refers to all text or just non-whitespace text. 4880 clarified
this was only non-whitespace text. */
if(RFC2440)
{
if( *p == '\r' )
p++;
if( *p == '\n' )
p++;
}
else
while(*p==' ' || *p=='\r' || *p=='\n' || *p=='\t')
p++;
if( *p )
return -1; /* garbage after dashes */
save_c = *save_p; *save_p = 0;
p = line+5;
for(i=0; (s=head_strings[i]); i++ )
if( !strcmp(s, p) )
break;
*save_p = save_c;
if (!s)
{
if (!strncmp (p, "BEGIN ", 6))
return 42;
return -1; /* unknown armor line */
}
if( opt.verbose > 1 )
log_info(_("armor: %s\n"), head_strings[i]);
return i;
}
/****************
* Parse a header lines
* Return 0: Empty line (end of header lines)
* -1: invalid header line
* >0: Good header line
*/
static int
parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len )
{
byte *p;
int hashes=0;
unsigned int len2;
len2 = length_sans_trailing_ws ( line, len );
if( !len2 ) {
afx->buffer_pos = len2; /* (it is not the fine way to do it here) */
return 0; /* WS only: same as empty line */
}
/*
This is fussy. The spec says that a header line is delimited
with a colon-space pair. This means that a line such as
"Comment: " (with nothing else) is actually legal as an empty
string comment. However, email and cut-and-paste being what it
is, that trailing space may go away. Therefore, we accept empty
headers delimited with only a colon. --rfc2440, as always,
makes this strict and enforces the colon-space pair. -dms
*/
p = strchr( line, ':');
if (!p && afx->dearmor_state)
return 0; /* Special treatment in --dearmor mode. */
if( !p || (RFC2440 && p[1]!=' ')
|| (!RFC2440 && p[1]!=' ' && p[1]!='\n' && p[1]!='\r'))
{
log_error (_("invalid armor header: "));
es_write_sanitized (log_get_stream (), line, len, NULL, NULL);
log_printf ("\n");
return -1;
}
/* Chop off the whitespace we detected before */
len=len2;
line[len2]='\0';
if( opt.verbose ) {
log_info(_("armor header: "));
es_write_sanitized (log_get_stream (), line, len, NULL, NULL);
log_printf ("\n");
}
if (afx->dearmor_mode)
;
else if (afx->in_cleartext)
{
if( (hashes=parse_hash_header( line )) )
afx->hashes |= hashes;
else if( strlen(line) > 15 && !memcmp( line, "NotDashEscaped:", 15 ) )
afx->not_dash_escaped = 1;
else
{
log_error(_("invalid clearsig header\n"));
return -1;
}
}
else if(!is_armor_tag(line))
{
/* Section 6.2: "Unknown keys should be reported to the user,
but OpenPGP should continue to process the message." Note
that in a clearsigned message this applies to the signature
part (i.e. "BEGIN PGP SIGNATURE") and not the signed data
("BEGIN PGP SIGNED MESSAGE"). The only key allowed in the
signed data section is "Hash". */
log_info(_("unknown armor header: "));
es_write_sanitized (log_get_stream (), line, len, NULL, NULL);
log_printf ("\n");
}
return 1;
}
/* figure out whether the data is armored or not */
static int
check_input( armor_filter_context_t *afx, IOBUF a )
{
int rc = 0;
int i;
byte *line;
unsigned len;
unsigned maxlen;
int hdr_line = -1;
/* read the first line to see whether this is armored data */
maxlen = MAX_LINELEN;
len = afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
line = afx->buffer;
if( !maxlen ) {
/* line has been truncated: assume not armored */
afx->inp_checked = 1;
afx->inp_bypass = 1;
return 0;
}
if( !len ) {
return -1; /* eof */
}
/* (the line is always a C string but maybe longer) */
if( *line == '\n' || ( len && (*line == '\r' && line[1]=='\n') ) )
;
else if (len >= 2 && !is_armored (line)) {
afx->inp_checked = 1;
afx->inp_bypass = 1;
return 0;
}
/* find the armor header */
while(len) {
i = is_armor_header( line, len );
if ( i == 42 ) {
if (afx->dearmor_mode) {
afx->dearmor_state = 1;
break;
}
}
else if (i >= 0
&& !(afx->only_keyblocks && i != 1 && i != 5 && i != 6 )) {
hdr_line = i;
if( hdr_line == BEGIN_SIGNED_MSG_IDX ) {
if( afx->in_cleartext ) {
log_error(_("nested clear text signatures\n"));
rc = gpg_error (GPG_ERR_INV_ARMOR);
}
afx->in_cleartext = 1;
}
break;
}
/* read the next line (skip all truncated lines) */
do {
maxlen = MAX_LINELEN;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
line = afx->buffer;
len = afx->buffer_len;
} while( !maxlen );
}
/* Parse the header lines. */
while(len) {
/* Read the next line (skip all truncated lines). */
do {
maxlen = MAX_LINELEN;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
line = afx->buffer;
len = afx->buffer_len;
} while( !maxlen );
i = parse_header_line( afx, line, len );
if( i <= 0 ) {
if (i && RFC2440)
rc = GPG_ERR_INV_ARMOR;
break;
}
}
if( rc )
invalid_armor();
else if( afx->in_cleartext )
afx->faked = 1;
else {
afx->inp_checked = 1;
gcry_md_reset (afx->crc_md);
afx->idx = 0;
afx->radbuf[0] = 0;
}
return rc;
}
#define PARTIAL_CHUNK 512
#define PARTIAL_POW 9
/****************
* Fake a literal data packet and wait for the next armor line
* fixme: empty line handling and null length clear text signature are
* not implemented/checked.
*/
static int
fake_packet( armor_filter_context_t *afx, IOBUF a,
size_t *retn, byte *buf, size_t size )
{
int rc = 0;
size_t len = 0;
int lastline = 0;
unsigned maxlen, n;
byte *p;
byte tempbuf[PARTIAL_CHUNK];
size_t tempbuf_len=0;
int this_truncated;
while( !rc && size-len>=(PARTIAL_CHUNK+1)) {
/* copy what we have in the line buffer */
if( afx->faked == 1 )
afx->faked++; /* skip the first (empty) line */
else
{
/* It's full, so write this partial chunk */
if(tempbuf_len==PARTIAL_CHUNK)
{
buf[len++]=0xE0+PARTIAL_POW;
memcpy(&buf[len],tempbuf,PARTIAL_CHUNK);
len+=PARTIAL_CHUNK;
tempbuf_len=0;
continue;
}
while( tempbuf_len < PARTIAL_CHUNK
&& afx->buffer_pos < afx->buffer_len )
tempbuf[tempbuf_len++] = afx->buffer[afx->buffer_pos++];
if( tempbuf_len==PARTIAL_CHUNK )
continue;
}
/* read the next line */
maxlen = MAX_LINELEN;
afx->buffer_pos = 0;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
if( !afx->buffer_len ) {
rc = -1; /* eof (should not happen) */
continue;
}
if( !maxlen )
{
afx->truncated++;
this_truncated = 1;
}
else
this_truncated = 0;
p = afx->buffer;
n = afx->buffer_len;
/* Armor header or dash-escaped line? */
if(p[0]=='-')
{
/* 2440bis-10: When reversing dash-escaping, an
implementation MUST strip the string "- " if it occurs
at the beginning of a line, and SHOULD warn on "-" and
any character other than a space at the beginning of a
line. */
if(p[1]==' ' && !afx->not_dash_escaped)
{
/* It's a dash-escaped line, so skip over the
escape. */
afx->buffer_pos = 2;
}
else if(p[1]=='-' && p[2]=='-' && p[3]=='-' && p[4]=='-')
{
/* Five dashes in a row mean it's probably armor
header. */
int type = is_armor_header( p, n );
if (type == 42)
type = -1; /* Only OpenPGP armors are expected. */
if( afx->not_dash_escaped && type != BEGIN_SIGNATURE )
; /* this is okay */
else
{
if( type != BEGIN_SIGNATURE )
{
log_info(_("unexpected armor: "));
es_write_sanitized (log_get_stream (), p, n,
NULL, NULL);
log_printf ("\n");
}
lastline = 1;
rc = -1;
}
}
else if(!afx->not_dash_escaped)
{
/* Bad dash-escaping. */
log_info (_("invalid dash escaped line: "));
es_write_sanitized (log_get_stream (), p, n, NULL, NULL);
log_printf ("\n");
}
}
/* Now handle the end-of-line canonicalization */
if( !afx->not_dash_escaped || this_truncated)
{
int crlf = n > 1 && p[n-2] == '\r' && p[n-1]=='\n';
afx->buffer_len=
trim_trailing_chars( &p[afx->buffer_pos], n-afx->buffer_pos,
" \t\r\n");
afx->buffer_len+=afx->buffer_pos;
/* the buffer is always allocated with enough space to append
* the removed [CR], LF and a Nul
* The reason for this complicated procedure is to keep at least
* the original type of lineending - handling of the removed
* trailing spaces seems to be impossible in our method
* of faking a packet; either we have to use a temporary file
* or calculate the hash here in this module and somehow find
* a way to send the hash down the processing line (well, a special
* faked packet could do the job).
*
* To make sure that a truncated line triggers a bad
* signature error we replace a removed LF by a FF or
* append a FF. Right, this is a hack but better than a
* global variable and way easier than to introduce a new
* control packet or insert a line like "[truncated]\n"
* into the filter output.
*/
if( crlf )
afx->buffer[afx->buffer_len++] = '\r';
afx->buffer[afx->buffer_len++] = this_truncated? '\f':'\n';
afx->buffer[afx->buffer_len] = '\0';
}
}
if( lastline ) { /* write last (ending) length header */
if(tempbuf_len<192)
buf[len++]=tempbuf_len;
else
{
buf[len++]=((tempbuf_len-192)/256) + 192;
buf[len++]=(tempbuf_len-192) % 256;
}
memcpy(&buf[len],tempbuf,tempbuf_len);
len+=tempbuf_len;
rc = 0;
afx->faked = 0;
afx->in_cleartext = 0;
/* and now read the header lines */
afx->buffer_pos = 0;
for(;;) {
int i;
/* read the next line (skip all truncated lines) */
do {
maxlen = MAX_LINELEN;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
} while( !maxlen );
p = afx->buffer;
n = afx->buffer_len;
if( !n ) {
rc = -1;
break; /* eof */
}
i = parse_header_line( afx, p , n );
if( i <= 0 ) {
if( i )
invalid_armor();
break;
}
}
afx->inp_checked = 1;
gcry_md_reset (afx->crc_md);
afx->idx = 0;
afx->radbuf[0] = 0;
}
*retn = len;
return rc;
}
static int
invalid_crc(void)
{
if ( opt.ignore_crc_error )
return 0;
log_inc_errorcount();
return gpg_error (GPG_ERR_INV_ARMOR);
}
static int
radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
byte *buf, size_t size )
{
byte val;
int c;
u32 binc;
int checkcrc=0;
int rc = 0;
size_t n = 0;
int idx, onlypad=0;
int skip_fast = 0;
idx = afx->idx;
val = afx->radbuf[0];
for( n=0; n < size; ) {
if( afx->buffer_pos < afx->buffer_len )
c = afx->buffer[afx->buffer_pos++];
else { /* read the next line */
unsigned maxlen = MAX_LINELEN;
afx->buffer_pos = 0;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
if( !maxlen )
afx->truncated++;
if( !afx->buffer_len )
break; /* eof */
continue;
}
again:
binc = asctobin[0][c];
if( binc != 0xffffffffUL )
{
if( idx == 0 && skip_fast == 0
&& afx->buffer_pos + (16 - 1) < afx->buffer_len
&& n + 12 < size)
{
/* Fast path for radix64 to binary conversion. */
u32 b0,b1,b2,b3;
/* Speculatively load 15 more input bytes. */
b0 = binc << (3 * 6);
b0 |= asctobin[2][afx->buffer[afx->buffer_pos + 0]];
b0 |= asctobin[1][afx->buffer[afx->buffer_pos + 1]];
b0 |= asctobin[0][afx->buffer[afx->buffer_pos + 2]];
b1 = asctobin[3][afx->buffer[afx->buffer_pos + 3]];
b1 |= asctobin[2][afx->buffer[afx->buffer_pos + 4]];
b1 |= asctobin[1][afx->buffer[afx->buffer_pos + 5]];
b1 |= asctobin[0][afx->buffer[afx->buffer_pos + 6]];
b2 = asctobin[3][afx->buffer[afx->buffer_pos + 7]];
b2 |= asctobin[2][afx->buffer[afx->buffer_pos + 8]];
b2 |= asctobin[1][afx->buffer[afx->buffer_pos + 9]];
b2 |= asctobin[0][afx->buffer[afx->buffer_pos + 10]];
b3 = asctobin[3][afx->buffer[afx->buffer_pos + 11]];
b3 |= asctobin[2][afx->buffer[afx->buffer_pos + 12]];
b3 |= asctobin[1][afx->buffer[afx->buffer_pos + 13]];
b3 |= asctobin[0][afx->buffer[afx->buffer_pos + 14]];
/* Check if any of the input bytes were invalid. */
if( (b0 | b1 | b2 | b3) != 0xffffffffUL )
{
/* All 16 bytes are valid. */
buf[n + 0] = b0 >> (2 * 8);
buf[n + 1] = b0 >> (1 * 8);
buf[n + 2] = b0 >> (0 * 8);
buf[n + 3] = b1 >> (2 * 8);
buf[n + 4] = b1 >> (1 * 8);
buf[n + 5] = b1 >> (0 * 8);
buf[n + 6] = b2 >> (2 * 8);
buf[n + 7] = b2 >> (1 * 8);
buf[n + 8] = b2 >> (0 * 8);
buf[n + 9] = b3 >> (2 * 8);
buf[n + 10] = b3 >> (1 * 8);
buf[n + 11] = b3 >> (0 * 8);
afx->buffer_pos += 16 - 1;
n += 12;
continue;
}
else if( b0 == 0xffffffffUL )
{
/* byte[1..3] have invalid character(s). Switch to slow
path. */
skip_fast = 1;
}
else if( b1 == 0xffffffffUL )
{
/* byte[4..7] have invalid character(s), first 4 bytes are
valid. */
buf[n + 0] = b0 >> (2 * 8);
buf[n + 1] = b0 >> (1 * 8);
buf[n + 2] = b0 >> (0 * 8);
afx->buffer_pos += 4 - 1;
n += 3;
skip_fast = 1;
continue;
}
else if( b2 == 0xffffffffUL )
{
/* byte[8..11] have invalid character(s), first 8 bytes are
valid. */
buf[n + 0] = b0 >> (2 * 8);
buf[n + 1] = b0 >> (1 * 8);
buf[n + 2] = b0 >> (0 * 8);
buf[n + 3] = b1 >> (2 * 8);
buf[n + 4] = b1 >> (1 * 8);
buf[n + 5] = b1 >> (0 * 8);
afx->buffer_pos += 8 - 1;
n += 6;
skip_fast = 1;
continue;
}
else /*if( b3 == 0xffffffffUL )*/
{
/* byte[12..15] have invalid character(s), first 12 bytes
are valid. */
buf[n + 0] = b0 >> (2 * 8);
buf[n + 1] = b0 >> (1 * 8);
buf[n + 2] = b0 >> (0 * 8);
buf[n + 3] = b1 >> (2 * 8);
buf[n + 4] = b1 >> (1 * 8);
buf[n + 5] = b1 >> (0 * 8);
buf[n + 6] = b2 >> (2 * 8);
buf[n + 7] = b2 >> (1 * 8);
buf[n + 8] = b2 >> (0 * 8);
afx->buffer_pos += 12 - 1;
n += 9;
skip_fast = 1;
continue;
}
}
switch(idx)
{
case 0: val = binc << 2; break;
case 1: val |= (binc>>4)&3; buf[n++]=val;val=(binc<<4)&0xf0;break;
case 2: val |= (binc>>2)&15; buf[n++]=val;val=(binc<<6)&0xc0;break;
case 3: val |= binc&0x3f; buf[n++] = val; break;
}
idx = (idx+1) % 4;
continue;
}
skip_fast = 0;
if( c == '\n' || c == ' ' || c == '\r' || c == '\t' )
continue;
else if( c == '=' ) { /* pad character: stop */
/* some mailers leave quoted-printable encoded characters
* so we try to workaround this */
if( afx->buffer_pos+2 < afx->buffer_len ) {
int cc1, cc2, cc3;
cc1 = afx->buffer[afx->buffer_pos];
cc2 = afx->buffer[afx->buffer_pos+1];
cc3 = afx->buffer[afx->buffer_pos+2];
if( isxdigit(cc1) && isxdigit(cc2)
&& strchr( "=\n\r\t ", cc3 )) {
/* well it seems to be the case - adjust */
c = isdigit(cc1)? (cc1 - '0'): (ascii_toupper(cc1)-'A'+10);
c <<= 4;
c |= isdigit(cc2)? (cc2 - '0'): (ascii_toupper(cc2)-'A'+10);
afx->buffer_pos += 2;
afx->qp_detected = 1;
goto again;
}
}
/* Occasionally a bug MTA will leave the = escaped as
=3D. If the 4 characters following that are valid
Radix64 characters and they are following by a new
line, assume that this is the case and skip the
3D. */
if (afx->buffer_pos + 6 < afx->buffer_len
&& afx->buffer[afx->buffer_pos + 0] == '3'
&& afx->buffer[afx->buffer_pos + 1] == 'D'
&& asctobin[0][afx->buffer[afx->buffer_pos + 2]] != 0xffffffffUL
&& asctobin[0][afx->buffer[afx->buffer_pos + 3]] != 0xffffffffUL
&& asctobin[0][afx->buffer[afx->buffer_pos + 4]] != 0xffffffffUL
&& asctobin[0][afx->buffer[afx->buffer_pos + 5]] != 0xffffffffUL
&& afx->buffer[afx->buffer_pos + 6] == '\n')
{
afx->buffer_pos += 2;
afx->qp_detected = 1;
}
if (!n)
onlypad = 1;
if( idx == 1 )
buf[n++] = val;
checkcrc++;
break;
}
else if (c == '-'
&& afx->buffer_pos + 8 < afx->buffer_len
&& !strncmp (afx->buffer, "-----END ", 8)) {
break; /* End in --dearmor mode or No CRC. */
}
else {
log_error(_("invalid radix64 character %02X skipped\n"), c);
continue;
}
}
afx->idx = idx;
afx->radbuf[0] = val;
if( n )
gcry_md_write (afx->crc_md, buf, n);
if( checkcrc ) {
gcry_md_final (afx->crc_md);
afx->any_data = 1;
afx->inp_checked=0;
afx->faked = 0;
for(;;) { /* skip lf and pad characters */
if( afx->buffer_pos < afx->buffer_len )
c = afx->buffer[afx->buffer_pos++];
else { /* read the next line */
unsigned maxlen = MAX_LINELEN;
afx->buffer_pos = 0;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
if( !maxlen )
afx->truncated++;
if( !afx->buffer_len )
break; /* eof */
continue;
}
if( c == '\n' || c == ' ' || c == '\r'
|| c == '\t' || c == '=' )
continue;
break;
}
if( !afx->buffer_len )
log_error(_("premature eof (no CRC)\n"));
else {
u32 mycrc = 0;
idx = 0;
do {
if( (binc = asctobin[0][c]) == 0xffffffffUL )
break;
switch(idx) {
case 0: val = binc << 2; break;
case 1: val |= (binc>>4)&3; mycrc |= val << 16;val=(binc<<4)&0xf0;break;
case 2: val |= (binc>>2)&15; mycrc |= val << 8;val=(binc<<6)&0xc0;break;
case 3: val |= binc&0x3f; mycrc |= val; break;
}
for(;;) {
if( afx->buffer_pos < afx->buffer_len )
c = afx->buffer[afx->buffer_pos++];
else { /* read the next line */
unsigned maxlen = MAX_LINELEN;
afx->buffer_pos = 0;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size,
&maxlen );
if( !maxlen )
afx->truncated++;
if( !afx->buffer_len )
break; /* eof */
continue;
}
break;
}
if( !afx->buffer_len )
break; /* eof */
} while( ++idx < 4 );
if( !afx->buffer_len ) {
log_info(_("premature eof (in CRC)\n"));
rc = invalid_crc();
}
else if( idx == 0 ) {
/* No CRC at all is legal ("MAY") */
rc=0;
}
else if( idx != 4 ) {
log_info(_("malformed CRC\n"));
rc = invalid_crc();
}
else if( mycrc != get_afx_crc (afx) ) {
log_info (_("CRC error; %06lX - %06lX\n"),
(ulong)get_afx_crc (afx), (ulong)mycrc);
rc = invalid_crc();
}
else {
rc = 0;
/* FIXME: Here we should emit another control packet,
* so that we know in mainproc that we are processing
* a clearsign message */
#if 0
for(rc=0;!rc;) {
rc = 0 /*check_trailer( &fhdr, c )*/;
if( !rc ) {
if( (c=iobuf_get(a)) == -1 )
rc = 2;
}
}
if( rc == -1 )
rc = 0;
else if( rc == 2 ) {
log_error(_("premature eof (in trailer)\n"));
rc = GPG_ERR_INV_ARMOR;
}
else {
log_error(_("error in trailer line\n"));
rc = GPG_ERR_INV_ARMOR;
}
#endif
}
}
}
if( !n && !onlypad )
rc = -1;
*retn = n;
return rc;
}
static void
armor_output_buf_as_radix64 (armor_filter_context_t *afx, IOBUF a,
byte *buf, size_t size)
{
byte radbuf[sizeof (afx->radbuf)];
byte outbuf[64 + sizeof (afx->eol)];
unsigned int eollen = strlen (afx->eol);
u32 in, in2;
int idx, idx2;
int i;
idx = afx->idx;
idx2 = afx->idx2;
memcpy (radbuf, afx->radbuf, sizeof (afx->radbuf));
if (size && (idx || idx2))
{
/* preload eol to outbuf buffer */
memcpy (outbuf + 4, afx->eol, sizeof (afx->eol));
for (; size && (idx || idx2); buf++, size--)
{
radbuf[idx++] = *buf;
if (idx > 2)
{
idx = 0;
in = (u32)radbuf[0] << (2 * 8);
in |= (u32)radbuf[1] << (1 * 8);
in |= (u32)radbuf[2] << (0 * 8);
outbuf[0] = bintoasc[(in >> 18) & 077];
outbuf[1] = bintoasc[(in >> 12) & 077];
outbuf[2] = bintoasc[(in >> 6) & 077];
outbuf[3] = bintoasc[(in >> 0) & 077];
if (++idx2 >= (64/4))
{ /* pgp doesn't like 72 here */
idx2=0;
iobuf_write (a, outbuf, 4 + eollen);
}
else
{
iobuf_write (a, outbuf, 4);
}
}
}
}
if (size >= (64/4)*3)
{
/* preload eol to outbuf buffer */
memcpy (outbuf + 64, afx->eol, sizeof(afx->eol));
do
{
/* idx and idx2 == 0 */
for (i = 0; i < (64/8); i++)
{
in = (u32)buf[0] << (2 * 8);
in |= (u32)buf[1] << (1 * 8);
in |= (u32)buf[2] << (0 * 8);
in2 = (u32)buf[3] << (2 * 8);
in2 |= (u32)buf[4] << (1 * 8);
in2 |= (u32)buf[5] << (0 * 8);
outbuf[i*8+0] = bintoasc[(in >> 18) & 077];
outbuf[i*8+1] = bintoasc[(in >> 12) & 077];
outbuf[i*8+2] = bintoasc[(in >> 6) & 077];
outbuf[i*8+3] = bintoasc[(in >> 0) & 077];
outbuf[i*8+4] = bintoasc[(in2 >> 18) & 077];
outbuf[i*8+5] = bintoasc[(in2 >> 12) & 077];
outbuf[i*8+6] = bintoasc[(in2 >> 6) & 077];
outbuf[i*8+7] = bintoasc[(in2 >> 0) & 077];
buf+=6;
size-=6;
}
/* pgp doesn't like 72 here */
iobuf_write (a, outbuf, 64 + eollen);
}
while (size >= (64/4)*3);
/* restore eol for tail handling */
if (size)
memcpy (outbuf + 4, afx->eol, sizeof (afx->eol));
}
for (; size; buf++, size--)
{
radbuf[idx++] = *buf;
if (idx > 2)
{
idx = 0;
in = (u32)radbuf[0] << (2 * 8);
in |= (u32)radbuf[1] << (1 * 8);
in |= (u32)radbuf[2] << (0 * 8);
outbuf[0] = bintoasc[(in >> 18) & 077];
outbuf[1] = bintoasc[(in >> 12) & 077];
outbuf[2] = bintoasc[(in >> 6) & 077];
outbuf[3] = bintoasc[(in >> 0) & 077];
if (++idx2 >= (64/4))
{ /* pgp doesn't like 72 here */
idx2=0;
iobuf_write (a, outbuf, 4 + eollen);
}
else
{
iobuf_write (a, outbuf, 4);
}
}
}
memcpy (afx->radbuf, radbuf, sizeof (afx->radbuf));
afx->idx = idx;
afx->idx2 = idx2;
}
/****************
* This filter is used to handle the armor stuff
*/
static int
armor_filter( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
armor_filter_context_t *afx = opaque;
int rc=0, c;
byte radbuf[3];
int idx, idx2;
size_t n=0;
u32 crc;
#if 0
static FILE *fp ;
if( !fp ) {
fp = fopen("armor.out", "w");
log_assert(fp);
}
#endif
if( DBG_FILTER )
log_debug("armor-filter: control: %d\n", control );
if( control == IOBUFCTRL_UNDERFLOW && afx->inp_bypass ) {
n = 0;
if( afx->buffer_len ) {
/* Copy the data from AFX->BUFFER to BUF. */
for(; n < size && afx->buffer_pos < afx->buffer_len; n++ )
buf[n++] = afx->buffer[afx->buffer_pos++];
if( afx->buffer_pos >= afx->buffer_len )
afx->buffer_len = 0;
}
/* If there is still space in BUF, read directly into it. */
for(; n < size; n++ ) {
if( (c=iobuf_get(a)) == -1 )
break;
buf[n] = c & 0xff;
}
if( !n )
/* We didn't get any data. EOF. */
rc = -1;
*ret_len = n;
}
else if( control == IOBUFCTRL_UNDERFLOW ) {
/* We need some space for the faked packet. The minimum
* required size is the PARTIAL_CHUNK size plus a byte for the
* length itself */
if( size < PARTIAL_CHUNK+1 )
BUG(); /* supplied buffer too short */
if( afx->faked )
rc = fake_packet( afx, a, &n, buf, size );
else if( !afx->inp_checked ) {
rc = check_input( afx, a );
if( afx->inp_bypass ) {
for(n=0; n < size && afx->buffer_pos < afx->buffer_len; )
buf[n++] = afx->buffer[afx->buffer_pos++];
if( afx->buffer_pos >= afx->buffer_len )
afx->buffer_len = 0;
if( !n )
rc = -1;
}
else if( afx->faked ) {
unsigned int hashes = afx->hashes;
const byte *sesmark;
size_t sesmarklen;
sesmark = get_session_marker( &sesmarklen );
if ( sesmarklen > 20 )
BUG();
/* the buffer is at least 15+n*15 bytes long, so it
* is easy to construct the packets */
hashes &= 1|2|8|16|32|64;
if( !hashes ) {
hashes |= 2; /* Default to SHA-1. */
}
n=0;
/* First a gpg control packet... */
buf[n++] = 0xff; /* new format, type 63, 1 length byte */
n++; /* see below */
memcpy(buf+n, sesmark, sesmarklen ); n+= sesmarklen;
buf[n++] = CTRLPKT_CLEARSIGN_START;
buf[n++] = afx->not_dash_escaped? 0:1; /* sigclass */
if( hashes & 1 )
buf[n++] = DIGEST_ALGO_RMD160;
if( hashes & 2 )
buf[n++] = DIGEST_ALGO_SHA1;
if( hashes & 8 )
buf[n++] = DIGEST_ALGO_SHA224;
if( hashes & 16 )
buf[n++] = DIGEST_ALGO_SHA256;
if( hashes & 32 )
buf[n++] = DIGEST_ALGO_SHA384;
if( hashes & 64 )
buf[n++] = DIGEST_ALGO_SHA512;
buf[1] = n - 2;
/* ...followed by an invented plaintext packet.
Amusingly enough, this packet is not compliant with
2440 as the initial partial length is less than 512
bytes. Of course, we'll accept it anyway ;) */
buf[n++] = 0xCB; /* new packet format, type 11 */
buf[n++] = 0xE1; /* 2^1 == 2 bytes */
buf[n++] = 't'; /* canonical text mode */
buf[n++] = 0; /* namelength */
buf[n++] = 0xE2; /* 2^2 == 4 more bytes */
memset(buf+n, 0, 4); /* timestamp */
n += 4;
}
else if( !rc )
rc = radix64_read( afx, a, &n, buf, size );
}
else
rc = radix64_read( afx, a, &n, buf, size );
#if 0
if( n )
if( fwrite(buf, n, 1, fp ) != 1 )
BUG();
#endif
*ret_len = n;
}
else if( control == IOBUFCTRL_FLUSH && !afx->cancel ) {
if( !afx->status ) { /* write the header line */
const char *s;
strlist_t comment=opt.comments;
if( afx->what >= DIM(head_strings) )
log_bug("afx->what=%d", afx->what);
iobuf_writestr(a, "-----");
iobuf_writestr(a, head_strings[afx->what] );
iobuf_writestr(a, "-----" );
iobuf_writestr(a,afx->eol);
if (opt.emit_version)
{
iobuf_writestr (a, "Version: "GNUPG_NAME" v");
for (s=VERSION; *s && *s != '.'; s++)
iobuf_writebyte (a, *s);
if (opt.emit_version > 1 && *s)
{
iobuf_writebyte (a, *s++);
for (; *s && *s != '.'; s++)
iobuf_writebyte (a, *s);
if (opt.emit_version > 2)
{
for (; *s && *s != '-' && !spacep (s); s++)
iobuf_writebyte (a, *s);
if (opt.emit_version > 3)
iobuf_writestr (a, " (" PRINTABLE_OS_NAME ")");
}
}
iobuf_writestr(a,afx->eol);
}
/* write the comment strings */
for(;comment;comment=comment->next)
{
iobuf_writestr(a, "Comment: " );
for( s=comment->d; *s; s++ )
{
if( *s == '\n' )
iobuf_writestr(a, "\\n" );
else if( *s == '\r' )
iobuf_writestr(a, "\\r" );
else if( *s == '\v' )
iobuf_writestr(a, "\\v" );
else
iobuf_put(a, *s );
}
iobuf_writestr(a,afx->eol);
}
if ( afx->hdrlines ) {
for ( s = afx->hdrlines; *s; s++ ) {
#ifdef HAVE_DOSISH_SYSTEM
if ( *s == '\n' )
iobuf_put( a, '\r');
#endif
iobuf_put(a, *s );
}
}
iobuf_writestr(a,afx->eol);
afx->status++;
afx->idx = 0;
afx->idx2 = 0;
gcry_md_reset (afx->crc_md);
}
if( size ) {
gcry_md_write (afx->crc_md, buf, size);
armor_output_buf_as_radix64 (afx, a, buf, size);
}
}
else if( control == IOBUFCTRL_INIT )
{
if( !is_initialized )
initialize();
/* Figure out what we're using for line endings if the caller
didn't specify. */
if(afx->eol[0]==0)
{
#ifdef HAVE_DOSISH_SYSTEM
afx->eol[0]='\r';
afx->eol[1]='\n';
#else
afx->eol[0]='\n';
#endif
}
}
else if( control == IOBUFCTRL_CANCEL ) {
afx->cancel = 1;
}
else if( control == IOBUFCTRL_FREE ) {
if( afx->cancel )
;
else if( afx->status ) { /* pad, write checksum, and bottom line */
gcry_md_final (afx->crc_md);
crc = get_afx_crc (afx);
idx = afx->idx;
idx2 = afx->idx2;
if( idx ) {
c = bintoasc[(afx->radbuf[0]>>2)&077];
iobuf_put(a, c);
if( idx == 1 ) {
c = bintoasc[((afx->radbuf[0] << 4) & 060) & 077];
iobuf_put(a, c);
iobuf_put(a, '=');
iobuf_put(a, '=');
}
else { /* 2 */
c = bintoasc[(((afx->radbuf[0]<<4)&060)
|((afx->radbuf[1]>>4)&017))&077];
iobuf_put(a, c);
c = bintoasc[((afx->radbuf[1] << 2) & 074) & 077];
iobuf_put(a, c);
iobuf_put(a, '=');
}
if( ++idx2 >= (64/4) )
{ /* pgp doesn't like 72 here */
iobuf_writestr(a,afx->eol);
idx2=0;
}
}
/* may need a linefeed */
if( idx2 )
iobuf_writestr(a,afx->eol);
/* write the CRC */
iobuf_put(a, '=');
radbuf[0] = crc >>16;
radbuf[1] = crc >> 8;
radbuf[2] = crc;
c = bintoasc[(*radbuf >> 2) & 077];
iobuf_put(a, c);
c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
iobuf_put(a, c);
c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
iobuf_put(a, c);
c = bintoasc[radbuf[2]&077];
iobuf_put(a, c);
iobuf_writestr(a,afx->eol);
/* and the trailer */
if( afx->what >= DIM(tail_strings) )
log_bug("afx->what=%d", afx->what);
iobuf_writestr(a, "-----");
iobuf_writestr(a, tail_strings[afx->what] );
iobuf_writestr(a, "-----" );
iobuf_writestr(a,afx->eol);
}
else if( !afx->any_data && !afx->inp_bypass ) {
log_error(_("no valid OpenPGP data found.\n"));
afx->no_openpgp_data = 1;
write_status_text( STATUS_NODATA, "1" );
}
/* Note that in a cleartext signature truncated lines in the
* plaintext are detected and propagated to the signature
* checking code by inserting a \f into the plaintext. We do
* not use log_info here because some of the truncated lines
* are harmless. */
if( afx->truncated )
log_info(_("invalid armor: line longer than %d characters\n"),
MAX_LINELEN );
/* issue an error to enforce dissemination of correct software */
if( afx->qp_detected )
log_error(_("quoted printable character in armor - "
"probably a buggy MTA has been used\n") );
xfree( afx->buffer );
afx->buffer = NULL;
release_armor_context (afx);
}
else if( control == IOBUFCTRL_DESC )
mem2str (buf, "armor_filter", *ret_len);
return rc;
}
/****************
* create a radix64 encoded string.
*/
char *
make_radix64_string( const byte *data, size_t len )
{
char *buffer, *p;
buffer = p = xmalloc( (len+2)/3*4 + 1 );
for( ; len >= 3 ; len -= 3, data += 3 ) {
*p++ = bintoasc[(data[0] >> 2) & 077];
*p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
*p++ = bintoasc[(((data[1]<<2)&074)|((data[2]>>6)&03))&077];
*p++ = bintoasc[data[2]&077];
}
if( len == 2 ) {
*p++ = bintoasc[(data[0] >> 2) & 077];
*p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
*p++ = bintoasc[((data[1]<<2)&074)];
}
else if( len == 1 ) {
*p++ = bintoasc[(data[0] >> 2) & 077];
*p++ = bintoasc[(data[0] <<4)&060];
}
*p = 0;
return buffer;
}
diff --git a/g10/decrypt.c b/g10/decrypt.c
index b30359af4..e232c188f 100644
--- a/g10/decrypt.c
+++ b/g10/decrypt.c
@@ -1,290 +1,328 @@
/* decrypt.c - decrypt and verify data
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007, 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2024 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 "../common/status.h"
#include "../common/i18n.h"
+
+
/* Assume that the input is an encrypted message and decrypt
* (and if signed, verify the signature on) it.
* This command differs from the default operation, as it never
* writes to the filename which is included in the file and it
* rejects files which don't begin with an encrypted message.
+ *
+ * REMUSR is only used in the modify_recipients mode and speicifies
+ * the additional or new recipients to use.
*/
-int
-decrypt_message (ctrl_t ctrl, const char *filename)
+gpg_error_t
+decrypt_message (ctrl_t ctrl, const char *filename, strlist_t remusr)
{
- IOBUF fp;
+ gpg_error_t err;
+ iobuf_t fp;
armor_filter_context_t *afx = NULL;
progress_filter_context_t *pfx;
- int rc;
+ DEK *dek = NULL;
+ struct pubkey_enc_list *pkenc_list = NULL;
pfx = new_progress_context ();
/* Open the message file. */
fp = iobuf_open (filename);
if (fp && is_secured_file (iobuf_get_fd (fp)))
{
iobuf_close (fp);
fp = NULL;
gpg_err_set_errno (EPERM);
}
if ( !fp )
{
- rc = gpg_error_from_syserror ();
+ err = gpg_error_from_syserror ();
log_error (_("can't open '%s': %s\n"), print_fname_stdin(filename),
- gpg_strerror (rc));
+ gpg_strerror (err));
release_progress_context (pfx);
- return rc;
+ return err;
}
- handle_progress (pfx, fp, filename);
+ /* Push the progress filter unless we are in add recipient mode.
+ * The latter may also work but for now we better avoid any possible
+ * complications. */
+ if (!ctrl->modify_recipients)
+ handle_progress (pfx, fp, filename);
if ( !opt.no_armor )
{
if ( use_armor_filter( fp ) )
{
afx = new_armor_context ();
push_armor_filter ( afx, fp );
}
}
if (!opt.outfile)
{
opt.outfile = "-";
opt.flags.dummy_outfile = 1;
}
else
opt.flags.dummy_outfile = 0;
- rc = proc_encryption_packets (ctrl, NULL, fp );
+ if (!ctrl->modify_recipients)
+ err = proc_encryption_packets (ctrl, NULL, fp, NULL, NULL);
+ else
+ err = proc_encryption_packets (ctrl, NULL, fp, &dek, &pkenc_list);
if (opt.flags.dummy_outfile)
opt.outfile = NULL;
+ if (ctrl->modify_recipients && (err || !dek) )
+ log_error (_("modifiying the recipients is not possible: %s\n"),
+ err? gpg_strerror (err) : _("decryption failed"));
+ else if (ctrl->modify_recipients)
+ {
+ /* We apply an armor to the output if --armor was used or if the
+ * input was already armored and --no-armor was not given. */
+ int armor = opt.armor || (was_armored (afx) && !opt.no_armor);
+
+ err = reencrypt_to_new_recipients (ctrl, armor, filename, fp,
+ remusr, dek, pkenc_list);
+ }
+
+ xfree (dek);
+ while (pkenc_list)
+ {
+ struct pubkey_enc_list *tmp = pkenc_list->next;
+
+ release_pubkey_enc_parts (&pkenc_list->d);
+ xfree (pkenc_list);
+ pkenc_list = tmp;
+ }
iobuf_close (fp);
release_armor_context (afx);
release_progress_context (pfx);
- return rc;
+ return err;
}
/* Same as decrypt_message but takes a file descriptor for input and
- output. */
+ output. Only used by the unfinished server mode. */
gpg_error_t
decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_fd,
gnupg_fd_t output_fd)
{
#ifdef HAVE_W32_SYSTEM
/* No server mode yet. */
(void)ctrl;
(void)input_fd;
(void)output_fd;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#else
gpg_error_t err;
IOBUF fp;
armor_filter_context_t *afx = NULL;
progress_filter_context_t *pfx;
if (opt.outfp)
return gpg_error (GPG_ERR_BUG);
pfx = new_progress_context ();
/* Open the message file. */
fp = iobuf_fdopen_nc (input_fd, "rb");
if (fp && is_secured_file (iobuf_get_fd (fp)))
{
iobuf_close (fp);
fp = NULL;
gpg_err_set_errno (EPERM);
}
if (!fp)
{
char xname[64];
err = gpg_error_from_syserror ();
snprintf (xname, sizeof xname, "[fd %d]", input_fd);
log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (err));
release_progress_context (pfx);
return err;
}
if (is_secured_file (output_fd))
{
char xname[64];
err = gpg_error (GPG_ERR_EPERM);
snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (output_fd));
log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (err));
iobuf_close (fp);
release_progress_context (pfx);
return err;
}
opt.outfp = open_stream_nc (output_fd, "w");
if (!opt.outfp)
{
char xname[64];
err = gpg_error_from_syserror ();
snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (output_fd));
log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (err));
iobuf_close (fp);
release_progress_context (pfx);
return err;
}
if (!opt.no_armor)
{
if (use_armor_filter (fp))
{
afx = new_armor_context ();
push_armor_filter ( afx, fp );
}
}
- err = proc_encryption_packets (ctrl, NULL, fp );
+ err = proc_encryption_packets (ctrl, NULL, fp, NULL, NULL);
iobuf_close (fp);
es_fclose (opt.outfp);
opt.outfp = NULL;
release_armor_context (afx);
release_progress_context (pfx);
return err;
#endif
}
void
decrypt_messages (ctrl_t ctrl, int nfiles, char *files[])
{
IOBUF fp;
progress_filter_context_t *pfx;
char *p, *output = NULL;
int rc=0,use_stdin=0;
unsigned int lno=0;
if (opt.outfile)
{
log_error(_("--output doesn't work for this command\n"));
return;
}
pfx = new_progress_context ();
if(!nfiles)
use_stdin=1;
for(;;)
{
char line[2048];
char *filename=NULL;
if(use_stdin)
{
if(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);
else
{
line[strlen(line)-1] = '\0';
filename=line;
}
}
}
else
{
if(nfiles)
{
filename=*files;
nfiles--;
files++;
}
}
if(filename==NULL)
break;
print_file_status(STATUS_FILE_START, filename, 3);
output = make_outfile_name(filename);
if (!output)
goto next_file;
fp = iobuf_open(filename);
if (fp)
iobuf_ioctl (fp, IOBUF_IOCTL_NO_CACHE, 1, NULL);
if (fp && is_secured_file (iobuf_get_fd (fp)))
{
iobuf_close (fp);
fp = NULL;
gpg_err_set_errno (EPERM);
}
if (!fp)
{
log_error(_("can't open '%s'\n"), print_fname_stdin(filename));
goto next_file;
}
handle_progress (pfx, fp, filename);
if (!opt.no_armor)
{
if (use_armor_filter(fp))
{
armor_filter_context_t *afx = new_armor_context ();
rc = push_armor_filter (afx, fp);
if (rc)
log_error("failed to push armor filter");
release_armor_context (afx);
}
}
rc = proc_packets (ctrl,NULL, fp);
iobuf_close(fp);
if (rc)
log_error("%s: decryption failed: %s\n", print_fname_stdin(filename),
gpg_strerror (rc));
p = get_last_passphrase();
set_next_passphrase(p);
xfree (p);
next_file:
/* Note that we emit file_done even after an error. */
write_status( STATUS_FILE_DONE );
xfree(output);
reset_literals_seen();
}
set_next_passphrase(NULL);
release_progress_context (pfx);
}
diff --git a/g10/encrypt.c b/g10/encrypt.c
index cc8f37fe2..e4e56c8b1 100644
--- a/g10/encrypt.c
+++ b/g10/encrypt.c
@@ -1,1249 +1,1343 @@
/* 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);
+
+/* Helper for show the "encrypted for USER" during encryption.
+ * PUBKEY_USAGE is used to figure out whether this is an ADSK key. */
+static void
+show_encrypted_for_user_info (ctrl_t ctrl, unsigned int pubkey_usage,
+ PKT_pubkey_enc *enc, DEK *dek)
+{
+ char *ustr = get_user_id_string_native (ctrl, enc->keyid);
+ if ((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);
+}
+
+
/****************
* 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 appropriate 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
* identify 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. */
+/* This function handles the --symmetric only (MODE true) and --store
+ * (MODE false) cases. We don't want to use USE_SESKEY by default
+ * very old 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();
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;
}
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 (rc || (rc = open_outfile (GNUPG_INVALID_FD, 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 )
{
uint64_t tmpsize;
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 */
/* Register the cipher filter. */
if (mode)
iobuf_push_filter (out,
cfx.dek->use_aead? cipher_filter_aead
/**/ : cipher_filter_cfb,
&cfx );
if (do_compress
&& cfx.dek
&& (cfx.dek->use_mdc || cfx.dek->use_aead)
&& !opt.explicit_compress_option
&& is_file_compressed (inp))
{
if (opt.verbose)
log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]");
do_compress = 0;
}
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 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. */
iobuf_copy (out, inp);
if ((rc = iobuf_error (inp)))
log_error (_("error reading '%s': %s\n"),
iobuf_get_fname_nonnull (inp), gpg_strerror (rc));
else if ((rc = iobuf_error (out)))
log_error (_("error writing '%s': %s\n"),
iobuf_get_fname_nonnull (out), 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_KEYS; if not the function builds a list of keys on its own.
*
* Note that FILEFD and OUTPUTFD are currently only used by
* cmd_encrypt in the not yet finished server.c.
*/
int
encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename,
strlist_t remusr, int use_symkey, pk_list_t provided_keys,
gnupg_fd_t 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;
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;
if (filefd != GNUPG_INVALID_FD && 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 == GNUPG_INVALID_FD)
inp = iobuf_open (filename);
else
{
inp = NULL;
gpg_err_set_errno (ENOSYS);
}
#else
if (filefd == GNUPG_INVALID_FD)
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 != GNUPG_INVALID_FD)
snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (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));
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);
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 )
{
uint64_t tmpsize;
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 */
/* Register the cipher filter. */
iobuf_push_filter (out,
cfx.dek->use_aead? cipher_filter_aead
/**/ : cipher_filter_cfb,
&cfx);
/* 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 (inp))
{
if (opt.verbose)
log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]");
do_compress = 0;
}
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 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. */
iobuf_copy (out, inp);
if ((rc = iobuf_error (inp)))
log_error (_("error reading '%s': %s\n"),
iobuf_get_fname_nonnull (inp), gpg_strerror (rc));
else if ((rc = iobuf_error (out)))
log_error (_("error writing '%s': %s\n"),
iobuf_get_fname_nonnull (out), gpg_strerror (rc));
}
/* 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;
}
+/* Re-encrypt files with a set of new recipients. Note that this
+ * function is called by decrypt_message. INFP is the iobuf from the
+ * input file which is positioned right after the pubkey_enc and
+ * symkey_enc packets. */
+gpg_error_t
+reencrypt_to_new_recipients (ctrl_t ctrl, int armor, const char *filename,
+ iobuf_t infp, strlist_t recipients,
+ DEK *dek, struct pubkey_enc_list *pkenc_list)
+{
+ gpg_error_t err;
+ int save_no_encrypt_to;
+ pk_list_t newpk_list = NULL;
+ iobuf_t outfp = NULL;
+ armor_filter_context_t *outafx = NULL;
+ PACKET pkt;
+ struct pubkey_enc_list *el;
+ unsigned int count;
+
+ /* Get the keys for all additional recipients but do not encrypt to
+ * the encrypt-to keys. */
+ save_no_encrypt_to = opt.no_encrypt_to;
+ opt.no_encrypt_to = 1;
+ err = build_pk_list (ctrl, recipients, &newpk_list);
+ opt.no_encrypt_to = save_no_encrypt_to;
+ if (err)
+ goto leave;
+
+ /* Note that we use by default the suffixes .gpg or .asc */
+ err = open_outfile (GNUPG_INVALID_FD, filename, armor? 1:0, 0, &outfp);
+ if (err)
+ goto leave;
+
+ if (armor)
+ {
+ outafx = new_armor_context ();
+ push_armor_filter (outafx, outfp);
+ }
+
+ /* Write the new recipients first. */
+ err = write_pubkey_enc_from_list (ctrl, newpk_list, dek, outfp);
+ if (err)
+ goto leave;
+
+ /* The write the old recipients in --add-recipients mode. */
+ for (count=0, el = pkenc_list; el; el = el->next, count++)
+ if (!ctrl->clear_recipients)
+ {
+ if (opt.verbose)
+ show_encrypted_for_user_info (ctrl, 0, &el->d, dek);
+ init_packet (&pkt);
+ pkt.pkttype = PKT_PUBKEY_ENC;
+ pkt.pkt.pubkey_enc = &el->d;
+ err = build_packet (outfp, &pkt);
+ if (err)
+ log_error ("build_packet(pubkey_enc) failed: %s\n",
+ gpg_strerror (err));
+ }
+ if (ctrl->clear_recipients && opt.verbose)
+ log_info (_("number of removed recipients: %u\n"), count);
+
+ iobuf_put (outfp, ctrl->last_read_ctb);
+
+ /* Finally copy the bulk of the message. */
+ iobuf_copy (outfp, infp);
+ if ((err = iobuf_error (infp)))
+ log_error (_("error reading '%s': %s\n"),
+ iobuf_get_fname_nonnull (infp), gpg_strerror (err));
+ else if ((err = iobuf_error (outfp)))
+ log_error (_("error writing '%s': %s\n"),
+ iobuf_get_fname_nonnull (outfp), gpg_strerror (err));
+
+
+ leave:
+ if (err)
+ iobuf_cancel (outfp);
+ else
+ iobuf_close (outfp);
+ release_armor_context (outafx);
+ release_pk_list (newpk_list);
+ return err;
+}
+
+
+
/*
* 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;
enc->seskey_algo = dek->algo; /* (Used only by PUBKEY_ALGO_KYBER.) */
/* 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 integer 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, frame, dek->algo, enc->data);
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);
- }
+ show_encrypted_for_user_info (ctrl, pk->pubkey_usage, enc, dek);
/* 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, GNUPG_INVALID_FD, line, remusr,
0, NULL, GNUPG_INVALID_FD);
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, GNUPG_INVALID_FD, *files, remusr,
0, NULL, GNUPG_INVALID_FD)))
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 321b553dc..b15ce6aa5 100644
--- a/g10/filter.h
+++ b/g10/filter.h
@@ -1,206 +1,207 @@
/* 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 md_thd_filter_context *md_thd_filter_context_t;
void md_thd_filter_set_md (md_thd_filter_context_t mfx, gcry_md_hd_t md);
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 */
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);
int md_thd_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 was_armored (armor_filter_context_t *afx);
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 e61b6879a..6068eefd3 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -1,5941 +1,5975 @@
/* 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,
+ aAddRecipients,
+ aChangeRecipients,
aDecryptFiles,
aClearsign,
aStore,
aQuickKeygen,
aFullKeygen,
aKeygen,
aSignEncr,
aSignEncrSym,
aSignSym,
aSignKey,
aLSignKey,
aQuickSignKey,
aQuickLSignKey,
aQuickRevSig,
aQuickAddUid,
aQuickAddKey,
aQuickAddADSK,
aQuickRevUid,
aQuickSetExpire,
aQuickSetPrimaryUid,
aQuickUpdatePref,
aQuickSetOwnertrust,
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,
oWithV5Fingerprint,
oWithFingerprint,
oWithSubkeyFingerprint,
oWithoutSubkeyFingerprint,
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,
oNoCompress,
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,
oDisableFdTranslation,
oNoLiteral,
oSetFilesize,
oHonorHttpProxy,
oFastListMode,
oListOnly,
oIgnoreTimeConflict,
oIgnoreValidFrom,
oIgnoreCrcError,
oIgnoreMDCError,
oShowSessionKey,
oShowOnlySessionKey,
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,
oDefaultNewKeyADSK,
oWeakDigest,
oUnwrap,
oOnlySignTextIDs,
oDisableSignerUID,
oSender,
oKeyOrigin,
oRequestOrigin,
oNoSymkeyCache,
oUseOnlyOpenPGPCard,
oFullTimestrings,
oIncludeKeyBlock,
oNoIncludeKeyBlock,
oChUid,
oForceSignKey,
oForbidGenKey,
oRequireCompliance,
oCompatibilityFlags,
oAddDesigRevoker,
oAssertSigner,
oAssertPubkeyAlgo,
oKbxBufferSize,
oRequirePQCEncryption,
oProcAllSigs,
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 (aAddRecipients, "add-recipients", "@" ),
+ ARGPARSE_c (aChangeRecipients, "change-recipients", "@" ),
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 (aQuickSetOwnertrust, "quick-set-ownertrust", "@"),
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_s_s (oDefaultNewKeyADSK, "default-new-key-adsk", "@"),
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_n (oNoCompress, "no-compress", "@"),
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_s_s (oAssertPubkeyAlgo,"assert-pubkey-algo", "@"),
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", "@"),
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 (oShowOnlySessionKey, "show-only-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 (oWithV5Fingerprint, "with-v5-fingerprint", "@"),
ARGPARSE_s_n (oWithFingerprint, "with-fingerprint", "@"),
ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprint", "@"),
ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprints", "@"),
ARGPARSE_s_n (oWithoutSubkeyFingerprint, "without-subkey-fingerprint", "@"),
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 (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_s_n (oRequirePQCEncryption, "require-pqc-encryption", "@"),
ARGPARSE_header (NULL, N_("Options for unattended use")),
ARGPARSE_s_n (oBatch, "batch", "@"),
ARGPARSE_s_n (oNoBatch, "no-batch", "@"),
ARGPARSE_s_n (oProcAllSigs, "proc-all-sigs", "@"),
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_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"),
ARGPARSE_s_n (oDisableFdTranslation, "disable-fd-translation", "@"),
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" },
{ DBG_KEYDB_VALUE, "keydb" },
{ 0, NULL }
};
/* The list of compatibility flags. */
static struct compatibility_flags_s compatibility_flags [] =
{
{ COMPAT_PARALLELIZED, "parallelized" },
{ COMPAT_T7014_OLD, "t7014-old" },
{ 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 variable is not true
* gpg will be forced to return EXIT_FAILURE. */
int assert_signer_true = 0;
/* If opt.assert_pubkey_algo is used and this variable is not true
* gpg will be forced to return EXIT_FAILURE. */
int assert_pubkey_algo_false = 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);
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
* approval. */
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-x509-notations",LIST_SHOW_X509_NOTATIONS,NULL, NULL },
{"store-x509-notations",LIST_STORE_X509_NOTATIONS,NULL, NULL },
{"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-ownertrust", LIST_SHOW_OWNERTRUST, NULL,
N_("show ownertrust")},
{"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;
gpg_error_t tmperr;
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.with_subkey_fingerprint = 1;
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 initialized 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 configuration 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 aQuickSetOwnertrust:
case aExportOwnerTrust:
case aImportOwnerTrust:
case aRebuildKeydbCaches:
+ case aAddRecipients:
+ case aChangeRecipients:
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 oProcAllSigs:
opt.flags.proc_all_sigs = 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 oWithV5Fingerprint:
opt.with_v5_fingerprint = 1;
break;
case oWithFingerprint:
opt.with_fingerprint = 1;
opt.fingerprint++;
break;
case oWithSubkeyFingerprint:
opt.with_subkey_fingerprint = 1;
break;
case oWithoutSubkeyFingerprint:
opt.with_subkey_fingerprint = 0;
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 oRequirePQCEncryption:
opt.flags.require_pqc_encryption = 1;
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 oNoCompress:
/* --no-compress is the same as -z0 */
opt.compress_level = opt.bz2_compress_level = 0;
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:
#ifndef 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:
/* 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 oShowOnlySessionKey:
opt.show_only_session_key = 1;
/* fallthru */
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 oDisableFdTranslation:
disable_translate_sys2libc_fd ();
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:
xfree (opt.agent_program);
opt.agent_program = make_filename (pargs.r.ret_str, NULL);
break;
case oKeyboxdProgram:
xfree (opt.keyboxd_program);
opt.keyboxd_program = make_filename (pargs.r.ret_str, NULL);
break;
case oDirmngrProgram:
xfree (opt.dirmngr_program);
opt.dirmngr_program = make_filename (pargs.r.ret_str, NULL);
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 oDefaultNewKeyADSK:
if (!strcmp (pargs.r.ret_str, "clear"))
FREE_STRLIST (opt.def_new_key_adsks);
else if (!tokenize_to_strlist (&opt.def_new_key_adsks,
pargs.r.ret_str, " \t,")
&& (tmperr = gpg_err_code_from_syserror()) != GPG_ERR_ENOENT)
log_info (_("error parsing value for option '%s': %s\n"),
"--default-new-key-algo", gpg_strerror (tmperr));
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 oAssertPubkeyAlgo:
if (!opt.assert_pubkey_algos)
opt.assert_pubkey_algos = xstrdup (pargs.r.ret_str);
else
{
char *tmp = opt.assert_pubkey_algos;
opt.assert_pubkey_algos = xstrconcat (tmp, ",",
pargs.r.ret_str, NULL);
xfree (tmp);
}
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);
}
/* Set depended fingerprint options. */
if (opt.with_v5_fingerprint && !opt.with_fingerprint)
{
opt.with_fingerprint = 1;
if (!opt.fingerprint)
opt.fingerprint = 1;
}
/* 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 (opt.use_keyboxd && nrings)
log_info ("Note: Specified keyrings are ignored due to option \"%s\"\n",
"use-keyboxd");
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 ();
gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
assuan_control (ASSUAN_CONTROL_REINIT_SYSCALL_CLAMP, NULL);
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;
+ case aAddRecipients:
+ cmdname="--add-recipients";
+ break;
+ case aChangeRecipients:
+ cmdname="--change-recipients";
+ 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,
+ || cmd == aSignEncrSym
+ || cmd == aAddRecipients
+ || cmd == aChangeRecipients,
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)))
{
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 aQuickSetOwnertrust:
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, GNUPG_INVALID_FD, fname, remusr,
0, NULL, GNUPG_INVALID_FD)))
{
write_status_failure ("encrypt", rc);
log_error("%s: encryption failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
}
break;
+ case aChangeRecipients: /* Change recipients of the encrypted file. */
+ ctrl->clear_recipients = 1;
+ /* fallthru */
+ case aAddRecipients: /* Add recipients to the encrypted file. */
+ ctrl->modify_recipients = 1;
+ if (argc > 1)
+ {
+ if (cmd == aAddRecipients)
+ wrong_args("--add-recipients [filename]");
+ else
+ wrong_args("--change-recipients [filename]");
+ }
+ if ((rc = decrypt_message (ctrl, fname, remusr)))
+ {
+ write_status_failure ("modify-recipients", rc);
+ log_error ("%s: modify recipients 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, GNUPG_INVALID_FD, fname, remusr,
1, NULL, GNUPG_INVALID_FD)))
{
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) ))
+ if( (rc = decrypt_message (ctrl, fname, NULL) ))
{
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 aQuickSetOwnertrust:
{
if (argc != 2)
wrong_args ("--quick-set-ownertrust USER-ID"
" [enable|disable|full|...]");
keyedit_quick_set_ownertrust (ctrl, argv[0], argv[1]);
}
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 (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;
else if (opt.assert_pubkey_algos && assert_pubkey_algo_false)
rc = 1;
/* 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 ();
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
{
if (is_secured_filename (fname))
{
fp = NULL;
gpg_err_set_errno (EPERM);
}
else
fp = es_fopen (fname, "rb" );
}
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/gpg.h b/g10/gpg.h
index 167cdaf28..7646aa3ee 100644
--- a/g10/gpg.h
+++ b/g10/gpg.h
@@ -1,129 +1,138 @@
/* gpg.h - top level include file for gpg etc.
* Copyright (C) 2003, 2006, 2010 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 GNUPG_G10_GPG_H
#define GNUPG_G10_GPG_H
/* Note, that this file should be the first one after the system
header files. This is required to set the error source to the
correct value and may be of advantage if we ever have to do
special things. */
#ifdef HAVE_W32_SYSTEM
# define WIN32_LEAN_AND_MEAN 1
#endif
#ifdef GPG_ERR_SOURCE_DEFAULT
#error GPG_ERR_SOURCE_DEFAULT already defined
#endif
#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPG
#define map_assuan_err(a) \
map_assuan_err_with_source (GPG_ERR_SOURCE_DEFAULT, (a))
#include <gpg-error.h>
#include <gcrypt.h>
/* Number of bits we accept when reading or writing MPIs. */
#define MAX_EXTERN_MPI_BITS 16384
/* Number of bytes we accept when reading four-octet count prefixed
* key parameters. Needs to fit as a positive number into an int. */
#define MAX_EXTERN_KEYPARM_BITS (32768*8)
/* The maximum length of a binary fingerprints. This is used to
* provide a static buffer and will be increased if we need to support
* longer fingerprints. Warning: At some places we have some
* assumption on a 20 byte fingerprint.
* Watch out for FIXME(fingerprint) */
#define MAX_FINGERPRINT_LEN 32
/* The maximum length of a formatted fingerprint as returned by
* format_hexfingerprint(). */
#define MAX_FORMATTED_FINGERPRINT_LEN 60
/*
Forward declarations.
*/
/* Object used to keep state locally to server.c . */
struct server_local_s;
/* Object used to keep state locally to call-keyboxd.c . */
struct keyboxd_local_s;
typedef struct keyboxd_local_s *keyboxd_local_t;
/* Object used to keep state locally to call-dirmngr.c . */
struct dirmngr_local_s;
typedef struct dirmngr_local_s *dirmngr_local_t;
/* Object used to describe a keyblock node. */
typedef struct kbnode_struct *KBNODE; /* Deprecated use kbnode_t. */typedef struct kbnode_struct *kbnode_t;
/* The handle for keydb operations. */
typedef struct keydb_handle_s *KEYDB_HANDLE;
/* TOFU database meta object. */
struct tofu_dbs_s;
typedef struct tofu_dbs_s *tofu_dbs_t;
#if SIZEOF_UNSIGNED_LONG == 8
# define SERVER_CONTROL_MAGIC 0x53616c696e676572
#else
# define SERVER_CONTROL_MAGIC 0x53616c69
#endif
/* Session control object. This object is passed to most functions to
convey the status of a session. Note that the defaults are set by
gpg_init_default_ctrl(). */
struct server_control_s
{
/* Always has the value SERVER_CONTROL_MAGIC. */
unsigned long magic;
/* Local data for server.c */
struct server_local_s *server_local;
/* Local data for call-dirmngr.c */
dirmngr_local_t dirmngr_local;
/* Local data for call-keyboxd.c */
keyboxd_local_t keyboxd_local;
/* Local data for tofu.c */
struct {
tofu_dbs_t dbs;
int batch_updated_wanted;
} tofu;
/* This is used to cache a key data base handle. */
KEYDB_HANDLE cached_getkey_kdb;
/* Cached results from HAVEKEY --list. They are used if the pointer
* is not NULL. The length gives the length in bytes and is a
* multiple of 20. If the no_more flag is set the list shall not
* anymore be refreshed even if it has been freed and NULLed. */
unsigned char *secret_keygrips;
size_t secret_keygrips_len;
int no_more_secret_keygrips;
+
+ /* This first flag is set to true if we are running a
+ * --add-recipients or --change-recipients command. The second if
+ * it is --change-recipients. */
+ unsigned int modify_recipients:1;
+ unsigned int clear_recipients:1;
+
+ /* Temporary used to pass the last read byte up the call chain. */
+ int last_read_ctb;
};
#endif /*GNUPG_G10_GPG_H*/
diff --git a/g10/main.h b/g10/main.h
index 1c93c96e6..546a0b5b8 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -1,534 +1,540 @@
/* main.h
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
* 2008, 2009, 2010 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_MAIN_H
#define G10_MAIN_H
#include "../common/types.h"
#include "../common/iobuf.h"
#include "../common/util.h"
#include "keydb.h"
#include "keyedit.h"
/* It could be argued that the default cipher should be 3DES rather
than AES128, and the default compression should be 0
(i.e. uncompressed) rather than 1 (zip). However, the real world
issues of speed and size come into play here. */
#if GPG_USE_AES256
# define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES256
#elif GPG_USE_AES128
# define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES
#elif GPG_USE_CAST5
# define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5
#else
# define DEFAULT_CIPHER_ALGO CIPHER_ALGO_3DES
#endif
#define DEFAULT_DIGEST_ALGO ((GNUPG)? DIGEST_ALGO_SHA256:DIGEST_ALGO_SHA1)
#define DEFAULT_S2K_DIGEST_ALGO DEFAULT_DIGEST_ALGO
#ifdef HAVE_ZIP
# define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_ZIP
#else
# define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_NONE
#endif
#define S2K_DIGEST_ALGO (opt.s2k_digest_algo?opt.s2k_digest_algo:DEFAULT_S2K_DIGEST_ALGO)
/* Various data objects. */
typedef struct
{
ctrl_t ctrl;
int header_okay;
PK_LIST pk_list;
DEK *symkey_dek;
STRING2KEY *symkey_s2k;
cipher_filter_context_t cfx;
} encrypt_filter_context_t;
struct groupitem
{
char *name;
strlist_t values;
struct groupitem *next;
};
struct weakhash
{
enum gcry_md_algos algo;
int rejection_shown;
struct weakhash *next;
};
/*-- gpg.c --*/
extern int g10_errors_seen;
extern int assert_signer_true;
extern int assert_pubkey_algo_false;
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
void g10_exit(int rc) __attribute__ ((__noreturn__));
#else
void g10_exit(int rc);
#endif
void print_pubkey_algo_note (pubkey_algo_t algo);
void print_cipher_algo_note (cipher_algo_t algo);
void print_digest_algo_note (digest_algo_t algo);
void print_digest_rejected_note (enum gcry_md_algos algo);
void print_sha1_keysig_rejected_note (void);
void print_reported_error (gpg_error_t err, gpg_err_code_t skip_if_ec);
void print_further_info (const char *format, ...) GPGRT_ATTR_PRINTF(1,2);
void additional_weak_digest (const char* digestname);
int is_weak_digest (digest_algo_t algo);
/*-- armor.c --*/
char *make_radix64_string( const byte *data, size_t len );
/*-- misc.c --*/
void trap_unaligned(void);
void register_secured_file (const char *fname);
void unregister_secured_file (const char *fname);
int is_secured_file (gnupg_fd_t fd);
int is_secured_filename (const char *fname);
u16 checksum_u16( unsigned n );
u16 checksum( const byte *p, unsigned n );
u16 checksum_mpi( gcry_mpi_t a );
u32 buffer_to_u32( const byte *buffer );
const byte *get_session_marker( size_t *rlen );
enum gcry_cipher_algos map_cipher_openpgp_to_gcry (cipher_algo_t algo);
#define openpgp_cipher_open(_a,_b,_c,_d) \
gcry_cipher_open((_a),map_cipher_openpgp_to_gcry((_b)),(_c),(_d))
#define openpgp_cipher_get_algo_keylen(_a) \
gcry_cipher_get_algo_keylen(map_cipher_openpgp_to_gcry((_a)))
#define openpgp_cipher_get_algo_blklen(_a) \
gcry_cipher_get_algo_blklen(map_cipher_openpgp_to_gcry((_a)))
int openpgp_cipher_blocklen (cipher_algo_t algo);
int openpgp_cipher_test_algo(cipher_algo_t algo);
const char *openpgp_cipher_algo_name (cipher_algo_t algo);
const char *openpgp_cipher_algo_mode_name (cipher_algo_t algo,
aead_algo_t aead);
gpg_error_t openpgp_aead_test_algo (aead_algo_t algo);
const char *openpgp_aead_algo_name (aead_algo_t algo);
gpg_error_t openpgp_aead_algo_info (aead_algo_t algo,
enum gcry_cipher_modes *r_mode,
unsigned int *r_noncelen);
int openpgp_pk_test_algo (pubkey_algo_t algo);
int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use);
int openpgp_pk_algo_usage ( int algo );
const char *openpgp_pk_algo_name (pubkey_algo_t algo);
enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo);
int openpgp_md_test_algo (digest_algo_t algo);
const char *openpgp_md_algo_name (int algo);
struct expando_args
{
PKT_public_key *pk;
PKT_public_key *pksk;
byte imagetype;
int validity_info;
const char *validity_string;
const byte *namehash;
};
char *pct_expando (ctrl_t ctrl, const char *string,struct expando_args *args);
void deprecated_warning(const char *configname,unsigned int configlineno,
const char *option,const char *repl1,const char *repl2);
void deprecated_command (const char *name);
void obsolete_scdaemon_option (const char *configname,
unsigned int configlineno, const char *name);
int string_to_cipher_algo (const char *string);
aead_algo_t string_to_aead_algo (const char *string);
int string_to_digest_algo (const char *string);
const char *compress_algo_to_string(int algo);
int string_to_compress_algo(const char *string);
int check_compress_algo(int algo);
int default_cipher_algo(void);
int default_compress_algo(void);
void compliance_failure(void);
struct parse_options
{
char *name;
unsigned int bit;
char **value;
char *help;
};
char *optsep(char **stringp);
char *argsplit(char *string);
int parse_options(char *str,unsigned int *options,
struct parse_options *opts,int noisy);
const char *get_libexecdir (void);
int path_access(const char *file,int mode);
int pubkey_get_npkey (pubkey_algo_t algo);
int pubkey_get_nskey (pubkey_algo_t algo);
int pubkey_get_nsig (pubkey_algo_t algo);
int pubkey_get_nenc (pubkey_algo_t algo);
/* Temporary helpers. */
unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey );
int mpi_print (estream_t stream, gcry_mpi_t a, int mode);
unsigned int ecdsa_qbits_from_Q (unsigned int qbits);
/*-- cpr.c --*/
void set_status_fd ( int fd );
int is_status_enabled ( void );
void write_status ( int no );
void write_status_error (const char *where, gpg_error_t err);
void write_status_errcode (const char *where, int errcode);
void write_status_failure (const char *where, gpg_error_t err);
void write_status_text ( int no, const char *text );
void write_status_printf (int no, const char *format,
...) GPGRT_ATTR_PRINTF(2,3);
void write_status_strings (int no, const char *text,
...) GPGRT_ATTR_SENTINEL(0);
gpg_error_t write_status_strings2 (ctrl_t dummy, int no,
...) GPGRT_ATTR_SENTINEL(0);
void write_status_buffer ( int no,
const char *buffer, size_t len, int wrap );
void write_status_text_and_buffer ( int no, const char *text,
const char *buffer, size_t len, int wrap );
void write_status_begin_signing (gcry_md_hd_t md);
int cpr_enabled(void);
char *cpr_get( const char *keyword, const char *prompt );
char *cpr_get_no_help( const char *keyword, const char *prompt );
char *cpr_get_utf8( const char *keyword, const char *prompt );
char *cpr_get_hidden( const char *keyword, const char *prompt );
void cpr_kill_prompt(void);
int cpr_get_answer_is_yes_def (const char *keyword, const char *prompt,
int def_yes);
int cpr_get_answer_is_yes( const char *keyword, const char *prompt );
int cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt );
int cpr_get_answer_okay_cancel (const char *keyword,
const char *prompt,
int def_answer);
/*-- helptext.c --*/
void display_online_help( const char *keyword );
/*-- encrypt.c --*/
gpg_error_t setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek);
aead_algo_t use_aead (pk_list_t pk_list, int algo);
int use_mdc (pk_list_t pk_list,int algo);
int encrypt_symmetric (const char *filename );
int encrypt_store (const char *filename );
int encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename,
strlist_t remusr, int use_symkey, pk_list_t provided_keys,
gnupg_fd_t outputfd);
void encrypt_crypt_files (ctrl_t ctrl,
int nfiles, char **files, strlist_t remusr);
+gpg_error_t reencrypt_to_new_recipients (ctrl_t ctrl, int armor,
+ const char *filename, iobuf_t infp,
+ strlist_t recipients,
+ DEK *dek,
+ struct pubkey_enc_list *pkenc_list);
int encrypt_filter (void *opaque, int control,
iobuf_t a, byte *buf, size_t *ret_len);
int write_pubkey_enc (ctrl_t ctrl, PKT_public_key *pk, int throw_keyid,
DEK *dek, iobuf_t out);
/*-- sign.c --*/
int sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
int do_encrypt, strlist_t remusr, const char *outfile );
int clearsign_file (ctrl_t ctrl,
const char *fname, strlist_t locusr, const char *outfile);
int sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr);
/*-- sig-check.c --*/
void sig_check_dump_stats (void);
/* SIG is a revocation signature. Check if any of PK's designated
revokers generated it. If so, return 0. Note: this function
(correctly) doesn't care if the designated revoker is revoked. */
int check_revocation_keys (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig);
/* Check that the backsig BACKSIG from the subkey SUB_PK to its
primary key MAIN_PK is valid. */
int check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk,
PKT_signature *backsig);
/* Check that the signature SIG over a key (e.g., a key binding or a
key revocation) is valid. (To check signatures over data, use
check_signature.) */
int check_key_signature (ctrl_t ctrl, kbnode_t root, kbnode_t sig,
int *is_selfsig );
/* Like check_key_signature, but with the ability to specify some
additional parameters and get back additional information. See the
documentation for the implementation for details. */
int check_key_signature2 (ctrl_t ctrl, kbnode_t root, kbnode_t node,
PKT_public_key *check_pk, PKT_public_key *ret_pk,
int *is_selfsig, u32 *r_expiredate, int *r_expired);
/* Returns whether SIGNER generated the signature SIG over the packet
PACKET, which is a key, subkey or uid, and comes from the key block
KB. If SIGNER is NULL, it is looked up based on the information in
SIG. If not NULL, sets *IS_SELFSIG to indicate whether the
signature is a self-signature and *RET_PK to a copy of the signer's
key. */
gpg_error_t check_signature_over_key_or_uid (ctrl_t ctrl,
PKT_public_key *signer,
PKT_signature *sig,
KBNODE kb, PACKET *packet,
int *is_selfsig,
PKT_public_key *ret_pk);
/*-- delkey.c --*/
gpg_error_t delete_keys (ctrl_t ctrl,
strlist_t names, int secret, int allow_both);
/*-- keygen.c --*/
const char *get_default_pubkey_algo (void);
u32 parse_expire_string(const char *string);
u32 ask_expire_interval(int object,const char *def_expire);
u32 ask_expiredate(void);
unsigned int ask_key_flags (int algo, int subkey, unsigned int current);
const char *ask_curve (int *algo, int *subkey_algo, const char *current);
void quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr,
const char *usagestr, const char *expirestr);
void generate_keypair (ctrl_t ctrl, int full, const char *fname,
const char *card_serialno, int card_backup_key);
int keygen_set_std_prefs (const char *string,int personal);
PKT_user_id *keygen_get_std_prefs (void);
int keygen_add_key_expire( PKT_signature *sig, void *opaque );
int keygen_add_key_flags (PKT_signature *sig, void *opaque);
int keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque);
int keygen_add_std_prefs( PKT_signature *sig, void *opaque );
int keygen_upd_std_prefs( PKT_signature *sig, void *opaque );
int keygen_add_keyserver_url(PKT_signature *sig, void *opaque);
int keygen_add_notations(PKT_signature *sig,void *opaque);
int keygen_add_revkey(PKT_signature *sig, void *opaque);
gpg_error_t make_backsig (ctrl_t ctrl,
PKT_signature *sig, PKT_public_key *pk,
PKT_public_key *sub_pk, PKT_public_key *sub_psk,
u32 timestamp, const char *cache_nonce);
void keygen_prepare_new_key_adsks (void);
gpg_error_t append_all_default_adsks (ctrl_t ctrl, kbnode_t pub_root);
gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock,
const char *algostr,
const char *usagestr,
const char *expirestr);
#ifdef ENABLE_CARD_SUPPORT
gpg_error_t generate_card_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock,
int keyno, const char *serialno);
#endif
/*-- openfile.c --*/
int overwrite_filep( const char *fname );
char *make_outfile_name( const char *iname );
char *ask_outfile_name( const char *name, size_t namelen );
int open_outfile (gnupg_fd_t out_fd, const char *iname, int mode,
int restrictedperm, iobuf_t *a);
char *get_matching_datafile (const char *sigfilename);
iobuf_t open_sigfile (const char *sigfilename, progress_filter_context_t *pfx);
void try_make_homedir( const char *fname );
char *get_openpgp_revocdir (const char *home);
/*-- seskey.c --*/
void make_session_key( DEK *dek );
gcry_mpi_t encode_session_key( int openpgp_pk_algo, DEK *dek, unsigned nbits );
gcry_mpi_t encode_md_value (PKT_public_key *pk,
gcry_md_hd_t md, int hash_algo );
/*-- import.c --*/
struct import_stats_s;
typedef struct import_stats_s *import_stats_t;
struct import_filter_s;
typedef struct import_filter_s *import_filter_t;
typedef gpg_error_t (*import_screener_t)(kbnode_t keyblock, void *arg);
int parse_import_options(char *str,unsigned int *options,int noisy);
gpg_error_t parse_and_set_import_filter (const char *string);
import_filter_t save_and_clear_import_filter (void);
void restore_import_filter (import_filter_t filt);
gpg_error_t read_key_from_file_or_buffer (ctrl_t ctrl, const char *fname,
const void *buffer, size_t buflen,
kbnode_t *r_keyblock);
gpg_error_t import_included_key_block (ctrl_t ctrl, kbnode_t keyblock);
void import_keys (ctrl_t ctrl, char **fnames, int nnames,
import_stats_t stats_hd, unsigned int options,
int origin, const char *url);
gpg_error_t import_keys_es_stream (ctrl_t ctrl, estream_t fp,
import_stats_t stats_handle,
unsigned char **fpr, size_t *fpr_len,
unsigned int options,
import_screener_t screener, void *screener_arg,
int origin, const char *url);
gpg_error_t import_old_secring (ctrl_t ctrl, const char *fname);
import_stats_t import_new_stats_handle (void);
void import_release_stats_handle (import_stats_t hd);
void import_print_stats (import_stats_t hd);
/* Communication for impex_filter_getval */
struct impex_filter_parm_s
{
ctrl_t ctrl;
kbnode_t node;
char hexfpr[2*MAX_FINGERPRINT_LEN + 1];
};
const char *impex_filter_getval (void *cookie, const char *propname);
gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats,
kbnode_t sec_keyblock, int batch, int force,
int only_marked);
int collapse_uids (kbnode_t *keyblock);
int collapse_subkeys (kbnode_t *keyblock);
int get_revocation_reason (PKT_signature *sig, char **r_reason,
char **r_comment, size_t *r_commentlen);
/*-- export.c --*/
struct export_stats_s;
typedef struct export_stats_s *export_stats_t;
export_stats_t export_new_stats (void);
void export_release_stats (export_stats_t stats);
void export_print_stats (export_stats_t stats);
int parse_export_options(char *str,unsigned int *options,int noisy);
gpg_error_t parse_and_set_export_filter (const char *string);
void push_export_filters (void);
void pop_export_filters (void);
int exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, kbnode_t node);
int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options,
export_stats_t stats);
int export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options,
export_stats_t stats);
int export_secsubkeys (ctrl_t ctrl, strlist_t users, unsigned int options,
export_stats_t stats);
gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec,
unsigned int options,
const void *prefix, size_t prefixlen,
export_stats_t stats,
kbnode_t *r_keyblock,
void **r_data, size_t *r_datalen);
gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
int cleartext, int mode1003,
char **cache_nonce_addr,
const char *hexgrip,
PKT_public_key *pk, gcry_sexp_t *r_key);
gpg_error_t write_keyblock_to_output (kbnode_t keyblock,
int with_armor, unsigned int options);
gpg_error_t export_ssh_key (ctrl_t ctrl, const char *userid);
gpg_error_t export_secret_ssh_key (ctrl_t ctrl, const char *userid);
/*-- dearmor.c --*/
int dearmor_file( const char *fname );
int enarmor_file( const char *fname );
/*-- revoke.c --*/
struct revocation_reason_info;
int gen_standard_revoke (ctrl_t ctrl,
PKT_public_key *psk, const char *cache_nonce);
int gen_revoke (ctrl_t ctrl, const char *uname);
int gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr);
int revocation_reason_build_cb( PKT_signature *sig, void *opaque );
struct revocation_reason_info *
ask_revocation_reason( int key_rev, int cert_rev, int hint );
struct revocation_reason_info * get_default_uid_revocation_reason (void);
struct revocation_reason_info * get_default_sig_revocation_reason (void);
void release_revocation_reason_info (struct revocation_reason_info *reason);
/*-- keylist.c --*/
void public_key_list (ctrl_t ctrl, strlist_t list,
int locate_mode, int no_local);
void secret_key_list (ctrl_t ctrl, strlist_t list );
gpg_error_t parse_and_set_list_filter (const char *string);
void print_subpackets_colon(PKT_signature *sig);
void reorder_keyblock (KBNODE keyblock);
gpg_error_t list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret,
int has_secret, int fpr, int no_validity);
int cmp_signodes (const void *av, const void *bv);
void print_fingerprint (ctrl_t ctrl, estream_t fp,
PKT_public_key *pk, int mode);
void print_revokers (estream_t fp, int colon_mode, PKT_public_key *pk);
void show_preferences (PKT_user_id *uid, int indent, int mode, int verbose);
void show_policy_url(PKT_signature *sig,int indent,int mode);
void show_keyserver_url(PKT_signature *sig,int indent,int mode);
void show_notation(PKT_signature *sig,int indent,int mode,int which);
void dump_attribs (const PKT_user_id *uid, PKT_public_key *pk);
void set_attrib_fd(int fd);
void print_key_info (ctrl_t ctrl, estream_t fp, int indent,
PKT_public_key *pk, int secret);
void print_key_info_log (ctrl_t ctrl, int loglevel, int indent,
PKT_public_key *pk, int secret);
void print_card_key_info (estream_t fp, KBNODE keyblock);
void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret);
/*-- verify.c --*/
void print_file_status( int status, const char *name, int what );
int verify_signatures (ctrl_t ctrl, int nfiles, char **files );
int verify_files (ctrl_t ctrl, int nfiles, char **files );
int gpg_verify (ctrl_t ctrl, gnupg_fd_t sig_fd, gnupg_fd_t data_fd,
estream_t out_fp);
void check_assert_signer_list (const char *mainpkhex, const char *pkhex);
void check_assert_pubkey_algo (const char *algostr, const char *pkhex);
/*-- decrypt.c --*/
-int decrypt_message (ctrl_t ctrl, const char *filename );
+gpg_error_t decrypt_message (ctrl_t ctrl, const char *filename,
+ strlist_t remusr);
gpg_error_t decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_fd,
gnupg_fd_t output_fd);
void decrypt_messages (ctrl_t ctrl, int nfiles, char *files[]);
/*-- plaintext.c --*/
int hash_datafiles( gcry_md_hd_t md, gcry_md_hd_t md2,
strlist_t files, const char *sigfilename, int textmode);
int hash_datafile_by_fd (gcry_md_hd_t md, gcry_md_hd_t md2,
gnupg_fd_t data_fd, int textmode);
PKT_plaintext *setup_plaintext_name(const char *filename,IOBUF iobuf);
/*-- server.c --*/
int gpg_server (ctrl_t);
gpg_error_t gpg_proxy_pinentry_notify (ctrl_t ctrl,
const unsigned char *line);
#ifdef ENABLE_CARD_SUPPORT
/*-- card-util.c --*/
void change_pin (int no, int allow_admin);
void card_status (ctrl_t ctrl, estream_t fp, const char *serialno);
void card_edit (ctrl_t ctrl, strlist_t commands);
gpg_error_t card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock);
int card_store_subkey (KBNODE node, int use, strlist_t *processed_keys);
#endif
/*-- migrate.c --*/
void migrate_secring (ctrl_t ctrl);
#endif /*G10_MAIN_H*/
diff --git a/g10/mainproc.c b/g10/mainproc.c
index 3bb46b574..2429e1006 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -1,2835 +1,2896 @@
/* mainproc.c - handle packets
* Copyright (C) 1998-2009 Free Software Foundation, Inc.
* Copyright (C) 2013-2014 Werner Koch
* Copyright (C) 2020, 2024 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/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "gpg.h"
#include "../common/util.h"
#include "packet.h"
#include "../common/iobuf.h"
#include "options.h"
#include "keydb.h"
#include "filter.h"
#include "main.h"
#include "../common/status.h"
#include "../common/i18n.h"
#include "trustdb.h"
#include "keyserver-internal.h"
#include "photoid.h"
#include "../common/mbox-util.h"
#include "call-dirmngr.h"
#include "../common/compliance.h"
/* Put an upper limit on nested packets. The 32 is an arbitrary
value, a much lower should actually be sufficient. */
#define MAX_NESTING_DEPTH 32
/* An object to build a list of symkey packet info. */
struct symlist_item
{
struct symlist_item *next;
int cipher_algo;
int cfb_mode;
int other_error;
};
/*
* Object to hold the processing context.
*/
typedef struct mainproc_context *CTX;
struct mainproc_context
{
ctrl_t ctrl;
struct mainproc_context *anchor; /* May be useful in the future. */
PKT_public_key *last_pubkey;
PKT_user_id *last_user_id;
md_filter_context_t mfx;
int sigs_only; /* Process only signatures and reject all other stuff. */
int encrypt_only; /* Process only encryption messages. */
/* Name of the file with the complete signature or the file with the
detached signature. This is currently only used to deduce the
file name of the data file if that has not been given. */
const char *sigfilename;
/* A structure to describe the signed data in case of a detached
signature. */
struct
{
/* A file descriptor of the signed data. Only used if not -1. */
gnupg_fd_t data_fd;
/* A list of filenames with the data files or NULL. This is only
used if DATA_FD is -1. */
strlist_t data_names;
/* Flag to indicated that either one of the next previous fields
is used. This is only needed for better readability. */
int used;
} signed_data;
DEK *dek;
int last_was_session_key;
kbnode_t list; /* The current list of packets. */
iobuf_t iobuf; /* Used to get the filename etc. */
int trustletter; /* Temporary usage in list_node. */
ulong symkeys; /* Number of symmetrically encrypted session keys. */
struct pubkey_enc_list *pkenc_list; /* List of encryption packets. */
struct symlist_item *symenc_list; /* List of sym. encryption packets. */
int seen_pkt_encrypted_aead; /* PKT_ENCRYPTED_AEAD packet seen. */
int seen_pkt_encrypted_mdc; /* PKT_ENCRYPTED_MDC packet seen. */
struct {
unsigned int sig_seen:1; /* Set to true if a signature packet
has been seen. */
unsigned int data:1; /* Any data packet seen */
unsigned int uncompress_failed:1;
} any;
};
/* Counter with the number of literal data packets seen. Note that
* this is also bumped at the end of an encryption. This counter is
* used for a basic consistency check of a received PGP message. */
static int literals_seen;
/*** Local prototypes. ***/
-static int do_proc_packets (CTX c, iobuf_t a);
+static int do_proc_packets (CTX c, iobuf_t a, int keep_dek_and_list);
static void list_node (CTX c, kbnode_t node);
static void proc_tree (CTX c, kbnode_t node);
/*** Functions. ***/
/* Reset the literal data counter. This is required to setup a new
* decryption or verification context. */
void
reset_literals_seen(void)
{
literals_seen = 0;
}
static void
release_list( CTX c )
{
proc_tree (c, c->list);
release_kbnode (c->list);
while (c->pkenc_list)
{
struct pubkey_enc_list *tmp = c->pkenc_list->next;
release_pubkey_enc_parts (&c->pkenc_list->d);
xfree (c->pkenc_list);
c->pkenc_list = tmp;
}
c->pkenc_list = NULL;
while (c->symenc_list)
{
struct symlist_item *tmp = c->symenc_list->next;
xfree (c->symenc_list);
c->symenc_list = tmp;
}
c->symenc_list = NULL;
c->list = NULL;
c->any.data = 0;
c->any.uncompress_failed = 0;
c->last_was_session_key = 0;
c->seen_pkt_encrypted_aead = 0;
c->seen_pkt_encrypted_mdc = 0;
xfree (c->dek);
c->dek = NULL;
}
static int
add_onepass_sig (CTX c, PACKET *pkt)
{
kbnode_t node;
if (c->list) /* Add another packet. */
add_kbnode (c->list, new_kbnode (pkt));
else /* Insert the first one. */
c->list = node = new_kbnode (pkt);
return 1;
}
static int
add_gpg_control (CTX c, PACKET *pkt)
{
if ( pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START )
{
/* New clear text signature.
* Process the last one and reset everything */
release_list(c);
}
if (c->list) /* Add another packet. */
add_kbnode (c->list, new_kbnode (pkt));
else /* Insert the first one. */
c->list = new_kbnode (pkt);
return 1;
}
static int
add_user_id (CTX c, PACKET *pkt)
{
if (!c->list)
{
log_error ("orphaned user ID\n");
return 0;
}
add_kbnode (c->list, new_kbnode (pkt));
return 1;
}
static int
add_subkey (CTX c, PACKET *pkt)
{
if (!c->list)
{
log_error ("subkey w/o mainkey\n");
return 0;
}
add_kbnode (c->list, new_kbnode (pkt));
return 1;
}
static int
add_ring_trust (CTX c, PACKET *pkt)
{
if (!c->list)
{
log_error ("ring trust w/o key\n");
return 0;
}
add_kbnode (c->list, new_kbnode (pkt));
return 1;
}
static int
add_signature (CTX c, PACKET *pkt)
{
kbnode_t node;
c->any.sig_seen = 1;
if (pkt->pkttype == PKT_SIGNATURE && !c->list)
{
/* This is the first signature for the following datafile.
* GPG does not write such packets; instead it always uses
* onepass-sig packets. The drawback of PGP's method
* of prepending the signature to the data is
* that it is not possible to make a signature from data read
* from stdin. (GPG is able to read PGP stuff anyway.) */
node = new_kbnode (pkt);
c->list = node;
return 1;
}
else if (!c->list)
return 0; /* oops (invalid packet sequence)*/
else if (!c->list->pkt)
BUG(); /* so nicht */
/* Add a new signature node item at the end. */
node = new_kbnode (pkt);
add_kbnode (c->list, node);
return 1;
}
static gpg_error_t
symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
{
gpg_error_t err;
gcry_cipher_hd_t hd;
unsigned int noncelen, keylen;
enum gcry_cipher_modes ciphermode;
if (dek->use_aead)
{
err = openpgp_aead_algo_info (dek->use_aead, &ciphermode, &noncelen);
if (err)
return err;
}
else
{
ciphermode = GCRY_CIPHER_MODE_CFB;
noncelen = 0;
}
/* Check that the session key has a size of 16 to 32 bytes. */
if ((dek->use_aead && (slen < (noncelen + 16 + 16)
|| slen > (noncelen + 32 + 16)))
|| (!dek->use_aead && (slen < 17 || slen > 33)))
{
log_error ( _("weird size for an encrypted session key (%d)\n"),
(int)slen);
return gpg_error (GPG_ERR_BAD_KEY);
}
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, noncelen? seskey : NULL, noncelen);
if (err)
goto leave;
if (dek->use_aead)
{
byte ad[4];
ad[0] = (0xc0 | PKT_SYMKEY_ENC);
ad[1] = 5;
ad[2] = dek->algo;
ad[3] = dek->use_aead;
err = gcry_cipher_authenticate (hd, ad, 4);
if (err)
goto leave;
gcry_cipher_final (hd);
keylen = slen - noncelen - 16;
err = gcry_cipher_decrypt (hd, seskey+noncelen, keylen, NULL, 0);
if (err)
goto leave;
err = gcry_cipher_checktag (hd, seskey+noncelen+keylen, 16);
if (err)
goto leave;
/* Now we replace the dek components with the real session key to
* decrypt the contents of the sequencing packet. */
if (keylen > DIM(dek->key))
{
err = gpg_error (GPG_ERR_TOO_LARGE);
goto leave;
}
dek->keylen = keylen;
memcpy (dek->key, seskey + noncelen, dek->keylen);
}
else
{
gcry_cipher_decrypt (hd, seskey, slen, NULL, 0 );
/* Here we can only test whether the algo given in decrypted
* session key is a valid OpenPGP algo. With 11 defined
* symmetric algorithms we will miss 4.3% of wrong passphrases
* here. The actual checking is done later during bulk
* decryption; we can't bring this check forward easily. We
* need to use the GPG_ERR_CHECKSUM so that we won't run into
* the gnupg < 2.2 bug compatible case which would terminate the
* process on GPG_ERR_CIPHER_ALGO. Note that with AEAD (above)
* we will have a reliable test here. */
if (openpgp_cipher_test_algo (seskey[0])
|| openpgp_cipher_get_algo_keylen (seskey[0]) != slen - 1)
{
err = gpg_error (GPG_ERR_CHECKSUM);
goto leave;
}
/* Now we replace the dek components with the real session key to
* decrypt the contents of the sequencing packet. */
keylen = slen-1;
if (keylen > DIM(dek->key))
{
err = gpg_error (GPG_ERR_TOO_LARGE);
goto leave;
}
dek->algo = seskey[0];
dek->keylen = keylen;
memcpy (dek->key, seskey + 1, dek->keylen);
}
/*log_hexdump( "thekey", dek->key, dek->keylen );*/
leave:
gcry_cipher_close (hd);
return err;
}
static void
proc_symkey_enc (CTX c, PACKET *pkt)
{
gpg_error_t err;
PKT_symkey_enc *enc;
enc = pkt->pkt.symkey_enc;
if (!enc)
log_error ("invalid symkey encrypted packet\n");
else if(!c->dek)
{
int algo = enc->cipher_algo;
const char *s = openpgp_cipher_algo_name (algo);
const char *a = (enc->aead_algo ? openpgp_aead_algo_name (enc->aead_algo)
/**/ : "CFB");
if (!openpgp_cipher_test_algo (algo))
{
if (!opt.quiet)
{
if (enc->seskeylen)
log_info (_("%s.%s encrypted session key\n"), s, a );
else
log_info (_("%s.%s encrypted data\n"), s, a );
}
}
else
{
log_error (_("encrypted with unknown algorithm %d.%s\n"), algo, a);
s = NULL; /* Force a goto leave. */
}
if (openpgp_md_test_algo (enc->s2k.hash_algo))
{
log_error(_("passphrase generated with unknown digest"
" algorithm %d\n"),enc->s2k.hash_algo);
s = NULL;
}
c->last_was_session_key = 2;
if (!s || opt.list_only)
goto leave;
if (opt.override_session_key)
{
c->dek = xmalloc_clear (sizeof *c->dek);
if (get_override_session_key (c->dek, opt.override_session_key))
{
xfree (c->dek);
c->dek = NULL;
}
}
else
{
c->dek = passphrase_to_dek (algo, &enc->s2k, 0, 0, NULL,
GETPASSWORD_FLAG_SYMDECRYPT, NULL);
if (c->dek)
{
c->dek->symmetric = 1;
c->dek->use_aead = enc->aead_algo;
/* FIXME: This doesn't work perfectly if a symmetric key
comes before a public key in the message - if the
user doesn't know the passphrase, then there is a
chance that the "decrypted" algorithm will happen to
be a valid one, which will make the returned dek
appear valid, so we won't try any public keys that
come later. */
if (enc->seskeylen)
{
err = symkey_decrypt_seskey (c->dek,
enc->seskey, enc->seskeylen);
if (err)
{
log_info ("decryption of the symmetrically encrypted"
" session key failed: %s\n",
gpg_strerror (err));
if (gpg_err_code (err) != GPG_ERR_BAD_KEY
&& gpg_err_code (err) != GPG_ERR_CHECKSUM)
log_fatal ("process terminated to be bug compatible"
" with GnuPG <= 2.2\n");
else
write_status_text (STATUS_ERROR,
"symkey_decrypt.maybe_error"
" 11_BAD_PASSPHRASE");
if (c->dek->s2k_cacheid[0])
{
if (opt.debug)
log_debug ("cleared passphrase cached with ID:"
" %s\n", c->dek->s2k_cacheid);
passphrase_clear_cache (c->dek->s2k_cacheid);
}
xfree (c->dek);
c->dek = NULL;
}
}
else
c->dek->algo_info_printed = 1;
}
}
}
leave:
/* Record infos from the packet. */
{
struct symlist_item *symitem;
symitem = xcalloc (1, sizeof *symitem);
if (enc)
{
symitem->cipher_algo = enc->cipher_algo;
symitem->cfb_mode = !enc->aead_algo;
}
else
symitem->other_error = 1;
symitem->next = c->symenc_list;
c->symenc_list = symitem;
}
c->symkeys++;
free_packet (pkt, NULL);
}
static void
proc_pubkey_enc (CTX c, PACKET *pkt)
{
PKT_pubkey_enc *enc;
/* Check whether the secret key is available and store in this case. */
c->last_was_session_key = 1;
enc = pkt->pkt.pubkey_enc;
/*printf("enc: encrypted by a pubkey with keyid %08lX\n", enc->keyid[1] );*/
/* Hmmm: why do I have this algo check here - anyway there is
* function to check it. */
if (opt.verbose)
log_info (_("public key is %s\n"), keystr (enc->keyid));
if (is_status_enabled ())
{
char buf[50];
snprintf (buf, sizeof buf, "%08lX%08lX %d 0",
(ulong)enc->keyid[0], (ulong)enc->keyid[1], enc->pubkey_algo);
write_status_text (STATUS_ENC_TO, buf);
}
if (!opt.list_only && !opt.override_session_key)
{
struct pubkey_enc_list *x = xcalloc (1, sizeof *x);
copy_pubkey_enc_parts (&x->d, enc);
x->result = -1;
x->next = c->pkenc_list;
c->pkenc_list = x;
}
free_packet(pkt, NULL);
}
/*
* Print the list of public key encrypted packets which we could
* not decrypt.
*/
static void
print_pkenc_list (ctrl_t ctrl, struct pubkey_enc_list *list)
{
for (; list; list = list->next)
{
PKT_public_key *pk;
char pkstrbuf[PUBKEY_STRING_SIZE];
char *p;
pk = xmalloc_clear (sizeof *pk);
pk->pubkey_algo = list->d.pubkey_algo;
if (!get_pubkey (ctrl, pk, list->d.keyid))
{
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf);
log_info (_("encrypted with %s key, ID %s, created %s\n"),
pkstrbuf, keystr_from_pk (pk),
strtimestamp (pk->timestamp));
p = get_user_id_native (ctrl, list->d.keyid);
log_printf (_(" \"%s\"\n"), p);
xfree (p);
}
else
log_info (_("encrypted with %s key, ID %s\n"),
openpgp_pk_algo_name (list->d.pubkey_algo),
keystr (list->d.keyid));
if (opt.flags.require_pqc_encryption
&& pk->pubkey_algo != PUBKEY_ALGO_KYBER)
log_info (_("WARNING: key is not quantum-resistant\n"));
free_public_key (pk);
}
}
static void
proc_encrypted (CTX c, PACKET *pkt)
{
int result = 0;
int early_plaintext = literals_seen;
unsigned int compliance_de_vs = 0;
- if (pkt->pkttype == PKT_ENCRYPTED_AEAD)
- c->seen_pkt_encrypted_aead = 1;
- if (pkt->pkttype == PKT_ENCRYPTED_MDC)
- c->seen_pkt_encrypted_mdc = 1;
+ if (pkt)
+ {
+ if (pkt->pkttype == PKT_ENCRYPTED_AEAD)
+ c->seen_pkt_encrypted_aead = 1;
+ if (pkt->pkttype == PKT_ENCRYPTED_MDC)
+ c->seen_pkt_encrypted_mdc = 1;
+ }
+ else /* No PKT indicates the the add-recipients mode. */
+ log_assert (c->ctrl->modify_recipients);
if (early_plaintext)
{
log_info (_("WARNING: multiple plaintexts seen\n"));
write_status_errcode ("decryption.early_plaintext", GPG_ERR_BAD_DATA);
/* We fail only later so that we can print some more info first. */
}
if (!opt.quiet)
{
if (c->symkeys>1)
log_info (_("encrypted with %lu passphrases\n"), c->symkeys);
else if (c->symkeys == 1)
log_info (_("encrypted with 1 passphrase\n"));
print_pkenc_list (c->ctrl, c->pkenc_list);
}
/* Figure out the session key by looking at all pkenc packets. */
if (opt.list_only || c->dek)
;
else if (opt.override_session_key)
{
c->dek = xmalloc_clear (sizeof *c->dek);
result = get_override_session_key (c->dek, opt.override_session_key);
if (result)
{
xfree (c->dek);
c->dek = NULL;
log_info (_("public key decryption failed: %s\n"),
gpg_strerror (result));
write_status_error ("pkdecrypt_failed", result);
}
}
else if (c->pkenc_list)
{
c->dek = xmalloc_secure_clear (sizeof *c->dek);
result = get_session_key (c->ctrl, c->pkenc_list, c->dek);
if (is_status_enabled ())
{
struct pubkey_enc_list *list;
for (list = c->pkenc_list; list; list = list->next)
if (list->result)
{ /* Key was not tried or it caused an error. */
char buf[20];
snprintf (buf, sizeof buf, "%08lX%08lX",
(ulong)list->d.keyid[0], (ulong)list->d.keyid[1]);
write_status_text (STATUS_NO_SECKEY, buf);
}
}
if (result)
{
log_info (_("public key decryption failed: %s\n"),
gpg_strerror (result));
write_status_error ("pkdecrypt_failed", result);
/* Error: Delete the DEK. */
xfree (c->dek);
c->dek = NULL;
}
}
if (c->dek && opt.verbose > 1)
log_info (_("public key encrypted data: good DEK\n"));
+ if (c->ctrl->modify_recipients)
+ {
+ if (c->anchor)
+ {
+ log_error ("command not possible with nested data\n");
+ write_status_errcode ("decryption.mod_recp", GPG_ERR_BAD_DATA);
+ xfree (c->dek);
+ c->dek = NULL;
+ return;
+ }
+ literals_seen++;
+ /* Simply return here. Our caller will then test for DEK and
+ * the PK_list to decide whether decryption worked. */
+ return;
+ }
+
if (!opt.show_only_session_key)
write_status (STATUS_BEGIN_DECRYPTION);
/*log_debug("dat: %sencrypted data\n", c->dek?"":"conventional ");*/
if (opt.list_only)
result = -1;
else if (!c->dek && !c->last_was_session_key)
{
int algo;
STRING2KEY s2kbuf;
STRING2KEY *s2k = NULL;
int canceled;
if (opt.override_session_key)
{
c->dek = xmalloc_clear (sizeof *c->dek);
result = get_override_session_key (c->dek, opt.override_session_key);
if (result)
{
xfree (c->dek);
c->dek = NULL;
}
}
else
{
/* Assume this is old style conventional encrypted data. */
algo = opt.def_cipher_algo;
if (algo)
log_info (_("assuming %s encrypted data\n"),
openpgp_cipher_algo_name (algo));
else if (openpgp_cipher_test_algo (CIPHER_ALGO_IDEA))
{
algo = opt.def_cipher_algo;
if (!algo)
algo = opt.s2k_cipher_algo;
log_info (_("IDEA cipher unavailable, "
"optimistically attempting to use %s instead\n"),
openpgp_cipher_algo_name (algo));
}
else
{
algo = CIPHER_ALGO_IDEA;
if (!opt.s2k_digest_algo)
{
/* If no digest is given we assume SHA-1. */
s2kbuf.mode = 0;
s2kbuf.hash_algo = DIGEST_ALGO_SHA1;
s2k = &s2kbuf;
}
log_info (_("assuming %s encrypted data\n"), "IDEA");
}
c->dek = passphrase_to_dek (algo, s2k, 0, 0, NULL,
GETPASSWORD_FLAG_SYMDECRYPT, &canceled);
if (c->dek)
c->dek->algo_info_printed = 1;
else if (canceled)
result = gpg_error (GPG_ERR_CANCELED);
else
result = gpg_error (GPG_ERR_INV_PASSPHRASE);
}
}
else if (!c->dek)
{
if (c->symkeys && !c->pkenc_list)
result = gpg_error (GPG_ERR_BAD_KEY);
if (!result)
result = gpg_error (GPG_ERR_NO_SECKEY);
}
/* Compute compliance with CO_DE_VS. */
if (!result && (is_status_enabled () || opt.flags.require_compliance)
/* Overriding session key voids compliance. */
&& !opt.override_session_key
/* Check symmetric cipher. */
&& gnupg_gcrypt_is_compliant (CO_DE_VS)
&& gnupg_cipher_is_compliant (CO_DE_VS, c->dek->algo,
GCRY_CIPHER_MODE_CFB))
{
struct pubkey_enc_list *i;
struct symlist_item *si;
int compliant = 1;
PKT_public_key *pk = xmalloc (sizeof *pk);
if ( !(c->pkenc_list || c->symkeys) )
log_debug ("%s: where else did the session key come from?\n", __func__);
/* Check that all seen symmetric key packets use compliant
* algos. This is so that no non-compliant encrypted session
* key can be sneaked in. */
for (si = c->symenc_list; si && compliant; si = si->next)
{
if (!si->cfb_mode
|| !gnupg_cipher_is_compliant (CO_DE_VS, si->cipher_algo,
GCRY_CIPHER_MODE_CFB))
compliant = 0;
}
/* Check that every known public key used to encrypt the session key
* is compliant. */
for (i = c->pkenc_list; i && compliant; i = i->next)
{
memset (pk, 0, sizeof *pk);
pk->pubkey_algo = i->d.pubkey_algo;
if (!get_pubkey (c->ctrl, pk, i->d.keyid)
&& !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0,
pk->pkey, nbits_from_pk (pk), NULL))
compliant = 0;
release_public_key_parts (pk);
}
xfree (pk);
if (compliant)
compliance_de_vs |= 1;
}
if (!result)
{
int compl_error;
result = decrypt_data (c->ctrl, c, pkt->pkt.encrypted, c->dek,
&compl_error);
if (!result && !compl_error)
compliance_de_vs |= 2;
}
/* Trigger the deferred error. The second condition makes sure that a
* log_error printed in the cry_cipher_checktag never gets ignored. */
if (!result && early_plaintext)
result = gpg_error (GPG_ERR_BAD_DATA);
else if (!result && opt.show_only_session_key)
result = -1;
else if (!result && pkt->pkt.encrypted->aead_algo
&& log_get_errorcount (0))
result = gpg_error (GPG_ERR_BAD_SIGNATURE);
if (result == -1)
;
else if (!result
&& !opt.ignore_mdc_error
&& !pkt->pkt.encrypted->mdc_method
&& !pkt->pkt.encrypted->aead_algo)
{
/* The message has been decrypted but does not carry an MDC or
* uses AEAD encryption. --ignore-mdc-error has also not been
* used. To avoid attacks changing an MDC message to a non-MDC
* message, we fail here. */
log_error (_("WARNING: message was not integrity protected\n"));
if (!pkt->pkt.encrypted->mdc_method
&& (openpgp_cipher_get_algo_blklen (c->dek->algo) == 8
|| c->dek->algo == CIPHER_ALGO_TWOFISH))
{
/* Before 2.2.8 we did not fail hard for a missing MDC if
* one of the old ciphers where used. Although these cases
* are rare in practice we print a hint on how to decrypt
* such messages. */
log_string
(GPGRT_LOGLVL_INFO,
_("Hint: If this message was created before the year 2003 it is\n"
"likely that this message is legitimate. This is because back\n"
"then integrity protection was not widely used.\n"));
log_info (_("Use the option '%s' to decrypt anyway.\n"),
"--ignore-mdc-error");
write_status_errcode ("nomdc_with_legacy_cipher",
GPG_ERR_DECRYPT_FAILED);
}
log_info (_("decryption forced to fail!\n"));
write_status (STATUS_DECRYPTION_FAILED);
}
else if (!result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE
&& !pkt->pkt.encrypted->aead_algo
&& opt.ignore_mdc_error))
{
/* All is fine or for an MDC message the MDC failed but the
* --ignore-mdc-error option is active. For compatibility
* reasons we issue GOODMDC also for AEAD messages. */
write_status (STATUS_DECRYPTION_OKAY);
if (opt.verbose > 1)
log_info(_("decryption okay\n"));
if (pkt->pkt.encrypted->aead_algo)
{
write_status (STATUS_GOODMDC);
compliance_de_vs |= 4;
}
else if (pkt->pkt.encrypted->mdc_method && !result)
{
write_status (STATUS_GOODMDC);
compliance_de_vs |= 4;
}
else
log_info (_("WARNING: message was not integrity protected\n"));
}
else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE
|| gpg_err_code (result) == GPG_ERR_TRUNCATED)
{
glo_ctrl.lasterr = result;
log_error (_("WARNING: encrypted message has been manipulated!\n"));
write_status (STATUS_BADMDC);
write_status (STATUS_DECRYPTION_FAILED);
}
else
{
if (gpg_err_code (result) == GPG_ERR_BAD_KEY
|| gpg_err_code (result) == GPG_ERR_CHECKSUM
|| gpg_err_code (result) == GPG_ERR_CIPHER_ALGO)
{
if (c->symkeys)
write_status_text (STATUS_ERROR,
"symkey_decrypt.maybe_error"
" 11_BAD_PASSPHRASE");
if (c->dek && *c->dek->s2k_cacheid != '\0')
{
if (opt.debug)
log_debug ("cleared passphrase cached with ID: %s\n",
c->dek->s2k_cacheid);
passphrase_clear_cache (c->dek->s2k_cacheid);
}
}
glo_ctrl.lasterr = result;
write_status (STATUS_DECRYPTION_FAILED);
log_error (_("decryption failed: %s\n"), gpg_strerror (result));
/* Hmmm: does this work when we have encrypted using multiple
* ways to specify the session key (symmmetric and PK). */
}
/* If we concluded that the decryption was compliant, issue a
* compliance status before the end of the decryption status. */
if (compliance_de_vs == (4|2|1))
{
write_status_strings (STATUS_DECRYPTION_COMPLIANCE_MODE,
gnupg_status_compliance_flag (CO_DE_VS),
NULL);
}
xfree (c->dek);
c->dek = NULL;
free_packet (pkt, NULL);
c->last_was_session_key = 0;
if (!opt.show_only_session_key)
write_status (STATUS_END_DECRYPTION);
/* Bump the counter even if we have not seen a literal data packet
* inside an encryption container. This acts as a sentinel in case
* a misplace extra literal data packets follows after this
* encrypted packet. */
literals_seen++;
/* The --require-compliance option allows one to simplify decryption in
* de-vs compliance mode by just looking at the exit status. */
if (opt.flags.require_compliance
&& opt.compliance == CO_DE_VS
&& compliance_de_vs != (4|2|1)
&& !opt.show_only_session_key)
{
log_error (_("operation forced to fail due to"
" unfulfilled compliance rules\n"));
g10_errors_seen = 1;
}
}
static int
have_seen_pkt_encrypted_aead_or_mdc( CTX c )
{
CTX cc;
for (cc = c; cc; cc = cc->anchor)
{
if (cc->seen_pkt_encrypted_aead)
return 1;
if (cc->seen_pkt_encrypted_mdc)
return 1;
}
return 0;
}
static void
proc_plaintext( CTX c, PACKET *pkt )
{
PKT_plaintext *pt = pkt->pkt.plaintext;
int any, clearsig, rc;
kbnode_t n;
unsigned char *extrahash;
size_t extrahashlen;
/* This is a literal data packet. Bump a counter for later checks. */
literals_seen++;
if (pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8))
log_info (_("Note: sender requested \"for-your-eyes-only\"\n"));
else if (opt.verbose)
{
/* We don't use print_utf8_buffer because that would require a
* string change which we don't want in 2.2. It is also not
* clear whether the filename is always utf-8 encoded. */
char *tmp = make_printable_string (pt->name, pt->namelen, 0);
log_info (_("original file name='%.*s'\n"), (int)strlen (tmp), tmp);
xfree (tmp);
}
free_md_filter_context (&c->mfx);
if (gcry_md_open (&c->mfx.md, 0, 0))
BUG ();
/* fixme: we may need to push the textfilter if we have sigclass 1
* and no armoring - Not yet tested
* Hmmm, why don't we need it at all if we have sigclass 1
* Should we assume that plaintext in mode 't' has always sigclass 1??
* See: Russ Allbery's mail 1999-02-09
*/
any = clearsig = 0;
for (n=c->list; n; n = n->next )
{
if (n->pkt->pkttype == PKT_ONEPASS_SIG)
{
/* The onepass signature case. */
if (n->pkt->pkt.onepass_sig->digest_algo)
{
if (!opt.skip_verify)
gcry_md_enable (c->mfx.md,
n->pkt->pkt.onepass_sig->digest_algo);
any = 1;
}
}
else if (n->pkt->pkttype == PKT_GPG_CONTROL
&& n->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START)
{
/* The clearsigned message case. */
size_t datalen = n->pkt->pkt.gpg_control->datalen;
const byte *data = n->pkt->pkt.gpg_control->data;
/* Check that we have at least the sigclass and one hash. */
if (datalen < 2)
log_fatal ("invalid control packet CTRLPKT_CLEARSIGN_START\n");
/* Note that we don't set the clearsig flag for not-dash-escaped
* documents. */
clearsig = (*data == 0x01);
for (data++, datalen--; datalen; datalen--, data++)
if (!opt.skip_verify)
gcry_md_enable (c->mfx.md, *data);
any = 1;
break; /* Stop here as one-pass signature packets are not
expected. */
}
else if (n->pkt->pkttype == PKT_SIGNATURE)
{
/* The SIG+LITERAL case that PGP used to use. */
if (!opt.skip_verify)
gcry_md_enable (c->mfx.md, n->pkt->pkt.signature->digest_algo);
any = 1;
}
}
if (!any && !opt.skip_verify && !have_seen_pkt_encrypted_aead_or_mdc(c))
{
/* This is for the old GPG LITERAL+SIG case. It's not legal
according to 2440, so hopefully it won't come up that often.
There is no good way to specify what algorithms to use in
that case, so these there are the historical answer. */
gcry_md_enable (c->mfx.md, DIGEST_ALGO_RMD160);
gcry_md_enable (c->mfx.md, DIGEST_ALGO_SHA1);
}
if (DBG_HASHING)
{
gcry_md_debug (c->mfx.md, "verify");
if (c->mfx.md2)
gcry_md_debug (c->mfx.md2, "verify2");
}
rc=0;
if (literals_seen > 1)
{
log_info (_("WARNING: multiple plaintexts seen\n"));
write_status_text (STATUS_ERROR, "proc_pkt.plaintext 89_BAD_DATA");
log_inc_errorcount ();
rc = gpg_error (GPG_ERR_UNEXPECTED);
}
if (!rc)
{
/* It we are in --verify mode, we do not want to output the
* signed text. However, if --output is also used we do what
* has been requested and write out the signed data. */
rc = handle_plaintext (pt, &c->mfx,
(opt.outfp || opt.outfile)? 0 : c->sigs_only,
clearsig);
if (gpg_err_code (rc) == GPG_ERR_EACCES && !c->sigs_only)
{
/* Can't write output but we hash it anyway to check the
signature. */
rc = handle_plaintext( pt, &c->mfx, 1, clearsig );
}
}
if (rc)
log_error ("handle plaintext failed: %s\n", gpg_strerror (rc));
/* We add a marker control packet instead of the plaintext packet.
* This is so that we can later detect invalid packet sequences.
* The packet is further used to convey extra data from the
* plaintext packet to the signature verification. */
extrahash = xtrymalloc (6 + pt->namelen);
if (!extrahash)
{
/* No way to return an error. */
rc = gpg_error_from_syserror ();
log_error ("malloc failed in %s: %s\n", __func__, gpg_strerror (rc));
extrahashlen = 0;
}
else
{
extrahash[0] = pt->mode;
extrahash[1] = pt->namelen;
if (pt->namelen)
memcpy (extrahash+2, pt->name, pt->namelen);
extrahashlen = 2 + pt->namelen;
extrahash[extrahashlen++] = pt->timestamp >> 24;
extrahash[extrahashlen++] = pt->timestamp >> 16;
extrahash[extrahashlen++] = pt->timestamp >> 8;
extrahash[extrahashlen++] = pt->timestamp ;
}
free_packet (pkt, NULL);
c->last_was_session_key = 0;
n = new_kbnode (create_gpg_control (CTRLPKT_PLAINTEXT_MARK,
extrahash, extrahashlen));
xfree (extrahash);
if (c->list)
add_kbnode (c->list, n);
else
c->list = n;
}
static int
proc_compressed_cb (iobuf_t a, void *info)
{
if ( ((CTX)info)->signed_data.used
&& ((CTX)info)->signed_data.data_fd != GNUPG_INVALID_FD)
return proc_signature_packets_by_fd (((CTX)info)->ctrl, info, a,
((CTX)info)->signed_data.data_fd);
else
return proc_signature_packets (((CTX)info)->ctrl, info, a,
((CTX)info)->signed_data.data_names,
((CTX)info)->sigfilename );
}
static int
proc_encrypt_cb (iobuf_t a, void *info )
{
CTX c = info;
- return proc_encryption_packets (c->ctrl, info, a );
+ return proc_encryption_packets (c->ctrl, info, a, NULL, NULL);
}
static int
proc_compressed (CTX c, PACKET *pkt)
{
PKT_compressed *zd = pkt->pkt.compressed;
int rc;
/*printf("zip: compressed data packet\n");*/
if (c->sigs_only)
rc = handle_compressed (c->ctrl, c, zd, proc_compressed_cb, c);
else if( c->encrypt_only )
rc = handle_compressed (c->ctrl, c, zd, proc_encrypt_cb, c);
else
rc = handle_compressed (c->ctrl, c, zd, NULL, NULL);
if (gpg_err_code (rc) == GPG_ERR_BAD_DATA)
{
if (!c->any.uncompress_failed)
{
CTX cc;
for (cc=c; cc; cc = cc->anchor)
cc->any.uncompress_failed = 1;
log_error ("uncompressing failed: %s\n", gpg_strerror (rc));
}
}
else if (rc)
log_error ("uncompressing failed: %s\n", gpg_strerror (rc));
free_packet (pkt, NULL);
c->last_was_session_key = 0;
return rc;
}
/*
* Check the signature. If R_PK is not NULL a copy of the public key
* used to verify the signature will be stored there, or NULL if not
* found. If FORCED_PK is not NULL, this public key is used to verify
* _data signatures_ and no key lookup is done. Returns: 0 = valid
* signature or an error code
*/
static int
do_check_sig (CTX c, kbnode_t node, const void *extrahash, size_t extrahashlen,
PKT_public_key *forced_pk, int *is_selfsig,
int *is_expkey, int *is_revkey, PKT_public_key **r_pk)
{
PKT_signature *sig;
gcry_md_hd_t md = NULL;
gcry_md_hd_t md2 = NULL;
gcry_md_hd_t md_good = NULL;
int algo, rc;
if (r_pk)
*r_pk = NULL;
log_assert (node->pkt->pkttype == PKT_SIGNATURE);
if (is_selfsig)
*is_selfsig = 0;
sig = node->pkt->pkt.signature;
algo = sig->digest_algo;
rc = openpgp_md_test_algo (algo);
if (rc)
return rc;
if (sig->sig_class == 0x00)
{
if (c->mfx.md)
{
if (gcry_md_copy (&md, c->mfx.md ))
BUG ();
}
else /* detached signature */
{
/* check_signature() will enable the md. */
if (gcry_md_open (&md, 0, 0 ))
BUG ();
}
}
else if (sig->sig_class == 0x01)
{
/* How do we know that we have to hash the (already hashed) text
in canonical mode ??? (calculating both modes???) */
if (c->mfx.md)
{
if (gcry_md_copy (&md, c->mfx.md ))
BUG ();
if (c->mfx.md2 && gcry_md_copy (&md2, c->mfx.md2))
BUG ();
}
else /* detached signature */
{
log_debug ("Do we really need this here?");
/* check_signature() will enable the md*/
if (gcry_md_open (&md, 0, 0 ))
BUG ();
if (gcry_md_open (&md2, 0, 0 ))
BUG ();
}
}
else if ((sig->sig_class&~3) == 0x10
|| sig->sig_class == 0x18
|| sig->sig_class == 0x1f
|| sig->sig_class == 0x20
|| sig->sig_class == 0x28
|| sig->sig_class == 0x30)
{
if (c->list->pkt->pkttype == PKT_PUBLIC_KEY
|| c->list->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
return check_key_signature (c->ctrl, c->list, node, is_selfsig);
}
else if (sig->sig_class == 0x20)
{
log_error (_("standalone revocation - "
"use \"gpg --import\" to apply\n"));
return GPG_ERR_NOT_PROCESSED;
}
else
{
log_error ("invalid root packet for sigclass %02x\n", sig->sig_class);
return GPG_ERR_SIG_CLASS;
}
}
else
return GPG_ERR_SIG_CLASS;
/* We only get here if we are checking the signature of a binary
(0x00) or text document (0x01). */
rc = check_signature2 (c->ctrl, sig, md, extrahash, extrahashlen,
forced_pk,
NULL, is_expkey, is_revkey, r_pk);
if (! rc)
md_good = md;
else if (gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE && md2)
{
PKT_public_key *pk2;
rc = check_signature2 (c->ctrl, sig, md2, extrahash, extrahashlen,
forced_pk,
NULL, is_expkey, is_revkey,
r_pk? &pk2 : NULL);
if (!rc)
{
md_good = md2;
if (r_pk)
{
free_public_key (*r_pk);
*r_pk = pk2;
}
}
}
if (md_good)
{
unsigned char *buffer = gcry_md_read (md_good, sig->digest_algo);
sig->digest_len = gcry_md_get_algo_dlen (map_md_openpgp_to_gcry (algo));
memcpy (sig->digest, buffer, sig->digest_len);
}
gcry_md_close (md);
gcry_md_close (md2);
return rc;
}
static void
print_userid (PACKET *pkt)
{
if (!pkt)
BUG();
if (pkt->pkttype != PKT_USER_ID)
{
es_printf ("ERROR: unexpected packet type %d", pkt->pkttype );
return;
}
if (opt.with_colons)
{
if (pkt->pkt.user_id->attrib_data)
es_printf("%u %lu",
pkt->pkt.user_id->numattribs,
pkt->pkt.user_id->attrib_len);
else
es_write_sanitized (es_stdout, pkt->pkt.user_id->name,
pkt->pkt.user_id->len, ":", NULL);
}
else
print_utf8_buffer (es_stdout, pkt->pkt.user_id->name,
pkt->pkt.user_id->len );
}
/*
* List the keyblock in a user friendly way
*/
static void
list_node (CTX c, kbnode_t node)
{
if (!node)
;
else if (node->pkt->pkttype == PKT_PUBLIC_KEY
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
PKT_public_key *pk = node->pkt->pkt.public_key;
if (opt.with_colons)
{
u32 keyid[2];
keyid_from_pk( pk, keyid );
if (pk->flags.primary)
c->trustletter = (opt.fast_list_mode
? 0
: get_validity_info
(c->ctrl,
node->pkt->pkttype == PKT_PUBLIC_KEY
? node : NULL,
pk, NULL));
es_printf ("%s:", pk->flags.primary? "pub":"sub" );
if (c->trustletter)
es_putc (c->trustletter, es_stdout);
es_printf (":%u:%d:%08lX%08lX:%s:%s::",
nbits_from_pk( pk ),
pk->pubkey_algo,
(ulong)keyid[0],(ulong)keyid[1],
colon_datestr_from_pk( pk ),
colon_strtime (pk->expiredate) );
if (pk->flags.primary && !opt.fast_list_mode)
es_putc (get_ownertrust_info (c->ctrl, pk, 1), es_stdout);
es_putc (':', es_stdout);
es_putc ('\n', es_stdout);
}
else
{
print_key_line (c->ctrl, es_stdout, pk, 0);
}
if (opt.keyid_format == KF_NONE && !opt.with_colons)
; /* Already printed. */
else if ((pk->flags.primary && opt.fingerprint) || opt.fingerprint > 1)
print_fingerprint (c->ctrl, NULL, pk, 0);
if (pk->flags.primary)
{
int kl = opt.keyid_format == KF_NONE? 0 : keystrlen ();
/* Now list all userids with their signatures. */
for (node = node->next; node; node = node->next)
{
if (node->pkt->pkttype == PKT_SIGNATURE)
{
list_node (c, node );
}
else if (node->pkt->pkttype == PKT_USER_ID)
{
if (opt.with_colons)
es_printf ("%s:::::::::",
node->pkt->pkt.user_id->attrib_data?"uat":"uid");
else
es_printf ("uid%*s",
kl + (opt.legacy_list_mode? 9:11),
"" );
print_userid (node->pkt);
if (opt.with_colons)
es_putc (':', es_stdout);
es_putc ('\n', es_stdout);
}
else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
list_node(c, node );
}
}
}
}
else if (node->pkt->pkttype == PKT_SECRET_KEY
|| node->pkt->pkttype == PKT_SECRET_SUBKEY)
{
log_debug ("FIXME: No way to print secret key packets here\n");
/* fixme: We may use a function to turn a secret key packet into
a public key one and use that here. */
}
else if (node->pkt->pkttype == PKT_SIGNATURE)
{
PKT_signature *sig = node->pkt->pkt.signature;
int is_selfsig = 0;
int rc2 = 0;
size_t n;
char *p;
int sigrc = ' ';
if (!opt.verbose)
return;
if (sig->sig_class == 0x20 || sig->sig_class == 0x30)
es_fputs ("rev", es_stdout);
else
es_fputs ("sig", es_stdout);
if (opt.check_sigs)
{
fflush (stdout);
rc2 = do_check_sig (c, node, NULL, 0, NULL,
&is_selfsig, NULL, NULL, NULL);
switch (gpg_err_code (rc2))
{
case 0: sigrc = '!'; break;
case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break;
case GPG_ERR_NO_PUBKEY:
case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break;
default: sigrc = '%'; break;
}
}
else /* Check whether this is a self signature. */
{
u32 keyid[2];
if (c->list->pkt->pkttype == PKT_PUBLIC_KEY
|| c->list->pkt->pkttype == PKT_SECRET_KEY )
{
keyid_from_pk (c->list->pkt->pkt.public_key, keyid);
if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1])
is_selfsig = 1;
}
}
if (opt.with_colons)
{
es_putc (':', es_stdout);
if (sigrc != ' ')
es_putc (sigrc, es_stdout);
es_printf ("::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo,
(ulong)sig->keyid[0], (ulong)sig->keyid[1],
colon_datestr_from_sig (sig),
colon_expirestr_from_sig (sig));
if (sig->trust_depth || sig->trust_value)
es_printf ("%d %d",sig->trust_depth,sig->trust_value);
es_putc (':', es_stdout);
if (sig->trust_regexp)
es_write_sanitized (es_stdout, sig->trust_regexp,
strlen (sig->trust_regexp), ":", NULL);
es_putc (':', es_stdout);
}
else
es_printf ("%c %s %s ",
sigrc, keystr (sig->keyid), datestr_from_sig(sig));
if (sigrc == '%')
es_printf ("[%s] ", gpg_strerror (rc2) );
else if (sigrc == '?')
;
else if (is_selfsig)
{
if (opt.with_colons)
es_putc (':', es_stdout);
es_fputs (sig->sig_class == 0x18? "[keybind]":"[selfsig]", es_stdout);
if (opt.with_colons)
es_putc (':', es_stdout);
}
else if (!opt.fast_list_mode)
{
p = get_user_id (c->ctrl, sig->keyid, &n, NULL);
es_write_sanitized (es_stdout, p, n,
opt.with_colons?":":NULL, NULL );
xfree (p);
}
if (opt.with_colons)
es_printf (":%02x%c:", sig->sig_class, sig->flags.exportable?'x':'l');
es_putc ('\n', es_stdout);
}
else
log_error ("invalid node with packet of type %d\n", node->pkt->pkttype);
}
int
proc_packets (ctrl_t ctrl, void *anchor, iobuf_t a )
{
int rc;
CTX c = xmalloc_clear (sizeof *c);
c->ctrl = ctrl;
c->anchor = anchor;
- rc = do_proc_packets (c, a);
+ rc = do_proc_packets (c, a, 0);
xfree (c);
return rc;
}
int
proc_signature_packets (ctrl_t ctrl, void *anchor, iobuf_t a,
strlist_t signedfiles, const char *sigfilename )
{
CTX c = xmalloc_clear (sizeof *c);
int rc;
c->ctrl = ctrl;
c->anchor = anchor;
c->sigs_only = 1;
c->signed_data.data_fd = GNUPG_INVALID_FD;
c->signed_data.data_names = signedfiles;
c->signed_data.used = !!signedfiles;
c->sigfilename = sigfilename;
- rc = do_proc_packets (c, a);
+ rc = do_proc_packets (c, a, 0);
/* If we have not encountered any signature we print an error
messages, send a NODATA status back and return an error code.
Using log_error is required because verify_files does not check
error codes for each file but we want to terminate the process
with an error. */
if (!rc && !c->any.sig_seen)
{
write_status_text (STATUS_NODATA, "4");
log_error (_("no signature found\n"));
rc = GPG_ERR_NO_DATA;
}
/* Propagate the signature seen flag upward. Do this only on success
so that we won't issue the nodata status several times. */
if (!rc && c->anchor && c->any.sig_seen)
c->anchor->any.sig_seen = 1;
xfree (c);
return rc;
}
int
proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, iobuf_t a,
gnupg_fd_t signed_data_fd)
{
int rc;
CTX c;
c = xtrycalloc (1, sizeof *c);
if (!c)
return gpg_error_from_syserror ();
c->ctrl = ctrl;
c->anchor = anchor;
c->sigs_only = 1;
c->signed_data.data_fd = signed_data_fd;
c->signed_data.data_names = NULL;
c->signed_data.used = (signed_data_fd != GNUPG_INVALID_FD);
- rc = do_proc_packets (c, a);
+ rc = do_proc_packets (c, a, 0);
/* If we have not encountered any signature we print an error
messages, send a NODATA status back and return an error code.
Using log_error is required because verify_files does not check
error codes for each file but we want to terminate the process
with an error. */
if (!rc && !c->any.sig_seen)
{
write_status_text (STATUS_NODATA, "4");
log_error (_("no signature found\n"));
rc = gpg_error (GPG_ERR_NO_DATA);
}
/* Propagate the signature seen flag upward. Do this only on success
so that we won't issue the nodata status several times. */
if (!rc && c->anchor && c->any.sig_seen)
c->anchor->any.sig_seen = 1;
xfree ( c );
return rc;
}
-int
-proc_encryption_packets (ctrl_t ctrl, void *anchor, iobuf_t a )
+/* Handle encryption packets. If called recursively the caller's CTX
+ * should be given for ANCHOR. If R_DEK and R_LIST are not NULL the
+ * DEK (or NULL) is returned there and the list at R_LIST; the caller
+ * needs to release them; even if the function returns an error. */
+gpg_error_t
+proc_encryption_packets (ctrl_t ctrl, void *anchor, iobuf_t a,
+ DEK **r_dek, struct pubkey_enc_list **r_list)
{
CTX c = xmalloc_clear (sizeof *c);
int rc;
c->ctrl = ctrl;
c->anchor = anchor;
c->encrypt_only = 1;
- rc = do_proc_packets (c, a);
+ if (r_dek && r_list)
+ {
+ rc = do_proc_packets (c, a, 1);
+ *r_dek = c->dek;
+ c->dek = NULL;
+ *r_list = c->pkenc_list;
+ c->pkenc_list = NULL;
+ }
+ else
+ rc = do_proc_packets (c, a, 0);
xfree (c);
return rc;
}
static int
check_nesting (CTX c)
{
int level;
for (level=0; c; c = c->anchor)
level++;
if (level > MAX_NESTING_DEPTH)
{
log_error ("input data with too deeply nested packets\n");
write_status_text (STATUS_UNEXPECTED, "1");
return GPG_ERR_BAD_DATA;
}
return 0;
}
+/* Main processing loop. If KEEP_DEK_AND_LIST is set the DEK and
+ * PKENC_LIST of the context C are not released at the end of the
+ * function. The caller is then required to do this. */
static int
-do_proc_packets (CTX c, iobuf_t a)
+do_proc_packets (CTX c, iobuf_t a, int keep_dek_and_list)
{
PACKET *pkt;
struct parse_packet_ctx_s parsectx;
int rc = 0;
int any_data = 0;
int newpkt;
rc = check_nesting (c);
if (rc)
return rc;
pkt = xmalloc( sizeof *pkt );
c->iobuf = a;
init_packet(pkt);
init_parse_packet (&parsectx, a);
while ((rc=parse_packet (&parsectx, pkt)) != -1)
{
any_data = 1;
if (rc)
{
+ if (c->ctrl->modify_recipients && gpg_err_code (rc) == GPG_ERR_TRUE)
+ {
+ /* Save the last read CTB (which was the last byte
+ * actually read from the input) and get out of the
+ * loop. */
+ c->ctrl->last_read_ctb = parsectx.last_ctb;
+ /* We need to call the first part of the encrypted data
+ * handler to get the DEK. */
+ proc_encrypted (c, NULL);
+ rc = -1;
+ break;
+ }
free_packet (pkt, &parsectx);
/* Stop processing when an invalid packet has been encountered
* but don't do so when we are doing a --list-packets. */
if (gpg_err_code (rc) == GPG_ERR_INV_PACKET
&& opt.list_packets == 0)
break;
continue;
}
newpkt = -1;
if (opt.list_packets)
{
switch (pkt->pkttype)
{
case PKT_PUBKEY_ENC: proc_pubkey_enc (c, pkt); break;
case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD:proc_encrypted (c, pkt); break;
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
default: newpkt = 0; break;
}
}
else if (c->sigs_only)
{
switch (pkt->pkttype)
{
case PKT_PUBLIC_KEY:
case PKT_SECRET_KEY:
case PKT_USER_ID:
case PKT_SYMKEY_ENC:
case PKT_PUBKEY_ENC:
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD:
write_status_text( STATUS_UNEXPECTED, "0" );
rc = GPG_ERR_UNEXPECTED;
goto leave;
case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break;
case PKT_PLAINTEXT: proc_plaintext (c, pkt); break;
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;
case PKT_GPG_CONTROL: newpkt = add_gpg_control (c, pkt); break;
default: newpkt = 0; break;
}
}
else if (c->encrypt_only)
{
switch (pkt->pkttype)
{
case PKT_PUBLIC_KEY:
case PKT_SECRET_KEY:
case PKT_USER_ID:
write_status_text (STATUS_UNEXPECTED, "0");
rc = GPG_ERR_UNEXPECTED;
goto leave;
case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break;
- case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
- case PKT_PUBKEY_ENC: proc_pubkey_enc (c, pkt); break;
+
+ case PKT_SYMKEY_ENC:
+ case PKT_PUBKEY_ENC:
+ /* In --add-recipients mode set the stop flag as soon as
+ * we see the first of these packets. */
+ if (c->ctrl->modify_recipients)
+ parsectx.only_fookey_enc = 1;
+ if (pkt->pkttype == PKT_SYMKEY_ENC)
+ proc_symkey_enc (c, pkt);
+ else
+ proc_pubkey_enc (c, pkt);
+ break;
+
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break;
case PKT_PLAINTEXT: proc_plaintext (c, pkt); break;
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;
case PKT_GPG_CONTROL: newpkt = add_gpg_control (c, pkt); break;
default: newpkt = 0; break;
}
}
else
{
switch (pkt->pkttype)
{
case PKT_PUBLIC_KEY:
case PKT_SECRET_KEY:
release_list (c);
c->list = new_kbnode (pkt);
newpkt = 1;
break;
case PKT_PUBLIC_SUBKEY:
case PKT_SECRET_SUBKEY:
newpkt = add_subkey (c, pkt);
break;
case PKT_USER_ID: newpkt = add_user_id (c, pkt); break;
case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break;
case PKT_PUBKEY_ENC: proc_pubkey_enc (c, pkt); break;
case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break;
case PKT_PLAINTEXT: proc_plaintext (c, pkt); break;
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;
case PKT_GPG_CONTROL: newpkt = add_gpg_control(c, pkt); break;
case PKT_RING_TRUST: newpkt = add_ring_trust (c, pkt); break;
default: newpkt = 0; break;
}
}
if (rc)
goto leave;
/* This is a very ugly construct and frankly, I don't remember why
* I used it. Adding the MDC check here is a hack.
* The right solution is to initiate another context for encrypted
* packet and not to reuse the current one ... It works right
* when there is a compression packet between which adds just
* an extra layer.
* Hmmm: Rewrite this whole module here??
*/
if (pkt->pkttype != PKT_SIGNATURE && pkt->pkttype != PKT_MDC)
c->any.data = (pkt->pkttype == PKT_PLAINTEXT);
if (newpkt == -1)
;
else if (newpkt)
{
pkt = xmalloc (sizeof *pkt);
init_packet (pkt);
}
else
free_packet (pkt, &parsectx);
}
if (rc == GPG_ERR_INV_PACKET)
write_status_text (STATUS_NODATA, "3");
if (any_data)
rc = 0;
else if (rc == -1)
write_status_text (STATUS_NODATA, "2");
leave:
- release_list (c);
- xfree(c->dek);
+ if (!keep_dek_and_list)
+ release_list (c);
free_packet (pkt, &parsectx);
deinit_parse_packet (&parsectx);
xfree (pkt);
free_md_filter_context (&c->mfx);
return rc;
}
/* Return true if the AKL has the WKD method specified. */
static int
akl_has_wkd_method (void)
{
struct akl *akl;
for (akl = opt.auto_key_locate; akl; akl = akl->next)
if (akl->type == AKL_WKD)
return 1;
return 0;
}
/* Return the ISSUER fingerprint buffer and its length at R_LEN.
* Returns NULL if not available. The returned buffer is valid as
* long as SIG is not modified. */
const byte *
issuer_fpr_raw (PKT_signature *sig, size_t *r_len)
{
const byte *p;
size_t n;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_ISSUER_FPR, &n);
if (p && ((n == 21 && p[0] == 4) || (n == 33 && p[0] == 5)))
{
*r_len = n - 1;
return p+1;
}
*r_len = 0;
return NULL;
}
/* Return the ISSUER fingerprint string in human readable format if
* available. Caller must release the string. */
/* FIXME: Move to another file. */
char *
issuer_fpr_string (PKT_signature *sig)
{
const byte *p;
size_t n;
p = issuer_fpr_raw (sig, &n);
return p? bin2hex (p, n, NULL) : NULL;
}
static void
print_good_bad_signature (int statno, const char *keyid_str, kbnode_t un,
PKT_signature *sig, int rc)
{
char *p;
write_status_text_and_buffer (statno, keyid_str,
un? un->pkt->pkt.user_id->name:"[?]",
un? un->pkt->pkt.user_id->len:3,
-1);
if (un)
p = utf8_to_native (un->pkt->pkt.user_id->name,
un->pkt->pkt.user_id->len, 0);
else
p = xstrdup ("[?]");
if (rc)
log_info (_("BAD signature from \"%s\""), p);
else if (sig->flags.expired)
log_info (_("Expired signature from \"%s\""), p);
else
log_info (_("Good signature from \"%s\""), p);
xfree (p);
}
static int
check_sig_and_print (CTX c, kbnode_t node)
{
PKT_signature *sig = node->pkt->pkt.signature;
const char *astr;
gpg_error_t rc;
int is_expkey = 0;
int is_revkey = 0;
char *issuer_fpr = NULL;
PKT_public_key *pk = NULL; /* The public key for the signature or NULL. */
const void *extrahash = NULL;
size_t extrahashlen = 0;
kbnode_t included_keyblock = NULL;
char pkstrbuf[PUBKEY_STRING_SIZE] = { 0 };
if (opt.skip_verify)
{
log_info(_("signature verification suppressed\n"));
return 0;
}
/* Check that the message composition is valid.
*
* Per RFC-2440bis (-15) allowed:
*
* S{1,n} -- detached signature.
* S{1,n} P -- old style PGP2 signature
* O{1,n} P S{1,n} -- standard OpenPGP signature.
* C P S{1,n} -- cleartext signature.
*
*
* O = One-Pass Signature packet.
* S = Signature packet.
* P = OpenPGP Message packet (Encrypted | Compressed | Literal)
* (Note that the current rfc2440bis draft also allows
* for a signed message but that does not work as it
* introduces ambiguities.)
* We keep track of these packages using the marker packet
* CTRLPKT_PLAINTEXT_MARK.
* C = Marker packet for cleartext signatures.
*
* We reject all other messages.
*
* Actually we are calling this too often, i.e. for verification of
* each message but better have some duplicate work than to silently
* introduce a bug here.
*/
{
kbnode_t n;
int n_onepass, n_sig;
/* log_debug ("checking signature packet composition\n"); */
/* dump_kbnode (c->list); */
n = c->list;
log_assert (n);
if ( n->pkt->pkttype == PKT_SIGNATURE )
{
/* This is either "S{1,n}" case (detached signature) or
"S{1,n} P" (old style PGP2 signature). */
for (n = n->next; n; n = n->next)
if (n->pkt->pkttype != PKT_SIGNATURE)
break;
if (!n)
; /* Okay, this is a detached signature. */
else if (n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK) )
{
if (n->next)
goto ambiguous; /* We only allow one P packet. */
extrahash = n->pkt->pkt.gpg_control->data;
extrahashlen = n->pkt->pkt.gpg_control->datalen;
}
else
goto ambiguous;
}
else if (n->pkt->pkttype == PKT_ONEPASS_SIG)
{
/* This is the "O{1,n} P S{1,n}" case (standard signature). */
for (n_onepass=1, n = n->next;
n && n->pkt->pkttype == PKT_ONEPASS_SIG; n = n->next)
n_onepass++;
if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK)))
goto ambiguous;
extrahash = n->pkt->pkt.gpg_control->data;
extrahashlen = n->pkt->pkt.gpg_control->datalen;
for (n_sig=0, n = n->next;
n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
n_sig++;
if (!n_sig)
goto ambiguous;
/* If we wanted to disallow multiple sig verification, we'd do
* something like this:
*
* if (n)
* goto ambiguous;
*
* However, this can stay allowable as we can't get here. */
if (n_onepass != n_sig)
{
log_info ("number of one-pass packets does not match "
"number of signature packets\n");
goto ambiguous;
}
}
else if (n->pkt->pkttype == PKT_GPG_CONTROL
&& n->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START )
{
/* This is the "C P S{1,n}" case (clear text signature). */
n = n->next;
if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK)))
goto ambiguous;
extrahash = n->pkt->pkt.gpg_control->data;
extrahashlen = n->pkt->pkt.gpg_control->datalen;
for (n_sig=0, n = n->next;
n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
n_sig++;
if (n || !n_sig)
goto ambiguous;
}
else
{
ambiguous:
log_error(_("can't handle this ambiguous signature data\n"));
return 0;
}
} /* End checking signature packet composition. */
if (sig->signers_uid)
write_status_buffer (STATUS_NEWSIG,
sig->signers_uid, strlen (sig->signers_uid), 0);
else
write_status_text (STATUS_NEWSIG, NULL);
astr = openpgp_pk_algo_name ( sig->pubkey_algo );
issuer_fpr = issuer_fpr_string (sig);
if (issuer_fpr)
{
log_info (_("Signature made %s\n"), asctimestamp(sig->timestamp));
log_info (_(" using %s key %s\n"),
astr? astr: "?", issuer_fpr);
}
else if (!keystrlen () || keystrlen () > 8)
{
log_info (_("Signature made %s\n"), asctimestamp(sig->timestamp));
log_info (_(" using %s key %s\n"),
astr? astr: "?", keystr(sig->keyid));
}
else /* Legacy format. */
log_info (_("Signature made %s using %s key ID %s\n"),
asctimestamp(sig->timestamp), astr? astr: "?",
keystr(sig->keyid));
/* In verbose mode print the signers UID. */
if (sig->signers_uid)
log_info (_(" issuer \"%s\"\n"), sig->signers_uid);
rc = do_check_sig (c, node, extrahash, extrahashlen, NULL,
NULL, &is_expkey, &is_revkey, &pk);
/* If the key is not found but the signature includes a key block we
* use that key block for verification and on success import it. */
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
&& sig->flags.key_block
&& opt.flags.auto_key_import)
{
PKT_public_key *included_pk;
const byte *kblock;
size_t kblock_len;
included_pk = xcalloc (1, sizeof *included_pk);
kblock = parse_sig_subpkt (sig, 1, SIGSUBPKT_KEY_BLOCK, &kblock_len);
if (kblock && kblock_len > 1
&& !get_pubkey_from_buffer (c->ctrl, included_pk,
kblock+1, kblock_len-1,
sig->keyid, &included_keyblock))
{
rc = do_check_sig (c, node, extrahash, extrahashlen, included_pk,
NULL, &is_expkey, &is_revkey, &pk);
if (opt.verbose)
log_debug ("checked signature using included key block: %s\n",
gpg_strerror (rc));
if (!rc)
{
/* The keyblock has been verified, we now import it. */
rc = import_included_key_block (c->ctrl, included_keyblock);
}
}
free_public_key (included_pk);
}
/* If the key isn't found, check for a preferred keyserver. Note
* that this is only done if honor-keyserver-url has been set. We
* test for this in the loop so that we can show info about the
* preferred keyservers. */
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
&& sig->flags.pref_ks)
{
const byte *p;
int seq = 0;
size_t n;
int any_pref_ks = 0;
while ((p=enum_sig_subpkt (sig, 1, SIGSUBPKT_PREF_KS, &n, &seq, NULL)))
{
/* According to my favorite copy editor, in English grammar,
you say "at" if the key is located on a web page, but
"from" if it is located on a keyserver. I'm not going to
even try to make two strings here :) */
log_info(_("Key available at: ") );
print_utf8_buffer (log_get_stream(), p, n);
log_printf ("\n");
any_pref_ks = 1;
if ((opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE)
&& (opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL))
{
struct keyserver_spec *spec;
spec = parse_preferred_keyserver (sig);
if (spec)
{
int res;
if (DBG_LOOKUP)
log_debug ("trying auto-key-retrieve method %s\n",
"Pref-KS");
free_public_key (pk);
pk = NULL;
glo_ctrl.in_auto_key_retrieve++;
res = keyserver_import_keyid (c->ctrl, sig->keyid,spec,
KEYSERVER_IMPORT_FLAG_QUICK);
glo_ctrl.in_auto_key_retrieve--;
if (!res)
rc = do_check_sig (c, node, extrahash, extrahashlen, NULL,
NULL, &is_expkey, &is_revkey, &pk);
else if (DBG_LOOKUP)
log_debug ("lookup via %s failed: %s\n", "Pref-KS",
gpg_strerror (res));
free_keyserver_spec (spec);
if (!rc)
break;
}
}
}
if (any_pref_ks
&& (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE)
&& !(opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL))
log_info (_("Note: Use '%s' to make use of this info\n"),
"--keyserver-option honor-keyserver-url");
}
/* If the above methods didn't work, our next try is to retrieve the
* key from the WKD. This requires that WKD is in the AKL and the
* Signer's UID is in the signature. */
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
&& (opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE)
&& !opt.flags.disable_signer_uid
&& akl_has_wkd_method ()
&& sig->signers_uid)
{
int res;
if (DBG_LOOKUP)
log_debug ("trying auto-key-retrieve method %s\n", "WKD");
free_public_key (pk);
pk = NULL;
glo_ctrl.in_auto_key_retrieve++;
res = keyserver_import_wkd (c->ctrl, sig->signers_uid,
KEYSERVER_IMPORT_FLAG_QUICK, NULL, NULL);
glo_ctrl.in_auto_key_retrieve--;
/* Fixme: If the fingerprint is embedded in the signature,
* compare it to the fingerprint of the returned key. */
if (!res)
rc = do_check_sig (c, node, extrahash, extrahashlen, NULL,
NULL, &is_expkey, &is_revkey, &pk);
else if (DBG_LOOKUP)
log_debug ("lookup via %s failed: %s\n", "WKD", gpg_strerror (res));
}
/* If the above methods didn't work, our next try is to locate
* the key via its fingerprint from a keyserver. This requires
* that the signers fingerprint is encoded in the signature. */
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
&& (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE)
&& keyserver_any_configured (c->ctrl))
{
int res;
const byte *p;
size_t n;
p = issuer_fpr_raw (sig, &n);
if (p)
{
if (DBG_LOOKUP)
log_debug ("trying auto-key-retrieve method %s\n", "KS");
/* v4 or v5 packet with a SHA-1/256 fingerprint. */
free_public_key (pk);
pk = NULL;
glo_ctrl.in_auto_key_retrieve++;
res = keyserver_import_fpr (c->ctrl, p, n, opt.keyserver,
KEYSERVER_IMPORT_FLAG_QUICK);
glo_ctrl.in_auto_key_retrieve--;
if (!res)
rc = do_check_sig (c, node, extrahash, extrahashlen, NULL,
NULL, &is_expkey, &is_revkey, &pk);
else if (DBG_LOOKUP)
log_debug ("lookup via %s failed: %s\n", "KS", gpg_strerror (res));
}
}
/* Do something with the result of the signature checking. */
if (!rc || gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE)
{
/* We have checked the signature and the result is either a good
* signature or a bad signature. Further examination follows. */
kbnode_t un, keyblock;
int count = 0;
int keyblock_has_pk = 0; /* For failsafe check. */
int statno;
char keyid_str[50];
PKT_public_key *mainpk = NULL;
if (rc)
statno = STATUS_BADSIG;
else if (sig->flags.expired)
statno = STATUS_EXPSIG;
else if (is_expkey)
statno = STATUS_EXPKEYSIG;
else if(is_revkey)
statno = STATUS_REVKEYSIG;
else
statno = STATUS_GOODSIG;
/* FIXME: We should have the public key in PK and thus the
* keyblock has already been fetched. Thus we could use the
* fingerprint or PK itself to lookup the entire keyblock. That
* would best be done with a cache. */
if (included_keyblock)
{
keyblock = included_keyblock;
included_keyblock = NULL;
}
else
keyblock = get_pubkeyblock_for_sig (c->ctrl, sig);
snprintf (keyid_str, sizeof keyid_str, "%08lX%08lX [uncertain] ",
(ulong)sig->keyid[0], (ulong)sig->keyid[1]);
/* Find and print the primary user ID along with the
"Good|Expired|Bad signature" line. */
for (un=keyblock; un; un = un->next)
{
int valid;
if (!keyblock_has_pk
&& (un->pkt->pkttype == PKT_PUBLIC_KEY
|| un->pkt->pkttype == PKT_PUBLIC_SUBKEY)
&& !cmp_public_keys (un->pkt->pkt.public_key, pk))
{
keyblock_has_pk = 1;
}
if (un->pkt->pkttype == PKT_PUBLIC_KEY)
{
mainpk = un->pkt->pkt.public_key;
continue;
}
if (un->pkt->pkttype != PKT_USER_ID)
continue;
if (!un->pkt->pkt.user_id->created)
continue;
if (un->pkt->pkt.user_id->flags.revoked)
continue;
if (un->pkt->pkt.user_id->flags.expired)
continue;
if (!un->pkt->pkt.user_id->flags.primary)
continue;
/* We want the textual primary user ID here */
if (un->pkt->pkt.user_id->attrib_data)
continue;
log_assert (mainpk);
/* Since this is just informational, don't actually ask the
user to update any trust information. (Note: we register
the signature later.) Because print_good_bad_signature
does not print a LF we need to compute the validity
before calling that function. */
if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY))
valid = get_validity (c->ctrl, keyblock, mainpk,
un->pkt->pkt.user_id, NULL, 0);
else
valid = 0; /* Not used. */
keyid_str[17] = 0; /* cut off the "[uncertain]" part */
print_good_bad_signature (statno, keyid_str, un, sig, rc);
if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY))
log_printf (" [%s]\n",trust_value_to_string(valid));
else
log_printf ("\n");
count++;
/* At this point we could in theory stop because the primary
* UID flag is never set for more than one User ID per
* keyblock. However, we use this loop also for a failsafe
* check that the public key used to create the signature is
* contained in the keyring.*/
}
log_assert (mainpk);
if (!keyblock_has_pk)
{
log_error ("signature key lost from keyblock\n");
rc = gpg_error (GPG_ERR_INTERNAL);
}
/* In case we did not found a valid textual userid above
we print the first user id packet or a "[?]" instead along
with the "Good|Expired|Bad signature" line. */
if (!count)
{
/* Try for an invalid textual userid */
for (un=keyblock; un; un = un->next)
{
if (un->pkt->pkttype == PKT_USER_ID
&& !un->pkt->pkt.user_id->attrib_data)
break;
}
/* Try for any userid at all */
if (!un)
{
for (un=keyblock; un; un = un->next)
{
if (un->pkt->pkttype == PKT_USER_ID)
break;
}
}
if (opt.trust_model==TM_ALWAYS || !un)
keyid_str[17] = 0; /* cut off the "[uncertain]" part */
print_good_bad_signature (statno, keyid_str, un, sig, rc);
if (opt.trust_model != TM_ALWAYS && un)
log_printf (" %s",_("[uncertain]") );
log_printf ("\n");
}
/* If we have a good signature and already printed
* the primary user ID, print all the other user IDs */
if (count
&& !rc
&& !(opt.verify_options & VERIFY_SHOW_PRIMARY_UID_ONLY))
{
char *p;
for( un=keyblock; un; un = un->next)
{
if (un->pkt->pkttype != PKT_USER_ID)
continue;
if ((un->pkt->pkt.user_id->flags.revoked
|| un->pkt->pkt.user_id->flags.expired)
&& !(opt.verify_options & VERIFY_SHOW_UNUSABLE_UIDS))
continue;
/* Skip textual primary user ids which we printed above. */
if (un->pkt->pkt.user_id->flags.primary
&& !un->pkt->pkt.user_id->attrib_data )
continue;
/* If this user id has attribute data, print that. */
if (un->pkt->pkt.user_id->attrib_data)
{
dump_attribs (un->pkt->pkt.user_id, mainpk);
if (opt.verify_options&VERIFY_SHOW_PHOTOS)
show_photos (c->ctrl,
un->pkt->pkt.user_id->attribs,
un->pkt->pkt.user_id->numattribs,
mainpk ,un->pkt->pkt.user_id);
}
p = utf8_to_native (un->pkt->pkt.user_id->name,
un->pkt->pkt.user_id->len, 0);
log_info (_(" aka \"%s\""), p);
xfree (p);
if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY))
{
const char *valid;
if (un->pkt->pkt.user_id->flags.revoked)
valid = _("revoked");
else if (un->pkt->pkt.user_id->flags.expired)
valid = _("expired");
else
/* Since this is just informational, don't
actually ask the user to update any trust
information. */
valid = (trust_value_to_string
(get_validity (c->ctrl, keyblock, mainpk,
un->pkt->pkt.user_id, NULL, 0)));
log_printf (" [%s]\n",valid);
}
else
log_printf ("\n");
}
}
/* For good signatures print notation data. */
if (!rc)
{
if ((opt.verify_options & VERIFY_SHOW_POLICY_URLS))
show_policy_url (sig, 0, 1);
else
show_policy_url (sig, 0, 2);
if ((opt.verify_options & VERIFY_SHOW_KEYSERVER_URLS))
show_keyserver_url (sig, 0, 1);
else
show_keyserver_url (sig, 0, 2);
if ((opt.verify_options & VERIFY_SHOW_NOTATIONS))
show_notation
(sig, 0, 1,
(((opt.verify_options&VERIFY_SHOW_STD_NOTATIONS)?1:0)
+ ((opt.verify_options&VERIFY_SHOW_USER_NOTATIONS)?2:0)));
else
show_notation (sig, 0, 2, 0);
}
/* Fill PKSTRBUF with the algostring in case we later need it. */
if (pk)
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf);
/* For good signatures print the VALIDSIG status line. */
if (!rc && (is_status_enabled ()
|| opt.assert_signer_list
|| opt.assert_pubkey_algos) && pk)
{
char pkhex[MAX_FINGERPRINT_LEN*2+1];
char mainpkhex[MAX_FINGERPRINT_LEN*2+1];
hexfingerprint (pk, pkhex, sizeof pkhex);
hexfingerprint (mainpk, mainpkhex, sizeof mainpkhex);
/* TODO: Replace the reserved '0' in the field below with
bits for status flags (policy url, notation, etc.). */
write_status_printf (STATUS_VALIDSIG,
"%s %s %lu %lu %d 0 %d %d %02X %s",
pkhex,
strtimestamp (sig->timestamp),
(ulong)sig->timestamp,
(ulong)sig->expiredate,
sig->version, sig->pubkey_algo,
sig->digest_algo,
sig->sig_class,
mainpkhex);
/* Handle the --assert-signer option. */
check_assert_signer_list (mainpkhex, pkhex);
/* Handle the --assert-pubkey-algo option. */
check_assert_pubkey_algo (pkstrbuf, pkhex);
}
/* Print compliance warning for Good signatures. */
if (!rc && pk && !opt.quiet
&& !gnupg_pk_is_compliant (opt.compliance, pk->pubkey_algo, 0,
pk->pkey, nbits_from_pk (pk), NULL))
{
log_info (_("WARNING: This key is not suitable for signing"
" in %s mode\n"),
gnupg_compliance_option_string (opt.compliance));
}
/* For good signatures compute and print the trust information.
Note that in the Tofu trust model this may ask the user on
how to resolve a conflict. */
if (!rc)
{
rc = check_signatures_trust (c->ctrl, keyblock, pk, sig);
}
/* Print extra information about the signature. */
if (sig->flags.expired)
{
log_info (_("Signature expired %s\n"), asctimestamp(sig->expiredate));
if (!rc)
rc = gpg_error (GPG_ERR_GENERAL); /* Need a better error here? */
}
else if (sig->expiredate)
log_info (_("Signature expires %s\n"), asctimestamp(sig->expiredate));
if (opt.verbose)
{
log_info (_("%s signature, digest algorithm %s%s%s\n"),
sig->sig_class==0x00?_("binary"):
sig->sig_class==0x01?_("textmode"):_("unknown"),
gcry_md_algo_name (sig->digest_algo),
*pkstrbuf?_(", key algorithm "):"", pkstrbuf);
}
/* Print final warnings. */
if (!rc && !c->signed_data.used)
{
/* Signature is basically good but we test whether the
deprecated command
gpg --verify FILE.sig
was used instead of
gpg --verify FILE.sig FILE
to verify a detached signature. If we figure out that a
data file with a matching name exists, we print a warning.
The problem is that the first form would also verify a
standard signature. This behavior could be used to
create a made up .sig file for a tarball by creating a
standard signature from a valid detached signature packet
(for example from a signed git tag). Then replace the
sig file on the FTP server along with a changed tarball.
Using the first form the verify command would correctly
verify the signature but don't even consider the tarball. */
kbnode_t n;
char *dfile;
dfile = get_matching_datafile (c->sigfilename);
if (dfile)
{
for (n = c->list; n; n = n->next)
if (n->pkt->pkttype != PKT_SIGNATURE)
break;
if (n)
{
/* Not only signature packets in the tree thus this
is not a detached signature. */
log_info (_("WARNING: not a detached signature; "
"file '%s' was NOT verified!\n"), dfile);
assert_signer_true = 0;
}
xfree (dfile);
}
}
/* Compute compliance with CO_DE_VS. */
if (pk
&& gnupg_gcrypt_is_compliant (CO_DE_VS)
&& gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey,
nbits_from_pk (pk), NULL)
&& gnupg_digest_is_compliant (CO_DE_VS, sig->digest_algo))
write_status_strings (STATUS_VERIFICATION_COMPLIANCE_MODE,
gnupg_status_compliance_flag (CO_DE_VS),
NULL);
else if (opt.flags.require_compliance
&& opt.compliance == CO_DE_VS)
{
log_error (_("operation forced to fail due to"
" unfulfilled compliance rules\n"));
if (!rc)
rc = gpg_error (GPG_ERR_FORBIDDEN);
}
free_public_key (pk);
pk = NULL;
release_kbnode( keyblock );
if (rc)
g10_errors_seen = 1;
}
else /* Error checking the signature. (neither Good nor Bad). */
{
write_status_printf (STATUS_ERRSIG, "%08lX%08lX %d %d %02x %lu %d %s",
(ulong)sig->keyid[0], (ulong)sig->keyid[1],
sig->pubkey_algo, sig->digest_algo,
sig->sig_class, (ulong)sig->timestamp,
gpg_err_code (rc),
issuer_fpr? issuer_fpr:"-");
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY)
{
write_status_printf (STATUS_NO_PUBKEY, "%08lX%08lX",
(ulong)sig->keyid[0], (ulong)sig->keyid[1]);
}
if (gpg_err_code (rc) != GPG_ERR_NOT_PROCESSED)
log_error (_("Can't check signature: %s\n"), gpg_strerror (rc));
}
free_public_key (pk);
release_kbnode (included_keyblock);
xfree (issuer_fpr);
return rc;
}
/*
* Process the tree which starts at node
*/
static void
proc_tree (CTX c, kbnode_t node)
{
kbnode_t n1;
int rc;
if (opt.list_packets || opt.list_only)
return;
/* We must skip our special plaintext marker packets here because
they may be the root packet. These packets are only used in
additional checks and skipping them here doesn't matter. */
while (node
&& node->pkt->pkttype == PKT_GPG_CONTROL
&& node->pkt->pkt.gpg_control->control == CTRLPKT_PLAINTEXT_MARK)
{
node = node->next;
}
if (!node)
return;
c->trustletter = ' ';
if (node->pkt->pkttype == PKT_PUBLIC_KEY
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
merge_keys_and_selfsig (c->ctrl, node);
list_node (c, node);
}
else if (node->pkt->pkttype == PKT_SECRET_KEY)
{
merge_keys_and_selfsig (c->ctrl, node);
list_node (c, node);
}
else if (node->pkt->pkttype == PKT_ONEPASS_SIG)
{
/* Check all signatures. */
if (!c->any.data)
{
int use_textmode = 0;
free_md_filter_context (&c->mfx);
/* Prepare to create all requested message digests. */
rc = gcry_md_open (&c->mfx.md, 0, 0);
if (rc)
goto hash_err;
/* Fixme: why looking for the signature packet and not the
one-pass packet? */
for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));)
gcry_md_enable (c->mfx.md, n1->pkt->pkt.signature->digest_algo);
if (n1 && n1->pkt->pkt.onepass_sig->sig_class == 0x01)
use_textmode = 1;
/* Ask for file and hash it. */
if (c->sigs_only)
{
if (c->signed_data.used
&& c->signed_data.data_fd != GNUPG_INVALID_FD)
rc = hash_datafile_by_fd (c->mfx.md, NULL,
c->signed_data.data_fd,
use_textmode);
else
rc = hash_datafiles (c->mfx.md, NULL,
c->signed_data.data_names,
c->sigfilename,
use_textmode);
}
else
{
rc = ask_for_detached_datafile (c->mfx.md, NULL,
iobuf_get_real_fname (c->iobuf),
use_textmode);
}
hash_err:
if (rc)
{
log_error ("can't hash datafile: %s\n", gpg_strerror (rc));
return;
}
}
else if (c->signed_data.used)
{
log_error (_("not a detached signature\n"));
return;
}
for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));)
if (check_sig_and_print (c, n1) && opt.batch
&& !opt.flags.proc_all_sigs)
break;
}
else if (node->pkt->pkttype == PKT_GPG_CONTROL
&& node->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START)
{
/* Clear text signed message. */
if (!c->any.data)
{
log_error ("cleartext signature without data\n");
return;
}
else if (c->signed_data.used)
{
log_error (_("not a detached signature\n"));
return;
}
for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));)
if (check_sig_and_print (c, n1) && opt.batch
&& !opt.flags.proc_all_sigs)
break;
}
else if (node->pkt->pkttype == PKT_SIGNATURE)
{
PKT_signature *sig = node->pkt->pkt.signature;
int multiple_ok = 1;
n1 = find_next_kbnode (node, PKT_SIGNATURE);
if (n1)
{
byte class = sig->sig_class;
byte hash = sig->digest_algo;
for (; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE)))
{
/* We can't currently handle multiple signatures of
* different classes (we'd pretty much have to run a
* different hash context for each), but if they are all
* the same and it is detached signature, we make an
* exception. Note that the old code also disallowed
* multiple signatures if the digest algorithms are
* different. We softened this restriction only for
* detached signatures, to be on the safe side. */
if (n1->pkt->pkt.signature->sig_class != class
|| (c->any.data
&& n1->pkt->pkt.signature->digest_algo != hash))
{
multiple_ok = 0;
log_info (_("WARNING: multiple signatures detected. "
"Only the first will be checked.\n"));
break;
}
}
}
if (sig->sig_class != 0x00 && sig->sig_class != 0x01)
{
log_info(_("standalone signature of class 0x%02x\n"), sig->sig_class);
}
else if (!c->any.data)
{
/* Detached signature */
free_md_filter_context (&c->mfx);
rc = gcry_md_open (&c->mfx.md, sig->digest_algo, 0);
if (rc)
goto detached_hash_err;
if (multiple_ok)
{
/* If we have and want to handle multiple signatures we
* need to enable all hash algorithms for the context. */
for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE)); )
if (!openpgp_md_test_algo (n1->pkt->pkt.signature->digest_algo))
gcry_md_enable (c->mfx.md,
map_md_openpgp_to_gcry
(n1->pkt->pkt.signature->digest_algo));
}
if (RFC2440 || RFC4880)
; /* Strict RFC mode. */
else if (sig->digest_algo == DIGEST_ALGO_SHA1
&& sig->pubkey_algo == PUBKEY_ALGO_DSA
&& sig->sig_class == 0x01)
{
/* Enable a workaround for a pgp5 bug when the detached
* signature has been created in textmode. Note that we
* do not implement this for multiple signatures with
* different hash algorithms. */
rc = gcry_md_open (&c->mfx.md2, sig->digest_algo, 0);
if (rc)
goto detached_hash_err;
}
/* Here we used to have another hack to work around a pgp
* 2 bug: It worked by not using the textmode for detached
* signatures; this would let the first signature check
* (on md) fail but the second one (on md2), which adds an
* extra CR would then have produced the "correct" hash.
* This is very, very ugly hack but it may haved help in
* some cases (and break others).
* c->mfx.md2? 0 :(sig->sig_class == 0x01)
*/
if (DBG_HASHING)
{
gcry_md_debug (c->mfx.md, "verify");
if (c->mfx.md2)
gcry_md_debug (c->mfx.md2, "verify2");
}
if (c->sigs_only)
{
if (c->signed_data.used
&& c->signed_data.data_fd != GNUPG_INVALID_FD)
rc = hash_datafile_by_fd (c->mfx.md, c->mfx.md2,
c->signed_data.data_fd,
(sig->sig_class == 0x01));
else
rc = hash_datafiles (c->mfx.md, c->mfx.md2,
c->signed_data.data_names,
c->sigfilename,
(sig->sig_class == 0x01));
}
else
{
rc = ask_for_detached_datafile (c->mfx.md, c->mfx.md2,
iobuf_get_real_fname(c->iobuf),
(sig->sig_class == 0x01));
}
detached_hash_err:
if (rc)
{
log_error ("can't hash datafile: %s\n", gpg_strerror (rc));
return;
}
}
else if (c->signed_data.used)
{
log_error (_("not a detached signature\n"));
return;
}
else if (!opt.quiet)
log_info (_("old style (PGP 2.x) signature\n"));
if (multiple_ok)
{
for (n1 = node; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE)))
if (check_sig_and_print (c, n1) && opt.batch
&& !opt.flags.proc_all_sigs)
break;
}
else
check_sig_and_print (c, node);
}
else
{
dump_kbnode (c->list);
log_error ("invalid root packet detected in proc_tree()\n");
dump_kbnode (node);
}
}
diff --git a/g10/openfile.c b/g10/openfile.c
index 01f323399..42d470889 100644
--- a/g10/openfile.c
+++ b/g10/openfile.c
@@ -1,405 +1,405 @@
/* openfile.c
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2009,
* 2010 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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "gpg.h"
#include "../common/util.h"
#include "../common/ttyio.h"
#include "options.h"
#include "main.h"
#include "../common/status.h"
#include "../common/i18n.h"
#ifdef HAVE_W32_SYSTEM
#define NAME_OF_DEV_NULL "nul"
#else
#define NAME_OF_DEV_NULL "/dev/null"
#endif
#if defined (HAVE_DRIVE_LETTERS) || defined (__riscos__)
#define CMP_FILENAME(a,b) ascii_strcasecmp( (a), (b) )
#else
#define CMP_FILENAME(a,b) strcmp( (a), (b) )
#endif
/* FIXME: Implement opt.interactive. */
/*
* Check whether FNAME exists and ask if it's okay to overwrite an
* existing one.
* Returns: True: it's okay to overwrite or the file does not exist
* False: Do not overwrite
*/
int
overwrite_filep( const char *fname )
{
if ( iobuf_is_pipe_filename (fname) )
return 1; /* Writing to stdout is always okay. */
if ( gnupg_access( fname, F_OK ) )
return 1; /* Does not exist. */
if ( !compare_filenames (fname, NAME_OF_DEV_NULL) )
return 1; /* Does not do any harm. */
if (opt.answer_yes)
return 1;
if (opt.answer_no || opt.batch)
return 0; /* Do not overwrite. */
tty_printf (_("File '%s' exists. "), fname);
if (cpr_enabled ())
tty_printf ("\n");
if (cpr_get_answer_is_yes ("openfile.overwrite.okay",
_("Overwrite? (y/N) ")) )
return 1;
return 0;
}
/*
* Strip known extensions from iname and return a newly allocated
* filename. Return NULL if we can't do that.
*/
char *
make_outfile_name (const char *iname)
{
size_t n;
if (iobuf_is_pipe_filename (iname))
return xstrdup ("-");
n = strlen (iname);
if (n > 4 && (!CMP_FILENAME(iname+n-4, EXTSEP_S GPGEXT_GPG)
|| !CMP_FILENAME(iname+n-4, EXTSEP_S "pgp")
|| !CMP_FILENAME(iname+n-4, EXTSEP_S "sig")
|| !CMP_FILENAME(iname+n-4, EXTSEP_S "asc")))
{
char *buf = xstrdup (iname);
buf[n-4] = 0;
return buf;
}
else if (n > 5 && !CMP_FILENAME(iname+n-5, EXTSEP_S "sign"))
{
char *buf = xstrdup (iname);
buf[n-5] = 0;
return buf;
}
log_info (_("%s: unknown suffix\n"), iname);
return NULL;
}
/* Ask for an output filename; use the given one as default. Return
NULL if no file has been given or if it is not possible to ask the
user. NAME is the template len which might contain enbedded Nuls.
NAMELEN is its actual length.
*/
char *
ask_outfile_name( const char *name, size_t namelen )
{
size_t n;
const char *s;
char *prompt;
char *fname;
char *defname;
if ( opt.batch )
return NULL;
defname = name && namelen? make_printable_string (name, namelen, 0) : NULL;
s = _("Enter new filename");
n = strlen(s) + (defname?strlen (defname):0) + 10;
prompt = xmalloc (n);
if (defname)
snprintf (prompt, n, "%s [%s]: ", s, defname );
else
snprintf (prompt, n, "%s: ", s );
tty_enable_completion(NULL);
fname = cpr_get ("openfile.askoutname", prompt );
cpr_kill_prompt ();
tty_disable_completion ();
xfree (prompt);
if ( !*fname )
{
xfree (fname);
fname = defname;
defname = NULL;
}
xfree (defname);
if (fname)
trim_spaces (fname);
return fname;
}
/*
* Make an output filename for the inputfile INAME.
- * Returns an IOBUF and an errorcode
+ * Returns an IOBUF at A and an errorcode
* Mode 0 = use ".gpg"
* 1 = use ".asc"
* 2 = use ".sig"
* 3 = use ".rev"
*
* With RESTRICTEDPERM a file will be created with mode 700 if
* possible.
*
* If OUT_FD is not -1 the function simply creates an IOBUF for that
* file descriptor and ignores INAME and MODE. Note that OUT_FD won't
* be closed if the returned IOBUF is closed. This is used for gpg's
* --server mode. */
int
open_outfile (gnupg_fd_t out_fd, const char *iname, int mode,
int restrictedperm, iobuf_t *a)
{
int rc = 0;
*a = NULL;
if (out_fd != GNUPG_INVALID_FD)
{
char xname[64];
*a = iobuf_fdopen_nc (out_fd, "wb");
if (!*a)
{
rc = gpg_error_from_syserror ();
snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (out_fd));
log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (rc));
}
else if (opt.verbose)
{
snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (out_fd));
log_info (_("writing to '%s'\n"), xname);
}
}
else if (iobuf_is_pipe_filename (iname) && !opt.outfile)
{
*a = iobuf_create (NULL, 0);
if ( !*a )
{
rc = gpg_error_from_syserror ();
log_error (_("can't open '%s': %s\n"), "[stdout]", strerror(errno) );
}
else if ( opt.verbose )
log_info (_("writing to stdout\n"));
}
else
{
char *buf = NULL;
const char *name;
if (opt.dry_run)
name = NAME_OF_DEV_NULL;
else if (opt.outfile)
name = opt.outfile;
else
{
#ifdef USE_ONLY_8DOT3
if (opt.mangle_dos_filenames)
{
/* It is quite common for DOS systems to have only one
dot in a filename. If we have something like this,
we simple replace the suffix except in cases where
the suffix is larger than 3 characters and not the
same as the new one. We don't map the filenames to
8.3 because this is a duty of the file system. */
char *dot;
const char *newsfx;
newsfx = (mode==1 ? ".asc" :
mode==2 ? ".sig" :
mode==3 ? ".rev" : ".gpg");
buf = xmalloc (strlen(iname)+4+1);
strcpy (buf, iname);
dot = strchr (buf, '.' );
if ( dot && dot > buf && dot[1] && strlen(dot) <= 4
&& CMP_FILENAME (newsfx, dot) )
strcpy (dot, newsfx);
else if (dot && !dot[1]) /* Do not duplicate a dot. */
strcpy (dot, newsfx+1);
else
strcat (buf, newsfx);
}
if (!buf)
#endif /* USE_ONLY_8DOT3 */
{
buf = xstrconcat (iname,
(mode==1 ? EXTSEP_S "asc" :
mode==2 ? EXTSEP_S "sig" :
mode==3 ? EXTSEP_S "rev" :
/* */ EXTSEP_S GPGEXT_GPG),
NULL);
}
name = buf;
}
rc = 0;
while ( !overwrite_filep (name) )
{
char *tmp = ask_outfile_name (NULL, 0);
if ( !tmp || !*tmp )
{
xfree (tmp);
rc = gpg_error (GPG_ERR_EEXIST);
break;
}
xfree (buf);
name = buf = tmp;
}
if ( !rc )
{
if (is_secured_filename (name) )
{
*a = NULL;
gpg_err_set_errno (EPERM);
}
else
*a = iobuf_create (name, restrictedperm);
if (!*a)
{
rc = gpg_error_from_syserror ();
log_error(_("can't create '%s': %s\n"), name, strerror(errno) );
}
else if( opt.verbose )
log_info (_("writing to '%s'\n"), name );
}
xfree(buf);
}
if (*a)
iobuf_ioctl (*a, IOBUF_IOCTL_NO_CACHE, 1, NULL);
return rc;
}
/* Find a matching data file for the signature file SIGFILENAME and
return it as a malloced string. If no matching data file is found,
return NULL. */
char *
get_matching_datafile (const char *sigfilename)
{
char *fname = NULL;
size_t len;
if (iobuf_is_pipe_filename (sigfilename))
return NULL;
len = strlen (sigfilename);
if (len > 4
&& (!strcmp (sigfilename + len - 4, EXTSEP_S "sig")
|| (len > 5 && !strcmp(sigfilename + len - 5, EXTSEP_S "sign"))
|| !strcmp(sigfilename + len - 4, EXTSEP_S "asc")))
{
fname = xstrdup (sigfilename);
fname[len-(fname[len-1]=='n'?5:4)] = 0 ;
if (gnupg_access (fname, R_OK ))
{
/* Not found or other error. */
xfree (fname);
fname = NULL;
}
}
return fname;
}
/*
* Try to open a file without the extension ".sig" or ".asc"
* Return NULL if such a file is not available.
*/
iobuf_t
open_sigfile (const char *sigfilename, progress_filter_context_t *pfx)
{
iobuf_t a = NULL;
char *buf;
buf = get_matching_datafile (sigfilename);
if (buf)
{
a = iobuf_open (buf);
if (a && is_secured_file (iobuf_get_fd (a)))
{
iobuf_close (a);
a = NULL;
gpg_err_set_errno (EPERM);
}
if (a)
log_info (_("assuming signed data in '%s'\n"), buf);
if (a && pfx)
handle_progress (pfx, a, buf);
xfree (buf);
}
return a;
}
/* Create the directory only if the supplied directory name is the
same as the default one. This way we avoid to create arbitrary
directories when a non-default home directory is used. To cope
with HOME, we do compare only the suffix if we see that the default
homedir does start with a tilde. */
void
try_make_homedir (const char *fname)
{
if ( opt.dry_run || opt.no_homedir_creation )
return;
gnupg_maybe_make_homedir (fname, opt.quiet);
}
/* Get and if needed create a string with the directory used to store
openpgp revocations. */
char *
get_openpgp_revocdir (const char *home)
{
char *fname;
struct stat statbuf;
fname = make_filename (home, GNUPG_OPENPGP_REVOC_DIR, NULL);
if (gnupg_stat (fname, &statbuf) && errno == ENOENT)
{
if (gnupg_mkdir (fname, "-rwx"))
log_error (_("can't create directory '%s': %s\n"),
fname, strerror (errno) );
else if (!opt.quiet)
log_info (_("directory '%s' created\n"), fname);
}
return fname;
}
diff --git a/g10/packet.h b/g10/packet.h
index 80238fcea..5cef17543 100644
--- a/g10/packet.h
+++ b/g10/packet.h
@@ -1,982 +1,989 @@
/* packet.h - OpenPGP packet definitions
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007 Free Software Foundation, Inc.
* Copyright (C) 2015 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/>.
*/
#ifndef G10_PACKET_H
#define G10_PACKET_H
#include "../common/types.h"
#include "../common/iobuf.h"
#include "../common/strlist.h"
#include "dek.h"
#include "filter.h"
#include "../common/openpgpdefs.h"
#include "../common/userids.h"
#include "../common/util.h"
#define DEBUG_PARSE_PACKET 1
/* Maximum length of packets to avoid excessive memory allocation. */
#define MAX_KEY_PACKET_LENGTH (256 * 1024)
#define MAX_UID_PACKET_LENGTH ( 2 * 1024)
#define MAX_COMMENT_PACKET_LENGTH ( 64 * 1024)
#define MAX_ATTR_PACKET_LENGTH ( 16 * 1024*1024)
/* Constants to allocate static MPI arrays. */
#define PUBKEY_MAX_NPKEY OPENPGP_MAX_NPKEY
#define PUBKEY_MAX_NSKEY OPENPGP_MAX_NSKEY
#define PUBKEY_MAX_NSIG OPENPGP_MAX_NSIG
#define PUBKEY_MAX_NENC OPENPGP_MAX_NENC
/* Usage flags */
#define PUBKEY_USAGE_SIG GCRY_PK_USAGE_SIGN /* Good for signatures. */
#define PUBKEY_USAGE_ENC GCRY_PK_USAGE_ENCR /* Good for encryption. */
#define PUBKEY_USAGE_CERT GCRY_PK_USAGE_CERT /* Also good to certify keys.*/
#define PUBKEY_USAGE_AUTH GCRY_PK_USAGE_AUTH /* Good for authentication. */
#define PUBKEY_USAGE_UNKNOWN GCRY_PK_USAGE_UNKN /* Unknown usage flag. */
#define PUBKEY_USAGE_NONE 256 /* No usage given. */
#if (GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR | GCRY_PK_USAGE_CERT \
| GCRY_PK_USAGE_AUTH | GCRY_PK_USAGE_UNKN) >= 256
# error Please choose another value for PUBKEY_USAGE_NONE
#endif
#define PUBKEY_USAGE_GROUP 512 /* Group flag. */
#define PUBKEY_USAGE_RENC 1024 /* Restricted encryption. */
#define PUBKEY_USAGE_TIME 2048 /* Timestamp use. */
/* The usage bits which can be derived from the algo. */
#define PUBKEY_USAGE_BASIC_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC\
|PUBKEY_USAGE_CERT|PUBKEY_USAGE_AUTH)
/* The usage bits which define encryption. */
#define PUBKEY_USAGE_XENC_MASK (PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC)
/* Bitflags to convey hints on what kind of signature is created. */
#define SIGNHINT_KEYSIG 1
#define SIGNHINT_SELFSIG 2
#define SIGNHINT_ADSK 4
/* Helper macros. */
#define is_RSA(a) ((a)==PUBKEY_ALGO_RSA || (a)==PUBKEY_ALGO_RSA_E \
|| (a)==PUBKEY_ALGO_RSA_S )
#define is_ELGAMAL(a) ((a)==PUBKEY_ALGO_ELGAMAL_E)
#define is_DSA(a) ((a)==PUBKEY_ALGO_DSA)
/* A pointer to the packet object. */
typedef struct packet_struct PACKET;
/* PKT_GPG_CONTROL types */
typedef enum {
CTRLPKT_CLEARSIGN_START = 1,
CTRLPKT_PIPEMODE = 2,
CTRLPKT_PLAINTEXT_MARK =3
} ctrlpkttype_t;
typedef enum {
PREFTYPE_NONE = 0,
PREFTYPE_SYM = 1,
PREFTYPE_HASH = 2,
PREFTYPE_ZIP = 3,
PREFTYPE_AEAD = 4
} preftype_t;
typedef struct {
byte type;
byte value;
} prefitem_t;
/* A string-to-key specifier as defined in RFC 4880, Section 3.7. */
typedef struct
{
int mode; /* Must be an integer due to the GNU modes 1001 et al. */
byte hash_algo;
byte salt[8];
/* The *coded* (i.e., the serialized version) iteration count. */
u32 count;
} STRING2KEY;
/* A symmetric-key encrypted session key packet as defined in RFC
4880, Section 5.3. All fields are serialized. */
typedef struct {
/* We support version 4 (rfc4880) and 5 (rfc4880bis). */
byte version;
/* The cipher algorithm used to encrypt the session key. Note that
* this may be different from the algorithm that is used to encrypt
* bulk data. */
byte cipher_algo;
/* The AEAD algorithm or 0 for CFB encryption. */
byte aead_algo;
/* The string-to-key specifier. */
STRING2KEY s2k;
/* The length of SESKEY in bytes or 0 if this packet does not
encrypt a session key. (In the latter case, the results of the
S2K function on the password is the session key. See RFC 4880,
Section 5.3.) */
byte seskeylen;
/* The session key as encrypted by the S2K specifier. For AEAD this
* includes the nonce and the authentication tag. */
byte seskey[1];
} PKT_symkey_enc;
/* A public-key encrypted session key packet as defined in RFC 4880,
Section 5.1. All fields are serialized. */
typedef struct {
/* The 64-bit keyid. */
u32 keyid[2];
/* The packet's version. Currently, only version 3 is defined. */
byte version;
/* The algorithm used for the public key encryption scheme. */
byte pubkey_algo;
/* The session key algorithm used by some pubkey algos. */
byte seskey_algo;
/* Whether to hide the key id. This value is not directly
serialized. */
byte throw_keyid;
/* The session key. */
gcry_mpi_t data[PUBKEY_MAX_NENC];
} PKT_pubkey_enc;
/* An object to build a list of public-key encrypted session key. */
struct pubkey_enc_list
{
struct pubkey_enc_list *next;
int result;
PKT_pubkey_enc d;
};
/* A one-pass signature packet as defined in RFC 4880, Section
5.4. All fields are serialized. */
typedef struct {
u32 keyid[2]; /* The 64-bit keyid */
/* The signature's classification (RFC 4880, Section 5.2.1). */
byte sig_class;
byte digest_algo; /* algorithm used for digest */
byte pubkey_algo; /* algorithm used for public key scheme */
/* A message can be signed by multiple keys. In this case, there
are n one-pass signature packets before the message to sign and
n signatures packets after the message. It is conceivable that
someone wants to not only sign the message, but all of the
signatures. Now we need to distinguish between signing the
message and signing the message plus the surrounding
signatures. This is the point of this flag. If set, it means:
I sign all of the data starting at the next packet. */
byte last;
} PKT_onepass_sig;
/* A v4 OpenPGP signature has a hashed and unhashed area containing
co-called signature subpackets (RFC 4880, Section 5.2.3). These
areas are described by this data structure. Use enum_sig_subpkt to
parse this area. */
typedef struct {
size_t size; /* allocated */
size_t len; /* used (serialized) */
byte data[1]; /* the serialized subpackes (serialized) */
} subpktarea_t;
/* The in-memory representation of a designated revoker signature
subpacket (RFC 4880, Section 5.2.3.15). */
struct revocation_key {
/* A bit field. 0x80 must be set. 0x40 means this information is
sensitive (and should not be uploaded to a keyserver by
default). */
byte class;
/* The public-key algorithm ID. */
byte algid;
/* The length of the fingerprint. */
byte fprlen;
/* The fingerprint of the authorized key. */
byte fpr[MAX_FINGERPRINT_LEN];
};
/* A signature packet (RFC 4880, Section 5.2). Only a subset of these
fields are directly serialized (these are marked as such); the rest
are read from the subpackets, which are not synthesized when
serializing this data structure (i.e., when using build_packet()).
Instead, the subpackets must be created by hand. */
typedef struct
{
struct
{
unsigned checked:1; /* Signature has been checked. */
unsigned valid:1; /* Signature is good (if checked is set). */
unsigned chosen_selfsig:1; /* A selfsig that is the chosen one. */
unsigned unknown_critical:1;
unsigned exportable:1;
unsigned revocable:1;
unsigned policy_url:1; /* At least one policy URL is present */
unsigned notation:1; /* At least one notation is present */
unsigned pref_ks:1; /* At least one preferred keyserver is present */
unsigned key_block:1; /* A key block subpacket is present. */
unsigned expired:1;
} flags;
/* The key that allegedly generated this signature. (Directly
serialized in v3 sigs; for v4 sigs, this must be explicitly added
as an issuer subpacket (5.2.3.5.) */
u32 keyid[2];
/* When the signature was made (seconds since the Epoch). (Directly
serialized in v3 sigs; for v4 sigs, this must be explicitly added
as a signature creation time subpacket (5.2.3.4).) */
u32 timestamp;
u32 expiredate; /* Expires at this date or 0 if not at all. */
/* The serialization format used / to use. If 0, then defaults to
version 3. (Serialized.) */
byte version;
/* The signature type. (See RFC 4880, Section 5.2.1.) */
byte sig_class;
/* Algorithm used for public key scheme (e.g., PUBKEY_ALGO_RSA).
(Serialized.) */
byte pubkey_algo;
/* Algorithm used for digest (e.g., DIGEST_ALGO_SHA1).
(Serialized.) */
byte digest_algo;
byte trust_depth;
byte trust_value;
const byte *trust_regexp;
struct revocation_key *revkey;
int numrevkeys;
int help_counter; /* Used internally by some functions. */
char *signers_uid; /* Malloced value of the SIGNERS_UID
* subpacket or NULL. This string has
* already been sanitized. */
subpktarea_t *hashed; /* All subpackets with hashed data (v4 only). */
subpktarea_t *unhashed; /* Ditto for unhashed data. */
/* First 2 bytes of the digest. (Serialized. Note: this is not
automatically filled in when serializing a signature!) */
byte digest_start[2];
/* The signature. (Serialized.) */
gcry_mpi_t data[PUBKEY_MAX_NSIG];
/* The message digest and its length (in bytes). Note the maximum
digest length is 512 bits (64 bytes). If DIGEST_LEN is 0, then
the digest's value has not been saved here. */
byte digest[512 / 8];
int digest_len;
} PKT_signature;
#define ATTRIB_IMAGE 1
/* This is the cooked form of attributes. */
struct user_attribute {
byte type;
const byte *data;
u32 len;
};
/* A user id (RFC 4880, Section 5.11) or a user attribute packet (RFC
4880, Section 5.12). Only a subset of these fields are directly
serialized (these are marked as such); the rest are read from the
self-signatures in merge_keys_and_selfsig()). */
typedef struct
{
int ref; /* reference counter */
/* The length of NAME. */
int len;
struct user_attribute *attribs;
int numattribs;
/* If this is not NULL, the packet is a user attribute rather than a
user id (See RFC 4880 5.12). (Serialized.) */
byte *attrib_data;
/* The length of ATTRIB_DATA. */
unsigned long attrib_len;
byte *namehash;
u16 help_key_usage;
u32 help_key_expire;
int help_full_count;
int help_marginal_count;
u32 expiredate; /* expires at this date or 0 if not at all */
prefitem_t *prefs; /* list of preferences (may be NULL)*/
u32 created; /* according to the self-signature */
u32 keyupdate; /* From the ring trust packet. */
char *updateurl; /* NULL or the URL of the last update origin. */
byte keyorg; /* From the ring trust packet. */
byte selfsigversion;
struct
{
unsigned int mdc:1;
unsigned int aead:1;
unsigned int ks_modify:1;
unsigned int compacted:1;
unsigned int primary:2; /* 2 if set via the primary flag, 1 if calculated */
/* Note that this flag is set in a
* keyblock at max for one User ID and for
* one User Attribute per keyblock. */
unsigned int revoked:1;
unsigned int expired:1;
} flags;
char *mbox; /* NULL or the result of mailbox_from_userid. */
/* The text contained in the user id packet, which is normally the
* name and email address of the key holder (See RFC 4880 5.11).
* (Serialized.). For convenience an extra Nul is always appended. */
char name[1];
} PKT_user_id;
struct revoke_info
{
/* revoked at this date */
u32 date;
/* the keyid of the revoking key (selfsig or designated revoker) */
u32 keyid[2];
/* the algo of the revoking key */
byte algo;
};
/* Information pertaining to secret keys. */
struct seckey_info
{
unsigned int is_protected:1; /* The secret info is protected and must */
/* be decrypted before use, the protected */
/* MPIs are simply (void*) pointers to memory */
/* and should never be passed to a mpi_xxx() */
unsigned int sha1chk:1; /* SHA1 is used instead of a 16 bit checksum */
u16 csum; /* Checksum for old protection modes. */
byte algo; /* Cipher used to protect the secret information. */
STRING2KEY s2k; /* S2K parameter. */
byte ivlen; /* Used length of the IV. */
byte iv[16]; /* Initialization vector for CFB mode. */
};
/****************
* The in-memory representation of a public key (RFC 4880, Section
* 5.5). Note: this structure contains significantly more information
* than is contained in an OpenPGP public key packet. This
* information is derived from the self-signed signatures (by
* merge_keys_and_selfsig()) and is ignored when serializing the
* packet. The fields that are actually written out when serializing
* this packet are marked as accordingly.
*
* We assume that secret keys have the same number of parameters as
* the public key and that the public parameters are the first items
* in the PKEY array. Thus NPKEY is always less than NSKEY and it is
* possible to compare the secret and public keys by comparing the
* first NPKEY elements of the PKEY array. Note that since GnuPG 2.1
* we don't use secret keys anymore directly because they are managed
* by gpg-agent. However for parsing OpenPGP key files we need a way
* to temporary store those secret keys. We do this by putting them
* into the public key structure and extending the PKEY field to NSKEY
* elements; the extra secret key information are stored in the
* SECKEY_INFO field.
*/
typedef struct
{
/* When the key was created. (Serialized.) */
u32 timestamp;
u32 expiredate; /* expires at this date or 0 if not at all */
u32 max_expiredate; /* must not expire past this date */
struct revoke_info revoked;
/* An OpenPGP packet consists of a header and a body. This is the
size of the header. If this is 0, an appropriate size is
automatically chosen based on the size of the body.
(Serialized.) */
byte hdrbytes;
/* The serialization format. If 0, the default version (4) is used
when serializing. (Serialized.) */
byte version;
byte selfsigversion; /* highest version of all of the self-sigs */
/* The public key algorithm. (Serialized.) */
byte pubkey_algo;
u16 pubkey_usage; /* carries the usage info. */
byte req_usage; /* hack to pass a request to getkey() */
byte fprlen; /* 0 or length of FPR. */
u32 has_expired; /* set to the expiration date if expired */
/* keyid of the primary key. Never access this value directly.
Instead, use pk_main_keyid(). */
u32 main_keyid[2];
/* keyid of this key. Never access this value directly! Instead,
use pk_keyid(). */
u32 keyid[2];
/* Fingerprint of the key. Only valid if FPRLEN is not 0. */
byte fpr[MAX_FINGERPRINT_LEN];
prefitem_t *prefs; /* list of preferences (may be NULL) */
struct
{
unsigned int mdc:1; /* MDC feature set. */
unsigned int aead:1; /* AEAD feature set. */
unsigned int disabled_valid:1;/* The next flag is valid. */
unsigned int disabled:1; /* The key has been disabled. */
unsigned int primary:1; /* This is a primary key. */
unsigned int revoked:2; /* Key has been revoked.
1 = revoked by the owner
2 = revoked by designated revoker. */
unsigned int maybe_revoked:1; /* A designated revocation is
present, but without the key to
check it. */
unsigned int valid:1; /* Key (especially subkey) is valid. */
unsigned int dont_cache:1; /* Do not cache this key. */
unsigned int backsig:2; /* 0=none, 1=bad, 2=good. */
unsigned int serialno_valid:1;/* SERIALNO below is valid. */
unsigned int exact:1; /* Found via exact (!) search. */
} flags;
PKT_user_id *user_id; /* If != NULL: found by that uid. */
struct revocation_key *revkey;
int numrevkeys;
u32 trust_timestamp;
byte trust_depth;
byte trust_value;
byte keyorg; /* From the ring trust packet. */
u32 keyupdate; /* From the ring trust packet. */
char *updateurl; /* NULL or the URL of the last update origin. */
const byte *trust_regexp;
char *serialno; /* Malloced hex string or NULL if it is
likely not on a card. See also
flags.serialno_valid. */
/* If not NULL this malloced structure describes a secret key.
(Serialized.) */
struct seckey_info *seckey_info;
/* The public key. Contains pubkey_get_npkey (pubkey_algo) +
pubkey_get_nskey (pubkey_algo) MPIs. (If pubkey_get_npkey
returns 0, then the algorithm is not understood and the PKEY
contains a single opaque MPI.) (Serialized.) */
gcry_mpi_t pkey[PUBKEY_MAX_NSKEY]; /* Right, NSKEY elements. */
} PKT_public_key;
/* Evaluates as true if the pk is disabled, and false if it isn't. If
there is no disable value cached, fill one in. */
#define pk_is_disabled(a) \
(((a)->flags.disabled_valid)? \
((a)->flags.disabled):(cache_disabled_value(ctrl,(a))))
typedef struct {
int len; /* length of data */
char data[1];
} PKT_comment;
/* A compression packet (RFC 4880, Section 5.6). */
typedef struct {
/* Not used. */
u32 len;
/* Whether the serialized version of the packet used / should use
the new format. */
byte new_ctb;
/* The compression algorithm. */
byte algorithm;
/* An iobuf holding the data to be decompressed. (This is not used
for compression!) */
iobuf_t buf;
} PKT_compressed;
/* A symmetrically encrypted data packet (RFC 4880, Section 5.7) or a
symmetrically encrypted integrity protected data packet (Section
5.13) */
typedef struct {
/* Remaining length of encrypted data. */
u32 len;
/* When encrypting in CFB mode, the first block size bytes of data
* are random data and the following 2 bytes are copies of the last
* two bytes of the random data (RFC 4880, Section 5.7). This
* provides a simple check that the key is correct. EXTRALEN is the
* size of this extra data or, in AEAD mode, the length of the
* headers and the tags. This is used by build_packet when writing
* out the packet's header. */
int extralen;
/* Whether the serialized version of the packet used / should use
the new format. */
byte new_ctb;
/* Whether the packet has an indeterminate length (old format) or
was encoded using partial body length headers (new format).
Note: this is ignored when encrypting. */
byte is_partial;
/* If 0, MDC is disabled. Otherwise, the MDC method that was used
(only DIGEST_ALGO_SHA1 has ever been defined). */
byte mdc_method;
/* If 0, AEAD is not used. Otherwise, the used AEAD algorithm.
* MDC_METHOD (above) shall be zero if AEAD is used. */
byte aead_algo;
/* The cipher algo for/from the AEAD packet. 0 for other encryption
* packets. */
byte cipher_algo;
/* The chunk byte from the AEAD packet. */
byte chunkbyte;
/* An iobuf holding the data to be decrypted. (This is not used for
encryption!) */
iobuf_t buf;
} PKT_encrypted;
typedef struct {
byte hash[20];
} PKT_mdc;
/* Subtypes for the ring trust packet. */
#define RING_TRUST_SIG 0 /* The classical signature cache. */
#define RING_TRUST_KEY 1 /* A KEYORG on a primary key. */
#define RING_TRUST_UID 2 /* A KEYORG on a user id. */
/* The local only ring trust packet which OpenPGP declares as
* implementation defined. GnuPG uses this to cache signature
* verification status and since 2.1.18 also to convey information
* about the origin of a key. Note that this packet is not part
* struct packet_struct because we use it only local in the packet
* parser and builder. */
typedef struct {
unsigned int trustval;
unsigned int sigcache;
unsigned char subtype; /* The subtype of this ring trust packet. */
unsigned char keyorg; /* The origin of the key (KEYORG_*). */
u32 keyupdate; /* The wall time the key was last updated. */
char *url; /* NULL or the URL of the source. */
} PKT_ring_trust;
/* A plaintext packet (see RFC 4880, 5.9). */
typedef struct {
/* The length of data in BUF or 0 if unknown. */
u32 len;
/* A buffer containing the data stored in the packet's body. */
iobuf_t buf;
byte new_ctb;
byte is_partial; /* partial length encoded */
/* The data's formatting. This is either 'b', 't', 'u', 'l' or '1'
(however, the last two are deprecated). */
int mode;
u32 timestamp;
/* The name of the file. This can be at most 255 characters long,
since namelen is just a byte in the serialized format. */
int namelen;
char name[1];
} PKT_plaintext;
typedef struct {
int control;
size_t datalen;
char data[1];
} PKT_gpg_control;
/* combine all packets into a union */
struct packet_struct {
pkttype_t pkttype;
union {
void *generic;
PKT_symkey_enc *symkey_enc; /* PKT_SYMKEY_ENC */
PKT_pubkey_enc *pubkey_enc; /* PKT_PUBKEY_ENC */
PKT_onepass_sig *onepass_sig; /* PKT_ONEPASS_SIG */
PKT_signature *signature; /* PKT_SIGNATURE */
PKT_public_key *public_key; /* PKT_PUBLIC_[SUB]KEY */
PKT_public_key *secret_key; /* PKT_SECRET_[SUB]KEY */
PKT_comment *comment; /* PKT_COMMENT */
PKT_user_id *user_id; /* PKT_USER_ID */
PKT_compressed *compressed; /* PKT_COMPRESSED */
PKT_encrypted *encrypted; /* PKT_ENCRYPTED[_MDC] */
PKT_mdc *mdc; /* PKT_MDC */
PKT_plaintext *plaintext; /* PKT_PLAINTEXT */
PKT_gpg_control *gpg_control; /* PKT_GPG_CONTROL */
} pkt;
};
#define init_packet(a) do { (a)->pkttype = 0; \
(a)->pkt.generic = NULL; \
} while(0)
/* A notation. See RFC 4880, Section 5.2.3.16. */
struct notation
{
/* The notation's name. */
char *name;
/* If the notation is human readable, then the value is stored here
as a NUL-terminated string. If it is not human readable a human
readable approximation of the binary value _may_ be stored
here. */
char *value;
/* Sometimes we want to %-expand the value. In these cases, we save
that transformed value here. */
char *altvalue;
/* If the notation is not human readable or the function does not
want to distinguish that, then the value is stored here. */
unsigned char *bdat;
/* The amount of data stored in BDAT.
Note: if this is 0 and BDAT is NULL, this does not necessarily
mean that the value is human readable. It could be that we have
a 0-length value. To determine whether the notation is human
readable, always check if VALUE is not NULL. This works, because
if a human-readable value has a length of 0, we will still
allocate space for the NUL byte. */
size_t blen;
struct
{
/* The notation is critical. */
unsigned int critical:1;
/* The notation is human readable. */
unsigned int human:1;
/* The notation should be deleted. */
unsigned int ignore:1;
} flags;
/* A field to facilitate creating a list of notations. */
struct notation *next;
};
typedef struct notation *notation_t;
/*-- mainproc.c --*/
void reset_literals_seen(void);
int proc_packets (ctrl_t ctrl, void *ctx, iobuf_t a );
int proc_signature_packets (ctrl_t ctrl, void *ctx, iobuf_t a,
strlist_t signedfiles, const char *sigfile );
int proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, IOBUF a,
gnupg_fd_t signed_data_fd);
-int proc_encryption_packets (ctrl_t ctrl, void *ctx, iobuf_t a);
+gpg_error_t proc_encryption_packets (ctrl_t ctrl, void *ctx, iobuf_t a,
+ DEK **r_dek,
+ struct pubkey_enc_list **r_list);
+
int list_packets( iobuf_t a );
const byte *issuer_fpr_raw (PKT_signature *sig, size_t *r_len);
char *issuer_fpr_string (PKT_signature *sig);
/*-- parse-packet.c --*/
void register_known_notation (const char *string);
/* Sets the packet list mode to MODE (i.e., whether we are dumping a
packet or not). Returns the current mode. This allows for
temporarily suspending dumping by doing the following:
int saved_mode = set_packet_list_mode (0);
...
set_packet_list_mode (saved_mode);
*/
int set_packet_list_mode( int mode );
/* A context used with parse_packet. */
struct parse_packet_ctx_s
{
iobuf_t inp; /* The input stream with the packets. */
struct packet_struct last_pkt; /* The last parsed packet. */
int free_last_pkt; /* Indicates that LAST_PKT must be freed. */
int skip_meta; /* Skip ring trust packets. */
+ int only_fookey_enc; /* Stop if the packet is not {sym,pub}key_enc. */
unsigned int n_parsed_packets; /* Number of parsed packets. */
+ int last_ctb; /* The last CTB read. */
};
typedef struct parse_packet_ctx_s *parse_packet_ctx_t;
#define init_parse_packet(a,i) do { \
(a)->inp = (i); \
(a)->last_pkt.pkttype = 0; \
(a)->last_pkt.pkt.generic= NULL;\
(a)->free_last_pkt = 0; \
(a)->skip_meta = 0; \
+ (a)->only_fookey_enc = 0; \
(a)->n_parsed_packets = 0; \
+ (a)->last_ctb = 1; \
} while (0)
#define deinit_parse_packet(a) do { \
if ((a)->free_last_pkt) \
free_packet (NULL, (a)); \
} while (0)
#if DEBUG_PARSE_PACKET
/* There are debug functions and should not be used directly. */
int dbg_search_packet (parse_packet_ctx_t ctx, PACKET *pkt,
off_t *retpos, int with_uid,
const char* file, int lineno );
int dbg_parse_packet (parse_packet_ctx_t ctx, PACKET *ret_pkt,
const char *file, int lineno);
int dbg_copy_all_packets( iobuf_t inp, iobuf_t out,
const char* file, int lineno );
int dbg_copy_some_packets( iobuf_t inp, iobuf_t out, off_t stopoff,
const char* file, int lineno );
int dbg_skip_some_packets( iobuf_t inp, unsigned n,
const char* file, int lineno );
#define search_packet( a,b,c,d ) \
dbg_search_packet( (a), (b), (c), (d), __FILE__, __LINE__ )
#define parse_packet( a, b ) \
dbg_parse_packet( (a), (b), __FILE__, __LINE__ )
#define copy_all_packets( a,b ) \
dbg_copy_all_packets((a),(b), __FILE__, __LINE__ )
#define copy_some_packets( a,b,c ) \
dbg_copy_some_packets((a),(b),(c), __FILE__, __LINE__ )
#define skip_some_packets( a,b ) \
dbg_skip_some_packets((a),(b), __FILE__, __LINE__ )
#else
/* Return the next valid OpenPGP packet in *PKT. (This function will
* skip any packets whose type is 0.) CTX must have been setup prior to
* calling this function.
*
* Returns 0 on success, -1 if EOF is reached, and an error code
* otherwise. In the case of an error, the packet in *PKT may be
* partially constructed. As such, even if there is an error, it is
* necessary to free *PKT to avoid a resource leak. To detect what
* has been allocated, clear *PKT before calling this function. */
int parse_packet (parse_packet_ctx_t ctx, PACKET *pkt);
/* Return the first OpenPGP packet in *PKT that contains a key (either
* a public subkey, a public key, a secret subkey or a secret key) or,
* if WITH_UID is set, a user id.
*
* Saves the position in the pipeline of the start of the returned
* packet (according to iobuf_tell) in RETPOS, if it is not NULL.
*
* The return semantics are the same as parse_packet. */
int search_packet (parse_packet_ctx_t ctx, PACKET *pkt,
off_t *retpos, int with_uid);
/* Copy all packets (except invalid packets, i.e., those with a type
* of 0) from INP to OUT until either an error occurs or EOF is
* reached.
*
* Returns -1 when end of file is reached or an error code, if an
* error occurred. (Note: this function never returns 0, because it
* effectively keeps going until it gets an EOF.) */
int copy_all_packets (iobuf_t inp, iobuf_t out );
/* Like copy_all_packets, but stops at the first packet that starts at
* or after STOPOFF (as indicated by iobuf_tell).
*
* Example: if STOPOFF is 100, the first packet in INP goes from
* 0 to 110 and the next packet starts at offset 111, then the packet
* starting at offset 0 will be completely processed (even though it
* extends beyond STOPOFF) and the packet starting at offset 111 will
* not be processed at all. */
int copy_some_packets (iobuf_t inp, iobuf_t out, off_t stopoff);
/* Skips the next N packets from INP.
*
* If parsing a packet returns an error code, then the function stops
* immediately and returns the error code. Note: in the case of an
* error, this function does not indicate how many packets were
* successfully processed. */
int skip_some_packets (iobuf_t inp, unsigned int n);
#endif
/* Parse a signature packet and store it in *SIG.
The signature packet is read from INP. The OpenPGP header (the tag
and the packet's length) have already been read; the next byte read
from INP should be the first byte of the packet's contents. The
packet's type (as extract from the tag) must be passed as PKTTYPE
and the packet's length must be passed as PKTLEN. This is used as
the upper bound on the amount of data read from INP. If the packet
is shorter than PKTLEN, the data at the end will be silently
skipped. If an error occurs, an error code will be returned. -1
means the EOF was encountered. 0 means parsing was successful. */
int parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen,
PKT_signature *sig );
/* Given a signature packet, either:
*
* - test whether there are any subpackets with the critical bit set
* that we don't understand,
*
* - list the subpackets, or,
*
* - find a subpacket with a specific type.
*
* The WANT_HASHED flag indicates that the hashed area shall be
* considered.
*
* REQTYPE indicates the type of operation.
*
* If REQTYPE is SIGSUBPKT_TEST_CRITICAL, then this function checks
* whether there are any subpackets that have the critical bit and
* which GnuPG cannot handle. If GnuPG understands all subpackets
* whose critical bit is set, then this function returns simply
* returns SUBPKTS. If there is a subpacket whose critical bit is set
* and which GnuPG does not understand, then this function returns
* NULL and, if START is not NULL, sets *START to the 1-based index of
* the subpacket that violates the constraint.
*
* If REQTYPE is SIGSUBPKT_LIST_HASHED or SIGSUBPKT_LIST_UNHASHED, the
* packets are dumped. Note: if REQTYPE is SIGSUBPKT_LIST_HASHED,
* this function does not check whether the hash is correct; this is
* merely an indication of the section that the subpackets came from.
*
* If REQTYPE is anything else, then this function interprets the
* values as a subpacket type and looks for the first subpacket with
* that type. If such a packet is found, *CRITICAL (if not NULL) is
* set if the critical bit was set, *RET_N is set to the offset of the
* subpacket's content within the SUBPKTS buffer, *START is set to the
* 1-based index of the subpacket within the buffer, and returns
* &SUBPKTS[*RET_N].
*
* *START is the number of initial subpackets to not consider. Thus,
* if *START is 2, then the first 2 subpackets are ignored.
*/
const byte *enum_sig_subpkt (PKT_signature *sig, int want_hashed,
sigsubpkttype_t reqtype,
size_t *ret_n, int *start, int *critical );
/* Shorthand for:
*
* enum_sig_subpkt (sig, want_hashed, reqtype, ret_n, NULL, NULL);
*/
const byte *parse_sig_subpkt (PKT_signature *sig, int want_hashed,
sigsubpkttype_t reqtype,
size_t *ret_n );
/* This calls parse_sig_subpkt first on the hashed signature area in
* SIG and then, if that returns NULL, calls parse_sig_subpkt on the
* unhashed subpacket area in SIG. */
const byte *parse_sig_subpkt2 (PKT_signature *sig, sigsubpkttype_t reqtype);
/* Returns whether the N byte large buffer BUFFER is sufficient to
hold a subpacket of type TYPE. Note: the buffer refers to the
contents of the subpacket (not the header) and it must already be
initialized: for some subpackets, it checks some internal
constraints.
Returns 0 if the size is acceptable. Returns -2 if the buffer is
definitely too short. To check for an error, check whether the
return value is less than 0. */
int parse_one_sig_subpkt( const byte *buffer, size_t n, int type );
/* Looks for revocation key subpackets (see RFC 4880 5.2.3.15) in the
hashed area of the signature packet. Any that are found are added
to SIG->REVKEY and SIG->NUMREVKEYS is updated appropriately. */
void parse_revkeys(PKT_signature *sig);
/* Extract the attributes from the buffer at UID->ATTRIB_DATA and
update UID->ATTRIBS and UID->NUMATTRIBS accordingly. */
int parse_attribute_subpkts(PKT_user_id *uid);
/* Set the UID->NAME field according to the attributes. MAX_NAMELEN
must be at least 71. */
void make_attribute_uidname(PKT_user_id *uid, size_t max_namelen);
/* Allocate and initialize a new GPG control packet. DATA is the data
to save in the packet. */
PACKET *create_gpg_control ( ctrlpkttype_t type,
const byte *data,
size_t datalen );
/*-- build-packet.c --*/
gpg_error_t build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf);
int build_packet (iobuf_t out, PACKET *pkt);
gpg_error_t build_packet_and_meta (iobuf_t out, PACKET *pkt);
gpg_error_t gpg_mpi_write (iobuf_t out, gcry_mpi_t a, unsigned int *t_nwritten);
gpg_error_t gpg_mpi_write_opaque_nohdr (iobuf_t out, gcry_mpi_t a);
gpg_error_t gpg_mpi_write_opaque_32 (iobuf_t out, gcry_mpi_t a,
unsigned int *r_nwritten);
u32 calc_packet_length( PACKET *pkt );
void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type,
const byte *buffer, size_t buflen );
void build_sig_subpkt_from_sig (PKT_signature *sig, PKT_public_key *pksk,
unsigned int signhints);
int delete_sig_subpkt(subpktarea_t *buffer, sigsubpkttype_t type );
void build_attribute_subpkt(PKT_user_id *uid,byte type,
const void *buf,u32 buflen,
const void *header,u32 headerlen);
struct notation *string_to_notation(const char *string,int is_utf8);
struct notation *blob_to_notation(const char *name,
const char *data, size_t len);
struct notation *sig_to_notation(PKT_signature *sig);
struct notation *search_sig_notations (PKT_signature *sig, const char *name);
void free_notation (struct notation *notation);
/*-- free-packet.c --*/
void free_symkey_enc( PKT_symkey_enc *enc );
void release_pubkey_enc_parts (PKT_pubkey_enc *enc);
void free_pubkey_enc( PKT_pubkey_enc *enc );
void copy_pubkey_enc_parts (PKT_pubkey_enc *dst, PKT_pubkey_enc *src);
void free_seckey_enc( PKT_signature *enc );
void release_public_key_parts( PKT_public_key *pk );
void free_public_key( PKT_public_key *key );
void free_attributes(PKT_user_id *uid);
void free_user_id( PKT_user_id *uid );
void free_comment( PKT_comment *rem );
void free_packet (PACKET *pkt, parse_packet_ctx_t parsectx);
prefitem_t *copy_prefs (const prefitem_t *prefs);
PKT_public_key *copy_public_key_basics (PKT_public_key *d, PKT_public_key *s);
PKT_public_key *copy_public_key( PKT_public_key *d, PKT_public_key *s );
PKT_signature *copy_signature( PKT_signature *d, PKT_signature *s );
PKT_user_id *scopy_user_id (PKT_user_id *sd );
int cmp_public_keys( PKT_public_key *a, PKT_public_key *b );
int cmp_signatures( PKT_signature *a, PKT_signature *b );
int cmp_user_ids( PKT_user_id *a, PKT_user_id *b );
/*-- sig-check.c --*/
/* Check a signature. This is shorthand for check_signature2 with
the unnamed arguments passed as NULL. */
int check_signature (ctrl_t ctrl, PKT_signature *sig, gcry_md_hd_t digest);
/* Check a signature. Looks up the public key from the key db. (If
* R_PK is not NULL, it is stored at RET_PK.) DIGEST contains a
* valid hash context that already includes the signed data. This
* function adds the relevant meta-data to the hash before finalizing
* it and verifying the signature. FOCRED_PK is usually NULL. */
gpg_error_t check_signature2 (ctrl_t ctrl,
PKT_signature *sig, gcry_md_hd_t digest,
const void *extrahash, size_t extrahashlen,
PKT_public_key *forced_pk,
u32 *r_expiredate, int *r_expired, int *r_revoked,
PKT_public_key **r_pk);
/*-- pubkey-enc.c --*/
gpg_error_t get_session_key (ctrl_t ctrl, struct pubkey_enc_list *k, DEK *dek);
gpg_error_t get_override_session_key (DEK *dek, const char *string);
/*-- compress.c --*/
int handle_compressed (ctrl_t ctrl, void *ctx, PKT_compressed *cd,
int (*callback)(iobuf_t, void *), void *passthru );
/*-- decrypt-data.c --*/
int decrypt_data (ctrl_t ctrl, void *ctx, PKT_encrypted *ed, DEK *dek,
int *compliance_error);
/*-- plaintext.c --*/
gpg_error_t get_output_file (const byte *embedded_name, int embedded_namelen,
iobuf_t data, char **fnamep, estream_t *fpp);
int handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx,
int nooutput, int clearsig );
int ask_for_detached_datafile( gcry_md_hd_t md, gcry_md_hd_t md2,
const char *inname, int textmode );
/*-- sign.c --*/
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);
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 );
/*-- keygen.c --*/
PKT_user_id *generate_user_id (kbnode_t keyblock, const char *uidstr);
#endif /*G10_PACKET_H*/
diff --git a/g10/parse-packet.c b/g10/parse-packet.c
index 8bd283b4b..a16f70fea 100644
--- a/g10/parse-packet.c
+++ b/g10/parse-packet.c
@@ -1,3962 +1,3969 @@
/* parse-packet.c - read packets
* Copyright (C) 1998-2007, 2009-2010 Free Software Foundation, Inc.
* Copyright (C) 2014, 2018 Werner Koch
* Copyright (C) 2015 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+
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gpg.h"
#include "../common/util.h"
#include "packet.h"
#include "../common/iobuf.h"
#include "filter.h"
#include "photoid.h"
#include "options.h"
#include "main.h"
#include "../common/i18n.h"
#include "../common/host2net.h"
#include "../common/mbox-util.h"
static int mpi_print_mode;
static int list_mode;
static estream_t listfp;
/* A linked list of known notation names. Note that the FLAG is used
* to store the length of the name to speed up the check. */
static strlist_t known_notations_list;
static int parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts,
off_t * retpos, int *skip, IOBUF out, int do_skip
#if DEBUG_PARSE_PACKET
, const char *dbg_w, const char *dbg_f, int dbg_l
#endif
);
static int copy_packet (IOBUF inp, IOBUF out, int pkttype,
unsigned long pktlen, int partial);
static void skip_packet (IOBUF inp, int pkttype,
unsigned long pktlen, int partial);
static void *read_rest (IOBUF inp, size_t pktlen);
static int parse_marker (IOBUF inp, int pkttype, unsigned long pktlen);
static int parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet);
static int parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet);
static int parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen,
PKT_onepass_sig * ops);
static int parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
byte * hdr, int hdrlen, PACKET * packet);
static int parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet);
static int parse_attribute (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet);
static int parse_comment (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet);
static gpg_error_t parse_ring_trust (parse_packet_ctx_t ctx,
unsigned long pktlen);
static int parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int new_ctb, int partial);
static int parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int new_ctb);
static int parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int new_ctb, int partial);
static gpg_error_t parse_encrypted_aead (IOBUF inp, int pkttype,
unsigned long pktlen, PACKET *packet,
int partial);
static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int new_ctb);
static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int partial);
/* Read a 16-bit value in MSB order (big endian) from an iobuf. */
static unsigned short
read_16 (IOBUF inp)
{
unsigned short a;
a = (unsigned short)iobuf_get_noeof (inp) << 8;
a |= iobuf_get_noeof (inp);
return a;
}
/* Read a 32-bit value in MSB order (big endian) from an iobuf. */
static unsigned long
read_32 (IOBUF inp)
{
unsigned long a;
a = (unsigned long)iobuf_get_noeof (inp) << 24;
a |= iobuf_get_noeof (inp) << 16;
a |= iobuf_get_noeof (inp) << 8;
a |= iobuf_get_noeof (inp);
return a;
}
/* Read an external representation of an MPI and return the MPI. The
external format is a 16-bit unsigned value stored in network byte
order giving the number of bits for the following integer. The
integer is stored MSB first and is left padded with zero bits to
align on a byte boundary.
The caller must set *RET_NREAD to the maximum number of bytes to
read from the pipeline INP. This function sets *RET_NREAD to be
the number of bytes actually read from the pipeline.
If SECURE is true, the integer is stored in secure memory
(allocated using gcry_xmalloc_secure). */
static gcry_mpi_t
mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
{
int c, c1, c2, i;
unsigned int nmax = *ret_nread;
unsigned int nbits, nbytes;
size_t nread = 0;
gcry_mpi_t a = NULL;
byte *buf = NULL;
byte *p;
if (!nmax)
goto overflow;
if ((c = c1 = iobuf_get (inp)) == -1)
goto leave;
if (++nread == nmax)
goto overflow;
nbits = c << 8;
if ((c = c2 = iobuf_get (inp)) == -1)
goto leave;
++nread;
nbits |= c;
if (nbits > MAX_EXTERN_MPI_BITS)
{
log_error ("mpi too large (%u bits)\n", nbits);
goto leave;
}
nbytes = (nbits + 7) / 8;
buf = secure ? gcry_xmalloc_secure (nbytes + 2) : gcry_xmalloc (nbytes + 2);
p = buf;
p[0] = c1;
p[1] = c2;
for (i = 0; i < nbytes; i++)
{
if (nread == nmax)
goto overflow;
c = iobuf_get (inp);
if (c == -1)
goto leave;
p[i + 2] = c;
nread ++;
}
if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread))
a = NULL;
*ret_nread = nread;
gcry_free(buf);
return a;
overflow:
log_error ("mpi larger than indicated length (%u bits)\n", 8*nmax);
leave:
*ret_nread = nread;
gcry_free(buf);
return a;
}
/* If NLENGTH is zero read an octet string of length NBYTES from INP
* and return it at R_DATA.
*
* If NLENGTH is either 1, 2, or 4 and NLENGTH is zero read an
* NLENGTH-octet count and use this count number octets from INP and
* return it at R_DATA.
*
* On error return an error code and store NULL at R_DATA. PKTLEN
* shall give the current length of the packet and is updated with
* each read. If SECURE is true, the integer is stored in secure
* memory (allocated using gcry_xmalloc_secure).
*/
static gpg_error_t
read_octet_string (iobuf_t inp, unsigned long *pktlen,
unsigned int nlength, unsigned int nbytes,
int secure, gcry_mpi_t *r_data)
{
gpg_error_t err;
int c, i;
byte *buf = NULL;
byte *p;
*r_data = NULL;
if ((nbytes && nlength)
|| (!nbytes && !(nlength == 1 || nlength == 2 || nlength == 4)))
{
err = gpg_error (GPG_ERR_INV_ARG);
goto leave;
}
if (nlength)
{
for (i = 0; i < nlength; i++)
{
if (!*pktlen)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
c = iobuf_readbyte (inp);
if (c < 0)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
--*pktlen;
nbytes <<= 8;
nbytes |= c;
}
if (!nbytes)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
}
if (nbytes*8 > (nbytes==4? MAX_EXTERN_KEYPARM_BITS:MAX_EXTERN_MPI_BITS)
|| (nbytes*8 < nbytes))
{
log_error ("octet string too large (%u octets)\n", nbytes);
err = gpg_error (GPG_ERR_TOO_LARGE);
goto leave;
}
if (nbytes > *pktlen)
{
log_error ("octet string larger than packet (%u octets)\n", nbytes);
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
buf = secure ? gcry_malloc_secure (nbytes) : gcry_malloc (nbytes);
if (!buf)
{
err = gpg_error_from_syserror ();
goto leave;
}
p = buf;
for (i = 0; i < nbytes; i++)
{
c = iobuf_get (inp);
if (c == -1)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
p[i] = c;
--*pktlen;
}
*r_data = gcry_mpi_set_opaque (NULL, buf, nbytes*8);
gcry_mpi_set_flag (*r_data, GCRYMPI_FLAG_USER2);
return 0;
leave:
gcry_free (buf);
return err;
}
/* Read an external representation of an SOS and return the opaque MPI
with GCRYMPI_FLAG_USER2. The external format is a 16-bit unsigned
value stored in network byte order giving information for the
following octets.
The caller must set *RET_NREAD to the maximum number of bytes to
read from the pipeline INP. This function sets *RET_NREAD to be
the number of bytes actually read from the pipeline.
If SECURE is true, the integer is stored in secure memory
(allocated using gcry_xmalloc_secure). */
static gcry_mpi_t
sos_read (iobuf_t inp, unsigned int *ret_nread, int secure)
{
int c, c1, c2, i;
unsigned int nmax = *ret_nread;
unsigned int nbits, nbytes;
size_t nread = 0;
gcry_mpi_t a = NULL;
byte *buf = NULL;
byte *p;
if (!nmax)
goto overflow;
if ((c = c1 = iobuf_get (inp)) == -1)
goto leave;
if (++nread == nmax)
goto overflow;
nbits = c << 8;
if ((c = c2 = iobuf_get (inp)) == -1)
goto leave;
++nread;
nbits |= c;
if (nbits > MAX_EXTERN_MPI_BITS)
{
log_error ("mpi too large (%u bits)\n", nbits);
goto leave;
}
nbytes = (nbits + 7) / 8;
buf = secure ? gcry_xmalloc_secure (nbytes) : gcry_xmalloc (nbytes);
p = buf;
for (i = 0; i < nbytes; i++)
{
if (nread == nmax)
goto overflow;
c = iobuf_get (inp);
if (c == -1)
goto leave;
p[i] = c;
nread ++;
}
a = gcry_mpi_set_opaque (NULL, buf, nbits);
gcry_mpi_set_flag (a, GCRYMPI_FLAG_USER2);
*ret_nread = nread;
return a;
overflow:
log_error ("mpi larger than indicated length (%u bits)\n", 8*nmax);
leave:
*ret_nread = nread;
gcry_free(buf);
return a;
}
/* Register STRING as a known critical notation name. */
void
register_known_notation (const char *string)
{
strlist_t sl;
if (!known_notations_list)
{
sl = add_to_strlist (&known_notations_list,
"preferred-email-encoding@pgp.com");
sl->flags = 32; /* Length of the string. */
}
if (!string)
return; /* Only initialized the default known notations. */
/* In --set-notation we use an exclamation mark to indicate a
* critical notation. As a convenience skip this here. */
if (*string == '!')
string++;
if (!*string || strlist_find (known_notations_list, string))
return; /* Empty string or already registered. */
sl = add_to_strlist (&known_notations_list, string);
sl->flags = strlen (string);
}
int
set_packet_list_mode (int mode)
{
int old = list_mode;
list_mode = mode;
/* We use stdout only if invoked by the --list-packets command
but switch to stderr in all other cases. This breaks the
previous behaviour but that seems to be more of a bug than
intentional. I don't believe that any application makes use of
this long standing annoying way of printing to stdout except when
doing a --list-packets. If this assumption fails, it will be easy
to add an option for the listing stream. Note that we initialize
it only once; mainly because there is code which switches
opt.list_mode back to 1 and we want to have all output to the
same stream. The MPI_PRINT_MODE will be enabled if the
corresponding debug flag is set or if we are in --list-packets
and --verbose is given.
Using stderr is not actually very clean because it bypasses the
logging code but it is a special thing anyway. I am not sure
whether using log_stream() would be better. Perhaps we should
enable the list mode only with a special option. */
if (!listfp)
{
if (opt.list_packets)
{
listfp = es_stdout;
if (opt.verbose)
mpi_print_mode = 1;
}
else
listfp = es_stderr;
if (DBG_MPI)
mpi_print_mode = 1;
}
return old;
}
/* If OPT.VERBOSE is set, print a warning that the algorithm ALGO is
not suitable for signing and encryption. */
static void
unknown_pubkey_warning (int algo)
{
static byte unknown_pubkey_algos[256];
/* First check whether the algorithm is usable but not suitable for
encryption/signing. */
if (pubkey_get_npkey (algo))
{
if (opt.verbose && !glo_ctrl.silence_parse_warnings)
{
if (!pubkey_get_nsig (algo))
log_info ("public key algorithm %s not suitable for %s\n",
openpgp_pk_algo_name (algo), "signing");
if (!pubkey_get_nenc (algo))
log_info ("public key algorithm %s not suitable for %s\n",
openpgp_pk_algo_name (algo), "encryption");
}
}
else
{
algo &= 0xff;
if (!unknown_pubkey_algos[algo])
{
if (opt.verbose && !glo_ctrl.silence_parse_warnings)
log_info (_("can't handle public key algorithm %d\n"), algo);
unknown_pubkey_algos[algo] = 1;
}
}
}
#if DEBUG_PARSE_PACKET
int
dbg_parse_packet (parse_packet_ctx_t ctx, PACKET *pkt,
const char *dbg_f, int dbg_l)
{
int skip, rc;
do
{
rc = parse (ctx, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l);
}
while (skip && ! rc);
return rc;
}
#else /*!DEBUG_PARSE_PACKET*/
int
parse_packet (parse_packet_ctx_t ctx, PACKET *pkt)
{
int skip, rc;
do
{
rc = parse (ctx, pkt, 0, NULL, &skip, NULL, 0);
}
while (skip && ! rc);
return rc;
}
#endif /*!DEBUG_PARSE_PACKET*/
/*
* Like parse packet, but only return secret or public (sub)key
* packets.
*/
#if DEBUG_PARSE_PACKET
int
dbg_search_packet (parse_packet_ctx_t ctx, PACKET *pkt,
off_t * retpos, int with_uid,
const char *dbg_f, int dbg_l)
{
int skip, rc;
do
{
rc = parse (ctx, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0, "search",
dbg_f, dbg_l);
}
while (skip && ! rc);
return rc;
}
#else /*!DEBUG_PARSE_PACKET*/
int
search_packet (parse_packet_ctx_t ctx, PACKET *pkt,
off_t * retpos, int with_uid)
{
int skip, rc;
do
{
rc = parse (ctx, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0);
}
while (skip && ! rc);
return rc;
}
#endif /*!DEBUG_PARSE_PACKET*/
/*
* Copy all packets from INP to OUT, thereby removing unused spaces.
*/
#if DEBUG_PARSE_PACKET
int
dbg_copy_all_packets (iobuf_t inp, iobuf_t out, const char *dbg_f, int dbg_l)
{
PACKET pkt;
struct parse_packet_ctx_s parsectx;
int skip, rc = 0;
if (! out)
log_bug ("copy_all_packets: OUT may not be NULL.\n");
init_parse_packet (&parsectx, inp);
do
{
init_packet (&pkt);
}
while (!
(rc =
parse (&parsectx, &pkt, 0, NULL, &skip, out, 0, "copy",
dbg_f, dbg_l)));
deinit_parse_packet (&parsectx);
return rc;
}
#else /*!DEBUG_PARSE_PACKET*/
int
copy_all_packets (iobuf_t inp, iobuf_t out)
{
PACKET pkt;
struct parse_packet_ctx_s parsectx;
int skip, rc = 0;
if (! out)
log_bug ("copy_all_packets: OUT may not be NULL.\n");
init_parse_packet (&parsectx, inp);
do
{
init_packet (&pkt);
}
while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0)));
deinit_parse_packet (&parsectx);
return rc;
}
#endif /*!DEBUG_PARSE_PACKET*/
/*
* Copy some packets from INP to OUT, thereby removing unused spaces.
* Stop at offset STOPoff (i.e. don't copy packets at this or later
* offsets)
*/
#if DEBUG_PARSE_PACKET
int
dbg_copy_some_packets (iobuf_t inp, iobuf_t out, off_t stopoff,
const char *dbg_f, int dbg_l)
{
int rc = 0;
PACKET pkt;
int skip;
struct parse_packet_ctx_s parsectx;
init_parse_packet (&parsectx, inp);
do
{
if (iobuf_tell (inp) >= stopoff)
{
deinit_parse_packet (&parsectx);
return 0;
}
init_packet (&pkt);
}
while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0,
"some", dbg_f, dbg_l)));
deinit_parse_packet (&parsectx);
return rc;
}
#else /*!DEBUG_PARSE_PACKET*/
int
copy_some_packets (iobuf_t inp, iobuf_t out, off_t stopoff)
{
int rc = 0;
PACKET pkt;
struct parse_packet_ctx_s parsectx;
int skip;
init_parse_packet (&parsectx, inp);
do
{
if (iobuf_tell (inp) >= stopoff)
{
deinit_parse_packet (&parsectx);
return 0;
}
init_packet (&pkt);
}
while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0)));
deinit_parse_packet (&parsectx);
return rc;
}
#endif /*!DEBUG_PARSE_PACKET*/
/*
* Skip over N packets
*/
#if DEBUG_PARSE_PACKET
int
dbg_skip_some_packets (iobuf_t inp, unsigned n, const char *dbg_f, int dbg_l)
{
int rc = 0;
int skip;
PACKET pkt;
struct parse_packet_ctx_s parsectx;
init_parse_packet (&parsectx, inp);
for (; n && !rc; n--)
{
init_packet (&pkt);
rc = parse (&parsectx, &pkt, 0, NULL, &skip, NULL, 1, "skip",
dbg_f, dbg_l);
}
deinit_parse_packet (&parsectx);
return rc;
}
#else /*!DEBUG_PARSE_PACKET*/
int
skip_some_packets (iobuf_t inp, unsigned int n)
{
int rc = 0;
int skip;
PACKET pkt;
struct parse_packet_ctx_s parsectx;
init_parse_packet (&parsectx, inp);
for (; n && !rc; n--)
{
init_packet (&pkt);
rc = parse (&parsectx, &pkt, 0, NULL, &skip, NULL, 1);
}
deinit_parse_packet (&parsectx);
return rc;
}
#endif /*!DEBUG_PARSE_PACKET*/
/* Parse a packet and save it in *PKT.
If OUT is not NULL and the packet is valid (its type is not 0),
then the header, the initial length field and the packet's contents
are written to OUT. In this case, the packet is not saved in *PKT.
ONLYKEYPKTS is a simple packet filter. If ONLYKEYPKTS is set to 1,
then only public subkey packets, public key packets, private subkey
packets and private key packets are parsed. The rest are skipped
(i.e., the header and the contents are read from the pipeline and
discarded). If ONLYKEYPKTS is set to 2, then in addition to the
above 4 types of packets, user id packets are also accepted.
DO_SKIP is a more coarse grained filter. Unless ONLYKEYPKTS is set
to 2 and the packet is a user id packet, all packets are skipped.
Finally, if a packet is invalid (it's type is 0), it is skipped.
If a packet is skipped and SKIP is not NULL, then *SKIP is set to
1.
Note: ONLYKEYPKTS and DO_SKIP are only respected if OUT is NULL,
i.e., the packets are not simply being copied.
If RETPOS is not NULL, then the position of CTX->INP (as returned by
iobuf_tell) is saved there before any data is read from CTX->INP.
*/
static int
parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
int *skip, IOBUF out, int do_skip
#if DEBUG_PARSE_PACKET
, const char *dbg_w, const char *dbg_f, int dbg_l
#endif
)
{
int rc = 0;
iobuf_t inp;
int c, ctb, pkttype, lenbytes;
unsigned long pktlen;
byte hdr[8];
int hdrlen;
int new_ctb = 0, partial = 0;
int with_uid = (onlykeypkts == 2);
off_t pos;
*skip = 0;
inp = ctx->inp;
again:
log_assert (!pkt->pkt.generic);
if (retpos || list_mode)
{
pos = iobuf_tell (inp);
if (retpos)
*retpos = pos;
}
else
pos = 0; /* (silence compiler warning) */
/* The first byte of a packet is the so-called tag. The highest bit
must be set. */
if ((ctb = iobuf_get (inp)) == -1)
{
rc = -1;
goto leave;
}
+ ctx->last_ctb = ctb;
hdrlen = 0;
hdr[hdrlen++] = ctb;
if (!(ctb & 0x80))
{
log_error ("%s: invalid packet (ctb=%02x)\n", iobuf_where (inp), ctb);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
/* Immediately following the header is the length. There are two
- formats: the old format and the new format. If bit 6 (where the
- least significant bit is bit 0) is set in the tag, then we are
- dealing with a new format packet. Otherwise, it is an old format
- packet. */
+ * formats: the old format and the new format. If bit 6 (where the
+ * least significant bit is bit 0) is set in the tag, then we are
+ * dealing with a new format packet. Otherwise, it is an old format
+ * packet. In the new format the packet's type is encoded in the 6
+ * least significant bits of the tag; in the old format it is
+ * encoded in bits 2-5. */
pktlen = 0;
new_ctb = !!(ctb & 0x40);
if (new_ctb)
+ pkttype = ctb & 0x3f;
+ else
+ pkttype = (ctb >> 2) & 0xf;
+
+ if (ctx->only_fookey_enc
+ && !(pkttype == PKT_SYMKEY_ENC || pkttype == PKT_PUBKEY_ENC))
{
- /* Get the packet's type. This is encoded in the 6 least
- significant bits of the tag. */
- pkttype = ctb & 0x3f;
+ rc = gpg_error (GPG_ERR_TRUE);
+ goto leave;
+ }
+ if (new_ctb)
+ {
/* Extract the packet's length. New format packets have 4 ways
to encode the packet length. The value of the first byte
determines the encoding and partially determines the length.
See section 4.2.2 of RFC 4880 for details. */
if ((c = iobuf_get (inp)) == -1)
{
log_error ("%s: 1st length byte missing\n", iobuf_where (inp));
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
hdr[hdrlen++] = c;
if (c < 192)
pktlen = c;
else if (c < 224)
{
pktlen = (c - 192) * 256;
if ((c = iobuf_get (inp)) == -1)
{
log_error ("%s: 2nd length byte missing\n",
iobuf_where (inp));
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
hdr[hdrlen++] = c;
pktlen += c + 192;
}
else if (c == 255)
{
int i;
char value[4];
for (i = 0; i < 4; i ++)
{
if ((c = iobuf_get (inp)) == -1)
{
log_error ("%s: 4 byte length invalid\n", iobuf_where (inp));
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
value[i] = hdr[hdrlen++] = c;
}
pktlen = buf32_to_ulong (value);
}
else /* Partial body length. */
{
switch (pkttype)
{
case PKT_PLAINTEXT:
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD:
case PKT_COMPRESSED:
iobuf_set_partial_body_length_mode (inp, c & 0xff);
pktlen = 0; /* To indicate partial length. */
partial = 1;
break;
default:
log_error ("%s: partial length invalid for"
" packet type %d\n", iobuf_where (inp), pkttype);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
}
}
- else
- /* This is an old format packet. */
+ else /* This is an old format packet. */
{
- /* Extract the packet's type. This is encoded in bits 2-5. */
- pkttype = (ctb >> 2) & 0xf;
-
/* The type of length encoding is encoded in bits 0-1 of the
tag. */
lenbytes = ((ctb & 3) == 3) ? 0 : (1 << (ctb & 3));
if (!lenbytes)
{
pktlen = 0; /* Don't know the value. */
/* This isn't really partial, but we can treat it the same
in a "read until the end" sort of way. */
partial = 1;
if (pkttype != PKT_ENCRYPTED && pkttype != PKT_PLAINTEXT
&& pkttype != PKT_COMPRESSED)
{
log_error ("%s: indeterminate length for invalid"
" packet type %d\n", iobuf_where (inp), pkttype);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
}
else
{
for (; lenbytes; lenbytes--)
{
pktlen <<= 8;
c = iobuf_get (inp);
if (c == -1)
{
log_error ("%s: length invalid\n", iobuf_where (inp));
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
pktlen |= hdr[hdrlen++] = c;
}
}
}
/* Sometimes the decompressing layer enters an error state in which
it simply outputs 0xff for every byte read. If we have a stream
of 0xff bytes, then it will be detected as a new format packet
with type 63 and a 4-byte encoded length that is 4G-1. Since
packets with type 63 are private and we use them as a control
packet, which won't be 4 GB, we reject such packets as
invalid. */
if (pkttype == 63 && pktlen == 0xFFFFFFFF)
{
/* With some probability this is caused by a problem in the
* the uncompressing layer - in some error cases it just loops
* and spits out 0xff bytes. */
log_error ("%s: garbled packet detected\n", iobuf_where (inp));
g10_exit (2);
}
if (out && pkttype)
{
/* This type of copying won't work if the packet uses a partial
body length. (In other words, this only works if HDR is
actually the length.) Currently, no callers require this
functionality so we just log this as an error. */
if (partial)
{
log_error ("parse: Can't copy partial packet. Aborting.\n");
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
rc = iobuf_write (out, hdr, hdrlen);
if (!rc)
rc = copy_packet (inp, out, pkttype, pktlen, partial);
goto leave;
}
if (with_uid && pkttype == PKT_USER_ID)
/* If ONLYKEYPKTS is set to 2, then we never skip user id packets,
even if DO_SKIP is set. */
;
else if (do_skip
/* type==0 is not allowed. This is an invalid packet. */
|| !pkttype
/* When ONLYKEYPKTS is set, we don't skip keys. */
|| (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY
&& pkttype != PKT_PUBLIC_KEY
&& pkttype != PKT_SECRET_SUBKEY && pkttype != PKT_SECRET_KEY))
{
iobuf_skip_rest (inp, pktlen, partial);
*skip = 1;
rc = 0;
goto leave;
}
if (DBG_PACKET)
{
#if DEBUG_PARSE_PACKET
log_debug ("parse_packet(iob=%d): type=%d length=%lu%s (%s.%s.%d)\n",
iobuf_id (inp), pkttype, pktlen, new_ctb ? " (new_ctb)" : "",
dbg_w, dbg_f, dbg_l);
#else
log_debug ("parse_packet(iob=%d): type=%d length=%lu%s\n",
iobuf_id (inp), pkttype, pktlen,
new_ctb ? " (new_ctb)" : "");
#endif
}
if (list_mode)
es_fprintf (listfp, "# off=%lu ctb=%02x tag=%d hlen=%d plen=%lu%s%s\n",
(unsigned long)pos, ctb, pkttype, hdrlen, pktlen,
partial? (new_ctb ? " partial" : " indeterminate") :"",
new_ctb? " new-ctb":"");
/* Count it. */
ctx->n_parsed_packets++;
pkt->pkttype = pkttype;
rc = GPG_ERR_UNKNOWN_PACKET; /* default error */
switch (pkttype)
{
case PKT_PUBLIC_KEY:
case PKT_PUBLIC_SUBKEY:
case PKT_SECRET_KEY:
case PKT_SECRET_SUBKEY:
pkt->pkt.public_key = xmalloc_clear (sizeof *pkt->pkt.public_key);
rc = parse_key (inp, pkttype, pktlen, hdr, hdrlen, pkt);
break;
case PKT_SYMKEY_ENC:
rc = parse_symkeyenc (inp, pkttype, pktlen, pkt);
break;
case PKT_PUBKEY_ENC:
rc = parse_pubkeyenc (inp, pkttype, pktlen, pkt);
break;
case PKT_SIGNATURE:
pkt->pkt.signature = xmalloc_clear (sizeof *pkt->pkt.signature);
rc = parse_signature (inp, pkttype, pktlen, pkt->pkt.signature);
break;
case PKT_ONEPASS_SIG:
pkt->pkt.onepass_sig = xmalloc_clear (sizeof *pkt->pkt.onepass_sig);
rc = parse_onepass_sig (inp, pkttype, pktlen, pkt->pkt.onepass_sig);
break;
case PKT_USER_ID:
rc = parse_user_id (inp, pkttype, pktlen, pkt);
break;
case PKT_ATTRIBUTE:
pkt->pkttype = pkttype = PKT_USER_ID; /* we store it in the userID */
rc = parse_attribute (inp, pkttype, pktlen, pkt);
break;
case PKT_OLD_COMMENT:
case PKT_COMMENT:
rc = parse_comment (inp, pkttype, pktlen, pkt);
break;
case PKT_RING_TRUST:
{
rc = parse_ring_trust (ctx, pktlen);
if (!rc)
goto again; /* Directly read the next packet. */
}
break;
case PKT_PLAINTEXT:
rc = parse_plaintext (inp, pkttype, pktlen, pkt, new_ctb, partial);
break;
case PKT_COMPRESSED:
rc = parse_compressed (inp, pkttype, pktlen, pkt, new_ctb);
break;
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
rc = parse_encrypted (inp, pkttype, pktlen, pkt, new_ctb, partial);
break;
case PKT_MDC:
rc = parse_mdc (inp, pkttype, pktlen, pkt, new_ctb);
break;
case PKT_ENCRYPTED_AEAD:
rc = parse_encrypted_aead (inp, pkttype, pktlen, pkt, partial);
break;
case PKT_GPG_CONTROL:
rc = parse_gpg_control (inp, pkttype, pktlen, pkt, partial);
break;
case PKT_MARKER:
rc = parse_marker (inp, pkttype, pktlen);
break;
default:
/* Unknown packet. Skip it. */
skip_packet (inp, pkttype, pktlen, partial);
break;
}
/* Store a shallow copy of certain packets in the context. */
free_packet (NULL, ctx);
if (!rc && (pkttype == PKT_PUBLIC_KEY
|| pkttype == PKT_SECRET_KEY
|| pkttype == PKT_USER_ID
|| pkttype == PKT_ATTRIBUTE
|| pkttype == PKT_SIGNATURE))
{
ctx->last_pkt = *pkt;
}
leave:
/* FIXME: We leak in case of an error (see the xmalloc's above). */
if (!rc && iobuf_error (inp))
rc = GPG_ERR_INV_KEYRING;
/* FIXME: We use only the error code for now to avoid problems with
callers which have not been checked to always use gpg_err_code()
when comparing error codes. */
return rc == -1? -1 : gpg_err_code (rc);
}
static void
dump_hex_line (int c, int *i)
{
if (*i && !(*i % 8))
{
if (*i && !(*i % 24))
es_fprintf (listfp, "\n%4d:", *i);
else
es_putc (' ', listfp);
}
if (c == -1)
es_fprintf (listfp, " EOF");
else
es_fprintf (listfp, " %02x", c);
++*i;
}
/* Copy the contents of a packet from the pipeline IN to the pipeline
OUT.
The header and length have already been read from INP and the
decoded values are given as PKGTYPE and PKTLEN.
If the packet is a partial body length packet (RFC 4880, Section
4.2.2.4), then iobuf_set_partial_block_modeiobuf_set_partial_block_mode
should already have been called on INP and PARTIAL should be set.
If PARTIAL is set or PKTLEN is 0 and PKTTYPE is PKT_COMPRESSED,
copy until the first EOF is encountered on INP.
Returns 0 on success and an error code if an error occurs. */
static int
copy_packet (IOBUF inp, IOBUF out, int pkttype,
unsigned long pktlen, int partial)
{
int rc;
int n;
char buf[100];
if (partial)
{
while ((n = iobuf_read (inp, buf, sizeof (buf))) != -1)
if ((rc = iobuf_write (out, buf, n)))
return rc; /* write error */
}
else if (!pktlen && pkttype == PKT_COMPRESSED)
{
log_debug ("copy_packet: compressed!\n");
/* compressed packet, copy till EOF */
while ((n = iobuf_read (inp, buf, sizeof (buf))) != -1)
if ((rc = iobuf_write (out, buf, n)))
return rc; /* write error */
}
else
{
for (; pktlen; pktlen -= n)
{
n = pktlen > sizeof (buf) ? sizeof (buf) : pktlen;
n = iobuf_read (inp, buf, n);
if (n == -1)
return gpg_error (GPG_ERR_EOF);
if ((rc = iobuf_write (out, buf, n)))
return rc; /* write error */
}
}
return 0;
}
/* Skip an unknown packet. PKTTYPE is the packet's type, PKTLEN is
the length of the packet's content and PARTIAL is whether partial
body length encoding in used (in this case PKTLEN is ignored). */
static void
skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial)
{
if (list_mode)
{
es_fprintf (listfp, ":unknown packet: type %2d, length %lu\n",
pkttype, pktlen);
if (pkttype)
{
int c, i = 0;
es_fputs ("dump:", listfp);
if (partial)
{
while ((c = iobuf_get (inp)) != -1)
dump_hex_line (c, &i);
}
else
{
for (; pktlen; pktlen--)
{
dump_hex_line ((c = iobuf_get (inp)), &i);
if (c == -1)
break;
}
}
es_putc ('\n', listfp);
return;
}
}
iobuf_skip_rest (inp, pktlen, partial);
}
/* Read PKTLEN bytes from INP and return them in a newly allocated
* buffer. In case of an error (including reading fewer than PKTLEN
* bytes from INP before EOF is returned), NULL is returned and an
* error message is logged. */
static void *
read_rest (IOBUF inp, size_t pktlen)
{
int c;
byte *buf, *p;
buf = xtrymalloc (pktlen);
if (!buf)
{
gpg_error_t err = gpg_error_from_syserror ();
log_error ("error reading rest of packet: %s\n", gpg_strerror (err));
return NULL;
}
for (p = buf; pktlen; pktlen--)
{
c = iobuf_get (inp);
if (c == -1)
{
log_error ("premature eof while reading rest of packet\n");
xfree (buf);
return NULL;
}
*p++ = c;
}
return buf;
}
/* Read a special size+body from INP. On success store an opaque MPI
* with it at R_DATA. The caller shall store the remaining size of
* the packet at PKTLEN. On error return an error code and store NULL
* at R_DATA. Even in the error case store the number of read bytes
* at PKTLEN is updated. */
static gpg_error_t
read_sized_octet_string (iobuf_t inp, unsigned long *pktlen, gcry_mpi_t *r_data)
{
char buffer[256];
char *tmpbuf;
int i, c, nbytes;
*r_data = NULL;
if (!*pktlen)
return gpg_error (GPG_ERR_INV_PACKET);
c = iobuf_readbyte (inp);
if (c < 0)
return gpg_error (GPG_ERR_INV_PACKET);
--*pktlen;
nbytes = c;
if (nbytes < 2 || nbytes > 254)
return gpg_error (GPG_ERR_INV_PACKET);
if (nbytes > *pktlen)
return gpg_error (GPG_ERR_INV_PACKET);
buffer[0] = nbytes;
for (i = 0; i < nbytes; i++)
{
c = iobuf_get (inp);
if (c < 0)
return gpg_error (GPG_ERR_INV_PACKET);
--*pktlen;
buffer[1+i] = c;
}
tmpbuf = xtrymalloc (1 + nbytes);
if (!tmpbuf)
return gpg_error_from_syserror ();
memcpy (tmpbuf, buffer, 1 + nbytes);
*r_data = gcry_mpi_set_opaque (NULL, tmpbuf, 8 * (1 + nbytes));
if (!*r_data)
{
xfree (tmpbuf);
return gpg_error_from_syserror ();
}
return 0;
}
/* Parse a marker packet. */
static int
parse_marker (IOBUF inp, int pkttype, unsigned long pktlen)
{
(void) pkttype;
if (pktlen != 3)
goto fail;
if (iobuf_get (inp) != 'P')
{
pktlen--;
goto fail;
}
if (iobuf_get (inp) != 'G')
{
pktlen--;
goto fail;
}
if (iobuf_get (inp) != 'P')
{
pktlen--;
goto fail;
}
if (list_mode)
es_fputs (":marker packet: PGP\n", listfp);
return 0;
fail:
log_error ("invalid marker packet\n");
if (list_mode)
es_fputs (":marker packet: [invalid]\n", listfp);
iobuf_skip_rest (inp, pktlen, 0);
return GPG_ERR_INV_PACKET;
}
static int
parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet)
{
PKT_symkey_enc *k;
int rc = 0;
int i, version, s2kmode, cipher_algo, aead_algo, hash_algo, seskeylen, minlen;
if (pktlen < 4)
goto too_short;
version = iobuf_get_noeof (inp);
pktlen--;
if (version == 4)
;
else if (version == 5)
;
else
{
log_error ("packet(%d) with unknown version %d\n", pkttype, version);
if (list_mode)
es_fprintf (listfp, ":symkey enc packet: [unknown version]\n");
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
if (pktlen > 200)
{ /* (we encode the seskeylen in a byte) */
log_error ("packet(%d) too large\n", pkttype);
if (list_mode)
es_fprintf (listfp, ":symkey enc packet: [too large]\n");
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
cipher_algo = iobuf_get_noeof (inp);
pktlen--;
if (version == 5)
{
aead_algo = iobuf_get_noeof (inp);
pktlen--;
}
else
aead_algo = 0;
if (pktlen < 2)
goto too_short;
s2kmode = iobuf_get_noeof (inp);
pktlen--;
hash_algo = iobuf_get_noeof (inp);
pktlen--;
switch (s2kmode)
{
case 0: /* Simple S2K. */
minlen = 0;
break;
case 1: /* Salted S2K. */
minlen = 8;
break;
case 3: /* Iterated+salted S2K. */
minlen = 9;
break;
default:
log_error ("unknown S2K mode %d\n", s2kmode);
if (list_mode)
es_fprintf (listfp, ":symkey enc packet: [unknown S2K mode]\n");
goto leave;
}
if (minlen > pktlen)
{
log_error ("packet with S2K %d too short\n", s2kmode);
if (list_mode)
es_fprintf (listfp, ":symkey enc packet: [too short]\n");
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
seskeylen = pktlen - minlen;
k = packet->pkt.symkey_enc = xmalloc_clear (sizeof *packet->pkt.symkey_enc
+ seskeylen - 1);
k->version = version;
k->cipher_algo = cipher_algo;
k->aead_algo = aead_algo;
k->s2k.mode = s2kmode;
k->s2k.hash_algo = hash_algo;
if (s2kmode == 1 || s2kmode == 3)
{
for (i = 0; i < 8 && pktlen; i++, pktlen--)
k->s2k.salt[i] = iobuf_get_noeof (inp);
}
if (s2kmode == 3)
{
k->s2k.count = iobuf_get_noeof (inp);
pktlen--;
}
k->seskeylen = seskeylen;
if (k->seskeylen)
{
for (i = 0; i < seskeylen && pktlen; i++, pktlen--)
k->seskey[i] = iobuf_get_noeof (inp);
/* What we're watching out for here is a session key decryptor
with no salt. The RFC says that using salt for this is a
MUST. */
if (s2kmode != 1 && s2kmode != 3)
log_info (_("WARNING: potentially insecure symmetrically"
" encrypted session key\n"));
}
log_assert (!pktlen);
if (list_mode)
{
es_fprintf (listfp,
":symkey enc packet: version %d, cipher %d, aead %d,"
" s2k %d, hash %d",
version, cipher_algo, aead_algo, s2kmode, hash_algo);
if (seskeylen)
{
/* To compute the size of the session key we need to know
* the size of the AEAD nonce which we may not know. Thus
* we show only the size of the entire encrypted session
* key. */
if (aead_algo)
es_fprintf (listfp, ", encrypted seskey %d bytes", seskeylen);
else
es_fprintf (listfp, ", seskey %d bits", (seskeylen - 1) * 8);
}
es_fprintf (listfp, "\n");
if (s2kmode == 1 || s2kmode == 3)
{
es_fprintf (listfp, "\tsalt ");
es_write_hexstring (listfp, k->s2k.salt, 8, 0, NULL);
if (s2kmode == 3)
es_fprintf (listfp, ", count %lu (%lu)",
S2K_DECODE_COUNT ((ulong) k->s2k.count),
(ulong) k->s2k.count);
es_fprintf (listfp, "\n");
}
}
leave:
iobuf_skip_rest (inp, pktlen, 0);
return rc;
too_short:
log_error ("packet(%d) too short\n", pkttype);
if (list_mode)
es_fprintf (listfp, ":symkey enc packet: [too short]\n");
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
/* Parse a public key encrypted packet (Tag 1). */
static int
parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet)
{
int rc = 0;
int i, ndata;
unsigned int n;
PKT_pubkey_enc *k;
k = packet->pkt.pubkey_enc = xmalloc_clear (sizeof *packet->pkt.pubkey_enc);
if (pktlen < 12)
{
log_error ("packet(%d) too short\n", pkttype);
if (list_mode)
es_fputs (":pubkey enc packet: [too short]\n", listfp);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
k->version = iobuf_get_noeof (inp);
pktlen--;
if (k->version != 2 && k->version != 3)
{
log_error ("packet(%d) with unknown version %d\n", pkttype, k->version);
if (list_mode)
es_fputs (":pubkey enc packet: [unknown version]\n", listfp);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
k->keyid[0] = read_32 (inp);
pktlen -= 4;
k->keyid[1] = read_32 (inp);
pktlen -= 4;
k->pubkey_algo = iobuf_get_noeof (inp);
pktlen--;
k->throw_keyid = 0; /* Only used as flag for build_packet. */
if (list_mode)
es_fprintf (listfp,
":pubkey enc packet: version %d, algo %d, keyid %08lX%08lX\n",
k->version, k->pubkey_algo, (ulong) k->keyid[0],
(ulong) k->keyid[1]);
ndata = pubkey_get_nenc (k->pubkey_algo);
if (!ndata)
{
if (list_mode)
es_fprintf (listfp, "\tunsupported algorithm %d\n", k->pubkey_algo);
unknown_pubkey_warning (k->pubkey_algo);
k->data[0] = NULL; /* No need to store the encrypted data. */
}
else if (k->pubkey_algo == PUBKEY_ALGO_ECDH)
{
log_assert (ndata == 2);
/* Get the ephemeral public key. */
n = pktlen;
k->data[0] = sos_read (inp, &n, 0);
pktlen -= n;
if (!k->data[0])
{
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
/* Get the wrapped symmetric key. */
rc = read_sized_octet_string (inp, &pktlen, k->data + 1);
if (rc)
goto leave;
}
else if (k->pubkey_algo == PUBKEY_ALGO_KYBER)
{
log_assert (ndata == 3);
/* Get the ephemeral public key. */
n = pktlen;
k->data[0] = sos_read (inp, &n, 0);
pktlen -= n;
if (!k->data[0])
{
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
/* Get the Kyber ciphertext. */
rc = read_octet_string (inp, &pktlen, 4, 0, 0, k->data + 1);
if (rc)
goto leave;
/* Get the algorithm id for the session key. */
if (!pktlen)
{
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
k->seskey_algo = iobuf_get_noeof (inp);
pktlen--;
/* Get the encrypted symmetric key. */
rc = read_octet_string (inp, &pktlen, 1, 0, 0, k->data + 2);
if (rc)
goto leave;
}
else
{
for (i = 0; i < ndata; i++)
{
n = pktlen;
k->data[i] = mpi_read (inp, &n, 0);
pktlen -= n;
if (!k->data[i])
rc = gpg_error (GPG_ERR_INV_PACKET);
}
if (rc)
goto leave;
}
if (list_mode)
{
if (k->seskey_algo)
es_fprintf (listfp, "\tsession key algo: %d\n", k->seskey_algo);
for (i = 0; i < ndata; i++)
{
es_fprintf (listfp, "\tdata: ");
mpi_print (listfp, k->data[i], mpi_print_mode);
es_putc ('\n', listfp);
}
}
leave:
iobuf_skip_rest (inp, pktlen, 0);
return rc;
}
/* Dump a subpacket to LISTFP. BUFFER contains the subpacket in
* question and points to the type field in the subpacket header (not
* the start of the header). TYPE is the subpacket's type with the
* critical bit cleared. CRITICAL is the value of the CRITICAL bit.
* BUFLEN is the length of the buffer and LENGTH is the length of the
* subpacket according to the subpacket's header. DIGEST_ALGO is the
* digest algo of the signature. */
static void
dump_sig_subpkt (int hashed, int type, int critical,
const byte * buffer, size_t buflen, size_t length,
int digest_algo)
{
const char *p = NULL;
int i;
int nprinted;
/* The CERT has warning out with explains how to use GNUPG to detect
* the ARRs - we print our old message here when it is a faked ARR
* and add an additional notice. */
if (type == SIGSUBPKT_ARR && !hashed)
{
es_fprintf (listfp,
"\tsubpkt %d len %u (additional recipient request)\n"
"WARNING: PGP versions > 5.0 and < 6.5.8 will automagically "
"encrypt to this key and thereby reveal the plaintext to "
"the owner of this ARR key. Detailed info follows:\n",
type, (unsigned) length);
}
buffer++;
length--;
nprinted = es_fprintf (listfp, "\t%s%ssubpkt %d len %u (", /*) */
critical ? "critical " : "",
hashed ? "hashed " : "", type, (unsigned) length);
if (nprinted < 1)
nprinted = 1; /*(we use (nprinted-1) later.)*/
if (length > buflen)
{
es_fprintf (listfp, "too short: buffer is only %u)\n", (unsigned) buflen);
return;
}
switch (type)
{
case SIGSUBPKT_SIG_CREATED:
if (length >= 4)
es_fprintf (listfp, "sig created %s",
strtimestamp (buf32_to_u32 (buffer)));
break;
case SIGSUBPKT_SIG_EXPIRE:
if (length >= 4)
{
if (buf32_to_u32 (buffer))
es_fprintf (listfp, "sig expires after %s",
strtimevalue (buf32_to_u32 (buffer)));
else
es_fprintf (listfp, "sig does not expire");
}
break;
case SIGSUBPKT_EXPORTABLE:
if (length)
es_fprintf (listfp, "%sexportable", *buffer ? "" : "not ");
break;
case SIGSUBPKT_TRUST:
if (length != 2)
p = "[invalid trust subpacket]";
else
es_fprintf (listfp, "trust signature of depth %d, value %d", buffer[0],
buffer[1]);
break;
case SIGSUBPKT_REGEXP:
if (!length)
p = "[invalid regexp subpacket]";
else
{
es_fprintf (listfp, "regular expression: \"");
es_write_sanitized (listfp, buffer, length, "\"", NULL);
p = "\"";
}
break;
case SIGSUBPKT_REVOCABLE:
if (length)
es_fprintf (listfp, "%srevocable", *buffer ? "" : "not ");
break;
case SIGSUBPKT_KEY_EXPIRE:
if (length >= 4)
{
if (buf32_to_u32 (buffer))
es_fprintf (listfp, "key expires after %s",
strtimevalue (buf32_to_u32 (buffer)));
else
es_fprintf (listfp, "key does not expire");
}
break;
case SIGSUBPKT_PREF_SYM:
es_fputs ("pref-sym-algos:", listfp);
for (i = 0; i < length; i++)
es_fprintf (listfp, " %d", buffer[i]);
break;
case SIGSUBPKT_PREF_AEAD:
es_fputs ("pref-aead-algos:", listfp);
for (i = 0; i < length; i++)
es_fprintf (listfp, " %d", buffer[i]);
break;
case SIGSUBPKT_REV_KEY:
es_fputs ("revocation key: ", listfp);
if (length < 22)
p = "[too short]";
else
{
es_fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1]);
for (i = 2; i < length; i++)
es_fprintf (listfp, "%02X", buffer[i]);
}
break;
case SIGSUBPKT_ISSUER:
if (length >= 8)
es_fprintf (listfp, "issuer key ID %08lX%08lX",
(ulong) buf32_to_u32 (buffer),
(ulong) buf32_to_u32 (buffer + 4));
break;
case SIGSUBPKT_ISSUER_FPR:
if (length >= 21)
{
char *tmp;
es_fprintf (listfp, "issuer fpr v%d ", buffer[0]);
tmp = bin2hex (buffer+1, length-1, NULL);
if (tmp)
{
es_fputs (tmp, listfp);
xfree (tmp);
}
}
break;
case SIGSUBPKT_NOTATION:
{
es_fputs ("notation: ", listfp);
if (length < 8)
p = "[too short]";
else
{
const byte *s = buffer;
size_t n1, n2;
n1 = (s[4] << 8) | s[5];
n2 = (s[6] << 8) | s[7];
s += 8;
if (8 + n1 + n2 != length)
p = "[error]";
else
{
es_write_sanitized (listfp, s, n1, ")", NULL);
es_putc ('=', listfp);
if (*buffer & 0x80)
es_write_sanitized (listfp, s + n1, n2, ")", NULL);
else
p = "[not human readable]";
}
}
}
break;
case SIGSUBPKT_PREF_HASH:
es_fputs ("pref-hash-algos:", listfp);
for (i = 0; i < length; i++)
es_fprintf (listfp, " %d", buffer[i]);
break;
case SIGSUBPKT_PREF_COMPR:
es_fputs ("pref-zip-algos:", listfp);
for (i = 0; i < length; i++)
es_fprintf (listfp, " %d", buffer[i]);
break;
case SIGSUBPKT_KS_FLAGS:
es_fputs ("keyserver preferences:", listfp);
for (i = 0; i < length; i++)
es_fprintf (listfp, " %02X", buffer[i]);
break;
case SIGSUBPKT_PREF_KS:
es_fputs ("preferred keyserver: ", listfp);
es_write_sanitized (listfp, buffer, length, ")", NULL);
break;
case SIGSUBPKT_PRIMARY_UID:
p = "primary user ID";
break;
case SIGSUBPKT_POLICY:
es_fputs ("policy: ", listfp);
es_write_sanitized (listfp, buffer, length, ")", NULL);
break;
case SIGSUBPKT_KEY_FLAGS:
es_fputs ("key flags:", listfp);
for (i = 0; i < length; i++)
es_fprintf (listfp, " %02X", buffer[i]);
break;
case SIGSUBPKT_SIGNERS_UID:
p = "signer's user ID";
break;
case SIGSUBPKT_REVOC_REASON:
if (length)
{
es_fprintf (listfp, "revocation reason 0x%02x (", *buffer);
es_write_sanitized (listfp, buffer + 1, length - 1, ")", NULL);
p = ")";
}
break;
case SIGSUBPKT_ARR:
es_fputs ("Big Brother's key (ignored): ", listfp);
if (length < 22)
p = "[too short]";
else
{
es_fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1]);
if (length > 2)
es_write_hexstring (listfp, buffer+2, length-2, 0, NULL);
}
break;
case SIGSUBPKT_FEATURES:
es_fputs ("features:", listfp);
for (i = 0; i < length; i++)
es_fprintf (listfp, " %02x", buffer[i]);
break;
case SIGSUBPKT_SIGNATURE:
es_fputs ("signature: ", listfp);
if (length < 17)
p = "[too short]";
else
es_fprintf (listfp, "v%d, class 0x%02X, algo %d, digest algo %d",
buffer[0],
buffer[0] == 3 ? buffer[2] : buffer[1],
buffer[0] == 3 ? buffer[15] : buffer[2],
buffer[0] == 3 ? buffer[16] : buffer[3]);
break;
case SIGSUBPKT_ATTST_SIGS:
{
unsigned int hlen;
es_fputs ("attst-sigs: ", listfp);
hlen = gcry_md_get_algo_dlen (map_md_openpgp_to_gcry (digest_algo));
if (!hlen)
p = "[unknown digest algo]";
else if ((length % hlen))
p = "[invalid length]";
else
{
es_fprintf (listfp, "%u", (unsigned int)length/hlen);
while (length)
{
es_fprintf (listfp, "\n\t%*s", nprinted-1, "");
es_write_hexstring (listfp, buffer, hlen, 0, NULL);
buffer += hlen;
length -= hlen;
}
}
}
break;
case SIGSUBPKT_KEY_BLOCK:
es_fputs ("key-block: ", listfp);
if (length && buffer[0])
p = "[unknown reserved octet]";
else if (length < 50) /* 50 is an arbitrary min. length. */
p = "[invalid subpacket]";
else
{
/* estream_t fp; */
/* fp = es_fopen ("a.key-block", "wb"); */
/* log_assert (fp); */
/* es_fwrite ( buffer+1, length-1, 1, fp); */
/* es_fclose (fp); */
es_fprintf (listfp, "[%u octets]", (unsigned int)length-1);
}
break;
default:
if (type >= 100 && type <= 110)
p = "experimental / private subpacket";
else
p = "?";
break;
}
es_fprintf (listfp, "%s)\n", p ? p : "");
}
/*
* Returns: >= 0 use this offset into buffer
* -1 explicitly reject returning this type
* -2 subpacket too short
*/
int
parse_one_sig_subpkt (const byte * buffer, size_t n, int type)
{
switch (type)
{
case SIGSUBPKT_REV_KEY:
if (n < 22)
break;
return 0;
case SIGSUBPKT_SIG_CREATED:
case SIGSUBPKT_SIG_EXPIRE:
case SIGSUBPKT_KEY_EXPIRE:
if (n < 4)
break;
return 0;
case SIGSUBPKT_KEY_FLAGS:
case SIGSUBPKT_KS_FLAGS:
case SIGSUBPKT_PREF_SYM:
case SIGSUBPKT_PREF_AEAD:
case SIGSUBPKT_PREF_HASH:
case SIGSUBPKT_PREF_COMPR:
case SIGSUBPKT_POLICY:
case SIGSUBPKT_PREF_KS:
case SIGSUBPKT_FEATURES:
case SIGSUBPKT_REGEXP:
case SIGSUBPKT_ATTST_SIGS:
return 0;
case SIGSUBPKT_SIGNATURE:
case SIGSUBPKT_EXPORTABLE:
case SIGSUBPKT_REVOCABLE:
case SIGSUBPKT_REVOC_REASON:
if (!n)
break;
return 0;
case SIGSUBPKT_ISSUER: /* issuer key ID */
if (n < 8)
break;
return 0;
case SIGSUBPKT_ISSUER_FPR: /* issuer key fingerprint */
if (n < 21)
break;
return 0;
case SIGSUBPKT_NOTATION:
/* minimum length needed, and the subpacket must be well-formed
where the name length and value length all fit inside the
packet. */
if (n < 8
|| 8 + ((buffer[4] << 8) | buffer[5]) +
((buffer[6] << 8) | buffer[7]) != n)
break;
return 0;
case SIGSUBPKT_PRIMARY_UID:
if (n != 1)
break;
return 0;
case SIGSUBPKT_TRUST:
if (n != 2)
break;
return 0;
case SIGSUBPKT_KEY_BLOCK:
if (n && buffer[0])
return -1; /* Unknown version - ignore. */
if (n < 50)
break; /* Definitely too short to carry a key block. */
return 0;
default:
return 0;
}
return -2;
}
/* Return true if we understand the critical notation. */
static int
can_handle_critical_notation (const byte *name, size_t len)
{
strlist_t sl;
register_known_notation (NULL); /* Make sure it is initialized. */
for (sl = known_notations_list; sl; sl = sl->next)
if (sl->flags == len && !memcmp (sl->d, name, len))
return 1; /* Known */
if (opt.verbose && !glo_ctrl.silence_parse_warnings)
{
log_info(_("Unknown critical signature notation: ") );
print_utf8_buffer (log_get_stream(), name, len);
log_printf ("\n");
}
return 0; /* Unknown. */
}
static int
can_handle_critical (const byte * buffer, size_t n, int type)
{
switch (type)
{
case SIGSUBPKT_NOTATION:
if (n >= 8)
{
size_t notation_len = ((buffer[4] << 8) | buffer[5]);
if (n - 8 >= notation_len)
return can_handle_critical_notation (buffer + 8, notation_len);
}
return 0;
case SIGSUBPKT_SIGNATURE:
case SIGSUBPKT_SIG_CREATED:
case SIGSUBPKT_SIG_EXPIRE:
case SIGSUBPKT_KEY_EXPIRE:
case SIGSUBPKT_EXPORTABLE:
case SIGSUBPKT_REVOCABLE:
case SIGSUBPKT_REV_KEY:
case SIGSUBPKT_ISSUER: /* issuer key ID */
case SIGSUBPKT_ISSUER_FPR: /* issuer fingerprint */
case SIGSUBPKT_PREF_SYM:
case SIGSUBPKT_PREF_AEAD:
case SIGSUBPKT_PREF_HASH:
case SIGSUBPKT_PREF_COMPR:
case SIGSUBPKT_KEY_FLAGS:
case SIGSUBPKT_PRIMARY_UID:
case SIGSUBPKT_FEATURES:
case SIGSUBPKT_TRUST:
case SIGSUBPKT_REGEXP:
case SIGSUBPKT_ATTST_SIGS:
/* Is it enough to show the policy or keyserver? */
case SIGSUBPKT_POLICY:
case SIGSUBPKT_PREF_KS:
case SIGSUBPKT_REVOC_REASON: /* At least we know about it. */
return 1;
case SIGSUBPKT_KEY_BLOCK:
if (n && !buffer[0])
return 1;
else
return 0;
default:
return 0;
}
}
const byte *
enum_sig_subpkt (PKT_signature *sig, int want_hashed, sigsubpkttype_t reqtype,
size_t *ret_n, int *start, int *critical)
{
const byte *buffer;
int buflen;
int type;
int critical_dummy;
int offset;
size_t n;
const subpktarea_t *pktbuf = want_hashed? sig->hashed : sig->unhashed;
int seq = 0;
int reqseq = start ? *start : 0;
if (!critical)
critical = &critical_dummy;
if (!pktbuf || reqseq == -1)
{
static char dummy[] = "x";
/* Return a value different from NULL to indicate that
* there is no critical bit we do not understand. */
return reqtype == SIGSUBPKT_TEST_CRITICAL ? dummy : NULL;
}
buffer = pktbuf->data;
buflen = pktbuf->len;
while (buflen)
{
n = *buffer++;
buflen--;
if (n == 255) /* 4 byte length header. */
{
if (buflen < 4)
goto too_short;
n = buf32_to_size_t (buffer);
buffer += 4;
buflen -= 4;
}
else if (n >= 192) /* 4 byte special encoded length header. */
{
if (buflen < 2)
goto too_short;
n = ((n - 192) << 8) + *buffer + 192;
buffer++;
buflen--;
}
if (buflen < n)
goto too_short;
if (!buflen)
goto no_type_byte;
type = *buffer;
if (type & 0x80)
{
type &= 0x7f;
*critical = 1;
}
else
*critical = 0;
if (!(++seq > reqseq))
;
else if (reqtype == SIGSUBPKT_TEST_CRITICAL)
{
if (*critical)
{
if (n - 1 > buflen + 1)
goto too_short;
if (!can_handle_critical (buffer + 1, n - 1, type))
{
if (opt.verbose && !glo_ctrl.silence_parse_warnings)
log_info (_("subpacket of type %d has "
"critical bit set\n"), type);
if (start)
*start = seq;
return NULL; /* This is an error. */
}
}
}
else if (reqtype < 0) /* List packets. */
dump_sig_subpkt (reqtype == SIGSUBPKT_LIST_HASHED,
type, *critical, buffer, buflen, n, sig->digest_algo);
else if (type == reqtype) /* Found. */
{
buffer++;
n--;
if (n > buflen)
goto too_short;
if (ret_n)
*ret_n = n;
offset = parse_one_sig_subpkt (buffer, n, type);
switch (offset)
{
case -2:
log_error ("subpacket of type %d too short\n", type);
return NULL;
case -1:
return NULL;
default:
break;
}
if (start)
*start = seq;
return buffer + offset;
}
buffer += n;
buflen -= n;
}
if (reqtype == SIGSUBPKT_TEST_CRITICAL)
/* Returning NULL means we found a subpacket with the critical bit
set that we don't grok. We've iterated over all the subpackets
and haven't found such a packet so we need to return a non-NULL
value. */
return buffer;
/* Critical bit we don't understand. */
if (start)
*start = -1;
return NULL; /* End of packets; not found. */
too_short:
if (opt.debug && !glo_ctrl.silence_parse_warnings)
{
es_fflush (es_stdout);
log_printhex (pktbuf->data, pktbuf->len > 16? 16 : pktbuf->len,
"buffer shorter than subpacket (%zu/%d/%zu); dump:",
pktbuf->len, buflen, n);
}
if (start)
*start = -1;
return NULL;
no_type_byte:
if (opt.verbose && !glo_ctrl.silence_parse_warnings)
log_info ("type octet missing in subpacket\n");
if (start)
*start = -1;
return NULL;
}
const byte *
parse_sig_subpkt (PKT_signature *sig, int want_hashed, sigsubpkttype_t reqtype,
size_t *ret_n)
{
return enum_sig_subpkt (sig, want_hashed, reqtype, ret_n, NULL, NULL);
}
const byte *
parse_sig_subpkt2 (PKT_signature *sig, sigsubpkttype_t reqtype)
{
const byte *p;
p = parse_sig_subpkt (sig, 1, reqtype, NULL);
if (!p)
p = parse_sig_subpkt (sig, 0, reqtype, NULL);
return p;
}
/* Find all revocation keys. Look in hashed area only. */
void
parse_revkeys (PKT_signature * sig)
{
const byte *revkey;
int seq = 0;
size_t len;
if (sig->sig_class != 0x1F)
return;
while ((revkey = enum_sig_subpkt (sig, 1, SIGSUBPKT_REV_KEY,
&len, &seq, NULL)))
{
/* Consider only valid packets. They must have a length of
* either 2+20 or 2+32 octets and bit 7 of the class octet must
* be set. */
if ((len == 22 || len == 34)
&& (revkey[0] & 0x80))
{
sig->revkey = xrealloc (sig->revkey,
sizeof (struct revocation_key) *
(sig->numrevkeys + 1));
sig->revkey[sig->numrevkeys].class = revkey[0];
sig->revkey[sig->numrevkeys].algid = revkey[1];
len -= 2;
sig->revkey[sig->numrevkeys].fprlen = len;
memcpy (sig->revkey[sig->numrevkeys].fpr, revkey+2, len);
memset (sig->revkey[sig->numrevkeys].fpr+len, 0,
sizeof (sig->revkey[sig->numrevkeys].fpr) - len);
sig->numrevkeys++;
}
}
}
int
parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
PKT_signature * sig)
{
int md5_len = 0;
unsigned n;
int is_v4or5 = 0;
int rc = 0;
int i, ndata;
if (pktlen < 16)
{
log_error ("packet(%d) too short\n", pkttype);
if (list_mode)
es_fputs (":signature packet: [too short]\n", listfp);
goto leave;
}
sig->version = iobuf_get_noeof (inp);
pktlen--;
if (sig->version == 4 || sig->version == 5)
is_v4or5 = 1;
else if (sig->version != 2 && sig->version != 3)
{
log_error ("packet(%d) with unknown version %d\n",
pkttype, sig->version);
if (list_mode)
es_fputs (":signature packet: [unknown version]\n", listfp);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
if (!is_v4or5)
{
if (pktlen == 0)
goto underflow;
md5_len = iobuf_get_noeof (inp);
pktlen--;
}
if (pktlen == 0)
goto underflow;
sig->sig_class = iobuf_get_noeof (inp);
pktlen--;
if (!is_v4or5)
{
if (pktlen < 12)
goto underflow;
sig->timestamp = read_32 (inp);
pktlen -= 4;
sig->keyid[0] = read_32 (inp);
pktlen -= 4;
sig->keyid[1] = read_32 (inp);
pktlen -= 4;
}
if (pktlen < 2)
goto underflow;
sig->pubkey_algo = iobuf_get_noeof (inp);
pktlen--;
sig->digest_algo = iobuf_get_noeof (inp);
pktlen--;
sig->flags.exportable = 1;
sig->flags.revocable = 1;
if (is_v4or5) /* Read subpackets. */
{
if (pktlen < 2)
goto underflow;
n = read_16 (inp);
pktlen -= 2; /* Length of hashed data. */
if (pktlen < n)
goto underflow;
if (n > 10000)
{
log_error ("signature packet: hashed data too long\n");
if (list_mode)
es_fputs (":signature packet: [hashed data too long]\n", listfp);
rc = GPG_ERR_INV_PACKET;
goto leave;
}
if (n)
{
sig->hashed = xmalloc (sizeof (*sig->hashed) + n - 1);
sig->hashed->size = n;
sig->hashed->len = n;
if (iobuf_read (inp, sig->hashed->data, n) != n)
{
log_error ("premature eof while reading "
"hashed signature data\n");
if (list_mode)
es_fputs (":signature packet: [premature eof]\n", listfp);
rc = -1;
goto leave;
}
pktlen -= n;
}
if (pktlen < 2)
goto underflow;
n = read_16 (inp);
pktlen -= 2; /* Length of unhashed data. */
if (pktlen < n)
goto underflow;
if (n > 10000)
{
log_error ("signature packet: unhashed data too long\n");
if (list_mode)
es_fputs (":signature packet: [unhashed data too long]\n", listfp);
rc = GPG_ERR_INV_PACKET;
goto leave;
}
if (n)
{
sig->unhashed = xmalloc (sizeof (*sig->unhashed) + n - 1);
sig->unhashed->size = n;
sig->unhashed->len = n;
if (iobuf_read (inp, sig->unhashed->data, n) != n)
{
log_error ("premature eof while reading "
"unhashed signature data\n");
if (list_mode)
es_fputs (":signature packet: [premature eof]\n", listfp);
rc = -1;
goto leave;
}
pktlen -= n;
}
}
if (pktlen < 2)
goto underflow;
sig->digest_start[0] = iobuf_get_noeof (inp);
pktlen--;
sig->digest_start[1] = iobuf_get_noeof (inp);
pktlen--;
if (is_v4or5 && sig->pubkey_algo) /* Extract required information. */
{
const byte *p;
size_t len;
/* Set sig->flags.unknown_critical if there is a critical bit
* set for packets which we do not understand. */
if (!parse_sig_subpkt (sig, 1, SIGSUBPKT_TEST_CRITICAL, NULL)
|| !parse_sig_subpkt (sig, 0, SIGSUBPKT_TEST_CRITICAL, NULL))
sig->flags.unknown_critical = 1;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_SIG_CREATED, NULL);
if (p)
sig->timestamp = buf32_to_u32 (p);
else if (!(sig->pubkey_algo >= 100 && sig->pubkey_algo <= 110)
&& opt.verbose > 1 && !glo_ctrl.silence_parse_warnings)
log_info ("signature packet without timestamp\n");
/* Set the key id. We first try the issuer fingerprint and if
* it is a v4 signature the fallback to the issuer. Note that
* only the issuer packet is also searched in the unhashed area. */
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_ISSUER_FPR, &len);
if (p && len == 21 && p[0] == 4)
{
sig->keyid[0] = buf32_to_u32 (p + 1 + 12);
sig->keyid[1] = buf32_to_u32 (p + 1 + 16);
}
else if (p && len == 33 && p[0] == 5)
{
sig->keyid[0] = buf32_to_u32 (p + 1 );
sig->keyid[1] = buf32_to_u32 (p + 1 + 4);
}
else if ((p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER)))
{
sig->keyid[0] = buf32_to_u32 (p);
sig->keyid[1] = buf32_to_u32 (p + 4);
}
else if (!(sig->pubkey_algo >= 100 && sig->pubkey_algo <= 110)
&& opt.verbose > 1 && !glo_ctrl.silence_parse_warnings)
log_info ("signature packet without keyid\n");
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_SIG_EXPIRE, NULL);
if (p && buf32_to_u32 (p))
sig->expiredate = sig->timestamp + buf32_to_u32 (p);
if (sig->expiredate && sig->expiredate <= make_timestamp ())
sig->flags.expired = 1;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_POLICY, NULL);
if (p)
sig->flags.policy_url = 1;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_KS, NULL);
if (p)
sig->flags.pref_ks = 1;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_SIGNERS_UID, &len);
if (p && len)
{
char *mbox;
sig->signers_uid = try_make_printable_string (p, len, 0);
if (!sig->signers_uid)
{
rc = gpg_error_from_syserror ();
goto leave;
}
mbox = mailbox_from_userid (sig->signers_uid, 0);
if (mbox)
{
xfree (sig->signers_uid);
sig->signers_uid = mbox;
}
}
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_KEY_BLOCK, NULL);
if (p)
sig->flags.key_block = 1;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_NOTATION, NULL);
if (p)
sig->flags.notation = 1;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_REVOCABLE, NULL);
if (p && *p == 0)
sig->flags.revocable = 0;
p = parse_sig_subpkt (sig, 1, SIGSUBPKT_TRUST, &len);
if (p && len == 2)
{
sig->trust_depth = p[0];
sig->trust_value = p[1];
/* Only look for a regexp if there is also a trust
subpacket. */
sig->trust_regexp =
parse_sig_subpkt (sig, 1, SIGSUBPKT_REGEXP, &len);
/* If the regular expression is of 0 length, there is no
regular expression. */
if (len == 0)
sig->trust_regexp = NULL;
}
/* We accept the exportable subpacket from either the hashed or
unhashed areas as older versions of gpg put it in the
unhashed area. In theory, anyway, we should never see this
packet off of a local keyring. */
p = parse_sig_subpkt2 (sig, SIGSUBPKT_EXPORTABLE);
if (p && *p == 0)
sig->flags.exportable = 0;
/* Find all revocation keys. */
if (sig->sig_class == 0x1F)
parse_revkeys (sig);
}
if (list_mode)
{
es_fprintf (listfp, ":signature packet: algo %d, keyid %08lX%08lX\n"
"\tversion %d, created %lu, md5len %d, sigclass 0x%02x\n"
"\tdigest algo %d, begin of digest %02x %02x\n",
sig->pubkey_algo,
(ulong) sig->keyid[0], (ulong) sig->keyid[1],
sig->version, (ulong) sig->timestamp, md5_len, sig->sig_class,
sig->digest_algo, sig->digest_start[0], sig->digest_start[1]);
if (is_v4or5)
{
parse_sig_subpkt (sig, 1, SIGSUBPKT_LIST_HASHED, NULL);
parse_sig_subpkt (sig, 0, SIGSUBPKT_LIST_UNHASHED, NULL);
}
}
ndata = pubkey_get_nsig (sig->pubkey_algo);
if (!ndata)
{
if (list_mode)
es_fprintf (listfp, "\tunknown algorithm %d\n", sig->pubkey_algo);
unknown_pubkey_warning (sig->pubkey_algo);
/* We store the plain material in data[0], so that we are able
* to write it back with build_packet(). */
if (pktlen > (5 * MAX_EXTERN_MPI_BITS / 8))
{
/* We include a limit to avoid too trivial DoS attacks by
having gpg allocate too much memory. */
log_error ("signature packet: too much data\n");
rc = GPG_ERR_INV_PACKET;
}
else
{
void *tmpp;
tmpp = read_rest (inp, pktlen);
sig->data[0] = gcry_mpi_set_opaque (NULL, tmpp, tmpp? pktlen * 8 : 0);
pktlen = 0;
}
}
else
{
for (i = 0; i < ndata; i++)
{
n = pktlen;
if (sig->pubkey_algo == PUBKEY_ALGO_ECDSA
|| sig->pubkey_algo == PUBKEY_ALGO_EDDSA)
sig->data[i] = sos_read (inp, &n, 0);
else
sig->data[i] = mpi_read (inp, &n, 0);
pktlen -= n;
if (list_mode)
{
es_fprintf (listfp, "\tdata: ");
mpi_print (listfp, sig->data[i], mpi_print_mode);
es_putc ('\n', listfp);
}
if (!sig->data[i])
rc = GPG_ERR_INV_PACKET;
}
}
leave:
iobuf_skip_rest (inp, pktlen, 0);
return rc;
underflow:
log_error ("packet(%d) too short\n", pkttype);
if (list_mode)
es_fputs (":signature packet: [too short]\n", listfp);
iobuf_skip_rest (inp, pktlen, 0);
return GPG_ERR_INV_PACKET;
}
static int
parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen,
PKT_onepass_sig * ops)
{
int version;
int rc = 0;
if (pktlen < 13)
{
log_error ("packet(%d) too short\n", pkttype);
if (list_mode)
es_fputs (":onepass_sig packet: [too short]\n", listfp);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
version = iobuf_get_noeof (inp);
pktlen--;
if (version != 3)
{
log_error ("onepass_sig with unknown version %d\n", version);
if (list_mode)
es_fputs (":onepass_sig packet: [unknown version]\n", listfp);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
ops->sig_class = iobuf_get_noeof (inp);
pktlen--;
ops->digest_algo = iobuf_get_noeof (inp);
pktlen--;
ops->pubkey_algo = iobuf_get_noeof (inp);
pktlen--;
ops->keyid[0] = read_32 (inp);
pktlen -= 4;
ops->keyid[1] = read_32 (inp);
pktlen -= 4;
ops->last = iobuf_get_noeof (inp);
pktlen--;
if (list_mode)
es_fprintf (listfp,
":onepass_sig packet: keyid %08lX%08lX\n"
"\tversion %d, sigclass 0x%02x, digest %d, pubkey %d, "
"last=%d\n",
(ulong) ops->keyid[0], (ulong) ops->keyid[1],
version, ops->sig_class,
ops->digest_algo, ops->pubkey_algo, ops->last);
leave:
iobuf_skip_rest (inp, pktlen, 0);
return rc;
}
static int
parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
byte * hdr, int hdrlen, PACKET * pkt)
{
gpg_error_t err = 0;
int i, version, algorithm;
unsigned long timestamp, expiredate, max_expiredate;
int npkey, nskey;
u32 keyid[2];
PKT_public_key *pk;
int is_v5;
unsigned int pkbytes; /* For v5 keys: Number of bytes in the public
* key material. For v4 keys: 0. */
(void) hdr;
pk = pkt->pkt.public_key; /* PK has been cleared. */
version = iobuf_get_noeof (inp);
pktlen--;
if (pkttype == PKT_PUBLIC_SUBKEY && version == '#')
{
/* Early versions of G10 used the old PGP comments packets;
* luckily all those comments are started by a hash. */
if (list_mode)
{
es_fprintf (listfp, ":rfc1991 comment packet: \"");
for (; pktlen; pktlen--)
{
int c;
c = iobuf_get (inp);
if (c == -1)
break; /* Ooops: shorter than indicated. */
if (c >= ' ' && c <= 'z')
es_putc (c, listfp);
else
es_fprintf (listfp, "\\x%02x", c);
}
es_fprintf (listfp, "\"\n");
}
iobuf_skip_rest (inp, pktlen, 0);
return 0;
}
else if (version == 4)
is_v5 = 0;
else if (version == 5)
is_v5 = 1;
else if (version == 2 || version == 3)
{
/* Not anymore supported since 2.1. Use an older gpg version
* (i.e. gpg 1.4) to parse v3 packets. */
if (opt.verbose > 1 && !glo_ctrl.silence_parse_warnings)
log_info ("packet(%d) with obsolete version %d\n", pkttype, version);
if (list_mode)
es_fprintf (listfp, ":key packet: [obsolete version %d]\n", version);
pk->version = version;
err = gpg_error (GPG_ERR_LEGACY_KEY);
goto leave;
}
else
{
log_error ("packet(%d) with unknown version %d\n", pkttype, version);
if (list_mode)
es_fputs (":key packet: [unknown version]\n", listfp);
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
if (pktlen < (is_v5? 15:11))
{
log_error ("packet(%d) too short\n", pkttype);
if (list_mode)
es_fputs (":key packet: [too short]\n", listfp);
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
else if (pktlen > MAX_KEY_PACKET_LENGTH)
{
log_error ("packet(%d) too large\n", pkttype);
if (list_mode)
es_fputs (":key packet: [too large]\n", listfp);
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
timestamp = read_32 (inp);
pktlen -= 4;
expiredate = 0; /* have to get it from the selfsignature */
max_expiredate = 0;
algorithm = iobuf_get_noeof (inp);
pktlen--;
if (is_v5)
{
pkbytes = read_32 (inp);
pktlen -= 4;
}
else
pkbytes = 0;
if (list_mode)
{
es_fprintf (listfp, ":%s key packet:\n"
"\tversion %d, algo %d, created %lu, expires %lu",
pkttype == PKT_PUBLIC_KEY ? "public" :
pkttype == PKT_SECRET_KEY ? "secret" :
pkttype == PKT_PUBLIC_SUBKEY ? "public sub" :
pkttype == PKT_SECRET_SUBKEY ? "secret sub" : "??",
version, algorithm, timestamp, expiredate);
if (is_v5)
es_fprintf (listfp, ", pkbytes %u\n", pkbytes);
else
es_fprintf (listfp, "\n");
}
pk->timestamp = timestamp;
pk->expiredate = expiredate;
pk->max_expiredate = max_expiredate;
pk->hdrbytes = hdrlen;
pk->version = version;
pk->flags.primary = (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY);
pk->pubkey_algo = algorithm;
nskey = pubkey_get_nskey (algorithm);
npkey = pubkey_get_npkey (algorithm);
if (!npkey)
{
if (list_mode)
es_fprintf (listfp, "\tunknown algorithm %d\n", algorithm);
unknown_pubkey_warning (algorithm);
}
if (!npkey)
{
/* Unknown algorithm - put data into an opaque MPI. */
void *tmpp = read_rest (inp, pktlen);
/* Current gcry_mpi_cmp does not handle a (NULL,n>0) nicely and
* thus we avoid to create such an MPI. */
pk->pkey[0] = gcry_mpi_set_opaque (NULL, tmpp, tmpp? pktlen * 8 : 0);
pktlen = 0;
goto leave;
}
else
{
for (i = 0; i < npkey; i++)
{
if ( (algorithm == PUBKEY_ALGO_ECDSA && (i == 0))
|| (algorithm == PUBKEY_ALGO_EDDSA && (i == 0))
|| (algorithm == PUBKEY_ALGO_ECDH && (i == 0 || i == 2))
|| (algorithm == PUBKEY_ALGO_KYBER && (i == 0)))
{
/* Read the OID (i==0) or the KDF params (i==2). */
err = read_sized_octet_string (inp, &pktlen, pk->pkey+i);
}
else if (algorithm == PUBKEY_ALGO_KYBER && i == 2)
{
/* Read the four-octet count prefixed Kyber public key. */
err = read_octet_string (inp, &pktlen, 4, 0, 0, pk->pkey+i);
}
else
{
/* Read MPI or SOS. */
unsigned int n = pktlen;
if (algorithm == PUBKEY_ALGO_ECDSA
|| algorithm == PUBKEY_ALGO_EDDSA
|| algorithm == PUBKEY_ALGO_ECDH
|| algorithm == PUBKEY_ALGO_KYBER)
pk->pkey[i] = sos_read (inp, &n, 0);
else
pk->pkey[i] = mpi_read (inp, &n, 0);
pktlen -= n;
if (!pk->pkey[i])
err = gpg_error (GPG_ERR_INV_PACKET);
}
if (err)
goto leave;
if (list_mode)
{
es_fprintf (listfp, "\tpkey[%d]: ", i);
mpi_print (listfp, pk->pkey[i], mpi_print_mode);
if ((algorithm == PUBKEY_ALGO_ECDSA
|| algorithm == PUBKEY_ALGO_EDDSA
|| algorithm == PUBKEY_ALGO_ECDH
|| algorithm == PUBKEY_ALGO_KYBER) && i==0)
{
char *curve = openpgp_oid_to_str (pk->pkey[0]);
const char *name = openpgp_oid_to_curve (curve, 0);
es_fprintf (listfp, " %s (%s)", name?name:"", curve);
xfree (curve);
}
es_putc ('\n', listfp);
}
}
}
if (list_mode)
keyid_from_pk (pk, keyid);
if (pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY)
{
struct seckey_info *ski;
byte temp[16];
size_t snlen = 0;
unsigned int skbytes;
if (pktlen < 1)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
if (!pk->seckey_info)
{
err = gpg_error_from_syserror ();
goto leave;
}
ski->algo = iobuf_get_noeof (inp);
pktlen--;
if (is_v5)
{
unsigned int protcount = 0;
/* Read the one octet count of the following key-protection
* material. Only required in case of unknown values. */
if (!pktlen)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
protcount = iobuf_get_noeof (inp);
pktlen--;
if (list_mode)
es_fprintf (listfp, "\tprotbytes: %u\n", protcount);
}
if (ski->algo)
{
ski->is_protected = 1;
ski->s2k.count = 0;
if (ski->algo == 253)
{
if (list_mode)
es_fprintf (listfp,
"\tS2K pseudo algo %d is not yet supported\n",
ski->algo);
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
goto leave;
}
else if (ski->algo == 254 || ski->algo == 255)
{
if (pktlen < 3)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
ski->sha1chk = (ski->algo == 254);
ski->algo = iobuf_get_noeof (inp);
pktlen--;
/* Note that a ski->algo > 110 is illegal, but I'm not
* erroring out here as otherwise there would be no way
* to delete such a key. */
ski->s2k.mode = iobuf_get_noeof (inp);
pktlen--;
ski->s2k.hash_algo = iobuf_get_noeof (inp);
pktlen--;
/* Check for the special GNU extension. */
if (ski->s2k.mode == 101)
{
for (i = 0; i < 4 && pktlen; i++, pktlen--)
temp[i] = iobuf_get_noeof (inp);
if (i < 4 || memcmp (temp, "GNU", 3))
{
if (list_mode)
es_fprintf (listfp, "\tunknown S2K %d\n",
ski->s2k.mode);
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
/* Here we know that it is a GNU extension. What
* follows is the GNU protection mode: All values
* have special meanings and they are mapped to MODE
* with a base of 1000. */
ski->s2k.mode = 1000 + temp[3];
}
/* Read the salt. */
if (ski->s2k.mode == 3 || ski->s2k.mode == 1)
{
for (i = 0; i < 8 && pktlen; i++, pktlen--)
temp[i] = iobuf_get_noeof (inp);
if (i < 8)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
memcpy (ski->s2k.salt, temp, 8);
}
/* Check the mode. */
switch (ski->s2k.mode)
{
case 0:
if (list_mode)
es_fprintf (listfp, "\tsimple S2K");
break;
case 1:
if (list_mode)
es_fprintf (listfp, "\tsalted S2K");
break;
case 3:
if (list_mode)
es_fprintf (listfp, "\titer+salt S2K");
break;
case 1001:
if (list_mode)
es_fprintf (listfp, "\tgnu-dummy");
break;
case 1002:
if (list_mode)
es_fprintf (listfp, "\tgnu-divert-to-card");
break;
case 1003:
if (list_mode)
es_fprintf (listfp, "\tgnu-mode1003");
break;
default:
if (list_mode)
es_fprintf (listfp, "\tunknown %sS2K %d\n",
ski->s2k.mode < 1000 ? "" : "GNU ",
ski->s2k.mode);
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
/* Print some info. */
if (list_mode && ski->s2k.mode != 1003)
{
es_fprintf (listfp, ", algo: %d,%s hash: %d",
ski->algo,
ski->sha1chk ? " SHA1 protection,"
: " simple checksum,", ski->s2k.hash_algo);
if (ski->s2k.mode == 1 || ski->s2k.mode == 3)
{
es_fprintf (listfp, ", salt: ");
es_write_hexstring (listfp, ski->s2k.salt, 8, 0, NULL);
}
}
if (list_mode)
es_putc ('\n', listfp);
/* Read remaining protection parameters. */
if (ski->s2k.mode == 3)
{
if (pktlen < 1)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
ski->s2k.count = iobuf_get_noeof (inp);
pktlen--;
if (list_mode)
es_fprintf (listfp, "\tprotect count: %lu (%lu)\n",
(ulong)S2K_DECODE_COUNT ((ulong)ski->s2k.count),
(ulong) ski->s2k.count);
}
else if (ski->s2k.mode == 1002)
{
/* Read the serial number. */
if (pktlen < 1)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
snlen = iobuf_get (inp);
pktlen--;
if (pktlen < snlen || snlen == (size_t)(-1))
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
}
}
else /* Old version; no S2K, so we set mode to 0, hash MD5. */
{
/* Note that a ski->algo > 110 is illegal, but I'm not
erroring on it here as otherwise there would be no
way to delete such a key. */
ski->s2k.mode = 0;
ski->s2k.hash_algo = DIGEST_ALGO_MD5;
if (list_mode)
es_fprintf (listfp, "\tprotect algo: %d (hash algo: %d)\n",
ski->algo, ski->s2k.hash_algo);
}
/* It is really ugly that we don't know the size
* of the IV here in cases we are not aware of the algorithm.
* so a
* ski->ivlen = cipher_get_blocksize (ski->algo);
* won't work. The only solution I see is to hardwire it.
* NOTE: if you change the ivlen above 16, don't forget to
* enlarge temp.
* FIXME: For v5 keys we can deduce this info!
*/
ski->ivlen = openpgp_cipher_blocklen (ski->algo);
log_assert (ski->ivlen <= sizeof (temp));
if (ski->s2k.mode == 1001 || ski->s2k.mode == 1003)
ski->ivlen = 0;
else if (ski->s2k.mode == 1002)
ski->ivlen = snlen < 16 ? snlen : 16;
if (pktlen < ski->ivlen)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
for (i = 0; i < ski->ivlen; i++, pktlen--)
temp[i] = iobuf_get_noeof (inp);
if (list_mode && ski->s2k.mode != 1003)
{
es_fprintf (listfp,
ski->s2k.mode == 1002 ? "\tserial-number: "
: "\tprotect IV: ");
for (i = 0; i < ski->ivlen; i++)
es_fprintf (listfp, " %02x", temp[i]);
es_putc ('\n', listfp);
}
memcpy (ski->iv, temp, ski->ivlen);
}
/* Skip count of secret key material. */
if (is_v5)
{
if (pktlen < 4)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
skbytes = read_32 (inp);
pktlen -= 4;
if (list_mode)
es_fprintf (listfp, "\tskbytes: %u\n", skbytes);
}
/* It does not make sense to read it into secure memory.
* If the user is so careless, not to protect his secret key,
* we can assume, that he operates an open system :=(.
* So we put the key into secure memory when we unprotect it. */
if (ski->s2k.mode == 1001 || ski->s2k.mode == 1002)
{
/* Better set some dummy stuff here. */
pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
xstrdup ("dummydata"),
10 * 8);
pktlen = 0;
}
else if (ski->s2k.mode == 1003)
{
void *tmpp;
if (pktlen < 2) /* At least two bytes for parenthesis. */
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
tmpp = read_rest (inp, pktlen);
if (list_mode)
{
if (mpi_print_mode)
{
char *tmpsxp = canon_sexp_to_string (tmpp, pktlen);
es_fprintf (listfp, "\tskey[%d]: %s\n", npkey,
tmpsxp? trim_trailing_spaces (tmpsxp)
/* */: "[invalid S-expression]");
xfree (tmpsxp);
}
else
es_fprintf (listfp, "\tskey[%d]: [s-expression %lu octets]\n",
npkey, pktlen);
}
pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
tmpp, tmpp? pktlen * 8 : 0);
pktlen = 0;
}
else if (ski->is_protected)
{
void *tmpp;
if (pktlen < 2) /* At least two bytes for the length. */
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
/* Ugly: The length is encrypted too, so we read all stuff
* up to the end of the packet into the first SKEY
* element.
* FIXME: We can do better for v5 keys. */
tmpp = read_rest (inp, pktlen);
pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
tmpp, tmpp? pktlen * 8 : 0);
/* Mark that MPI as protected - we need this information for
* importing a key. The OPAQUE flag can't be used because
* we also store public EdDSA values in opaque MPIs. */
if (pk->pkey[npkey])
gcry_mpi_set_flag (pk->pkey[npkey], GCRYMPI_FLAG_USER1);
pktlen = 0;
if (list_mode)
es_fprintf (listfp, "\tskey[%d]: [v4 protected]\n", npkey);
}
else
{
/* Not encrypted. */
for (i = npkey; i < nskey; i++)
{
if (pktlen < 2) /* At least two bytes for the length. */
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
if (algorithm == PUBKEY_ALGO_KYBER && i == npkey+1)
{
err = read_octet_string (inp, &pktlen, 4, 0, 1, pk->pkey+i);
if (err)
goto leave;
}
else
{
unsigned int n = pktlen;
if (algorithm == PUBKEY_ALGO_ECDSA
|| algorithm == PUBKEY_ALGO_EDDSA
|| algorithm == PUBKEY_ALGO_ECDH
|| algorithm == PUBKEY_ALGO_KYBER)
pk->pkey[i] = sos_read (inp, &n, 0);
else
pk->pkey[i] = mpi_read (inp, &n, 0);
pktlen -= n;
}
if (list_mode)
{
es_fprintf (listfp, "\tskey[%d]: ", i);
mpi_print (listfp, pk->pkey[i], mpi_print_mode);
es_putc ('\n', listfp);
}
if (!pk->pkey[i])
err = gpg_error (GPG_ERR_INV_PACKET);
}
if (err)
goto leave;
if (pktlen < 2)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
ski->csum = read_16 (inp);
pktlen -= 2;
if (list_mode)
es_fprintf (listfp, "\tchecksum: %04hx\n", ski->csum);
}
}
/* Note that KEYID below has been initialized above in list_mode. */
if (list_mode)
es_fprintf (listfp, "\tkeyid: %08lX%08lX\n",
(ulong) keyid[0], (ulong) keyid[1]);
leave:
iobuf_skip_rest (inp, pktlen, 0);
return err;
}
/* Attribute subpackets have the same format as v4 signature
subpackets. This is not part of OpenPGP, but is done in several
versions of PGP nevertheless. */
int
parse_attribute_subpkts (PKT_user_id * uid)
{
size_t n;
int count = 0;
struct user_attribute *attribs = NULL;
const byte *buffer = uid->attrib_data;
int buflen = uid->attrib_len;
byte type;
xfree (uid->attribs);
while (buflen)
{
n = *buffer++;
buflen--;
if (n == 255) /* 4 byte length header. */
{
if (buflen < 4)
goto too_short;
n = buf32_to_size_t (buffer);
buffer += 4;
buflen -= 4;
}
else if (n >= 192) /* 2 byte special encoded length header. */
{
if (buflen < 2)
goto too_short;
n = ((n - 192) << 8) + *buffer + 192;
buffer++;
buflen--;
}
if (buflen < n)
goto too_short;
if (!n)
{
/* Too short to encode the subpacket type. */
if (opt.verbose)
log_info ("attribute subpacket too short\n");
break;
}
attribs = xrealloc (attribs,
(count + 1) * sizeof (struct user_attribute));
memset (&attribs[count], 0, sizeof (struct user_attribute));
type = *buffer;
buffer++;
buflen--;
n--;
attribs[count].type = type;
attribs[count].data = buffer;
attribs[count].len = n;
buffer += n;
buflen -= n;
count++;
}
uid->attribs = attribs;
uid->numattribs = count;
return count;
too_short:
if (opt.verbose && !glo_ctrl.silence_parse_warnings)
log_info ("buffer shorter than attribute subpacket\n");
uid->attribs = attribs;
uid->numattribs = count;
return count;
}
static int
parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
{
byte *p;
/* Cap the size of a user ID at 2k: a value absurdly large enough
that there is no sane user ID string (which is printable text
as of RFC2440bis) that won't fit in it, but yet small enough to
avoid allocation problems. A large pktlen may not be
allocatable, and a very large pktlen could actually cause our
allocation to wrap around in xmalloc to a small number. */
if (pktlen > MAX_UID_PACKET_LENGTH)
{
log_error ("packet(%d) too large\n", pkttype);
if (list_mode)
es_fprintf (listfp, ":user ID packet: [too large]\n");
iobuf_skip_rest (inp, pktlen, 0);
return GPG_ERR_INV_PACKET;
}
packet->pkt.user_id = xmalloc_clear (sizeof *packet->pkt.user_id + pktlen);
packet->pkt.user_id->len = pktlen;
packet->pkt.user_id->ref = 1;
p = packet->pkt.user_id->name;
for (; pktlen; pktlen--, p++)
*p = iobuf_get_noeof (inp);
*p = 0;
if (list_mode)
{
int n = packet->pkt.user_id->len;
es_fprintf (listfp, ":user ID packet: \"");
/* fixme: Hey why don't we replace this with es_write_sanitized?? */
for (p = packet->pkt.user_id->name; n; p++, n--)
{
if (*p >= ' ' && *p <= 'z')
es_putc (*p, listfp);
else
es_fprintf (listfp, "\\x%02x", *p);
}
es_fprintf (listfp, "\"\n");
}
return 0;
}
void
make_attribute_uidname (PKT_user_id * uid, size_t max_namelen)
{
log_assert (max_namelen > 70);
if (uid->numattribs <= 0)
sprintf (uid->name, "[bad attribute packet of size %lu]",
uid->attrib_len);
else if (uid->numattribs > 1)
sprintf (uid->name, "[%d attributes of size %lu]",
uid->numattribs, uid->attrib_len);
else
{
/* Only one attribute, so list it as the "user id" */
if (uid->attribs->type == ATTRIB_IMAGE)
{
u32 len;
byte type;
if (parse_image_header (uid->attribs, &type, &len))
sprintf (uid->name, "[%.20s image of size %lu]",
image_type_to_string (type, 1), (ulong) len);
else
sprintf (uid->name, "[invalid image]");
}
else
sprintf (uid->name, "[unknown attribute of size %lu]",
(ulong) uid->attribs->len);
}
uid->len = strlen (uid->name);
}
static int
parse_attribute (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet)
{
byte *p;
(void) pkttype;
/* We better cap the size of an attribute packet to make DoS not too
easy. 16MB should be more then enough for one attribute packet
(ie. a photo). */
if (pktlen > MAX_ATTR_PACKET_LENGTH)
{
log_error ("packet(%d) too large\n", pkttype);
if (list_mode)
es_fprintf (listfp, ":attribute packet: [too large]\n");
iobuf_skip_rest (inp, pktlen, 0);
return GPG_ERR_INV_PACKET;
}
#define EXTRA_UID_NAME_SPACE 71
packet->pkt.user_id = xmalloc_clear (sizeof *packet->pkt.user_id
+ EXTRA_UID_NAME_SPACE);
packet->pkt.user_id->ref = 1;
packet->pkt.user_id->attrib_data = xmalloc (pktlen? pktlen:1);
packet->pkt.user_id->attrib_len = pktlen;
p = packet->pkt.user_id->attrib_data;
for (; pktlen; pktlen--, p++)
*p = iobuf_get_noeof (inp);
/* Now parse out the individual attribute subpackets. This is
somewhat pointless since there is only one currently defined
attribute type (jpeg), but it is correct by the spec. */
parse_attribute_subpkts (packet->pkt.user_id);
make_attribute_uidname (packet->pkt.user_id, EXTRA_UID_NAME_SPACE);
if (list_mode)
{
es_fprintf (listfp, ":attribute packet: %s\n", packet->pkt.user_id->name);
}
return 0;
}
static int
parse_comment (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
{
byte *p;
/* Cap comment packet at a reasonable value to avoid an integer
overflow in the malloc below. Comment packets are actually not
anymore define my OpenPGP and we even stopped to use our
private comment packet. */
if (pktlen > MAX_COMMENT_PACKET_LENGTH)
{
log_error ("packet(%d) too large\n", pkttype);
if (list_mode)
es_fprintf (listfp, ":%scomment packet: [too large]\n",
pkttype == PKT_OLD_COMMENT ? "OpenPGP draft " : "");
iobuf_skip_rest (inp, pktlen, 0);
return GPG_ERR_INV_PACKET;
}
packet->pkt.comment = xmalloc (sizeof *packet->pkt.comment + pktlen - 1);
packet->pkt.comment->len = pktlen;
p = packet->pkt.comment->data;
for (; pktlen; pktlen--, p++)
*p = iobuf_get_noeof (inp);
if (list_mode)
{
int n = packet->pkt.comment->len;
es_fprintf (listfp, ":%scomment packet: \"", pkttype == PKT_OLD_COMMENT ?
"OpenPGP draft " : "");
for (p = packet->pkt.comment->data; n; p++, n--)
{
if (*p >= ' ' && *p <= 'z')
es_putc (*p, listfp);
else
es_fprintf (listfp, "\\x%02x", *p);
}
es_fprintf (listfp, "\"\n");
}
return 0;
}
/* Parse a ring trust packet RFC4880 (5.10).
*
* This parser is special in that the packet is not stored as a packet
* but its content is merged into the previous packet. */
static gpg_error_t
parse_ring_trust (parse_packet_ctx_t ctx, unsigned long pktlen)
{
gpg_error_t err;
iobuf_t inp = ctx->inp;
PKT_ring_trust rt = {0};
int c;
int not_gpg = 0;
if (!pktlen)
{
if (list_mode)
es_fprintf (listfp, ":trust packet: empty\n");
err = 0;
goto leave;
}
c = iobuf_get_noeof (inp);
pktlen--;
rt.trustval = c;
if (pktlen)
{
if (!c)
{
c = iobuf_get_noeof (inp);
/* We require that bit 7 of the sigcache is 0 (easier
* eof handling). */
if (!(c & 0x80))
rt.sigcache = c;
}
else
iobuf_get_noeof (inp); /* Dummy read. */
pktlen--;
}
/* Next is the optional subtype. */
if (pktlen > 3)
{
char tmp[4];
tmp[0] = iobuf_get_noeof (inp);
tmp[1] = iobuf_get_noeof (inp);
tmp[2] = iobuf_get_noeof (inp);
tmp[3] = iobuf_get_noeof (inp);
pktlen -= 4;
if (!memcmp (tmp, "gpg", 3))
rt.subtype = tmp[3];
else
not_gpg = 1;
}
/* If it is a key or uid subtype read the remaining data. */
if ((rt.subtype == RING_TRUST_KEY || rt.subtype == RING_TRUST_UID)
&& pktlen >= 6 )
{
int i;
unsigned int namelen;
rt.keyorg = iobuf_get_noeof (inp);
pktlen--;
rt.keyupdate = read_32 (inp);
pktlen -= 4;
namelen = iobuf_get_noeof (inp);
pktlen--;
if (namelen && pktlen)
{
rt.url = xtrymalloc (namelen + 1);
if (!rt.url)
{
err = gpg_error_from_syserror ();
goto leave;
}
for (i = 0; pktlen && i < namelen; pktlen--, i++)
rt.url[i] = iobuf_get_noeof (inp);
rt.url[i] = 0;
}
}
if (list_mode)
{
if (rt.subtype == RING_TRUST_SIG)
es_fprintf (listfp, ":trust packet: sig flag=%02x sigcache=%02x\n",
rt.trustval, rt.sigcache);
else if (rt.subtype == RING_TRUST_UID || rt.subtype == RING_TRUST_KEY)
{
unsigned char *p;
es_fprintf (listfp, ":trust packet: %s upd=%lu src=%d%s",
(rt.subtype == RING_TRUST_UID? "uid" : "key"),
(unsigned long)rt.keyupdate,
rt.keyorg,
(rt.url? " url=":""));
if (rt.url)
{
for (p = rt.url; *p; p++)
{
if (*p >= ' ' && *p <= 'z')
es_putc (*p, listfp);
else
es_fprintf (listfp, "\\x%02x", *p);
}
}
es_putc ('\n', listfp);
}
else if (not_gpg)
es_fprintf (listfp, ":trust packet: not created by gpg\n");
else
es_fprintf (listfp, ":trust packet: subtype=%02x\n",
rt.subtype);
}
/* Now transfer the data to the respective packet. Do not do this
* if SKIP_META is set. */
if (!ctx->last_pkt.pkt.generic || ctx->skip_meta)
;
else if (rt.subtype == RING_TRUST_SIG
&& ctx->last_pkt.pkttype == PKT_SIGNATURE)
{
PKT_signature *sig = ctx->last_pkt.pkt.signature;
if ((rt.sigcache & 1))
{
sig->flags.checked = 1;
sig->flags.valid = !!(rt.sigcache & 2);
}
}
else if (rt.subtype == RING_TRUST_UID
&& (ctx->last_pkt.pkttype == PKT_USER_ID
|| ctx->last_pkt.pkttype == PKT_ATTRIBUTE))
{
PKT_user_id *uid = ctx->last_pkt.pkt.user_id;
uid->keyorg = rt.keyorg;
uid->keyupdate = rt.keyupdate;
uid->updateurl = rt.url;
rt.url = NULL;
}
else if (rt.subtype == RING_TRUST_KEY
&& (ctx->last_pkt.pkttype == PKT_PUBLIC_KEY
|| ctx->last_pkt.pkttype == PKT_SECRET_KEY))
{
PKT_public_key *pk = ctx->last_pkt.pkt.public_key;
pk->keyorg = rt.keyorg;
pk->keyupdate = rt.keyupdate;
pk->updateurl = rt.url;
rt.url = NULL;
}
err = 0;
leave:
xfree (rt.url);
free_packet (NULL, ctx); /* This sets ctx->last_pkt to NULL. */
iobuf_skip_rest (inp, pktlen, 0);
return err;
}
static int
parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * pkt, int new_ctb, int partial)
{
int rc = 0;
int mode, namelen;
PKT_plaintext *pt;
byte *p;
int c, i;
if (!partial && pktlen < 6)
{
log_error ("packet(%d) too short (%lu)\n", pkttype, (ulong) pktlen);
if (list_mode)
es_fputs (":literal data packet: [too short]\n", listfp);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
mode = iobuf_get_noeof (inp);
if (pktlen)
pktlen--;
namelen = iobuf_get_noeof (inp);
if (pktlen)
pktlen--;
/* Note that namelen will never exceed 255 bytes. */
pt = pkt->pkt.plaintext =
xmalloc (sizeof *pkt->pkt.plaintext + namelen - 1);
pt->new_ctb = new_ctb;
pt->mode = mode;
pt->namelen = namelen;
pt->is_partial = partial;
if (pktlen)
{
for (i = 0; pktlen > 4 && i < namelen; pktlen--, i++)
pt->name[i] = iobuf_get_noeof (inp);
}
else
{
for (i = 0; i < namelen; i++)
if ((c = iobuf_get (inp)) == -1)
break;
else
pt->name[i] = c;
}
/* Fill up NAME so that a check with valgrind won't complain about
* reading from uninitialized memory. This case may be triggred by
* corrupted packets. */
for (; i < namelen; i++)
pt->name[i] = 0;
pt->timestamp = read_32 (inp);
if (pktlen)
pktlen -= 4;
pt->len = pktlen;
pt->buf = inp;
if (list_mode)
{
es_fprintf (listfp, ":literal data packet:\n"
"\tmode %c (%X), created %lu, name=\"",
mode >= ' ' && mode < 'z' ? mode : '?', mode,
(ulong) pt->timestamp);
for (p = pt->name, i = 0; i < namelen; p++, i++)
{
if (*p >= ' ' && *p <= 'z')
es_putc (*p, listfp);
else
es_fprintf (listfp, "\\x%02x", *p);
}
es_fprintf (listfp, "\",\n\traw data: ");
if (partial)
es_fprintf (listfp, "unknown length\n");
else
es_fprintf (listfp, "%lu bytes\n", (ulong) pt->len);
}
leave:
return rc;
}
static int
parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * pkt, int new_ctb)
{
PKT_compressed *zd;
/* PKTLEN is here 0, but data follows (this should be the last
object in a file or the compress algorithm should know the
length). */
(void) pkttype;
(void) pktlen;
zd = pkt->pkt.compressed = xmalloc (sizeof *pkt->pkt.compressed);
zd->algorithm = iobuf_get_noeof (inp);
zd->len = 0; /* not used */
zd->new_ctb = new_ctb;
zd->buf = inp;
if (list_mode)
es_fprintf (listfp, ":compressed packet: algo=%d\n", zd->algorithm);
return 0;
}
static int
parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * pkt, int new_ctb, int partial)
{
int rc = 0;
PKT_encrypted *ed;
unsigned long orig_pktlen = pktlen;
ed = pkt->pkt.encrypted = xmalloc (sizeof *pkt->pkt.encrypted);
/* ed->len is set below. */
ed->extralen = 0; /* Unknown here; only used in build_packet. */
ed->buf = NULL;
ed->new_ctb = new_ctb;
ed->is_partial = partial;
ed->aead_algo = 0;
ed->cipher_algo = 0; /* Only used with AEAD. */
ed->chunkbyte = 0; /* Only used with AEAD. */
if (pkttype == PKT_ENCRYPTED_MDC)
{
/* Fixme: add some pktlen sanity checks. */
int version;
version = iobuf_get_noeof (inp);
if (orig_pktlen)
pktlen--;
if (version != 1)
{
log_error ("encrypted_mdc packet with unknown version %d\n",
version);
if (list_mode)
es_fputs (":encrypted data packet: [unknown version]\n", listfp);
/*skip_rest(inp, pktlen); should we really do this? */
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
ed->mdc_method = DIGEST_ALGO_SHA1;
}
else
ed->mdc_method = 0;
/* A basic sanity check. We need at least an 8 byte IV plus the 2
detection bytes. Note that we don't known the algorithm and thus
we may only check against the minimum blocksize. */
if (orig_pktlen && pktlen < 10)
{
/* Actually this is blocksize+2. */
log_error ("packet(%d) too short\n", pkttype);
if (list_mode)
es_fputs (":encrypted data packet: [too short]\n", listfp);
rc = GPG_ERR_INV_PACKET;
iobuf_skip_rest (inp, pktlen, partial);
goto leave;
}
/* Store the remaining length of the encrypted data (i.e. without
the MDC version number but with the IV etc.). This value is
required during decryption. */
ed->len = pktlen;
if (list_mode)
{
if (orig_pktlen)
es_fprintf (listfp, ":encrypted data packet:\n\tlength: %lu\n",
orig_pktlen);
else
es_fprintf (listfp, ":encrypted data packet:\n\tlength: unknown\n");
if (ed->mdc_method)
es_fprintf (listfp, "\tmdc_method: %d\n", ed->mdc_method);
}
ed->buf = inp;
leave:
return rc;
}
/* Note, that this code is not anymore used in real life because the
MDC checking is now done right after the decryption in
decrypt_data. */
static int
parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * pkt, int new_ctb)
{
int rc = 0;
PKT_mdc *mdc;
byte *p;
(void) pkttype;
mdc = pkt->pkt.mdc = xmalloc (sizeof *pkt->pkt.mdc);
if (list_mode)
es_fprintf (listfp, ":mdc packet: length=%lu\n", pktlen);
if (!new_ctb || pktlen != 20)
{
log_error ("mdc_packet with invalid encoding\n");
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
p = mdc->hash;
for (; pktlen; pktlen--, p++)
*p = iobuf_get_noeof (inp);
leave:
return rc;
}
static gpg_error_t
parse_encrypted_aead (iobuf_t inp, int pkttype, unsigned long pktlen,
PACKET *pkt, int partial)
{
int rc = 0;
PKT_encrypted *ed;
unsigned long orig_pktlen = pktlen;
int version;
ed = pkt->pkt.encrypted = xtrymalloc (sizeof *pkt->pkt.encrypted);
if (!ed)
return gpg_error_from_syserror ();
ed->len = 0;
ed->extralen = 0; /* (only used in build_packet.) */
ed->buf = NULL;
ed->new_ctb = 1; /* (packet number requires a new CTB anyway.) */
ed->is_partial = partial;
ed->mdc_method = 0;
/* A basic sanity check. We need one version byte, one algo byte,
* one aead algo byte, one chunkbyte, at least 15 byte IV. */
if (orig_pktlen && pktlen < 19)
{
log_error ("packet(%d) too short\n", pkttype);
if (list_mode)
es_fputs (":aead encrypted packet: [too short]\n", listfp);
rc = gpg_error (GPG_ERR_INV_PACKET);
iobuf_skip_rest (inp, pktlen, partial);
goto leave;
}
version = iobuf_get_noeof (inp);
if (orig_pktlen)
pktlen--;
if (version != 1)
{
log_error ("aead encrypted packet with unknown version %d\n",
version);
if (list_mode)
es_fputs (":aead encrypted packet: [unknown version]\n", listfp);
/*skip_rest(inp, pktlen); should we really do this? */
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
ed->cipher_algo = iobuf_get_noeof (inp);
if (orig_pktlen)
pktlen--;
ed->aead_algo = iobuf_get_noeof (inp);
if (orig_pktlen)
pktlen--;
ed->chunkbyte = iobuf_get_noeof (inp);
if (orig_pktlen)
pktlen--;
/* Store the remaining length of the encrypted data. We read the
* rest during decryption. */
ed->len = pktlen;
if (list_mode)
{
es_fprintf (listfp, ":aead encrypted packet: cipher=%u aead=%u cb=%u\n",
ed->cipher_algo, ed->aead_algo, ed->chunkbyte);
if (orig_pktlen)
es_fprintf (listfp, "\tlength: %lu\n", orig_pktlen);
else
es_fprintf (listfp, "\tlength: unknown\n");
}
ed->buf = inp;
leave:
return rc;
}
/*
* This packet is internally generated by us (in armor.c) to transfer
* some information to the lower layer. To make sure that this packet
* is really a GPG faked one and not one coming from outside, we
* first check that there is a unique tag in it.
*
* The format of such a control packet is:
* n byte session marker
* 1 byte control type CTRLPKT_xxxxx
* m byte control data
*/
static int
parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int partial)
{
byte *p;
const byte *sesmark;
size_t sesmarklen;
int i;
(void) pkttype;
if (list_mode)
es_fprintf (listfp, ":packet 63: length %lu ", pktlen);
sesmark = get_session_marker (&sesmarklen);
if (pktlen < sesmarklen + 1) /* 1 is for the control bytes */
goto skipit;
for (i = 0; i < sesmarklen; i++, pktlen--)
{
if (sesmark[i] != iobuf_get_noeof (inp))
goto skipit;
}
if (pktlen > 4096)
goto skipit; /* Definitely too large. We skip it to avoid an
overflow in the malloc. */
if (list_mode)
es_fputs ("- gpg control packet", listfp);
packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control
+ pktlen - 1);
packet->pkt.gpg_control->control = iobuf_get_noeof (inp);
pktlen--;
packet->pkt.gpg_control->datalen = pktlen;
p = packet->pkt.gpg_control->data;
for (; pktlen; pktlen--, p++)
*p = iobuf_get_noeof (inp);
return 0;
skipit:
if (list_mode)
{
int c;
i = 0;
es_fprintf (listfp, "- private (rest length %lu)\n", pktlen);
if (partial)
{
while ((c = iobuf_get (inp)) != -1)
dump_hex_line (c, &i);
}
else
{
for (; pktlen; pktlen--)
{
dump_hex_line ((c = iobuf_get (inp)), &i);
if (c == -1)
break;
}
}
es_putc ('\n', listfp);
}
iobuf_skip_rest (inp, pktlen, 0);
return gpg_error (GPG_ERR_INV_PACKET);
}
/* Create a GPG control packet to be used internally as a placeholder. */
PACKET *
create_gpg_control (ctrlpkttype_t type, const byte * data, size_t datalen)
{
PACKET *packet;
byte *p;
if (!data)
datalen = 0;
packet = xmalloc (sizeof *packet);
init_packet (packet);
packet->pkttype = PKT_GPG_CONTROL;
packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control + datalen);
packet->pkt.gpg_control->control = type;
packet->pkt.gpg_control->datalen = datalen;
p = packet->pkt.gpg_control->data;
for (; datalen; datalen--, p++)
*p = *data++;
return packet;
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Jan 30, 8:16 PM (1 d, 12 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a2/9d/b98de57e60b339028cc4e1726aab

Event Timeline