Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F35134829
parse-packet.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
71 KB
Subscribers
None
parse-packet.c
View Options
/* parse-packet.c - read packets
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007, 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 <http://www.gnu.org/licenses/>.
*/
#include
<config.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<assert.h>
#include
"gpg.h"
#include
"util.h"
#include
"packet.h"
#include
"iobuf.h"
#include
"cipher.h"
#include
"filter.h"
#include
"photoid.h"
#include
"options.h"
#include
"main.h"
#include
"i18n.h"
static
int
mpi_print_mode
;
static
int
list_mode
;
static
estream_t
listfp
;
static
int
parse
(
IOBUF
inp
,
PACKET
*
pkt
,
int
onlykeypkts
,
off_t
*
retpos
,
int
*
skip
,
IOBUF
out
,
int
do_skip
#ifdef 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
,
int
partial
);
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
void
parse_trust
(
IOBUF
inp
,
int
pkttype
,
unsigned
long
pktlen
,
PACKET
*
packet
);
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
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
);
static
unsigned
short
read_16
(
IOBUF
inp
)
{
unsigned
short
a
;
a
=
iobuf_get_noeof
(
inp
)
<<
8
;
a
|=
iobuf_get_noeof
(
inp
);
return
a
;
}
static
unsigned
long
read_32
(
IOBUF
inp
)
{
unsigned
long
a
;
a
=
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 with MSB first (left padded with zeroes to align
* on a byte boundary). */
static
gcry_mpi_t
mpi_read
(
iobuf_t
inp
,
unsigned
int
*
ret_nread
,
int
secure
)
{
/*FIXME: Needs to be synced with gnupg14/mpi/mpicoder.c */
int
c
,
c1
,
c2
,
i
;
unsigned
int
nbits
,
nbytes
;
size_t
nread
=
0
;
gcry_mpi_t
a
=
NULL
;
byte
*
buf
=
NULL
;
byte
*
p
;
if
((
c
=
c1
=
iobuf_get
(
inp
))
==
-1
)
goto
leave
;
nbits
=
c
<<
8
;
if
((
c
=
c2
=
iobuf_get
(
inp
))
==
-1
)
goto
leave
;
nbits
|=
c
;
if
(
nbits
>
MAX_EXTERN_MPI_BITS
)
{
log_error
(
"mpi too large (%u bits)
\n
"
,
nbits
);
goto
leave
;
}
nread
=
2
;
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
++
)
{
p
[
i
+
2
]
=
iobuf_get
(
inp
)
&
0xff
;
nread
++
;
}
if
(
nread
>=
2
&&
!
(
buf
[
0
]
<<
8
|
buf
[
1
]))
{
/* Libgcrypt < 1.5.0 accidently rejects zero-length (i.e. zero)
MPIs. We fix this here. */
a
=
gcry_mpi_new
(
0
);
}
else
{
if
(
gcry_mpi_scan
(
&
a
,
GCRYMPI_FMT_PGP
,
buf
,
nread
,
&
nread
))
a
=
NULL
;
}
leave
:
gcry_free
(
buf
);
if
(
nread
>
*
ret_nread
)
log_bug
(
"mpi larger than packet"
);
else
*
ret_nread
=
nread
;
return
a
;
}
int
set_packet_list_mode
(
int
mode
)
{
int
old
=
list_mode
;
list_mode
=
mode
;
/* FIXME(gcrypt) mpi_print_mode = DBG_MPI; */
/* We use stdout print 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 some code may switch the option
value later back to 1 and we want to have all output to the same
stream.
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 mdoe only with a special option. */
if
(
!
listfp
)
listfp
=
opt
.
list_packets
==
2
?
es_stdout
:
es_stderr
;
return
old
;
}
static
void
unknown_pubkey_warning
(
int
algo
)
{
static
byte
unknown_pubkey_algos
[
256
];
algo
&=
0xff
;
if
(
!
unknown_pubkey_algos
[
algo
])
{
if
(
opt
.
verbose
)
log_info
(
_
(
"can't handle public key algorithm %d
\n
"
),
algo
);
unknown_pubkey_algos
[
algo
]
=
1
;
}
}
/* Parse a packet and return it in packet structure.
* Returns: 0 := valid packet in pkt
* -1 := no more packets
* >0 := error
* Note: The function may return an error and a partly valid packet;
* caller must free this packet. */
#ifdef DEBUG_PARSE_PACKET
int
dbg_parse_packet
(
IOBUF
inp
,
PACKET
*
pkt
,
const
char
*
dbg_f
,
int
dbg_l
)
{
int
skip
,
rc
;
do
{
rc
=
parse
(
inp
,
pkt
,
0
,
NULL
,
&
skip
,
NULL
,
0
,
"parse"
,
dbg_f
,
dbg_l
);
}
while
(
skip
);
return
rc
;
}
#else
/*!DEBUG_PARSE_PACKET*/
int
parse_packet
(
IOBUF
inp
,
PACKET
*
pkt
)
{
int
skip
,
rc
;
do
{
rc
=
parse
(
inp
,
pkt
,
0
,
NULL
,
&
skip
,
NULL
,
0
);
}
while
(
skip
);
return
rc
;
}
#endif
/*!DEBUG_PARSE_PACKET*/
/*
* Like parse packet, but only return secret or public (sub)key
* packets.
*/
#ifdef DEBUG_PARSE_PACKET
int
dbg_search_packet
(
IOBUF
inp
,
PACKET
*
pkt
,
off_t
*
retpos
,
int
with_uid
,
const
char
*
dbg_f
,
int
dbg_l
)
{
int
skip
,
rc
;
do
{
rc
=
parse
(
inp
,
pkt
,
with_uid
?
2
:
1
,
retpos
,
&
skip
,
NULL
,
0
,
"search"
,
dbg_f
,
dbg_l
);
}
while
(
skip
);
return
rc
;
}
#else
/*!DEBUG_PARSE_PACKET*/
int
search_packet
(
IOBUF
inp
,
PACKET
*
pkt
,
off_t
*
retpos
,
int
with_uid
)
{
int
skip
,
rc
;
do
{
rc
=
parse
(
inp
,
pkt
,
with_uid
?
2
:
1
,
retpos
,
&
skip
,
NULL
,
0
);
}
while
(
skip
);
return
rc
;
}
#endif
/*!DEBUG_PARSE_PACKET*/
/*
* Copy all packets from INP to OUT, thereby removing unused spaces.
*/
#ifdef DEBUG_PARSE_PACKET
int
dbg_copy_all_packets
(
IOBUF
inp
,
IOBUF
out
,
const
char
*
dbg_f
,
int
dbg_l
)
{
PACKET
pkt
;
int
skip
,
rc
=
0
;
do
{
init_packet
(
&
pkt
);
}
while
(
!
(
rc
=
parse
(
inp
,
&
pkt
,
0
,
NULL
,
&
skip
,
out
,
0
,
"copy"
,
dbg_f
,
dbg_l
)));
return
rc
;
}
#else
/*!DEBUG_PARSE_PACKET*/
int
copy_all_packets
(
IOBUF
inp
,
IOBUF
out
)
{
PACKET
pkt
;
int
skip
,
rc
=
0
;
do
{
init_packet
(
&
pkt
);
}
while
(
!
(
rc
=
parse
(
inp
,
&
pkt
,
0
,
NULL
,
&
skip
,
out
,
0
)));
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)
*/
#ifdef DEBUG_PARSE_PACKET
int
dbg_copy_some_packets
(
IOBUF
inp
,
IOBUF
out
,
off_t
stopoff
,
const
char
*
dbg_f
,
int
dbg_l
)
{
PACKET
pkt
;
int
skip
,
rc
=
0
;
do
{
if
(
iobuf_tell
(
inp
)
>=
stopoff
)
return
0
;
init_packet
(
&
pkt
);
}
while
(
!
(
rc
=
parse
(
inp
,
&
pkt
,
0
,
NULL
,
&
skip
,
out
,
0
,
"some"
,
dbg_f
,
dbg_l
)));
return
rc
;
}
#else
/*!DEBUG_PARSE_PACKET*/
int
copy_some_packets
(
IOBUF
inp
,
IOBUF
out
,
off_t
stopoff
)
{
PACKET
pkt
;
int
skip
,
rc
=
0
;
do
{
if
(
iobuf_tell
(
inp
)
>=
stopoff
)
return
0
;
init_packet
(
&
pkt
);
}
while
(
!
(
rc
=
parse
(
inp
,
&
pkt
,
0
,
NULL
,
&
skip
,
out
,
0
)));
return
rc
;
}
#endif
/*!DEBUG_PARSE_PACKET*/
/*
* Skip over N packets
*/
#ifdef DEBUG_PARSE_PACKET
int
dbg_skip_some_packets
(
IOBUF
inp
,
unsigned
n
,
const
char
*
dbg_f
,
int
dbg_l
)
{
int
skip
,
rc
=
0
;
PACKET
pkt
;
for
(;
n
&&
!
rc
;
n
--
)
{
init_packet
(
&
pkt
);
rc
=
parse
(
inp
,
&
pkt
,
0
,
NULL
,
&
skip
,
NULL
,
1
,
"skip"
,
dbg_f
,
dbg_l
);
}
return
rc
;
}
#else
/*!DEBUG_PARSE_PACKET*/
int
skip_some_packets
(
IOBUF
inp
,
unsigned
n
)
{
int
skip
,
rc
=
0
;
PACKET
pkt
;
for
(;
n
&&
!
rc
;
n
--
)
{
init_packet
(
&
pkt
);
rc
=
parse
(
inp
,
&
pkt
,
0
,
NULL
,
&
skip
,
NULL
,
1
);
}
return
rc
;
}
#endif
/*!DEBUG_PARSE_PACKET*/
/*
* Parse packet. Stores 1 at SKIP 1 if the packet should be skipped;
* this is the case if either ONLYKEYPKTS is set and the parsed packet
* isn't a key packet or the packet-type is 0, indicating deleted
* stuff. If OUT is not NULL, a special copymode is used.
*/
static
int
parse
(
IOBUF
inp
,
PACKET
*
pkt
,
int
onlykeypkts
,
off_t
*
retpos
,
int
*
skip
,
IOBUF
out
,
int
do_skip
#ifdef DEBUG_PARSE_PACKET
,
const
char
*
dbg_w
,
const
char
*
dbg_f
,
int
dbg_l
#endif
)
{
int
rc
=
0
,
c
,
ctb
,
pkttype
,
lenbytes
;
unsigned
long
pktlen
;
byte
hdr
[
8
];
int
hdrlen
;
int
new_ctb
=
0
,
partial
=
0
;
int
with_uid
=
(
onlykeypkts
==
2
);
*
skip
=
0
;
assert
(
!
pkt
->
pkt
.
generic
);
if
(
retpos
)
*
retpos
=
iobuf_tell
(
inp
);
if
((
ctb
=
iobuf_get
(
inp
))
==
-1
)
{
rc
=
-1
;
goto
leave
;
}
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
;
}
pktlen
=
0
;
new_ctb
=
!!
(
ctb
&
0x40
);
if
(
new_ctb
)
{
pkttype
=
ctb
&
0x3f
;
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
)
{
pktlen
=
(
hdr
[
hdrlen
++
]
=
iobuf_get_noeof
(
inp
))
<<
24
;
pktlen
|=
(
hdr
[
hdrlen
++
]
=
iobuf_get_noeof
(
inp
))
<<
16
;
pktlen
|=
(
hdr
[
hdrlen
++
]
=
iobuf_get_noeof
(
inp
))
<<
8
;
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
;
}
pktlen
|=
(
hdr
[
hdrlen
++
]
=
c
);
}
else
/* Partial body length. */
{
switch
(
pkttype
)
{
case
PKT_PLAINTEXT
:
case
PKT_ENCRYPTED
:
case
PKT_ENCRYPTED_MDC
:
case
PKT_COMPRESSED
:
iobuf_set_partial_block_mode
(
inp
,
c
&
0xff
);
pktlen
=
0
;
/* To indicate partial length. */
partial
=
1
;
break
;
default
:
log_error
(
"%s: partial length for invalid"
" packet type %d
\n
"
,
iobuf_where
(
inp
),
pkttype
);
rc
=
gpg_error
(
GPG_ERR_INV_PACKET
);
goto
leave
;
}
}
}
else
{
pkttype
=
(
ctb
>>
2
)
&
0xf
;
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
;
pktlen
|=
hdr
[
hdrlen
++
]
=
iobuf_get_noeof
(
inp
);
}
}
}
if
(
pktlen
==
(
unsigned
long
)
(
-1
))
{
/* 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
)
{
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
)
;
else
if
(
do_skip
||
!
pkttype
||
(
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
)
{
#ifdef 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
}
pkt
->
pkttype
=
pkttype
;
rc
=
G10ERR_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
:
parse_trust
(
inp
,
pkttype
,
pktlen
,
pkt
);
rc
=
0
;
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_GPG_CONTROL
:
rc
=
parse_gpg_control
(
inp
,
pkttype
,
pktlen
,
pkt
,
partial
);
break
;
case
PKT_MARKER
:
rc
=
parse_marker
(
inp
,
pkttype
,
pktlen
);
break
;
default
:
skip_packet
(
inp
,
pkttype
,
pktlen
,
partial
);
break
;
}
leave
:
/* FIXME: Do we leak in case of an error? */
if
(
!
rc
&&
iobuf_error
(
inp
))
rc
=
G10ERR_INV_KEYRING
;
return
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
;
}
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
,
100
))
!=
-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
,
100
))
!=
-1
)
if
((
rc
=
iobuf_write
(
out
,
buf
,
n
)))
return
rc
;
/* write error */
}
else
{
for
(;
pktlen
;
pktlen
-=
n
)
{
n
=
pktlen
>
100
?
100
:
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
;
}
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
);
}
static
void
*
read_rest
(
IOBUF
inp
,
size_t
pktlen
,
int
partial
)
{
byte
*
p
;
int
i
;
if
(
partial
)
{
log_error
(
"read_rest: can't store stream data
\n
"
);
p
=
NULL
;
}
else
{
p
=
xmalloc
(
pktlen
);
for
(
i
=
0
;
pktlen
;
pktlen
--
,
i
++
)
p
[
i
]
=
iobuf_get
(
inp
);
}
return
p
;
}
/* Read a special size+body from INP. On success store an opaque MPI
with it at R_DATA. On error return an error code and store NULL at
R_DATA. Even in the error case store the number of read bytes at
R_NREAD. The caller shall pass the remaining size of the packet in
PKTLEN. */
static
gpg_error_t
read_size_body
(
iobuf_t
inp
,
int
pktlen
,
size_t
*
r_nread
,
gcry_mpi_t
*
r_data
)
{
char
buffer
[
256
];
char
*
tmpbuf
;
int
i
,
c
,
nbytes
;
*
r_nread
=
0
;
*
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
--
;
++*
r_nread
;
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
);
++*
r_nread
;
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
"
);
iobuf_skip_rest
(
inp
,
pktlen
,
0
);
return
G10ERR_INVALID_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
,
hash_algo
,
seskeylen
,
minlen
;
if
(
pktlen
<
4
)
{
log_error
(
"packet(%d) too short
\n
"
,
pkttype
);
rc
=
gpg_error
(
GPG_ERR_INV_PACKET
);
goto
leave
;
}
version
=
iobuf_get_noeof
(
inp
);
pktlen
--
;
if
(
version
!=
4
)
{
log_error
(
"packet(%d) with unknown version %d
\n
"
,
pkttype
,
version
);
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
);
rc
=
gpg_error
(
GPG_ERR_INV_PACKET
);
goto
leave
;
}
cipher_algo
=
iobuf_get_noeof
(
inp
);
pktlen
--
;
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
);
goto
leave
;
}
if
(
minlen
>
pktlen
)
{
log_error
(
"packet with S2K %d too short
\n
"
,
s2kmode
);
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
->
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
(
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
"
));
}
assert
(
!
pktlen
);
if
(
list_mode
)
{
es_fprintf
(
listfp
,
":symkey enc packet: version %d, cipher %d, s2k %d, hash %d"
,
version
,
cipher_algo
,
s2kmode
,
hash_algo
);
if
(
seskeylen
)
es_fprintf
(
listfp
,
", seskey %d bits"
,
(
seskeylen
-
1
)
*
8
);
es_fprintf
(
listfp
,
"
\n
"
);
if
(
s2kmode
==
1
||
s2kmode
==
3
)
{
es_fprintf
(
listfp
,
"
\t
salt "
);
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
;
}
static
int
parse_pubkeyenc
(
IOBUF
inp
,
int
pkttype
,
unsigned
long
pktlen
,
PACKET
*
packet
)
{
unsigned
int
n
;
int
rc
=
0
;
int
i
,
ndata
;
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
);
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
);
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
,
"
\t
unsupported algorithm %d
\n
"
,
k
->
pubkey_algo
);
unknown_pubkey_warning
(
k
->
pubkey_algo
);
k
->
data
[
0
]
=
NULL
;
/* No need to store the encrypted data. */
}
else
{
for
(
i
=
0
;
i
<
ndata
;
i
++
)
{
if
(
k
->
pubkey_algo
==
PUBKEY_ALGO_ECDH
&&
i
==
1
)
{
rc
=
read_size_body
(
inp
,
pktlen
,
&
n
,
k
->
data
+
i
);
pktlen
-=
n
;
}
else
{
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
)
{
es_fprintf
(
listfp
,
"
\t
data: "
);
mpi_print
(
listfp
,
k
->
data
[
i
],
mpi_print_mode
);
es_putc
(
'\n'
,
listfp
);
}
}
}
leave
:
iobuf_skip_rest
(
inp
,
pktlen
,
0
);
return
rc
;
}
static
void
dump_sig_subpkt
(
int
hashed
,
int
type
,
int
critical
,
const
byte
*
buffer
,
size_t
buflen
,
size_t
length
)
{
const
char
*
p
=
NULL
;
int
i
;
/* 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
,
"
\t
subpkt %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
--
;
es_fprintf
(
listfp
,
"
\t
%s%ssubpkt %d len %u ("
,
/*) */
critical
?
"critical "
:
""
,
hashed
?
"hashed "
:
""
,
type
,
(
unsigned
)
length
);
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
(
buffer_to_u32
(
buffer
)));
break
;
case
SIGSUBPKT_SIG_EXPIRE
:
if
(
length
>=
4
)
{
if
(
buffer_to_u32
(
buffer
))
es_fprintf
(
listfp
,
"sig expires after %s"
,
strtimevalue
(
buffer_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:
\"
%s
\"
"
,
buffer
);
break
;
case
SIGSUBPKT_REVOCABLE
:
if
(
length
)
es_fprintf
(
listfp
,
"%srevocable"
,
*
buffer
?
""
:
"not "
);
break
;
case
SIGSUBPKT_KEY_EXPIRE
:
if
(
length
>=
4
)
{
if
(
buffer_to_u32
(
buffer
))
es_fprintf
(
listfp
,
"key expires after %s"
,
strtimevalue
(
buffer_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_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
)
buffer_to_u32
(
buffer
),
(
ulong
)
buffer_to_u32
(
buffer
+
4
));
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
(
"key server preferences:"
,
listfp
);
for
(
i
=
0
;
i
<
length
;
i
++
)
es_fprintf
(
listfp
,
" %02X"
,
buffer
[
i
]);
break
;
case
SIGSUBPKT_PREF_KS
:
es_fputs
(
"preferred key server: "
,
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
;
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_HASH
:
case
SIGSUBPKT_PREF_COMPR
:
case
SIGSUBPKT_POLICY
:
case
SIGSUBPKT_PREF_KS
:
case
SIGSUBPKT_FEATURES
:
case
SIGSUBPKT_REGEXP
:
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_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
;
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
)
{
if
(
len
==
32
&&
memcmp
(
name
,
"preferred-email-encoding@pgp.com"
,
32
)
==
0
)
return
1
;
if
(
len
==
21
&&
memcmp
(
name
,
"pka-address@gnupg.org"
,
21
)
==
0
)
return
1
;
return
0
;
}
static
int
can_handle_critical
(
const
byte
*
buffer
,
size_t
n
,
int
type
)
{
switch
(
type
)
{
case
SIGSUBPKT_NOTATION
:
if
(
n
>=
8
)
return
can_handle_critical_notation
(
buffer
+
8
,
(
buffer
[
4
]
<<
8
)
|
buffer
[
5
]);
else
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_PREF_SYM
:
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
:
/* Is it enough to show the policy or keyserver? */
case
SIGSUBPKT_POLICY
:
case
SIGSUBPKT_PREF_KS
:
return
1
;
default
:
return
0
;
}
}
const
byte
*
enum_sig_subpkt
(
const
subpktarea_t
*
pktbuf
,
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
;
int
seq
=
0
;
int
reqseq
=
start
?
*
start
:
0
;
if
(
!
critical
)
critical
=
&
critical_dummy
;
if
(
!
pktbuf
||
reqseq
==
-1
)
{
/* return some value different from NULL to indicate that
* there is no critical bit we do not understand. The caller
* will never use the value. Yes I know, it is an ugly hack */
return
reqtype
==
SIGSUBPKT_TEST_CRITICAL
?
(
const
byte
*
)
&
pktbuf
:
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
=
(
buffer
[
0
]
<<
24
)
|
(
buffer
[
1
]
<<
16
)
|
(
buffer
[
2
]
<<
8
)
|
buffer
[
3
];
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
;
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
)
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
);
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
)
return
buffer
;
/* Used as True to indicate that there is no. */
/* Critical bit we don't understand. */
if
(
start
)
*
start
=
-1
;
return
NULL
;
/* End of packets; not found. */
too_short
:
if
(
opt
.
verbose
)
log_info
(
"buffer shorter than subpacket
\n
"
);
if
(
start
)
*
start
=
-1
;
return
NULL
;
}
const
byte
*
parse_sig_subpkt
(
const
subpktarea_t
*
buffer
,
sigsubpkttype_t
reqtype
,
size_t
*
ret_n
)
{
return
enum_sig_subpkt
(
buffer
,
reqtype
,
ret_n
,
NULL
,
NULL
);
}
const
byte
*
parse_sig_subpkt2
(
PKT_signature
*
sig
,
sigsubpkttype_t
reqtype
,
size_t
*
ret_n
)
{
const
byte
*
p
;
p
=
parse_sig_subpkt
(
sig
->
hashed
,
reqtype
,
ret_n
);
if
(
!
p
)
p
=
parse_sig_subpkt
(
sig
->
unhashed
,
reqtype
,
ret_n
);
return
p
;
}
/* Find all revocation keys. Look in hashed area only. */
void
parse_revkeys
(
PKT_signature
*
sig
)
{
struct
revocation_key
*
revkey
;
int
seq
=
0
;
size_t
len
;
if
(
sig
->
sig_class
!=
0x1F
)
return
;
while
((
revkey
=
(
struct
revocation_key
*
)
enum_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_REV_KEY
,
&
len
,
&
seq
,
NULL
)))
{
if
(
len
==
sizeof
(
struct
revocation_key
)
&&
(
revkey
->
class
&
0x80
))
/* 0x80 bit must be set. */
{
sig
->
revkey
=
xrealloc
(
sig
->
revkey
,
sizeof
(
struct
revocation_key
*
)
*
(
sig
->
numrevkeys
+
1
));
sig
->
revkey
[
sig
->
numrevkeys
]
=
revkey
;
sig
->
numrevkeys
++
;
}
}
}
int
parse_signature
(
IOBUF
inp
,
int
pkttype
,
unsigned
long
pktlen
,
PKT_signature
*
sig
)
{
int
md5_len
=
0
;
unsigned
n
;
int
is_v4
=
0
;
int
rc
=
0
;
int
i
,
ndata
;
if
(
pktlen
<
16
)
{
log_error
(
"packet(%d) too short
\n
"
,
pkttype
);
goto
leave
;
}
sig
->
version
=
iobuf_get_noeof
(
inp
);
pktlen
--
;
if
(
sig
->
version
==
4
)
is_v4
=
1
;
else
if
(
sig
->
version
!=
2
&&
sig
->
version
!=
3
)
{
log_error
(
"packet(%d) with unknown version %d
\n
"
,
pkttype
,
sig
->
version
);
rc
=
gpg_error
(
GPG_ERR_INV_PACKET
);
goto
leave
;
}
if
(
!
is_v4
)
{
md5_len
=
iobuf_get_noeof
(
inp
);
pktlen
--
;
}
sig
->
sig_class
=
iobuf_get_noeof
(
inp
);
pktlen
--
;
if
(
!
is_v4
)
{
sig
->
timestamp
=
read_32
(
inp
);
pktlen
-=
4
;
sig
->
keyid
[
0
]
=
read_32
(
inp
);
pktlen
-=
4
;
sig
->
keyid
[
1
]
=
read_32
(
inp
);
pktlen
-=
4
;
}
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_v4
)
/* Read subpackets. */
{
n
=
read_16
(
inp
);
pktlen
-=
2
;
/* Length of hashed data. */
if
(
n
>
10000
)
{
log_error
(
"signature packet: hashed data too long
\n
"
);
rc
=
G10ERR_INVALID_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
"
);
rc
=
-1
;
goto
leave
;
}
pktlen
-=
n
;
}
n
=
read_16
(
inp
);
pktlen
-=
2
;
/* Length of unhashed data. */
if
(
n
>
10000
)
{
log_error
(
"signature packet: unhashed data too long
\n
"
);
rc
=
G10ERR_INVALID_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
"
);
rc
=
-1
;
goto
leave
;
}
pktlen
-=
n
;
}
}
if
(
pktlen
<
5
)
/* Sanity check. */
{
log_error
(
"packet(%d) too short
\n
"
,
pkttype
);
rc
=
G10ERR_INVALID_PACKET
;
goto
leave
;
}
sig
->
digest_start
[
0
]
=
iobuf_get_noeof
(
inp
);
pktlen
--
;
sig
->
digest_start
[
1
]
=
iobuf_get_noeof
(
inp
);
pktlen
--
;
if
(
is_v4
&&
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
->
hashed
,
SIGSUBPKT_TEST_CRITICAL
,
NULL
)
||
!
parse_sig_subpkt
(
sig
->
unhashed
,
SIGSUBPKT_TEST_CRITICAL
,
NULL
))
sig
->
flags
.
unknown_critical
=
1
;
p
=
parse_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_SIG_CREATED
,
NULL
);
if
(
p
)
sig
->
timestamp
=
buffer_to_u32
(
p
);
else
if
(
!
(
sig
->
pubkey_algo
>=
100
&&
sig
->
pubkey_algo
<=
110
)
&&
opt
.
verbose
)
log_info
(
"signature packet without timestamp
\n
"
);
p
=
parse_sig_subpkt2
(
sig
,
SIGSUBPKT_ISSUER
,
NULL
);
if
(
p
)
{
sig
->
keyid
[
0
]
=
buffer_to_u32
(
p
);
sig
->
keyid
[
1
]
=
buffer_to_u32
(
p
+
4
);
}
else
if
(
!
(
sig
->
pubkey_algo
>=
100
&&
sig
->
pubkey_algo
<=
110
)
&&
opt
.
verbose
)
log_info
(
"signature packet without keyid
\n
"
);
p
=
parse_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_SIG_EXPIRE
,
NULL
);
if
(
p
&&
buffer_to_u32
(
p
))
sig
->
expiredate
=
sig
->
timestamp
+
buffer_to_u32
(
p
);
if
(
sig
->
expiredate
&&
sig
->
expiredate
<=
make_timestamp
())
sig
->
flags
.
expired
=
1
;
p
=
parse_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_POLICY
,
NULL
);
if
(
p
)
sig
->
flags
.
policy_url
=
1
;
p
=
parse_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_PREF_KS
,
NULL
);
if
(
p
)
sig
->
flags
.
pref_ks
=
1
;
p
=
parse_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_NOTATION
,
NULL
);
if
(
p
)
sig
->
flags
.
notation
=
1
;
p
=
parse_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_REVOCABLE
,
NULL
);
if
(
p
&&
*
p
==
0
)
sig
->
flags
.
revocable
=
0
;
p
=
parse_sig_subpkt
(
sig
->
hashed
,
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
->
hashed
,
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
,
NULL
);
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
"
"
\t
version %d, created %lu, md5len %d, sigclass 0x%02x
\n
"
"
\t
digest 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_v4
)
{
parse_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_LIST_HASHED
,
NULL
);
parse_sig_subpkt
(
sig
->
unhashed
,
SIGSUBPKT_LIST_UNHASHED
,
NULL
);
}
}
ndata
=
pubkey_get_nsig
(
sig
->
pubkey_algo
);
if
(
!
ndata
)
{
if
(
list_mode
)
es_fprintf
(
listfp
,
"
\t
unknown 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
=
G10ERR_INVALID_PACKET
;
}
else
{
sig
->
data
[
0
]
=
gcry_mpi_set_opaque
(
NULL
,
read_rest
(
inp
,
pktlen
,
0
),
pktlen
*
8
);
pktlen
=
0
;
}
}
else
{
for
(
i
=
0
;
i
<
ndata
;
i
++
)
{
n
=
pktlen
;
sig
->
data
[
i
]
=
mpi_read
(
inp
,
&
n
,
0
);
pktlen
-=
n
;
if
(
list_mode
)
{
es_fprintf
(
listfp
,
"
\t
data: "
);
mpi_print
(
listfp
,
sig
->
data
[
i
],
mpi_print_mode
);
es_putc
(
'\n'
,
listfp
);
}
if
(
!
sig
->
data
[
i
])
rc
=
G10ERR_INVALID_PACKET
;
}
}
leave
:
iobuf_skip_rest
(
inp
,
pktlen
,
0
);
return
rc
;
}
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
);
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
);
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
"
"
\t
version %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
gcry_mpi_t
read_protected_v3_mpi
(
IOBUF
inp
,
unsigned
long
*
length
)
{
int
c
;
unsigned
int
nbits
,
nbytes
;
unsigned
char
*
buf
,
*
p
;
gcry_mpi_t
val
;
if
(
*
length
<
2
)
{
log_error
(
"mpi too small
\n
"
);
return
NULL
;
}
if
((
c
=
iobuf_get
(
inp
))
==
-1
)
return
NULL
;
--*
length
;
nbits
=
c
<<
8
;
if
((
c
=
iobuf_get
(
inp
))
==
-1
)
return
NULL
;
--*
length
;
nbits
|=
c
;
if
(
nbits
>
16384
)
{
log_error
(
"mpi too large (%u bits)
\n
"
,
nbits
);
return
NULL
;
}
nbytes
=
(
nbits
+
7
)
/
8
;
buf
=
p
=
xmalloc
(
2
+
nbytes
);
*
p
++
=
nbits
>>
8
;
*
p
++
=
nbits
;
for
(;
nbytes
&&
*
length
;
nbytes
--
,
--*
length
)
*
p
++
=
iobuf_get
(
inp
);
if
(
nbytes
)
{
log_error
(
"packet shorter than mpi
\n
"
);
xfree
(
buf
);
return
NULL
;
}
/* Convert buffer into an opaque MPI. */
val
=
gcry_mpi_set_opaque
(
NULL
,
buf
,
(
p
-
buf
)
*
8
);
return
val
;
}
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
n
;
unsigned
long
timestamp
,
expiredate
,
max_expiredate
;
int
npkey
,
nskey
;
int
is_v4
=
0
;
int
rc
=
0
;
u32
keyid
[
2
];
PKT_public_key
*
pk
;
(
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_noeof
(
inp
);
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_v4
=
1
;
else
if
(
version
!=
2
&&
version
!=
3
)
{
log_error
(
"packet(%d) with unknown version %d
\n
"
,
pkttype
,
version
);
err
=
gpg_error
(
GPG_ERR_INV_PACKET
);
goto
leave
;
}
if
(
pktlen
<
11
)
{
log_error
(
"packet(%d) too short
\n
"
,
pkttype
);
err
=
gpg_error
(
GPG_ERR_INV_PACKET
);
goto
leave
;
}
timestamp
=
read_32
(
inp
);
pktlen
-=
4
;
if
(
is_v4
)
{
expiredate
=
0
;
/* have to get it from the selfsignature */
max_expiredate
=
0
;
}
else
{
unsigned
short
ndays
;
ndays
=
read_16
(
inp
);
pktlen
-=
2
;
if
(
ndays
)
expiredate
=
timestamp
+
ndays
*
86400L
;
else
expiredate
=
0
;
max_expiredate
=
expiredate
;
}
algorithm
=
iobuf_get_noeof
(
inp
);
pktlen
--
;
if
(
list_mode
)
es_fprintf
(
listfp
,
":%s key packet:
\n
"
"
\t
version %d, algo %d, created %lu, expires %lu
\n
"
,
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
);
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
,
"
\t
unknown algorithm %d
\n
"
,
algorithm
);
unknown_pubkey_warning
(
algorithm
);
}
if
(
!
npkey
)
{
/* Unknown algorithm - put data into an opaque MPI. */
pk
->
pkey
[
0
]
=
gcry_mpi_set_opaque
(
NULL
,
read_rest
(
inp
,
pktlen
,
0
),
pktlen
*
8
);
pktlen
=
0
;
goto
leave
;
}
else
{
for
(
i
=
0
;
i
<
npkey
;
i
++
)
{
if
((
algorithm
==
PUBKEY_ALGO_ECDSA
||
algorithm
==
PUBKEY_ALGO_ECDH
)
&&
(
i
==
0
||
i
==
2
))
{
err
=
read_size_body
(
inp
,
pktlen
,
&
n
,
pk
->
pkey
+
i
);
pktlen
-=
n
;
}
else
{
n
=
pktlen
;
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
,
"
\t
pkey[%d]: "
,
i
);
mpi_print
(
listfp
,
pk
->
pkey
[
i
],
mpi_print_mode
);
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
;
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
(
ski
->
algo
)
{
ski
->
is_protected
=
1
;
ski
->
s2k
.
count
=
0
;
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 on it 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
(
is_v4
&&
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
,
"
\t
unknown 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. */
switch
(
ski
->
s2k
.
mode
)
{
case
1
:
case
3
:
for
(
i
=
0
;
i
<
8
&&
pktlen
;
i
++
,
pktlen
--
)
temp
[
i
]
=
iobuf_get_noeof
(
inp
);
memcpy
(
ski
->
s2k
.
salt
,
temp
,
8
);
break
;
}
/* Check the mode. */
switch
(
ski
->
s2k
.
mode
)
{
case
0
:
if
(
list_mode
)
es_fprintf
(
listfp
,
"
\t
simple S2K"
);
break
;
case
1
:
if
(
list_mode
)
es_fprintf
(
listfp
,
"
\t
salted S2K"
);
break
;
case
3
:
if
(
list_mode
)
es_fprintf
(
listfp
,
"
\t
iter+salt S2K"
);
break
;
case
1001
:
if
(
list_mode
)
es_fprintf
(
listfp
,
"
\t
gnu-dummy S2K"
);
break
;
case
1002
:
if
(
list_mode
)
es_fprintf
(
listfp
,
"
\t
gnu-divert-to-card S2K"
);
break
;
default
:
if
(
list_mode
)
es_fprintf
(
listfp
,
"
\t
unknown %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
)
{
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
);
}
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
(
inp
);
pktlen
--
;
if
(
list_mode
)
es_fprintf
(
listfp
,
"
\t
protect count: %lu
\n
"
,
(
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
,
"
\t
protect 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. */
ski
->
ivlen
=
openpgp_cipher_blocklen
(
ski
->
algo
);
assert
(
ski
->
ivlen
<=
sizeof
(
temp
));
if
(
ski
->
s2k
.
mode
==
1001
)
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
&&
pktlen
;
i
++
,
pktlen
--
)
temp
[
i
]
=
iobuf_get_noeof
(
inp
);
if
(
list_mode
)
{
es_fprintf
(
listfp
,
ski
->
s2k
.
mode
==
1002
?
"
\t
serial-number: "
:
"
\t
protect IV: "
);
for
(
i
=
0
;
i
<
ski
->
ivlen
;
i
++
)
es_fprintf
(
listfp
,
" %02x"
,
temp
[
i
]);
es_putc
(
'\n'
,
listfp
);
}
memcpy
(
ski
->
iv
,
temp
,
ski
->
ivlen
);
}
/* 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
(
is_v4
&&
ski
->
is_protected
)
{
/* Ugly: The length is encrypted too, so we read all stuff
* up to the end of the packet into the first SKEY
* element. */
pk
->
pkey
[
npkey
]
=
gcry_mpi_set_opaque
(
NULL
,
read_rest
(
inp
,
pktlen
,
0
),
pktlen
*
8
);
pktlen
=
0
;
if
(
list_mode
)
es_fprintf
(
listfp
,
"
\t
skey[%d]: [v4 protected]
\n
"
,
npkey
);
}
else
{
/* The v3 method: The mpi length is not encrypted. */
for
(
i
=
npkey
;
i
<
nskey
;
i
++
)
{
if
(
ski
->
is_protected
)
{
pk
->
pkey
[
i
]
=
read_protected_v3_mpi
(
inp
,
&
pktlen
);
if
(
list_mode
)
es_fprintf
(
listfp
,
"
\t
skey[%d]: [v3 protected]
\n
"
,
i
);
}
else
{
n
=
pktlen
;
pk
->
pkey
[
i
]
=
mpi_read
(
inp
,
&
n
,
0
);
pktlen
-=
n
;
if
(
list_mode
)
{
es_fprintf
(
listfp
,
"
\t
skey[%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
;
ski
->
csum
=
read_16
(
inp
);
pktlen
-=
2
;
if
(
list_mode
)
es_fprintf
(
listfp
,
"
\t
checksum: %04hx
\n
"
,
ski
->
csum
);
}
}
if
(
list_mode
)
es_fprintf
(
listfp
,
"
\t
keyid: %08lX%08lX
\n
"
,
(
ulong
)
keyid
[
0
],
(
ulong
)
keyid
[
1
]);
leave
:
iobuf_skip_rest
(
inp
,
pktlen
,
0
);
return
rc
;
}
/* 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
=
(
buffer
[
0
]
<<
24
)
|
(
buffer
[
1
]
<<
16
)
|
(
buffer
[
2
]
<<
8
)
|
buffer
[
3
];
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
;
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
)
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
>
2048
)
{
log_error
(
"packet(%d) too large
\n
"
,
pkttype
);
iobuf_skip_rest
(
inp
,
pktlen
,
0
);
return
G10ERR_INVALID_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
)
{
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
;
#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
);
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
>
65536
)
{
log_error
(
"packet(%d) too large
\n
"
,
pkttype
);
iobuf_skip_rest
(
inp
,
pktlen
,
0
);
return
G10ERR_INVALID_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
;
}
static
void
parse_trust
(
IOBUF
inp
,
int
pkttype
,
unsigned
long
pktlen
,
PACKET
*
pkt
)
{
int
c
;
(
void
)
pkttype
;
if
(
pktlen
)
{
c
=
iobuf_get_noeof
(
inp
);
pktlen
--
;
pkt
->
pkt
.
ring_trust
=
xmalloc
(
sizeof
*
pkt
->
pkt
.
ring_trust
);
pkt
->
pkt
.
ring_trust
->
trustval
=
c
;
pkt
->
pkt
.
ring_trust
->
sigcache
=
0
;
if
(
!
c
&&
pktlen
==
1
)
{
c
=
iobuf_get_noeof
(
inp
);
pktlen
--
;
/* We require that bit 7 of the sigcache is 0 (easier eof
handling). */
if
(
!
(
c
&
0x80
))
pkt
->
pkt
.
ring_trust
->
sigcache
=
c
;
}
if
(
list_mode
)
es_fprintf
(
listfp
,
":trust packet: flag=%02x sigcache=%02x
\n
"
,
pkt
->
pkt
.
ring_trust
->
trustval
,
pkt
->
pkt
.
ring_trust
->
sigcache
);
}
else
{
if
(
list_mode
)
es_fprintf
(
listfp
,
":trust packet: empty
\n
"
);
}
iobuf_skip_rest
(
inp
,
pktlen
,
0
);
}
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
);
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
;
}
pt
->
timestamp
=
read_32
(
inp
);
if
(
pktlen
)
pktlen
-=
4
;
pt
->
len
=
pktlen
;
pt
->
buf
=
inp
;
pktlen
=
0
;
if
(
list_mode
)
{
es_fprintf
(
listfp
,
":literal data packet:
\n
"
"
\t
mode %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\t
raw 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
;
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
);
/*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
);
rc
=
G10ERR_INVALID_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\t
length: %lu
\n
"
,
orig_pktlen
);
else
es_fprintf
(
listfp
,
":encrypted data packet:
\n\t
length: unknown
\n
"
);
if
(
ed
->
mdc_method
)
es_fprintf
(
listfp
,
"
\t
mdc_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
;
}
/*
* This packet is internally generated by us (ibn 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 comming 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
)
puts
(
"- gpg control packet"
);
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
;
packet
=
xmalloc
(
sizeof
*
packet
);
init_packet
(
packet
);
packet
->
pkttype
=
PKT_GPG_CONTROL
;
packet
->
pkt
.
gpg_control
=
xmalloc
(
sizeof
*
packet
->
pkt
.
gpg_control
+
datalen
-
1
);
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
Details
Attached
Mime Type
text/x-c
Expires
Sun, Feb 1, 7:56 PM (8 h, 24 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
7b/f9/97b2d7cc84f2caa4bcb033146e3f
Attached To
rG GnuPG
Event Timeline
Log In to Comment