.. | .. |
---|
67 | 67 | efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; |
---|
68 | 68 | efi_guid_t rng_algo_raw = EFI_RNG_ALGORITHM_RAW; |
---|
69 | 69 | efi_guid_t rng_table_guid = LINUX_EFI_RANDOM_SEED_TABLE_GUID; |
---|
| 70 | + struct linux_efi_random_seed *prev_seed, *seed = NULL; |
---|
| 71 | + int prev_seed_size = 0, seed_size = EFI_RANDOM_SEED_SIZE; |
---|
70 | 72 | efi_rng_protocol_t *rng = NULL; |
---|
71 | | - struct linux_efi_random_seed *seed = NULL; |
---|
72 | 73 | efi_status_t status; |
---|
73 | 74 | |
---|
74 | 75 | status = efi_bs_call(locate_protocol, &rng_proto, NULL, (void **)&rng); |
---|
.. | .. |
---|
76 | 77 | return status; |
---|
77 | 78 | |
---|
78 | 79 | /* |
---|
| 80 | + * Check whether a seed was provided by a prior boot stage. In that |
---|
| 81 | + * case, instead of overwriting it, let's create a new buffer that can |
---|
| 82 | + * hold both, and concatenate the existing and the new seeds. |
---|
| 83 | + * Note that we should read the seed size with caution, in case the |
---|
| 84 | + * table got corrupted in memory somehow. |
---|
| 85 | + */ |
---|
| 86 | + prev_seed = get_efi_config_table(LINUX_EFI_RANDOM_SEED_TABLE_GUID); |
---|
| 87 | + if (prev_seed && prev_seed->size <= 512U) { |
---|
| 88 | + prev_seed_size = prev_seed->size; |
---|
| 89 | + seed_size += prev_seed_size; |
---|
| 90 | + } |
---|
| 91 | + |
---|
| 92 | + /* |
---|
79 | 93 | * Use EFI_ACPI_RECLAIM_MEMORY here so that it is guaranteed that the |
---|
80 | 94 | * allocation will survive a kexec reboot (although we refresh the seed |
---|
81 | 95 | * beforehand) |
---|
82 | 96 | */ |
---|
83 | 97 | status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, |
---|
84 | | - sizeof(*seed) + EFI_RANDOM_SEED_SIZE, |
---|
| 98 | + struct_size(seed, bits, seed_size), |
---|
85 | 99 | (void **)&seed); |
---|
86 | | - if (status != EFI_SUCCESS) |
---|
87 | | - return status; |
---|
| 100 | + if (status != EFI_SUCCESS) { |
---|
| 101 | + efi_warn("Failed to allocate memory for RNG seed.\n"); |
---|
| 102 | + goto err_warn; |
---|
| 103 | + } |
---|
88 | 104 | |
---|
89 | 105 | status = efi_call_proto(rng, get_rng, &rng_algo_raw, |
---|
90 | | - EFI_RANDOM_SEED_SIZE, seed->bits); |
---|
| 106 | + EFI_RANDOM_SEED_SIZE, seed->bits); |
---|
91 | 107 | |
---|
92 | 108 | if (status == EFI_UNSUPPORTED) |
---|
93 | 109 | /* |
---|
.. | .. |
---|
100 | 116 | if (status != EFI_SUCCESS) |
---|
101 | 117 | goto err_freepool; |
---|
102 | 118 | |
---|
103 | | - seed->size = EFI_RANDOM_SEED_SIZE; |
---|
| 119 | + seed->size = seed_size; |
---|
| 120 | + if (prev_seed_size) |
---|
| 121 | + memcpy(seed->bits + EFI_RANDOM_SEED_SIZE, prev_seed->bits, |
---|
| 122 | + prev_seed_size); |
---|
| 123 | + |
---|
104 | 124 | status = efi_bs_call(install_configuration_table, &rng_table_guid, seed); |
---|
105 | 125 | if (status != EFI_SUCCESS) |
---|
106 | 126 | goto err_freepool; |
---|
107 | 127 | |
---|
| 128 | + if (prev_seed_size) { |
---|
| 129 | + /* wipe and free the old seed if we managed to install the new one */ |
---|
| 130 | + memzero_explicit(prev_seed->bits, prev_seed_size); |
---|
| 131 | + efi_bs_call(free_pool, prev_seed); |
---|
| 132 | + } |
---|
108 | 133 | return EFI_SUCCESS; |
---|
109 | 134 | |
---|
110 | 135 | err_freepool: |
---|
| 136 | + memzero_explicit(seed, struct_size(seed, bits, seed_size)); |
---|
111 | 137 | efi_bs_call(free_pool, seed); |
---|
| 138 | + efi_warn("Failed to obtain seed from EFI_RNG_PROTOCOL\n"); |
---|
| 139 | +err_warn: |
---|
| 140 | + if (prev_seed) |
---|
| 141 | + efi_warn("Retaining bootloader-supplied seed only"); |
---|
112 | 142 | return status; |
---|
113 | 143 | } |
---|