diff options
author | Havoc Pennington <hp@redhat.com> | 2003-01-30 04:20:44 +0000 |
---|---|---|
committer | Havoc Pennington <hp@redhat.com> | 2003-01-30 04:20:44 +0000 |
commit | 7ba714ad7fe8256edfaad7d9a0f09aeb9611ca44 (patch) | |
tree | 921d4ee8a780d5fe03f168405a5811287c10a926 /dbus/dbus-marshal.c | |
parent | 8fdd8915bd7424cdf90bf59a018838a1290ac0c4 (diff) |
2003-01-30 Havoc Pennington <hp@pobox.com>
* dbus/dbus-message.c: use message->byte_order instead of
DBUS_COMPILER_BYTE_ORDER throughout.
(dbus_message_create_header): pad header to align the
start of the body of the message to 8-byte boundary
* dbus/dbus-marshal.h: make all the demarshalers take const
DBusString arguments.
* dbus/dbus-message.c (_dbus_message_loader_return_buffer):
validate message args here, so we don't have to do slow validation
later, and so we catch bad messages as they are incoming. Also add
better checks on header_len and body_len. Also fill in
message->byte_order
* dbus/dbus-string.c (_dbus_string_validate_utf8): new (not
implemented properly)
(_dbus_string_validate_nul): new function to check all-nul
* dbus/dbus-marshal.c (_dbus_marshal_get_field_end_pos): rename
get_arg_end_pos and remove all validation
(_dbus_marshal_validate_arg): actually do validation here.
Diffstat (limited to 'dbus/dbus-marshal.c')
-rw-r--r-- | dbus/dbus-marshal.c | 371 |
1 files changed, 314 insertions, 57 deletions
diff --git a/dbus/dbus-marshal.c b/dbus/dbus-marshal.c index 9c17aabc..efdc7efb 100644 --- a/dbus/dbus-marshal.c +++ b/dbus/dbus-marshal.c @@ -73,7 +73,7 @@ _dbus_unpack_uint32 (int byte_order, return DBUS_UINT32_FROM_LE (*(dbus_uint32_t*)data); else return DBUS_UINT32_FROM_BE (*(dbus_uint32_t*)data); -} +} /** * Unpacks a 32 bit signed integer from a data pointer @@ -504,7 +504,7 @@ _dbus_marshal_string_array (DBusString *str, * @returns the demarshaled double. */ double -_dbus_demarshal_double (DBusString *str, +_dbus_demarshal_double (const DBusString *str, int byte_order, int pos, int *new_pos) @@ -537,7 +537,7 @@ _dbus_demarshal_double (DBusString *str, * @returns the demarshaled integer. */ dbus_int32_t -_dbus_demarshal_int32 (DBusString *str, +_dbus_demarshal_int32 (const DBusString *str, int byte_order, int pos, int *new_pos) @@ -564,7 +564,7 @@ _dbus_demarshal_int32 (DBusString *str, * @returns the demarshaled integer. */ dbus_uint32_t -_dbus_demarshal_uint32 (DBusString *str, +_dbus_demarshal_uint32 (const DBusString *str, int byte_order, int pos, int *new_pos) @@ -598,7 +598,7 @@ _dbus_demarshal_uint32 (DBusString *str, * @returns the demarshaled string. */ char * -_dbus_demarshal_string (DBusString *str, +_dbus_demarshal_string (const DBusString *str, int byte_order, int pos, int *new_pos) @@ -606,7 +606,7 @@ _dbus_demarshal_string (DBusString *str, int len; char *retval; const char *data; - + len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos); retval = dbus_malloc (len + 1); @@ -641,7 +641,7 @@ _dbus_demarshal_string (DBusString *str, * @returns the demarshaled data. */ unsigned char * -_dbus_demarshal_byte_array (DBusString *str, +_dbus_demarshal_byte_array (const DBusString *str, int byte_order, int pos, int *new_pos, @@ -685,7 +685,7 @@ _dbus_demarshal_byte_array (DBusString *str, * @returns the demarshaled data. */ dbus_int32_t * -_dbus_demarshal_int32_array (DBusString *str, +_dbus_demarshal_int32_array (const DBusString *str, int byte_order, int pos, int *new_pos, @@ -724,7 +724,7 @@ _dbus_demarshal_int32_array (DBusString *str, * @returns the demarshaled data. */ dbus_uint32_t * -_dbus_demarshal_uint32_array (DBusString *str, +_dbus_demarshal_uint32_array (const DBusString *str, int byte_order, int pos, int *new_pos, @@ -763,7 +763,7 @@ _dbus_demarshal_uint32_array (DBusString *str, * @returns the demarshaled data. */ double * -_dbus_demarshal_double_array (DBusString *str, +_dbus_demarshal_double_array (const DBusString *str, int byte_order, int pos, int *new_pos, @@ -802,7 +802,7 @@ _dbus_demarshal_double_array (DBusString *str, * @returns the demarshaled data. */ char ** -_dbus_demarshal_string_array (DBusString *str, +_dbus_demarshal_string_array (const DBusString *str, int byte_order, int pos, int *new_pos, @@ -843,32 +843,24 @@ _dbus_demarshal_string_array (DBusString *str, } /** - * Returns the position right after the end position - * end position of a field. Validates the field - * contents as required (e.g. ensures that - * string fields have a valid length and - * are nul-terminated). + * Returns the position right after the end of an argument. PERFORMS + * NO VALIDATION WHATSOEVER. The message must have been previously + * validated. * - * @todo security: audit the field validation code. - * - * @todo warns on invalid type in a message, but - * probably the whole message needs to be dumped, - * or we might even drop the connection due - * to bad protocol. Needs better error handling. - * Possible security issue. + * @todo handle DBUS_TYPE_NIL * * @param str a string * @param byte_order the byte order to use - * @param pos the pos where the field starts + * @param pos the pos where the arg starts * @param end_pos pointer where the position right * after the end position will follow - * @returns TRUE if more data exists after the field + * @returns TRUE if more data exists after the arg */ dbus_bool_t -_dbus_marshal_get_field_end_pos (DBusString *str, - int byte_order, - int pos, - int *end_pos) +_dbus_marshal_get_arg_end_pos (const DBusString *str, + int byte_order, + int pos, + int *end_pos) { const char *data; @@ -906,21 +898,8 @@ _dbus_marshal_get_field_end_pos (DBusString *str, len = _dbus_demarshal_uint32 (str, byte_order, pos + 1, &pos); *end_pos = pos + len + 1; - - if (*end_pos > _dbus_string_get_length (str)) - { - _dbus_verbose ("string length outside length of the message\n"); - return FALSE; - } - - if (_dbus_string_get_byte (str, pos+len) != '\0') - { - _dbus_verbose ("string field not nul-terminated\n"); - return FALSE; - } - - break; } + break; case DBUS_TYPE_BYTE_ARRAY: { @@ -930,9 +909,8 @@ _dbus_marshal_get_field_end_pos (DBusString *str, len = _dbus_demarshal_uint32 (str, byte_order, pos + 1, &pos); *end_pos = pos + len; - - break; } + break; case DBUS_TYPE_INT32_ARRAY: { @@ -943,9 +921,8 @@ _dbus_marshal_get_field_end_pos (DBusString *str, *end_pos = _DBUS_ALIGN_VALUE (new_pos, sizeof (dbus_int32_t)) + (len * sizeof (dbus_int32_t)); - - break; } + break; case DBUS_TYPE_UINT32_ARRAY: { @@ -956,9 +933,8 @@ _dbus_marshal_get_field_end_pos (DBusString *str, *end_pos = _DBUS_ALIGN_VALUE (new_pos, sizeof (dbus_uint32_t)) + (len * sizeof (dbus_uint32_t)); - - break; } + break; case DBUS_TYPE_DOUBLE_ARRAY: { @@ -969,9 +945,8 @@ _dbus_marshal_get_field_end_pos (DBusString *str, *end_pos = _DBUS_ALIGN_VALUE (new_pos, sizeof (double)) + (len * sizeof (double)); - - break; } + break; case DBUS_TYPE_STRING_ARRAY: { @@ -990,10 +965,262 @@ _dbus_marshal_get_field_end_pos (DBusString *str, } *end_pos = pos; - break; - } + } + break; + + default: + _dbus_warn ("Unknown message arg type %d\n", *data); + _dbus_assert_not_reached ("Unknown message argument type\n"); + return FALSE; + } + + if (*end_pos > _dbus_string_get_length (str)) + return FALSE; + + return TRUE; +} + +/** + * Demarshals and validates a length; returns < 0 if the validation + * fails. The length is required to be small enough that + * len*sizeof(double) will not overflow, and small enough to fit in a + * signed integer. + * + * @param str the string + * @param byte_order the byte order + * @param pos the unaligned string position (snap to next aligned) + */ +static int +demarshal_and_validate_len (const DBusString *str, + int byte_order, + int pos, + int *new_pos) +{ + int align_4 = _DBUS_ALIGN_VALUE (pos, 4); + unsigned int len; + + if ((align_4 + 4) >= _dbus_string_get_length (str)) + { + _dbus_verbose ("not enough room in message for array length\n"); + return -1; + } + + if (!_dbus_string_validate_nul (str, pos, + align_4 - pos)) + { + _dbus_verbose ("array length alignment padding not initialized to nul\n"); + return -1; + } + + len = _dbus_demarshal_uint32 (str, byte_order, align_4, new_pos); + + /* note that the len may be a number of doubles, so we need it to be + * at least SIZE_T_MAX / 8, but make it smaller just to keep things + * sane. We end up using ints for most sizes to avoid unsigned mess + * so limit to maximum 32-bit signed int divided by at least 8, more + * for a bit of paranoia margin. INT_MAX/32 is about 65 megabytes. + */ +#define MAX_ARRAY_LENGTH (((unsigned int)_DBUS_INT_MAX) / 32) + if (len > MAX_ARRAY_LENGTH) + { + _dbus_verbose ("array length %u exceeds maximum of %u\n", + len, MAX_ARRAY_LENGTH); + return -1; + } + else + return (int) len; +} + +static dbus_bool_t +validate_string (const DBusString *str, + int pos, + int len_without_nul, + int *end_pos) +{ + *end_pos = pos + len_without_nul + 1; + + if (*end_pos > _dbus_string_get_length (str)) + { + _dbus_verbose ("string length outside length of the message\n"); + return FALSE; + } + + if (_dbus_string_get_byte (str, pos + len_without_nul) != '\0') + { + _dbus_verbose ("string arg not nul-terminated\n"); + return FALSE; + } + + if (!_dbus_string_validate_utf8 (str, pos, len_without_nul)) + { + _dbus_verbose ("string is not valid UTF-8\n"); + return FALSE; + } + + return TRUE; +} + +/** + * Validates an argument, checking that it is well-formed, for example + * no ludicrous length fields, strings are nul-terminated, etc. + * Returns the end position of the argument in end_pos, and + * returns #TRUE if a valid arg begins at "pos" + * + * @todo security: need to audit this function. + * + * @todo handle DBUS_TYPE_NIL + * + * @param str a string + * @param byte_order the byte order to use + * @param pos the pos where the arg starts (offset of its typecode) + * @param end_pos pointer where the position right + * after the end position will follow + * @returns #TRUE if the arg is valid. + */ +dbus_bool_t +_dbus_marshal_validate_arg (const DBusString *str, + int byte_order, + int pos, + int *end_pos) +{ + const char *data; + + if (pos >= _dbus_string_get_length (str)) + return FALSE; + + _dbus_string_get_const_data_len (str, &data, pos, 1); + + switch (*data) + { + case DBUS_TYPE_INVALID: + return FALSE; + break; + + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + { + int align_4 = _DBUS_ALIGN_VALUE (pos + 1, 4); + + if (!_dbus_string_validate_nul (str, pos + 1, + align_4 - pos - 1)) + { + _dbus_verbose ("int32/uint32 alignment padding not initialized to nul\n"); + return FALSE; + } + + *end_pos = align_4 + 4; + } + break; + + case DBUS_TYPE_DOUBLE: + { + int align_8 = _DBUS_ALIGN_VALUE (pos + 1, 8); + + _dbus_verbose_bytes_of_string (str, pos, (align_8 + 8 - pos)); + + if (!_dbus_string_validate_nul (str, pos + 1, + align_8 - pos - 1)) + { + _dbus_verbose ("double alignment padding not initialized to nul\n"); + return FALSE; + } + + *end_pos = align_8 + 8; + } + break; + + case DBUS_TYPE_STRING: + { + int len; + + /* Demarshal the length, which does NOT include + * nul termination + */ + len = demarshal_and_validate_len (str, byte_order, pos + 1, &pos); + if (len < 0) + return FALSE; + + if (!validate_string (str, pos, len, end_pos)) + return FALSE; + } + break; + + case DBUS_TYPE_BYTE_ARRAY: + { + int len; + + len = demarshal_and_validate_len (str, byte_order, pos + 1, &pos); + if (len < 0) + return FALSE; + + *end_pos = pos + len; + } + break; + + case DBUS_TYPE_INT32_ARRAY: + case DBUS_TYPE_UINT32_ARRAY: + { + int len; + + len = demarshal_and_validate_len (str, byte_order, pos + 1, &pos); + if (len < 0) + return FALSE; + + _dbus_assert (_DBUS_ALIGN_VALUE (pos, 4) == (unsigned int) pos); + + *end_pos = pos + len * 4; + } + break; + + case DBUS_TYPE_DOUBLE_ARRAY: + { + int len; + int align_8; + + len = demarshal_and_validate_len (str, byte_order, pos + 1, &pos); + if (len < 0) + return FALSE; + + align_8 = _DBUS_ALIGN_VALUE (pos, 8); + if (!_dbus_string_validate_nul (str, pos, + align_8 - pos)) + { + _dbus_verbose ("double array alignment padding not initialized to nul\n"); + return FALSE; + } + + *end_pos = align_8 + len * 8; + } + break; + + case DBUS_TYPE_STRING_ARRAY: + { + int len; + int i; + + len = demarshal_and_validate_len (str, byte_order, pos + 1, &pos); + if (len < 0) + return FALSE; + + for (i = 0; i < len; i++) + { + int str_len; + + str_len = demarshal_and_validate_len (str, byte_order, + pos, &pos); + if (str_len < 0) + return FALSE; + + if (!validate_string (str, pos, str_len, &pos)) + return FALSE; + } + + *end_pos = pos; + } + break; + default: - _dbus_warn ("Unknown message field type %d\n", *data); + _dbus_warn ("Unknown message arg type %d\n", *data); return FALSE; } @@ -1003,6 +1230,7 @@ _dbus_marshal_get_field_end_pos (DBusString *str, return TRUE; } + /** * If in verbose mode, print a block of binary data. * @@ -1018,6 +1246,8 @@ _dbus_verbose_bytes (const unsigned char *data, int i; const unsigned char *aligned; + _dbus_assert (len >= 0); + /* Print blanks on first row if appropriate */ aligned = _DBUS_ALIGN_ADDRESS (data, 4); if (aligned > data) @@ -1026,7 +1256,7 @@ _dbus_verbose_bytes (const unsigned char *data, if (aligned != data) { - _dbus_verbose ("%5d\t%p: ", - (data - aligned), aligned); + _dbus_verbose ("%4d\t%p: ", - (data - aligned), aligned); while (aligned != data) { _dbus_verbose (" "); @@ -1040,7 +1270,7 @@ _dbus_verbose_bytes (const unsigned char *data, { if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i]) { - _dbus_verbose ("%5d\t%p: ", + _dbus_verbose ("%4d\t%p: ", i, &data[i]); } @@ -1056,9 +1286,16 @@ _dbus_verbose_bytes (const unsigned char *data, if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i]) { if (i > 3) - _dbus_verbose ("big: %d little: %d", + _dbus_verbose ("BE: %d LE: %d", _dbus_unpack_uint32 (DBUS_BIG_ENDIAN, &data[i-4]), _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN, &data[i-4])); + + if (i > 7 && + _DBUS_ALIGN_ADDRESS (&data[i], 8) == &data[i]) + { + _dbus_verbose (" dbl: %g", + *(double*)&data[i-8]); + } _dbus_verbose ("\n"); } @@ -1080,7 +1317,27 @@ _dbus_verbose_bytes_of_string (const DBusString *str, int len) { const char *d; + int real_len; + + real_len = _dbus_string_get_length (str); + _dbus_assert (start >= 0); + + if (start > real_len) + { + _dbus_verbose (" [%d,%d) is not inside string of length %d\n", + start, len, real_len); + return; + } + + if ((start + len) > real_len) + { + _dbus_verbose (" [%d,%d) extends outside string of length %d\n", + start, len, real_len); + len = real_len - start; + } + + _dbus_string_get_const_data_len (str, &d, start, len); _dbus_verbose_bytes (d, len); |