/*
* $Id: libnet_asn1.c,v 1.1.1.3 2023/09/27 11:11:38 misho Exp $
*
* libnet 1.1
* libnet_asn1.c - Abstract Syntax Notation One routines
*
* Abstract Syntax Notation One, ASN.1
* As defined in ISO/IS 8824 and ISO/IS 8825
* This implements a subset of the above International Standards that
* is sufficient to implement SNMP.
*
* Encodes abstract data types into a machine independent stream of bytes.
*
* Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of CMU not be
* used in advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
*
* CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
* CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
* ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*
* Copyright (c) 1998 - 2004 Mike D. Schiffman <mike@infonexus.com>
* All rights reserved.
*
* Copyright (c) 1993, 1994, 1995, 1996, 1998
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "common.h"
uint8_t *
libnet_build_asn1_int(uint8_t *data, int *datalen, uint8_t type, int32_t *int_p,
int int_s)
{
/*
* ASN.1 integer ::= 0x02 asnlength byte {byte}*
*/
register int32_t integer;
register uint32_t mask;
if (int_s != sizeof (int32_t))
{
return (NULL);
}
integer = *int_p;
/*
* Truncate "unnecessary" bytes off of the most significant end of this
* 2's complement integer. There should be no sequence of 9 consecutive
* 1's or 0's at the most significant end of the integer.
*/
mask = ((uint32_t) 0x1FF) << ((8 * (sizeof (int32_t) - 1)) - 1);
/* mask is 0xFF800000 on a big-endian machine */
while ((((integer & mask) == 0) || ((integer & mask) == mask)) && int_s > 1)
{
int_s--;
integer <<= 8;
}
data = libnet_build_asn1_header(data, datalen, type, int_s);
if (data == NULL || *datalen < int_s)
{
return (NULL);
}
*datalen -= int_s;
mask = ((uint32_t) 0xFF) << (8 * (sizeof(int32_t) - 1));
/* mask is 0xFF000000 on a big-endian machine */
while (int_s--)
{
*data++ = (uint8_t)((integer & mask) >> (8 * (sizeof (int32_t) - 1)));
integer <<= 8;
}
return (data);
}
uint8_t *
libnet_build_asn1_uint(uint8_t *data, int *datalen, uint8_t type, uint32_t *int_p,
int int_s)
{
/*
* ASN.1 integer ::= 0x02 asnlength byte {byte}*
*/
register uint32_t integer;
register uint32_t mask;
int add_null_byte = 0;
if (int_s != sizeof (int32_t))
{
return (NULL);
}
integer = *int_p;
mask = ((uint32_t) 0xFF) << (8 * (sizeof (int32_t) - 1));
/* mask is 0xFF000000 on a big-endian machine */
if ((uint8_t)((integer & mask) >> (8 * (sizeof (int32_t) - 1))) & 0x80)
{
/* if MSB is set */
add_null_byte = 1;
int_s++;
}
else
{
/*
* Truncate "unnecessary" bytes off of the most significant end of this
* 2's complement integer. There should be no sequence of 9
* consecutive 1's or 0's at the most significant end of the
* integer.
*/
mask = ((uint32_t) 0x1FF) << ((8 * (sizeof(int32_t) - 1)) - 1);
/* mask is 0xFF800000 on a big-endian machine */
while (((integer & mask) == 0) && int_s > 1)
{
int_s--;
integer <<= 8;
}
}
data = libnet_build_asn1_header(data, datalen, type, int_s);
if (data == NULL || *datalen < int_s)
{
return (NULL);
}
*datalen -= int_s;
if (add_null_byte == 1)
{
*data++ = '\0';
int_s--;
}
mask = ((uint32_t) 0xFF) << (8 * (sizeof(int32_t) - 1));
/* mask is 0xFF000000 on a big-endian machine */
while (int_s--)
{
*data++ = (uint8_t)((integer & mask) >> (8 * (sizeof (int32_t) - 1)));
integer <<= 8;
}
return (data);
}
uint8_t *
libnet_build_asn1_string(uint8_t *data, int *datalen, uint8_t type,
uint8_t *string, int str_s)
{
/*
* ASN.1 octet string ::= primstring | cmpdstring
* primstring ::= 0x04 asnlength byte {byte}*
* cmpdstring ::= 0x24 asnlength string {string}*
* This code will never send a compound string.
*/
data = libnet_build_asn1_header(data, datalen, type, str_s);
if (data == NULL || *datalen < str_s)
{
return (NULL);
}
memmove(data, string, str_s);
*datalen -= str_s;
return (data + str_s);
}
uint8_t *
libnet_build_asn1_header(uint8_t *data, int *datalen, uint8_t type, int len)
{
if (*datalen < 1)
{
return (NULL);
}
*data++ = type;
(*datalen)--;
return (libnet_build_asn1_length(data, datalen, len));
}
uint8_t *
libnet_build_asn1_sequence(uint8_t *data, int *datalen, uint8_t type, int len)
{
*datalen -= 4;
if (*datalen < 0)
{
*datalen += 4; /* fix up before punting */
return (NULL);
}
*data++ = type;
*data++ = (uint8_t)(0x02 | ASN_LONG_LEN);
*data++ = (uint8_t)((len >> 8) & 0xFF);
*data++ = (uint8_t)(len & 0xFF);
return (data);
}
uint8_t *
libnet_build_asn1_length(uint8_t *data, int *datalen, int len)
{
uint8_t *start_data = data;
/* no indefinite lengths sent */
if (len < 0x80)
{
if (*datalen < 1)
{
return (NULL);
}
*data++ = (uint8_t)len;
}
else if (len <= 0xFF)
{
if (*datalen < 2)
{
return (NULL);
}
*data++ = (uint8_t)(0x01 | ASN_LONG_LEN);
*data++ = (uint8_t)len;
}
else /* 0xFF < len <= 0xFFFF */
{
if (*datalen < 3)
{
return (NULL);
}
*data++ = (uint8_t)(0x02 | ASN_LONG_LEN);
*data++ = (uint8_t)((len >> 8) & 0xFF);
*data++ = (uint8_t)(len & 0xFF);
}
*datalen -= (int)(data - start_data);
return (data);
}
uint8_t *
libnet_build_asn1_objid(uint8_t *data, int *datalen, uint8_t type, oid *objid,
int objidlen)
{
/*
* ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}*
* subidentifier ::= {leadingbyte}* lastbyte
* leadingbyte ::= 1 7bitvalue
* lastbyte ::= 0 7bitvalue
*/
int asnlen;
register oid *op = objid;
uint8_t objid_size[MAX_OID_LEN];
register uint32_t objid_val;
uint32_t first_objid_val;
register int i;
/* check if there are at least 2 sub-identifiers */
if (objidlen < 2)
{
/* there are not, so make OID have two with value of zero */
objid_val = 0;
objidlen = 2;
}
else
{
/* combine the first two values */
objid_val = (op[0] * 40) + op[1];
op += 2;
}
first_objid_val = objid_val;
/* calculate the number of bytes needed to store the encoded value */
for (i = 1, asnlen = 0;;)
{
if (objid_val < (unsigned)0x80)
{
objid_size[i] = 1;
asnlen += 1;
}
else if (objid_val < (unsigned)0x4000)
{
objid_size[i] = 2;
asnlen += 2;
}
else if (objid_val < (unsigned)0x200000)
{
objid_size[i] = 3;
asnlen += 3;
}
else if (objid_val < (unsigned)0x10000000)
{
objid_size[i] = 4;
asnlen += 4;
}
else
{
objid_size[i] = 5;
asnlen += 5;
}
i++;
if (i >= objidlen)
{
break;
}
objid_val = *op++;
}
/* store the ASN.1 tag and length */
data = libnet_build_asn1_header(data, datalen, type, asnlen);
if (data == NULL || *datalen < asnlen)
{
return (NULL);
}
/* store the encoded OID value */
for (i = 1, objid_val = first_objid_val, op = objid + 2; i < objidlen; i++)
{
if (i != 1)
{
objid_val = *op++;
}
switch (objid_size[i])
{
case 1:
*data++ = (uint8_t)objid_val;
break;
case 2:
*data++ = (uint8_t)((objid_val >> 7) | 0x80);
*data++ = (uint8_t)(objid_val & 0x07f);
break;
case 3:
*data++ = (uint8_t)((objid_val >> 14) | 0x80);
*data++ = (uint8_t)((objid_val >> 7 & 0x7f) | 0x80);
*data++ = (uint8_t)(objid_val & 0x07f);
break;
case 4:
*data++ = (uint8_t)((objid_val >> 21) | 0x80);
*data++ = (uint8_t)((objid_val >> 14 & 0x7f) | 0x80);
*data++ = (uint8_t)((objid_val >> 7 & 0x7f) | 0x80);
*data++ = (uint8_t)(objid_val & 0x07f);
break;
case 5:
*data++ = (uint8_t)((objid_val >> 28) | 0x80);
*data++ = (uint8_t)((objid_val >> 21 & 0x7f) | 0x80);
*data++ = (uint8_t)((objid_val >> 14 & 0x7f) | 0x80);
*data++ = (uint8_t)((objid_val >> 7 & 0x7f) | 0x80);
*data++ = (uint8_t)(objid_val & 0x07f);
break;
}
}
/* return the length and data ptagr */
*datalen -= asnlen;
return (data);
}
uint8_t *
libnet_build_asn1_null(uint8_t *data, int *datalen, uint8_t type)
{
/*
* ASN.1 null ::= 0x05 0x00
*/
return (libnet_build_asn1_header(data, datalen, type, 0));
}
uint8_t *
libnet_build_asn1_bitstring(uint8_t *data, int *datalen, uint8_t type,
uint8_t *string, int str_s)
{
/*
* ASN.1 bit string ::= 0x03 asnlength unused {byte}*
*/
if (str_s < 1 || *string > 7)
{
return (NULL);
}
data = libnet_build_asn1_header(data, datalen, type, str_s);
if (data == NULL || *datalen < str_s)
{
return (NULL);
}
memmove(data, string, str_s);
*datalen -= str_s;
return (data + str_s);
}
/**
* Local Variables:
* indent-tabs-mode: nil
* c-file-style: "stroustrup"
* End:
*/
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>