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