gpgme/src/parsetlv.c
Werner Koch 830e017e5d
core: Protect against a theoretical integer overflow in parsetlv.c
* src/parsetlv.c (_gpgme_parse_tlv): Detect integer overflow.
--

Although there is no concrete case where we use for example
(to.nhdr+ti.length), it feels safer to protect against this anyway.
2022-10-24 13:50:41 +02:00

108 lines
2.6 KiB
C

/* parsetlv.c - ASN.1 TLV functions
* Copyright (C) 2005, 2007, 2008, 2012 g10 Code GmbH
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <https://gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "parsetlv.h"
/* Simple but pretty complete ASN.1 BER parser. Parse the data at the
address of BUFFER with a length given at the address of SIZE. On
success return 0 and update BUFFER and SIZE to point to the value.
Do not update them on error. The information about the object are
stored in the caller allocated TI structure. */
int
_gpgme_parse_tlv (char const **buffer, size_t *size, tlvinfo_t *ti)
{
int c;
unsigned long tag;
const unsigned char *buf = (const unsigned char *)(*buffer);
size_t length = *size;
ti->cls = 0;
ti->tag = 0;
ti->is_cons = 0;
ti->is_ndef = 0;
ti->length = 0;
ti->nhdr = 0;
if (!length)
return -1;
c = *buf++; length--; ++ti->nhdr;
ti->cls = (c & 0xc0) >> 6;
ti->is_cons = !!(c & 0x20);
tag = c & 0x1f;
if (tag == 0x1f)
{
tag = 0;
do
{
tag <<= 7;
if (!length)
return -1;
c = *buf++; length--; ++ti->nhdr;
tag |= c & 0x7f;
}
while (c & 0x80);
}
ti->tag = tag;
if (!length)
return -1;
c = *buf++; length--; ++ti->nhdr;
if ( !(c & 0x80) )
ti->length = c;
else if (c == 0x80)
ti->is_ndef = 1;
else if (c == 0xff)
return -1;
else
{
unsigned long len = 0;
int count = (c & 0x7f);
if (count > sizeof (len) || count > sizeof (size_t))
return -1;
for (; count; count--)
{
len <<= 8;
if (!length)
return -1;
c = *buf++; length--; ++ti->nhdr;
len |= c & 0xff;
}
ti->length = len;
}
if (ti->length > ti->nhdr && (ti->nhdr + ti->length) < ti->length)
return -1; /* Integer overflow. */
*buffer = (void*)buf;
*size = length;
return 0;
}