1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2024 Hans Rosenfeld
5 * Author: Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
6 */
7
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <sys/un.h>
11
12 #include <err.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #ifdef __FreeBSD__
16 #include <malloc_np.h>
17 #else
18 #include <sys/vmm.h>
19 #endif
20 #include <stdlib.h>
21 #include <string.h>
22 #include <strings.h>
23 #include <unistd.h>
24
25 #include "config.h"
26 #include "tpm_device.h"
27 #include "tpm_emul.h"
28
29 struct tpm_swtpm {
30 int fd;
31 };
32
33 struct tpm_resp_hdr {
34 uint16_t tag;
35 uint32_t len;
36 uint32_t errcode;
37 } __packed;
38
39 static int
tpm_swtpm_init(void ** sc,nvlist_t * nvl)40 tpm_swtpm_init(void **sc, nvlist_t *nvl)
41 {
42 struct tpm_swtpm *tpm;
43 const char *path;
44 struct sockaddr_un tpm_addr;
45
46 tpm = calloc(1, sizeof (struct tpm_swtpm));
47 if (tpm == NULL) {
48 warnx("%s: failed to allocate tpm_swtpm", __func__);
49 return (ENOMEM);
50 }
51
52 path = get_config_value_node(nvl, "path");
53 if (path == NULL) {
54 warnx("%s: no socket path specified", __func__);
55 return (ENOENT);
56 }
57
58 tpm->fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
59 if (tpm->fd < 0) {
60 warnx("%s: unable to open tpm socket", __func__);
61 return (ENOENT);
62 }
63
64 bzero(&tpm_addr, sizeof (tpm_addr));
65 tpm_addr.sun_family = AF_UNIX;
66 strlcpy(tpm_addr.sun_path, path, sizeof (tpm_addr.sun_path) - 1);
67
68 if (connect(tpm->fd, (struct sockaddr *)&tpm_addr, sizeof (tpm_addr)) ==
69 -1) {
70 warnx("%s: unable to connect to tpm socket \"%s\"", __func__,
71 path);
72 return (ENOENT);
73 }
74
75 *sc = tpm;
76
77 return (0);
78 }
79
80 static int
tpm_swtpm_execute_cmd(void * sc,void * cmd,uint32_t cmd_size,void * rsp,uint32_t rsp_size)81 tpm_swtpm_execute_cmd(void *sc, void *cmd, uint32_t cmd_size, void *rsp,
82 uint32_t rsp_size)
83 {
84 struct tpm_swtpm *tpm;
85 ssize_t len;
86
87 if (rsp_size < (ssize_t)sizeof(struct tpm_resp_hdr)) {
88 warn("%s: rsp_size of %u is too small", __func__, rsp_size);
89 return (EINVAL);
90 }
91
92 tpm = sc;
93
94 #ifdef __FreeBSD__
95 len = send(tpm->fd, cmd, cmd_size, MSG_NOSIGNAL|MSG_DONTWAIT);
96 if (len == -1)
97 err(1, "%s: cmd send failed, is swtpm running?", __func__);
98 if (len != cmd_size) {
99 warn("%s: cmd write failed (bytes written: %zd / %d)", __func__,
100 len, cmd_size);
101 return (EFAULT);
102 }
103
104 len = recv(tpm->fd, rsp, rsp_size, 0);
105 if (len == -1)
106 err(1, "%s: rsp recv failed, is swtpm running?", __func__);
107 if (len < (ssize_t)sizeof(struct tpm_resp_hdr)) {
108 warn("%s: rsp read failed (bytes read: %zd / %d)", __func__,
109 len, rsp_size);
110 return (EFAULT);
111 }
112 #else
113 while (cmd_size > 0) {
114 len = send(tpm->fd, cmd, cmd_size, MSG_NOSIGNAL|MSG_DONTWAIT);
115 if (len == -1) {
116 if (errno == EINTR)
117 continue;
118 err(1, "%s: cmd send failed, is swtpm running?",
119 __func__);
120 }
121 cmd += len;
122 cmd_size -= len;
123 }
124
125 size_t buflen = rsp_size;
126 size_t rcvd = 0;
127 while (buflen > 0 && rcvd < sizeof (struct tpm_resp_hdr)) {
128 len = recv(tpm->fd, rsp, buflen, 0);
129 if (len == -1) {
130 if (errno == EINTR)
131 continue;
132 err(1, "%s: rsp recv failed, is swtpm running?",
133 __func__);
134 }
135 if (len == 0)
136 break;
137 rsp += len;
138 buflen -= len;
139 rcvd += len;
140 }
141 #endif
142
143 return (0);
144 }
145
146 static void
tpm_swtpm_deinit(void * sc)147 tpm_swtpm_deinit(void *sc)
148 {
149 struct tpm_swtpm *tpm;
150
151 tpm = sc;
152 if (tpm == NULL)
153 return;
154
155 if (tpm->fd >= 0)
156 close(tpm->fd);
157
158 free(tpm);
159 }
160
161 static const struct tpm_emul tpm_emul_swtpm = {
162 .name = "swtpm",
163 .init = tpm_swtpm_init,
164 .deinit = tpm_swtpm_deinit,
165 .execute_cmd = tpm_swtpm_execute_cmd,
166 };
167 TPM_EMUL_SET(tpm_emul_swtpm);
168