📄 asn1.c
字号:
/**** @file asn1.c**** @author Daniel Roelker <droelker@sourcefire.com>**** @brief ASN.1 Decoding API for BER and DER encodings.**** Copyright (C) 2004, Daniel Roelker and Sourcefire, Inc.**** ASN.1 decoding functions that incorporate an internal stack for** processing. That way we don't have to worry about attackers trying** to overload the machine stack.**** Handles both DER and BER encodings, and also the indefinite encoding ** that BER supports. Lots of functionality can be added on top of** this library. SNMP will probably be the first.**** NOTES:** - Stop using global variables so we can have multiple instances,** but we don't need that functionality right now.*/ #include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <sys/types.h>#ifdef WIN32#include <winsock.h>#endif#include "asn1.h"/*** Macros*/#define SF_ASN1_CLASS(c) (((u_char)c) & SF_ASN1_CLASS_MASK)#define SF_ASN1_FLAG(c) (((u_char)c) & SF_ASN1_FLAG_MASK)#define SF_ASN1_TAG(c) (((u_char)c) & SF_ASN1_TAG_MASK)#define SF_ASN1_LEN_EXT(c) (((u_char)c) & SF_BER_LEN_MASK)#define ASN1_OOB(s,e,d) (!(((s) <= (d)) && ((d) < (e))))#define ASN1_FATAL_ERR(e) ((e) < 0)#define ASN1_NONFATAL_ERR(e) ((e) > 0)#define ASN1_MAX_STACK 128static ASN1_TYPE *g_asn1_mem = NULL;static int g_asn1_max_nodes = 0;static int g_asn1_node_index = 0;/*** NAME** asn1_init_node_index::*//**** This function should get called whenever we decode a new ASN.1** string to initialize the memory.**** @return void*/static void asn1_init_node_index(void){ g_asn1_node_index = 0; return;}/*** NAME** asn1_node_alloc::*//**** Allocate an ASN1_NODE.**** @return ASN1_TYPE ***** @retval NULL memory allocation failed** @retval !NULL function successful*/static ASN1_TYPE *asn1_node_alloc(void){ if(!g_asn1_mem || (g_asn1_max_nodes <= g_asn1_node_index)) return NULL; return &g_asn1_mem[g_asn1_node_index++];}/*** NAME** asn1_init_mem::*//**** This function initializes the number of nodes that we want to track in** an ASN.1 decode. Pass in the max number of nodes for an ASN.1 decode and** we will track that many.**** @return integer**** @retval ASN1_OK function successful** @retval ASN1_ERR_MEM_ALLOC memory allocation failed** @retval ASN1_ERR_INVALID_ARG invalid argument*/int asn1_init_mem(int iNodes){ if(iNodes <= 0) return ASN1_ERR_INVALID_ARG; /* ** This makes sure that we don't initialize multiple times. */ if(g_asn1_mem && g_asn1_max_nodes > 0) return ASN1_OK; g_asn1_mem = (ASN1_TYPE *)malloc(sizeof(ASN1_TYPE)*iNodes); if(!g_asn1_mem) return ASN1_ERR_MEM_ALLOC; g_asn1_max_nodes = iNodes; return ASN1_OK;}/*** NAME** asn1_decode_tag_num_ext::*//**** This routine decodes extended tag numbers and checks for overlong** tag numbers, etc.**** @param ASN1_DATA ptr to data** @param u_int ptr to tag num**** @return integer**** @retval ASN1_OK function successful** @retval ASN1_ERR_OVERLONG_LEN tag number too large** @retval ASN1_ERR_OOB encoding goes out of bounds** @retval ASN1_ERR_NULL_MEM function arguments are NULL*/static int asn1_decode_tag_num_ext(ASN1_DATA *asn1_data, u_int *tag_num){ int iExtension = 0; u_int new_tag_num; if(!asn1_data || !tag_num) return ASN1_ERR_NULL_MEM; *tag_num = 0; /* ** Loop through the tag type while extension bit is set */ do { /* ** Is this an extension byte? */ iExtension = SF_ASN1_LEN_EXT(*asn1_data->data); new_tag_num = ((*tag_num << 7) | (*asn1_data->data & 0x7f)); if(*tag_num != 0 && new_tag_num <= *tag_num) { return ASN1_ERR_OVERLONG_LEN; } *tag_num = new_tag_num; asn1_data->data++; if(ASN1_OOB(asn1_data->start, asn1_data->end, asn1_data->data)) { return ASN1_ERR_OOB; } } while(iExtension); return ASN1_OK;}/*** NAME** asn1_decode_ident::*//**** This function decodes the identifier byte(s) of an ASN.1 structure.** We handle long tag numbers and check for overflows in the extended** tag numbers.**** @return integer**** @retval ASN1_ERR_NULL_MEM function arguments are NULL** @retval ASN1_ERR_OOB buffer out of bounds** @retval ASN1_ERR_INVALID_BER_TAG_LEN tag num too large or bad encoding** @retval ASN1_OK function ok*/static int asn1_decode_ident(ASN1_TYPE *asn1_type, ASN1_DATA *asn1_data){ ASN1_IDENT *ident; int iRet; if(!asn1_type || !asn1_data) return ASN1_ERR_NULL_MEM; ident = &asn1_type->ident; ident->class = SF_ASN1_CLASS(*asn1_data->data); ident->flag = SF_ASN1_FLAG(*asn1_data->data); ident->tag = SF_ASN1_TAG(*asn1_data->data); asn1_data->data++; if(ASN1_OOB(asn1_data->start, asn1_data->end, asn1_data->data)) { //printf("** decode_ident: oob\n"); return ASN1_ERR_OOB; } /* ** Is tag extended? */ if(ident->tag == SF_ASN1_TAG_EXTENSION) { ident->tag_type = SF_ASN1_TAG_EXTENSION; iRet = asn1_decode_tag_num_ext(asn1_data, &ident->tag); if(iRet) { //printf("** decode_ident: ext_len error\n"); return ASN1_ERR_INVALID_BER_TAG_LEN; } } return ASN1_OK;}/*** NAME** asn1_decode_len_type::*//**** Determine the type of len encoding. Could be short, long or** indeterminate.**** @return integer**** @retval SF_BER_LEN_DEF_LONG extended length** @retval SF_BER_LEN_DEF_SHORT one byte length < 127** @retval SF_BER_LEN_INDEF indeterminate length*/static int asn1_decode_len_type(u_char *data){ int iExt; iExt = SF_ASN1_LEN_EXT(*data); if(iExt) { if(*data & 0x7f) { return SF_BER_LEN_DEF_LONG; } else { return SF_BER_LEN_INDEF; } } return SF_BER_LEN_DEF_SHORT;}/*** NAME** asn1_decode_len_ext::*//**** Decode the extended length version. Basically we read the first** byte for the number of bytes in the extended length. We then read** that number of bytes to determine the length. If the number of bytes** in the length is greater than our variable, then we return ** ASN1_ERR_OVERLONG_LEN, and exit decoding.**** @return integer**** @retval ASN1_ERR_NULL_MEM function arguments NULL** @retval ASN1_ERR_OVERLONG_LEN length to long for us to decode** @retval ASN1_ERR_OOB out of bounds condition** @retval ASN1_OK function successful*/static int asn1_decode_len_ext(ASN1_DATA *asn1_data, u_int *size){ int iBytes; int iCtr; u_int new_size; if(!asn1_data || !size) return ASN1_ERR_NULL_MEM; *size = 0; iBytes = (*asn1_data->data & 0x7f); asn1_data->data++; if(ASN1_OOB(asn1_data->start, asn1_data->end, asn1_data->data)) { return ASN1_ERR_OOB; } for(iCtr = 0; iCtr < iBytes; iCtr++) { new_size = ((*size << 8) | (*asn1_data->data)); /* ** If we've just added some data to the size, and ** we are still the same or less than the previous ** size, we've just overflowed our variable */ if(*size != 0 && new_size <= *size) { return ASN1_ERR_OVERLONG_LEN; } *size = new_size; asn1_data->data++; if(ASN1_OOB(asn1_data->start, asn1_data->end, asn1_data->data)) { /* ** Check to see if this was just an extended length that was zero at ** the end of the buffer. If it was, then return normal. */ if(*size == 0 && (iCtr+1) == iBytes) break; return ASN1_ERR_OOB; } } return ASN1_OK;}/*** NAME** asn1_decode_len::*//**** This function decodes the ASN.1 type length. Determines what type of** BER encoding is used for the length and decodes that length.**** @return integer**** @retval ASN1_ERR_NULL_MEM function arguments NULL** @retval ASN1_ERR_FATAL should never get this** @retval ASN1_ERR_OOB out of bounds condition** @retval ASN1_OK function successful*/static int asn1_decode_len(ASN1_TYPE *asn1_type, ASN1_DATA *asn1_data){ ASN1_LEN *len; int iRet; if(!asn1_type || !asn1_data) return ASN1_ERR_NULL_MEM; len = &asn1_type->len; len->type = asn1_decode_len_type(asn1_data->data); switch(len->type) { case SF_BER_LEN_DEF_SHORT: len->size = *asn1_data->data; (asn1_data->data)++; if(ASN1_OOB(asn1_data->start, asn1_data->end, asn1_data->data)) { /* ** Only return OOB if the short length wasn't zero. Otherwise, ** it's a valid encoding. */ if(len->size != 0) return ASN1_ERR_OOB; } break; case SF_BER_LEN_DEF_LONG: iRet = asn1_decode_len_ext(asn1_data, &len->size); if(iRet) return iRet; break; case SF_BER_LEN_INDEF: /* ** Not sure what to do here, so we'll just set the length ** to 0 and proceed for now. */ len->size = 0; asn1_data->data++; if(ASN1_OOB(asn1_data->start, asn1_data->end, asn1_data->data)) return ASN1_ERR_OOB; break; default: /* ** This should be one of the three values. So we are in ** error condition. */ return ASN1_ERR_FATAL; } return ASN1_OK;}/*** NAME** asn1_is_eoc::*//**** This function checks and ASN1_TYPE for end-of-content encoding. This** doesn't determine that this is what it is, but what it could be.**** @return int**** @retval 0 not EOC** @retval 1 is EOC*/static int asn1_is_eoc(ASN1_TYPE *asn1){ if(!asn1) return 0; if(asn1->ident.class == 0x00 && asn1->ident.flag == 0x00 && asn1->ident.tag == 0x00 && asn1->len.type == SF_BER_LEN_DEF_SHORT && asn1->len.size == 0) { return 1; } return 0;}/*** NAME** asn1_decode_type::*//**** This function decodes an ASN1_TYPE structure. It processes the type in** three parts.**** 1) Identifier** 2) Length** 3) Data**** The data processing skips over primitive data (if it can) and processes** construct data (if it can).**** This function also updates the data and len ptrs so we continue moving** through the data.**** @return integer**** @retval ASN1_OK function successful** @retval ASN1_ERR_MEM_ALLOC memory allocation failed** @retval ASN1_ERR_INVALID_INDEF_LEN invalid indefinite encoding** @retval ASN1_ERR_INVALID_ARG invalid argument** @retval ASN1_ERR_OOB out of bounds*/static int asn1_decode_type(u_char **data, u_int *len, ASN1_TYPE **asn1_type){ ASN1_DATA asn1data; u_int uiRawLen; int iRet; if(!*data) return ASN1_ERR_INVALID_ARG; *asn1_type = NULL; /* ** Check len first, because if it's 0, then we already decoded a valid ** construct. We let the caller know this, by returning OK, but setting ** the asn1_type ptr to NULL. */ if(*len == 0) return ASN1_OK; if(ASN1_OOB(*data, (*data) + *len, *data)) return ASN1_ERR_OOB; *asn1_type = asn1_node_alloc(); if(*asn1_type == NULL) { return ASN1_ERR_MEM_ALLOC; } memset(*asn1_type, 0x00, sizeof(ASN1_TYPE)); asn1data.start = *data; asn1data.end = (*data) + *len; asn1data.data = *data; iRet = asn1_decode_ident(*asn1_type, &asn1data); if(iRet) { return iRet; } iRet = asn1_decode_len(*asn1_type, &asn1data); if(iRet) { return iRet; } /* ** Set this varible here, so we can set the data_len for ** indeterminate constructs. */ uiRawLen = asn1data.end - asn1data.data; /* ** This is an important check. If the length is zero, it means that ** we've either hit a zero length type or we've hit a BER indefinite ** encoding (hate those). ** ** Standard says that only constructs can have the indefinite length ** encoding, but we still need to "prove" that. Thanks M$. */ if(!(*asn1_type)->len.size) { if((*asn1_type)->len.type != SF_BER_LEN_INDEF || (*asn1_type)->ident.flag == SF_ASN1_FLAG_CONSTRUCT) { (*asn1_type)->data = asn1data.data; if((*asn1_type)->len.type == SF_BER_LEN_INDEF) { (*asn1_type)->data_len = uiRawLen; } else { /* ** If we're not an indefinite type, then we check to ** see if we are an eoc, so we don't have to check again.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -