librpc: Fix string length checking in ndr_pull_charset_to_null()
authorAndreas Schneider <asn@samba.org>
Mon, 16 Dec 2019 14:50:17 +0000 (15:50 +0100)
committerAndrew Bartlett <abartlet@samba.org>
Fri, 20 Dec 2019 07:35:41 +0000 (07:35 +0000)
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14219

Pair-Programmed-With: Guenther Deschner <gd@samba.org>
Signed-off-by: Guenther Deschner <gd@samba.org>
Signed-off-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
librpc/ndr/ndr_string.c

index eb0af57a6ab22b68d82abd6641dbe98439f51b8a..25f211b2ad3a83bde1f6f9fc0fc0f95a4dfca8f3 100644 (file)
@@ -560,6 +560,47 @@ _PUBLIC_ uint32_t ndr_string_length(const void *_var, uint32_t element_size)
        return i+1;
 }
 
+/**
+ * @brief Get the string length including the null terminator if available.
+ *
+ * This checks the string length based on the elements. The returned number
+ * includes the terminating null byte(s) if found.
+ *
+ * @param[in]  _var    The string the calculate the length for.
+ *
+ * @param[in]  length  The length of the buffer passed by _var.
+ *
+ * @param[in]  element_size The element_size of a string char in bytes.
+ *
+ * @return The length of the strings or 0.
+ */
+static uint32_t ndr_string_n_length(const void *_var,
+                                   size_t length,
+                                   uint32_t element_size)
+{
+       size_t i = 0;
+       uint8_t zero[4] = {0,0,0,0};
+       const char *var = (const char *)_var;
+       int cmp;
+
+       if (element_size > 4) {
+               return 0;
+       }
+
+       for (i = 0; i < length; i++, var += element_size) {
+               cmp = memcmp(var, zero, element_size);
+               if (cmp == 0) {
+                       break;
+               }
+       }
+
+       if (i == length) {
+               return length;
+       }
+
+       return i + 1;
+}
+
 _PUBLIC_ enum ndr_err_code ndr_check_string_terminator(struct ndr_pull *ndr, uint32_t count, uint32_t element_size)
 {
        uint32_t i;
@@ -629,8 +670,12 @@ _PUBLIC_ enum ndr_err_code ndr_pull_charset_to_null(struct ndr_pull *ndr, int nd
 
        NDR_PULL_NEED_BYTES(ndr, length*byte_mul);
 
-       str_len = ndr_string_length(ndr->data+ndr->offset, byte_mul);
-       str_len = MIN(str_len, length); /* overrun protection */
+       str_len = ndr_string_n_length(ndr->data+ndr->offset, length, byte_mul);
+       if (str_len == 0) {
+               return ndr_pull_error(ndr, NDR_ERR_LENGTH,
+                                     "Invalid length");
+       }
+
        if (!convert_string_talloc(ndr->current_mem_ctx, chset, CH_UNIX,
                                   ndr->data+ndr->offset, str_len*byte_mul,
                                   discard_const_p(void *, var),