/* Unix SMB/CIFS implementation. Main metadata server / Spotlight routines Copyright (C) Ralph Boehme 2012-2014 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" #include "dalloc.h" #include "marshalling.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_RPC_SRV /* * This is used to talloc an array that will hold the table of * contents of a marshalled Spotlight RPC (S-RPC) reply. Each ToC * entry is 8 bytes, so we allocate space for 1024 entries which * should be sufficient for even the largest S-RPC replies. * * The total buffersize for S-RPC packets is typically limited to 64k, * so we can only store so many elements there anyway. */ #define MAX_SLQ_TOC 1024*8 #define MAX_SLQ_TOCIDX 1024 #define MAX_SLQ_COUNT 4096 #define MAX_SL_STRLEN 1024 /****************************************************************************** * RPC data marshalling and unmarshalling ******************************************************************************/ /* Spotlight epoch is 1.1.2001 00:00 UTC */ #define SPOTLIGHT_TIME_DELTA 978307200 /* Diff from UNIX epoch to Spotlight epoch */ #define SQ_TYPE_NULL 0x0000 #define SQ_TYPE_COMPLEX 0x0200 #define SQ_TYPE_INT64 0x8400 #define SQ_TYPE_BOOL 0x0100 #define SQ_TYPE_FLOAT 0x8500 #define SQ_TYPE_DATA 0x0700 #define SQ_TYPE_CNIDS 0x8700 #define SQ_TYPE_UUID 0x0e00 #define SQ_TYPE_DATE 0x8600 #define SQ_TYPE_TOC 0x8800 #define SQ_CPX_TYPE_ARRAY 0x0a00 #define SQ_CPX_TYPE_STRING 0x0c00 #define SQ_CPX_TYPE_UTF16_STRING 0x1c00 #define SQ_CPX_TYPE_DICT 0x0d00 #define SQ_CPX_TYPE_CNIDS 0x1a00 #define SQ_CPX_TYPE_FILEMETA 0x1b00 struct sl_tag { int type; int count; size_t length; size_t size; }; static ssize_t sl_pack_loop(DALLOC_CTX *query, char *buf, ssize_t offset, size_t bufsize, char *toc_buf, int *toc_idx, int *count); static ssize_t sl_unpack_loop(DALLOC_CTX *query, const char *buf, ssize_t offset, size_t bufsize, int count, ssize_t toc_offset, int encoding); static ssize_t sl_pack(DALLOC_CTX *query, char *buf, size_t bufsize); /****************************************************************************** * Wrapper functions for the *VAL macros with bound checking ******************************************************************************/ static ssize_t sl_push_uint64_val(char *buf, ssize_t offset, size_t max_offset, uint64_t val) { if (offset + 8 > max_offset) { DEBUG(1, ("%s: offset: %zd, max_offset: %zu", __func__, offset, max_offset)); return -1; } SBVAL(buf, offset, val); return offset + 8; } static ssize_t sl_pull_uint64_val(const char *buf, ssize_t offset, size_t bufsize, uint encoding, uint64_t *presult) { uint64_t val; if (offset + 8 > bufsize) { DEBUG(1,("%s: buffer overflow\n", __func__)); return -1; } if (encoding == SL_ENC_LITTLE_ENDIAN) { val = BVAL(buf, offset); } else { val = RBVAL(buf, offset); } *presult = val; return offset + 8; } /* * Returns the UTF-16 string encoding, by checking the 2-byte byte order mark. * If there is no byte order mark, -1 is returned. */ static int spotlight_get_utf16_string_encoding(const char *buf, ssize_t offset, size_t query_length, int encoding) { int utf16_encoding; /* Assumed encoding in absence of a bom is little endian */ utf16_encoding = SL_ENC_LITTLE_ENDIAN; if (query_length >= 2) { uint8_t le_bom[] = {0xff, 0xfe}; uint8_t be_bom[] = {0xfe, 0xff}; if (memcmp(le_bom, buf + offset, sizeof(uint16_t)) == 0) { utf16_encoding = SL_ENC_LITTLE_ENDIAN | SL_ENC_UTF_16; } else if (memcmp(be_bom, buf + offset, sizeof(uint16_t)) == 0) { utf16_encoding = SL_ENC_BIG_ENDIAN | SL_ENC_UTF_16; } } return utf16_encoding; } /****************************************************************************** * marshalling functions ******************************************************************************/ static inline uint64_t sl_pack_tag(uint16_t type, uint16_t size_or_count, uint32_t val) { uint64_t tag = ((uint64_t)val << 32) | ((uint64_t)type << 16) | size_or_count; return tag; } static ssize_t sl_pack_float(double d, char *buf, ssize_t offset, size_t bufsize) { union { double d; uint64_t w; } ieee_fp_union; ieee_fp_union.d = d; offset = sl_push_uint64_val(buf, offset, bufsize, sl_pack_tag(SQ_TYPE_FLOAT, 2, 1)); if (offset == -1) { return -1; } offset = sl_push_uint64_val(buf, offset, bufsize, ieee_fp_union.w); if (offset == -1) { return -1; } return offset; } static ssize_t sl_pack_uint64(uint64_t u, char *buf, ssize_t offset, size_t bufsize) { uint64_t tag; tag = sl_pack_tag(SQ_TYPE_INT64, 2, 1); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } offset = sl_push_uint64_val(buf, offset, bufsize, u); if (offset == -1) { return -1; } return offset; } static ssize_t sl_pack_uint64_array(uint64_t *u, char *buf, ssize_t offset, size_t bufsize, int *toc_count) { int count, i; uint64_t tag; count = talloc_array_length(u); tag = sl_pack_tag(SQ_TYPE_INT64, count + 1, count); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } for (i = 0; i < count; i++) { offset = sl_push_uint64_val(buf, offset, bufsize, u[i]); if (offset == -1) { return -1; } } if (count > 1) { *toc_count += (count - 1); } return offset; } static ssize_t sl_pack_bool(sl_bool_t val, char *buf, ssize_t offset, size_t bufsize) { uint64_t tag; tag = sl_pack_tag(SQ_TYPE_BOOL, 1, val ? 1 : 0); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } return offset; } static ssize_t sl_pack_nil(char *buf, ssize_t offset, size_t bufsize) { uint64_t tag; tag = sl_pack_tag(SQ_TYPE_NULL, 1, 1); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } return offset; } static ssize_t sl_pack_date(sl_time_t t, char *buf, ssize_t offset, size_t bufsize) { uint64_t data; uint64_t tag; union { double d; uint64_t w; } ieee_fp_union; tag = sl_pack_tag(SQ_TYPE_DATE, 2, 1); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } ieee_fp_union.d = (double)(t.tv_sec - SPOTLIGHT_TIME_DELTA); ieee_fp_union.d += (double)t.tv_usec / 1000000; data = ieee_fp_union.w; offset = sl_push_uint64_val(buf, offset, bufsize, data); if (offset == -1) { return -1; } return offset; } static ssize_t sl_pack_uuid(sl_uuid_t *uuid, char *buf, ssize_t offset, size_t bufsize) { uint64_t tag; tag = sl_pack_tag(SQ_TYPE_UUID, 3, 1); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } if (offset + 16 > bufsize) { return -1; } memcpy(buf + offset, uuid, 16); return offset + 16; } static ssize_t sl_pack_CNID(sl_cnids_t *cnids, char *buf, ssize_t offset, size_t bufsize, char *toc_buf, int *toc_idx) { ssize_t result; int len, i; int cnid_count = dalloc_size(cnids->ca_cnids); uint64_t tag; uint64_t id; void *p; tag = sl_pack_tag(SQ_CPX_TYPE_CNIDS, offset / 8, 0); result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag); if (result == -1) { return -1; } tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } *toc_idx += 1; len = cnid_count + 1; if (cnid_count > 0) { len ++; } /* unknown meaning, but always 8 */ tag = sl_pack_tag(SQ_TYPE_CNIDS, len, 8 ); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } if (cnid_count > 0) { tag = sl_pack_tag(cnids->ca_unkn1, cnid_count, cnids->ca_context); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } for (i = 0; i < cnid_count; i++) { p = dalloc_get_object(cnids->ca_cnids, i); if (p == NULL) { return -1; } memcpy(&id, p, sizeof(uint64_t)); offset = sl_push_uint64_val(buf, offset, bufsize, id); if (offset == -1) { return -1; } } } return offset; } static ssize_t sl_pack_array(sl_array_t *array, char *buf, ssize_t offset, size_t bufsize, char *toc_buf, int *toc_idx) { ssize_t result; int count = dalloc_size(array); int octets = offset / 8; uint64_t tag; int toc_idx_save = *toc_idx; tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } *toc_idx += 1; offset = sl_pack_loop(array, buf, offset, bufsize - offset, toc_buf, toc_idx, &count); tag = sl_pack_tag(SQ_CPX_TYPE_ARRAY, octets, count); result = sl_push_uint64_val(toc_buf, toc_idx_save * 8, MAX_SLQ_TOC, tag); if (result == -1) { return -1; } return offset; } static ssize_t sl_pack_dict(sl_array_t *dict, char *buf, ssize_t offset, size_t bufsize, char *toc_buf, int *toc_idx, int *count) { ssize_t result; uint64_t tag; tag = sl_pack_tag(SQ_CPX_TYPE_DICT, offset / 8, dalloc_size(dict)); result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag); if (result == -1) { return -1; } tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } *toc_idx += 1; offset = sl_pack_loop(dict, buf, offset, bufsize - offset, toc_buf, toc_idx, count); return offset; } static ssize_t sl_pack_filemeta(sl_filemeta_t *fm, char *buf, ssize_t offset, size_t bufsize, char *toc_buf, int *toc_idx) { ssize_t result; ssize_t fmlen; ssize_t saveoff = offset; uint64_t tag; tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } offset += 8; fmlen = sl_pack(fm, buf + offset, bufsize - offset); if (fmlen == -1) { return -1; } /* * Check for empty filemeta array, if it's only 40 bytes, it's * only the header but no content */ if (fmlen > 40) { offset += fmlen; } else { fmlen = 0; } /* unknown meaning, but always 8 */ tag = sl_pack_tag(SQ_TYPE_DATA, (fmlen / 8) + 1, 8); result = sl_push_uint64_val(buf, saveoff + 8, bufsize, tag); if (result == -1) { return -1; } tag = sl_pack_tag(SQ_CPX_TYPE_FILEMETA, saveoff / 8, fmlen / 8); result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag); if (result == -1) { return -1; } *toc_idx += 1; return offset; } static ssize_t sl_pack_string(char *s, char *buf, ssize_t offset, size_t bufsize, char *toc_buf, int *toc_idx) { ssize_t result; size_t len, octets, used_in_last_octet; uint64_t tag; len = strlen(s); if (len > MAX_SL_STRLEN) { return -1; } octets = (len + 7) / 8; used_in_last_octet = len % 8; if (used_in_last_octet == 0) { used_in_last_octet = 8; } tag = sl_pack_tag(SQ_CPX_TYPE_STRING, offset / 8, used_in_last_octet); result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag); if (result == -1) { return -1; } tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } *toc_idx += 1; tag = sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { return -1; } if (offset + (octets * 8) > bufsize) { return -1; } memset(buf + offset, 0, octets * 8); memcpy(buf + offset, s, len); offset += octets * 8; return offset; } static ssize_t sl_pack_string_as_utf16(char *s, char *buf, ssize_t offset, size_t bufsize, char *toc_buf, int *toc_idx) { ssize_t result; int utf16_plus_bom_len, octets, used_in_last_octet; char *utf16string = NULL; char bom[] = { 0xff, 0xfe }; size_t slen, utf16len; uint64_t tag; bool ok; slen = strlen(s); if (slen > MAX_SL_STRLEN) { return -1; } ok = convert_string_talloc(talloc_tos(), CH_UTF8, CH_UTF16LE, s, slen, &utf16string, &utf16len); if (!ok) { return -1; } utf16_plus_bom_len = utf16len + 2; octets = (utf16_plus_bom_len + 7) / 8; used_in_last_octet = utf16_plus_bom_len % 8; if (used_in_last_octet == 0) { used_in_last_octet = 8; } tag = sl_pack_tag(SQ_CPX_TYPE_UTF16_STRING, offset / 8, used_in_last_octet); result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag); if (result == -1) { offset = -1; goto done; } tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { goto done; } *toc_idx += 1; tag = sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet); offset = sl_push_uint64_val(buf, offset, bufsize, tag); if (offset == -1) { goto done; } if (offset + (octets * 8) > bufsize) { offset = -1; goto done; } memset(buf + offset, 0, octets * 8); memcpy(buf + offset, &bom, sizeof(bom)); memcpy(buf + offset + 2, utf16string, utf16len); offset += octets * 8; done: TALLOC_FREE(utf16string); return offset; } static ssize_t sl_pack_loop(DALLOC_CTX *query, char *buf, ssize_t offset, size_t bufsize, char *toc_buf, int *toc_idx, int *count) { const char *type; int n; uint64_t i; sl_bool_t bl; double d; sl_time_t t; void *p; for (n = 0; n < dalloc_size(query); n++) { type = dalloc_get_name(query, n); if (type == NULL) { return -1; } p = dalloc_get_object(query, n); if (p == NULL) { return -1; } if (strcmp(type, "sl_array_t") == 0) { offset = sl_pack_array(p, buf, offset, bufsize, toc_buf, toc_idx); } else if (strcmp(type, "sl_dict_t") == 0) { offset = sl_pack_dict(p, buf, offset, bufsize, toc_buf, toc_idx, count); } else if (strcmp(type, "sl_filemeta_t") == 0) { offset = sl_pack_filemeta(p, buf, offset, bufsize, toc_buf, toc_idx); } else if (strcmp(type, "uint64_t") == 0) { memcpy(&i, p, sizeof(uint64_t)); offset = sl_pack_uint64(i, buf, offset, bufsize); } else if (strcmp(type, "uint64_t *") == 0) { offset = sl_pack_uint64_array(p, buf, offset, bufsize, count); } else if (strcmp(type, "char *") == 0) { offset = sl_pack_string(p, buf, offset, bufsize, toc_buf, toc_idx); } else if (strcmp(type, "smb_ucs2_t *") == 0) { offset = sl_pack_string_as_utf16(p, buf, offset, bufsize, toc_buf, toc_idx); } else if (strcmp(type, "sl_bool_t") == 0) { memcpy(&bl, p, sizeof(sl_bool_t)); offset = sl_pack_bool(bl, buf, offset, bufsize); } else if (strcmp(type, "double") == 0) { memcpy(&d, p, sizeof(double)); offset = sl_pack_float(d, buf, offset, bufsize); } else if (strcmp(type, "sl_nil_t") == 0) { offset = sl_pack_nil(buf, offset, bufsize); } else if (strcmp(type, "sl_time_t") == 0) { memcpy(&t, p, sizeof(sl_time_t)); offset = sl_pack_date(t, buf, offset, bufsize); } else if (strcmp(type, "sl_uuid_t") == 0) { offset = sl_pack_uuid(p, buf, offset, bufsize); } else if (strcmp(type, "sl_cnids_t") == 0) { offset = sl_pack_CNID(p, buf, offset, bufsize, toc_buf, toc_idx); } else { DEBUG(1, ("unknown type: %s", type)); return -1; } if (offset == -1) { DEBUG(1, ("error packing type: %s\n", type)); return -1; } } return offset; } /****************************************************************************** * unmarshalling functions ******************************************************************************/ static ssize_t sl_unpack_tag(const char *buf, ssize_t offset, size_t bufsize, uint encoding, struct sl_tag *tag) { uint64_t val; if (offset + 8 > bufsize) { DEBUG(1,("%s: buffer overflow\n", __func__)); return -1; } if (encoding == SL_ENC_LITTLE_ENDIAN) { val = BVAL(buf, offset); } else { val = RBVAL(buf, offset); } tag->size = (val & 0xffff) * 8; tag->type = (val & 0xffff0000) >> 16; tag->count = val >> 32; tag->length = tag->count * 8; if (tag->size > MAX_SL_FRAGMENT_SIZE) { DEBUG(1,("%s: size limit %zu\n", __func__, tag->size)); return -1; } if (tag->length > MAX_SL_FRAGMENT_SIZE) { DEBUG(1,("%s: length limit %zu\n", __func__, tag->length)); return -1; } if (tag->count > MAX_SLQ_COUNT) { DEBUG(1,("%s: count limit %d\n", __func__, tag->count)); return -1; } return offset + 8; } static int sl_unpack_ints(DALLOC_CTX *query, const char *buf, ssize_t offset, size_t bufsize, int encoding) { int i, result; struct sl_tag tag; uint64_t query_data64; offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); if (offset == -1) { return -1; } for (i = 0; i < tag.count; i++) { offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64); if (offset == -1) { return -1; } result = dalloc_add_copy(query, &query_data64, uint64_t); if (result != 0) { return -1; } } return tag.count; } static int sl_unpack_date(DALLOC_CTX *query, const char *buf, ssize_t offset, size_t bufsize, int encoding) { int i, result; struct sl_tag tag; uint64_t query_data64; union { double d; uint64_t w; } ieee_fp_union; double fraction; sl_time_t t; offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); if (offset == -1) { return -1; } for (i = 0; i < tag.count; i++) { offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64); if (offset == -1) { return -1; } ieee_fp_union.w = query_data64; fraction = ieee_fp_union.d - (uint64_t)ieee_fp_union.d; t = (sl_time_t) { .tv_sec = ieee_fp_union.d + SPOTLIGHT_TIME_DELTA, .tv_usec = fraction * 1000000 }; result = dalloc_add_copy(query, &t, sl_time_t); if (result != 0) { return -1; } } return tag.count; } static int sl_unpack_uuid(DALLOC_CTX *query, const char *buf, ssize_t offset, size_t bufsize, int encoding) { int i, result; sl_uuid_t uuid; struct sl_tag tag; offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); if (offset == -1) { return -1; } for (i = 0; i < tag.count; i++) { if (offset + 16 > bufsize) { DEBUG(1,("%s: buffer overflow\n", __func__)); return -1; } memcpy(uuid.sl_uuid, buf + offset, 16); result = dalloc_add_copy(query, &uuid, sl_uuid_t); if (result != 0) { return -1; } offset += 16; } return tag.count; } static int sl_unpack_floats(DALLOC_CTX *query, const char *buf, ssize_t offset, size_t bufsize, int encoding) { int i, result; union { double d; uint32_t w[2]; } ieee_fp_union; struct sl_tag tag; offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); if (offset == -1) { return -1; } for (i = 0; i < tag.count; i++) { if (offset + 8 > bufsize) { DEBUG(1,("%s: buffer overflow\n", __func__)); return -1; } if (encoding == SL_ENC_LITTLE_ENDIAN) { #ifdef WORDS_BIGENDIAN ieee_fp_union.w[0] = IVAL(buf, offset + 4); ieee_fp_union.w[1] = IVAL(buf, offset); #else ieee_fp_union.w[0] = IVAL(buf, offset); ieee_fp_union.w[1] = IVAL(buf, offset + 4); #endif } else { #ifdef WORDS_BIGENDIAN ieee_fp_union.w[0] = RIVAL(buf, offset); ieee_fp_union.w[1] = RIVAL(buf, offset + 4); #else ieee_fp_union.w[0] = RIVAL(buf, offset + 4); ieee_fp_union.w[1] = RIVAL(buf, offset); #endif } result = dalloc_add_copy(query, &ieee_fp_union.d, double); if (result != 0) { return -1; } offset += 8; } return tag.count; } static int sl_unpack_CNID(DALLOC_CTX *query, const char *buf, ssize_t offset, size_t bufsize, int length, int encoding) { int i, count, result; uint64_t query_data64; sl_cnids_t *cnids; cnids = talloc_zero(query, sl_cnids_t); if (cnids == NULL) { return -1; } cnids->ca_cnids = dalloc_new(cnids); if (cnids->ca_cnids == NULL) { return -1; } if (length < 8) { return -1; } if (length == 8) { /* * That's permitted, length=8 is an empty CNID array. */ result = dalloc_add(query, cnids, sl_cnids_t); if (result != 0) { return -1; } return 0; } offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64); if (offset == -1) { return -1; } /* * Note: ca_unkn1 and ca_context could be taken from the tag * type and count members, but the fields are packed * differently in this context, so we can't use * sl_unpack_tag(). */ count = query_data64 & 0xffff;; cnids->ca_unkn1 = (query_data64 & 0xffff0000) >> 16; cnids->ca_context = query_data64 >> 32; for (i = 0; i < count; i++) { offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64); if (offset == -1) { return -1; } result = dalloc_add_copy(cnids->ca_cnids, &query_data64, uint64_t); if (result != 0) { return -1; } } result = dalloc_add(query, cnids, sl_cnids_t); if (result != 0) { return -1; } return 0; } static ssize_t sl_unpack_cpx(DALLOC_CTX *query, const char *buf, ssize_t offset, size_t bufsize, int cpx_query_type, int cpx_query_count, ssize_t toc_offset, int encoding) { int result; ssize_t roffset = offset; int unicode_encoding; bool mark_exists; char *p; size_t slen, tmp_len; sl_array_t *sl_array; sl_dict_t *sl_dict; sl_filemeta_t *sl_fm; bool ok; struct sl_tag tag; switch (cpx_query_type) { case SQ_CPX_TYPE_ARRAY: sl_array = dalloc_zero(query, sl_array_t); if (sl_array == NULL) { return -1; } roffset = sl_unpack_loop(sl_array, buf, offset, bufsize, cpx_query_count, toc_offset, encoding); if (roffset == -1) { return -1; } result = dalloc_add(query, sl_array, sl_array_t); if (result != 0) { return -1; } break; case SQ_CPX_TYPE_DICT: sl_dict = dalloc_zero(query, sl_dict_t); if (sl_dict == NULL) { return -1; } roffset = sl_unpack_loop(sl_dict, buf, offset, bufsize, cpx_query_count, toc_offset, encoding); if (roffset == -1) { return -1; } result = dalloc_add(query, sl_dict, sl_dict_t); if (result != 0) { return -1; } break; case SQ_CPX_TYPE_STRING: case SQ_CPX_TYPE_UTF16_STRING: offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); if (offset == -1) { return -1; } if (tag.size < 16) { DEBUG(1,("%s: string buffer too small\n", __func__)); return -1; } slen = tag.size - 16 + tag.count; if (slen > MAX_SL_FRAGMENT_SIZE) { return -1; } if (offset + slen > bufsize) { DEBUG(1,("%s: buffer overflow\n", __func__)); return -1; } if (cpx_query_type == SQ_CPX_TYPE_STRING) { p = talloc_strndup(query, buf + offset, slen); if (p == NULL) { return -1; } } else { unicode_encoding = spotlight_get_utf16_string_encoding( buf, offset, slen, encoding); mark_exists = (unicode_encoding & SL_ENC_UTF_16) ? true : false; if (unicode_encoding & SL_ENC_BIG_ENDIAN) { DEBUG(1, ("Unsupported big endian UTF16 string")); return -1; } slen -= mark_exists ? 2 : 0; ok = convert_string_talloc( query, CH_UTF16LE, CH_UTF8, buf + offset + (mark_exists ? 2 : 0), slen, &p, &tmp_len); if (!ok) { return -1; } } result = dalloc_stradd(query, p); if (result != 0) { return -1; } roffset += tag.size; break; case SQ_CPX_TYPE_FILEMETA: offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); if (offset == -1) { return -1; } if (tag.size < 8) { DBG_WARNING("size too mall: %zu\n", tag.size); return -1; } sl_fm = dalloc_zero(query, sl_filemeta_t); if (sl_fm == NULL) { return -1; } if (tag.size >= 16) { result = sl_unpack(sl_fm, buf + offset, bufsize - offset ); if (result == -1) { return -1; } } result = dalloc_add(query, sl_fm, sl_filemeta_t); if (result != 0) { return -1; } roffset += tag.size; break; case SQ_CPX_TYPE_CNIDS: offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); if (offset == -1) { return -1; } result = sl_unpack_CNID(query, buf, offset, bufsize, tag.size, encoding); if (result == -1) { return -1; } roffset += tag.size; break; default: DEBUG(1, ("unknown complex query type: %u", cpx_query_type)); return -1; } return roffset; } static ssize_t sl_unpack_loop(DALLOC_CTX *query, const char *buf, ssize_t offset, size_t bufsize, int count, ssize_t toc_offset, int encoding) { int i, toc_index, subcount; uint64_t result; while (count > 0) { struct sl_tag tag; if (offset >= toc_offset) { return -1; } result = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); if (result == -1) { return -1; } switch (tag.type) { case SQ_TYPE_COMPLEX: { struct sl_tag cpx_tag; if (tag.count < 1) { DEBUG(1,("%s: invalid tag.count: %d\n", __func__, tag.count)); return -1; } toc_index = tag.count - 1; if (toc_index > MAX_SLQ_TOCIDX) { DEBUG(1,("%s: toc_index too large: %d\n", __func__, toc_index)); return -1; } result = sl_unpack_tag(buf, toc_offset + (toc_index * 8), bufsize, encoding, &cpx_tag); if (result == -1) { return -1; } offset = sl_unpack_cpx(query, buf, offset + 8, bufsize, cpx_tag.type, cpx_tag.count, toc_offset, encoding); if (offset == -1) { return -1; } /* * tag.size is not the size here, so we need * to use the offset returned from sl_unpack_cpx() * instead of offset += tag.size; */ count--; break; } case SQ_TYPE_NULL: { sl_nil_t nil = 0; subcount = tag.count; if (subcount < 1 || subcount > count) { return -1; } for (i = 0; i < subcount; i++) { result = dalloc_add_copy(query, &nil, sl_nil_t); if (result != 0) { return -1; } } offset += tag.size; count -= subcount; break; } case SQ_TYPE_BOOL: { sl_bool_t b = (tag.count != 0); result = dalloc_add_copy(query, &b, sl_bool_t); if (result != 0) { return -1; } offset += tag.size; count--; break; } case SQ_TYPE_INT64: subcount = sl_unpack_ints(query, buf, offset, bufsize, encoding); if (subcount < 1 || subcount > count) { return -1; } offset += tag.size; count -= subcount; break; case SQ_TYPE_UUID: subcount = sl_unpack_uuid(query, buf, offset, bufsize, encoding); if (subcount < 1 || subcount > count) { return -1; } offset += tag.size; count -= subcount; break; case SQ_TYPE_FLOAT: subcount = sl_unpack_floats(query, buf, offset, bufsize, encoding); if (subcount < 1 || subcount > count) { return -1; } offset += tag.size; count -= subcount; break; case SQ_TYPE_DATE: subcount = sl_unpack_date(query, buf, offset, bufsize, encoding); if (subcount < 1 || subcount > count) { return -1; } offset += tag.size; count -= subcount; break; default: DEBUG(1, ("unknown query type: %d\n", tag.type)); return -1; } } return offset; } static ssize_t sl_pack(DALLOC_CTX *query, char *buf, size_t bufsize) { ssize_t result; char *toc_buf; int toc_index = 0; int toc_count = 0; ssize_t offset, len; uint64_t hdr; uint32_t total_octets; uint32_t data_octets; uint64_t tag; memset(buf, 0, bufsize); toc_buf = talloc_zero_size(query, MAX_SLQ_TOC + 8); if (toc_buf == NULL) { return -1; } offset = sl_pack_loop(query, buf, 16, bufsize, toc_buf + 8, &toc_index, &toc_count); if (offset == -1 || offset < 16) { DEBUG(10,("%s: sl_pack_loop error\n", __func__)); return -1; } len = offset - 16; /* * Marshalling overview: * * 16 bytes at the start of buf: * * 8 bytes byte order mark * 4 bytes total octets * 4 bytes table of content octets * * x bytes total octets * 8 from sl_pack_loop * x bytes ToC octets * 8 from toc_buf */ /* Byte-order mark - we are using little endian only for now */ memcpy(buf, "432130dm", strlen("432130dm")); /* * The data buffer and ToC buffer sizes are enocoded in number * of octets (size / 8), plus one, because the octet encoding * the sizes is included. */ data_octets = (len / 8) + 1; total_octets = data_octets + toc_index + 1; hdr = total_octets; hdr |= ((uint64_t)data_octets << 32); /* HDR */ result = sl_push_uint64_val(buf, 8, bufsize, hdr); if (result == -1) { return -1; } /* * ToC tag with number of ToC entries plus one, the ToC tag * header. */ tag = sl_pack_tag(SQ_TYPE_TOC, toc_index + 1, 0); result = sl_push_uint64_val(toc_buf, 0, MAX_SLQ_TOC, tag); if (result == -1) { return -1; } if ((16 + len + ((toc_index + 1 ) * 8)) > bufsize) { DEBUG(1, ("%s: exceeding size limit %zu", __func__, bufsize)); return -1; } memcpy(buf + 16 + len, toc_buf, (toc_index + 1 ) * 8); len += 16 + (toc_index + 1 ) * 8; return len; } /****************************************************************************** * Global functions for packing und unpacking ******************************************************************************/ NTSTATUS sl_pack_alloc(TALLOC_CTX *mem_ctx, DALLOC_CTX *d, struct mdssvc_blob *b, size_t max_fragment_size) { ssize_t len; b->spotlight_blob = talloc_zero_array(mem_ctx, uint8_t, max_fragment_size); if (b->spotlight_blob == NULL) { return NT_STATUS_NO_MEMORY; } len = sl_pack(d, (char *)b->spotlight_blob, max_fragment_size); if (len == -1) { return NT_STATUS_DATA_ERROR; } b->length = len; b->size = len; return NT_STATUS_OK; } bool sl_unpack(DALLOC_CTX *query, const char *buf, size_t bufsize) { ssize_t result; ssize_t offset = 0; int encoding; uint64_t hdr; uint32_t total_octets; uint64_t total_bytes; uint32_t data_octets; uint64_t data_bytes; uint64_t toc_offset; struct sl_tag toc_tag; if (bufsize > MAX_SL_FRAGMENT_SIZE) { return false; } if (bufsize < 8) { return false; } if (strncmp(buf + offset, "md031234", 8) == 0) { encoding = SL_ENC_BIG_ENDIAN; } else { encoding = SL_ENC_LITTLE_ENDIAN; } offset += 8; offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &hdr); if (offset == -1) { return false; } total_octets = hdr & UINT32_MAX; data_octets = hdr >> 32; /* * Both fields contain the number of octets of the * corresponding buffer plus the tag octet. We adjust the * values to match just the number of octets in the buffers. */ if (total_octets < 1) { return false; } if (data_octets < 1) { return false; } total_octets--; data_octets--; data_bytes = ((uint64_t)data_octets) * 8; total_bytes = ((uint64_t)total_octets) * 8; if (data_bytes >= total_bytes) { DEBUG(1,("%s: data_bytes: %" PRIu64 ", total_bytes: %" PRIu64 "\n", __func__, data_bytes, total_bytes)); return false; } if (total_bytes > (bufsize - offset)) { return false; } toc_offset = data_bytes; toc_offset = sl_unpack_tag(buf + offset, toc_offset, bufsize - offset, encoding, &toc_tag); if (toc_offset == -1) { return false; } if (toc_tag.type != SQ_TYPE_TOC) { DEBUG(1,("%s: unknown tag type %d\n", __func__, toc_tag.type)); return false; } /* * Check toc_tag.size even though we don't use it when unmarshalling */ if (toc_tag.size > MAX_SLQ_TOC) { DEBUG(1,("%s: bad size %zu\n", __func__, toc_tag.size)); return false; } if (toc_tag.size > (total_bytes - data_bytes)) { DEBUG(1,("%s: bad size %zu\n", __func__, toc_tag.size)); return false; } if (toc_tag.count != 0) { DEBUG(1,("%s: bad count %u\n", __func__, toc_tag.count)); return false; } /* * We already consumed 16 bytes from the buffer (BOM and size * tag), so we start at buf + offset. */ result = sl_unpack_loop(query, buf + offset, 0, bufsize - offset, 1, toc_offset, encoding); if (result == -1) { DEBUG(1,("%s: sl_unpack_loop failed\n", __func__)); return false; } return true; }