diff options
Diffstat (limited to 'third_party/sipcc/sdp_utils.c')
-rw-r--r-- | third_party/sipcc/sdp_utils.c | 725 |
1 files changed, 725 insertions, 0 deletions
diff --git a/third_party/sipcc/sdp_utils.c b/third_party/sipcc/sdp_utils.c new file mode 100644 index 0000000000..a5b558282e --- /dev/null +++ b/third_party/sipcc/sdp_utils.c @@ -0,0 +1,725 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <errno.h> +#include <limits.h> +#include <ctype.h> +#include "sdp_os_defs.h" +#include "sipcc_sdp.h" +#include "sdp_private.h" +#include "sdp_log.h" + +#define MKI_BUF_LEN 4 + +static const char* logTag = "sdp_utils"; + +// Actually checks for ASCII only unlike isdigit() on Windows. +// Also avoids UB issues with isdigit() sign extension when +// char is signed. +int sdp_is_ascii_digit(const char c) { + return '0' <= c && c <= '9'; +} + +sdp_mca_t *sdp_alloc_mca (uint32_t line) { + sdp_mca_t *mca_p; + + /* Allocate resource for new media stream. */ + mca_p = (sdp_mca_t *)SDP_MALLOC(sizeof(sdp_mca_t)); + if (mca_p == NULL) { + return (NULL); + } + /* Initialize mca structure */ + mca_p->media = SDP_MEDIA_INVALID; + mca_p->conn.nettype = SDP_NT_INVALID; + mca_p->conn.addrtype = SDP_AT_INVALID; + mca_p->conn.conn_addr[0] = '\0'; + mca_p->conn.is_multicast = FALSE; + mca_p->conn.ttl = 0; + mca_p->conn.num_of_addresses = 0; + mca_p->transport = SDP_TRANSPORT_INVALID; + mca_p->port = SDP_INVALID_VALUE; + mca_p->num_ports = SDP_INVALID_VALUE; + mca_p->vpi = SDP_INVALID_VALUE; + mca_p->vci = 0; + mca_p->vcci = SDP_INVALID_VALUE; + mca_p->cid = SDP_INVALID_VALUE; + mca_p->num_payloads = 0; + mca_p->sessinfo_found = FALSE; + mca_p->encrypt.encrypt_type = SDP_ENCRYPT_INVALID; + mca_p->media_attrs_p = NULL; + mca_p->next_p = NULL; + mca_p->mid = 0; + mca_p->bw.bw_data_count = 0; + mca_p->bw.bw_data_list = NULL; + mca_p->line_number = line; + mca_p->sctp_fmt = SDP_SCTP_MEDIA_FMT_UNKNOWN; + + return (mca_p); +} + +/* + * next_token + * + * copy token param with chars from str until null, cr, lf, or one of the delimiters is found. + * delimiters at the beginning will be skipped. + * The pointer *string_of_tokens is moved forward to the next token on sucess. + * + */ +static sdp_result_e next_token(const char **string_of_tokens, char *token, unsigned token_max_len, const char *delim) +{ + int flag2moveon = 0; + const char *str; + const char *token_end; + const char *next_delim; + + if (!string_of_tokens || !*string_of_tokens || !token || !delim) { + return SDP_FAILURE; + } + + str = *string_of_tokens; + token_end = token + token_max_len - 1; + + /* Locate front of token, skipping any delimiters */ + for ( ; ((*str != '\0') && (*str != '\n') && (*str != '\r')); str++) { + flag2moveon = 1; /* Default to move on unless we find a delimiter */ + for (next_delim=delim; *next_delim; next_delim++) { + if (*str == *next_delim) { + flag2moveon = 0; + break; + } + } + if( flag2moveon ) { + break; /* We're at the beginning of the token */ + } + } + + /* Make sure there's really a token present. */ + if ((*str == '\0') || (*str == '\n') || (*str == '\r')) { + return SDP_EMPTY_TOKEN; + } + + /* Now locate end of token */ + flag2moveon = 0; + + while ((token < token_end) && + (*str != '\0') && (*str != '\n') && (*str != '\r')) { + for (next_delim=delim; *next_delim; next_delim++) { + if (*str == *next_delim) { + flag2moveon = 1; + break; + } + } + if( flag2moveon ) { + break; + } else { + *token++ = *str++; + } + } + + /* mark end of token */ + *token = '\0'; + + /* set the string of tokens to the next token */ + *string_of_tokens = str; + + return SDP_SUCCESS; +} + +/* + * verify_sdescriptions_mki + * + * Verifies the syntax of the MKI parameter. + * + * mki = mki-value ":" mki-length + * mki-value = 1*DIGIT + * mki-length = 1*3DIGIT ; range 1..128 + * + * Inputs: + * buf - ptr to start of MKI string assumes NULL + * terminated string + * mkiValue - buffer to store the MKI value, assumes calling + * function has provided memory for this. + * mkiLen - integer to store the MKI length + * + * Outputs: + * Returns TRUE if syntax is correct and stores the + * MKI value in mkiVal and stores the length in mkiLen. + * Returns FALSE otherwise. + */ + +tinybool +verify_sdescriptions_mki (char *buf, char *mkiVal, uint16_t *mkiLen) +{ + + char *ptr, + mkiValBuf[SDP_SRTP_MAX_MKI_SIZE_BYTES], + mkiLenBuf[MKI_BUF_LEN]; + int idx = 0; + unsigned long strtoul_result; + char *strtoul_end; + + ptr = buf; + /* MKI must begin with a digit */ + if (!ptr || (!sdp_is_ascii_digit(*ptr))) { + return FALSE; + } + + /* scan until we reach a non-digit or colon */ + while (*ptr) { + if (*ptr == ':') { + /* terminate the MKI value */ + mkiValBuf[idx] = 0; + ptr++; + break; + } else if ((sdp_is_ascii_digit(*ptr) && (idx < SDP_SRTP_MAX_MKI_SIZE_BYTES-1))) { + mkiValBuf[idx++] = *ptr; + } else { + return FALSE; + } + + ptr++; + } + + /* there has to be a mki length */ + if (*ptr == 0) { + return FALSE; + } + + idx = 0; + + /* verify the mki length (max 3 digits) */ + while (*ptr) { + if (sdp_is_ascii_digit(*ptr) && (idx < 3)) { + mkiLenBuf[idx++] = *ptr; + } else { + return FALSE; + } + + ptr++; + } + + mkiLenBuf[idx] = 0; + + errno = 0; + strtoul_result = strtoul(mkiLenBuf, &strtoul_end, 10); + + /* mki len must be between 1..128 */ + if (errno || mkiLenBuf == strtoul_end || strtoul_result < 1 || strtoul_result > 128) { + *mkiLen = 0; + return FALSE; + } + + *mkiLen = (uint16_t) strtoul_result; + sstrncpy(mkiVal, mkiValBuf, MKI_BUF_LEN); + + return TRUE; +} + +/* + * verify_srtp_lifetime + * + * Verifies the Lifetime parameter syntax. + * + * lifetime = ["2^"] 1*(DIGIT) + * + * Inputs: + * buf - pointer to start of lifetime string. Assumes string is + * NULL terminated. + * Outputs: + * Returns TRUE if syntax is correct. Returns FALSE otherwise. + */ + +tinybool +verify_sdescriptions_lifetime (char *buf) +{ + + char *ptr; + tinybool tokenFound = FALSE; + + ptr = buf; + if (!ptr || *ptr == 0) { + return FALSE; + } + + while (*ptr) { + if (*ptr == '^') { + if (tokenFound) { + /* make sure we don't have multiple ^ */ + return FALSE; + } else { + tokenFound = TRUE; + /* Lifetime is in power of 2 format, make sure first and second + * chars are 2^ + */ + + if (buf[0] != '2' || buf[1] != '^') { + return FALSE; + } + } + } else if (!sdp_is_ascii_digit(*ptr)) { + return FALSE; + } + + ptr++; + + } + + /* Make sure if the format is 2^ that there is a number after the ^. */ + if (tokenFound) { + if (strlen(buf) <= 2) { + return FALSE; + } + } + + return TRUE; +} + + +/* + * sdp_validate_maxprate + * + * This function validates that the string passed in is of the form: + * packet-rate = 1*DIGIT ["." 1*DIGIT] + */ +tinybool +sdp_validate_maxprate(const char *string_parm) +{ + tinybool retval = FALSE; + + if (string_parm && (*string_parm)) { + while (sdp_is_ascii_digit(*string_parm)) { + string_parm++; + } + + if (*string_parm == '.') { + string_parm++; + while (sdp_is_ascii_digit(*string_parm)) { + string_parm++; + } + } + + if (*string_parm == '\0') { + retval = TRUE; + } else { + retval = FALSE; + } + } + + return retval; +} + +char *sdp_findchar (const char *ptr, char *char_list) +{ + int i; + + for (;*ptr != '\0'; ptr++) { + for (i=0; char_list[i] != '\0'; i++) { + if (*ptr == char_list[i]) { + return ((char *)ptr); + } + } + } + return ((char *)ptr); +} + +/* Locate the next token in a line. The delim characters are passed in + * as a param. The token also will not go past a new line char or the + * end of the string. Skip any delimiters before the token. + */ +const char *sdp_getnextstrtok (const char *str, char *tokenstr, unsigned tokenstr_len, + const char *delim, sdp_result_e *result) +{ + const char *token_list = str; + + if (!str || !tokenstr || !delim || !result) { + if (result) { + *result = SDP_FAILURE; + } + return str; + } + + *result = next_token(&token_list, tokenstr, tokenstr_len, delim); + + return token_list; +} + + + +/* Locate the next null ("-") or numeric token in a string. The delim + * characters are passed in as a param. The token also will not go past + * a new line char or the end of the string. Skip any delimiters before + * the token. + */ +uint32_t sdp_getnextnumtok_or_null (const char *str, const char **str_end, + const char *delim, tinybool *null_ind, + sdp_result_e *result) +{ + const char *token_list = str; + char temp_token[SDP_MAX_STRING_LEN]; + char *strtoul_end; + unsigned long numval; + + if (null_ind) { + *null_ind = FALSE; + } + + if (!str || !str_end || !delim || !null_ind || !result) { + if (result) { + *result = SDP_FAILURE; + } + return 0; + } + + *result = next_token(&token_list, temp_token, sizeof(temp_token), delim); + + if (*result != SDP_SUCCESS) { + return 0; + } + + /* First see if its the null char ("-") */ + if (temp_token[0] == '-') { + *null_ind = TRUE; + *result = SDP_SUCCESS; + *str_end = str; + return 0; + } + + errno = 0; + numval = strtoul(temp_token, &strtoul_end, 10); + + if (errno || strtoul_end == temp_token || numval > UINT_MAX) { + *result = SDP_FAILURE; + return 0; + } + + *result = SDP_SUCCESS; + *str_end = token_list; + return (uint32_t) numval; +} + + +/* Locate the next numeric token in a string. The delim characters are + * passed in as a param. The token also will not go past a new line char + * or the end of the string. Skip any delimiters before the token. + */ +uint32_t sdp_getnextnumtok (const char *str, const char **str_end, + const char *delim, sdp_result_e *result) +{ + const char *token_list = str; + char temp_token[SDP_MAX_STRING_LEN]; + char *strtoul_end; + unsigned long numval; + + if (!str || !str_end || !delim || !result) { + if (result) { + *result = SDP_FAILURE; + } + return 0; + } + + *result = next_token(&token_list, temp_token, sizeof(temp_token), delim); + + if (*result != SDP_SUCCESS) { + return 0; + } + + errno = 0; + numval = strtoul(temp_token, &strtoul_end, 10); + + if (errno || strtoul_end == temp_token || numval > UINT_MAX) { + *result = SDP_FAILURE; + return 0; + } + + *result = SDP_SUCCESS; + *str_end = token_list; + return (uint32_t) numval; +} + + +/* + * SDP Crypto Utility Functions. + * + * First a few common definitions. + */ + +/* + * Constants + * + * crypto_string = The string used to identify the start of sensative + * crypto data. + * + * inline_string = The string used to identify the start of key/salt + * crypto data. + * + * star_string = The string used to overwrite sensative data. + * + * '*_strlen' = The length of '*_string' in bytes (not including '\0') + */ +static const char crypto_string[] = "X-crypto:"; +static const int crypto_strlen = sizeof(crypto_string) - 1; +static const char inline_string[] = "inline:"; +static const int inline_strlen = sizeof(inline_string) - 1; +/* 40 characters is the current maximum for a Base64 encoded key/salt */ +static const char star_string[] = "****************************************"; +static const int star_strlen = sizeof(star_string) - 1; + +/* + * MIN_CRYPTO_STRING_SIZE_BYTES = This value defines the minimum + * size of a string that could contain a key/salt. This value + * is used to skip out of parsing when there is no reasonable + * assumption that sensative data will be found. The general + * format of a SRTP Key Salt in SDP looks like: + * + * X-crypto:<crypto_suite_name> inline:<master_key_salt>|| + * + * if <crypto_suite_name> and <master_key_salt> is at least + * one character and one space is used before the "inline:", + * then this translates to a size of (aligned by collumn from + * the format shown above): + * + * 9+ 1+ 1+7+ 1+ 2 = 21 + * + */ +#define MIN_CRYPTO_STRING_SIZE_BYTES 21 + +/* + * Utility macros + * + * CHAR_IS_WHITESPACE = macro to determine if the passed _test_char + * is whitespace. + * + * SKIP_WHITESPACE = Macro to advance _cptr to the next non-whitespace + * character. _cptr will not be advanced past _max_cptr. + * + * FIND_WHITESPACE = Macro to advance _cptr until whitespace is found. + * _cptr will not be advanced past _max_cptr. + */ +#define CHAR_IS_WHITESPACE(_test_char) \ + ((((_test_char)==' ')||((_test_char)=='\t'))?1:0) + +#define SKIP_WHITESPACE(_cptr, _max_cptr) \ + while ((_cptr)<=(_max_cptr)) { \ + if (!CHAR_IS_WHITESPACE(*(_cptr))) break; \ + (_cptr)++; \ + } + +#define FIND_WHITESPACE(_cptr, _max_cptr) \ + while ((_cptr)<=(_max_cptr)) { \ + if (CHAR_IS_WHITESPACE(*(_cptr))) break; \ + (_cptr)++; \ + } + +/* Function: sdp_crypto_debug + * Description: Check the passed buffer for sensitive data that should + * not be output (such as SRTP Master Key/Salt) and output + * the buffer as debug. Sensitive data will be replaced + * with the '*' character(s). This function may be used + * to display very large buffers so this function ensures + * that buginf is not overloaded. + * Parameters: buffer pointer to the message buffer to filter. + * length_bytes size of message buffer in bytes. + * Returns: Nothing. + */ +void sdp_crypto_debug (char *buffer, ulong length_bytes) +{ + char *current, *start; + char *last = buffer + length_bytes; + int result; + + /* + * For SRTP Master Key/Salt has the form: + * X-crypto:<crypto_suite_name> inline:<master_key_salt>|| + * Where <master_key_salt> is the data to elide (filter). + */ + for (start=current=buffer; + current<=last-MIN_CRYPTO_STRING_SIZE_BYTES; + current++) { + if ((*current == 'x') || (*current == 'X')) { + result = cpr_strncasecmp(current, crypto_string, crypto_strlen); + if (!result) { + current += crypto_strlen; + if (current > last) break; + + /* Skip over crypto suite name */ + FIND_WHITESPACE(current, last); + + /* Skip over whitespace */ + SKIP_WHITESPACE(current, last); + + /* identify inline keyword */ + result = cpr_strncasecmp(current, inline_string, inline_strlen); + if (!result) { + int star_count = 0; + + current += inline_strlen; + if (current > last) break; + + sdp_dump_buffer(start, current - start); + + /* Hide sensitive key/salt data */ + while (current<=last) { + if (*current == '|' || *current == '\n') { + /* Done, print the stars */ + while (star_count > star_strlen) { + /* + * This code is only for the case where + * too much base64 data was supplied + */ + sdp_dump_buffer((char*)star_string, star_strlen); + star_count -= star_strlen; + } + sdp_dump_buffer((char*)star_string, star_count); + break; + } else { + star_count++; + current++; + } + } + /* Update start pointer */ + start=current; + } + } + } + } + + if (last > start) { + /* Display remainder of buffer */ + sdp_dump_buffer(start, last - start); + } +} + +/* + * sdp_debug_msg_filter + * + * DESCRIPTION + * Check the passed message buffer for sensitive data that should + * not be output (such as SRTP Master Key/Salt). Sensitive data + * will be replaced with the '*' character(s). + * + * PARAMETERS + * buffer: pointer to the message buffer to filter. + * + * length_bytes: size of message buffer in bytes. + * + * RETURN VALUE + * The buffer modified. + */ +char * sdp_debug_msg_filter (char *buffer, ulong length_bytes) +{ + char *current; + char *last = buffer + length_bytes; + int result; + + SDP_PRINT("\n%s:%d: Eliding sensitive data from debug output", + __FILE__, __LINE__); + /* + * For SRTP Master Key/Salt has the form: + * X-crypto:<crypto_suite_name> inline:<master_key_salt>|| + * Where <master_key_salt> is the data to elide (filter). + */ + for (current=buffer; + current<=last-MIN_CRYPTO_STRING_SIZE_BYTES; + current++) { + if ((*current == 'x') || (*current == 'X')) { + result = cpr_strncasecmp(current, crypto_string, crypto_strlen); + if (!result) { + current += crypto_strlen; + if (current > last) break; + + /* Skip over crypto suite name */ + FIND_WHITESPACE(current, last); + + /* Skip over whitespace */ + SKIP_WHITESPACE(current, last); + + /* identify inline keyword */ + result = cpr_strncasecmp(current, inline_string, inline_strlen); + if (!result) { + current += inline_strlen; + if (current > last) break; + + /* Hide sensitive key/salt data */ + while (current<=last) { + if (*current == '|' || *current == '\n') { + /* Done */ + break; + } else { + *current = '*'; + current++; + } + } + } + } + } + } + + return buffer; +} + + +/* Function: sdp_checkrange + * Description: This checks the range of a ulong value to make sure its + * within the range of 0 and 4Gig. stroul cannot be used since + * for values greater greater than 4G, stroul will either wrap + * around or return ULONG_MAX. + * Parameters: sdp_p Pointer to the sdp structure + * num The number to check the range for + * u_val This variable get populated with the ulong value + * if the number is within the range. + * Returns: tinybool - returns TRUE if the number passed is within the + * range, FALSE otherwise + */ +tinybool sdp_checkrange (sdp_t *sdp_p, char *num, ulong *u_val) +{ + ulong l_val; + char *endP = NULL; + *u_val = 0; + + if (!num || !*num) { + return FALSE; + } + + if (*num == '-') { + if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) { + SDPLogError(logTag, "%s ERROR: Parameter value is a negative number: %s", + sdp_p->debug_str, num); + } + return FALSE; + } + + l_val = strtoul(num, &endP, 10); + if (*endP == '\0') { + + if (l_val > 4294967295UL) { + if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) { + SDPLogError(logTag, "%s ERROR: Parameter value: %s is greater than 4294967295", + sdp_p->debug_str, num); + } + return FALSE; + } + + if (l_val == 4294967295UL) { + /* + * On certain platforms where ULONG_MAX is equivalent to + * 4294967295, strtoul will return ULONG_MAX even if the the + * value of the string is greater than 4294967295. To detect + * that scenario we make an explicit check here. + */ + if (strcmp("4294967295", num)) { + if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) { + SDPLogError(logTag, "%s ERROR: Parameter value: %s is greater than 4294967295", + sdp_p->debug_str, num); + } + return FALSE; + } + } + } + *u_val = l_val; + return TRUE; +} + +#undef CHAR_IS_WHITESPACE +#undef SKIP_WHITESPACE +#undef FIND_WHITESPACE |