summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/glib-ectomy.c87
-rw-r--r--common/glib-ectomy.h2
-rw-r--r--hcid/dbus-adapter.c4
-rw-r--r--hcid/security.c3
4 files changed, 94 insertions, 2 deletions
diff --git a/common/glib-ectomy.c b/common/glib-ectomy.c
index 33d20533..56357648 100644
--- a/common/glib-ectomy.c
+++ b/common/glib-ectomy.c
@@ -515,3 +515,90 @@ gint g_timeout_remove(const guint id)
return -1;
}
+
+/* UTF-8 Validation: approximate copy/paste from glib2. */
+
+#define UNICODE_VALID(c) \
+ ((c) < 0x110000 && \
+ (((c) & 0xFFFFF800) != 0xD800) && \
+ ((c) < 0xFDD0 || (c) > 0xFDEF) && \
+ ((c) & 0xFFFE) != 0xFFFE)
+
+#define CONTINUATION_CHAR(c, val) \
+ do { \
+ if (((c) & 0xc0) != 0x80) /* 10xxxxxx */ \
+ goto failed; \
+ (val) <<= 6; \
+ (val) |= (c) & 0x3f; \
+ } while (0)
+
+#define INCREMENT_AND_CHECK_MAX(i, max_len) \
+ do { \
+ (i)++; \
+ if ((max_len) >= 0 && (i) >= (max_len)) \
+ goto failed; \
+ } while (0)
+
+
+gboolean g_utf8_validate(const gchar *str, gssize max_len, const gchar **end)
+{
+ unsigned long val, min, i;
+ const unsigned char *p, *last;
+
+ min = val = 0;
+
+ for (p = (unsigned char *) str, i = 0; p[i]; i++) {
+ if (max_len >= 0 && i >= max_len)
+ break;
+
+ if (p[i] < 128)
+ continue;
+
+ last = &p[i];
+
+ if ((p[i] & 0xe0) == 0xc0) { /* 110xxxxx */
+ if ((p[i] & 0x1e) == 0)
+ goto failed;
+ INCREMENT_AND_CHECK_MAX(i, max_len);
+ if ((p[i] & 0xc0) != 0x80)
+ goto failed; /* 10xxxxxx */
+ } else {
+ if ((p[i] & 0xf0) == 0xe0) {
+ /* 1110xxxx */
+ min = (1 << 11);
+ val = p[i] & 0x0f;
+ goto two_remaining;
+ } else if ((p[i] & 0xf8) == 0xf0) {
+ /* 11110xxx */
+ min = (1 << 16);
+ val = p[i] & 0x07;
+ } else
+ goto failed;
+
+ INCREMENT_AND_CHECK_MAX(i, max_len);
+ CONTINUATION_CHAR(p[i], val);
+two_remaining:
+ INCREMENT_AND_CHECK_MAX(i, max_len);
+ CONTINUATION_CHAR(p[i], val);
+
+ INCREMENT_AND_CHECK_MAX(i, max_len);
+ CONTINUATION_CHAR(p[i], val);
+
+ if (val < min || !UNICODE_VALID(val))
+ goto failed;
+ }
+ }
+
+ if (end)
+ *end = (const gchar *) &p[i];
+
+ return TRUE;
+
+failed:
+ if (end)
+ *end = (const gchar *) last;
+
+ return FALSE;
+}
+
+
diff --git a/common/glib-ectomy.h b/common/glib-ectomy.h
index 2885f019..4d00870c 100644
--- a/common/glib-ectomy.h
+++ b/common/glib-ectomy.h
@@ -93,6 +93,8 @@ void g_main_loop_unref(GMainLoop *loop);
guint g_timeout_add(guint interval, GSourceFunc function, gpointer data);
gint g_timeout_remove(const guint id);
+gboolean g_utf8_validate(const gchar *str, gssize max_len, const gchar **end);
+
#define g_main_new(is_running) g_main_loop_new(NULL, is_running);
#define g_main_run(loop) g_main_loop_run(loop)
#define g_main_quit(loop) g_main_loop_quit(loop)
diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c
index 3fbd0c36..2989536c 100644
--- a/hcid/dbus-adapter.c
+++ b/hcid/dbus-adapter.c
@@ -1036,8 +1036,8 @@ static DBusHandlerResult handle_dev_set_name_req(DBusConnection *conn, DBusMessa
dbus_error_free(&err);
return error_invalid_arguments(conn, msg);
}
-
- if (strlen(str_ptr) == 0) {
+
+ if (strlen(str_ptr) == 0 || !g_utf8_validate(str_ptr, -1, NULL)) {
error("Name change failed: Invalid parameter");
return error_invalid_arguments(conn, msg);
}
diff --git a/hcid/security.c b/hcid/security.c
index d44bfea4..e1c29ea9 100644
--- a/hcid/security.c
+++ b/hcid/security.c
@@ -464,7 +464,10 @@ static inline void remote_name_information(int dev, bdaddr_t *sba, void *ptr)
bacpy(&dba, &evt->bdaddr);
if (!evt->status) {
+ char *end;
memcpy(name, evt->name, 248);
+ if (!g_utf8_validate(name, -1, &end))
+ *end = '\0';
write_device_name(sba, &dba, name);
}