Files
microser/mmslib/asn1/asn1r.c
2026-06-15 15:48:16 +08:00

1694 lines
62 KiB
C

/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/* (c) Copyright Systems Integration Specialists Company, Inc., */
/* 1986 - 2009, All Rights Reserved. */
/* */
/* PROPRIETARY AND CONFIDENTIAL */
/* */
/* MODULE NAME : asn1r.c */
/* PRODUCT(S) : ASN1DE */
/* */
/* MODULE DESCRIPTION : */
/* This module contains the main ASN.1 decode and encode tools. */
/* */
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */
/* */
/* MODIFICATION LOG : */
/* Date Who Rev Comments */
/* -------- --- ------ ------------------------------------------- */
/* 07/21/09 JRB 32 Cleanup new asn1r_get_tag. */
/* 05/29/09 JRB 31 Add asn1r_decode_init, asn1r_get_tag functs. */
/* For indefinite len constr, increase initial */
/* byte_count (couldn't handle big constr). */
/* Add asn1r_strt_constr_indef (FOR TESTING). */
/* 05/28/09 JRB 30 On decode, don't log constr content(redundant)*/
/* 01/15/09 MDE 29 Added asn1_peek,made asn1r_head_decode static*/
/* 11/12/08 MDE 28 Added overreach checks to asn1r_head_decode */
/* 11/12/08 MDE 27 Removed assert to improve robustness */
/* 10/03/08 JRB 28 Prevent crashes while decoding by improving */
/* len checks & doing it BEFORE logging. */
/* 10/04/07 MDE 27 Tweaked LOGCFG_VALUE_GROUP/LOGCFGX_VALUE_MAP */
/* 12/20/06 JRB 26 Add bstrcmp. */
/* 03/17/06 JRB 25 Add asn1r_anytag_fun, ASN1_ANYTAG_METHOD. */
/* 03/15/06 JRB 24 Add asn1r_skip_elmnt. */
/* 02/28/06 EJV 23 asn1_convert_...: added typecast from time_t */
/* 12/19/05 JRB 22 Chg cr to cr_asn1, del static to fix warning.*/
/* 05/23/05 EJV 21 Add asn1LogMaskMapCtrl for parsing logcfg.xml*/
/* 07/08/04 JRB 20 Use define TIME_T_1984_JAN_1 instead of mktime*/
/* Use define SECONDS_PER_DAY from asn1r.h */
/* Add asn1_convert_timet_to_btime6. */
/* Use 0x01000000 (2**24) in fraction computations*/
/* 01/14/03 JRB 19 Avoid redundant logging of encode overrun. */
/* fin_constr decrement msg_level on encode overrun*/
/* 11/05/03 EJV 18 Fixed logging. */
/* 10/13/03 JRB 17 asn1r_decode_asn1: Chg assert to err return. */
/* 04/28/03 JRB 16 Add assert to asn1r_decode_asn1. */
/* 04/02/03 JRB 15 On unexpected tag error, log the tag. */
/* 02/07/03 JRB 14 Add bstrcpy, bvstrcpy. */
/* 10/29/02 JRB 13 Use new asn1r_magic in ASN1_ENC_CTXT. */
/* 07/03/02 EJV 12 MMS_UTC_TIME: chg name usec to fraction. */
/* 03/05/02 JRB 11 Eliminate compiler warnings. */
/* 01/25/02 JRB 10 Move INT_MAX chk to #if, avoids unreachable */
/* code. Make SD_CONST match header on *logstr. */
/* 01/22/02 JRB 09 asn1r_strt_asn1_bld set new "asn1r_buf_end". */
/* Chg asn1r_end_of_buffer to asn1r_buf_start */
/* & asn1r_field_start to asn1r_field_end */
/* & _asn1_constr_start to asn1r_constr_end */
/* in ENC_CTX (start means start, end means end)*/
/* 01/07/02 EJV 08 Added asn1_ prefix to convert_... functions. */
/* 01/04/02 EJV 07 Add convert_btod_to_utc, convert_utc_to_btod */
/* Cleanup few function header comments. */
/* Updated cr[] to 2002. */
/* 12/20/01 JRB 06 Chg ASN1_CTXT to ASN1_DEC_CTXT, ASN1_ENC_CTXT*/
/* Delete #define USE_WITH_ASN1. */
/* Del asn1_old_err_fun & *_old_id_fun. */
/* asn1r_objidcmp chged to asn1_objidcmp (same */
/* as function in asn1.c). */
/* Del unused strt_asn1r, asn1r_end_asn_bld. */
/* 11/28/01 EJV 05 fin_prim: corrected len for logging. */
/* 11/12/01 EJV 04 asn1r_decode_asn1: removed static variables */
/* 09/13/99 MDE 03 Added SD_CONST modifiers */
/* 07/29/99 MDE 02 Added TABLE method */
/* 07/26/99 MDE 01 New module, derived from asn1.c */
/************************************************************************/
#include "glbtypes.h"
#include "sysincs.h"
#include <limits.h>
#include "asn1r.h"
#include "asn1log.h"
#include "mem_chk.h"
/************************************************************************/
const ST_CHAR cr_asn1[] =
"(c) COPYRIGHT SYSTEMS INTEGRATION SPECIALISTS COMPANY INC., 1986 - 2005. All Rights Reserved.";
/************************************************************************/
/* For debug version, use a static pointer to avoid duplication of */
/* __FILE__ strings. */
#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif
#if !defined(NO_GLB_VAR_INIT)
ST_UINT asn1_debug_sel = ASN1_LOG_ERR;
#else
ST_UINT asn1_debug_sel;
#endif
#ifdef DEBUG_SISCO
SD_CONST ST_CHAR *SD_CONST _asn1_log_dec_logstr = "ASN1_LOG_DEC";
SD_CONST ST_CHAR *SD_CONST _asn1_log_enc_logstr = "ASN1_LOG_ENC";
SD_CONST ST_CHAR *SD_CONST _asn1_log_err_logstr = "ASN1_LOG_ERR";
SD_CONST ST_CHAR *SD_CONST _asn1_log_nerr_logstr = "ASN1_LOG_NERR";
LOGCFGX_VALUE_MAP asn1LogMaskMaps[] =
{
{"ASN1_LOG_ERR", ASN1_LOG_ERR, &asn1_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Error"},
{"ASN1_LOG_NERR", ASN1_LOG_NERR, &asn1_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Notice"},
{"ASN1_LOG_DEC", ASN1_LOG_DEC, &asn1_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Decode"},
{"ASN1_LOG_ENC", ASN1_LOG_ENC, &asn1_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Encode"}
};
LOGCFG_VALUE_GROUP asn1LogMaskMapCtrl =
{
{NULL,NULL},
"Asn1LogMasks", /* Parent Tag */
sizeof(asn1LogMaskMaps)/sizeof(LOGCFGX_VALUE_MAP),
asn1LogMaskMaps
};
#endif /* DEBUG_SISCO */
/************************************************************************/
/* General variables and pointers used by the ASN1 system. */
/************************************************************************/
/************************************************************************/
/* VMS Float Format select */
/* VMS has different forms of float format; select the default here */
#if !defined(NO_GLB_VAR_INIT) && defined(VAXC)
ST_INT asn1_vax_double_type = A_D_FLOAT;
#endif
#if !defined(NO_GLB_VAR_INIT) && defined( __ALPHA )
ST_INT asn1_vax_double_type = A_G_FLOAT;
#endif
#if defined(NO_GLB_VAR_INIT) && (defined( VAXC ) || defined( __ALPHA ))
ST_INT asn1_vax_double_type;
#endif
/************************************************************************/
/* Static functions in this module */
static ST_RET _asn1r_head_decode (ASN1_DEC_CTXT *ac);
static ST_VOID _do_decode_asn1 (ASN1_DEC_CTXT *ac, ST_UCHAR *ptr, ST_INT len);
static ST_BOOLEAN _indef_eoc (ASN1_DEC_CTXT *ac);
static ST_VOID _check_level_done (ASN1_DEC_CTXT *ac);
static ST_VOID _call_user (ASN1_DEC_CTXT *ac);
static ST_VOID _wr_asn1_len (ASN1_ENC_CTXT *ac, ST_INT len);
static ST_VOID _wr_ident (ASN1_ENC_CTXT *ac, ST_UINT16 id_code, ST_UINT16 tag);
#ifdef DEBUG_SISCO
static ST_VOID _list_elmnt (ASN1_DEC_CTXT *ac);
#endif
/************************************************************************/
/************************************************************************/
/* ASN.1 MESSAGE DECODE */
/************************************************************************/
/* asn1r_decode_asn1_seq */
/* This function is used to decode a ASN.1 data element when the outer */
/* constructor has been stripped. */
/************************************************************************/
ST_VOID asn1r_decode_asn1_seq (ASN1_DEC_CTXT *ac, ST_UCHAR *ptr, ST_INT len)
{
ac->_asn1_indef_track[0] = SD_FALSE; /* level 0 is always definite */
ac->_asn1_indef_track[1] = SD_FALSE; /* so is level 1 in this case */
ac->_asn1_byte_count[0] = 0; /* assumes definite */
ac->_asn1_byte_count[1] = len; /* contents of missing outer cstr */
ac->asn1r_field_ptr = ptr; /* point to first element */
ac->asn1r_msg_level = 1; /* start at message level 1 */
ac->asn1r_c_done_fun[ac->asn1r_msg_level] = NULL;
_do_decode_asn1 (ac, ptr,len);
}
/************************************************************************/
/* asn1r_decode_asn1 */
/* Function to drive the decoding of an ASN.1 encoded message. */
/* The user must set up the class or tag function ptrs, and the error */
/* and done function ptrs before calling this function. Pass the length */
/* and location of the message as inputs. */
/************************************************************************/
ST_VOID asn1r_decode_asn1 (ASN1_DEC_CTXT *ac, ST_UCHAR *ptr, ST_INT len)
{
#if !defined (MMS_LITE)
ST_UCHAR *save_asn1r_ptr = NULL;
#endif
/* The caller MUST clear ASN1_DEC_CTXT struct before calling this funct.*/
/* After clearing, caller may set a few params, so chk a param that */
/* caller would NEVER set. */
if (ac->asn1r_msg_level != 0)
{
ALOG_ERR0 ("ASN.1 decode context not initialized");
ac->asn1r_pdu_dec_err = ASN1E_DECODE_OTHER; /* set error code */
return;
}
/* initialize flags, counters, pointers for decode */
ac->asn1r_indef_flag = SD_FALSE; /* clear flag */
ac->_asn1_indef_track[0] = SD_FALSE; /* level 0 is always definite */
ac->_asn1_byte_count[0] = len; /* set # bytes left in level 0 */
ac->asn1r_msg_level = 0; /* start at message level 0 */
ac->asn1r_c_done_fun[0] = NULL;
#if !defined (MMS_LITE)
/* save the data in case we need to log it if decode fails */
if ( (asn1_debug_sel & ASN1_LOG_NERR) &&
!(asn1_debug_sel & ASN1_LOG_DEC))
{
save_asn1r_ptr = chk_malloc (len);
memcpy (save_asn1r_ptr, ptr, len);
}
#endif
_do_decode_asn1 (ac, ptr,len);
#if !defined (MMS_LITE)
if (ac->asn1r_pdu_dec_err != NO_DECODE_ERR) /* if an error was present */
{
if (save_asn1r_ptr)
{
ALOG_NERR0 ("ASN.1 decode: badly formed PDU:");
ALOG_NERRH (len, save_asn1r_ptr);
}
}
if (save_asn1r_ptr)
chk_free (save_asn1r_ptr);
#endif
}
/************************************************************************/
/* _do_decode_asn1 */
/************************************************************************/
static ST_VOID _do_decode_asn1 (ASN1_DEC_CTXT *ac, ST_UCHAR *ptr, ST_INT len)
{
ST_INT log_len;
ST_INT gone_len;
ST_INT i;
ST_RET rc;
ac->asn1r_pdu_dec_err = NO_DECODE_ERR; /* reset error flag */
ac->asn1r_decode_done = SD_FALSE; /* initialize flag */
ac->asn1r_field_ptr = ptr; /* initialize asn1_field_ptr */
ac->asn1r_done_ptr = ac->asn1r_field_ptr + len;
/* Validate length to avoid possible problems */
if (len <= 0)
{
ALOG_NERR0 ("ASN.1 decode: Length zero or negative");
asn1r_set_dec_err(ac, ASN1E_END_O_BUFFER);
if (ac->asn1r_err_fun != NULL)
(*ac->asn1r_err_fun)(ac, ac->asn1r_pdu_dec_err); /* call error function */
ac->_asn1r_ntag = 0; /* set # tags = 0 for next time */
return;
}
ALOG_DECLF ();
ALOG_DEC1 ("ASN.1 MESSAGE TO DECODE : len = %d",len);
ALOG_DECH (len,ptr);
/* Enter the message parse loop. */
/* Parse the message until all done or asn1_decode_done flag is set. */
/* asn1_field_ptr points to the start of the data element to be decoded. */
while (ac->asn1r_field_ptr < ac->asn1r_done_ptr && !ac->asn1r_decode_done)
{
ALOG_DEC1 ("Current decode offset: 0x%05x", ac->asn1r_field_ptr - ptr);
ac->asn1r_field_start = ac->asn1r_field_ptr; /* save field start address */
rc = _asn1r_head_decode (ac); /* decodes the data element header */
if (rc != SD_SUCCESS)
{
ALOG_NERR0 ("ASN.1 decode: header decode error");
asn1r_set_dec_err(ac, rc);
break;
}
/* OK, the header looks good and has been validated */
log_len = ac->asn1r_field_ptr - ac->asn1r_field_start;
/* For primitive, log the whole element */
if (!ac->asn1r_constr_elmnt)
log_len += ac->asn1r_elmnt_len;
ALOG_DECH (log_len, ac->asn1r_field_start);
ALOG_PAUSEDEC ("");
#ifdef DEBUG_SISCO
_list_elmnt (ac); /* print element attributes */
#endif
if (_indef_eoc (ac)) /* check for indefinite level EOC elmnt */
{ /* EOC is a primitive */
_check_level_done (ac); /* primitive complete */
continue; /* service next field if not done */
}
/* not indefinite length EOC */
if (ac->asn1r_decode_done) /* if errors detected in decode */
break;
if (ac->asn1r_constr_elmnt) /* if constructor, bump asn1_msg_level */
{
if (++ac->asn1r_msg_level >= ASN1_MAX_LEVEL)
{
ALOG_ERR0 ("Error : ASN.1 decode exceeded nesting level");
asn1r_set_dec_err(ac, ASN1E_NEST_TOO_DEEP);
break;
}
ac->asn1r_c_done_fun[ac->asn1r_msg_level] = asn1r_cstr_done_err;
}
/* Call the user function specified for the given class or tag. The */
/* user function is expected to parse the contents of this data element */
/* (when it's primitive) and move asn1_field_ptr to the start of the next */
/* data element. */
_call_user (ac);
/* The user function has been invoked, must set the asn1_decode_done flag */
/* if the message is complete or in error. The user function must move */
/* asn1_field_ptr to the start of the next data element. */
gone_len = ac->asn1r_field_ptr - ac->asn1r_field_start;/* number of bytes gone */
/* adjust count */
/* For primitive data elements, decrement _asn1_byte_count for current */
/* message level and jump directly down to _check_level_done at the end */
/* of the while loop. */
if (!ac->asn1r_constr_elmnt)
{
if (ac->_asn1_byte_count[ac->asn1r_msg_level] < gone_len)
{
ALOG_NERR0 ("ASN.1 decode: invalid constructor length");
asn1r_set_dec_err (ac, ASN1E_CSTR_INVALID_LEN); /* call error function */
break; /* exit parse loop */
}
ac->_asn1_byte_count[ac->asn1r_msg_level] -= gone_len;
}
/* Constructors are much more involved. First decrement the _asn1_byte_count */
/* for the previous message level (the one containing the new constr.), */
/* then set _asn1_byte_count for this message level. */
else
{
if (ac->_asn1_byte_count[ac->asn1r_msg_level-1] < gone_len)
{
ALOG_NERR0 ("ASN.1 decode: invalid constructor length");
asn1r_set_dec_err (ac, ASN1E_CSTR_INVALID_LEN); /* call error function */
break; /* exit parse loop */
}
ac->_asn1_byte_count[ac->asn1r_msg_level-1] -= gone_len;
/* save length type */
if ((ac->_asn1_indef_track[ac->asn1r_msg_level] = ac->asn1r_indef_flag)
!= SD_FALSE) /* if indefinite */
{ /* (INDEF = 0xFF) */
ac->_asn1_constr_start[ac->asn1r_msg_level] = ac->asn1r_field_ptr; /* save start pointer */
ac->_asn1_byte_count[ac->asn1r_msg_level] = INT_MAX;
ac->asn1r_indef_flag = SD_FALSE; /* clear flag */
}
else /* definite length constructor */
{ /* byte count in element known */
ac->_asn1_byte_count[ac->asn1r_msg_level] = ac->asn1r_elmnt_len;
if (ac->_asn1_byte_count[ac->asn1r_msg_level-1] < (ST_INT) ac->asn1r_elmnt_len)
{
ALOG_NERR0 ("ASN.1 decode: invalid constructor length");
asn1r_set_dec_err (ac, ASN1E_CSTR_INVALID_LEN); /* call error function */
break; /* exit parse loop */
}
ac->_asn1_byte_count[ac->asn1r_msg_level-1] -= ac->asn1r_elmnt_len;
}
}
/* After decrementing _asn1_byte_count, check to see if this message level is */
/* done (and if so, check the next msg level, etc...). */
_check_level_done (ac);
} /* Now return to top of parse loop. */
/* End of outer while loop. Message parse is complete. */
/* At this point the asn1_field_ptr should match the done_ptr, and the */
/* asn1_pdu_dec_err variable should be = NO_DECODE_ERR, and asn1_msg_level == -1.*/
/* If any of these conditions are not true, the user error function */
/* will be executed (typically will return error message). If no */
/* decode error, the user function pointed to by (*asn1_decode_done_fun) */
/* is executed. */
ALOG_CDEC0 ("Message parse complete");
ALOG_PAUSEDEC ("");
/* don't overwrite previous err */
if (ac->asn1r_pdu_dec_err == NO_DECODE_ERR &&
(ac->asn1r_field_ptr != ac->asn1r_done_ptr || ac->asn1r_msg_level != -1))
{ /* Check for indef cstr done */
for (i = ac->asn1r_msg_level; i >= 0; --i)
{
if (ac->_asn1_indef_track[i] == SD_TRUE) /* if indef length cstr */
{ /* check for cstr done (00 00) */
if ((ac->asn1r_field_ptr+2 > ac->asn1r_done_ptr) ||
*(ac->asn1r_field_ptr++) || *(ac->asn1r_field_ptr++))
{
ALOG_NERR0 ("ASN.1 decode: indefinite length constructor not terminated");
asn1r_set_dec_err (ac, ASN1E_END_O_BUFFER); /* call error function */
break;
}
}
}
if (ac->asn1r_field_ptr != ac->asn1r_done_ptr)
{
ALOG_NERR0 ("ASN.1 decode: PDU decode terminated before complete");
asn1r_set_dec_err (ac, ASN1E_END_O_BUFFER); /* call error function */
}
}
if (ac->asn1r_pdu_dec_err != NO_DECODE_ERR) /* if an error was present */
{
if (ac->asn1r_err_fun != NULL)
(*ac->asn1r_err_fun)(ac, ac->asn1r_pdu_dec_err); /* call error function */
}
else /* if not - */
{
if (ac->asn1r_decode_done_fun != NULL)
(*ac->asn1r_decode_done_fun)(ac); /* invoke the done function */
}
ac->_asn1r_ntag = 0; /* set # tags = 0 for next time */
}
/************************************************************************/
/* _check_level_done */
/* primitive field service complete, check to see if this level is */
/* complete, and if so, what the next level to service is. */
/************************************************************************/
static ST_VOID _check_level_done (ASN1_DEC_CTXT *ac)
{
ST_INT i;
while (ac->asn1r_msg_level >= 0 && !ac->_asn1_byte_count[ac->asn1r_msg_level])
{ /* Search for first lower level with bytes left - */
ALOG_CDEC1 ("Level %d parse complete",ac->asn1r_msg_level);
if (!ac->asn1r_decode_done) /* if user has not terminated */
{
ac->_asn1r_ntag = 0; /* clear tags, and call user */
i = ac->asn1r_msg_level--;
if (ac->asn1r_c_done_fun[i] != NULL)
(*ac->asn1r_c_done_fun[i])(ac); /* done fcn for this asn1_msg_level */
}
else /* decode terminated, just dec. */
ac->asn1r_msg_level--;
}
if (ac->asn1r_msg_level < 0) /* if no bytes left at any lvl, */
ac->asn1r_decode_done = SD_TRUE; /* stop decode */
}
/************************************************************************/
/* _indef_eoc */
/* Function to check for universal EOC type, and perform level fixup if */
/* so. The fix consists of determining the number of bytes that were in */
/* the element and subtracting this number from the previous level byte */
/* count. */
/* Returns 0 if NOT EOC, else != 0 */
/************************************************************************/
static ST_BOOLEAN _indef_eoc (ASN1_DEC_CTXT *ac)
{
ST_INT len;
if (ac->asn1r_constr_elmnt) /* can only be a primitive */
return (SD_FALSE);
if (ac->asn1r_elmnt_class != UNI) /* must be universal */
return (SD_FALSE);
if (ac->asn1r_elmnt_id != 0) /* EOC is id_code 0 */
return (SD_FALSE);
/* This looks like the EOC from a indefinite length constructor */
if (ac->_asn1_indef_track[ac->asn1r_msg_level] == SD_TRUE) /* if indef len */
{
ALOG_CDEC1 ("EOC detected for level %d",ac->asn1r_msg_level);
len = ac->asn1r_field_ptr - ac->_asn1_constr_start[ac->asn1r_msg_level];
if (ac->_asn1_byte_count[ac->asn1r_msg_level - 1] < len)
{
ALOG_NERR0 ("ASN.1 decode: misplaced end of indefinite length constructor");
asn1r_set_dec_err(ac, ASN1E_CSTR_INVALID_LEN); /* call error function */
}
ac->_asn1_byte_count[ac->asn1r_msg_level-1] -= len;
ac->_asn1_byte_count[ac->asn1r_msg_level] = 0; /* set this level count = 0 */
return (SD_TRUE); /* indicate EOC found */
}
else
{
ALOG_NERR0 ("ASN.1 decode: unexpected end of indefinite length constructor");
asn1r_set_dec_err(ac, ASN1E_UNEXPECTED_TAG); /* EOC not allowed here */
}
return (SD_FALSE);
}
/************************************************************************/
/* asn1r_head_decode */
/* function to decode the ASN.1 data element identified and length */
/* components and put the result into the component variables. */
/* The character ptr asn1r_field_ptr points to the data element start, */
/* and is left pointing to the start of the contents. */
/************************************************************************/
#define _CHECK_ASN1_LEFT(x) {if((ac->asn1r_field_ptr+x) > ac->asn1r_done_ptr)return(ASN1E_END_O_BUFFER);}
static ST_RET _asn1r_head_decode (ASN1_DEC_CTXT *ac)
{
ST_UCHAR c;
ST_UINT16 ui16;
ST_INT i, i2, i3;
/* We don't want to read past end of buffer */
_CHECK_ASN1_LEFT(1);
c = *(ac->asn1r_field_ptr++); /* read the first byte of the element */
/* pointer points to next byte */
ac->asn1r_constr_elmnt = (ST_UCHAR) (c & CONSTR);/* isolate the constr bit */
ac->asn1r_elmnt_class = (ST_UCHAR) (c & 0xC0); /* get the class bits */
if ((c &= 0x1F) == 0x1F) /* if ID extender - */
{
_CHECK_ASN1_LEFT(1);
if ((c = *(ac->asn1r_field_ptr++)) & 0x80) /* if next byte is not last - */
{
_CHECK_ASN1_LEFT(1);
if (*ac->asn1r_field_ptr & 0x80) /* if more than two extention */
{ /* ID octets, reject */
ALOG_NERR0 ("ASN.1 decode: element id too long");
return (ASN1E_ID_TOO_BIG);
}
ui16 = (ST_UINT16) (c & 0x7F) << 7; /* support 2 extention bytes */
_CHECK_ASN1_LEFT(1);
ui16 |= (*(ac->asn1r_field_ptr++) & 0x7F);/* merge the 14 ID bits (only*/
ac->asn1r_elmnt_id = ui16; /* 13 usable in this impl'n).*/
}
else /* one octet extended */
ac->asn1r_elmnt_id = (ST_UINT16) c; /* write masked ID code */
}
else /* not extended ID code */
ac->asn1r_elmnt_id = (ST_UINT16) c; /* write masked ID code */
/* asn1_field_ptr points to contents length field start. */
/* Support all length forms : short, long, and indefinite. */
_CHECK_ASN1_LEFT(1);
if ((c = *(ac->asn1r_field_ptr++)) & 0x80) /* check for long,indef forms */
{
if (c &= 0x7F) /* if long form */
{
_CHECK_ASN1_LEFT((int) c);
switch (c) /* switch on # bytes in length */
{
case 1 : /* one byte length */
ac->asn1r_elmnt_len = *(ac->asn1r_field_ptr++) & 0xFF;
break;
case 2 : /* two byte length */
i = (ST_INT) ((ST_UINT) *(ac->asn1r_field_ptr++) << 8);
ac->asn1r_elmnt_len = (ST_INT)
(((ST_UINT) *(ac->asn1r_field_ptr++) & 0xFF) | i);
break;
case 3 : /* three byte length */
i = (ST_INT) ((ST_UINT) *(ac->asn1r_field_ptr++) << 16);
#if (INT_MAX <= 0x7FFF)
if (i != 0)
{
ALOG_NERR0 ("ASN.1 decode: element length too long");
return(ASN1E_INVALID_LENGTH); /* other lengths not supported */
}
#endif
i2 = (ST_INT) (((ST_UINT) *(ac->asn1r_field_ptr++) << 8) | i);
ac->asn1r_elmnt_len = (ST_INT)
(((ST_UINT) *(ac->asn1r_field_ptr++) & 0xFF) | i2);
break;
case 4 : /* four byte length */
i = (ST_INT) ((ST_UINT) *(ac->asn1r_field_ptr++) << 24);
i2 = (ST_INT) (((ST_UINT) *(ac->asn1r_field_ptr++) << 16) | i);
#if (INT_MAX <= 0x7FFF)
if (i != 0 || i2 != 0)
{
ALOG_NERR0 ("ASN.1 decode: element length too long");
return (ASN1E_INVALID_LENGTH); /* other lengths not supported */
}
#endif
i3 = (ST_INT) (((ST_UINT) *(ac->asn1r_field_ptr++) << 8) | i2);
ac->asn1r_elmnt_len = (ST_INT)
(((ST_UINT) *(ac->asn1r_field_ptr++) & 0xFF) | i3);
break;
default :
ALOG_NERR0 ("ASN.1 decode: length length > 4");
return (ASN1E_INVALID_LENGTH); /* others not supported */
break;
}
}
else /* indefinite length */
{
if (ac->asn1r_constr_elmnt) /* valid only for constructors */
ac->asn1r_indef_flag = SD_TRUE; /* set 'indefinite len' flag */
else
{
ALOG_NERR0 ("ASN.1 decode: indefinite length primitive");
return (ASN1E_INVALID_LENGTH); /* Bad PDU structure */
}
}
}
else /* short form length */
ac->asn1r_elmnt_len = (ST_INT) c; /* write length */
/* If NOT indefinite length, make sure length is legal. */
if (!ac->asn1r_indef_flag)
{
if ((ac->asn1r_elmnt_len < 0) || ((ac->asn1r_field_ptr+ac->asn1r_elmnt_len) > ac->asn1r_done_ptr))
{
ALOG_NERR2 ("ASN.1 decode element length error: ASN.1 length=%d, remaining ASN.1 buffer %d ",
ac->asn1r_elmnt_len, ac->asn1r_done_ptr - ac->asn1r_field_ptr);
return (ASN1E_INVALID_LENGTH);
}
}
return (SD_SUCCESS);
} /* data element decode complete */
/************************************************************************/
/* _call_user */
/* Function to call the user function for the particular ID class. */
/* Passes the id code for the data element to the selected function */
/* For Universal class, also checks for EOC. */
/* */
/* The user functions are responsible for ensuring that the asn1_field_ptr */
/* is left pointing to the start of the next field. */
/************************************************************************/
static ST_VOID _call_user (ASN1_DEC_CTXT *ac)
{
ST_UINT16 curtag;
ST_INT i;
ST_INT num_tags;
ASN1R_TAG_CTRL_1 *tag_ctrl;
ASN1R_TAG_PAIR *tags;
/* When parsing is being done on the basis of tag, compare the current */
/* tag with the list of allowable tags and call the corresponding fcn */
/* pointer. Error checking was done previously when tag_add was called. */
if (ac->asn1r_decode_method == ASN1_TABLE_METHOD)
{
curtag = (ST_UINT16) (ac->asn1r_elmnt_class | ac->asn1r_constr_elmnt) << 8;
curtag |= ac->asn1r_elmnt_id;
tag_ctrl = (ASN1R_TAG_CTRL_1 *) ac->asn1r_tag_table;
tags = tag_ctrl->tags;
num_tags = tag_ctrl->num_tags;
for (i = 0; i < num_tags; ++i, ++tags)
{
if (curtag == tags->_asn1r_tag)
{
(*tags->_asn1r_tag_fun)(ac); /* Call user-defined function. */
return;
}
}
ALOG_NERR1 ("ASN.1 decode: unexpected tag 0x%X", (ST_UINT) curtag);
asn1r_set_dec_err(ac, ASN1E_UNEXPECTED_TAG);
return;
}
if (ac->asn1r_decode_method == ASN1_TAG_METHOD)
{
curtag = (ST_UINT16) (ac->asn1r_elmnt_class | ac->asn1r_constr_elmnt) << 8;
curtag |= ac->asn1r_elmnt_id;
for (i = 0; i < ac->_asn1r_ntag; i++)
{
if (curtag == ac->_asn1r_valid_tags[i])
{
ac->_asn1r_old_ntag = ac->_asn1r_ntag; /* Tags are deleted once used, */
ac->_asn1r_ntag = 0; /* but are recoverable. */
(*ac->_asn1r_tag_fun[i])(ac); /* Call user-defined function. */
return;
}
}
ALOG_NERR1 ("ASN.1 decode: unexpected tag 0x%X", (ST_UINT) curtag);
asn1r_set_dec_err(ac, ASN1E_UNEXPECTED_TAG);
return;
}
if (ac->asn1r_decode_method == ASN1_ANYTAG_METHOD)
{
curtag = (ST_UINT16) (ac->asn1r_elmnt_class | ac->asn1r_constr_elmnt) << 8;
curtag |= ac->asn1r_elmnt_id;
(*ac->asn1r_anytag_fun)(ac, curtag);
return;
}
ac->_asn1r_ntag = 0; /* class method, no valid tags */
/* When parsing is being done on the basis of class, switch on the */
/* class of the current data element and call the corresponding fcn */
/* pointer. Do error checking where possible. */
switch (ac->asn1r_elmnt_class)
{
case CTX : /* class = context specific */
(*ac->asn1r_c_id_fun)(ac, ac->asn1r_elmnt_id);
break;
case UNI : /* class = universal */ /* Perform additional checks */
switch (ac->asn1r_elmnt_id) /* for constr/prim allowed */
{ /* check selected types */
case INT_CODE : /* integer must be primitive */
if (ac->asn1r_constr_elmnt)
{
ALOG_NERR0 ("ASN.1 decode: invalid INTEGER element");
asn1r_set_dec_err (ac, ASN1E_UNEXPECTED_FORM);
}
else
{
(*ac->asn1r_u_id_fun)(ac, ac->asn1r_elmnt_id);
}
break;
case SEQ_CODE : /* sequence valid in constr */
if (!ac->asn1r_constr_elmnt)
{
ALOG_NERR0 ("ASN.1 decode: invalid SEQ element");
asn1r_set_dec_err (ac, ASN1E_UNEXPECTED_FORM);
}
else
{
(*ac->asn1r_u_id_fun)(ac, ac->asn1r_elmnt_id);
}
break;
case SET_CODE : /* set valid in constructor */
if (!ac->asn1r_constr_elmnt)
{
ALOG_NERR0 ("ASN.1 decode: invalid SET element");
asn1r_set_dec_err (ac, ASN1E_UNEXPECTED_FORM);
}
else
{
(*ac->asn1r_u_id_fun)(ac, ac->asn1r_elmnt_id);
}
break;
case BOOL_CODE : /* boolean must be primitive, */
if (ac->asn1r_constr_elmnt || ac->asn1r_elmnt_len != 1) /* 1 octet */
{
ALOG_NERR0 ("ASN.1 decode: invalid BOOL element");
asn1r_set_dec_err (ac, ASN1E_INVALID_BOOLEAN);
}
else
{
(*ac->asn1r_u_id_fun)(ac, ac->asn1r_elmnt_id);
}
break;
case NULL_CODE : /* null must be primitive */
if (ac->asn1r_constr_elmnt || ac->asn1r_elmnt_len)
{
ALOG_NERR0 ("ASN.1 decode: invalid NULL element");
asn1r_set_dec_err (ac, ASN1E_UNEXPECTED_TAG);
}
else
{
(*ac->asn1r_u_id_fun)(ac, ac->asn1r_elmnt_id);
}
break;
default : /* all others can be either */
{
(*ac->asn1r_u_id_fun)(ac, ac->asn1r_elmnt_id);
}
break;
}
break; /* end of universal */
case APP : /* class = application wide */
(*ac->asn1r_a_id_fun)(ac, ac->asn1r_elmnt_id);
break;
case PRV : /* class = private use */
(*ac->asn1r_p_id_fun)(ac, ac->asn1r_elmnt_id);
break;
}
}
/************************************************************************/
/* asn1r_set_all_cstr_done */
/************************************************************************/
ST_VOID asn1r_set_all_cstr_done (ASN1_DEC_CTXT *ac)
{
ST_INT i;
for (i = 0; i < ASN1_MAX_LEVEL; ++i)
ac->asn1r_c_done_fun[i] = NULL;
}
/************************************************************************/
/* asn1r_tag_add */
/************************************************************************/
ST_VOID asn1r_tag_add (ASN1_DEC_CTXT *ac, ST_UINT16 tag,
ST_VOID (*fcn_ptr)(ASN1_DEC_CTXT *ac))
{
#ifdef DEBUG_SISCO
if (ac->_asn1r_ntag >= MAX_TAG_FUN) /* Array is MAX_TAG_FUN deep */
{
ALOG_ERR0 ("ASN.1 Decode Internal Error : Tag Table Buffer Overflow");
return;
}
#endif
ac->_asn1r_valid_tags[ac->_asn1r_ntag] = tag;
ac->_asn1r_tag_fun[ac->_asn1r_ntag] = (ST_VOID (*)(ASN1_DEC_CTXT *)) fcn_ptr;
ac->_asn1r_ntag++;
return;
}
/************************************************************************/
/* asn1r_tag_restore */
/* This fun restores the el'ts of the tag[] and _asn1_tag_fun[] arrays. */
/************************************************************************/
ST_VOID asn1r_tag_restore (ASN1_DEC_CTXT *ac)
{
ac->_asn1r_ntag = ac->_asn1r_old_ntag;
}
/************************************************************************/
/************************************************************************/
/* asn1_peek */
/************************************************************************/
ST_RET asn1_peek (ASN1_DEC_CTXT *ac, ST_UCHAR *ptr, ST_INT len)
{
ST_RET rc;
ac->asn1r_pdu_dec_err = NO_DECODE_ERR; /* reset error flag */
ac->asn1r_field_ptr = ptr; /* initialize asn1_field_ptr */
ac->asn1r_done_ptr = ac->asn1r_field_ptr + len;
/* Validate length to avoid possible problems */
if (len <= 0)
{
ALOG_NERR0 ("ASN.1 peek: Length zero or negative");
asn1r_set_dec_err(ac, ASN1E_END_O_BUFFER);
return (SD_FAILURE);
}
rc = _asn1r_head_decode (ac); /* decodes the data element header */
if (rc != SD_SUCCESS)
{
ALOG_NERR0 ("ASN.1 decode: header decode error");
return (rc);
}
return (SD_SUCCESS);
}
/************************************************************************/
/************************************************************************/
/* ASN.1 message construct tools. */
/************************************************************************/
/************************************************************************/
/* asn1r_strt_asn1_bld */
/* Function to initialize the ASN.1 build parameters. Should be called */
/* before starting construction of each message. */
/************************************************************************/
ST_VOID asn1r_strt_asn1_bld (ASN1_ENC_CTXT *ac, ST_UCHAR *bufptr, ST_INT buflen)
{
ALOG_ENC0 ("Starting ASN.1 message construct");
/* Initialize variables in the encode context structure. */
ac->asn1r_encode_overrun = SD_FALSE;
ac->asn1r_buf_start = bufptr;
/* Initialize a bunch of variables to the "end" of buffer. */
ac->asn1r_constr_end[0] = ac->asn1r_field_ptr = ac->asn1r_field_end =
ac->asn1r_buf_end = bufptr + buflen - 1;
ac->asn1r_msg_level = 0; /* init current level */
/* Chk this later to verify that "asn1r_strt_asn1_bld" was called. */
ac->asn1r_magic = ASN1_ENC_MAGIC_NUMBER;
}
/************************************************************************/
/* asn1r_strt_constr */
/* Function to start a constructor build (from back). */
/* Simply increments the message level (if notmax) sets the start */
/* pointer for the level at the current position */
/************************************************************************/
ST_VOID asn1r_strt_constr (ASN1_ENC_CTXT *ac)
{
if (++ac->asn1r_msg_level >= ASN1_MAX_LEVEL) /* increment the level */
{
ALOG_ERR0 ("Error : ASN.1 encode exceeded nesting level");
--ac->asn1r_msg_level; /* if at top level */
}
ALOG_CENC1 ("Starting constructor @ level %d",ac->asn1r_msg_level);
ac->asn1r_constr_end[ac->asn1r_msg_level] = ac->asn1r_field_ptr; /* save msg ptr */
}
/************************************************************************/
/* asn1r_strt_constr_indef */
/* FOR TEST APPLICATIONS ONLY. */
/* SISCO applications never encode Indefinite Length constructors (slow)*/
/* but this function may be needed by test applications to generate */
/* ASN.1 messages with Indefinite Length constructors. */
/************************************************************************/
ST_VOID asn1r_strt_constr_indef (ASN1_ENC_CTXT *ac)
{
if (++ac->asn1r_msg_level >= ASN1_MAX_LEVEL) /* increment the level */
{
ALOG_ERR0 ("Error : ASN.1 encode exceeded nesting level");
--ac->asn1r_msg_level; /* if at top level */
}
ALOG_CENC1 ("Starting 'indefinite length' constructor @ level %d",ac->asn1r_msg_level);
*ac->asn1r_field_ptr-- = 0; /* write 2 zeros for 'end of constructor'*/
*ac->asn1r_field_ptr-- = 0;
ac->asn1r_constr_end[ac->asn1r_msg_level] = ac->asn1r_field_ptr;
/* CRITICAL: set ..field_end so length of last prim can be computed later.*/
ac->asn1r_field_end = ac->asn1r_field_ptr;
}
/************************************************************************/
/* asn1r_fin_constr */
/* Function to finish a constructor. */
/* Writes the constructor length, then the identifier as requested. */
/* */
/* Pass the required id_code, the tag bits (upper 3 bits), and flag to */
/* indicate when the indefinite length is required (indef != 0) */
/************************************************************************/
ST_VOID asn1r_fin_constr (ASN1_ENC_CTXT *ac, ST_UINT16 id_code, ST_UINT16 el_tag, ST_BOOLEAN indef)
{
ST_INT len;
if (ac->asn1r_field_ptr - ASN1_MAX_ELEMENT_OVERHEAD < ac->asn1r_buf_start)
{
/* Do NOT encode anything, but decrement msg_level to avoid nesting */
/* errors if user continues trying to encode. */
--ac->asn1r_msg_level;
ac->asn1r_encode_overrun = SD_TRUE; /* May already be SD_TRUE. */
return;
}
if (!indef) /* write the length field (moves ptr) */
{
len = ac->asn1r_constr_end[ac->asn1r_msg_level] - ac->asn1r_field_ptr;
_wr_asn1_len (ac, len);
}
else /* if not indefinite length - */
*(ac->asn1r_field_ptr--) = 0x80;
/* write identifier (moves ptr) */
_wr_ident (ac, id_code, (ST_UINT16) (el_tag | CONSTR));
#ifdef DEBUG_SISCO
ALOG_CENC1 ("Finish level %d constructor",ac->asn1r_msg_level);
if (indef)
{
ALOG_CENC0 ("Indefinite length");
}
else
{
ALOG_CENC1 ("Length = %d",ac->asn1r_constr_end[ac->asn1r_msg_level] - ac->asn1r_field_ptr);
}
ALOG_ENCH (ac->asn1r_constr_end[ac->asn1r_msg_level] - ac->asn1r_field_ptr, ac->asn1r_field_ptr + 1);
#endif
if (--ac->asn1r_msg_level < 0) /* if was level 0 already */
ac->asn1r_msg_level = 0; /* set = 0 */
ac->asn1r_field_end = ac->asn1r_field_ptr; /* reset the field end pointer*/
}
/************************************************************************/
/* asn1r_fin_prim */
/* Function to finish writing a primitive data element. */
/* Pass the constructor type (set,sequence) and data element type. */
/************************************************************************/
ST_VOID asn1r_fin_prim (ASN1_ENC_CTXT *ac, ST_UINT16 id_code, ST_UINT16 el_tag)
{
ST_INT len;
if (ac->asn1r_field_ptr - ASN1_MAX_ELEMENT_OVERHEAD < ac->asn1r_buf_start)
{
ac->asn1r_encode_overrun = SD_TRUE; /* May already be SD_TRUE. */
return;
}
len = ac->asn1r_field_end - ac->asn1r_field_ptr;
/* write the length field (moves ptr) */
_wr_asn1_len (ac, len);
_wr_ident (ac, id_code, el_tag); /* write the identifier (moves ptr) */
len = ac->asn1r_field_end - ac->asn1r_field_ptr;
ALOG_CENC0 ("Finish primitive");
ALOG_ENCH (len, ac->asn1r_field_ptr + 1);
ac->asn1r_field_end = ac->asn1r_field_ptr; /* reset the field end pointer*/
}
/************************************************************************/
/* _wr_ident */
/* Function to write an ASN.1 identifier field. */
/* Pass the id code (0 to 0x3FFF), and the tag (class and constr bits) */
/************************************************************************/
static ST_VOID _wr_ident (ASN1_ENC_CTXT *ac, ST_UINT16 id_code, ST_UINT16 el_tag)
{
if (id_code < 0x1F) /* if no extension octets required */
*(ac->asn1r_field_ptr--) = (ST_UCHAR)(id_code | el_tag); /* one byte identifier */
else
{
if (id_code < 0x80) /* if just one extension byte - */
*(ac->asn1r_field_ptr--) = (ST_UCHAR)id_code; /* write id_code */
else
{ /* need two extension bytes */
*(ac->asn1r_field_ptr--) = (ST_UCHAR)(id_code & 0x7F); /* write low 7 bits */
*(ac->asn1r_field_ptr--) = (ST_UCHAR)(((id_code >> 7) & 0x7F) | 0x80);
}
*(ac->asn1r_field_ptr--) = (ST_UCHAR)(el_tag | 0x1F); /* indicte extnd'r */
}
}
/************************************************************************/
/* _wr_asn1_len */
/* Function to write an ASN.1 length field */
/* Lengths from 0 to INT_MAX are supported. */
/************************************************************************/
static ST_VOID _wr_asn1_len (ASN1_ENC_CTXT *ac, ST_INT len)
{
if (len <= 0x7F) /* see if short form will do */
*(ac->asn1r_field_ptr--) = (ST_UCHAR)len;
else
{ /* need long form */
if (len <= 0xFF) /* if one byte will do */
{
*(ac->asn1r_field_ptr--) = (ST_UCHAR)len; /* write length */
*(ac->asn1r_field_ptr--) = (ST_UCHAR) 0x81; /* write # bytes in len */
}
else if (len <= 0xFFFF)
{
*(ac->asn1r_field_ptr--) = (ST_UCHAR)(len & 0xFF); /* low byte */
*(ac->asn1r_field_ptr--) = (ST_UCHAR)((len >> 8) & 0xFF); /* high byte */
*(ac->asn1r_field_ptr--) = (ST_UCHAR) 0x82; /* #len bytes*/
}
else if (len <= (ST_INT) 0xFFFFFF)
{
*(ac->asn1r_field_ptr--) = (ST_UCHAR)(len & 0xFF); /* low byte */
*(ac->asn1r_field_ptr--) = (ST_UCHAR)((len >> 8) & 0xFF); /* mid byte */
*(ac->asn1r_field_ptr--) = (ST_UCHAR)((len >> 16) & 0xFF); /* high byte */
*(ac->asn1r_field_ptr--) = (ST_UCHAR) 0x83; /* #len bytes*/
}
else
{
*(ac->asn1r_field_ptr--) = (ST_UCHAR)(len & 0xFF); /* low byte */
*(ac->asn1r_field_ptr--) = (ST_UCHAR)((len >> 8) & 0xFF); /* mid log byte */
*(ac->asn1r_field_ptr--) = (ST_UCHAR)((len >> 16) & 0xFF); /* mid high byte */
*(ac->asn1r_field_ptr--) = (ST_UCHAR)((len >> 24) & 0xFF); /* high byte */
*(ac->asn1r_field_ptr--) = (ST_UCHAR) 0x84; /* #len bytes*/
}
}
}
/************************************************************************/
/* asn1r_set_dec_err */
/* Function called from anywhere when an error is detected. Sets the */
/* passed error code = asn1_pdu_dec_err, and sets asn1_decode_done. */
/************************************************************************/
ST_VOID asn1r_set_dec_err (ASN1_DEC_CTXT *ac, ST_RET err_code)
{
ALOG_NERR1 ("ASN.1 decode error : 0x%X", err_code);
ac->asn1r_pdu_dec_err = err_code; /* set the problem type */
ac->asn1r_decode_done = SD_TRUE; /* quit parse */
}
/************************************************************************/
/* asn1r_done_err */
/* function to eliminate premature decode complete, allow release of */
/* allocated resources by invoking the error function. */
/* Use as a asn1_decode_done_fun. */
/************************************************************************/
ST_VOID asn1r_done_err (ASN1_DEC_CTXT *ac)
{
ALOG_NERR0 ("ASN1 decode: PDU ended before valid");
asn1r_set_dec_err (ac, ASN1E_END_OF_MESSAGE); /* set the error */
if (ac->asn1r_err_fun != NULL)
(*ac->asn1r_err_fun)(ac, ASN1E_END_OF_MESSAGE); /* call the error function */
}
/************************************************************************/
/* asn1r_class_err */
/* This function is a nominal function to be invoked when a undesired */
/* class data element is found during the message parse. */
/* Just set an error code, stop parse. */
/************************************************************************/
ST_VOID asn1r_class_err (ASN1_DEC_CTXT *ac, ST_UINT16 id_code)
{
ALOG_NERR0 ("CLASS ERROR");
asn1r_set_dec_err (ac, ASN1E_UNEXPECTED_CLASS); /* just set the error flag */
}
/************************************************************************/
/* asn1_cstr_done_err */
/* Function to prevent premature constructor completion. */
/************************************************************************/
ST_VOID asn1r_cstr_done_err (ASN1_DEC_CTXT *ac)
{
ALOG_NERR0 ("CONSTRUCTOR DONE ERROR");
asn1r_set_dec_err(ac, ASN1E_END_OF_CSTR);
}
/************************************************************************/
/* asn1r_chk_getcstr_done */
/* General constructor done function to be used with various pieces */
/* of code to get information from a constructor inside a message. */
/* To use, the variables _asn1_save_method and _asn1r_fun_save MUST be */
/* setup and the ASN.1 function pointer cstr_done_fun must point */
/* to this function. */
/************************************************************************/
ST_VOID asn1r_chk_getcstr_done (ASN1_DEC_CTXT *ac)
{
ac->asn1r_decode_method = ac->asn1r_save_method; /* restore the decode method */
ac->asn1r_decode_done_fun = ac->_asn1r_fun_save; /* restore the decode done fun*/
(*ac->_asn1r_cstr_done_save)(ac); /* call the user selected fun */
}
/************************************************************************/
/* asn1_objidcmp */
/* Function to compare to OBJECT IDENTIFIERs */
/* RETURN: 0 if same */
/* 1 if different */
/************************************************************************/
ST_BOOLEAN asn1_objidcmp (MMS_OBJ_ID *obj1, MMS_OBJ_ID *obj2)
{
ST_INT i;
if (obj1->num_comps != obj2->num_comps)
return (1);
for (i = 0; i < obj1->num_comps; i++)
{
if (obj1->comps[i] != obj2->comps[i])
return (SD_TRUE);
}
return (SD_FALSE);
}
/************************************************************************/
/* asn1r_init_glb_vars */
/************************************************************************/
#if defined(NO_GLB_VAR_INIT)
ST_VOID asn1r_init_glb_vars (ST_VOID)
{
asn1_debug_sel = ASN1_LOG_ERR;
#if defined( __ALPHA )
asn1_vax_double_type = A_G_FLOAT;
#endif
#if defined( VAXC )
asn1_vax_double_type = A_D_FLOAT;
#endif
}
#endif
/************************************************************************/
#ifdef DEBUG_SISCO
/************************************************************************/
/************************************************************************/
/* _list_elmnt */
/* Function to print the attributes of a data element */
/************************************************************************/
static ST_VOID _list_elmnt (ASN1_DEC_CTXT *ac)
{
ST_CHAR *ctxt;
ST_CHAR *pc;
ST_CHAR len[50];
ctxt ="Bogus";
if ((asn1_debug_sel & ASN1_LOG_DEC) == 0)
return;
switch (ac->asn1r_elmnt_class)
{
case (0x00) :
ctxt = "Univ";
break;
case (0x40) :
ctxt = "App ";
break;
case (0x80) :
ctxt = "Ctxt";
break;
case (0xC0) :
ctxt = "Priv";
break;
}
if (ac->asn1r_constr_elmnt)
pc = "Cstr";
else
pc = "Prim";
if (!ac->asn1r_indef_flag)
sprintf (len,"%u", (unsigned int) ac->asn1r_elmnt_len);
else
strcpy (len,"Indef");
ALOG_DECLF ();
ALOG_CDEC4 ("Element : %s, %s, ID = %d, Len = %s",
ctxt, pc, (int) ac->asn1r_elmnt_id, len);
}
/************************************************************************/
#endif /* end ifdef DEBUG_SISCO */
/************************************************************************/
/*------------------------------------------------------*/
/* MMS_BTOD to/from MMS_UTC_TIME conversion functions */
/*------------------------------------------------------*/
/************************************************************************/
/* asn1_convert_btod_to_utc */
/* This function converts MMS_BTOD (time relative to 1/1/1984) to the */
/* MMS_UTC_TIME (time relative to 1/1/1970). */
/* The qflags field in the MMS_UTC_TIME need to be set by the calling */
/* function. */
/* Only the MMS_BTOD6 form of the MMS_BTOD struct can be converted to */
/* the MMS_UTC_TIME. */
/* Parameters: */
/* btod pointer to MMS_BTOD struct that should be converted to */
/* the MMS_UTC_TIME */
/* utc pointer to MMS_UTC_TIME struct where the result of the */
/* conversion will be placed */
/* Return: */
/* SD_SUCCESS if function successful */
/* SD_FAILURE otherwise */
/************************************************************************/
ST_RET asn1_convert_btod_to_utc (MMS_BTOD *btod, MMS_UTC_TIME *utc)
{
time_t tJan84 = TIME_T_1984_JAN_1;
if (btod->form != MMS_BTOD6)
{
ALOG_NERR0 ("convert_btod_to_utc: MMS_BTOD4 can't be converted to UTC time");
return (SD_FAILURE);
}
/* Now compute the MMS_UTC_TIME */
utc->secs = (ST_UINT32) (tJan84 +
(time_t) (btod->day * SECONDS_PER_DAY) + (time_t) (btod->ms / 1000));
/* num of seconds since Jan 1, 1970 */
/* NOTE: use 0x01000000 (2**24) in fraction computations. */
utc->fraction = (ST_UINT32) ((ST_DOUBLE)(btod->ms % 1000) / 1000.0 * (ST_DOUBLE)0x01000000);
/* fraction of a second on 24-bits */
return (SD_SUCCESS);
}
/************************************************************************/
/* asn1_convert_utc_to_btod */
/* This function converts MMS_UTC_TIME (time relative to 1/1/1970) to */
/* the MMS_BTOD (time relative to 1/1/1984). */
/* The form field in the MMS_BTOD is set to MMS_BTOD6 by this function. */
/* Parameters: */
/* utc pointer to MMS_UTC_TIME struct that should be converted */
/* to the MMS_BTOD */
/* btod pointer to MMS_BTOD struct where the result of the */
/* conversion will be placed */
/* Return: */
/* SD_SUCCESS if function successful */
/* SD_FAILURE otherwise */
/************************************************************************/
ST_RET asn1_convert_utc_to_btod (MMS_UTC_TIME *utc, MMS_BTOD *btod)
{
time_t tJan84 = TIME_T_1984_JAN_1;
/* Now compute the MMS_BTOD time */
btod->day = (ST_INT32) (utc->secs - tJan84) / SECONDS_PER_DAY; /* num of days since 1/1/1984 */
btod->ms = (ST_INT32) ((utc->secs - tJan84) % SECONDS_PER_DAY) * 1000; /* num milliseconds since midnight */
/* NOTE: use 0x01000000 (2**24) in fraction computations. */
btod->ms += (ST_INT32) ((ST_DOUBLE) utc->fraction * 1000.0/(ST_DOUBLE)0x01000000);
/* add the milliseconds left in a sec */
btod->form = MMS_BTOD6;
return (SD_SUCCESS);
}
/************************************************************************/
/* asn1_convert_timet_to_btime6 */
/* Convert time_t value to MMS_BTIME6. */
/* Input "time_t" value is always based on UTC (GMT). It is compared */
/* to the define TIME_T_1984_JAN_1 which is also based on UTC (GMT). */
/* The difference is used to compute the number of seconds elapsed */
/* since 1984. Then seconds is converted to days & milliseconds. */
/************************************************************************/
ST_RET asn1_convert_timet_to_btime6 (time_t tThis, MMS_BTIME6 *btime6)
{
time_t tJan84 = TIME_T_1984_JAN_1;
long elapsed; /* elapsed time since 1984-Jan-1 (in seconds) */
ldiv_t divResult;
ST_RET retcode;
/* Find the number of seconds since "1984-Jan-1" and convert to days & msec.*/
/* NOTE: difftime returns "double" (don't know why), but ldiv needs "long".*/
elapsed = (long) difftime (tThis, tJan84);
if (elapsed < 0)
{ /* "negative" elapsed time NOT ALLOWED */
btime6->day = 0; /* days since 1984-Jan-1 */
btime6->ms = 0; /* milliseconds this day */
retcode = SD_FAILURE;
}
else
{
divResult = ldiv (elapsed, SECONDS_PER_DAY);
btime6->day = divResult.quot; /* days since 1984 */
btime6->ms = (divResult.rem * 1000); /* milliseconds this day */
retcode = SD_SUCCESS;
}
return (retcode);
}
/************************************************************************/
/* bstrcpy */
/************************************************************************/
ST_VOID bstrcpy (ST_UCHAR *dstptr, ST_UCHAR *srcptr, ST_INT numbits)
{
ST_INT num_bytes = numbits/8; /* Number of complete bytes */
ST_INT extra_bits = numbits%8; /* Number of extra bits */
ST_UCHAR src_mask;
ST_UCHAR dst_mask;
if (num_bytes)
memcpy (dstptr, srcptr, num_bytes);
if (extra_bits)
{
src_mask = 0xFF << (8-extra_bits); /* Use high bits from src */
dst_mask = 0xFF >> extra_bits; /* Use low bits from dst */
dstptr[num_bytes] &= dst_mask;
dstptr[num_bytes] |= (srcptr[num_bytes] & src_mask);
}
}
/************************************************************************/
/* bvstrcpy */
/* Copy one "variable length bitstring" to another. */
/************************************************************************/
ST_VOID bvstrcpy (MMS_BVSTRING *dstptr, MMS_BVSTRING *srcptr)
{
dstptr->len = srcptr->len;
bstrcpy (dstptr->data, srcptr->data, srcptr->len);
}
/************************************************************************/
/* bstrcmp */
/* Compare bitstrings. */
/* RETURNS: 0 if bitstrings identical, 1 if different. */
/************************************************************************/
ST_INT bstrcmp (ST_UCHAR *dstptr, ST_UCHAR *srcptr, ST_INT numbits)
{
ST_INT num_bytes = numbits/8; /* Number of complete bytes */
ST_INT extra_bits = numbits%8; /* Number of extra bits */
ST_UCHAR mask;
if (num_bytes)
{
if (memcmp (dstptr, srcptr, num_bytes))
return (1); /* different */
}
if (extra_bits)
{
/* Mask off extra (low order) bits in last byte. */
mask = 0xFF << (8-extra_bits); /* Use high order bits */
if ((dstptr[num_bytes] & mask) != (srcptr[num_bytes] & mask))
return (1); /* different */
}
return (0); /* same */
}
/************************************************************************/
/* asn1r_skip_elmnt */
/* Skip over current ASN.1 element without decoding. */
/* Does not work for indefinite length encoding (returns SD_FAILURE). */
/* DEBUG: If indefinite len support needed, try using asn1r_parse_next. */
/************************************************************************/
ST_RET asn1r_skip_elmnt (ASN1_DEC_CTXT *aCtx)
{
ST_RET retcode = SD_SUCCESS;
if (aCtx->asn1r_constr_elmnt)
{
if (aCtx->asn1r_indef_flag)
{
ALOG_ERR0 ("asn1r_skip_elmnt: indefinite length encoding not supported");
asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);
retcode = SD_FAILURE;
}
else
{
aCtx->asn1r_constr_elmnt = 0; /* Make ASN.1 treat it like primitive. */
--aCtx->asn1r_msg_level; /* Already incremented for constructor */
/* so decrement to act like primitive. */
aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
}
}
else
{ /* primitive: just update field_ptr */
aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
}
return (retcode);
}
/*======================================================================*/
/*======================================================================*/
/* The following functions are for a new decode method that does */
/* NOT use any function pointers: */
/* asn1r_decode_init */
/* asn1r_byte_count_decrement (static) */
/* asn1r_get_tag */
/*======================================================================*/
/*======================================================================*/
/************************************************************************/
/* asn1r_decode_init */
/* Initialize the ASN.1 decode context. */
/* IMPORTANT: Use ONLY with "asn1r_get_tag". */
/* NOTE: This function does NOT use any of the function pointers in */
/* ASN1_DEC_CTXT (_asn1r_tag_fun, asn1r_c_done_fun, etc.). */
/* Also, several other parameters in ASN1_DEC_CTXT are NOT used. */
/* NOTE: To avoid a slow "memset" call, this function only sets the */
/* necessary parameters in ASN1_DEC_CTXT (other parameters contain */
/* random data). */
/************************************************************************/
ST_RET asn1r_decode_init (ASN1_DEC_CTXT *ac,
ST_UCHAR *ptr, /* ASN.1 message to decode */
ST_INT len) /* length of ASN.1 message */
{
/* Validate length to avoid possible problems */
if (len <= 0)
{
ALOG_NERR0 ("ASN.1 decode: Length zero or negative");
return (ASN1E_END_O_BUFFER);
}
/* initialize flags, counters, pointers for decode */
ac->asn1r_indef_flag = SD_FALSE; /* clear flag */
ac->_asn1_indef_track[0] = SD_FALSE; /* level 0 is always definite */
ac->_asn1_byte_count[0] = len; /* set # bytes left in level 0 */
ac->asn1r_msg_level = 0; /* start at message level 0 */
ac->asn1r_field_ptr = ptr;
ac->asn1r_done_ptr = ptr + len;
ac->asn1r_msg_start = ptr; /* save start ptr to compute */
/* offset for logging. */
ALOG_DEC1 ("ASN.1 MESSAGE TO DECODE : len = %d",len);
ALOG_DECH (len,ptr);
return (SD_SUCCESS);
}
/************************************************************************/
/* asn1r_byte_count_decrement */
/* If this is a "definite length" constructor, decrement its byte count.*/
/* NOTE: For "indefinite length" constructor, DO NOTHING. */
/************************************************************************/
static ST_RET asn1r_byte_count_decrement (ASN1_DEC_CTXT *ac,
ST_INT msg_level, /* constructor nesting level */
ST_INT byte_count) /* number of bytes processed */
{
if ( ! ac->_asn1_indef_track[msg_level])
{
/* This is "definite length" constructor. Decrement its byte count. */
if (ac->_asn1_byte_count[msg_level] < byte_count)
{
if (msg_level == 0)
ALOG_ERR0 ("ASN.1 decode: data exceeds message size");
else
ALOG_ERR1 ("ASN.1 decode: data exceeds constructor length at message level %d", msg_level);
return (ASN1E_CSTR_INVALID_LEN);
}
ac->_asn1_byte_count[msg_level] -= byte_count;
}
return (SD_SUCCESS);
}
/************************************************************************/
/* asn1r_get_tag */
/* Decode the next ASN.1 tag. */
/* IMPORTANT: Use ONLY with "asn1r_decode_init". */
/* Must call 'asn1r_decode_init' once before any calls to this function.*/
/* NOTE: This function does NOT use any of the function pointers in */
/* ASN1_DEC_CTXT (_asn1r_tag_fun, asn1r_c_done_fun, etc.). */
/* Also does NOT use several other parameters in ASN1_DEC_CTXT. */
/************************************************************************/
ST_RET asn1r_get_tag (ASN1_DEC_CTXT *ac,
ST_UINT16 *tag_out) /* out: decoded tag */
{
ST_INT head_len; /* num bytes processed by asn1r_head_decode */
ST_RET rc;
/* Before decoding the next tag, if current constructor is */
/* "definite length", check if it is finished. */
/* NOTE: end of "indefinite length" constructor is a real tag, so */
/* it is processed AFTER the tag is decoded. */
if ( ac->asn1r_msg_level > 0
&& ac->_asn1_indef_track[ac->asn1r_msg_level] == SD_FALSE /* definite length*/
&& ac->_asn1_byte_count[ac->asn1r_msg_level] == 0) /* constr done */
{
/* 0 bytes left at this constructor level (i.e. this constr done). */
ALOG_CDEC1 ("Level %d parse complete",ac->asn1r_msg_level);
ac->asn1r_msg_level--;
*tag_out = 0; /* 0 always indicates constructor done */
return (SD_SUCCESS);
}
if (ac->asn1r_field_ptr >= ac->asn1r_done_ptr)
{
ALOG_ERR0 ("ASN.1 decode: past end of buffer");
return (ASN1E_END_O_BUFFER);
}
ALOG_DEC1 ("Current decode offset: 0x%X",
ac->asn1r_field_ptr - ac->asn1r_msg_start);
ac->asn1r_field_start = ac->asn1r_field_ptr; /* save field start address*/
/* Clear old asn1r_indef_flag. May be set again by _asn1r_head_decode.*/
ac->asn1r_indef_flag = SD_FALSE; /* clear flag */
/* Decode the ASN.1 element header. */
rc = _asn1r_head_decode (ac);
if (rc != SD_SUCCESS)
{
ALOG_NERR0 ("ASN.1 decode: header decode error");
return(rc);
}
/* Log this element (if enabled). */
#ifdef DEBUG_SISCO
if (asn1_debug_sel & ASN1_LOG_DEC)
{
ST_INT log_len;
log_len = ac->asn1r_field_ptr - ac->asn1r_field_start;
/* For primitive, log the whole element */
if (!ac->asn1r_constr_elmnt)
log_len += ac->asn1r_elmnt_len;
ALOG_DECH (log_len, ac->asn1r_field_start); /* log bytes just processed*/
_list_elmnt (ac); /* log element attributes */
}
#endif
/* Generate tag to return to caller (*tag_out). */
/* Our storage of tags in ST_UINT16 can only handle elmnt_id of */
/* 13 bits (high 3 bits indicate Class and Constructor). */
/* This should be good enough for anything we need to parse. */
if (ac->asn1r_elmnt_id > 0x1fff)
{
ALOG_ERR1 ("ASN.1 element id = %u unsupported", ac->asn1r_elmnt_id);
return (SD_FAILURE); /* should NEVER get here */
}
*tag_out = ((ST_UINT16) (ac->asn1r_elmnt_class | ac->asn1r_constr_elmnt) << 8)
| ac->asn1r_elmnt_id;
/* Check for Indefinite Length End of Constructor (EOC)(i.e. tag==0) */
if (*tag_out == 0)
{
/* EOC found. Was constr at this level "indefinite length"? */
if (ac->_asn1_indef_track[ac->asn1r_msg_level])
{
ST_INT indef_len; /* Length of this indefinite length constructor */
ALOG_CDEC1 ("EOC detected for level %d",ac->asn1r_msg_level);
indef_len = ac->asn1r_field_ptr - ac->_asn1_constr_start[ac->asn1r_msg_level];
/* Decrement the byte count of the "higher level" constr. */
rc = asn1r_byte_count_decrement (ac, ac->asn1r_msg_level - 1, indef_len);
if (rc)
return (rc);
ALOG_CDEC1 ("Level %d parse complete",ac->asn1r_msg_level);
ac->asn1r_msg_level--;
}
else
{
ALOG_NERR0 ("ASN.1 decode: unexpected end of indefinite length constructor");
return (ASN1E_UNEXPECTED_TAG); /* EOC not allowed here */
}
/* ALWAYS return when EOC found. */
/* NOTE: "*tag_out" already set to 0 to indicate constructor done. */
return (SD_SUCCESS);
} /* end "if (*tag_out == 0)" */
/* Compute number of bytes processed by asn1r_head_decode. */
/* Need this to decrement constructor byte counts below. */
head_len = ac->asn1r_field_ptr - ac->asn1r_field_start;
if (ac->asn1r_constr_elmnt)
{
/* Process constructor. */
if (++ac->asn1r_msg_level >= ASN1_MAX_LEVEL)
{
ALOG_ERR0 ("Error : ASN.1 decode exceeded nesting level");
return (ASN1E_NEST_TOO_DEEP);
}
/* Save "indefinite length" flag for this constructor level. */
ac->_asn1_indef_track[ac->asn1r_msg_level] = ac->asn1r_indef_flag;
if (ac->asn1r_indef_flag)
{
/* Save start ptr for this constructor. Use to compute len later. */
ac->_asn1_constr_start[ac->asn1r_msg_level] = ac->asn1r_field_ptr;
/* _asn1_byte_count NOT USED for indef len constructor. Set to 0, */
/* just so it's easier to see in the debugger. */
ac->_asn1_byte_count[ac->asn1r_msg_level] = 0; /* NOT USED for indef*/
/* Decrement the byte count of the "higher level" constr. */
/* Use "head_len". Adjust for "data len" later when EOC is found. */
rc = asn1r_byte_count_decrement (ac, ac->asn1r_msg_level - 1, head_len);
if (rc)
return (rc);
}
else
{
/* definite length constructor. Initialize byte count for it. */
ac->_asn1_byte_count[ac->asn1r_msg_level] = ac->asn1r_elmnt_len;
/* Decrement the byte count of the "higher level" constr. */
rc = asn1r_byte_count_decrement (ac, ac->asn1r_msg_level - 1,
head_len+ac->asn1r_elmnt_len);
if (rc)
return (rc);
}
} /* end constructor processing */
else
{
/* This is a primitive. */
/* Decrement the byte count of the "current" constr. */
rc = asn1r_byte_count_decrement (ac, ac->asn1r_msg_level,
head_len+ac->asn1r_elmnt_len);
if (rc)
return (rc);
}
return (SD_SUCCESS);
}