/************************************************************************/ /* 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 #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); }