enable scrub on Debian
[libguestfs.git] / hivex / hivex.c
index 148d837..7d26eeb 100644 (file)
 #include "hivex.h"
 #include "byte_conversions.h"
 
+/* These limits are in place to stop really stupid stuff and/or exploits. */
+#define HIVEX_MAX_SUBKEYS       10000
+#define HIVEX_MAX_VALUES         1000
+#define HIVEX_MAX_VALUE_LEN   1000000
+#define HIVEX_MAX_ALLOCATION  1000000
+
 static char *windows_utf16_to_utf8 (/* const */ char *input, size_t len);
 
 struct hive_h {
@@ -223,8 +229,7 @@ struct ntreg_vk_record {
   uint16_t name_len;            /* length of name */
   /* length of the data:
    * If data_len is <= 4, then it's stored inline.
-   * If data_len is 0x80000000, then it's an inline dword.
-   * Top bit may be set or not set at random.
+   * Top bit is set to indicate inline.
    */
   uint32_t data_len;
   uint32_t data_offset;         /* pointer to the data (or data if inline) */
@@ -719,7 +724,7 @@ get_children (hive_h *h, hive_node_h node,
     goto ok;
 
   /* Arbitrarily limit the number of subkeys we will ever deal with. */
-  if (nr_subkeys_in_nk > 1000000) {
+  if (nr_subkeys_in_nk > HIVEX_MAX_SUBKEYS) {
     errno = ERANGE;
     goto error;
   }
@@ -989,7 +994,7 @@ get_values (hive_h *h, hive_node_h node,
     goto ok;
 
   /* Arbitrarily limit the number of values we will ever deal with. */
-  if (nr_values > 100000) {
+  if (nr_values > HIVEX_MAX_VALUES) {
     errno = ERANGE;
     goto error;
   }
@@ -1145,11 +1150,7 @@ hivex_value_type (hive_h *h, hive_value_h value, hive_type *t, size_t *len)
 
   if (len) {
     *len = le32toh (vk->data_len);
-    if (*len == 0x80000000) {   /* special case */
-      *len = 4;
-      if (t) *t = hive_t_dword;
-    }
-    *len &= 0x7fffffff;
+    *len &= 0x7fffffff;         /* top bit indicates if data is stored inline */
   }
 
   return 0;
@@ -1168,27 +1169,30 @@ hivex_value_value (hive_h *h, hive_value_h value,
 
   hive_type t;
   size_t len;
+  int is_inline;
 
   t = le32toh (vk->data_type);
 
   len = le32toh (vk->data_len);
-  if (len == 0x80000000) {      /* special case */
-    len = 4;
-    t = hive_t_dword;
-  }
+  is_inline = !!(len & 0x80000000);
   len &= 0x7fffffff;
 
   if (h->msglvl >= 2)
-    fprintf (stderr, "hivex_value_value: value=0x%zx, t=%d, len=%zu\n",
-             value, t, len);
+    fprintf (stderr, "hivex_value_value: value=0x%zx, t=%d, len=%zu, inline=%d\n",
+             value, t, len, is_inline);
 
   if (t_rtn)
     *t_rtn = t;
   if (len_rtn)
     *len_rtn = len;
 
+  if (is_inline && len > 4) {
+    errno = ENOTSUP;
+    return NULL;
+  }
+
   /* Arbitrarily limit the length that we will read. */
-  if (len > 1000000) {
+  if (len > HIVEX_MAX_VALUE_LEN) {
     errno = ERANGE;
     return NULL;
   }
@@ -1197,8 +1201,7 @@ hivex_value_value (hive_h *h, hive_value_h value,
   if (ret == NULL)
     return NULL;
 
-  /* If length is <= 4 it's always stored inline. */
-  if (len <= 4) {
+  if (is_inline) {
     memcpy (ret, (char *) &vk->data_offset, len);
     return ret;
   }
@@ -1812,7 +1815,7 @@ allocate_block (hive_h *h, size_t seg_len, const char id[2])
   }
 
   /* Refuse really large allocations. */
-  if (seg_len > 1000000) {
+  if (seg_len > HIVEX_MAX_ALLOCATION) {
     if (h->msglvl >= 2)
       fprintf (stderr, "allocate_block: refusing large allocation (%zu), returning ERANGE\n",
                seg_len);
@@ -1843,7 +1846,7 @@ allocate_block (hive_h *h, size_t seg_len, const char id[2])
     (struct ntreg_hbin_block *) (h->addr + offset);
 
   blockhdr->seg_len = htole32 (- (int32_t) seg_len);
-  if (id[0] && id[1] && seg_len >= 6) {
+  if (id[0] && id[1] && seg_len >= sizeof (struct ntreg_hbin_block)) {
     blockhdr->id[0] = id[0];
     blockhdr->id[1] = id[1];
   }
@@ -1918,12 +1921,12 @@ delete_values (hive_h *h, hive_node_h node)
       (struct ntreg_vk_record *) (h->addr + values[i]);
 
     size_t len;
+    int is_inline;
     len = le32toh (vk->data_len);
-    if (len == 0x80000000)      /* special case */
-      len = 4;
+    is_inline = !!(len & 0x80000000); /* top bit indicates is inline */
     len &= 0x7fffffff;
 
-    if (len > 4) {              /* non-inline, so remove data block */
+    if (!is_inline) {           /* non-inline, so remove data block */
       size_t data_offset = le32toh (vk->data_offset);
       data_offset += 0x1000;
       mark_block_unused (h, data_offset);
@@ -2527,10 +2530,13 @@ hivex_node_set_values (hive_h *h, hive_node_h node,
     vk->name_len = htole16 (name_len);
     strcpy (vk->name, values[i].key);
     vk->data_type = htole32 (values[i].t);
-    vk->data_len = htole16 (values[i].len);
+    uint32_t len = values[i].len;
+    if (len <= 4)               /* store it inline => set MSB flag */
+      len |= 0x80000000;
+    vk->data_len = htole32 (len);
     vk->flags = name_len == 0 ? 0 : 1;
 
-    if (values[i].len <= 4)     /* Store data inline. */
+    if (values[i].len <= 4)     /* store it inline */
       memcpy (&vk->data_offset, values[i].value, values[i].len);
     else {
       size_t offs = allocate_block (h, values[i].len + 4, nul_id);
@@ -2541,6 +2547,7 @@ hivex_node_set_values (hive_h *h, hive_node_h node,
     }
 
     if (name_len * 2 > le32toh (nk->max_vk_name_len))
+      /* * 2 for UTF16-LE "reencoding" */
       nk->max_vk_name_len = htole32 (name_len * 2);
     if (values[i].len > le32toh (nk->max_vk_data_len))
       nk->max_vk_data_len = htole32 (values[i].len);