iso9660_extractor.c (18300B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2026 Vidyut Samanta and Christian Grothoff 4 5 libextractor is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libextractor is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libextractor; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file plugins/iso9660_extractor.c 22 * @brief plugin to support iso9660 files 23 * @author Christian Grothoff 24 * 25 * Reads the volume descriptor set of an ECMA-119 (ISO 9660) image. The 26 * descriptors carry the identifiers of the machine, the person and the 27 * mastering program that produced the disc, which is the provenance a 28 * first pass is after; the file tree is deliberately not walked. 29 * 30 * References: ECMA-119 (ISO 9660), the "El Torito" bootable CD-ROM 31 * specification, the Joliet specification and IEEE P1282 (Rock Ridge). 32 */ 33 #include "platform.h" 34 #include "extractor.h" 35 #include "forensics.h" 36 37 /** 38 * Size of a logical sector in the volume descriptor set. ECMA-119 39 * fixes this at 2048 regardless of the logical block size the volume 40 * announces for its files. 41 */ 42 #define ISO_SECTOR 2048 43 44 /** 45 * Byte offset of the first volume descriptor: sector 16, after the 46 * 32 KiB system area reserved for boot code. 47 */ 48 #define ISO_VD_OFFSET (16 * ISO_SECTOR) 49 50 /** 51 * How many volume descriptors we are prepared to walk before giving 52 * up on finding the terminator. 53 */ 54 #define ISO_MAX_DESCRIPTORS 32 55 56 /** 57 * Bytes of each descriptor we look at while walking: enough for the 58 * type, the standard identifier, the boot system identifier of a boot 59 * record and the escape sequences of a supplementary descriptor. 60 */ 61 #define ISO_PROBE 96 62 63 64 /** 65 * Days from 1970-01-01 to @a y - @a m - @a d, proleptic Gregorian. 66 * 67 * Written out rather than using timegm(), which is not available 68 * everywhere libextractor builds. 69 * 70 * @param y the year 71 * @param m the month, 1 to 12 72 * @param d the day of month, 1 to 31 73 * @return days since the Unix epoch, negative before it 74 */ 75 static int64_t 76 days_from_civil (int64_t y, 77 unsigned int m, 78 unsigned int d) 79 { 80 int64_t era; 81 unsigned int yoe; 82 unsigned int doy; 83 unsigned int doe; 84 85 y -= (m <= 2) ? 1 : 0; 86 era = ((y >= 0) ? y : (y - 399)) / 400; 87 yoe = (unsigned int) (y - era * 400); 88 doy = (153 * (m + ((m > 2) ? -3 : 9)) + 2) / 5 + d - 1; 89 doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; 90 return era * 146097 + (int64_t) doe - 719468; 91 } 92 93 94 /** 95 * Parse a 17-byte ECMA-119 `dec-datetime' field. 96 * 97 * The field is "YYYYMMDDHHMMSSss" in ASCII digits followed by one 98 * signed byte holding the offset from GMT in 15-minute units. A field 99 * that is all ASCII zeroes means "not specified". 100 * 101 * @param f the 17 bytes 102 * @param[out] when where to store the time in seconds since the epoch 103 * @return 1 if @a f held a usable date 104 */ 105 static int 106 parse_dec_datetime (const unsigned char *f, 107 int64_t *when) 108 { 109 unsigned int v[7]; 110 static const unsigned int width[7] = { 4, 2, 2, 2, 2, 2, 2 }; 111 size_t pos = 0; 112 int all_zero = 1; 113 114 for (unsigned int i = 0; i < 16; i++) 115 { 116 if ( ('0' > f[i]) || 117 ('9' < f[i]) ) 118 return 0; /* not a filled-in date field */ 119 if ('0' != f[i]) 120 all_zero = 0; 121 } 122 if (all_zero) 123 return 0; /* the documented spelling of "unset" */ 124 for (unsigned int i = 0; i < 7; i++) 125 { 126 v[i] = 0; 127 for (unsigned int k = 0; k < width[i]; k++) 128 v[i] = v[i] * 10 + (unsigned int) (f[pos++] - '0'); 129 } 130 if ( (v[0] < 1900) || (v[0] > 2200) || 131 (v[1] < 1) || (v[1] > 12) || 132 (v[2] < 1) || (v[2] > 31) || 133 (v[3] > 23) || (v[4] > 59) || (v[5] > 60) ) 134 return 0; 135 *when = days_from_civil (v[0], 136 v[1], 137 v[2]) * 86400LL 138 + v[3] * 3600LL + v[4] * 60LL + v[5] 139 /* the offset is what has to be *subtracted* to reach GMT */ 140 - ((int64_t) (signed char) f[16]) * 15LL * 60LL; 141 return 1; 142 } 143 144 145 /** 146 * Is @a bs a logical block size an ECMA-119 volume may announce? 147 * 148 * @param bs the raw field 149 * @return 1 if @a bs is usable for arithmetic 150 */ 151 static int 152 block_size_ok (uint16_t bs) 153 { 154 return ( (bs >= 512) && 155 (bs <= 32768) && 156 (0 == (bs & (bs - 1))) ); 157 } 158 159 160 /** 161 * Emit a fixed-width text field of the primary volume descriptor. 162 * 163 * @param ec extraction context 164 * @param type meta data type 165 * @param pvd the primary volume descriptor 166 * @param off offset of the field within @a pvd 167 * @param len width of the field 168 * @return 1 if the caller should stop extracting, 0 to continue 169 */ 170 static int 171 emit_field (struct EXTRACTOR_ExtractContext *ec, 172 enum EXTRACTOR_MetaType type, 173 const unsigned char *pvd, 174 size_t off, 175 size_t len) 176 { 177 return EXTRACTOR_forensic_emit_text_ (ec, 178 "iso9660", 179 type, 180 (const char *) &pvd[off], 181 len); 182 } 183 184 185 /** 186 * Parse and emit one of the descriptor's `dec-datetime' fields. 187 * 188 * Unset fields (all ASCII zeroes) and implausible ones are dropped 189 * without a word, which is what a bulk pass wants. 190 * 191 * @param ec extraction context 192 * @param type meta data type 193 * @param field the 17-byte field 194 * @return 1 if the caller should stop extracting, 0 to continue 195 */ 196 static int 197 emit_date (struct EXTRACTOR_ExtractContext *ec, 198 enum EXTRACTOR_MetaType type, 199 const unsigned char *field) 200 { 201 int64_t when; 202 203 if (! parse_dec_datetime (field, 204 &when)) 205 return 0; 206 return EXTRACTOR_forensic_emit_unix_time_ (ec, 207 "iso9660", 208 type, 209 when); 210 } 211 212 213 /** 214 * Look at the root directory record's system use area for the SUSP and 215 * Rock Ridge signatures. 216 * 217 * The `SP' entry that announces SUSP lives in the "." record of the 218 * root directory, not in the copy of the root record embedded in the 219 * descriptor, so this costs one extra sector read. We do it because 220 * the presence of Rock Ridge changes what a disc actually contains 221 * (real POSIX names, permissions and symlinks) and one bounded read is 222 * cheap. 223 * 224 * @param ec extraction context 225 * @param pvd the primary volume descriptor 226 * @param block_size the volume's logical block size 227 * @param file_size size of the image, UINT64_MAX if unknown 228 * @return 1 if Rock Ridge extensions are present 229 */ 230 static int 231 has_rock_ridge (struct EXTRACTOR_ExtractContext *ec, 232 const unsigned char *pvd, 233 uint32_t block_size, 234 uint64_t file_size) 235 { 236 unsigned char sector[ISO_SECTOR]; 237 uint32_t extent; 238 size_t want; 239 uint64_t at; 240 unsigned int rec_len; 241 unsigned int fi_len; 242 unsigned int pos; 243 244 /* the root directory record sits at offset 156 of the descriptor; 245 its extent is a both-endian 32-bit value at offset 2 */ 246 extent = EXTRACTOR_forensic_le32_ (&pvd[156 + 2]); 247 if (0 == extent) 248 return 0; 249 at = ((uint64_t) extent) * block_size; 250 want = (block_size > ISO_SECTOR) ? ISO_SECTOR : (size_t) block_size; 251 if ( (at > INT64_MAX - want) || 252 ( (UINT64_MAX != file_size) && 253 (at + want > file_size) ) ) 254 return 0; /* the root directory is not inside this file */ 255 if (! EXTRACTOR_forensic_read_ (ec, 256 (int64_t) at, 257 sector, 258 want)) 259 return 0; 260 rec_len = sector[0]; 261 if ( (rec_len < 34) || 262 (rec_len > want) ) 263 return 0; 264 fi_len = sector[32]; 265 pos = 33 + fi_len; 266 if (0 != (pos & 1)) 267 pos++; /* the identifier is padded to an even length */ 268 while (pos + 4 <= rec_len) 269 { 270 unsigned int elen = sector[pos + 2]; 271 272 if (elen < 4) 273 return 0; /* zero-length entry: refuse to spin */ 274 if (pos + elen > rec_len) 275 return 0; 276 if ( ('R' == sector[pos]) && 277 ('R' == sector[pos + 1]) ) 278 return 1; /* the Rock Ridge marker itself */ 279 if ( ('S' == sector[pos]) && 280 ('P' == sector[pos + 1]) && 281 (7 == elen) && 282 (0xBE == sector[pos + 5]) && 283 (0xEF == sector[pos + 6]) ) 284 return 1; /* SUSP, which on a data CD means Rock Ridge */ 285 pos += elen; 286 } 287 return 0; 288 } 289 290 291 /** 292 * Main entry method for the iso9660 extraction plugin. 293 * 294 * @param ec extraction context provided to the plugin 295 */ 296 void 297 EXTRACTOR_iso9660_extract_method (struct EXTRACTOR_ExtractContext *ec); 298 299 void 300 EXTRACTOR_iso9660_extract_method (struct EXTRACTOR_ExtractContext *ec) 301 { 302 unsigned char probe[ISO_PROBE]; 303 unsigned char pvd[ISO_SECTOR]; 304 uint64_t file_size; 305 uint64_t pvd_at = 0; 306 uint32_t block_size; 307 uint32_t space_size; 308 uint16_t set_size; 309 int64_t when; 310 int have_pvd = 0; 311 int joliet = 0; 312 int el_torito = 0; 313 314 file_size = ec->get_size (ec->cls); 315 if ( (UINT64_MAX != file_size) && 316 (file_size < ISO_VD_OFFSET + ISO_SECTOR) ) 317 return; /* too short to hold a volume descriptor */ 318 if (! EXTRACTOR_forensic_read_ (ec, 319 ISO_VD_OFFSET, 320 probe, 321 sizeof (probe))) 322 return; 323 if (0 != memcmp (&probe[1], 324 "CD001", 325 5)) 326 return; /* not an ISO 9660 image */ 327 if (0 != 328 EXTRACTOR_forensic_emit_ (ec, 329 "iso9660", 330 EXTRACTOR_METATYPE_MIMETYPE, 331 "%s", 332 "application/x-iso9660-image")) 333 return; 334 for (unsigned int i = 0; i < ISO_MAX_DESCRIPTORS; i++) 335 { 336 uint64_t at = ISO_VD_OFFSET + ((uint64_t) i) * ISO_SECTOR; 337 338 if ( (UINT64_MAX != file_size) && 339 (at + ISO_SECTOR > file_size) ) 340 break; 341 if ( (0 != i) && 342 (! EXTRACTOR_forensic_read_ (ec, 343 (int64_t) at, 344 probe, 345 sizeof (probe))) ) 346 break; 347 if (0 != memcmp (&probe[1], 348 "CD001", 349 5)) 350 break; /* the descriptor set ended without a terminator */ 351 switch (probe[0]) 352 { 353 case 0: 354 /* boot record; El Torito puts its name in the boot system 355 identifier at offset 7 */ 356 if (0 == memcmp (&probe[7], 357 "EL TORITO SPECIFICATION", 358 23)) 359 el_torito = 1; 360 break; 361 case 1: 362 if (! have_pvd) 363 { 364 have_pvd = 1; 365 pvd_at = at; 366 } 367 break; 368 case 2: 369 /* supplementary descriptor; Joliet announces itself with one of 370 three escape sequences for UCS-2 at offset 88 */ 371 if ( (0x25 == probe[88]) && 372 (0x2F == probe[89]) && 373 ( (0x40 == probe[90]) || 374 (0x43 == probe[90]) || 375 (0x45 == probe[90]) ) ) 376 joliet = 1; 377 break; 378 case 255: 379 i = ISO_MAX_DESCRIPTORS; /* terminator */ 380 break; 381 default: 382 break; 383 } 384 } 385 if (0 != 386 EXTRACTOR_forensic_emit_ (ec, 387 "iso9660", 388 EXTRACTOR_METATYPE_FILESYSTEM_TYPE, 389 "%s", 390 "ISO 9660")) 391 return; 392 if ( (joliet) && 393 (0 != EXTRACTOR_forensic_emit_ (ec, 394 "iso9660", 395 EXTRACTOR_METATYPE_FILESYSTEM_TYPE, 396 "%s", 397 "Joliet")) ) 398 return; 399 if ( (el_torito) && 400 (0 != EXTRACTOR_forensic_emit_ (ec, 401 "iso9660", 402 EXTRACTOR_METATYPE_COMMENT, 403 "%s", 404 "bootable (El Torito)")) ) 405 return; 406 if (! have_pvd) 407 return; /* a descriptor set without a primary descriptor */ 408 if (! EXTRACTOR_forensic_read_ (ec, 409 (int64_t) pvd_at, 410 pvd, 411 sizeof (pvd))) 412 return; 413 414 /* -- the identifier fields, in descriptor order -- */ 415 if (0 != 416 emit_field (ec, 417 EXTRACTOR_METATYPE_SYSTEM_IDENTIFIER, 418 pvd, 419 8, 420 32)) 421 return; 422 if (0 != 423 emit_field (ec, 424 EXTRACTOR_METATYPE_VOLUME_NAME, 425 pvd, 426 40, 427 32)) 428 return; 429 if (0 != 430 emit_field (ec, 431 EXTRACTOR_METATYPE_PUBLISHER, 432 pvd, 433 318, 434 128)) 435 return; 436 if (0 != 437 emit_field (ec, 438 EXTRACTOR_METATYPE_DATA_PREPARER, 439 pvd, 440 446, 441 128)) 442 return; 443 /* the application identifier names the mastering software -- mkisofs, 444 Nero, ImgBurn -- which is the best provenance the descriptor has */ 445 if (0 != 446 emit_field (ec, 447 EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE, 448 pvd, 449 574, 450 128)) 451 return; 452 if (0 != 453 emit_field (ec, 454 EXTRACTOR_METATYPE_COPYRIGHT, 455 pvd, 456 702, 457 37)) 458 return; 459 if (0 != 460 emit_field (ec, 461 EXTRACTOR_METATYPE_ABSTRACT, 462 pvd, 463 739, 464 37)) 465 return; 466 /* The bibliographic file identifier has no meta type of its own; it 467 names a file on the disc, so it only means something with a label 468 attached. */ 469 { 470 size_t blen = EXTRACTOR_forensic_trim_ ((const char *) &pvd[776], 471 37); 472 int printable = (0 != blen); 473 474 for (size_t i = 0; i < blen; i++) 475 if ( (pvd[776 + i] < 0x20) || 476 (pvd[776 + i] > 0x7e) ) 477 printable = 0; 478 if ( (printable) && 479 (0 != EXTRACTOR_forensic_emit_ (ec, 480 "iso9660", 481 EXTRACTOR_METATYPE_COMMENT, 482 "bibliographic file: %.*s", 483 (int) blen, 484 (const char *) &pvd[776])) ) 485 return; 486 } 487 488 /* -- geometry -- */ 489 block_size = EXTRACTOR_forensic_le16_ (&pvd[128]); 490 if (block_size_ok ((uint16_t) block_size)) 491 { 492 if (0 != 493 EXTRACTOR_forensic_emit_ (ec, 494 "iso9660", 495 EXTRACTOR_METATYPE_BLOCK_SIZE, 496 "%u", 497 (unsigned int) block_size)) 498 return; 499 /* both factors are bounded (2^32 sectors of at most 32 KiB), so 500 the product cannot overflow 64 bits */ 501 space_size = EXTRACTOR_forensic_le32_ (&pvd[80]); 502 if ( (0 != space_size) && 503 (0 != EXTRACTOR_forensic_emit_size_ (ec, 504 "iso9660", 505 EXTRACTOR_METATYPE_VOLUME_SIZE, 506 ((uint64_t) space_size) 507 * block_size)) ) 508 return; 509 } 510 else 511 { 512 block_size = 0; 513 } 514 /* how many volumes the set has; only worth saying when it is a set */ 515 set_size = EXTRACTOR_forensic_le16_ (&pvd[120]); 516 if ( (set_size > 1) && 517 (0 != EXTRACTOR_forensic_emit_ (ec, 518 "iso9660", 519 EXTRACTOR_METATYPE_ENTRY_COUNT, 520 "%u", 521 (unsigned int) set_size)) ) 522 return; 523 524 /* -- the four dec-datetime fields -- */ 525 if (0 != 526 emit_date (ec, 527 EXTRACTOR_METATYPE_CREATION_DATE, 528 &pvd[813])) 529 return; 530 if (0 != 531 emit_date (ec, 532 EXTRACTOR_METATYPE_MODIFICATION_DATE, 533 &pvd[830])) 534 return; 535 if (0 != 536 emit_date (ec, 537 EXTRACTOR_METATYPE_EXPIRATION_DATE, 538 &pvd[847])) 539 return; 540 /* The effective date -- the date before which the volume should not 541 be used -- has no meta type of its own, so it goes out as a 542 comment rather than being silently dropped. */ 543 if (parse_dec_datetime (&pvd[864], 544 &when)) 545 { 546 struct tm tm; 547 time_t t = (time_t) when; 548 char buf[32]; 549 550 if ( (when > 315532800LL) && 551 (when < 4102444800LL) && 552 (NULL != gmtime_r (&t, 553 &tm)) && 554 (0 != strftime (buf, 555 sizeof (buf), 556 "%Y-%m-%dT%H:%M:%SZ", 557 &tm)) && 558 (0 != EXTRACTOR_forensic_emit_ (ec, 559 "iso9660", 560 EXTRACTOR_METATYPE_COMMENT, 561 "volume effective from %s", 562 buf)) ) 563 return; 564 } 565 566 /* -- Rock Ridge, which needs one more sector -- */ 567 if ( (0 != block_size) && 568 (has_rock_ridge (ec, 569 pvd, 570 block_size, 571 file_size)) && 572 (0 != EXTRACTOR_forensic_emit_ (ec, 573 "iso9660", 574 EXTRACTOR_METATYPE_FILESYSTEM_TYPE, 575 "%s", 576 "Rock Ridge")) ) 577 return; 578 } 579 580 581 /* end of iso9660_extractor.c */