diff --git a/ChangeLog b/ChangeLog index b953c43..bf05a4d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ bzr branch lp:libisofs/for-libisoburn (to become libisofs-1.4.6.tar.gz) =============================================================================== -- no novelties yet - +* New API calls iso_generate_gpt_guid() and +iso_write_opts_set_gpt_guid(). libisofs-1.4.4.tar.gz Fri Jul 01 2016 =============================================================================== diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c index 9d13ffc..137bd75 100644 --- a/libisofs/ecma119.c +++ b/libisofs/ecma119.c @@ -2362,6 +2362,31 @@ void ecma119_determine_now_time(Ecma119Image *target) target->now = now; } +static +int gpt_disk_guid_setup(Ecma119Image *target) +{ + if (target->opts->gpt_disk_guid_mode == 0) { + /* Random UUID production delayed until really needed */ + return ISO_SUCCESS; + } else if (target->opts->gpt_disk_guid_mode == 1) { + memcpy(target->gpt_uuid_base, target->opts->gpt_disk_guid, 16); + } else if (target->opts->gpt_disk_guid_mode == 2) { + if (target->opts->vol_uuid[0] == 0) + return ISO_GPT_NO_VOL_UUID; + /* Move centi-seconds part to byte 9 and 10 */ + memcpy(target->gpt_uuid_base, target->opts->vol_uuid, 9); + memcpy(target->gpt_uuid_base + 9, target->opts->vol_uuid + 14, 2); + memcpy(target->gpt_uuid_base + 11, target->opts->vol_uuid + 9, 5); + iso_mark_guid_version_4(target->gpt_uuid_base); + } else { + return ISO_BAD_GPT_GUID_MODE; + } + memcpy(target->gpt_disk_guid, target->gpt_uuid_base, 16); + target->gpt_disk_guid_set = 1; + target->gpt_uuid_counter = 1; + return ISO_SUCCESS; +} + static int ecma119_image_new(IsoImage *src, IsoWriteOpts *in_opts, Ecma119Image **img) { @@ -2597,7 +2622,12 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *in_opts, Ecma119Image **img) target->gpt_req_count = 0; target->gpt_req_flags = 0; target->gpt_backup_outside = 0; + memset(target->gpt_uuid_base, 0, 16); + target->gpt_uuid_counter = 0; target->gpt_disk_guid_set = 0; + ret = gpt_disk_guid_setup(target); + if (ret < 0) + goto target_cleanup; target->gpt_part_start = 0; target->gpt_backup_end = 0; target->gpt_backup_size = 0; @@ -3430,7 +3460,7 @@ int iso_write_opts_new(IsoWriteOpts **opts, int profile) wopts->vol_modification_time = 0; wopts->vol_expiration_time = 0; wopts->vol_effective_time = 0; - wopts->vol_uuid[0] = 0; + memset(wopts->vol_uuid, 0, 17); wopts->partition_offset = 0; wopts->partition_secs_per_head = 0; wopts->partition_heads_per_cyl = 0; @@ -3461,6 +3491,8 @@ int iso_write_opts_new(IsoWriteOpts **opts, int profile) wopts->hfsp_serial_number[i] = 0; wopts->apm_block_size = 0; wopts->hfsp_block_size = 0; + memset(wopts->gpt_disk_guid, 0, 16); + wopts->gpt_disk_guid_mode = 0; *opts = wopts; return ISO_SUCCESS; @@ -4212,6 +4244,16 @@ int iso_write_opts_set_hfsp_block_size(IsoWriteOpts *opts, return ISO_SUCCESS; } +int iso_write_opts_set_gpt_guid(IsoWriteOpts *opts, uint8_t guid[16], int mode) +{ + if (mode < 0 || mode > 2) + return ISO_BAD_GPT_GUID_MODE; + opts->gpt_disk_guid_mode = mode; + if (opts->gpt_disk_guid_mode == 1) + memcpy(opts->gpt_disk_guid, guid, 16); + return ISO_SUCCESS; +} + /* * @param flag diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h index 601dff9..9792dca 100644 --- a/libisofs/ecma119.h +++ b/libisofs/ecma119.h @@ -513,6 +513,13 @@ struct iso_write_opts { */ int apm_block_size; + /* User defined GUID for GPT header and base of reproducible partition + GUIDs. (Not to be confused with volume "UUID", which is actually a + timestamp.) + See API call iso_write_opts_set_gpt_guid(). + */ + uint8_t gpt_disk_guid[16]; + int gpt_disk_guid_mode; }; typedef struct ecma119_image Ecma119Image; @@ -844,6 +851,11 @@ struct ecma119_image /* Whether the eventual backup GPT is not part of the ISO filesystem */ int gpt_backup_outside; + /* The base UUID for the generated GPT UUIDs */ + uint8_t gpt_uuid_base[16]; + /* The counter which distinguishes the GPT UUIDs */ + uint32_t gpt_uuid_counter; + uint32_t efi_boot_part_size; IsoFileSrc *efi_boot_part_filesrc; /* Just a pointer. Do not free. */ diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 2e051ad..6fdd05e 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -2595,6 +2595,49 @@ int iso_write_opts_set_prep_img(IsoWriteOpts *opts, char *image_path, int iso_write_opts_set_efi_bootp(IsoWriteOpts *opts, char *image_path, int flag); +/** + * Control whether the emerging GPT gets a pseudo-randomly generated disk GUID + * or * whether it gets a user supplied GUID. + * The partition GUIDs will be generated in a reproducible way by exoring a + * little-endian 32 bit counter with the disk GUID beginning at byte offset 9. + * + * @param opts + * The option set to be manipulated. + * @param guid + * 16 bytes of user supplied GUID. + * The upper 4 bit of guid[6] and guid[7] should bear the value 4 to + * express the version 4 in both endiannesses. Bit 7 of byte[8] should + * be set to 1 and bit 6 be set to 0, in order to express the RFC 4122 + * variant of GUID, where version 4 means "random". + * @param mode + * 0 = ignore parameter guid and produce the GPT disk GUID by a + * pseudo-random algorithm. This is the default setting. + * 1 = use parameter guid as GPT disk GUID + * 2 = ignore parameter guid and derive the GPT disk GUID from + * parameter vol_uuid of iso_write_opts_set_pvd_times(). + * The 16 bytes of vol_uuid get copied and bytes 6, 7, 8 get their + * upper bits changed to comply to RFC 4122. + * Error ISO_GPT_NO_VOL_UUID will occur if image production begins + * before vol_uuid was set. + * + * @return + * ISO_SUCCESS or ISO_BAD_GPT_GUID_MODE + * + * @since 1.4.6 + */ +int iso_write_opts_set_gpt_guid(IsoWriteOpts *opts, uint8_t guid[16], + int mode); + +/** + * Generate a pseudo-random GUID suitable for iso_write_opts_set_gpt_guid(). + * + * @param guid + * Will be filled by 16 bytes of generated GUID. + * + * @since 1.4.6 + */ +void iso_generate_gpt_guid(uint8_t guid[16]); + /** * Cause an arbitrary data file to be appended to the ISO image and to be * described by a partition table entry in an MBR or SUN Disk Label at the @@ -8787,6 +8830,15 @@ int iso_conv_name_chars(IsoWriteOpts *opts, char *name, size_t name_len, /** Unrecognized file type of IsoFileSrc object (SORRY, HIGH, -415) */ #define ISO_BAD_FSRC_FILETYPE 0xE030FE61 +/** Cannot derive GPT GUID from undefined pseudo-UUID volume timestamp + (FAILURE, HIGH, -416) */ +#define ISO_GPT_NO_VOL_UUID 0xE830FE60 + +/** Unrecognized GPT disk GUID setup mode + (FAILURE, HIGH, -417) */ +#define ISO_BAD_GPT_GUID_MODE 0xE830FE5F + + /* Internal developer note: Place new error codes directly above this comment. Newly introduced errors must get a message entry in diff --git a/libisofs/libisofs.ver b/libisofs/libisofs.ver index 436ee47..ac93120 100644 --- a/libisofs/libisofs.ver +++ b/libisofs/libisofs.ver @@ -68,6 +68,7 @@ iso_filesystem_ref; iso_filesystem_unref; iso_finish; iso_fs_global_id; +iso_generate_gpt_guid; iso_get_local_charset; iso_get_messenger; iso_gzip_get_refcounts; @@ -320,6 +321,7 @@ iso_write_opts_set_disc_label; iso_write_opts_set_efi_bootp; iso_write_opts_set_fat; iso_write_opts_set_fifo_size; +iso_write_opts_set_gpt_guid; iso_write_opts_set_hardlinks; iso_write_opts_set_hfsp_block_size; iso_write_opts_set_hfsp_serial_number; diff --git a/libisofs/messages.c b/libisofs/messages.c index b344281..6aedba6 100644 --- a/libisofs/messages.c +++ b/libisofs/messages.c @@ -543,6 +543,10 @@ const char *iso_error_to_msg(int errcode) return "A general note message was issued"; case ISO_BAD_FSRC_FILETYPE: return "Unrecognized file type of IsoFileSrc object"; + case ISO_GPT_NO_VOL_UUID: + return "Cannot derive GPT GUID from undefined pseudo-UUID volume timestamp"; + case ISO_BAD_GPT_GUID_MODE: + return "Unrecognized GPT disk GUID setup mode"; default: return "Unknown error"; } diff --git a/libisofs/system_area.c b/libisofs/system_area.c index e2b4bbf..8db8cd4 100644 --- a/libisofs/system_area.c +++ b/libisofs/system_area.c @@ -1476,8 +1476,12 @@ static void iso_write_gpt_entry(Ecma119Image *t, uint8_t *buf, for (i = 0; i < 16; i++) if (part_uuid[i]) break; - if (i == 16) - iso_random_uuid(t, part_uuid); + if (i == 16) { + if (!t->gpt_disk_guid_set) + iso_gpt_uuid(t, t->gpt_disk_guid); + t->gpt_disk_guid_set = 1; + iso_gpt_uuid(t, part_uuid); + } memcpy(wpt, part_uuid, 16); wpt += 16; iso_lsb_to_buf(&wpt, start_lba & 0xffffffff, 4, 0); @@ -1538,9 +1542,8 @@ int iso_write_gpt_header_block(Ecma119Image *t, uint32_t img_blocks, (uint32_t) ((back_lba - max_entries / 4 - 1) >> 32), 4, 1); /* Disk GUID */ - /* >>> Make adjustable */ if (!t->gpt_disk_guid_set) - iso_random_uuid(t, t->gpt_disk_guid); + iso_gpt_uuid(t, t->gpt_disk_guid); t->gpt_disk_guid_set = 1; memcpy(wpt, t->gpt_disk_guid, 16); wpt += 16; @@ -2357,67 +2360,70 @@ uint32_t iso_crc32_gpt(unsigned char *data, int count, int flag) return result ^ 0xffffffff; } - -void iso_random_uuid(Ecma119Image *t, uint8_t uuid[16]) +void iso_mark_guid_version_4(uint8_t *u) { + /* Mark as UUID version 4. RFC 4122 says u[6], but isohybrid swapping + effectively puts the 4 into u[7]. So i mark both. 4 bits wasted. + */ + u[6] = (u[6] & 0x0f) | 0x40; + u[7] = (u[7] & 0x0f) | 0x40; + + /* Variant is "1 0 x" as described in RFC 4122. + */ + u[8] = (u[8] & 0x3f) | 0x80; + + return; +} + +void iso_generate_gpt_guid(uint8_t guid[16]) +{ + #ifdef Libisofs_with_uuid_generatE + uuid_t u; + + uuid_generate(u); + swap_uuid((void *) u); + memcpy(guid, u, 16); + #else - uint8_t u[16]; + + uint8_t *u; /* produced by uuid_generate() and byte-swapped to isohybrid.c habits */ static uint8_t uuid_template[16] = { 0xee, 0x29, 0x9d, 0xfc, 0x65, 0xcc, 0x7c, 0x40, 0x92, 0x61, 0x5b, 0xcd, 0x6f, 0xed, 0x08, 0x34 }; - static uint8_t uuid_urandom[16]; uint32_t rnd, salt; struct timeval tv; pid_t pid; - static int counter = 0, use_urandom = 0; int i, ret, fd; -#endif -#ifdef Libisofs_with_uuid_generatE + u = guid; - uuid_generate(u); - swap_uuid((void *) u); - memcpy(uuid, u, 16); - -#else - - /* First try /dev/urandom. - (Weakening the result by 8 bit saves a lot of pool entropy.) + /* First try /dev/urandom */ - if ((counter & 0xff) == 0) { - fd = open("/dev/urandom", O_RDONLY | O_BINARY); - if (fd == -1) - goto fallback; - ret = read(fd, uuid_urandom, 16); - if (ret != 16) { - close(fd); - goto fallback; - } - /* Mark as UUID version 4 */ - uuid_urandom[7] = (uuid_urandom[7] & 0x0f) | 0x40; - uuid_urandom[8] = (uuid_urandom[8] & 0x3f) | 0x80; - close(fd); - use_urandom = 1; - } - if (!use_urandom) + fd = open("/dev/urandom", O_RDONLY | O_BINARY); + if (fd == -1) goto fallback; - memcpy(uuid, uuid_urandom, 16); - uuid[9] ^= counter & 0xff; - counter++; + ret = read(fd, u, 16); + if (ret != 16) { + close(fd); + goto fallback; + } + close(fd); + iso_mark_guid_version_4(u); return; + fallback:; pid = getpid(); - salt = iso_crc32_gpt((unsigned char *) t, sizeof(Ecma119Image), 0) ^ pid; + salt = iso_crc32_gpt((unsigned char *) &guid, sizeof(uint8_t *), 0) ^ pid; /* This relies on the uniqueness of the template and the rareness of - bootable ISO image production via libisofs. Estimated 53 bits of + bootable ISO image production via libisofs. Estimated 48 bits of entropy should influence the production of a single day. - So first collisions are to be expected with about 100 million images + So first collisions are to be expected with about 16 million images per day. */ memcpy(u, uuid_template, 16); @@ -2429,19 +2435,36 @@ fallback:; u[6] = ((salt >> 8) ^ (pid >> 16)) & 0xff; rnd = ((0xffffff & tv.tv_sec) << 8) | (((tv.tv_usec >> 16) ^ (salt & 0xf0)) & 0xff); - u[9] ^= counter & 0xff; for (i = 0; i < 4; i++) u[10 + i] ^= (rnd >> (8 * i)) & 0xff; u[14] ^= (tv.tv_usec >> 8) & 0xff; u[15] ^= tv.tv_usec & 0xff; - counter++; - memcpy(uuid, u, 16); + iso_mark_guid_version_4(u); + return; #endif /* ! Libisofs_with_uuid_generatE */ } +void iso_gpt_uuid(Ecma119Image *t, uint8_t uuid[16]) +{ + if (t->gpt_uuid_counter == 0) + iso_generate_gpt_guid(t->gpt_uuid_base); + + memcpy(uuid, t->gpt_uuid_base, 16); + + /* Previous implementation changed only byte 9. So i expand it by applying + the counter in little-endian style. + */ + uuid[9] ^= t->gpt_uuid_counter & 0xff; + uuid[10] ^= (t->gpt_uuid_counter >> 8) & 0xff; + uuid[11] ^= (t->gpt_uuid_counter >> 16) & 0xff; + uuid[12] ^= (t->gpt_uuid_counter >> 24) & 0xff; + t->gpt_uuid_counter++; + return; +} + int assess_appended_gpt(Ecma119Image *t, int flag) { static uint8_t basic_data_uuid[16] = { diff --git a/libisofs/system_area.h b/libisofs/system_area.h index fa89b67..b144b03 100644 --- a/libisofs/system_area.h +++ b/libisofs/system_area.h @@ -182,10 +182,15 @@ int iso_quick_apm_entry(struct iso_apm_partition_request **req_array, run on other machines with the same process number at the same time. */ -/* Produces a weakly random variation of a hardcoded real random uuid +/* Produces a GPT disk or partition GUID. + Pseudo-random by iso_generate_gpt_guid() if t->gpt_uuid_counter is 0. + Else derived reproducibly by counter number from t->gpt_uuid_base. */ -void iso_random_uuid(Ecma119Image *t, uint8_t uuid[16]); +void iso_gpt_uuid(Ecma119Image *t, uint8_t uuid[16]); +/* Mark a given byte string as UUID version 4, RFC 4122. +*/ +void iso_mark_guid_version_4(uint8_t *u); /* The parameter struct for production of a single GPT entry. See also the partial GPT description in doc/boot_sectors.txt.