1 /* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
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 <stdint.h>
10 
11 #define _STUB_IMPLEMENTATION_
12 #include "tlcl.h"
13 #include "tlcl_internal.h"
14 #include "utility.h"
15 #include "vboot_api.h"
16 
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <time.h>
27 #include <unistd.h>
28 
29 
30 #define TPM_DEVICE_PATH "/dev/tpm0"
31 /* Retry failed open()s for 5 seconds in 10ms polling intervals. */
32 #define OPEN_RETRY_DELAY_NS (10 * 1000 * 1000)
33 #define OPEN_RETRY_MAX_NUM  500
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 
47 /* Similar to VbExError, only handle the non-exit case.
48  */
DoError(VbError_t result,const char * format,...)49 static VbError_t DoError(VbError_t result, const char* format, ...) {
50   va_list ap;
51   va_start(ap, format);
52   fprintf(stderr, "ERROR: ");
53   vfprintf(stderr, format, ap);
54   va_end(ap);
55   if (exit_on_failure)
56     exit(1);
57   return result;
58 }
59 
60 
61 /* Print |n| bytes from array |a|, with newlines.
62  */
PrintBytes(const uint8_t * a,int n)63 __attribute__((unused)) static void PrintBytes(const uint8_t* a, int n) {
64   int i;
65   for (i = 0; i < n; i++) {
66     VBDEBUG(("%02x ", a[i]));
67     if ((i + 1) % 16 == 0) {
68       VBDEBUG(("\n"));
69     }
70   }
71   if (i % 16 != 0) {
72     VBDEBUG(("\n"));
73   }
74 }
75 
76 
77 /* Executes a command on the TPM.
78  */
TpmExecute(const uint8_t * in,const uint32_t in_len,uint8_t * out,uint32_t * pout_len)79 static VbError_t TpmExecute(const uint8_t *in, const uint32_t in_len,
80                 uint8_t *out, uint32_t *pout_len) {
81   uint8_t response[TPM_MAX_COMMAND_SIZE];
82   if (in_len <= 0) {
83     return DoError(TPM_E_INPUT_TOO_SMALL,
84                    "invalid command length %d for command 0x%x\n",
85                    in_len, in[9]);
86   } else if (tpm_fd < 0) {
87     return DoError(TPM_E_NO_DEVICE,
88                    "the TPM device was not opened.  " \
89                    "Forgot to call TlclLibInit?\n");
90   } else {
91     int n = write(tpm_fd, in, in_len);
92     if (n != in_len) {
93       return DoError(TPM_E_WRITE_FAILURE,
94                      "write failure to TPM device: %s\n", strerror(errno));
95     }
96     n = read(tpm_fd, response, sizeof(response));
97     if (n == 0) {
98       return DoError(TPM_E_READ_EMPTY, "null read from TPM device\n");
99     } else if (n < 0) {
100       return DoError(TPM_E_READ_FAILURE, "read failure from TPM device: %s\n",
101                      strerror(errno));
102     } else {
103       if (n > *pout_len) {
104         return DoError(TPM_E_RESPONSE_TOO_LARGE,
105                        "TPM response too long for output buffer\n");
106       } else {
107         *pout_len = n;
108         Memcpy(out, response, n);
109       }
110     }
111   }
112   return VBERROR_SUCCESS;
113 }
114 
115 
116 /* Gets the tag field of a TPM command.
117  */
118 __attribute__((unused))
TpmTag(const uint8_t * buffer)119 static inline int TpmTag(const uint8_t* buffer) {
120   uint16_t tag;
121   FromTpmUint16(buffer, &tag);
122   return (int) tag;
123 }
124 
125 
126 /* Gets the size field of a TPM command.
127  */
128 __attribute__((unused))
TpmResponseSize(const uint8_t * buffer)129 static inline int TpmResponseSize(const uint8_t* buffer) {
130   uint32_t size;
131   FromTpmUint32(buffer + sizeof(uint16_t), &size);
132   return (int) size;
133 }
134 
135 
VbExTpmInit(void)136 VbError_t VbExTpmInit(void) {
137   char *no_exit = getenv("TPM_NO_EXIT");
138   if (no_exit)
139     exit_on_failure = !atoi(no_exit);
140   return VbExTpmOpen();
141 }
142 
143 
VbExTpmClose(void)144 VbError_t VbExTpmClose(void) {
145   if (tpm_fd != -1) {
146     close(tpm_fd);
147     tpm_fd = -1;
148   }
149   return VBERROR_SUCCESS;
150 }
151 
152 
VbExTpmOpen(void)153 VbError_t VbExTpmOpen(void) {
154   char* device_path;
155   struct timespec delay;
156   int retries, saved_errno;
157 
158   if (tpm_fd >= 0)
159     return VBERROR_SUCCESS;  /* Already open */
160 
161   device_path = getenv("TPM_DEVICE_PATH");
162   if (device_path == NULL) {
163     device_path = TPM_DEVICE_PATH;
164   }
165 
166   /* Retry TPM opens on EBUSY failures. */
167   for (retries = 0; retries < OPEN_RETRY_MAX_NUM; ++ retries) {
168     errno = 0;
169     tpm_fd = open(device_path, O_RDWR);
170     saved_errno = errno;
171     if (tpm_fd >= 0)
172       return VBERROR_SUCCESS;
173     if (saved_errno != EBUSY)
174       break;
175 
176     VBDEBUG(("TPM: retrying %s: %s\n", device_path, strerror(errno)));
177 
178      /* Stall until TPM comes back. */
179      delay.tv_sec = 0;
180      delay.tv_nsec = OPEN_RETRY_DELAY_NS;
181      nanosleep(&delay, NULL);
182   }
183   return DoError(TPM_E_NO_DEVICE, "TPM: Cannot open TPM device %s: %s\n",
184                  device_path, strerror(saved_errno));
185 }
186 
187 
VbExTpmSendReceive(const uint8_t * request,uint32_t request_length,uint8_t * response,uint32_t * response_length)188 VbError_t VbExTpmSendReceive(const uint8_t* request, uint32_t request_length,
189                              uint8_t* response, uint32_t* response_length) {
190   /*
191    * In a real firmware implementation, this function should contain
192    * the equivalent API call for the firmware TPM driver which takes a
193    * raw sequence of bytes as input command and a pointer to the
194    * output buffer for putting in the results.
195    *
196    * For EFI firmwares, this can make use of the EFI TPM driver as
197    * follows (based on page 16, of TCG EFI Protocol Specs Version 1.20
198    * availaible from the TCG website):
199    *
200    * EFI_STATUS status;
201    * status = TcgProtocol->EFI_TCG_PASS_THROUGH_TO_TPM(TpmCommandSize(request),
202    *                                                   request,
203    *                                                   max_length,
204    *                                                   response);
205    * // Error checking depending on the value of the status above
206    */
207 #ifndef NDEBUG
208   int tag, response_tag;
209 #endif
210   VbError_t result;
211 
212   struct timeval before, after;
213   gettimeofday(&before, NULL);
214   result = TpmExecute(request, request_length, response, response_length);
215   if (result != VBERROR_SUCCESS)
216     return result;
217   gettimeofday(&after, NULL);
218 
219 #ifdef VBOOT_DEBUG
220   {
221     int x = request_length;
222     int y = *response_length;
223     VBDEBUG(("request (%d bytes): ", x));
224     PrintBytes(request, 10);
225     PrintBytes(request + 10, x - 10);
226     VBDEBUG(("response (%d bytes): ", y));
227     PrintBytes(response, 10);
228     PrintBytes(response + 10, y - 10);
229     VBDEBUG(("execution time: %dms\n",
230             (int) ((after.tv_sec - before.tv_sec) * 1000 +
231                    (after.tv_usec - before.tv_usec) / 1000)));
232   }
233 #endif
234 
235 #ifndef NDEBUG
236   /* sanity checks */
237   tag = TpmTag(request);
238   response_tag = TpmTag(response);
239   assert(
240     (tag == TPM_TAG_RQU_COMMAND &&
241      response_tag == TPM_TAG_RSP_COMMAND) ||
242     (tag == TPM_TAG_RQU_AUTH1_COMMAND &&
243      response_tag == TPM_TAG_RSP_AUTH1_COMMAND) ||
244     (tag == TPM_TAG_RQU_AUTH2_COMMAND &&
245      response_tag == TPM_TAG_RSP_AUTH2_COMMAND));
246   assert(*response_length == TpmResponseSize(response));
247 #endif
248 
249   return VBERROR_SUCCESS;
250 }
251