1 /*- 2 * Copyright (c) 2008 Christos Zoulas 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 */ 26 #include "file.h" 27 28 #ifndef lint 29 FILE_RCSID("@(#)$File: readcdf.c,v 1.40 2014/03/06 15:23:33 christos Exp $") 30 #endif 31 32 #include <assert.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <string.h> 36 #include <time.h> 37 #include <ctype.h> 38 #if defined(HAVE_LOCALE_H) 39 #include <locale.h> 40 #endif 41 42 #include "cdf.h" 43 #include "magic.h" 44 45 #define NOTMIME(ms) (((ms)->flags & MAGIC_MIME) == 0) 46 47 static const struct nv { 48 const char *pattern; 49 const char *mime; 50 } app2mime[] = { 51 { "Word", "msword", }, 52 { "Excel", "vnd.ms-excel", }, 53 { "Powerpoint", "vnd.ms-powerpoint", }, 54 { "Crystal Reports", "x-rpt", }, 55 { "Advanced Installer", "vnd.ms-msi", }, 56 { "InstallShield", "vnd.ms-msi", }, 57 { "Microsoft Patch Compiler", "vnd.ms-msi", }, 58 { "NAnt", "vnd.ms-msi", }, 59 { "Windows Installer", "vnd.ms-msi", }, 60 { NULL, NULL, }, 61 }, name2mime[] = { 62 { "WordDocument", "msword", }, 63 { "PowerPoint", "vnd.ms-powerpoint", }, 64 { "DigitalSignature", "vnd.ms-msi", }, 65 { NULL, NULL, }, 66 }, name2desc[] = { 67 { "WordDocument", "Microsoft Office Word",}, 68 { "PowerPoint", "Microsoft PowerPoint", }, 69 { "DigitalSignature", "Microsoft Installer", }, 70 { NULL, NULL, }, 71 }; 72 73 static const struct cv { 74 uint64_t clsid[2]; 75 const char *mime; 76 } clsid2mime[] = { 77 { 78 { 0x00000000000c1084LLU, 0x46000000000000c0LLU }, 79 "x-msi", 80 } 81 }, clsid2desc[] = { 82 { 83 { 0x00000000000c1084LLU, 0x46000000000000c0LLU }, 84 "MSI Installer", 85 }, 86 }; 87 88 private const char * 89 cdf_clsid_to_mime(const uint64_t clsid[2], const struct cv *cv) 90 { 91 size_t i; 92 for (i = 0; cv[i].mime != NULL; i++) { 93 if (clsid[0] == cv[i].clsid[0] && clsid[1] == cv[i].clsid[1]) 94 return cv[i].mime; 95 } 96 return NULL; 97 } 98 99 private const char * 100 cdf_app_to_mime(const char *vbuf, const struct nv *nv) 101 { 102 size_t i; 103 const char *rv = NULL; 104 char *old_lc_ctype; 105 106 old_lc_ctype = setlocale(LC_CTYPE, NULL); 107 assert(old_lc_ctype != NULL); 108 old_lc_ctype = strdup(old_lc_ctype); 109 assert(old_lc_ctype != NULL); 110 (void)setlocale(LC_CTYPE, "C"); 111 for (i = 0; nv[i].pattern != NULL; i++) 112 if (strcasestr(vbuf, nv[i].pattern) != NULL) { 113 rv = nv[i].mime; 114 break; 115 } 116 (void)setlocale(LC_CTYPE, old_lc_ctype); 117 free(old_lc_ctype); 118 return rv; 119 } 120 121 private int 122 cdf_file_property_info(struct magic_set *ms, const cdf_property_info_t *info, 123 size_t count, const uint64_t clsid[2]) 124 { 125 size_t i; 126 cdf_timestamp_t tp; 127 struct timespec ts; 128 char buf[64]; 129 const char *str = NULL; 130 const char *s; 131 int len; 132 133 if (!NOTMIME(ms)) 134 str = cdf_clsid_to_mime(clsid, clsid2mime); 135 136 for (i = 0; i < count; i++) { 137 cdf_print_property_name(buf, sizeof(buf), info[i].pi_id); 138 switch (info[i].pi_type) { 139 case CDF_NULL: 140 break; 141 case CDF_SIGNED16: 142 if (NOTMIME(ms) && file_printf(ms, ", %s: %hd", buf, 143 info[i].pi_s16) == -1) 144 return -1; 145 break; 146 case CDF_SIGNED32: 147 if (NOTMIME(ms) && file_printf(ms, ", %s: %d", buf, 148 info[i].pi_s32) == -1) 149 return -1; 150 break; 151 case CDF_UNSIGNED32: 152 if (NOTMIME(ms) && file_printf(ms, ", %s: %u", buf, 153 info[i].pi_u32) == -1) 154 return -1; 155 break; 156 case CDF_FLOAT: 157 if (NOTMIME(ms) && file_printf(ms, ", %s: %g", buf, 158 info[i].pi_f) == -1) 159 return -1; 160 break; 161 case CDF_DOUBLE: 162 if (NOTMIME(ms) && file_printf(ms, ", %s: %g", buf, 163 info[i].pi_d) == -1) 164 return -1; 165 break; 166 case CDF_LENGTH32_STRING: 167 case CDF_LENGTH32_WSTRING: 168 len = info[i].pi_str.s_len; 169 if (len > 1) { 170 char vbuf[1024]; 171 size_t j, k = 1; 172 173 if (info[i].pi_type == CDF_LENGTH32_WSTRING) 174 k++; 175 s = info[i].pi_str.s_buf; 176 for (j = 0; j < sizeof(vbuf) && len--; 177 j++, s += k) { 178 if (*s == '\0') 179 break; 180 if (isprint((unsigned char)*s)) 181 vbuf[j] = *s; 182 } 183 if (j == sizeof(vbuf)) 184 --j; 185 vbuf[j] = '\0'; 186 if (NOTMIME(ms)) { 187 if (vbuf[0]) { 188 if (file_printf(ms, ", %s: %s", 189 buf, vbuf) == -1) 190 return -1; 191 } 192 } else if (str == NULL && info[i].pi_id == 193 CDF_PROPERTY_NAME_OF_APPLICATION) { 194 str = cdf_app_to_mime(vbuf, app2mime); 195 } 196 } 197 break; 198 case CDF_FILETIME: 199 tp = info[i].pi_tp; 200 if (tp != 0) { 201 char tbuf[64]; 202 if (tp < 1000000000000000LL) { 203 cdf_print_elapsed_time(tbuf, 204 sizeof(tbuf), tp); 205 if (NOTMIME(ms) && file_printf(ms, 206 ", %s: %s", buf, tbuf) == -1) 207 return -1; 208 } else { 209 char *c, *ec; 210 cdf_timestamp_to_timespec(&ts, tp); 211 c = cdf_ctime(&ts.tv_sec, tbuf); 212 if (c != NULL && 213 (ec = strchr(c, '\n')) != NULL) 214 *ec = '\0'; 215 216 if (NOTMIME(ms) && file_printf(ms, 217 ", %s: %s", buf, c) == -1) 218 return -1; 219 } 220 } 221 break; 222 case CDF_CLIPBOARD: 223 break; 224 default: 225 return -1; 226 } 227 } 228 if (!NOTMIME(ms)) { 229 if (str == NULL) 230 return 0; 231 if (file_printf(ms, "application/%s", str) == -1) 232 return -1; 233 } 234 return 1; 235 } 236 237 private int 238 cdf_file_summary_info(struct magic_set *ms, const cdf_header_t *h, 239 const cdf_stream_t *sst, const uint64_t clsid[2]) 240 { 241 cdf_summary_info_header_t si; 242 cdf_property_info_t *info; 243 size_t count; 244 int m; 245 246 if (cdf_unpack_summary_info(sst, h, &si, &info, &count) == -1) 247 return -1; 248 249 if (NOTMIME(ms)) { 250 const char *str; 251 252 if (file_printf(ms, "Composite Document File V2 Document") 253 == -1) 254 return -1; 255 256 if (file_printf(ms, ", %s Endian", 257 si.si_byte_order == 0xfffe ? "Little" : "Big") == -1) 258 return -2; 259 switch (si.si_os) { 260 case 2: 261 if (file_printf(ms, ", Os: Windows, Version %d.%d", 262 si.si_os_version & 0xff, 263 (uint32_t)si.si_os_version >> 8) == -1) 264 return -2; 265 break; 266 case 1: 267 if (file_printf(ms, ", Os: MacOS, Version %d.%d", 268 (uint32_t)si.si_os_version >> 8, 269 si.si_os_version & 0xff) == -1) 270 return -2; 271 break; 272 default: 273 if (file_printf(ms, ", Os %d, Version: %d.%d", si.si_os, 274 si.si_os_version & 0xff, 275 (uint32_t)si.si_os_version >> 8) == -1) 276 return -2; 277 break; 278 } 279 str = cdf_clsid_to_mime(clsid, clsid2desc); 280 if (str) 281 if (file_printf(ms, ", %s", str) == -1) 282 return -2; 283 } 284 285 m = cdf_file_property_info(ms, info, count, clsid); 286 free(info); 287 288 return m == -1 ? -2 : m; 289 } 290 291 #ifdef notdef 292 private char * 293 format_clsid(char *buf, size_t len, const uint64_t uuid[2]) { 294 snprintf(buf, len, "%.8" PRIx64 "-%.4" PRIx64 "-%.4" PRIx64 "-%.4" 295 PRIx64 "-%.12" PRIx64, 296 (uuid[0] >> 32) & (uint64_t)0x000000000ffffffffLLU, 297 (uuid[0] >> 16) & (uint64_t)0x0000000000000ffffLLU, 298 (uuid[0] >> 0) & (uint64_t)0x0000000000000ffffLLU, 299 (uuid[1] >> 48) & (uint64_t)0x0000000000000ffffLLU, 300 (uuid[1] >> 0) & (uint64_t)0x0000fffffffffffffLLU); 301 return buf; 302 } 303 #endif 304 305 protected int 306 file_trycdf(struct magic_set *ms, int fd, const unsigned char *buf, 307 size_t nbytes) 308 { 309 cdf_info_t info; 310 cdf_header_t h; 311 cdf_sat_t sat, ssat; 312 cdf_stream_t sst, scn; 313 cdf_dir_t dir; 314 int i; 315 const char *expn = ""; 316 const char *corrupt = "corrupt: "; 317 318 info.i_fd = fd; 319 info.i_buf = buf; 320 info.i_len = nbytes; 321 if (ms->flags & MAGIC_APPLE) 322 return 0; 323 if (cdf_read_header(&info, &h) == -1) 324 return 0; 325 #ifdef CDF_DEBUG 326 cdf_dump_header(&h); 327 #endif 328 329 if ((i = cdf_read_sat(&info, &h, &sat)) == -1) { 330 expn = "Can't read SAT"; 331 goto out0; 332 } 333 #ifdef CDF_DEBUG 334 cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h)); 335 #endif 336 337 if ((i = cdf_read_ssat(&info, &h, &sat, &ssat)) == -1) { 338 expn = "Can't read SSAT"; 339 goto out1; 340 } 341 #ifdef CDF_DEBUG 342 cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h)); 343 #endif 344 345 if ((i = cdf_read_dir(&info, &h, &sat, &dir)) == -1) { 346 expn = "Can't read directory"; 347 goto out2; 348 } 349 350 const cdf_directory_t *root_storage; 351 if ((i = cdf_read_short_stream(&info, &h, &sat, &dir, &sst, 352 &root_storage)) == -1) { 353 expn = "Cannot read short stream"; 354 goto out3; 355 } 356 #ifdef CDF_DEBUG 357 cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir); 358 #endif 359 #ifdef notdef 360 if (root_storage) { 361 if (NOTMIME(ms)) { 362 char clsbuf[128]; 363 if (file_printf(ms, "CLSID %s, ", 364 format_clsid(clsbuf, sizeof(clsbuf), 365 root_storage->d_storage_uuid)) == -1) 366 return -1; 367 } 368 } 369 #endif 370 371 if ((i = cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir, 372 &scn)) == -1) { 373 if (errno == ESRCH) { 374 corrupt = expn; 375 expn = "No summary info"; 376 } else { 377 expn = "Cannot read summary info"; 378 } 379 goto out4; 380 } 381 #ifdef CDF_DEBUG 382 cdf_dump_summary_info(&h, &scn); 383 #endif 384 if ((i = cdf_file_summary_info(ms, &h, &scn, 385 root_storage->d_storage_uuid)) < 0) 386 expn = "Can't expand summary_info"; 387 388 if (i == 0) { 389 const char *str = NULL; 390 cdf_directory_t *d; 391 char name[__arraycount(d->d_name)]; 392 size_t j, k; 393 394 for (j = 0; str == NULL && j < dir.dir_len; j++) { 395 d = &dir.dir_tab[j]; 396 for (k = 0; k < sizeof(name); k++) 397 name[k] = (char)cdf_tole2(d->d_name[k]); 398 str = cdf_app_to_mime(name, 399 NOTMIME(ms) ? name2desc : name2mime); 400 } 401 if (NOTMIME(ms)) { 402 if (str != NULL) { 403 if (file_printf(ms, "%s", str) == -1) 404 return -1; 405 i = 1; 406 } 407 } else { 408 if (str == NULL) 409 str = "vnd.ms-office"; 410 if (file_printf(ms, "application/%s", str) == -1) 411 return -1; 412 i = 1; 413 } 414 } 415 free(scn.sst_tab); 416 out4: 417 free(sst.sst_tab); 418 out3: 419 free(dir.dir_tab); 420 out2: 421 free(ssat.sat_tab); 422 out1: 423 free(sat.sat_tab); 424 out0: 425 if (i == -1) { 426 if (NOTMIME(ms)) { 427 if (file_printf(ms, 428 "Composite Document File V2 Document") == -1) 429 return -1; 430 if (*expn) 431 if (file_printf(ms, ", %s%s", corrupt, expn) == -1) 432 return -1; 433 } else { 434 if (file_printf(ms, "application/CDFV2-corrupt") == -1) 435 return -1; 436 } 437 i = 1; 438 } 439 return i; 440 } 441