b21141d40843fc0898ed229b9126440cb3bc490d
[aaf/sshsm.git] / TPM2-Plugin / lib / tpm2_attr_util.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 <stdbool.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include <tss2/tss2_sys.h>
36
37 #include "log.h"
38 #include "tpm2_attr_util.h"
39 #include "tpm2_util.h"
40
41 #define dispatch_no_arg_add(x) \
42     { .name = str(x), .callback=(action)x, .width = 1 }
43
44 #define dispatch_arg_add(x, w) \
45     { .name = str(x), .callback=(action)x, .width = w }
46
47 #define dispatch_reserved(pos) \
48     { .name = "<reserved("xstr(pos)")>", .callback=NULL, .width = 1 }
49
50 typedef enum dispatch_error dispatch_error;
51 enum dispatch_error {
52     dispatch_ok = 0,
53     dispatch_err,
54     dispatch_no_match,
55 };
56
57 typedef bool (*action)(void *obj, char *arg);
58
59 typedef struct dispatch_table dispatch_table;
60 struct dispatch_table {
61     char *name;
62     action callback;
63     unsigned width; /* the width of the field, CANNOT be 0 */
64 };
65
66 static bool authread(TPMA_NV *nv, char *arg) {
67
68     UNUSED(arg);
69     *nv |= TPMA_NV_AUTHREAD;
70     return true;
71 }
72
73 static bool authwrite(TPMA_NV *nv, char *arg) {
74
75     UNUSED(arg);
76     *nv |= TPMA_NV_AUTHWRITE;
77     return true;
78 }
79
80 static bool clear_stclear(TPMA_NV *nv, char *arg) {
81
82     UNUSED(arg);
83     *nv |= TPMA_NV_CLEAR_STCLEAR;
84     return true;
85 }
86
87 static bool globallock(TPMA_NV *nv, char *arg) {
88
89     UNUSED(arg);
90     *nv |= TPMA_NV_GLOBALLOCK;
91     return true;
92 }
93
94 static bool no_da(TPMA_NV *nv, char *arg) {
95
96     UNUSED(arg);
97     *nv |= TPMA_NV_NO_DA;
98     return true;
99 }
100
101 static bool orderly(TPMA_NV *nv, char *arg) {
102
103     UNUSED(arg);
104     *nv |= TPMA_NV_ORDERLY;
105     return true;
106 }
107
108 static bool ownerread(TPMA_NV *nv, char *arg) {
109
110     UNUSED(arg);
111     *nv |= TPMA_NV_OWNERREAD;
112     return true;
113 }
114
115 static bool ownerwrite(TPMA_NV *nv, char *arg) {
116
117     UNUSED(arg);
118     *nv |= TPMA_NV_OWNERWRITE;
119     return true;
120 }
121
122 static bool platformcreate(TPMA_NV *nv, char *arg) {
123
124     UNUSED(arg);
125     *nv |= TPMA_NV_PLATFORMCREATE;
126     return true;
127 }
128
129 static bool policyread(TPMA_NV *nv, char *arg) {
130
131     UNUSED(arg);
132     *nv |= TPMA_NV_POLICYREAD;
133     return true;
134 }
135
136 static bool policywrite(TPMA_NV *nv, char *arg) {
137
138     UNUSED(arg);
139     *nv |= TPMA_NV_POLICYWRITE;
140     return true;
141 }
142
143 static bool policydelete(TPMA_NV *nv, char *arg) {
144
145     UNUSED(arg);
146     *nv |= TPMA_NV_POLICY_DELETE;
147     return true;
148 }
149
150 static bool ppread(TPMA_NV *nv, char *arg) {
151
152     UNUSED(arg);
153     *nv |= TPMA_NV_PPREAD;
154     return true;
155 }
156
157 static bool ppwrite(TPMA_NV *nv, char *arg) {
158
159     UNUSED(arg);
160     *nv |= TPMA_NV_PPWRITE;
161     return true;
162 }
163
164 static bool readlocked(TPMA_NV *nv, char *arg) {
165
166     UNUSED(arg);
167     *nv |= TPMA_NV_READLOCKED;
168     return true;
169 }
170
171 static bool read_stclear(TPMA_NV *nv, char *arg) {
172
173     UNUSED(arg);
174     *nv |= TPMA_NV_READ_STCLEAR;
175     return true;
176 }
177
178 static bool writeall(TPMA_NV *nv, char *arg) {
179
180     UNUSED(arg);
181     *nv |= TPMA_NV_WRITEALL;
182     return true;
183 }
184
185 static bool writedefine(TPMA_NV *nv, char *arg) {
186
187     UNUSED(arg);
188     *nv |= TPMA_NV_WRITEDEFINE;
189     return true;
190 }
191
192 static bool writelocked(TPMA_NV *nv, char *arg) {
193
194     UNUSED(arg);
195     *nv |= TPMA_NV_WRITELOCKED;
196     return true;
197 }
198
199 static bool write_stclear(TPMA_NV *nv, char *arg) {
200
201     UNUSED(arg);
202     *nv |= TPMA_NV_WRITE_STCLEAR;
203     return true;
204 }
205
206 static bool written(TPMA_NV *nv, char *arg) {
207
208     UNUSED(arg);
209     *nv |= TPMA_NV_WRITTEN;
210     return true;
211 }
212
213 static bool nt(TPMA_NV *nv, char *arg) {
214
215     uint16_t value;
216     bool result = tpm2_util_string_to_uint16(arg, &value);
217     if (!result) {
218         LOG_ERR("Could not convert \"%s\", to a number", arg);
219         return false;
220     }
221
222     /* nt space is 4 bits, so max of 15 */
223     if (value > 0x0F) {
224         LOG_ERR("Field TPM_NT of type TPMA_NV is only 4 bits,"
225                 "value \"%s\" to big!", arg);
226         return false;
227     }
228
229     *nv &= ~TPMA_NV_TPM2_NT_MASK;
230     *nv |= value << 4;
231     return true;
232 }
233
234 /*
235  * The order of this table must be in order with the bit defines in table 204:
236  * https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
237  *
238  * This table is in bitfield order, thus the index of a bit set in a TPMA_NV
239  * can be used to lookup the name.
240  *
241  * if not the logic in tpm2_nv_util_strtoattr would need to change!
242  */
243 static dispatch_table nv_attr_table[] = { // Bit Index
244     dispatch_no_arg_add(ppwrite),         //  0
245     dispatch_no_arg_add(ownerwrite),      //  1
246     dispatch_no_arg_add(authwrite),       //  2
247     dispatch_no_arg_add(policywrite),     //  3
248     dispatch_arg_add(nt, 4),              //  4
249     dispatch_arg_add(nt, 3),              //  5
250     dispatch_arg_add(nt, 2),              //  6
251     dispatch_arg_add(nt, 1),              //  7
252     dispatch_reserved(8),                 //  8
253     dispatch_reserved(9),                 //  9
254     dispatch_no_arg_add(policydelete),    // 10
255     dispatch_no_arg_add(writelocked),     // 11
256     dispatch_no_arg_add(writeall),        // 12
257     dispatch_no_arg_add(writedefine),     // 13
258     dispatch_no_arg_add(write_stclear),   // 14
259     dispatch_no_arg_add(globallock),      // 15
260     dispatch_no_arg_add(ppread),          // 16
261     dispatch_no_arg_add(ownerread),       // 17
262     dispatch_no_arg_add(authread),        // 18
263     dispatch_no_arg_add(policyread),      // 19
264     dispatch_reserved(20),                // 20
265     dispatch_reserved(21),                // 21
266     dispatch_reserved(22),                // 22
267     dispatch_reserved(23),                // 23
268     dispatch_reserved(24),                // 24
269     dispatch_no_arg_add(no_da),           // 25
270     dispatch_no_arg_add(orderly),         // 26
271     dispatch_no_arg_add(clear_stclear),   // 27
272     dispatch_no_arg_add(readlocked),      // 28
273     dispatch_no_arg_add(written),         // 29
274     dispatch_no_arg_add(platformcreate),  // 30
275     dispatch_no_arg_add(read_stclear),    // 31
276 };
277
278 static bool fixedtpm(TPMA_OBJECT *obj, char *arg) {
279
280     UNUSED(arg);
281     *obj |= TPMA_OBJECT_FIXEDTPM;
282     return true;
283 }
284
285 static bool stclear(TPMA_OBJECT *obj, char *arg) {
286
287     UNUSED(arg);
288     *obj |= TPMA_OBJECT_STCLEAR;
289     return true;
290 }
291
292 static bool fixedparent(TPMA_OBJECT *obj, char *arg) {
293
294     UNUSED(arg);
295     *obj |= TPMA_OBJECT_FIXEDPARENT;
296     return true;
297 }
298
299 static bool sensitivedataorigin(TPMA_OBJECT *obj, char *arg) {
300
301     UNUSED(arg);
302     *obj |= TPMA_OBJECT_SENSITIVEDATAORIGIN;
303     return true;
304 }
305
306 static bool userwithauth(TPMA_OBJECT *obj, char *arg) {
307
308     UNUSED(arg);
309     *obj |= TPMA_OBJECT_USERWITHAUTH;
310     return true;
311 }
312
313 static bool adminwithpolicy(TPMA_OBJECT *obj, char *arg) {
314
315     UNUSED(arg);
316     *obj |= TPMA_OBJECT_ADMINWITHPOLICY;
317     return true;
318 }
319
320 static bool noda(TPMA_OBJECT *obj, char *arg) {
321
322     UNUSED(arg);
323     *obj |= TPMA_OBJECT_NODA;
324     return true;
325 }
326
327 static bool encryptedduplication(TPMA_OBJECT *obj, char *arg) {
328
329     UNUSED(arg);
330     *obj |= TPMA_OBJECT_ENCRYPTEDDUPLICATION;
331     return true;
332 }
333
334 static bool restricted(TPMA_OBJECT *obj, char *arg) {
335
336     UNUSED(arg);
337     *obj |= TPMA_OBJECT_RESTRICTED;
338     return true;
339 }
340
341 static bool decrypt(TPMA_OBJECT *obj, char *arg) {
342
343     UNUSED(arg);
344     *obj |= TPMA_OBJECT_DECRYPT;
345     return true;
346 }
347
348 static bool sign(TPMA_OBJECT *obj, char *arg) {
349
350     UNUSED(arg);
351     *obj |= TPMA_OBJECT_SIGN_ENCRYPT;
352     return true;
353 }
354
355 static dispatch_table obj_attr_table[] = {         // Bit Index
356         dispatch_reserved(0),                      //  0
357         dispatch_no_arg_add(fixedtpm),             //  1
358         dispatch_no_arg_add(stclear),              //  2
359         dispatch_reserved(3),                      //  3
360         dispatch_no_arg_add(fixedparent),          //  4
361         dispatch_no_arg_add(sensitivedataorigin),  //  5
362         dispatch_no_arg_add(userwithauth),         //  6
363         dispatch_no_arg_add(adminwithpolicy),      //  7
364         dispatch_reserved(8),                      //  8
365         dispatch_reserved(9),                      //  9
366         dispatch_no_arg_add(noda),                 // 10
367         dispatch_no_arg_add(encryptedduplication), // 11
368         dispatch_reserved(12),                     // 12
369         dispatch_reserved(13),                     // 13
370         dispatch_reserved(14),                     // 14
371         dispatch_reserved(15),                     // 15
372         dispatch_no_arg_add(restricted),           // 16
373         dispatch_no_arg_add(decrypt),              // 17
374         dispatch_no_arg_add(sign),                 // 18
375         dispatch_reserved(19),                     // 19
376         dispatch_reserved(20),                     // 20
377         dispatch_reserved(21),                     // 21
378         dispatch_reserved(22),                     // 22
379         dispatch_reserved(23),                     // 23
380         dispatch_reserved(24),                     // 24
381         dispatch_reserved(25),                     // 25
382         dispatch_reserved(26),                     // 26
383         dispatch_reserved(27),                     // 27
384         dispatch_reserved(28),                     // 28
385         dispatch_reserved(29),                     // 29
386         dispatch_reserved(30),                     // 30
387         dispatch_reserved(31),                     // 31
388 };
389
390 static bool token_match(const char *name, const char *token, bool has_arg, char **sep) {
391
392     /* if it has an argument, we expect a separator */
393     size_t match_len = strlen(token);
394     if (has_arg) {
395         *sep = strchr(token, '=');
396         if (*sep) {
397             match_len = *sep - token;
398         }
399     }
400
401     return !strncmp(name, token, match_len);
402 }
403
404 static dispatch_error handle_dispatch(dispatch_table *d, char *token,
405         TPMA_NV *nvattrs) {
406
407     char *name = d->name;
408     action cb = d->callback;
409     bool has_arg = d->width > 1;
410
411     /* if no callback, then its a reserved block, just skip it */
412     if (!cb) {
413         return dispatch_no_match;
414     }
415
416     char *sep = NULL;
417     bool match = token_match(name, token, has_arg, &sep);
418     if (!match) {
419         return dispatch_no_match;
420     }
421
422     /*
423      * If it has an argument, match should have found the seperator.
424      */
425     char *arg = NULL;
426     if (has_arg) {
427         if (!sep) {
428             LOG_ERR("Expected argument for \"%s\", got none.", token);
429             return dispatch_err;
430         }
431
432         /* split token on = */
433         *sep = '\0';
434         sep++;
435         if (!*sep) {
436             LOG_ERR("Expected argument for \"%s\", got none.", token);
437             return dispatch_err;
438         }
439
440         /* valid argument string, assign */
441         arg = sep;
442     }
443
444     bool result = cb(nvattrs, arg);
445     return result ? dispatch_ok : dispatch_err;
446 }
447
448 static bool common_strtoattr(char *attribute_list, void *attrs, dispatch_table *table, size_t size) {
449
450     char *token;
451     char *save;
452
453     /*
454      * This check is soley to prevent GCC from complaining on:
455      * error: â€˜attribute_list’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
456      * Might as well check nvattrs as well.
457      */
458     if (!attribute_list || !attrs) {
459         LOG_ERR("attribute list or attributes structure is NULL");
460         return false;
461     }
462
463     while ((token = strtok_r(attribute_list, "|", &save))) {
464         attribute_list = NULL;
465
466         bool did_dispatch = false;
467
468         size_t i;
469         for (i = 0; i < size; i++) {
470             dispatch_table *d = &table[i];
471
472             dispatch_error err = handle_dispatch(d, token, attrs);
473             if (err == dispatch_ok) {
474                 did_dispatch = true;
475                 break;
476             } else if (err == dispatch_err) {
477                 return false;
478             }
479             /* dispatch_no_match --> keep looking */
480         }
481
482         /* did we dispatch?, If not log error and return */
483         if (!did_dispatch) {
484             char *tmp = strchr(token, '=');
485             if (tmp) {
486                 *tmp = '\0';
487             }
488             LOG_ERR("Unknown token: \"%s\" found.", token);
489             return false;
490         }
491     }
492
493     return true;
494 }
495
496 bool tpm2_attr_util_nv_strtoattr(char *attribute_list, TPMA_NV *nvattrs) {
497
498     return common_strtoattr(attribute_list, nvattrs, nv_attr_table, ARRAY_LEN(nv_attr_table));
499 }
500
501 bool tpm2_attr_util_obj_strtoattr(char *attribute_list, TPMA_OBJECT *objattrs) {
502
503     return common_strtoattr(attribute_list, objattrs, obj_attr_table, ARRAY_LEN(obj_attr_table));
504 }
505
506
507 static UINT8 find_first_set(UINT32 bits) {
508
509     UINT8 n = 0;
510
511     if (!bits) {
512         return n;
513     }
514
515     if (!(bits & 0x0000FFFF)) { n += 16; bits >>= 16; }
516     if (!(bits & 0x000000FF)) { n +=  8; bits >>=  8; }
517     if (!(bits & 0x0000000F)) { n +=  4; bits >>=  4; }
518     if (!(bits & 0x00000003)) { n +=  2; bits >>=  2; }
519     if (!(bits & 0x00000001))   n +=  1;
520
521     return n;
522 }
523
524 static char *tpm2_attr_util_common_attrtostr(UINT32 attrs, dispatch_table *table, size_t size) {
525
526     if (attrs == 0) {
527         return strdup("<none>");
528     }
529
530     /*
531      * Get how many bits are set in the attributes and then find the longest
532      * "name".
533      *
534      * pop_cnt * max_name_len + pop_cnt - 1 (for the | separators) + 4
535      * (for nv field equals in hex) + 1 for null byte.
536      *
537      * This will provide us an ample buffer size for generating the string
538      * in without having to constantly realloc.
539      */
540     UINT32 pop_cnt = tpm2_util_pop_count(attrs);
541
542     size_t i;
543     size_t max_name_len = 0;
544     for (i=0; i < size; i++) {
545         dispatch_table *d = &table[i];
546         size_t name_len = strlen(d->name);
547         max_name_len = name_len > max_name_len ? name_len : max_name_len;
548     }
549
550     size_t length = pop_cnt * max_name_len + pop_cnt - 1 + 3;
551
552     char *str = calloc(length, 1);
553     if (!str) {
554         return NULL;
555     }
556
557
558     size_t string_index = 0;
559
560     /*
561      * Start at the lowest, first bit set, index into the array,
562      * grab the data needed, and move on.
563      */
564     while (attrs) {
565         UINT8 bit_index = find_first_set(attrs);
566
567         dispatch_table *d = &table[bit_index];
568
569         const char *name = d->name;
570         unsigned w = d->width;
571
572         /* current position and size left of the string */
573         char *s = &str[string_index];
574         size_t left = length - string_index;
575
576         /* this is a mask that is field width wide */
577         UINT8 mask = ((UINT32)1 << w) - 1;
578
579         /* get the value in the field before clearing attrs out */
580         UINT8 field_values = (attrs & mask << bit_index) >> bit_index;
581
582         /*
583          * turn off the parsed bit(s) index, using width to turn off everything in a
584          * field
585          */
586         attrs &= ~(mask << bit_index);
587
588         /*
589          * if the callback is NULL, we are either in a field middle or reserved
590          * section which is weird, just add the name in. In the case of being
591          * in the middle of the field, we will add a bunch of errors to the string,
592          * but it would be better to attempt to show all the data in string form,
593          * rather than bail.
594          *
595          * Fields are either 1 or > 1.
596          */
597         if (w == 1) {
598             /*
599              * set the format to a middle output, unless we're parsing
600              * the first or last. Let the format be static with the routine
601              * so the compiler can do printf style format specifier checking.
602              */
603             if (!string_index) {
604                 /* on the first write, if we are already done, no pipes */
605                 string_index += !attrs ? snprintf(s, left, "%s", name) :
606                         snprintf(s, left, "%s|", name);
607             } else if (!attrs) {
608                 string_index += snprintf(s, left, "%s", name);
609             } else {
610                 string_index += snprintf(s, left, "%s|", name);
611             }
612         } else {
613             /* deal with the field */
614             if (!string_index) {
615                 /* on the first write, if we are already done, no pipes */
616                 string_index += !attrs ? snprintf(s, left, "%s=0x%X", name, field_values) :
617                         snprintf(s, left, "%s=0x%X|", name, field_values);
618             } else if (!attrs) {
619                 string_index += snprintf(s, left, "%s=0x%X", name, field_values);
620             } else {
621                 string_index += snprintf(s, left, "%s=0x%X|", name, field_values);
622             }
623         }
624     }
625
626     return str;
627 }
628
629 char *tpm2_attr_util_nv_attrtostr(TPMA_NV nvattrs) {
630     return tpm2_attr_util_common_attrtostr(nvattrs, nv_attr_table, ARRAY_LEN(nv_attr_table));
631 }
632
633 char *tpm2_attr_util_obj_attrtostr(TPMA_OBJECT objattrs) {
634     return tpm2_attr_util_common_attrtostr(objattrs, obj_attr_table, ARRAY_LEN(obj_attr_table));
635 }
636
637 bool tpm2_attr_util_obj_from_optarg(char *argvalue, TPMA_OBJECT *objattrs) {
638
639     bool res = tpm2_util_string_to_uint32(argvalue, objattrs);
640     if (!res) {
641         res = tpm2_attr_util_obj_strtoattr(argvalue, objattrs);
642     }
643
644     return res;
645 }