1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include <errno.h>
6 #include <limits.h>
7 #include <ctype.h>
8 #include "sdp_os_defs.h"
9 #include "sipcc_sdp.h"
10 #include "sdp_private.h"
11 #include "sdp_log.h"
12 
13 #define MKI_BUF_LEN 4
14 
15 static const char* logTag = "sdp_utils";
16 
17 // Actually checks for ASCII only unlike isdigit() on Windows.
18 // Also avoids UB issues with isdigit() sign extension when
19 // char is signed.
sdp_is_ascii_digit(const char c)20 int sdp_is_ascii_digit(const char c) {
21   return '0' <= c && c <= '9';
22 }
23 
sdp_alloc_mca(uint32_t line)24 sdp_mca_t *sdp_alloc_mca (uint32_t line) {
25     sdp_mca_t           *mca_p;
26 
27     /* Allocate resource for new media stream. */
28     mca_p = (sdp_mca_t *)SDP_MALLOC(sizeof(sdp_mca_t));
29     if (mca_p == NULL) {
30         return (NULL);
31     }
32     /* Initialize mca structure */
33     mca_p->media              = SDP_MEDIA_INVALID;
34     mca_p->conn.nettype       = SDP_NT_INVALID;
35     mca_p->conn.addrtype      = SDP_AT_INVALID;
36     mca_p->conn.conn_addr[0]  = '\0';
37     mca_p->conn.is_multicast  = FALSE;
38     mca_p->conn.ttl           = 0;
39     mca_p->conn.num_of_addresses = 0;
40     mca_p->transport          = SDP_TRANSPORT_INVALID;
41     mca_p->port               = SDP_INVALID_VALUE;
42     mca_p->num_ports          = SDP_INVALID_VALUE;
43     mca_p->vpi                = SDP_INVALID_VALUE;
44     mca_p->vci                = 0;
45     mca_p->vcci               = SDP_INVALID_VALUE;
46     mca_p->cid                = SDP_INVALID_VALUE;
47     mca_p->num_payloads       = 0;
48     mca_p->sessinfo_found     = FALSE;
49     mca_p->encrypt.encrypt_type  = SDP_ENCRYPT_INVALID;
50     mca_p->media_attrs_p      = NULL;
51     mca_p->next_p             = NULL;
52     mca_p->mid                = 0;
53     mca_p->bw.bw_data_count   = 0;
54     mca_p->bw.bw_data_list    = NULL;
55     mca_p->line_number        = line;
56     mca_p->sctp_fmt           = SDP_SCTP_MEDIA_FMT_UNKNOWN;
57 
58     return (mca_p);
59 }
60 
61 /*
62  * next_token
63  *
64  * copy token param with chars from str until null, cr, lf, or one of the delimiters is found.
65  * delimiters at the beginning will be skipped.
66  * The pointer *string_of_tokens is moved forward to the next token on sucess.
67  *
68  */
next_token(const char ** string_of_tokens,char * token,unsigned token_max_len,const char * delim)69 static sdp_result_e next_token(const char **string_of_tokens, char *token, unsigned token_max_len, const char *delim)
70 {
71   int flag2moveon = 0;
72   const char *str;
73   const char *token_end;
74   const char *next_delim;
75 
76   if (!string_of_tokens || !*string_of_tokens || !token || !delim) {
77     return SDP_FAILURE;
78   }
79 
80   str = *string_of_tokens;
81   token_end = token + token_max_len - 1;
82 
83   /* Locate front of token, skipping any delimiters */
84   for ( ; ((*str != '\0') && (*str != '\n') && (*str != '\r')); str++) {
85     flag2moveon = 1;  /* Default to move on unless we find a delimiter */
86     for (next_delim=delim; *next_delim; next_delim++) {
87       if (*str == *next_delim) {
88         flag2moveon = 0;
89         break;
90       }
91     }
92     if( flag2moveon ) {
93       break;  /* We're at the beginning of the token */
94     }
95   }
96 
97   /* Make sure there's really a token present. */
98   if ((*str == '\0') || (*str == '\n') || (*str == '\r')) {
99     return SDP_EMPTY_TOKEN;
100   }
101 
102   /* Now locate end of token */
103   flag2moveon = 0;
104 
105   while ((token < token_end) &&
106          (*str != '\0') && (*str != '\n') && (*str != '\r')) {
107       for (next_delim=delim; *next_delim; next_delim++) {
108           if (*str == *next_delim) {
109               flag2moveon = 1;
110               break;
111           }
112       }
113       if( flag2moveon ) {
114           break;
115       } else {
116           *token++ = *str++;
117       }
118   }
119 
120   /* mark end of token */
121   *token = '\0';
122 
123   /* set the string of tokens to the next token */
124   *string_of_tokens = str;
125 
126   return SDP_SUCCESS;
127 }
128 
129 /*
130  * verify_sdescriptions_mki
131  *
132  * Verifies the syntax of the MKI parameter.
133  *
134  * mki            = mki-value ":" mki-length
135  * mki-value      = 1*DIGIT
136  * mki-length     = 1*3DIGIT   ; range 1..128
137  *
138  * Inputs:
139  *   buf      - ptr to start of MKI string assumes NULL
140  *              terminated string
141  *   mkiValue - buffer to store the MKI value, assumes calling
142  *              function has provided memory for this.
143  *   mkiLen   - integer to store the MKI length
144  *
145  * Outputs:
146  *   Returns TRUE if syntax is correct and stores the
147  *   MKI value in mkiVal and stores the length in mkiLen.
148  *   Returns FALSE otherwise.
149  */
150 
151 tinybool
verify_sdescriptions_mki(char * buf,char * mkiVal,uint16_t * mkiLen)152 verify_sdescriptions_mki (char *buf, char *mkiVal, uint16_t *mkiLen)
153 {
154 
155     char       *ptr,
156                mkiValBuf[SDP_SRTP_MAX_MKI_SIZE_BYTES],
157                mkiLenBuf[MKI_BUF_LEN];
158     int        idx = 0;
159     unsigned long strtoul_result;
160     char *strtoul_end;
161 
162     ptr = buf;
163     /* MKI must begin with a digit */
164     if (!ptr || (!sdp_is_ascii_digit(*ptr))) {
165         return FALSE;
166     }
167 
168     /* scan until we reach a non-digit or colon */
169     while (*ptr) {
170         if (*ptr == ':') {
171             /* terminate the MKI value */
172             mkiValBuf[idx] = 0;
173             ptr++;
174             break;
175         } else if ((sdp_is_ascii_digit(*ptr) && (idx < SDP_SRTP_MAX_MKI_SIZE_BYTES-1))) {
176              mkiValBuf[idx++] = *ptr;
177         } else {
178              return FALSE;
179         }
180 
181         ptr++;
182     }
183 
184     /* there has to be a mki length */
185     if (*ptr == 0) {
186         return FALSE;
187     }
188 
189     idx = 0;
190 
191     /* verify the mki length (max 3 digits) */
192     while (*ptr) {
193         if (sdp_is_ascii_digit(*ptr) && (idx < 3)) {
194             mkiLenBuf[idx++] = *ptr;
195         } else {
196             return FALSE;
197         }
198 
199         ptr++;
200     }
201 
202     mkiLenBuf[idx] = 0;
203 
204     errno = 0;
205     strtoul_result = strtoul(mkiLenBuf, &strtoul_end, 10);
206 
207     /* mki len must be between 1..128 */
208     if (errno || mkiLenBuf == strtoul_end || strtoul_result < 1 || strtoul_result > 128) {
209       *mkiLen = 0;
210       return FALSE;
211     }
212 
213     *mkiLen = (uint16_t) strtoul_result;
214     sstrncpy(mkiVal, mkiValBuf, MKI_BUF_LEN);
215 
216     return TRUE;
217 }
218 
219 /*
220  * verify_srtp_lifetime
221  *
222  * Verifies the Lifetime parameter syntax.
223  *
224  *  lifetime = ["2^"] 1*(DIGIT)
225  *
226  * Inputs:
227  *   buf - pointer to start of lifetime string. Assumes string is
228  *         NULL terminated.
229  * Outputs:
230  *   Returns TRUE if syntax is correct. Returns FALSE otherwise.
231  */
232 
233 tinybool
verify_sdescriptions_lifetime(char * buf)234 verify_sdescriptions_lifetime (char *buf)
235 {
236 
237     char     *ptr;
238     tinybool tokenFound = FALSE;
239 
240     ptr = buf;
241     if (!ptr || *ptr == 0) {
242         return FALSE;
243     }
244 
245     while (*ptr) {
246         if (*ptr == '^') {
247             if (tokenFound) {
248                 /* make sure we don't have multiple ^ */
249                 return FALSE;
250             } else {
251                 tokenFound = TRUE;
252                 /* Lifetime is in power of 2 format, make sure first and second
253                  * chars are 2^
254                  */
255 
256                 if (buf[0] != '2' || buf[1] != '^') {
257                     return FALSE;
258                 }
259             }
260         } else if (!sdp_is_ascii_digit(*ptr)) {
261                    return FALSE;
262         }
263 
264         ptr++;
265 
266     }
267 
268     /* Make sure if the format is 2^ that there is a number after the ^. */
269     if (tokenFound) {
270         if (strlen(buf) <= 2) {
271             return FALSE;
272         }
273     }
274 
275     return TRUE;
276 }
277 
278 
279 /*
280  * sdp_validate_maxprate
281  *
282  * This function validates that the string passed in is of the form:
283  * packet-rate = 1*DIGIT ["." 1*DIGIT]
284  */
285 tinybool
sdp_validate_maxprate(const char * string_parm)286 sdp_validate_maxprate(const char *string_parm)
287 {
288     tinybool retval = FALSE;
289 
290     if (string_parm && (*string_parm)) {
291         while (sdp_is_ascii_digit(*string_parm)) {
292             string_parm++;
293         }
294 
295         if (*string_parm == '.') {
296             string_parm++;
297             while (sdp_is_ascii_digit(*string_parm)) {
298                 string_parm++;
299             }
300         }
301 
302         if (*string_parm == '\0') {
303             retval = TRUE;
304         } else {
305             retval = FALSE;
306         }
307     }
308 
309     return retval;
310 }
311 
sdp_findchar(const char * ptr,char * char_list)312 char *sdp_findchar (const char *ptr, char *char_list)
313 {
314     int i;
315 
316     for (;*ptr != '\0'; ptr++) {
317         for (i=0; char_list[i] != '\0'; i++) {
318             if (*ptr == char_list[i]) {
319                 return ((char *)ptr);
320             }
321         }
322     }
323     return ((char *)ptr);
324 }
325 
326 /* Locate the next token in a line.  The delim characters are passed in
327  * as a param.  The token also will not go past a new line char or the
328  * end of the string.  Skip any delimiters before the token.
329  */
sdp_getnextstrtok(const char * str,char * tokenstr,unsigned tokenstr_len,const char * delim,sdp_result_e * result)330 const char *sdp_getnextstrtok (const char *str, char *tokenstr, unsigned tokenstr_len,
331                          const char *delim, sdp_result_e *result)
332 {
333   const char *token_list = str;
334 
335   if (!str || !tokenstr || !delim || !result) {
336     if (result) {
337       *result = SDP_FAILURE;
338     }
339     return str;
340   }
341 
342   *result = next_token(&token_list, tokenstr, tokenstr_len, delim);
343 
344   return token_list;
345 }
346 
347 
348 
349 /* Locate the next null ("-") or numeric token in a string.  The delim
350  * characters are passed in as a param.  The token also will not go past
351  * a new line char or the end of the string.  Skip any delimiters before
352  * the token.
353  */
sdp_getnextnumtok_or_null(const char * str,const char ** str_end,const char * delim,tinybool * null_ind,sdp_result_e * result)354 uint32_t sdp_getnextnumtok_or_null (const char *str, const char **str_end,
355                                const char *delim, tinybool *null_ind,
356                                sdp_result_e *result)
357 {
358   const char *token_list = str;
359   char temp_token[SDP_MAX_STRING_LEN];
360   char *strtoul_end;
361   unsigned long numval;
362 
363   if (null_ind) {
364     *null_ind = FALSE;
365   }
366 
367   if (!str || !str_end || !delim || !null_ind || !result) {
368     if (result) {
369       *result = SDP_FAILURE;
370     }
371     return 0;
372   }
373 
374   *result = next_token(&token_list, temp_token, sizeof(temp_token), delim);
375 
376   if (*result != SDP_SUCCESS) {
377     return 0;
378   }
379 
380   /* First see if its the null char ("-") */
381   if (temp_token[0] == '-') {
382       *null_ind = TRUE;
383       *result = SDP_SUCCESS;
384       *str_end = str;
385       return 0;
386   }
387 
388   errno = 0;
389   numval = strtoul(temp_token, &strtoul_end, 10);
390 
391   if (errno || strtoul_end == temp_token || numval > UINT_MAX) {
392     *result = SDP_FAILURE;
393     return 0;
394   }
395 
396   *result = SDP_SUCCESS;
397   *str_end = token_list;
398   return (uint32_t) numval;
399 }
400 
401 
402 /* Locate the next numeric token in a string.  The delim characters are
403  * passed in as a param.  The token also will not go past a new line char
404  * or the end of the string.  Skip any delimiters before the token.
405  */
sdp_getnextnumtok(const char * str,const char ** str_end,const char * delim,sdp_result_e * result)406 uint32_t sdp_getnextnumtok (const char *str, const char **str_end,
407                        const char *delim, sdp_result_e *result)
408 {
409   const char *token_list = str;
410   char temp_token[SDP_MAX_STRING_LEN];
411   char *strtoul_end;
412   unsigned long numval;
413 
414   if (!str || !str_end || !delim || !result) {
415     if (result) {
416       *result = SDP_FAILURE;
417     }
418     return 0;
419   }
420 
421   *result = next_token(&token_list, temp_token, sizeof(temp_token), delim);
422 
423   if (*result != SDP_SUCCESS) {
424     return 0;
425   }
426 
427   errno = 0;
428   numval = strtoul(temp_token, &strtoul_end, 10);
429 
430   if (errno || strtoul_end == temp_token || numval > UINT_MAX) {
431     *result = SDP_FAILURE;
432     return 0;
433   }
434 
435   *result = SDP_SUCCESS;
436   *str_end = token_list;
437   return (uint32_t) numval;
438 }
439 
440 
441 /*
442  * SDP Crypto Utility Functions.
443  *
444  * First a few common definitions.
445  */
446 
447 /*
448  * Constants
449  *
450  * crypto_string = The string used to identify the start of sensative
451  *	crypto data.
452  *
453  * inline_string = The string used to identify the start of key/salt
454  *	crypto data.
455  *
456  * star_string = The string used to overwrite sensative data.
457  *
458  * '*_strlen' = The length of '*_string' in bytes (not including '\0')
459  */
460 static const char crypto_string[] = "X-crypto:";
461 static const int crypto_strlen = sizeof(crypto_string) - 1;
462 static const char inline_string[] = "inline:";
463 static const int inline_strlen = sizeof(inline_string) - 1;
464 /* 40 characters is the current maximum for a Base64 encoded key/salt */
465 static const char star_string[] = "****************************************";
466 static const int star_strlen = sizeof(star_string) - 1;
467 
468 /*
469  * MIN_CRYPTO_STRING_SIZE_BYTES = This value defines the minimum
470  *	size of a string that could contain a key/salt. This value
471  *	is used to skip out of parsing when there is no reasonable
472  *	assumption that sensative data will be found. The general
473  *	format of a SRTP Key Salt in SDP looks like:
474  *
475  * X-crypto:<crypto_suite_name> inline:<master_key_salt>||
476  *
477  *	if <crypto_suite_name> and <master_key_salt> is at least
478  *	one character and one space is used before the "inline:",
479  *	then this translates to a size of (aligned by collumn from
480  *	the format shown above):
481  *
482  * 9+       1+                 1+7+    1+                2 = 21
483  *
484  */
485 #define MIN_CRYPTO_STRING_SIZE_BYTES 21
486 
487 /*
488  * Utility macros
489  *
490  * CHAR_IS_WHITESPACE = macro to determine if the passed _test_char
491  *	is whitespace.
492  *
493  * SKIP_WHITESPACE = Macro to advance _cptr to the next non-whitespace
494  *	character. _cptr will not be advanced past _max_cptr.
495  *
496  * FIND_WHITESPACE = Macro to advance _cptr until whitespace is found.
497  *	_cptr will not be advanced past _max_cptr.
498  */
499 #define CHAR_IS_WHITESPACE(_test_char) \
500     ((((_test_char)==' ')||((_test_char)=='\t'))?1:0)
501 
502 #define SKIP_WHITESPACE(_cptr, _max_cptr)           \
503     while ((_cptr)<=(_max_cptr)) {                  \
504         if (!CHAR_IS_WHITESPACE(*(_cptr))) break;   \
505         (_cptr)++;                                  \
506     }
507 
508 #define FIND_WHITESPACE(_cptr, _max_cptr)           \
509     while ((_cptr)<=(_max_cptr)) {                  \
510         if (CHAR_IS_WHITESPACE(*(_cptr))) break;    \
511         (_cptr)++;                                  \
512     }
513 
514 /* Function:    sdp_crypto_debug
515  * Description: Check the passed buffer for sensitive data that should
516  *		not be output (such as SRTP Master Key/Salt) and output
517  *		the buffer as debug. Sensitive data will be replaced
518  *		with the '*' character(s). This function may be used
519  *		to display very large buffers so this function ensures
520  *		that buginf is not overloaded.
521  * Parameters:  buffer		pointer to the message buffer to filter.
522  *              length_bytes	size of message buffer in bytes.
523  * Returns:     Nothing.
524  */
sdp_crypto_debug(char * buffer,ulong length_bytes)525 void sdp_crypto_debug (char *buffer, ulong length_bytes)
526 {
527     char *current, *start;
528     char *last = buffer + length_bytes;
529     int result;
530 
531     /*
532      * For SRTP Master Key/Salt has the form:
533      * X-crypto:<crypto_suite_name> inline:<master_key_salt>||
534      * Where <master_key_salt> is the data to elide (filter).
535      */
536     for (start=current=buffer;
537          current<=last-MIN_CRYPTO_STRING_SIZE_BYTES;
538          current++) {
539         if ((*current == 'x') || (*current == 'X')) {
540             result = cpr_strncasecmp(current, crypto_string, crypto_strlen);
541             if (!result) {
542                 current += crypto_strlen;
543                 if (current > last) break;
544 
545                 /* Skip over crypto suite name */
546                 FIND_WHITESPACE(current, last);
547 
548                 /* Skip over whitespace */
549                 SKIP_WHITESPACE(current, last);
550 
551                 /* identify inline keyword */
552                 result = cpr_strncasecmp(current, inline_string, inline_strlen);
553                 if (!result) {
554                     int star_count = 0;
555 
556                     current += inline_strlen;
557                     if (current > last) break;
558 
559                     sdp_dump_buffer(start, current - start);
560 
561                     /* Hide sensitive key/salt data */
562                     while (current<=last) {
563                         if (*current == '|' || *current == '\n') {
564                             /* Done, print the stars */
565                             while (star_count > star_strlen) {
566                                 /*
567                                  * This code is only for the case where
568                                  * too much base64 data was supplied
569                                  */
570                                 sdp_dump_buffer((char*)star_string, star_strlen);
571                                 star_count -= star_strlen;
572                             }
573                             sdp_dump_buffer((char*)star_string, star_count);
574                             break;
575                         } else {
576                             star_count++;
577                             current++;
578                         }
579                     }
580                     /* Update start pointer */
581                     start=current;
582                 }
583             }
584         }
585     }
586 
587     if (last > start) {
588         /* Display remainder of buffer */
589         sdp_dump_buffer(start, last - start);
590     }
591 }
592 
593 /*
594  * sdp_debug_msg_filter
595  *
596  * DESCRIPTION
597  *     Check the passed message buffer for sensitive data that should
598  *     not be output (such as SRTP Master Key/Salt). Sensitive data
599  *     will be replaced with the '*' character(s).
600  *
601  * PARAMETERS
602  *     buffer: pointer to the message buffer to filter.
603  *
604  *     length_bytes: size of message buffer in bytes.
605  *
606  * RETURN VALUE
607  *     The buffer modified.
608  */
sdp_debug_msg_filter(char * buffer,ulong length_bytes)609 char * sdp_debug_msg_filter (char *buffer, ulong length_bytes)
610 {
611     char *current;
612     char *last = buffer + length_bytes;
613     int result;
614 
615     SDP_PRINT("\n%s:%d: Eliding sensitive data from debug output",
616             __FILE__, __LINE__);
617     /*
618      * For SRTP Master Key/Salt has the form:
619      * X-crypto:<crypto_suite_name> inline:<master_key_salt>||
620      * Where <master_key_salt> is the data to elide (filter).
621      */
622     for (current=buffer;
623          current<=last-MIN_CRYPTO_STRING_SIZE_BYTES;
624          current++) {
625         if ((*current == 'x') || (*current == 'X')) {
626             result = cpr_strncasecmp(current, crypto_string, crypto_strlen);
627             if (!result) {
628                 current += crypto_strlen;
629                 if (current > last) break;
630 
631                 /* Skip over crypto suite name */
632                 FIND_WHITESPACE(current, last);
633 
634                 /* Skip over whitespace */
635                 SKIP_WHITESPACE(current, last);
636 
637                 /* identify inline keyword */
638                 result = cpr_strncasecmp(current, inline_string, inline_strlen);
639                 if (!result) {
640                     current += inline_strlen;
641                     if (current > last) break;
642 
643                     /* Hide sensitive key/salt data */
644                     while (current<=last) {
645                         if (*current == '|' || *current == '\n') {
646                             /* Done */
647                             break;
648                         } else {
649                             *current = '*';
650                             current++;
651                         }
652                     }
653                 }
654             }
655         }
656     }
657 
658     return buffer;
659 }
660 
661 
662 /* Function:    sdp_checkrange
663  * Description: This checks the range of a ulong value to make sure its
664  *              within the range of 0 and 4Gig. stroul cannot be used since
665  *              for values greater greater than 4G, stroul will either wrap
666  *              around or return ULONG_MAX.
667  * Parameters:  sdp_p       Pointer to the sdp structure
668  *              num         The number to check the range for
669  *              u_val       This variable get populated with the ulong value
670  *                          if the number is within the range.
671  * Returns:     tinybool - returns TRUE if the number passed is within the
672  *                         range, FALSE otherwise
673  */
sdp_checkrange(sdp_t * sdp_p,char * num,ulong * u_val)674 tinybool sdp_checkrange (sdp_t *sdp_p, char *num, ulong *u_val)
675 {
676     ulong l_val;
677     char *endP = NULL;
678     *u_val = 0;
679 
680     if (!num || !*num) {
681         return FALSE;
682     }
683 
684     if (*num == '-') {
685         if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
686             SDPLogError(logTag, "%s ERROR: Parameter value is a negative number: %s",
687                       sdp_p->debug_str, num);
688         }
689         return FALSE;
690     }
691 
692     l_val = strtoul(num, &endP, 10);
693     if (*endP == '\0') {
694 
695         if (l_val > 4294967295UL) {
696             if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
697                 SDPLogError(logTag, "%s ERROR: Parameter value: %s is greater than 4294967295",
698                           sdp_p->debug_str, num);
699             }
700             return FALSE;
701         }
702 
703         if (l_val == 4294967295UL) {
704             /*
705              * On certain platforms where ULONG_MAX is equivalent to
706              * 4294967295, strtoul will return ULONG_MAX even if the the
707              * value of the string is greater than 4294967295. To detect
708              * that scenario we make an explicit check here.
709              */
710             if (strcmp("4294967295", num)) {
711                 if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
712                     SDPLogError(logTag, "%s ERROR: Parameter value: %s is greater than 4294967295",
713                               sdp_p->debug_str, num);
714                 }
715                 return FALSE;
716             }
717         }
718     }
719     *u_val = l_val;
720     return TRUE;
721 }
722 
723 #undef CHAR_IS_WHITESPACE
724 #undef SKIP_WHITESPACE
725 #undef FIND_WHITESPACE
726