Mercurial > illumos > onarm
diff usr/src/cmd/agents/snmp/snmplib/asn1.c @ 0:c9caec207d52 b86
Initial porting based on b86
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Tue, 02 Jun 2009 18:56:50 +0900 |
parents | |
children | 1a15d5aaf794 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/agents/snmp/snmplib/asn1.c Tue Jun 02 18:56:50 2009 +0900 @@ -0,0 +1,758 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2001,2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "@(#)asn1.c 1.10 05/06/12 SMI" + +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in.h> +#include "snmp_msg.h" +#include "asn1.h" + + +/* + * asn_parse_int - pulls a int32_t out of an ASN int type. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the end of this object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +u_char * +asn_parse_int( + u_char *data, /* IN - pointer to start of object */ + uint32_t *datalength,/* IN/OUT - number of valid bytes left in buffer */ + u_char *type, /* OUT - asn type of object */ + int32_t *intp, /* IN/OUT - pointer to start of output buffer */ + uint32_t intsize, /* IN - size of output buffer */ + char *error_label) +{ +/* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + u_char *bufp = data; + uint32_t asn_length = 0; + int32_t value = 0; + + + error_label[0] = '\0'; + + if (intsize != sizeof (int32_t)){ + (void)sprintf(error_label, ERR_MSG_NOT_LONG); + return NULL; + } + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length, error_label); + if (bufp == NULL){ + (void)sprintf(error_label, ERR_MSG_BAD_LENGTH); + return NULL; + } + /* LINTED */ + if (asn_length + (uint32_t)(bufp - data) > *datalength){ + (void)sprintf(error_label, ERR_MSG_OVERFLOW); + return NULL; + } + if (asn_length > intsize){ + (void)sprintf(error_label, ERR_MSG_DONT_SUPPORT_LARGE_INT); + return NULL; + } + /* LINTED */ + *datalength -= asn_length + (uint32_t)(bufp - data); + if (*bufp & 0x80) + value = -1; /* integer is negative */ + while(asn_length--) + value = (value << 8) | *bufp++; + *intp = value; + return bufp; +} + +/* + * asn_parse_unsigned_int - pulls an unsigned int32_t out of an ASN int type. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the end of this object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +u_char * +asn_parse_unsigned_int( + u_char *data, /* IN - pointer to start of object */ + uint32_t * datalength,/* IN/OUT - number of valid bytes left in buffer */ + u_char *type, /* OUT - asn type of object */ + int32_t *intp, /* IN/OUT - pointer to start of output buffer */ + uint32_t intsize, /* IN - size of output buffer */ + char *error_label) +{ +/* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + u_char *bufp = data; + uint32_t asn_length; + uint32_t value = 0; + + error_label[0] = '\0'; + + if (intsize != sizeof (int32_t)){ + (void)sprintf(error_label, ERR_MSG_NOT_LONG); + return NULL; + } + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length, error_label); + if (bufp == NULL){ + (void)sprintf(error_label, ERR_MSG_BAD_LENGTH); + return NULL; + } + /* LINTED */ + if (asn_length + (uint32_t)(bufp - data) > *datalength){ + (void)sprintf(error_label, ERR_MSG_OVERFLOW); + return NULL; + } + if ((asn_length > (intsize + 1)) || + ((asn_length == intsize + 1) && *bufp != 0x00)){ + (void)sprintf(error_label, ERR_MSG_DONT_SUPPORT_LARGE_INT); + return NULL; + } + /* LINTED */ + *datalength -= asn_length + (uint32_t)(bufp - data); + if (*bufp & 0x80) + value = -1U; /* integer is negative */ + while(asn_length--) + value = (value << 8) | *bufp++; + *intp = value; + return bufp; +} + + +/* + * asn_build_int - builds an ASN object containing an integer. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the end of this object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +u_char * +asn_build_int( + u_char *data, /* IN - pointer to start of output buffer */ + uint32_t * datalength,/* IN/OUT - number of valid bytes left in buffer */ + u_char type, /* IN - asn type of object */ + int32_t *intp, /* IN - pointer to start of integer */ + uint32_t intsize, /* IN - size of *intp */ + char *error_label) +{ +/* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + + int32_t integer; + uint32_t mask; + + error_label[0] = '\0'; + + if (intsize != sizeof (int32_t)) + return NULL; + integer = *intp; + /* + * 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)) && intsize > 1){ + intsize--; + integer <<= 8; + } + data = asn_build_header(data, datalength, type, intsize, error_label); + if (data == NULL) + return NULL; + if (*datalength < intsize) + return NULL; + *datalength -= intsize; + + mask = ((uint32_t) 0xFF) << (8 * (sizeof(int32_t) - 1)); + + /* mask is 0xFF000000 on a big-endian machine */ + while(intsize--){ + /* LINTED */ + *data++ = (u_char)((integer & mask) >> (8 * (sizeof(int32_t) - 1))); + integer <<= 8; + } + return data; +} + +/* + * asn_build_unsigned_int - builds an ASN object containing an integer. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the end of this object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +u_char * +asn_build_unsigned_int( + u_char *data, /* IN - pointer to start of output buffer */ + uint32_t *datalength,/* IN/OUT - number of valid bytes left in buffer */ + u_char type, /* IN - asn type of object */ + int32_t *intp, /* IN - pointer to start of int32_t integer */ + uint32_t intsize, /* IN - size of *intp */ + char *error_label) +{ +/* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + + uint32_t integer; + uint32_t mask; + int add_null_byte = 0; + + error_label[0] = '\0'; + + if (intsize != sizeof (int32_t)) + return NULL; + integer = *intp; + mask = ((uint32_t) 0xFF) << (8 * (sizeof(int32_t) - 1)); + /* mask is 0xFF000000 on a big-endian machine */ + /* LINTED */ + if ((u_char)((integer & mask) >> (8 * (sizeof(int32_t) - 1))) & 0x80){ + /* if MSB is set */ + add_null_byte = 1; + intsize++; + } 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) && intsize > 1){ + intsize--; + integer <<= 8; + } + } + data = asn_build_header(data, datalength, type, intsize, error_label); + if (data == NULL) + return NULL; + if (*datalength < intsize) + return NULL; + *datalength -= intsize; + if (add_null_byte == 1){ + *data++ = '\0'; + intsize--; + } + mask = ((uint32_t) 0xFF) << (8 * (sizeof(int32_t) - 1)); + /* mask is 0xFF000000 on a big-endian machine */ + while(intsize--){ + /* LINTED */ + *data++ = (u_char)((integer & mask) >> (8 * (sizeof(int32_t) - 1))); + integer <<= 8; + } + return data; +} + + +/* + * asn_parse_string - pulls an octet string out of an ASN octet string type. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * "string" is filled with the octet string. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +u_char * +asn_parse_string( + u_char *data, /* IN - pointer to start of object */ + uint32_t *datalength, /* IN/OUT - number of valid bytes left in buffer */ + u_char *type, /* OUT - asn type of object */ + u_char *string, /* IN/OUT - pointer to start of output buffer */ + uint32_t *strlength, /* IN/OUT - size of output buffer */ + char *error_label) +{ +/* + * ASN.1 octet string ::= primstring | cmpdstring + * primstring ::= 0x04 asnlength byte {byte}* + * cmpdstring ::= 0x24 asnlength string {string}* + * This doesn't yet support the compound string. + */ + u_char *bufp = data; + uint32_t asn_length = 0; + + + error_label[0] = '\0'; + + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length, error_label); + if (bufp == NULL) + return NULL; + /* LINTED */ + if (asn_length + (uint32_t)(bufp - data) > *datalength){ + (void)sprintf(error_label, ERR_MSG_OVERFLOW); + return NULL; + } + if (asn_length > *strlength){ + (void)sprintf(error_label, ERR_MSG_DONT_SUPPORT_LARGE_STR); + return NULL; + } + memcpy(string, bufp, asn_length); + *strlength = asn_length; + /* LINTED */ + *datalength -= asn_length + (uint32_t)(bufp - data); + return bufp + asn_length; +} + + +/* + * asn_build_string - Builds an ASN octet string object containing the input string. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +u_char * +asn_build_string( + u_char *data, /* IN - pointer to start of object */ + uint32_t *datalength, /* IN/OUT - number of valid bytes left in buffer */ + u_char type, /* IN - ASN type of string */ + u_char *string, /* IN - pointer to start of input buffer */ + uint32_t strlength, /* IN - size of input buffer */ + char *error_label) +{ +/* + * ASN.1 octet string ::= primstring | cmpdstring + * primstring ::= 0x04 asnlength byte {byte}* + * cmpdstring ::= 0x24 asnlength string {string}* + * This code will never send a compound string. + */ + + error_label[0] = '\0'; + + data = asn_build_header(data, datalength, type, strlength, error_label); + if (data == NULL) + return NULL; + if (*datalength < strlength) + return NULL; + memcpy(data, string, strlength); + *datalength -= strlength; + return data + (intptr_t)strlength; +} + + +/* + * asn_parse_header - interprets the ID and length of the current object. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * in this object following the id and length. + * + * Returns a pointer to the first byte of the contents of this object. + * Returns NULL on any error. + */ +u_char * +asn_parse_header( + u_char *data, /* IN - pointer to start of object */ + uint32_t * datalength,/* IN/OUT - number of valid bytes left in buffer */ + u_char *type, /* OUT - ASN type of object */ + char *error_label) +{ + u_char *bufp = data; + uint32_t header_len; + uint32_t asn_length = 0; + + error_label[0] = '\0'; + + /* this only works on data types < 30, i.e. no extension octets */ + if (IS_EXTENSION_ID(*bufp)){ + (void)sprintf(error_label, ERR_MSG_CANT_PROCESS_LONG_ID); + return NULL; + } + *type = *bufp; + bufp = asn_parse_length(bufp + 1, &asn_length, error_label); + if (bufp == NULL) + return NULL; + + /* LINTED */ + header_len = (uint32_t)(bufp - data); + if (header_len + asn_length > *datalength){ + (void)sprintf(error_label, ERR_MSG_ASN_LEN_TOO_LONG); + return NULL; + } + *datalength = asn_length; + return bufp; +} + +/* + * asn_build_header - builds an ASN header for an object with the ID and + * length specified. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * in this object following the id and length. + * + * This only works on data types < 30, i.e. no extension octets. + * The maximum length is 0xFFFF; + * + * Returns a pointer to the first byte of the contents of this object. + * Returns NULL on any error. + */ +u_char * +asn_build_header( + u_char *data, /* IN - pointer to start of object */ + uint32_t *datalength,/* IN/OUT - number of valid bytes left in buffer */ + u_char type, /* IN - ASN type of object */ + uint32_t length, /* IN - length of object */ + char *error_label) +{ + error_label[0] = '\0'; + + if (*datalength == 0) + return NULL; + *data++ = type; + (*datalength)--; + return asn_build_length(data, datalength, length, error_label); + +} + +/* + * asn_parse_length - interprets the length of the current object. + * On exit, length contains the value of this length field. + * + * Returns a pointer to the first byte after this length + * field (aka: the start of the data field). + * Returns NULL on any error. + */ +u_char * +asn_parse_length( + u_char *data, /* IN - pointer to start of length field */ + uint32_t *length, /* OUT - value of length field */ + char *error_label) +{ + u_char lengthbyte = *data; + + error_label[0] = '\0'; + + if (lengthbyte & ASN_LONG_LEN){ + lengthbyte &= ~ASN_LONG_LEN; /* turn MSb off */ + if (lengthbyte == 0){ + (void)sprintf(error_label, ERR_MSG_DONT_SUPPORT_INDEF_LEN); + return NULL; + } + if (lengthbyte > sizeof(int32_t)){ + (void)sprintf(error_label, ERR_MSG_DONT_SUPPORT_SUCH_LEN); + return NULL; + } + memcpy(length, data + 1, (int)lengthbyte); + *length = ntohl(*length); + *length >>= (8 * ((sizeof *length) - lengthbyte)); + return data + lengthbyte + 1; + } else { /* short asnlength */ + *length = (int32_t)lengthbyte; + return data + 1; + } +} + +u_char * +asn_build_length( + u_char *data, /* IN - pointer to start of object */ + uint32_t *datalength, /* IN/OUT - number of valid bytes left in buffer */ + uint32_t length, /* IN - length of object */ + char *error_label) +{ + u_char *start_data = data; + + error_label[0] = '\0'; + + /* no indefinite lengths sent */ + if (length < 0x80){ + if (*datalength < 1) + goto errout; + /* LINTED */ + *data++ = (u_char)length; + } else if (length <= 0xFF){ + if (*datalength < 2) + goto errout; + /* LINTED */ + *data++ = (u_char)(0x01 | ASN_LONG_LEN); + /* LINTED */ + *data++ = (u_char)length; + } else { /* 0xFF < length <= 0xFFFF */ + if (*datalength < 3) + goto errout; + /* LINTED */ + *data++ = (u_char)(0x02 | ASN_LONG_LEN); + /* LINTED */ + *data++ = (u_char)((length >> 8) & 0xFF); + /* LINTED */ + *data++ = (u_char)(length & 0xFF); + } + /* LINTED */ + *datalength -= (uint32_t)(data - start_data); + return data; + +errout: + (void)sprintf(error_label, ERR_MSG_BUILD_LENGTH); + return NULL; +} + +/* + * asn_parse_objid - pulls an object indentifier out of an ASN object identifier type. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * "objid" is filled with the object identifier. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +u_char * +asn_parse_objid( + u_char *data, /* IN - pointer to start of object */ + uint32_t *datalength, /* IN/OUT - number of valid bytes left in buffer */ + u_char *type, /* OUT - ASN type of object */ + Subid *objid, /* IN/OUT - pointer to start of output buffer */ + int32_t *objidlength, /* IN/OUT - number of sub-id's in objid */ + char *error_label) +{ +/* + * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}* + * subidentifier ::= {leadingbyte}* lastbyte + * leadingbyte ::= 1 7bitvalue + * lastbyte ::= 0 7bitvalue + */ + u_char *bufp = data; + Subid *oidp = objid + 1; + uint32_t subidentifier; + int32_t length; + uint32_t asn_length = 0; + + + error_label[0] = '\0'; + + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length, error_label); + if (bufp == NULL) + return NULL; + /* LINTED */ + if (asn_length + (uint32_t)(bufp - data) > *datalength){ + (void)sprintf(error_label, ERR_MSG_OVERFLOW); + return NULL; + } + /* LINTED */ + *datalength -= asn_length + (uint32_t)(bufp - data); + + length = asn_length; + (*objidlength)--; /* account for expansion of first byte */ + while (length > 0 && (*objidlength)-- > 0){ + subidentifier = 0; + do { /* shift and add in low order 7 bits */ + subidentifier = (subidentifier << 7) + (*(u_char *)bufp & ~ASN_BIT8); + length--; + } while (*(u_char *)bufp++ & ASN_BIT8); /* last byte has high bit clear */ + if (subidentifier > (uint32_t)MAX_SUBID){ + (void)sprintf(error_label, ERR_MSG_SUBIDENTIFIER_TOO_LONG); + return NULL; + } + *oidp++ = (Subid)subidentifier; + } + + /* + * The first two subidentifiers are encoded into the first component + * with the value (X * 40) + Y, where: + * X is the value of the first subidentifier. + * Y is the value of the second subidentifier. + */ + subidentifier = (uint32_t)objid[1]; + /* LINTED */ + objid[1] = (u_char)(subidentifier % 0x28); + /* LINTED */ + objid[0] = (u_char)((subidentifier - objid[1]) / 0x28); + + /* LINTED */ + *objidlength = (int32_t)(oidp - objid); + return bufp; +} + +/* + * asn_build_objid - Builds an ASN object identifier object containing the input string. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +u_char * +asn_build_objid( + u_char *data, /* IN - pointer to start of object */ + uint32_t *datalength, /* IN/OUT - number of valid bytes left in buffer */ + u_char type, /* IN - ASN type of object */ + Subid *objid, /* IN - pointer to start of input buffer */ + int32_t objidlength, /* IN - number of sub-id's in objid */ + char *error_label) +{ +/* + * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}* + * subidentifier ::= {leadingbyte}* lastbyte + * leadingbyte ::= 1 7bitvalue + * lastbyte ::= 0 7bitvalue + */ + + uchar_t buf[MAX_OID_LEN * 5]; + uchar_t *bp = buf; + Subid objbuf[MAX_OID_LEN]; + Subid *op = objbuf; + uint32_t asnlength; + uint32_t subid, mask, testmask; + int bits, testbits; + + error_label[0] = '\0'; + + if (objidlength > MAX_OID_LEN) + return (NULL); + + memcpy(objbuf, objid, objidlength * (int32_t)sizeof (Subid)); + /* transform size in bytes to size in subid's */ + /* encode the first two components into the first subidentifier */ + op[1] = op[1] + (op[0] * 40); + op++; + objidlength--; + + while (objidlength-- > 0){ + subid = *op++; + mask = 0x7F; /* handle subid == 0 case */ + bits = 0; + /* testmask *MUST* !!!! be of an unsigned type */ + for (testmask = 0x7F, testbits = 0; testmask != 0; + testmask <<= 7, testbits += 7) { + if (subid & testmask) { /* if any bits set */ + mask = testmask; + bits = testbits; + } + } + /* mask can't be zero here */ + for (; mask != 0x7F; mask >>= 7, bits -= 7){ + if (mask == 0x1E00000) + /* fix a mask that got truncated above */ + mask = 0xFE00000; + /* LINTED */ + *bp++ = (uchar_t)(((subid & mask) >> bits) | ASN_BIT8); + } + /* LINTED */ + *bp++ = (uchar_t)(subid & mask); + } + /* LINTED */ + asnlength = (uint32_t)(bp - buf); + data = asn_build_header(data, datalength, type, asnlength, error_label); + if (data == NULL) + return (NULL); + if (*datalength < asnlength) + return (NULL); + memcpy(data, buf, asnlength); + *datalength -= asnlength; + return (data + (uintptr_t)asnlength); +} + +/* + * asn_parse_null - Interprets an ASN null type. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +u_char * +asn_parse_null( + u_char *data, /* IN - pointer to start of object */ + uint32_t *datalength, /* IN/OUT - number of valid bytes left in buffer */ + u_char *type, /* OUT - ASN type of object */ + char *error_label) +{ +/* + * ASN.1 null ::= 0x05 0x00 + */ + u_char *bufp = data; + uint32_t asn_length = 0; + + + error_label[0] = '\0'; + + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length, error_label); + if (bufp == NULL) + return NULL; + if (asn_length != 0){ + (void)sprintf(error_label, ERR_MSG_MALFORMED_NULL); + return NULL; + } + /* LINTED */ + *datalength -= (uint32_t)(bufp - data); + return bufp + (uintptr_t)asn_length; +} + +/* + * asn_build_null - Builds an ASN null object. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +u_char * +asn_build_null( + u_char *data, /* IN - pointer to start of object */ + uint32_t *datalength, /* IN/OUT - number of valid bytes left in buffer */ + u_char type, /* IN - ASN type of object */ + char *error_label) +{ +/* + * ASN.1 null ::= 0x05 0x00 + */ + error_label[0] = '\0'; + + return asn_build_header(data, datalength, type, 0, error_label); +}