Utility to Import external RSA pem key into TPM
[aaf/sshsm.git] / TPM2-Plugin / lib / files.c
1 //**********************************************************************;
2 // Copyright (c) 2017, Intel Corporation
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice,
12 // this list of conditions and the following disclaimer in the documentation
13 // and/or other materials provided with the distribution.
14 //
15 // 3. Neither the name of Intel Corporation nor the names of its contributors
16 // may be used to endorse or promote products derived from this software without
17 // specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29 // THE POSSIBILITY OF SUCH DAMAGE.
30 //**********************************************************************;
31 #include <errno.h>
32 #include <inttypes.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <string.h>
36
37 #include <tss2/tss2_sys.h>
38 #include <tss2/tss2_mu.h>
39
40 #include "files.h"
41 #include "log.h"
42 #include "tpm2_util.h"
43
44 bool files_get_file_size(FILE *fp, unsigned long *file_size, const char *path) {
45
46     long current = ftell(fp);
47     if (current < 0) {
48         if (path) {
49             LOG_ERR("Error getting current file offset for file \"%s\" error: %s", path, strerror(errno));
50         }
51         return false;
52     }
53
54     int rc = fseek(fp, 0, SEEK_END);
55     if (rc < 0) {
56         if (path) {
57             LOG_ERR("Error seeking to end of file \"%s\" error: %s", path, strerror(errno));
58         }
59         return false;
60     }
61
62     long size = ftell(fp);
63     if (size < 0) {
64         if (path) {
65             LOG_ERR("ftell on file \"%s\" failed: %s", path, strerror(errno));
66         }
67         return false;
68     }
69
70     rc = fseek(fp, current, SEEK_SET);
71     if (rc < 0) {
72         if (path) {
73             LOG_ERR("Could not restore initial stream position for file \"%s\" failed: %s", path, strerror(errno));
74         }
75         return false;
76     }
77
78     /* size cannot be negative at this point */
79     *file_size = (unsigned long)size;
80     return true;
81 }
82
83 static bool read_bytes_from_file(FILE *f, UINT8 *buf, UINT16 *size,
84                                  const char *path) {
85     unsigned long file_size;
86     bool result = files_get_file_size(f, &file_size, path);
87     if (!result) {
88         /* get_file_size() logs errors */
89         return false;
90     }
91
92     /* max is bounded on UINT16 */
93     if (file_size > *size) {
94         if (path) {
95             LOG_ERR(
96                     "File \"%s\" size is larger than buffer, got %lu expected less than %u",
97                     path, file_size, *size);
98         }
99         return false;
100     }
101
102     result = files_read_bytes(f, buf, file_size);
103     if (!result) {
104         if (path) {
105             LOG_ERR("Could not read data from file \"%s\"", path);
106         }
107         return false;
108     }
109
110     *size = file_size;
111
112     return true;
113 }
114
115 bool files_load_bytes_from_path(const char *path, UINT8 *buf, UINT16 *size) {
116     if (!buf || !size || !path) {
117         return false;
118     }
119
120     FILE *f = fopen(path, "rb");
121     if (!f) {
122         LOG_ERR("Could not open file \"%s\" error %s", path, strerror(errno));
123         return false;
124     }
125
126     bool result = read_bytes_from_file(f, buf, size, path);
127
128     fclose(f);
129     return result;
130 }
131
132 bool files_save_bytes_to_file(const char *path, UINT8 *buf, UINT16 size) {
133
134     if (!path || !buf) {
135         return false;
136     }
137
138     FILE *fp = fopen(path, "wb+");
139     if (!fp) {
140         LOG_ERR("Could not open file \"%s\", error: %s", path, strerror(errno));
141         return false;
142     }
143
144     bool result = files_write_bytes(fp, buf, size);
145     if (!result) {
146         LOG_ERR("Could not write data to file \"%s\"", path);
147     }
148     fclose(fp);
149     return result;
150 }
151
152 /*
153  * Current version to write TPMS_CONTEXT to disk.
154  */
155 #define CONTEXT_VERSION 1
156
157 bool files_save_tpm_context_to_file(TSS2_SYS_CONTEXT *sysContext, TPM2_HANDLE handle,
158         FILE *stream) {
159
160     TPMS_CONTEXT context;
161
162     TSS2_RC rval = Tss2_Sys_ContextSave(sysContext, handle, &context);
163     if (rval != TPM2_RC_SUCCESS) {
164         LOG_PERR(Tss2_Sys_ContextSave, rval);
165         return false;
166     }
167
168     /*
169      * Saving the TPMS_CONTEXT structure to disk, format:
170      * TPM2.0-TOOLS HEADER
171      * U32 hiearchy
172      * U32 savedHandle
173      * U64 sequence
174      * U16 contextBlobLength
175      * BYTE[] contextBlob
176      */
177     bool result = files_write_header(stream, CONTEXT_VERSION);
178     if (!result) {
179         LOG_ERR("Could not write context file header");
180         goto out;
181     }
182
183     // UINT32
184     result = files_write_32(stream, context.hierarchy);
185     if (!result) {
186         LOG_ERR("Could not write hierarchy");
187         goto out;
188     }
189
190     result = files_write_32(stream, context.savedHandle);
191     if (!result) {
192         LOG_ERR("Could not write savedHandle");
193         goto out;
194     }
195
196     // UINT64
197     result = files_write_64(stream, context.sequence);
198     if (!result) {
199         LOG_ERR("Could not write sequence");
200         goto out;
201     }
202
203     // U16 LENGTH
204     result = files_write_16(stream, context.contextBlob.size);
205     if (!result) {
206         LOG_ERR("Could not write contextBob size");
207         goto out;
208     }
209
210     // BYTE[] contextBlob
211     result = files_write_bytes(stream, context.contextBlob.buffer,
212             context.contextBlob.size);
213     if (!result) {
214         LOG_ERR("Could not write contextBlob buffer");
215     }
216     /* result is set by file_write_bytes() */
217
218 out:
219     return result;
220 }
221
222 bool files_save_tpm_context_to_path(TSS2_SYS_CONTEXT *sysContext, TPM2_HANDLE handle,
223         const char *path) {
224
225     FILE *f = fopen(path, "w+b");
226     if (!f) {
227         LOG_ERR("Error opening file \"%s\" due to error: %s", path,
228                 strerror(errno));
229         return false;
230     }
231
232     bool result = files_save_tpm_context_to_file(sysContext, handle, f);
233     fclose(f);
234     return result;
235 }
236
237
238 bool files_load_tpm_context_from_file(TSS2_SYS_CONTEXT *sapi_context,
239         TPM2_HANDLE *handle, FILE *fstream) {
240
241     TSS2_RC rval;
242
243     /*
244      * Reading the TPMS_CONTEXT structure to disk, format:
245      * TPM2.0-TOOLS HEADER
246      * U32 hiearchy
247      * U32 savedHandle
248      * U64 sequence
249      * U16 contextBlobLength
250      * BYTE[] contextBlob
251      */
252     UINT32 version;
253     TPMS_CONTEXT context;
254     bool result = files_read_header(fstream, &version);
255     if (!result) {
256         LOG_WARN(
257             "The loaded tpm context does not appear to be in the proper format,"
258             "assuming old format, this will be converted on the next save.");
259         rewind(fstream);
260         result = files_read_bytes(fstream, (UINT8 *) &context, sizeof(context));
261         if (!result) {
262             LOG_ERR("Could not load tpm context file");
263             goto out;
264         }
265         /* Success load the context into the TPM */
266         goto load_to_tpm;
267     }
268
269     if (version != CONTEXT_VERSION) {
270         LOG_ERR("Unsupported context file format version found, got: %"PRIu32,
271                 version);
272         result = false;
273         goto out;
274     }
275
276     result = files_read_32(fstream, &context.hierarchy);
277     if (!result) {
278         LOG_ERR("Error reading hierarchy!");
279         goto out;
280     }
281
282     result = files_read_32(fstream, &context.savedHandle);
283     if (!result) {
284         LOG_ERR("Error reading savedHandle!");
285         goto out;
286     }
287
288     result = files_read_64(fstream, &context.sequence);
289     if (!result) {
290         LOG_ERR("Error reading sequence!");
291         goto out;
292     }
293
294     result = files_read_16(fstream, &context.contextBlob.size);
295     if (!result) {
296         LOG_ERR("Error reading contextBlob.size!");
297         goto out;
298     }
299
300     if (context.contextBlob.size > sizeof(context.contextBlob.buffer)) {
301         LOG_ERR(
302                 "Size mismatch found on contextBlob, got %"PRIu16" expected less than or equal to %zu",
303                 context.contextBlob.size,
304                 sizeof(context.contextBlob.buffer));
305         result = false;
306         goto out;
307     }
308
309     result = files_read_bytes(fstream, context.contextBlob.buffer,
310             context.contextBlob.size);
311     if (!result) {
312         LOG_ERR("Error reading contextBlob.size!");
313         goto out;
314     }
315
316 load_to_tpm:
317     rval = Tss2_Sys_ContextLoad(sapi_context, &context, handle);
318     if (rval != TPM2_RC_SUCCESS) {
319         LOG_PERR(Tss2_Sys_ContextLoad, rval);
320         result = false;
321         goto out;
322     }
323
324     result = true;
325
326 out:
327     return result;
328 }
329
330 bool files_load_tpm_context_from_path(TSS2_SYS_CONTEXT *sapi_context,
331         TPM2_HANDLE *handle, const char *path) {
332
333     FILE *f = fopen(path, "rb");
334     if (!f) {
335         LOG_ERR("Error opening file \"%s\" due to error: %s", path,
336                 strerror(errno));
337         return false;
338     }
339
340     bool result = files_load_tpm_context_from_file(sapi_context, handle, f);
341
342     fclose(f);
343     return result;
344 }
345
346 bool files_does_file_exist(const char *path) {
347
348     if (!path) {
349         LOG_ERR("Path cannot be NULL");
350         return false;
351     }
352
353     FILE *fp = fopen(path,"rb");
354     if (fp) {
355         fclose(fp);
356         LOG_ERR("Path: %s already exists. Please rename or delete the file!",
357                 path);
358         return true;
359     }
360     return false;
361 }
362
363 bool files_get_file_size_path(const char *path, unsigned long *file_size) {
364
365     bool result = false;
366
367     if (!path) {
368         LOG_ERR("Must specify a path argument, cannot be NULL!");
369         return false;
370     }
371
372     if (!file_size) {
373         LOG_ERR("Must specify a file size argument, cannot be NULL!");
374         return false;
375     }
376
377     FILE *fp = fopen(path,"rb");
378     if(!fp) {
379         LOG_ERR("Could not open file: \"%s\" error: %s", path, strerror(errno));
380         return false;
381     }
382
383     result = files_get_file_size(fp, file_size, path);
384
385     fclose(fp);
386     return result;
387 }
388
389 /**
390  * This is the magic for the file header. The header is organized
391  * as a big endian U32 (BEU32) of MAGIC followed by a BEU32 of the
392  * version number. Tools can define their own, individual file
393  * formats as they make sense, but they should always have the header.
394  */
395 static const UINT32 MAGIC = 0xBADCC0DE;
396
397 /**
398  * Writes size bytes to a file, continuing on EINTR short writes.
399  * @param f
400  *  The file to write to.
401  * @param data
402  *  The data to write.
403  * @param size
404  *  The size, in bytes, of that data.
405  * @return
406  *  True on success, False otherwise.
407  */
408 static bool writex(FILE *f, UINT8 *data, size_t size) {
409
410     size_t wrote = 0;
411     size_t index = 0;
412     do {
413         wrote = fwrite(&data[index], 1, size, f);
414         if (wrote != size) {
415             if (errno != EINTR) {
416                 return false;
417             }
418             /* continue on EINTR */
419         }
420         size -= wrote;
421         index += wrote;
422     } while (size > 0);
423
424     return true;
425 }
426
427 /**
428  * Reads size bytes from a file, continuing on EINTR short reads.
429  * @param f
430  *  The file to read from.
431  * @param data
432  *  The data buffer to read into.
433  * @param size
434  *  The size of the buffer, which is also the amount of bytes to read.
435  * @return
436  *  True on success, False otherwise.
437  */
438 static bool readx(FILE *f, UINT8 *data, size_t size) {
439
440     size_t bread = 0;
441     size_t index = 0;
442     do {
443         bread = fread(&data[index], 1, size, f);
444         if (bread != size) {
445             if (feof(f) || (errno != EINTR)) {
446                 return false;
447             }
448             /* continue on EINTR */
449         }
450         size -= bread;
451         index += bread;
452     } while (size > 0);
453
454     return true;
455 }
456
457 #define BAIL_ON_NULL(param, x) \
458     do { \
459         if (!x) { \
460             LOG_ERR(param" must be specified"); \
461             return false; \
462         } \
463     } while(0)
464
465 #define BE_CONVERT(value, size) \
466     do { \
467         if (!tpm2_util_is_big_endian()) { \
468             value = tpm2_util_endian_swap_##size(value); \
469         } \
470     } while (0)
471
472 #define FILE_WRITE(size) \
473     bool files_write_##size(FILE *out, UINT##size data) { \
474         BAIL_ON_NULL("FILE", out); \
475         BE_CONVERT(data, size); \
476         return writex(out, (UINT8 *)&data, sizeof(data)); \
477     }
478
479 #define FILE_READ(size) \
480     bool files_read_##size(FILE *out, UINT##size *data) { \
481             BAIL_ON_NULL("FILE", out); \
482             BAIL_ON_NULL("data", data); \
483         bool res = readx(out, (UINT8 *)data, sizeof(*data)); \
484         if (res) { \
485             BE_CONVERT(*data, size); \
486         } \
487         return res; \
488     }
489
490 /*
491  * all the files_read|write_bytes_16|32|64 functions
492  */
493 FILE_READ(16);
494 FILE_WRITE(16)
495
496 FILE_READ(32);
497 FILE_WRITE(32)
498
499 FILE_READ(64)
500 FILE_WRITE(64)
501
502 bool files_read_bytes(FILE *out, UINT8 bytes[], size_t len) {
503
504     BAIL_ON_NULL("FILE", out);
505     BAIL_ON_NULL("bytes", bytes);
506     return readx(out, bytes, len);
507 }
508
509 bool files_write_bytes(FILE *out, uint8_t bytes[], size_t len) {
510
511     BAIL_ON_NULL("FILE", out);
512     BAIL_ON_NULL("bytes", bytes);
513     return writex(out, bytes, len);
514 }
515
516 bool files_write_header(FILE *out, UINT32 version) {
517
518     BAIL_ON_NULL("FILE", out);
519
520     bool res = files_write_32(out, MAGIC);
521     if (!res) {
522         return false;
523     }
524     return files_write_32(out, version);
525 }
526
527 bool files_read_header(FILE *out, uint32_t *version) {
528
529     BAIL_ON_NULL("FILE", out);
530     BAIL_ON_NULL("version", version);
531
532     UINT32 magic;
533     bool res = files_read_32(out, &magic);
534     if (!res) {
535         return false;
536     }
537
538     if (magic != MAGIC) {
539         LOG_ERR("Found magic 0x%x did not match expected magic of 0x%x!",
540                 magic, MAGIC);
541         return false;
542     }
543
544     return files_read_32(out, version);
545 }
546
547 bool files_load_bytes_from_file_or_stdin(const char *path, UINT16 *size, BYTE *buf) {
548
549     FILE *file =  path ? fopen(path, "rb") : stdin;
550     path = file != stdin ? path : "<stdin>";
551     if (!file) {
552         LOG_ERR("Could not open file: \"%s\", error: %s", path,
553                 strerror(errno));
554         return false;
555     }
556
557     /*
558      * Attempt to accurately read the file based on the file size.
559      * This may fail on stdin when it's a pipe.
560      */
561     if (file == stdin) {
562         path = NULL;
563     }
564
565     UINT16 original_size = *size;
566     bool res = files_load_bytes_from_path(path, buf,
567             size);
568     if (!res) {
569         res = true;
570         *size = fread(buf, 1,
571                 *size, file);
572         if (!feof(file)) {
573             LOG_ERR("Data to be sealed larger than expected. Got %u expected %u",
574                     original_size, res);
575             res = false;
576         }
577         else if (ferror(file)) {
578             LOG_ERR("Error reading sealed data from \"<stdin>\"");
579             res = false;
580         }
581     }
582
583     if (file != stdin) {
584         fclose(file);
585     }
586
587     return res;
588 }
589
590 #define SAVE_TYPE(type, name) \
591     bool files_save_##name(type *name, const char *path) { \
592     \
593         size_t offset = 0; \
594         UINT8 buffer[sizeof(*name)]; \
595         TSS2_RC rc = Tss2_MU_##type##_Marshal(name, buffer, sizeof(buffer), &offset); \
596         if (rc != TSS2_RC_SUCCESS) { \
597             LOG_ERR("Error serializing "str(name)" structure: 0x%x", rc); \
598             return false; \
599         } \
600     \
601         return files_save_bytes_to_file(path, buffer, offset); \
602     }
603
604 #define LOAD_TYPE(type, name) \
605     bool files_load_##name(const char *path, type *name) { \
606     \
607         UINT8 buffer[sizeof(*name)]; \
608         UINT16 size = sizeof(buffer); \
609         bool res = files_load_bytes_from_path(path, buffer, &size); \
610         if (!res) { \
611             return false; \
612         } \
613         \
614         size_t offset = 0; \
615         TSS2_RC rc = Tss2_MU_##type##_Unmarshal(buffer, size, &offset, name); \
616         if (rc != TSS2_RC_SUCCESS) { \
617             LOG_ERR("Error serializing "str(name)" structure: 0x%x", rc); \
618             return false; \
619         } \
620         \
621         return rc == TPM2_RC_SUCCESS; \
622     }
623
624 SAVE_TYPE(TPM2B_PUBLIC, public)
625 LOAD_TYPE(TPM2B_PUBLIC, public)
626
627 SAVE_TYPE(TPMT_SIGNATURE, signature)
628 LOAD_TYPE(TPMT_SIGNATURE, signature)
629
630 SAVE_TYPE(TPMT_TK_VERIFIED, ticket)
631 LOAD_TYPE(TPMT_TK_VERIFIED, ticket)
632
633 SAVE_TYPE(TPM2B_SENSITIVE, sensitive)
634 LOAD_TYPE(TPM2B_SENSITIVE, sensitive)
635
636 SAVE_TYPE(TPMT_TK_HASHCHECK, validation)
637 LOAD_TYPE(TPMT_TK_HASHCHECK, validation)
638
639 SAVE_TYPE(TPM2B_PRIVATE, private)
640 LOAD_TYPE(TPM2B_PRIVATE, private)