1 /* Copyright 2011 The ChromiumOS Authors
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Stub implementations of utility functions which call their linux-specific
6 * equivalents.
7 */
8
9 #include <assert.h>
10 #include <stdint.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/time.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <time.h>
21 #include <unistd.h>
22
23 #include "2common.h"
24 #include "2sysincludes.h"
25 #include "tlcl.h"
26 #include "tlcl_internal.h"
27 #include "tss_constants.h"
28
29 #define TPM_DEVICE_PATH "/dev/tpm0"
30 /* Retry failed open()s for 5 seconds in 10ms polling intervals. */
31 #define OPEN_RETRY_DELAY_NS (10 * 1000 * 1000)
32 #define OPEN_RETRY_MAX_NUM 500
33 #define COMM_RETRY_MAX_NUM 3
34
35 /* TODO: these functions should pass errors back rather than returning void */
36 /* TODO: if the only callers to these are just wrappers, should just
37 * remove the wrappers and call us directly. */
38
39
40 /* The file descriptor for the TPM device.
41 */
42 static int tpm_fd = -1;
43 /* If the library should exit during an OS-level TPM failure.
44 */
45 static int exit_on_failure = 1;
46
try_exit(uint32_t result)47 static inline uint32_t try_exit(uint32_t result)
48 {
49 if (exit_on_failure)
50 exit(1);
51 return result;
52 }
53
54 /* Print |n| bytes from array |a| to stderr, with newlines.
55 */
DbgPrintBytes(const uint8_t * a,int n)56 __attribute__((unused)) static void DbgPrintBytes(const uint8_t* a, int n)
57 {
58 int i;
59 VB2_DEBUG_RAW("DEBUG: ");
60 for (i = 0; i < n; i++) {
61 if (i && i % 16 == 0)
62 VB2_DEBUG_RAW("\nDEBUG: ");
63 VB2_DEBUG_RAW("%02x ", a[i]);
64 }
65 VB2_DEBUG_RAW("\n");
66 }
67
68
69 /* Executes a command on the TPM.
70 */
TpmExecute(const uint8_t * in,const uint32_t in_len,uint8_t * out,uint32_t * pout_len)71 static uint32_t TpmExecute(const uint8_t *in, const uint32_t in_len,
72 uint8_t *out, uint32_t *pout_len)
73 {
74 uint8_t response[TPM_MAX_COMMAND_SIZE];
75 if (in_len <= 0) {
76 VB2_DEBUG("ERROR: invalid command length %d for command %#x\n",
77 in_len, in[9]);
78 return try_exit(TPM_E_INPUT_TOO_SMALL);
79 } else if (tpm_fd < 0) {
80 VB2_DEBUG("ERROR: the TPM device was not opened. "
81 "Forgot to call TlclLibInit?\n");
82 return try_exit(TPM_E_NO_DEVICE);
83 } else {
84 int n;
85 int retries = 0;
86 int first_errno = 0;
87
88 /* Write command. Retry in case of communication errors.
89 */
90 for ( ; retries < COMM_RETRY_MAX_NUM; ++retries) {
91 n = write(tpm_fd, in, in_len);
92 if (n >= 0) {
93 break;
94 }
95 if (retries == 0) {
96 first_errno = errno;
97 }
98 VB2_DEBUG("TPM: write attempt %d failed: %s\n",
99 retries + 1, strerror(errno));
100 }
101 if (n < 0) {
102 VB2_DEBUG("ERROR: write failure to TPM device: %s "
103 "(first error %d)\n",
104 strerror(errno), first_errno);
105 return try_exit(TPM_E_WRITE_FAILURE);
106 } else if (n != in_len) {
107 VB2_DEBUG("ERROR: bad write size to TPM device: "
108 "%d vs %u (%d retries, first error %d)\n",
109 n, in_len, retries, first_errno);
110 return try_exit(TPM_E_WRITE_FAILURE);
111 }
112
113 /* Read response. Retry in case of communication errors.
114 */
115 for (retries = 0, first_errno = 0;
116 retries < COMM_RETRY_MAX_NUM; ++retries) {
117 n = read(tpm_fd, response, sizeof(response));
118 if (n >= 0) {
119 break;
120 }
121 if (retries == 0) {
122 first_errno = errno;
123 }
124 VB2_DEBUG("TPM: read attempt %d failed: %s\n",
125 retries + 1, strerror(errno));
126 }
127 if (n == 0) {
128 VB2_DEBUG("ERROR: null read from TPM device\n");
129 return try_exit(TPM_E_READ_EMPTY);
130 } else if (n < 0) {
131 VB2_DEBUG("ERROR: read failure from TPM device: %s "
132 "(first error %d)\n",
133 strerror(errno), first_errno);
134 return try_exit(TPM_E_READ_FAILURE);
135 } else {
136 if (n > *pout_len) {
137 VB2_DEBUG("ERROR: TPM response too long for "
138 "output buffer\n");
139 return try_exit(TPM_E_RESPONSE_TOO_LARGE);
140 } else {
141 *pout_len = n;
142 memcpy(out, response, n);
143 }
144 }
145 }
146 return TPM_SUCCESS;
147 }
148
149 /* Gets the tag field of a TPM command.
150 */
151 __attribute__((unused))
TpmTag(const uint8_t * buffer)152 static inline int TpmTag(const uint8_t* buffer)
153 {
154 uint16_t tag;
155 FromTpmUint16(buffer, &tag);
156 return (int) tag;
157 }
158
159 /* Gets the size field of a TPM command.
160 */
161 __attribute__((unused))
TpmResponseSize(const uint8_t * buffer)162 static inline int TpmResponseSize(const uint8_t* buffer)
163 {
164 uint32_t size;
165 FromTpmUint32(buffer + sizeof(uint16_t), &size);
166 return (int) size;
167 }
168
vb2ex_tpm_init(void)169 vb2_error_t vb2ex_tpm_init(void)
170 {
171 char *no_exit = getenv("TPM_NO_EXIT");
172 if (no_exit)
173 exit_on_failure = !atoi(no_exit);
174 return vb2ex_tpm_open();
175 }
176
vb2ex_tpm_close(void)177 vb2_error_t vb2ex_tpm_close(void)
178 {
179 if (tpm_fd != -1) {
180 close(tpm_fd);
181 tpm_fd = -1;
182 }
183 return VB2_SUCCESS;
184 }
185
vb2ex_tpm_open(void)186 vb2_error_t vb2ex_tpm_open(void)
187 {
188 const char *device_path;
189 struct timespec delay;
190 int retries, saved_errno;
191
192 if (tpm_fd >= 0)
193 return VB2_SUCCESS; /* Already open */
194
195 device_path = getenv("TPM_DEVICE_PATH");
196 if (device_path == NULL) {
197 device_path = TPM_DEVICE_PATH;
198 }
199
200 /* Retry TPM opens on EBUSY failures. */
201 for (retries = 0; retries < OPEN_RETRY_MAX_NUM; ++ retries) {
202 errno = 0;
203 tpm_fd = open(device_path, O_RDWR | O_CLOEXEC);
204 saved_errno = errno;
205 if (tpm_fd >= 0)
206 return VB2_SUCCESS;
207 if (saved_errno != EBUSY)
208 break;
209
210 VB2_DEBUG("TPM: retrying %s: %s\n",
211 device_path, strerror(errno));
212
213 /* Stall until TPM comes back. */
214 delay.tv_sec = 0;
215 delay.tv_nsec = OPEN_RETRY_DELAY_NS;
216 nanosleep(&delay, NULL);
217 }
218 VB2_DEBUG("ERROR: TPM: Cannot open TPM device %s: %s\n",
219 device_path, strerror(saved_errno));
220 return try_exit(VB2_ERROR_UNKNOWN);
221 }
222
vb2ex_tpm_send_recv(const uint8_t * request,uint32_t request_length,uint8_t * response,uint32_t * response_length)223 uint32_t vb2ex_tpm_send_recv(const uint8_t* request, uint32_t request_length,
224 uint8_t* response, uint32_t* response_length)
225 {
226 /*
227 * In a real firmware implementation, this function should contain
228 * the equivalent API call for the firmware TPM driver which takes a
229 * raw sequence of bytes as input command and a pointer to the
230 * output buffer for putting in the results.
231 *
232 * For EFI firmwares, this can make use of the EFI TPM driver as
233 * follows (based on page 16, of TCG EFI Protocol Specs Version 1.20
234 * availaible from the TCG website):
235 *
236 * EFI_STATUS status;
237 * status = TcgProtocol->EFI_TCG_PASS_THROUGH_TO_TPM(
238 * TpmCommandSize(request),
239 * request,
240 * max_length,
241 * response);
242 * // Error checking depending on the value of the status above
243 */
244 uint32_t result;
245
246 #ifdef VBOOT_DEBUG
247 struct timeval before, after;
248 VB2_DEBUG("request (%d bytes):\n", request_length);
249 DbgPrintBytes(request, request_length);
250 gettimeofday(&before, NULL);
251 #endif
252
253 result = TpmExecute(request, request_length, response, response_length);
254 if (result != TPM_SUCCESS)
255 return result;
256
257 #ifdef VBOOT_DEBUG
258 gettimeofday(&after, NULL);
259 VB2_DEBUG("response (%d bytes):\n", *response_length);
260 DbgPrintBytes(response, *response_length);
261 VB2_DEBUG("execution time: %dms\n",
262 (int) ((after.tv_sec - before.tv_sec) * VB2_MSEC_PER_SEC +
263 (after.tv_usec - before.tv_usec) / VB2_USEC_PER_MSEC));
264 #endif
265
266 #ifndef NDEBUG
267 #ifndef TPM2_MODE
268 /* validity checks */
269 int tag = TpmTag(request);
270 int response_tag = TpmTag(response);
271 assert(
272 (tag == TPM_TAG_RQU_COMMAND &&
273 response_tag == TPM_TAG_RSP_COMMAND) ||
274 (tag == TPM_TAG_RQU_AUTH1_COMMAND &&
275 response_tag == TPM_TAG_RSP_AUTH1_COMMAND) ||
276 (tag == TPM_TAG_RQU_AUTH2_COMMAND &&
277 response_tag == TPM_TAG_RSP_AUTH2_COMMAND));
278 assert(*response_length == TpmResponseSize(response));
279 #endif
280 #endif
281
282 return TPM_SUCCESS;
283 }
284
vb2ex_tpm_get_random(uint8_t * buf,uint32_t length)285 vb2_error_t vb2ex_tpm_get_random(uint8_t *buf, uint32_t length)
286 {
287 static int urandom_fd = -1;
288 if (urandom_fd < 0) {
289 urandom_fd = open("/dev/urandom", O_RDONLY);
290 if (urandom_fd == -1) {
291 return VB2_ERROR_UNKNOWN;
292 }
293 }
294
295 if (length != read(urandom_fd, buf, length)) {
296 return VB2_ERROR_UNKNOWN;
297 }
298
299 return VB2_SUCCESS;
300 }
301