From 6f0b7a2940b5472cd36cfb900922cd7def3a2df6 Mon Sep 17 00:00:00 2001 From: Brett Lynnes Date: Fri, 31 Jan 2020 16:57:37 -0800 Subject: [PATCH] Ability to read large data from offsets if requested from device --- src/Server.cpp | 2 ++ src/ServerUtils.cpp | 28 +++++++++++++++++++++++++++- src/ServerUtils.h | 5 ++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/Server.cpp b/src/Server.cpp index aa2f5cd..208b28c 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -382,6 +382,8 @@ Server::Server(const std::string &serviceName, const std::string &advertisingNam .onReadValue(CHARACTERISTIC_METHOD_CALLBACK_LAMBDA { const char *pTextString = self.getDataPointer("text/string", ""); + // for longer data, the reading device may ask to read starting at an offset (i.e, iOS devices read in 184 byte chunks) + pTextString += ServerUtils::getOffsetFromParameters(pParameters, strlen(pTextString)); self.methodReturnValue(pInvocation, pTextString, true); }) diff --git a/src/ServerUtils.cpp b/src/ServerUtils.cpp index 2a8baf9..b32b060 100644 --- a/src/ServerUtils.cpp +++ b/src/ServerUtils.cpp @@ -194,6 +194,32 @@ void ServerUtils::getManagedObjects(GDBusMethodInvocation *pInvocation) g_dbus_method_invocation_return_value(pInvocation, pParams); } +// Sometimes you get an additional parameter back that the previous read was only partial and +// needs a subsequent chunk starting at "offset". If "offset" exists in the pParameters +// we need to move the char * that many bytes forward. As an example, this happens on iOS +// devices attempting a read of over 184 bytes. +// +// It is important that the maximum value not exceed the current data that this offset will +// be applied to. An attacker can easily ask for an offset that is well beyond the data they +// are supposed to have access to. +uint16_t ServerUtils::getOffsetFromParameters(GVariant *pParameters, uint16_t dataLength) +{ + GVariant *params; + g_variant_get(pParameters, "(@a{sv})", ¶ms); + uint16_t offset = 0; + GVariant *value = g_variant_lookup_value(params, "offset", NULL); + if (value) + { + g_variant_get(value, "q", &offset); + } + if (offset > dataLength) + { + Logger::error(SSTR << "Attempt to get read offset beyond scope of data. Offset: " << offset << " vs. maximum length: " << dataLength); + offset = dataLength; + } + return offset; +} + // WARNING: Hacky code - don't count on this working properly on all systems // // This routine will attempt to parse /proc/cpuinfo to return the CPU count/model. Results are cached on the first call, with @@ -305,4 +331,4 @@ GVariant *ServerUtils::gvariantLocalTime() return pVariant; } -}; // namespace ggk \ No newline at end of file +}; // namespace ggk diff --git a/src/ServerUtils.h b/src/ServerUtils.h index e5d6346..ca1b745 100644 --- a/src/ServerUtils.h +++ b/src/ServerUtils.h @@ -34,6 +34,9 @@ struct ServerUtils // Builds the response to the method call `GetManagedObjects` from the D-Bus interface `org.freedesktop.DBus.ObjectManager` static void getManagedObjects(GDBusMethodInvocation *pInvocation); + // Devices will sometimes perform reads on long buffers in multiple chunks, this returns an offset if that occurs. + static uint16_t getOffsetFromParameters(GVariant *params, uint16_t dataLength); + // WARNING: Hacky code - don't count on this working properly on all systems // // This routine will attempt to parse /proc/cpuinfo to return the CPU count/model. Results are cached on the first call, with @@ -53,4 +56,4 @@ struct ServerUtils static GVariant *gvariantLocalTime(); }; -}; // namespace ggk \ No newline at end of file +}; // namespace ggk