]> git.proxmox.com Git - swtpm.git/blob - src/swtpm/swtpm_nvstore_linear.c
swtpm: Fix a forgotten endianess-conversion
[swtpm.git] / src / swtpm / swtpm_nvstore_linear.c
1 #include "config.h"
2
3 #define _GNU_SOURCE
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include <libtpms/tpm_error.h>
8 #include <libtpms/tpm_nvfilename.h>
9
10 #include "compiler_dependencies.h"
11 #include "sys_dependencies.h"
12 #include "swtpm.h"
13 #include "swtpm_debug.h"
14 #include "swtpm_nvstore.h"
15 #include "swtpm_nvstore_linear.h"
16 #include "logging.h"
17 #include "utils.h"
18
19 static struct {
20 TPM_BOOL initialized;
21 char *loaded_uri;
22 struct nvram_linear_store_ops *ops;
23
24 unsigned char *data;
25 uint32_t length;
26 struct nvram_linear_hdr *hdr; /* points into *data */
27 } state;
28
29 /*
30 Attempts to flush the header of the linear state, if required by the store.
31 */
32 static TPM_RESULT
33 SWTPM_NVRAM_Linear_FlushHeader(const char* uri)
34 {
35 if (state.ops->flush) {
36 return state.ops->flush(uri, 0, le16toh(state.hdr->hdrsize));
37 }
38 return 0;
39 }
40
41 /*
42 Attempts a resize and ensures that state is updated correctly and the given
43 new_size could actually be reached.
44 */
45 static TPM_RESULT
46 SWTPM_NVRAM_Linear_SafeResize(const char* uri, uint32_t new_size)
47 {
48 TPM_RESULT rc = 0;
49 uint32_t result;
50
51 if (!state.ops->resize) {
52 return new_size < state.length ? 0 : TPM_SIZE;
53 }
54
55 rc = state.ops->resize(uri, &state.data, &result, new_size);
56 if (rc) {
57 return rc;
58 }
59
60 /* base address might have changed, update pointers */
61 state.hdr = (struct nvram_linear_hdr*)state.data;
62 state.length = result;
63
64 if (result < new_size) {
65 return TPM_SIZE;
66 }
67
68 return rc;
69 }
70
71 #define FILE_NR_INVALID 0xffffffff
72
73 /*
74 Returns the offset into the state.hdr.files array given a TPM state name and
75 number. Will be FILE_NR_INVALID if out of bounds or unknown name.
76 */
77 static uint32_t
78 SWTPM_NVRAM_Linear_GetFileNr(const char *name)
79 {
80 uint32_t rc = 0;
81 if (strcmp(name, TPM_PERMANENT_ALL_NAME) == 0) {
82 rc += 0;
83 } else if (strcmp(name, TPM_VOLATILESTATE_NAME) == 0) {
84 rc += 1;
85 } else if (strcmp(name, TPM_SAVESTATE_NAME) == 0) {
86 rc += 2;
87 } else {
88 logprintf(STDERR_FILENO,
89 "SWTPM_NVRAM_Linear_GetFileOffset: Unknown name '%s'\n",
90 name);
91 return FILE_NR_INVALID;
92 }
93 if (rc >= SWTPM_NVSTORE_LINEAR_MAX_STATES) {
94 logprintf(STDERR_FILENO,
95 "SWTPM_NVRAM_Linear_GetFileOffset: File limit exceeded: %d\n",
96 rc);
97 return FILE_NR_INVALID;
98 }
99 return rc;
100 }
101
102 /*
103 Allocate a new file entry in the linear address space of state.data.
104 The new file will be placed at the end.
105
106 Importantly, this may perform a resize, so pointers into state.data or
107 state.hdr must not be kept over this function call.
108 */
109 static TPM_RESULT
110 SWTPM_NVRAM_Linear_AllocFile(const char *uri, uint32_t file_nr, uint32_t size)
111 {
112 TPM_RESULT rc = 0;
113 struct nvram_linear_hdr_file *file;
114 uint32_t new_offset = le16toh(state.hdr->hdrsize);
115 uint32_t new_size;
116 uint32_t cur_end;
117 uint32_t i;
118 uint32_t section_size = size;
119 ROUND_TO_NEXT_POWER_OF_2_32(section_size);
120
121 /* find end of current last file */
122 for (i = 0; i < SWTPM_NVSTORE_LINEAR_MAX_STATES; i++) {
123 file = &state.hdr->files[i];
124 if (!file->offset) {
125 continue;
126 }
127
128 cur_end = le32toh(file->offset) + le32toh(file->section_length);
129 if (cur_end > new_offset) {
130 new_offset = cur_end;
131 }
132 }
133
134 new_size = new_offset + section_size;
135 rc = SWTPM_NVRAM_Linear_SafeResize(uri, new_size);
136 if (rc) {
137 return rc;
138 }
139
140 file = &state.hdr->files[file_nr];
141 file->section_length = htole32(section_size);
142 file->data_length = htole32(size);
143 file->offset = htole32(new_offset);
144
145 TPM_DEBUG("SWTPM_NVRAM_Linear_AllocFile: allocated file %d @ %d "
146 "(len=%d section=%d)\n",
147 file_nr, new_offset, size, section_size);
148
149 return rc;
150 }
151
152 /*
153 Deallocate a file from state.data. It's entry in state.hdr will be zeroed,
154 and the file length adjusted accordingly (if 'resize' is TRUE).
155 If the file was not at the end, any following files will be moved forward,
156 as to not leave any holes. This simplifies the allocator strategy, since it
157 allows new files to always be placed at the end.
158
159 If resize is true, this may perform a resize, so pointers into state.data or
160 state.hdr must not be kept over this function call.
161 */
162 static TPM_RESULT
163 SWTPM_NVRAM_Linear_RemoveFile(const char *uri,
164 uint32_t file_nr,
165 TPM_BOOL resize)
166 {
167 TPM_RESULT rc = 0;
168 uint32_t next_offset = 0xffffffff;
169 uint32_t state_end = 0;
170 uint32_t new_len;
171 uint32_t i, cur_offset, cur_end;
172 struct nvram_linear_hdr_file *file;
173 struct nvram_linear_hdr_file old_file = state.hdr->files[file_nr];
174
175 if (old_file.offset == 0) {
176 return 0;
177 }
178
179 TPM_DEBUG("SWTPM_NVRAM_Linear_RemoveFile: removing filenr %d (resize=%d)\n",
180 file_nr, resize);
181
182 state.hdr->files[file_nr].offset = 0;
183 state.hdr->files[file_nr].data_length = 0;
184 state.hdr->files[file_nr].section_length = 0;
185
186 /* find offset of file right after the one we remove, and adjust offsets */
187 for (i = 0; i < SWTPM_NVSTORE_LINEAR_MAX_STATES; i++) {
188 file = &state.hdr->files[i];
189 if (!file->offset) {
190 continue;
191 }
192
193 cur_offset = le32toh(file->offset);
194 if (cur_offset > le32toh(old_file.offset)) {
195 if (cur_offset < next_offset) {
196 next_offset = cur_offset;
197 }
198 cur_end = cur_offset + le32toh(file->section_length);
199 if (cur_end > state_end) {
200 state_end = cur_end;
201 }
202 file->offset = htole32(cur_offset -
203 le32toh(old_file.section_length));
204 }
205 }
206
207 if (next_offset != 0xffffffff) {
208 TPM_DEBUG("SWTPM_NVRAM_Linear_RemoveFile: compacting\n");
209 /* if we weren't the end, compact by moving following files forward */
210 memmove(state.data + le32toh(old_file.offset),
211 state.data + next_offset,
212 state_end - next_offset);
213 }
214
215 if (resize) {
216 new_len = state.length - le32toh(old_file.section_length);
217 rc = SWTPM_NVRAM_Linear_SafeResize(uri, new_len);
218 if (rc == 0) {
219 state.length = new_len;
220 }
221 }
222
223 return rc;
224 }
225
226 static TPM_RESULT
227 SWTPM_NVRAM_Prepare_Linear(const char *uri)
228 {
229 TPM_RESULT rc = 0;
230
231 TPM_DEBUG("SWTPM_NVRAM_Prepare_Linear: uri='%s'\n", uri);
232
233 if (state.initialized) {
234 if (strcmp(state.loaded_uri, uri) == 0) {
235 /* same URI loaded, this is okay, nothing to be done */
236 return 0;
237 }
238
239 logprintf(STDERR_FILENO,
240 "SWTPM_NVRAM_PrepareLinear: Cannot prepare twice\n");
241 return TPM_FAIL;
242 }
243
244 state.loaded_uri = malloc(strlen(uri) + 1);
245 strcpy(state.loaded_uri, uri);
246
247 /* TODO: Parse URI prefixes ("iscsi://", "rbd://", etc...) */
248 state.ops = &nvram_linear_file_ops;
249
250 if ((rc = state.ops->open(uri, &state.data, &state.length))) {
251 return rc;
252 }
253
254 state.hdr = (struct nvram_linear_hdr*)state.data;
255
256 if (le64toh(state.hdr->magic) != SWTPM_NVSTORE_LINEAR_MAGIC) {
257 logprintf(STDOUT_FILENO,
258 "Formatting '%s' as new linear NVRAM store\n",
259 uri);
260
261 state.hdr->magic = htole64(SWTPM_NVSTORE_LINEAR_MAGIC);
262 state.hdr->version = SWTPM_NVSTORE_LINEAR_VERSION;
263 state.hdr->hdrsize = htole16(sizeof(struct nvram_linear_hdr));
264 memset(&state.hdr->files, 0, sizeof(state.hdr->files));
265
266 SWTPM_NVRAM_Linear_FlushHeader(uri);
267
268 } else {
269 /* assume valid state found */
270 if (state.hdr->version > SWTPM_NVSTORE_LINEAR_VERSION) {
271 logprintf(STDERR_FILENO,
272 "SWTPM_NVRAM_PrepareLinear: Unknown format version: %d\n",
273 state.hdr->version);
274 return TPM_FAIL;
275 }
276 }
277
278 state.initialized = TRUE;
279 return rc;
280 }
281
282 static TPM_RESULT
283 SWTPM_NVRAM_LoadData_Linear(unsigned char **data,
284 uint32_t *length,
285 uint32_t tpm_number SWTPM_ATTR_UNUSED,
286 const char *name,
287 const char *uri SWTPM_ATTR_UNUSED)
288 {
289 uint32_t file_nr;
290 uint32_t file_offset;
291 uint32_t file_data_len;
292 struct nvram_linear_hdr_file *file;
293
294 TPM_DEBUG("SWTPM_NVRAM_LoadData_Linear: request for %s:%d\n",
295 name, tpm_number);
296
297 file_nr = SWTPM_NVRAM_Linear_GetFileNr(name);
298 if (file_nr == FILE_NR_INVALID) {
299 return TPM_FAIL;
300 }
301
302 file = &state.hdr->files[file_nr];
303 file_offset = le32toh(file->offset);
304 file_data_len = le32toh(file->data_length);
305
306 if (!file_offset) {
307 return TPM_RETRY;
308 }
309
310 if (file_offset + file_data_len > state.length) {
311 /* shouldn't happen, but just to be safe */
312 return TPM_FAIL;
313 }
314
315 if (data == NULL) {
316 return TPM_FAIL;
317 }
318
319 /*
320 TODO: at the moment, callers require a pointer that can be 'free'd, but
321 for efficiency, it would be better to return the mapped area directly
322 */
323 *length = file_data_len;
324 *data = malloc(file_data_len);
325 if (*data == NULL) {
326 return TPM_FAIL;
327 }
328 memcpy(*data, state.data + file_offset, file_data_len);
329
330 TPM_DEBUG("SWTPM_NVRAM_LoadData_Linear: loaded %dB from %s:%d\n",
331 file_data_len, name, tpm_number);
332
333 return 0;
334 }
335
336 static TPM_RESULT
337 SWTPM_NVRAM_StoreData_Linear(unsigned char *filedata,
338 uint32_t filedata_length,
339 uint32_t tpm_number SWTPM_ATTR_UNUSED,
340 const char *name,
341 const char *uri)
342 {
343 TPM_RESULT rc = 0;
344 TPM_BOOL needs_hdr_flush = FALSE;
345 TPM_BOOL needs_full_flush = FALSE;
346 uint32_t file_nr;
347 uint32_t file_offset;
348 struct nvram_linear_hdr_file *file;
349
350 TPM_DEBUG("SWTPM_NVRAM_StoreData_Linear: request for %dB to %s:%d\n",
351 filedata_length, name, tpm_number);
352
353 file_nr = SWTPM_NVRAM_Linear_GetFileNr(name);
354 if (file_nr == FILE_NR_INVALID) {
355 return TPM_FAIL;
356 }
357
358 file = &state.hdr->files[file_nr];
359
360 if (!file->offset) {
361 /* alloc */
362 rc = SWTPM_NVRAM_Linear_AllocFile(uri, file_nr, filedata_length);
363 if (rc) {
364 return rc;
365 }
366 needs_hdr_flush = TRUE;
367 } else if (filedata_length > le32toh(file->section_length)) {
368 /* realloc, resize will be done by AllocFile */
369 rc = SWTPM_NVRAM_Linear_RemoveFile(uri, file_nr, FALSE);
370 if (rc) {
371 return rc;
372 }
373 rc = SWTPM_NVRAM_Linear_AllocFile(uri, file_nr, filedata_length);
374 if (rc) {
375 return rc;
376 }
377 needs_full_flush = TRUE;
378 }
379
380 /* resize might have changed pointer */
381 file = &state.hdr->files[file_nr];
382 file_offset = le32toh(file->offset);
383
384 if (filedata_length != le32toh(file->data_length)) {
385 file->data_length = htole32(filedata_length);
386 needs_hdr_flush = TRUE;
387 }
388
389 memcpy(state.data + file_offset, filedata, filedata_length);
390
391 TPM_DEBUG("SWTPM_NVRAM_StoreData_Linear: stored %dB to %s:%d\n",
392 filedata_length, name, tpm_number);
393
394 if (needs_full_flush) {
395 if (state.ops->flush) {
396 rc = state.ops->flush(uri, 0, state.length);
397 }
398 return rc;
399 }
400
401 if (needs_hdr_flush) {
402 rc = SWTPM_NVRAM_Linear_FlushHeader(uri);
403 }
404
405 if (rc == 0 && state.ops->flush) {
406 rc = state.ops->flush(uri, file_offset, filedata_length);
407 }
408
409 return rc;
410 }
411
412 static TPM_RESULT
413 SWTPM_NVRAM_DeleteName_Linear(uint32_t tpm_number SWTPM_ATTR_UNUSED,
414 const char *name,
415 TPM_BOOL mustExist SWTPM_ATTR_UNUSED,
416 const char *uri)
417 {
418 TPM_RESULT rc = 0;
419 uint32_t file_nr;
420
421 file_nr = SWTPM_NVRAM_Linear_GetFileNr(name);
422 if (file_nr == FILE_NR_INVALID) {
423 rc = TPM_FAIL;
424 }
425
426 if (rc == 0) {
427 rc = SWTPM_NVRAM_Linear_RemoveFile(uri, file_nr, TRUE);
428 }
429
430 if (rc == 0 && state.ops->flush) {
431 /* full flush, RemoveFile can move around data */
432 rc = state.ops->flush(uri, 0, state.length);
433 }
434
435 return rc;
436 }
437
438 static void SWTPM_NVRAM_Cleanup_Linear(void) {
439 if (state.ops && state.ops->cleanup) {
440 state.ops->cleanup();
441 }
442 if (state.loaded_uri) {
443 free(state.loaded_uri);
444 }
445 }
446
447 static TPM_RESULT
448 SWTPM_NVRAM_CheckState_Linear(const char *uri SWTPM_ATTR_UNUSED,
449 const char *name,
450 size_t *blobsize)
451 {
452 TPM_RESULT rc = 0;
453 uint32_t file_nr;
454 struct nvram_linear_hdr_file *file;
455
456 file_nr = SWTPM_NVRAM_Linear_GetFileNr(name);
457 if (file_nr == FILE_NR_INVALID) {
458 rc = TPM_FAIL;
459 }
460
461 if (rc == 0) {
462 file = &state.hdr->files[file_nr];
463 if (file->offset == 0) {
464 rc = TPM_RETRY;
465 } else {
466 *blobsize = le32toh(file->data_length);
467 }
468 }
469
470 return rc;
471 }
472
473 struct nvram_backend_ops nvram_linear_ops = {
474 .prepare = SWTPM_NVRAM_Prepare_Linear,
475 .load = SWTPM_NVRAM_LoadData_Linear,
476 .store = SWTPM_NVRAM_StoreData_Linear,
477 .delete = SWTPM_NVRAM_DeleteName_Linear,
478 .cleanup = SWTPM_NVRAM_Cleanup_Linear,
479 .check_state = SWTPM_NVRAM_CheckState_Linear,
480 };