aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorKylene Jo Hall <kjhall@us.ibm.com>2006-01-08 04:03:15 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-08 23:13:55 -0500
commit55a82ab3181be039c6440d3f2f69260ad6fe2988 (patch)
tree6acf566ae3831b0f4f8b428fbaa0e335bc7a2691 /drivers/char
parent485a6435abc3897934ce0dc530e31db93e9296a6 (diff)
[PATCH] tpm: add bios measurement log
According to the TCG specifications measurements or hashes of the BIOS code and data are extended into TPM PCRS and a log is kept in an ACPI table of these extensions for later validation if desired. This patch exports the values in the ACPI table through a security-fs seq_file. Signed-off-by: Seiji Munetoh <munetoh@jp.ibm.com> Signed-off-by: Stefan Berger <stefanb@us.ibm.com> Signed-off-by: Reiner Sailer <sailer@us.ibm.com> Signed-off-by: Kylene Hall <kjhall@us.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/tpm/Makefile3
-rw-r--r--drivers/char/tpm/tpm.c3
-rw-r--r--drivers/char/tpm/tpm.h15
-rw-r--r--drivers/char/tpm/tpm_bios.c508
4 files changed, 529 insertions, 0 deletions
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 2392e404e8d1..ba4582d160fd 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -2,6 +2,9 @@
2# Makefile for the kernel tpm device drivers. 2# Makefile for the kernel tpm device drivers.
3# 3#
4obj-$(CONFIG_TCG_TPM) += tpm.o 4obj-$(CONFIG_TCG_TPM) += tpm.o
5ifdef CONFIG_ACPI
6 obj-$(CONFIG_TCG_TPM) += tpm_bios.o
7endif
5obj-$(CONFIG_TCG_NSC) += tpm_nsc.o 8obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
6obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o 9obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
7obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o 10obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index a9be0e8eaea5..5a3870477ef1 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -466,6 +466,7 @@ void tpm_remove_hardware(struct device *dev)
466 kfree(chip->vendor->miscdev.name); 466 kfree(chip->vendor->miscdev.name);
467 467
468 sysfs_remove_group(&dev->kobj, chip->vendor->attr_group); 468 sysfs_remove_group(&dev->kobj, chip->vendor->attr_group);
469 tpm_bios_log_teardown(chip->bios_dir);
469 470
470 dev_mask[chip->dev_num / TPM_NUM_MASK_ENTRIES ] &= 471 dev_mask[chip->dev_num / TPM_NUM_MASK_ENTRIES ] &=
471 ~(1 << (chip->dev_num % TPM_NUM_MASK_ENTRIES)); 472 ~(1 << (chip->dev_num % TPM_NUM_MASK_ENTRIES));
@@ -593,6 +594,8 @@ dev_num_search_complete:
593 594
594 sysfs_create_group(&dev->kobj, chip->vendor->attr_group); 595 sysfs_create_group(&dev->kobj, chip->vendor->attr_group);
595 596
597 chip->bios_dir = tpm_bios_log_setup(devname);
598
596 return 0; 599 return 0;
597} 600}
598EXPORT_SYMBOL_GPL(tpm_register_hardware); 601EXPORT_SYMBOL_GPL(tpm_register_hardware);
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 159882ca69dd..fd3a4beaa53d 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -82,6 +82,8 @@ struct tpm_chip {
82 82
83 struct tpm_vendor_specific *vendor; 83 struct tpm_vendor_specific *vendor;
84 84
85 struct dentry **bios_dir;
86
85 struct list_head list; 87 struct list_head list;
86}; 88};
87 89
@@ -107,3 +109,16 @@ extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
107extern void tpm_remove_hardware(struct device *); 109extern void tpm_remove_hardware(struct device *);
108extern int tpm_pm_suspend(struct device *, pm_message_t); 110extern int tpm_pm_suspend(struct device *, pm_message_t);
109extern int tpm_pm_resume(struct device *); 111extern int tpm_pm_resume(struct device *);
112
113#ifdef CONFIG_ACPI
114extern struct dentry ** tpm_bios_log_setup(char *);
115extern void tpm_bios_log_teardown(struct dentry **);
116#else
117static inline struct dentry* tpm_bios_log_setup(char *name)
118{
119 return NULL;
120}
121static inline void tpm_bios_log_teardown(struct dentry **dir)
122{
123}
124#endif
diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c
new file mode 100644
index 000000000000..5e1f4dfefa17
--- /dev/null
+++ b/drivers/char/tpm/tpm_bios.c
@@ -0,0 +1,508 @@
1/*
2 * Copyright (C) 2005 IBM Corporation
3 *
4 * Authors:
5 * Seiji Munetoh <munetoh@jp.ibm.com>
6 * Stefan Berger <stefanb@us.ibm.com>
7 * Reiner Sailer <sailer@watson.ibm.com>
8 * Kylene Hall <kjhall@us.ibm.com>
9 *
10 * Access to the eventlog extended by the TCG BIOS of PC platform
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
16 *
17 */
18
19#include <linux/seq_file.h>
20#include <linux/fs.h>
21#include <linux/security.h>
22#include <linux/module.h>
23#include <acpi/acpi.h>
24#include <acpi/actypes.h>
25#include <acpi/actbl.h>
26#include "tpm.h"
27
28#define TCG_EVENT_NAME_LEN_MAX 255
29#define MAX_TEXT_EVENT 1000 /* Max event string length */
30#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */
31
32struct acpi_tcpa {
33 struct acpi_table_header hdr;
34 u16 reserved;
35 u32 log_max_len __attribute__ ((packed));
36 u32 log_start_addr __attribute__ ((packed));
37};
38
39struct tcpa_event {
40 u32 pcr_index;
41 u32 event_type;
42 u8 pcr_value[20]; /* SHA1 */
43 u32 event_size;
44 u8 event_data[0];
45};
46
47enum tcpa_event_types {
48 PREBOOT = 0,
49 POST_CODE,
50 UNUSED,
51 NO_ACTION,
52 SEPARATOR,
53 ACTION,
54 EVENT_TAG,
55 SCRTM_CONTENTS,
56 SCRTM_VERSION,
57 CPU_MICROCODE,
58 PLATFORM_CONFIG_FLAGS,
59 TABLE_OF_DEVICES,
60 COMPACT_HASH,
61 IPL,
62 IPL_PARTITION_DATA,
63 NONHOST_CODE,
64 NONHOST_CONFIG,
65 NONHOST_INFO,
66};
67
68static const char* tcpa_event_type_strings[] = {
69 "PREBOOT",
70 "POST CODE",
71 "",
72 "NO ACTION",
73 "SEPARATOR",
74 "ACTION",
75 "EVENT TAG",
76 "S-CRTM Contents",
77 "S-CRTM Version",
78 "CPU Microcode",
79 "Platform Config Flags",
80 "Table of Devices",
81 "Compact Hash",
82 "IPL",
83 "IPL Partition Data",
84 "Non-Host Code",
85 "Non-Host Config",
86 "Non-Host Info"
87};
88
89enum tcpa_pc_event_ids {
90 SMBIOS = 1,
91 BIS_CERT,
92 POST_BIOS_ROM,
93 ESCD,
94 CMOS,
95 NVRAM,
96 OPTION_ROM_EXEC,
97 OPTION_ROM_CONFIG,
98 OPTION_ROM_MICROCODE,
99 S_CRTM_VERSION,
100 S_CRTM_CONTENTS,
101 POST_CONTENTS,
102};
103
104static const char* tcpa_pc_event_id_strings[] = {
105 ""
106 "SMBIOS",
107 "BIS Certificate",
108 "POST BIOS ",
109 "ESCD ",
110 "CMOS",
111 "NVRAM",
112 "Option ROM",
113 "Option ROM config",
114 "Option ROM microcode",
115 "S-CRTM Version",
116 "S-CRTM Contents",
117 "S-CRTM POST Contents",
118};
119
120/* (Binary) bios measurement buffer */
121static void *tcg_eventlog;
122static void *tcg_eventlog_addr_limit; /* MAX */
123
124/* returns pointer to start of pos. entry of tcg log */
125static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos)
126{
127 loff_t i;
128 void *addr = tcg_eventlog;
129 struct tcpa_event *event;
130
131 /* read over *pos measurements */
132 for (i = 0; i < *pos; i++) {
133 event = addr;
134
135 if ((addr + sizeof(struct tcpa_event)) <
136 tcg_eventlog_addr_limit) {
137 if (event->event_type == 0 && event->event_size == 0)
138 return NULL;
139 addr +=
140 sizeof(struct tcpa_event) + event->event_size;
141 }
142 }
143
144 /* now check if current entry is valid */
145 if ((addr + sizeof(struct tcpa_event)) >= tcg_eventlog_addr_limit)
146 return NULL;
147
148 event = addr;
149
150 if ((event->event_type == 0 && event->event_size == 0) ||
151 ((addr + sizeof(struct tcpa_event) + event->event_size) >=
152 tcg_eventlog_addr_limit))
153 return NULL;
154
155 return addr;
156}
157
158static void *tpm_bios_measurements_next(struct seq_file *m, void *v,
159 loff_t *pos)
160{
161 struct tcpa_event *event = v;
162
163 v += sizeof(struct tcpa_event) + event->event_size;
164
165 /* now check if current entry is valid */
166 if ((v + sizeof(struct tcpa_event)) >= tcg_eventlog_addr_limit)
167 return NULL;
168
169 event = v;
170
171 if (event->event_type == 0 && event->event_size == 0)
172 return NULL;
173
174 if ((event->event_type == 0 && event->event_size == 0) ||
175 ((v + sizeof(struct tcpa_event) + event->event_size) >=
176 tcg_eventlog_addr_limit))
177 return NULL;
178
179 (*pos)++;
180 return v;
181}
182
183static void tpm_bios_measurements_stop(struct seq_file *m, void *v)
184{
185}
186
187static int get_event_name(char *dest, struct tcpa_event *event,
188 unsigned char * event_entry)
189{
190 const char *name = "";
191 char data[40] = "";
192 int i, n_len = 0, d_len = 0;
193 u32 event_id, event_data_size;
194
195 switch(event->event_type) {
196 case PREBOOT:
197 case POST_CODE:
198 case UNUSED:
199 case NO_ACTION:
200 case SCRTM_CONTENTS:
201 case SCRTM_VERSION:
202 case CPU_MICROCODE:
203 case PLATFORM_CONFIG_FLAGS:
204 case TABLE_OF_DEVICES:
205 case COMPACT_HASH:
206 case IPL:
207 case IPL_PARTITION_DATA:
208 case NONHOST_CODE:
209 case NONHOST_CONFIG:
210 case NONHOST_INFO:
211 name = tcpa_event_type_strings[event->event_type];
212 n_len = strlen(name);
213 break;
214 case SEPARATOR:
215 case ACTION:
216 if (MAX_TEXT_EVENT > event->event_size) {
217 name = event_entry;
218 n_len = event->event_size;
219 }
220 break;
221 case EVENT_TAG:
222 event_id = be32_to_cpu(event_entry);
223 event_data_size = be32_to_cpu(&event_entry[4]);
224
225 /* ToDo Row data -> Base64 */
226
227 switch (event_id) {
228 case SMBIOS:
229 case BIS_CERT:
230 case CMOS:
231 case NVRAM:
232 case OPTION_ROM_EXEC:
233 case OPTION_ROM_CONFIG:
234 case OPTION_ROM_MICROCODE:
235 case S_CRTM_VERSION:
236 case S_CRTM_CONTENTS:
237 case POST_CONTENTS:
238 name = tcpa_pc_event_id_strings[event_id];
239 n_len = strlen(name);
240 break;
241 case POST_BIOS_ROM:
242 case ESCD:
243 name = tcpa_pc_event_id_strings[event_id];
244 n_len = strlen(name);
245 for (i = 0; i < 20; i++)
246 d_len += sprintf(data, "%02x",
247 event_entry[8 + i]);
248 break;
249 default:
250 break;
251 }
252 default:
253 break;
254 }
255
256 return snprintf(dest, MAX_TEXT_EVENT, "[%.*s%.*s]",
257 n_len, name, d_len, data);
258
259}
260
261static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v)
262{
263
264 char *eventname;
265 char data[4];
266 u32 help;
267 int i, len;
268 struct tcpa_event *event = (struct tcpa_event *) v;
269 unsigned char *event_entry =
270 (unsigned char *) (v + sizeof(struct tcpa_event));
271
272 eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL);
273 if (!eventname) {
274 printk(KERN_ERR "%s: ERROR - No Memory for event name\n ",
275 __func__);
276 return -ENOMEM;
277 }
278
279 /* 1st: PCR used is in little-endian format (4 bytes) */
280 help = le32_to_cpu(event->pcr_index);
281 memcpy(data, &help, 4);
282 for (i = 0; i < 4; i++)
283 seq_putc(m, data[i]);
284
285 /* 2nd: SHA1 (20 bytes) */
286 for (i = 0; i < 20; i++)
287 seq_putc(m, event->pcr_value[i]);
288
289 /* 3rd: event type identifier (4 bytes) */
290 help = le32_to_cpu(event->event_type);
291 memcpy(data, &help, 4);
292 for (i = 0; i < 4; i++)
293 seq_putc(m, data[i]);
294
295 len = 0;
296
297 len += get_event_name(eventname, event, event_entry);
298
299 /* 4th: filename <= 255 + \'0' delimiter */
300 if (len > TCG_EVENT_NAME_LEN_MAX)
301 len = TCG_EVENT_NAME_LEN_MAX;
302
303 for (i = 0; i < len; i++)
304 seq_putc(m, eventname[i]);
305
306 /* 5th: delimiter */
307 seq_putc(m, '\0');
308
309 return 0;
310}
311
312static int tpm_bios_measurements_release(struct inode *inode,
313 struct file *file)
314{
315 if (tcg_eventlog) {
316 kfree(tcg_eventlog);
317 tcg_eventlog = NULL;
318 }
319 return seq_release(inode, file);
320}
321
322static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v)
323{
324 int len = 0;
325 int i;
326 char *eventname;
327 struct tcpa_event *event = v;
328 unsigned char *event_entry =
329 (unsigned char *) (v + sizeof(struct tcpa_event));
330
331 eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL);
332 if (!eventname) {
333 printk(KERN_ERR "%s: ERROR - No Memory for event name\n ",
334 __func__);
335 return -EFAULT;
336 }
337
338 seq_printf(m, "%2d ", event->pcr_index);
339
340 /* 2nd: SHA1 */
341 for (i = 0; i < 20; i++)
342 seq_printf(m, "%02x", event->pcr_value[i]);
343
344 /* 3rd: event type identifier */
345 seq_printf(m, " %02x", event->event_type);
346
347 len += get_event_name(eventname, event, event_entry);
348
349 /* 4th: eventname <= max + \'0' delimiter */
350 seq_printf(m, " %s\n", eventname);
351
352 return 0;
353}
354
355static struct seq_operations tpm_ascii_b_measurments_seqops = {
356 .start = tpm_bios_measurements_start,
357 .next = tpm_bios_measurements_next,
358 .stop = tpm_bios_measurements_stop,
359 .show = tpm_ascii_bios_measurements_show,
360};
361
362static struct seq_operations tpm_binary_b_measurments_seqops = {
363 .start = tpm_bios_measurements_start,
364 .next = tpm_bios_measurements_next,
365 .stop = tpm_bios_measurements_stop,
366 .show = tpm_binary_bios_measurements_show,
367};
368
369/* read binary bios log */
370static int read_log(void)
371{
372 struct acpi_tcpa *buff;
373 acpi_status status;
374 void *virt;
375
376 if (tcg_eventlog != NULL) {
377 printk(KERN_ERR
378 "%s: ERROR - Eventlog already initialized\n",
379 __func__);
380 return -EFAULT;
381 }
382
383 /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
384 status = acpi_get_firmware_table(ACPI_TCPA_SIG, 1,
385 ACPI_LOGICAL_ADDRESSING,
386 (struct acpi_table_header **)
387 &buff);
388
389 if (ACPI_FAILURE(status)) {
390 printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n",
391 __func__);
392 return -EIO;
393 }
394
395 if (buff->log_max_len == 0) {
396 printk(KERN_ERR "%s: ERROR - TCPA log area empty\n",
397 __func__);
398 return -EIO;
399 }
400
401 /* malloc EventLog space */
402 tcg_eventlog = kmalloc(buff->log_max_len, GFP_KERNEL);
403 if (!tcg_eventlog) {
404 printk
405 ("%s: ERROR - Not enough Memory for BIOS measurements\n",
406 __func__);
407 return -ENOMEM;
408 }
409
410 tcg_eventlog_addr_limit = tcg_eventlog + buff->log_max_len;
411
412 acpi_os_map_memory(buff->log_start_addr, buff->log_max_len, &virt);
413
414 memcpy(tcg_eventlog, virt, buff->log_max_len);
415
416 acpi_os_unmap_memory(virt, buff->log_max_len);
417 return 0;
418}
419
420static int tpm_ascii_bios_measurements_open(struct inode *inode,
421 struct file *file)
422{
423 int err;
424
425 if ((err = read_log()))
426 return err;
427
428 /* now register seq file */
429 return seq_open(file, &tpm_ascii_b_measurments_seqops);
430}
431
432struct file_operations tpm_ascii_bios_measurements_ops = {
433 .open = tpm_ascii_bios_measurements_open,
434 .read = seq_read,
435 .llseek = seq_lseek,
436 .release = tpm_bios_measurements_release,
437};
438
439static int tpm_binary_bios_measurements_open(struct inode *inode,
440 struct file *file)
441{
442 int err;
443
444 if ((err = read_log()))
445 return err;
446
447 /* now register seq file */
448 return seq_open(file, &tpm_binary_b_measurments_seqops);
449}
450
451struct file_operations tpm_binary_bios_measurements_ops = {
452 .open = tpm_binary_bios_measurements_open,
453 .read = seq_read,
454 .llseek = seq_lseek,
455 .release = tpm_bios_measurements_release,
456};
457
458struct dentry **tpm_bios_log_setup(char *name)
459{
460 struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file;
461
462 tpm_dir = securityfs_create_dir(name, NULL);
463 if (!tpm_dir)
464 goto out;
465
466 bin_file =
467 securityfs_create_file("binary_bios_measurements",
468 S_IRUSR | S_IRGRP, tpm_dir, NULL,
469 &tpm_binary_bios_measurements_ops);
470 if (!bin_file)
471 goto out_tpm;
472
473 ascii_file =
474 securityfs_create_file("ascii_bios_measurements",
475 S_IRUSR | S_IRGRP, tpm_dir, NULL,
476 &tpm_ascii_bios_measurements_ops);
477 if (!ascii_file)
478 goto out_bin;
479
480 ret = kmalloc(3 * sizeof(struct dentry *), GFP_KERNEL);
481 if (!ret)
482 goto out_ascii;
483
484 ret[0] = ascii_file;
485 ret[1] = bin_file;
486 ret[2] = tpm_dir;
487
488 return ret;
489
490out_ascii:
491 securityfs_remove(ascii_file);
492out_bin:
493 securityfs_remove(bin_file);
494out_tpm:
495 securityfs_remove(tpm_dir);
496out:
497 return NULL;
498}
499EXPORT_SYMBOL_GPL(tpm_bios_log_setup);
500
501void tpm_bios_log_teardown(struct dentry **lst)
502{
503 int i;
504
505 for (i = 0; i < 3; i++)
506 securityfs_remove(lst[i]);
507}
508EXPORT_SYMBOL_GPL(tpm_bios_log_teardown);