New API calls iso_generate_gpt_guid() and +iso_write_opts_set_gpt_guid().

This commit is contained in:
Thomas Schmitt 2016-08-12 18:18:47 +02:00
parent 2f134dcdcb
commit 866f647fad
8 changed files with 188 additions and 48 deletions

View File

@ -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
===============================================================================

View File

@ -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

View File

@ -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. */

View File

@ -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

View File

@ -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;

View File

@ -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";
}

View File

@ -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] = {

View File

@ -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.