-/* -*- mode: C; c-file-style: "gnu" -*- */
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dbus-marshal-header.c Managing marshaling/demarshaling of message headers
*
* Copyright (C) 2005 Red Hat, Inc.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
+#include <config.h>
+#include "dbus/dbus-shared.h"
#include "dbus-marshal-header.h"
#include "dbus-marshal-recursive.h"
+#include "dbus-marshal-byteswap.h"
/**
* @addtogroup DBusMarshal
/* Not thread locked, but strictly const/read-only so should be OK
*/
+/** Static #DBusString containing the signature of a message header */
_DBUS_STRING_DEFINE_STATIC(_dbus_header_signature_str, DBUS_HEADER_SIGNATURE);
-_DBUS_STRING_DEFINE_STATIC(_dbus_local_interface_str, DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL);
-_DBUS_STRING_DEFINE_STATIC(_dbus_local_path_str, DBUS_PATH_ORG_FREEDESKTOP_LOCAL);
+/** Static #DBusString containing the local interface */
+_DBUS_STRING_DEFINE_STATIC(_dbus_local_interface_str, DBUS_INTERFACE_LOCAL);
+/** Static #DBusString containing the local path */
+_DBUS_STRING_DEFINE_STATIC(_dbus_local_path_str, DBUS_PATH_LOCAL);
+/** Offset from start of _dbus_header_signature_str to the signature of the fields array */
#define FIELDS_ARRAY_SIGNATURE_OFFSET 6
+/** Offset from start of _dbus_header_signature_str to the signature of an element of the fields array */
#define FIELDS_ARRAY_ELEMENT_SIGNATURE_OFFSET 7
typedef struct
{
- unsigned char code;
- unsigned char type;
+ unsigned char code; /**< the field code */
+ unsigned char type; /**< the value type */
} HeaderFieldType;
static const HeaderFieldType
{ DBUS_HEADER_FIELD_REPLY_SERIAL, DBUS_TYPE_UINT32 },
{ DBUS_HEADER_FIELD_DESTINATION, DBUS_TYPE_STRING },
{ DBUS_HEADER_FIELD_SENDER, DBUS_TYPE_STRING },
- { DBUS_HEADER_FIELD_SIGNATURE, DBUS_TYPE_SIGNATURE }
+ { DBUS_HEADER_FIELD_SIGNATURE, DBUS_TYPE_SIGNATURE },
+ { DBUS_HEADER_FIELD_UNIX_FDS, DBUS_TYPE_UINT32 }
};
+/** Macro to look up the correct type for a field */
#define EXPECTED_TYPE_OF_FIELD(field) (_dbus_header_field_types[field].type)
+/** The most padding we could ever need for a header */
#define MAX_POSSIBLE_HEADER_PADDING 7
static dbus_bool_t
reserve_header_padding (DBusHeader *header)
header->padding = _dbus_string_get_length (&header->data) - unpadded_len;
}
+/** Compute the end of the header, ignoring padding */
#define HEADER_END_BEFORE_PADDING(header) \
(_dbus_string_get_length (&(header)->data) - (header)->padding)
int field_code,
DBusTypeReader *variant_reader)
{
- int variant_type;
-
- variant_type = _dbus_type_reader_get_current_type (variant_reader);
-
header->fields[field_code].value_pos =
_dbus_type_reader_get_value_pos (variant_reader);
}
/**
+ * Returns the header's byte order.
+ *
+ * @param header the header
+ * @returns the byte order
+ */
+char
+_dbus_header_get_byte_order (const DBusHeader *header)
+{
+ _dbus_assert (_dbus_string_get_length (&header->data) > BYTE_ORDER_OFFSET);
+
+ return (char) _dbus_string_get_byte (&header->data, BYTE_ORDER_OFFSET);
+}
+
+/**
* Revalidates the fields cache
*
* @param header the header
}
_dbus_type_reader_init (&reader,
- header->byte_order,
+ _dbus_header_get_byte_order (header),
&_dbus_header_signature_str,
FIELDS_ARRAY_SIGNATURE_OFFSET,
&header->data,
* Writes a struct of { byte, variant } with the given basic type.
*
* @param writer the writer (should be ready to write a struct)
+ * @param field the header field
* @param type the type of the value
* @param value the value as for _dbus_marshal_set_basic()
* @returns #FALSE if no memory
* Sets a struct of { byte, variant } with the given basic type.
*
* @param reader the reader (should be iterating over the array pointing at the field to set)
+ * @param field the header field
* @param type the type of the value
* @param value the value as for _dbus_marshal_set_basic()
* @param realign_root where to realign from
{
DBusTypeReader sub;
DBusTypeReader variant;
- unsigned char v_BYTE;
_dbus_type_reader_recurse (reader, &sub);
_dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE);
#ifndef DBUS_DISABLE_ASSERT
- _dbus_type_reader_read_basic (&sub, &v_BYTE);
- _dbus_assert (((int) v_BYTE) == field);
+ {
+ unsigned char v_BYTE;
+ _dbus_type_reader_read_basic (&sub, &v_BYTE);
+ _dbus_assert (((int) v_BYTE) == field);
+ }
#endif
if (!_dbus_type_reader_next (&sub))
_dbus_marshal_set_uint32 (&header->data,
SERIAL_OFFSET,
serial,
- header->byte_order);
+ _dbus_header_get_byte_order (header));
}
/**
{
return _dbus_marshal_read_uint32 (&header->data,
SERIAL_OFFSET,
- header->byte_order,
+ _dbus_header_get_byte_order (header),
NULL);
}
* _dbus_header_create().
*
* @param header header to re-initialize
- * @param byte_order byte order of the header
*/
void
-_dbus_header_reinit (DBusHeader *header,
- int byte_order)
+_dbus_header_reinit (DBusHeader *header)
{
_dbus_string_set_length (&header->data, 0);
- header->byte_order = byte_order;
header->padding = 0;
_dbus_header_cache_invalidate_all (header);
* to make the header valid, you have to call _dbus_header_create().
*
* @param header header to initialize
- * @param byte_order byte order of the header
* @returns #FALSE if not enough memory
*/
dbus_bool_t
-_dbus_header_init (DBusHeader *header,
- int byte_order)
+_dbus_header_init (DBusHeader *header)
{
if (!_dbus_string_init_preallocated (&header->data, 32))
return FALSE;
- _dbus_header_reinit (header, byte_order);
+ _dbus_header_reinit (header);
return TRUE;
}
* sense, and passing them in will trigger an assertion failure.
*
* @param header the header
+ * @param byte_order byte order of the header
* @param message_type the message type
- * @param destination service field or #NULL
+ * @param destination destination field or #NULL
* @param path path field or #NULL
* @param interface interface field or #NULL
* @param member member field or #NULL
*/
dbus_bool_t
_dbus_header_create (DBusHeader *header,
+ int byte_order,
int message_type,
const char *destination,
const char *path,
DBusTypeWriter writer;
DBusTypeWriter array;
- _dbus_assert ((interface && member) ||
+ _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
+ byte_order == DBUS_BIG_ENDIAN);
+ _dbus_assert (((interface || message_type != DBUS_MESSAGE_TYPE_SIGNAL) && member) ||
(error_name) ||
!(interface || member || error_name));
_dbus_assert (_dbus_string_get_length (&header->data) == 0);
if (!reserve_header_padding (header))
return FALSE;
- _dbus_type_writer_init_values_only (&writer, header->byte_order,
+ _dbus_type_writer_init_values_only (&writer, byte_order,
&_dbus_header_signature_str, 0,
&header->data,
HEADER_END_BEFORE_PADDING (header));
- v_BYTE = header->byte_order;
+ v_BYTE = byte_order;
if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE,
&v_BYTE))
goto oom;
* contain the entire message (assuming the claimed lengths are
* accurate). Also checks that the lengths are in sanity parameters.
*
+ * @param max_message_length maximum length of a valid message
* @param validity return location for why the data is invalid if it is
* @param byte_order return location for byte order
* @param fields_array_len return location for claimed fields array length
dbus_uint32_t body_len_unsigned;
_dbus_assert (start >= 0);
- _dbus_assert (start < _DBUS_INT_MAX / 2);
+ _dbus_assert (start < _DBUS_INT32_MAX / 2);
_dbus_assert (len >= 0);
_dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8));
/* overflow should be impossible since the lengths aren't allowed to
* be huge.
*/
- _dbus_assert (max_message_length < _DBUS_INT_MAX / 2);
+ _dbus_assert (max_message_length < _DBUS_INT32_MAX / 2);
if (body_len_unsigned + header_len_unsigned > (unsigned) max_message_length)
{
*validity = DBUS_INVALID_MESSAGE_TOO_LONG;
return FALSE;
}
- _dbus_assert (body_len_unsigned < (unsigned) _DBUS_INT_MAX);
- _dbus_assert (fields_array_len_unsigned < (unsigned) _DBUS_INT_MAX);
- _dbus_assert (header_len_unsigned < (unsigned) _DBUS_INT_MAX);
+ _dbus_assert (body_len_unsigned < (unsigned) _DBUS_INT32_MAX);
+ _dbus_assert (fields_array_len_unsigned < (unsigned) _DBUS_INT32_MAX);
+ _dbus_assert (header_len_unsigned < (unsigned) _DBUS_INT32_MAX);
*body_len = body_len_unsigned;
*fields_array_len = fields_array_len_unsigned;
int expected_type;
const DBusString *value_str;
int value_pos;
+ int str_data_pos;
dbus_uint32_t v_UINT32;
int bad_string_code;
dbus_bool_t (* string_validation_func) (const DBusString *str,
v_UINT32 = 0;
value_str = NULL;
value_pos = -1;
+ str_data_pos = -1;
bad_string_code = DBUS_VALID;
if (expected_type == DBUS_TYPE_UINT32)
{
_dbus_header_get_field_raw (header, field,
&value_str, &value_pos);
+ str_data_pos = _DBUS_ALIGN_VALUE (value_pos, 4) + 4;
}
else
{
switch (field)
{
case DBUS_HEADER_FIELD_DESTINATION:
- string_validation_func = _dbus_validate_service;
+ string_validation_func = _dbus_validate_bus_name;
bad_string_code = DBUS_INVALID_BAD_DESTINATION;
break;
case DBUS_HEADER_FIELD_INTERFACE:
if (_dbus_string_equal_substring (&_dbus_local_interface_str,
0,
_dbus_string_get_length (&_dbus_local_interface_str),
- value_str, value_pos))
+ value_str, str_data_pos))
{
_dbus_verbose ("Message is on the local interface\n");
return DBUS_INVALID_USES_LOCAL_INTERFACE;
break;
case DBUS_HEADER_FIELD_SENDER:
- string_validation_func = _dbus_validate_service;
+ string_validation_func = _dbus_validate_bus_name;
bad_string_code = DBUS_INVALID_BAD_SENDER;
break;
/* OBJECT_PATH was validated generically due to its type */
string_validation_func = NULL;
- _dbus_verbose ("value_str %p value_pos %d value_str_len %d\n",
- value_str, value_pos,
- _dbus_string_get_length (value_str));
if (_dbus_string_equal_substring (&_dbus_local_path_str,
0,
_dbus_string_get_length (&_dbus_local_path_str),
- value_str, value_pos))
+ value_str, str_data_pos))
{
_dbus_verbose ("Message is from the local path\n");
return DBUS_INVALID_USES_LOCAL_PATH;
}
break;
+ case DBUS_HEADER_FIELD_UNIX_FDS:
+ /* Every value makes sense */
+ break;
+
case DBUS_HEADER_FIELD_SIGNATURE:
/* SIGNATURE validated generically due to its type */
string_validation_func = NULL;
_dbus_assert (bad_string_code != DBUS_VALID);
len = _dbus_marshal_read_uint32 (value_str, value_pos,
- header->byte_order, NULL);
+ _dbus_header_get_byte_order (header),
+ NULL);
- if (!(*string_validation_func) (value_str, value_pos + 4, len))
+#if 0
+ _dbus_verbose ("Validating string header field; code %d if fails\n",
+ bad_string_code);
+#endif
+ if (!(*string_validation_func) (value_str, str_data_pos, len))
return bad_string_code;
}
}
/**
- * Creates a message header from untrusted data. The return value
- * is #TRUE if there was enough memory and the data was valid. If it
- * returns #TRUE, the header will be created. If it returns #FALSE
- * and *validity == #DBUS_VALID, then there wasn't enough memory. If
- * it returns #FALSE and *validity != #DBUS_VALID then the data was
+ * Creates a message header from potentially-untrusted data. The
+ * return value is #TRUE if there was enough memory and the data was
+ * valid. If it returns #TRUE, the header will be created. If it
+ * returns #FALSE and *validity == #DBUS_VALIDITY_UNKNOWN_OOM_ERROR,
+ * then there wasn't enough memory. If it returns #FALSE
+ * and *validity != #DBUS_VALIDITY_UNKNOWN_OOM_ERROR then the data was
* invalid.
*
* The byte_order, fields_array_len, and body_len args should be from
* already done.
*
* @param header the header (must be initialized)
+ * @param mode whether to do validation
* @param validity return location for invalidity reason
* @param byte_order byte order from header
* @param fields_array_len claimed length of fields array
* @returns #FALSE if no memory or data was invalid, #TRUE otherwise
*/
dbus_bool_t
-_dbus_header_load_untrusted (DBusHeader *header,
- DBusValidity *validity,
- int byte_order,
- int fields_array_len,
- int header_len,
- int body_len,
- const DBusString *str,
- int start,
- int len)
+_dbus_header_load (DBusHeader *header,
+ DBusValidationMode mode,
+ DBusValidity *validity,
+ int byte_order,
+ int fields_array_len,
+ int header_len,
+ int body_len,
+ const DBusString *str,
+ int start,
+ int len)
{
int leftover;
DBusValidity v;
if (!_dbus_string_copy_len (str, start, header_len, &header->data, 0))
{
_dbus_verbose ("Failed to copy buffer into new header\n");
- *validity = DBUS_VALID;
+ *validity = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
return FALSE;
}
- v = _dbus_validate_body_with_reason (&_dbus_header_signature_str, 0,
- byte_order,
- &leftover,
- str, start, len);
-
- if (v != DBUS_VALID)
+ if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
{
- *validity = v;
- goto invalid;
+ leftover = len - header_len - body_len - start;
+ }
+ else
+ {
+ v = _dbus_validate_body_with_reason (&_dbus_header_signature_str, 0,
+ byte_order,
+ &leftover,
+ str, start, len);
+
+ if (v != DBUS_VALID)
+ {
+ *validity = v;
+ goto invalid;
+ }
}
_dbus_assert (leftover < len);
_dbus_assert (start + header_len == (int) _DBUS_ALIGN_VALUE (padding_start, 8));
_dbus_assert (start + header_len == padding_start + padding_len);
- if (!_dbus_string_validate_nul (str, padding_start, padding_len))
+ if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
{
- *validity = DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
- goto invalid;
+ if (!_dbus_string_validate_nul (str, padding_start, padding_len))
+ {
+ *validity = DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
+ goto invalid;
+ }
}
header->padding = padding_len;
+ if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
+ {
+ *validity = DBUS_VALID;
+ return TRUE;
+ }
+
/* We now know the data is well-formed, but we have to check that
* it's valid.
*/
_dbus_marshal_set_uint32 (&header->data,
BODY_LENGTH_OFFSET,
body_len,
- header->byte_order);
+ _dbus_header_get_byte_order (header));
}
+/**
+ * Try to find the given field.
+ *
+ * @param header the header
+ * @param field the field code
+ * @param reader a type reader; on success this is left pointing at the struct
+ * (uv) for the field, while on failure it is left pointing into empty space
+ * at the end of the header fields
+ * @param realign_root another type reader; on success or failure it is left
+ * pointing to the beginning of the array of fields (i.e. the thing that might
+ * need realigning)
+ * @returns #TRUE on success
+ */
static dbus_bool_t
find_field_for_modification (DBusHeader *header,
int field,
retval = FALSE;
_dbus_type_reader_init (realign_root,
- header->byte_order,
+ _dbus_header_get_byte_order (header),
&_dbus_header_signature_str,
FIELDS_ARRAY_SIGNATURE_OFFSET,
&header->data,
int type,
const void *value)
{
- _dbus_return_val_if_fail (field <= DBUS_HEADER_FIELD_LAST, FALSE);
+ _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
if (!reserve_header_padding (header))
return FALSE;
DBusTypeWriter array;
_dbus_type_writer_init_values_only (&writer,
- header->byte_order,
+ _dbus_header_get_byte_order (header),
&_dbus_header_signature_str,
FIELDS_ARRAY_SIGNATURE_OFFSET,
&header->data,
_dbus_marshal_read_basic (&header->data,
header->fields[field].value_pos,
- type, value, header->byte_order,
+ type, value, _dbus_header_get_byte_order (header),
NULL);
return TRUE;
return (*flags_p & flag) != 0;
}
-/** @} */
+/**
+ * Swaps the header into the given order if required.
+ *
+ * @param header the header
+ * @param new_order the new byte order
+ */
+void
+_dbus_header_byteswap (DBusHeader *header,
+ int new_order)
+{
+ char byte_order;
-#ifdef DBUS_BUILD_TESTS
-#include "dbus-test.h"
-#include <stdio.h>
+ byte_order = _dbus_header_get_byte_order (header);
-dbus_bool_t
-_dbus_marshal_header_test (void)
-{
+ if (byte_order == new_order)
+ return;
- return TRUE;
+ _dbus_marshal_byteswap (&_dbus_header_signature_str,
+ 0, byte_order,
+ new_order,
+ &header->data, 0);
+
+ _dbus_string_set_byte (&header->data, BYTE_ORDER_OFFSET, new_order);
}
-#endif /* DBUS_BUILD_TESTS */
+/** @} */