/************************************************************************/ /* SISCO SOFTWARE MODULE HEADER *****************************************/ /************************************************************************/ /* (c) Copyright Systems Integration Specialists Company, Inc., */ /* 1986 - 2005, All Rights Reserved. */ /* */ /* PROPRIETARY AND CONFIDENTIAL */ /* */ /* MODULE NAME : ard_int.c */ /* PRODUCT(S) : ASN1DE */ /* */ /* MODULE DESCRIPTION : */ /* */ /* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */ /* */ /* MODIFICATION LOG : */ /* Date Who Rev Comments */ /* -------- --- ------ ------------------------------------------- */ /* 08/24/09 JRB 09 Fix signed int out of range error detection. */ /* Use separate signed & unsigned functions. */ /* Simplify negative value handling. */ /* 10/03/08 JRB 08 Fix crash by testing for asn1r_elmnt_len < 1.*/ /* 06/10/05 JRB 07 Use i64 suffix on WIN32, default to LL suffix*/ /* 03/03/05 EJV 06 Use LL suffix also on linux. */ /* 03/16/04 EJV 05 Added ST_(U)LONG typecast to logs,on some sys*/ /* ST_(U)INT32 can be (unsigned) long or int. */ /* asn1r_get_u8 (_u16): fixed potential problem.*/ /* 01/08/04 EJV 04 asn1r_get_int64: added define for sun. */ /* 12/20/01 JRB 03 Chg ASN1_CTXT to ASN1_DEC_CTXT. */ /* 09/13/99 MDE 02 Added SD_CONST modifiers */ /* 07/26/99 MDE 01 New module, derived from ad_int.c */ /************************************************************************/ #include "glbtypes.h" #include "sysincs.h" #include "asn1r.h" #include "asn1log.h" /************************************************************************/ /* 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 /************************************************************************/ /************************************************************************/ static ST_RET asn1r_get_int32 (ASN1_DEC_CTXT *ac, ST_INT32 *ptr); static ST_RET asn1r_get_uint32 (ASN1_DEC_CTXT *ac, ST_UINT32 *ptr); #ifdef INT64_SUPPORT static ST_RET asn1r_get_int64 (ASN1_DEC_CTXT *ac, ST_INT64 *ptr); static ST_RET asn1r_get_uint64 (ASN1_DEC_CTXT *ac, ST_UINT64 *ptr); #endif /************************************************************************/ /* get_i8 */ /* Function to read a short integer from a message being decoded. */ /* Assumes asn1_field_ptr points to data (content field), and asn1_elmnt_len */ /* equals exactly one byte. Leaves pointer set to next data element. */ /* Returns 0 if OK, non-zero if error. */ /************************************************************************/ ST_RET asn1r_get_i8 (ASN1_DEC_CTXT *ac, ST_INT8 *ptr) { ST_INT32 value; ST_RET ret; ret = asn1r_get_int32 (ac, &value); if (ret == SD_SUCCESS && (value > 127 || value < -128)) { ALOG_NERR1 ("ASN.1 decode: Signed8 out of range (%ld)", (ST_LONG) value); return (SD_FAILURE); } *ptr = (ST_CHAR) value; return (ret); } /************************************************************************/ /* get_i16 */ /* Function to read an integer from a message being decoded. */ /* Assume asn1_field_ptr points to data, and asn1_elmnt_len */ /* equals one or two bytes. Leaves pointer set to next data element. */ /* Returns 0 if OK, non-zero if error. */ /************************************************************************/ ST_RET asn1r_get_i16 (ASN1_DEC_CTXT *ac, ST_INT16 *ptr) { ST_INT32 value; ST_RET ret; ret = asn1r_get_int32 (ac, &value); if (ret == SD_SUCCESS && (value > 32767 || value < -32768)) { ALOG_NERR1 ("ASN.1 decode: Signed16 out of range (%ld)", (ST_LONG) value); return (SD_FAILURE); } *ptr = (ST_INT16) value; return (ret); } /************************************************************************/ /* get_i32 */ /* Function to read a long integer from a message being decoded. */ /* Assumes asn1_field_ptr points to data, and asn1_elmnt_len */ /* equals one to four bytes. Leaves pointer set to next data element. */ /* Returns 0 if OK, non-zero if error. */ /************************************************************************/ ST_RET asn1r_get_i32 (ASN1_DEC_CTXT *ac, ST_INT32 *ptr) { ST_INT32 value; ST_RET ret; ret = asn1r_get_int32 (ac, &value); *ptr = value; return (ret); } /************************************************************************/ /* get_u8 */ /* Function to read an ASN.1 INTEGER from a message being decoded */ /* into a 1-byte unsigned char. Assumes asn1_field_ptr points to data */ /* field and asn1_elmnt_len has 1 or 2 bytes. Leaves asn1_field_ptr pointing to */ /* next data element. Returns 0 if OK, non-zero if error. */ /************************************************************************/ ST_RET asn1r_get_u8 (ASN1_DEC_CTXT *ac, ST_UCHAR *ptr) { ST_UINT32 value; ST_RET ret; ret = asn1r_get_uint32 (ac, &value); if (ret == SD_SUCCESS && value > 0xFF) { ALOG_NERR1 ("ASN.1 decode: Unsigned16 out of range (%lu)", (ST_ULONG) value); return (SD_FAILURE); } *ptr = (ST_UCHAR) value; return (ret); } /************************************************************************/ /* get_u16 */ /* Function to read an ASN.1 INTEGER from a message being decoded */ /* into a 2-byte unsigned integer. Assumes asn1_field_ptr points to data */ /* field and asn1_elmnt_len has 1 to 3 bytes. Leaves asn1_field_ptr pointing to */ /* next data element. Returns 0 if OK, non-zero if error. */ /************************************************************************/ ST_RET asn1r_get_u16 (ASN1_DEC_CTXT *ac, ST_UINT16 *ptr) { ST_UINT32 value; ST_RET ret; ret = asn1r_get_uint32 (ac, &value); if (ret == SD_SUCCESS && value > 0xFFFF) { ALOG_NERR1 ("ASN.1 decode: Unsigned16 out of range (%lu)", (ST_ULONG) value); return (SD_FAILURE); } *ptr = (ST_UINT16) value; return (ret); } /************************************************************************/ /* get_u32 */ /* Function to read an ASN.1 INTEGER from a message being decoded */ /* into a 4-byte unsigned ST_INT32. Assumes asn1_field_ptr points to data */ /* field and asn1_elmnt_len has 1 to 5 bytes. Leaves asn1_field_ptr pointing */ /* to next data element. Returns 0 if OK, non-zero if error. */ /************************************************************************/ ST_RET asn1r_get_u32 (ASN1_DEC_CTXT *ac, ST_UINT32 *ptr) { ST_UINT32 value; ST_RET ret; ret = asn1r_get_uint32 (ac, &value); *ptr = value; return (ret); } /************************************************************************/ /* Use this function ONLY for "signed" integers. */ /************************************************************************/ static ST_RET asn1r_get_int32 (ASN1_DEC_CTXT *ac, ST_INT32 *ptr) { /* NOTE: Use all "unsigned" variables so shifting & casting does not */ /* cause sign extension, then cast final result to ST_INT32. */ ST_UINT32 value; /* decoded value */ ST_UINT32 tmp; ST_BOOLEAN positive; ST_INT i; #ifdef DEBUG_ASN1_DECODE if (!ptr) { slogCallStack (sLogCtrl, "get_i32: attempt to reference through a NULL pointer"); return(SD_FAILURE); } #endif /* ASN.1 spec says 'integer' must be at least 1 octet */ if (ac->asn1r_elmnt_len < 1) { ALOG_ERR0 ("ASN.1 decode: length < 1 not allowed for integer"); return (SD_FAILURE); } /* Check to see if the ASN.1 data value is positive or negative */ if (*ac->asn1r_field_ptr & 0x80) positive = SD_FALSE; else positive = SD_TRUE; /* Large 'unsigned' values may be 5 bytes, with a leading 0. */ /* This value is 'signed', so it should NEVER be more than 4 bytes. */ i = ac->asn1r_elmnt_len; if (i > 4) { ALOG_NERR0 ("ASN.1 decode: invalid 'signed' integer encoding (more than 4 bytes)"); return (SD_FAILURE); } /* Read the value into a ST_UINT32, shifting as we go */ if (!positive) value = 0xFFFFFF00; /* negative value, start with high bits set */ else value = 0; /* positive value, start with 0 */ while (SD_TRUE) { /* asn1r_field_ptr is (ST_UCHAR *), so this cast should NEVER sign extend.*/ tmp = (ST_UINT32) *(ac->asn1r_field_ptr++); value |= tmp; if (--i == 0) break; value <<= 8; /* more bytes to decode. Make room for next byte.*/ } /* Finally, after all shifting & masking, cast it to 'signed' value. */ *ptr = (ST_INT32) value; return (SD_SUCCESS); } /************************************************************************/ /* Use this function ONLY for "unsigned" integers. */ /************************************************************************/ static ST_RET asn1r_get_uint32 (ASN1_DEC_CTXT *ac, ST_UINT32 *ptr) { ST_UINT32 value; /* decoded value */ ST_UINT32 tmp; ST_INT i; #ifdef DEBUG_ASN1_DECODE if (!ptr) { slogCallStack (sLogCtrl, "get_u32: attempt to reference through a NULL pointer"); return(SD_FAILURE); } #endif /* ASN.1 spec says 'integer' must be at least 1 octet */ if (ac->asn1r_elmnt_len < 1) { ALOG_ERR0 ("ASN.1 decode: length < 1 not allowed for integer"); return (SD_FAILURE); } /* ASN.1 data value MUST BE positive. */ if (*ac->asn1r_field_ptr & 0x80) { ALOG_NERR0 ("ASN.1 decode: Negative number received for unsigned integer"); return (SD_FAILURE); } /* Large 'unsigned' values may be 5 bytes, with a leading 0. */ /* If so, we strip the leading 0. */ i = ac->asn1r_elmnt_len; if (i > 5) { ALOG_NERR0 ("ASN.1 decode: invalid UINT32 encoding (more than 5 bytes)"); return (SD_FAILURE); } if (i == 5) /* Large positive number, first byte must be 0. */ { if (*ac->asn1r_field_ptr != 0) { ALOG_NERR0 ("ASN.1 decode: invalid UINT32 encoding (5 bytes but first byte != 0)"); return (SD_FAILURE); } ++ac->asn1r_field_ptr; /* skip the leading 0 */ --i; } /* Read the value into a ST_UINT32, shifting as we go */ value = 0; while (SD_TRUE) { /* asn1r_field_ptr is (ST_UCHAR *), so this cast should NEVER sign extend.*/ tmp = (ST_UINT32) *(ac->asn1r_field_ptr++); value |= tmp; if (--i == 0) break; value <<= 8; /* more bytes to decode. Make room for next byte.*/ } *ptr = value; return (SD_SUCCESS); } /************************************************************************/ #ifdef INT64_SUPPORT /************************************************************************/ /************************************************************************/ ST_RET asn1r_get_u64 (ASN1_DEC_CTXT *ac, ST_UINT64 *ptr) { ST_UINT64 value; ST_RET ret; ret = asn1r_get_uint64 (ac, &value); *ptr = value; return (ret); } /************************************************************************/ ST_RET asn1r_get_i64 (ASN1_DEC_CTXT *ac, ST_INT64 *ptr) { ST_INT64 value; ST_RET ret; ret = asn1r_get_int64 (ac, &value); *ptr = value; return (ret); } /************************************************************************/ /* Use this function ONLY for "signed" integers. */ /************************************************************************/ static ST_RET asn1r_get_int64 (ASN1_DEC_CTXT *ac, ST_INT64 *ptr) { /* NOTE: Use all "unsigned" variables so shifting & casting does not */ /* cause sign extension, then cast final result to ST_INT64. */ ST_UINT64 value; /* decoded value */ ST_UINT64 tmp; ST_BOOLEAN positive; ST_INT i; #ifdef DEBUG_ASN1_DECODE if (!ptr) { slogCallStack (sLogCtrl, "get_i64: attempt to reference through a NULL pointer"); return(SD_FAILURE); } #endif /* ASN.1 spec says 'integer' must be at least 1 octet */ if (ac->asn1r_elmnt_len < 1) { ALOG_ERR0 ("ASN.1 decode: length < 1 not allowed for integer"); return (SD_FAILURE); } /* Check to see if the ASN.1 data value is positive or negative */ if (*ac->asn1r_field_ptr & 0x80) positive = SD_FALSE; else positive = SD_TRUE; /* Large 'unsigned' values may be 9 bytes, with a leading 0. */ /* This value is 'signed', so it should NEVER be more than 8 bytes. */ i = ac->asn1r_elmnt_len; if (i > 8) { ALOG_NERR0 ("ASN.1 decode: invalid 'signed' integer encoding (more than 8 bytes)"); return (SD_FAILURE); } /* Read the value into a ST_UINT64, shifting as we go */ if (!positive) { #if defined(_WIN32) value = 0xffffffffffffff00i64; /* negative value, start with high bits set*/ #else value = 0xffffffffffffff00LL; /* negative value, start with high bits set*/ #endif } else value = 0; /* positive value, start with 0 */ while (SD_TRUE) { /* asn1r_field_ptr is (ST_UCHAR *), so this cast should NEVER sign extend.*/ tmp = (ST_UINT64) *(ac->asn1r_field_ptr++); value |= tmp; if (--i == 0) break; value <<= 8; /* more bytes to decode. Make room for next byte.*/ } /* Finally, after all shifting & masking, cast it to 'signed' value. */ *ptr = (ST_INT64) value; return (SD_SUCCESS); } /************************************************************************/ /* Use this function ONLY for "unsigned" integers. */ /************************************************************************/ static ST_RET asn1r_get_uint64 (ASN1_DEC_CTXT *ac, ST_UINT64 *ptr) { ST_UINT64 value; /* decoded value */ ST_UINT64 tmp; ST_INT i; #ifdef DEBUG_ASN1_DECODE if (!ptr) { slogCallStack (sLogCtrl, "get_u64: attempt to reference through a NULL pointer"); return(SD_FAILURE); } #endif /* ASN.1 spec says 'integer' must be at least 1 octet */ if (ac->asn1r_elmnt_len < 1) { ALOG_ERR0 ("ASN.1 decode: length < 1 not allowed for integer"); return (SD_FAILURE); } /* ASN.1 data value MUST BE positive. */ if (*ac->asn1r_field_ptr & 0x80) { ALOG_NERR0 ("ASN.1 decode: Negative number received for unsigned integer"); return (SD_FAILURE); } /* Large 'unsigned' values may be 9 bytes, with a leading 0. */ /* If so, we strip the leading 0. */ i = ac->asn1r_elmnt_len; if (i > 9) { ALOG_NERR0 ("ASN.1 decode: invalid UINT64 encoding (more than 9 bytes)"); return (SD_FAILURE); } if (i == 9) /* Large positive number, first byte must be 0. */ { if (*ac->asn1r_field_ptr != 0) { ALOG_NERR0 ("ASN.1 decode: invalid UINT64 encoding (9 bytes but first byte != 0)"); return (SD_FAILURE); } ++ac->asn1r_field_ptr; /* skip the leading 0 */ --i; } /* Read the value into a ST_UINT64, shifting as we go */ value = 0; while (SD_TRUE) { /* asn1r_field_ptr is (ST_UCHAR *), so this cast should NEVER sign extend.*/ tmp = (ST_UINT64) *(ac->asn1r_field_ptr++); value |= tmp; if (--i == 0) break; value <<= 8; /* more bytes to decode. Make room for next byte.*/ } *ptr = value; return (SD_SUCCESS); } /************************************************************************/ #endif /* #ifdef INT64_SUPPORT */ /************************************************************************/