1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (c) 2017-2020, Linaro Limited
4 */
5
6 /* BINARY_PREFIX is expected by teec_trace.h */
7 #ifndef BINARY_PREFIX
8 #define BINARY_PREFIX "ckteec"
9 #endif
10
11 #include <errno.h>
12 #include <inttypes.h>
13 #include <pkcs11.h>
14 #include <pkcs11_ta.h>
15 #include <pthread.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/types.h>
19 #include <tee_client_api.h>
20 #include <teec_trace.h>
21 #include <unistd.h>
22
23 #include "ck_helpers.h"
24 #include "invoke_ta.h"
25 #include "local_utils.h"
26
27 struct ta_context {
28 pthread_mutex_t init_mutex;
29 bool initiated;
30 TEEC_Context context;
31 TEEC_Session session;
32 };
33
34 static struct ta_context ta_ctx = {
35 .init_mutex = PTHREAD_MUTEX_INITIALIZER,
36 };
37
ckteec_invoke_initiated(void)38 bool ckteec_invoke_initiated(void)
39 {
40 return ta_ctx.initiated;
41 }
42
ckteec_alloc_shm(size_t size,enum ckteec_shm_dir dir)43 TEEC_SharedMemory *ckteec_alloc_shm(size_t size, enum ckteec_shm_dir dir)
44 {
45 TEEC_SharedMemory *shm = NULL;
46
47 switch (dir) {
48 case CKTEEC_SHM_IN:
49 case CKTEEC_SHM_OUT:
50 case CKTEEC_SHM_INOUT:
51 break;
52 default:
53 return NULL;
54 }
55
56 shm = calloc(1, sizeof(TEEC_SharedMemory));
57 if (!shm)
58 return NULL;
59
60 shm->size = size;
61
62 if (dir == CKTEEC_SHM_IN || dir == CKTEEC_SHM_INOUT)
63 shm->flags |= TEEC_MEM_INPUT;
64 if (dir == CKTEEC_SHM_OUT || dir == CKTEEC_SHM_INOUT)
65 shm->flags |= TEEC_MEM_OUTPUT;
66
67 if (TEEC_AllocateSharedMemory(&ta_ctx.context, shm)) {
68 free(shm);
69 return NULL;
70 }
71
72 return shm;
73 }
74
ckteec_register_shm(void * buffer,size_t size,enum ckteec_shm_dir dir)75 TEEC_SharedMemory *ckteec_register_shm(void *buffer, size_t size,
76 enum ckteec_shm_dir dir)
77 {
78 TEEC_SharedMemory *shm = NULL;
79
80 switch (dir) {
81 case CKTEEC_SHM_IN:
82 case CKTEEC_SHM_OUT:
83 case CKTEEC_SHM_INOUT:
84 break;
85 default:
86 return NULL;
87 }
88
89 shm = calloc(1, sizeof(TEEC_SharedMemory));
90 if (!shm)
91 return NULL;
92
93 shm->buffer = buffer;
94 shm->size = size;
95
96 if (dir == CKTEEC_SHM_IN || dir == CKTEEC_SHM_INOUT)
97 shm->flags |= TEEC_MEM_INPUT;
98 if (dir == CKTEEC_SHM_OUT || dir == CKTEEC_SHM_INOUT)
99 shm->flags |= TEEC_MEM_OUTPUT;
100
101 if (TEEC_RegisterSharedMemory(&ta_ctx.context, shm)) {
102 free(shm);
103 return NULL;
104 }
105
106 return shm;
107 }
108
ckteec_free_shm(TEEC_SharedMemory * shm)109 void ckteec_free_shm(TEEC_SharedMemory *shm)
110 {
111 TEEC_ReleaseSharedMemory(shm);
112 free(shm);
113 }
114
is_output_shm(TEEC_SharedMemory * shm)115 static bool is_output_shm(TEEC_SharedMemory *shm)
116 {
117 return shm && (shm->flags & TEEC_MEM_OUTPUT);
118 }
119
ckteec_invoke_ta(unsigned long cmd,TEEC_SharedMemory * ctrl,TEEC_SharedMemory * io1,TEEC_SharedMemory * io2,size_t * out2_size,TEEC_SharedMemory * io3,size_t * out3_size)120 CK_RV ckteec_invoke_ta(unsigned long cmd, TEEC_SharedMemory *ctrl,
121 TEEC_SharedMemory *io1,
122 TEEC_SharedMemory *io2, size_t *out2_size,
123 TEEC_SharedMemory *io3, size_t *out3_size)
124 {
125 uint32_t command = (uint32_t)cmd;
126 TEEC_Operation op;
127 uint32_t origin = 0;
128 TEEC_Result res = TEEC_ERROR_GENERIC;
129 uint32_t ta_rc = PKCS11_CKR_GENERAL_ERROR;
130
131 if ((is_output_shm(io2) && !out2_size) ||
132 (is_output_shm(io3) && !out3_size))
133 return CKR_ARGUMENTS_BAD;
134
135 memset(&op, 0, sizeof(op));
136
137 if (ctrl && !(ctrl->flags & TEEC_MEM_INPUT &&
138 ctrl->flags & TEEC_MEM_OUTPUT))
139 return CKR_ARGUMENTS_BAD;
140
141 if (ctrl) {
142 op.paramTypes |= TEEC_PARAM_TYPES(TEEC_MEMREF_WHOLE, 0, 0, 0);
143 op.params[0].memref.parent = ctrl;
144 } else {
145 /* TA mandates param#0 as in/out memref for output status */
146 op.paramTypes |= TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INOUT,
147 0, 0, 0);
148 op.params[0].tmpref.buffer = &ta_rc;
149 op.params[0].tmpref.size = sizeof(ta_rc);
150 }
151
152 if (io1) {
153 op.paramTypes |= TEEC_PARAM_TYPES(0, TEEC_MEMREF_WHOLE, 0, 0);
154 op.params[1].memref.parent = io1;
155 }
156
157 if (io2) {
158 op.paramTypes |= TEEC_PARAM_TYPES(0, 0, TEEC_MEMREF_WHOLE, 0);
159 op.params[2].memref.parent = io2;
160 }
161
162 if (io3) {
163 op.paramTypes |= TEEC_PARAM_TYPES(0, 0, 0, TEEC_MEMREF_WHOLE);
164 op.params[3].memref.parent = io3;
165 }
166
167 res = TEEC_InvokeCommand(&ta_ctx.session, command, &op, &origin);
168 switch (res) {
169 case TEEC_SUCCESS:
170 /* Get PKCS11 TA return value from ctrl buffer */
171 if (ctrl) {
172 if (op.params[0].memref.size == sizeof(ta_rc))
173 memcpy(&ta_rc, ctrl->buffer, sizeof(ta_rc));
174 } else {
175 if (op.params[0].tmpref.size != sizeof(ta_rc))
176 ta_rc = PKCS11_CKR_GENERAL_ERROR;
177 }
178 break;
179 case TEEC_ERROR_SHORT_BUFFER:
180 ta_rc = CKR_BUFFER_TOO_SMALL;
181 break;
182 case TEEC_ERROR_OUT_OF_MEMORY:
183 return CKR_DEVICE_MEMORY;
184 default:
185 return CKR_GENERAL_ERROR;
186 }
187
188 if (ta_rc == CKR_OK || ta_rc == CKR_BUFFER_TOO_SMALL) {
189 if (is_output_shm(io2))
190 *out2_size = op.params[2].memref.size;
191 if (is_output_shm(io3))
192 *out3_size = op.params[3].memref.size;
193 }
194
195 return ta_rc;
196 }
197
ping_ta(void)198 static CK_RV ping_ta(void)
199 {
200 TEEC_Operation op = { 0 };
201 uint32_t origin = 0;
202 TEEC_Result res = TEEC_SUCCESS;
203 uint32_t ta_version[3] = { 0 };
204 uint32_t status = 0;
205
206 memset(&op, 0, sizeof(op));
207 op.params[0].tmpref.buffer = &status;
208 op.params[0].tmpref.size = sizeof(status);
209 op.params[2].tmpref.buffer = ta_version;
210 op.params[2].tmpref.size = sizeof(ta_version);
211 op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INOUT, TEEC_NONE,
212 TEEC_MEMREF_TEMP_OUTPUT, TEEC_NONE);
213
214 res = TEEC_InvokeCommand(&ta_ctx.session, PKCS11_CMD_PING, &op,
215 &origin);
216
217 if (res != TEEC_SUCCESS ||
218 origin != TEEC_ORIGIN_TRUSTED_APP ||
219 op.params[0].tmpref.size != sizeof(status) ||
220 status != PKCS11_CKR_OK)
221 return CKR_DEVICE_ERROR;
222
223 if (ta_version[0] != PKCS11_TA_VERSION_MAJOR &&
224 ta_version[1] > PKCS11_TA_VERSION_MINOR) {
225 EMSG("PKCS11 TA version mismatch: %"PRIu32".%"PRIu32".%"PRIu32,
226 ta_version[0], ta_version[1], ta_version[2]);
227
228 return CKR_DEVICE_ERROR;
229 }
230
231 DMSG("PKCS11 TA version %"PRIu32".%"PRIu32".%"PRIu32,
232 ta_version[0], ta_version[1], ta_version[2]);
233
234 return CKR_OK;
235 }
236
ckteec_invoke_init(void)237 CK_RV ckteec_invoke_init(void)
238 {
239 TEEC_UUID uuid = PKCS11_TA_UUID;
240 uint32_t origin = 0;
241 TEEC_Result res = TEEC_SUCCESS;
242 CK_RV rv = CKR_CRYPTOKI_ALREADY_INITIALIZED;
243 const char *login_type_env = NULL;
244 const char *login_gid_env = NULL;
245 uint32_t login_method = TEEC_LOGIN_PUBLIC;
246 void *login_data = NULL;
247 gid_t login_gid = 0;
248 unsigned long tmpconv = 0;
249 char *endp = NULL;
250 int e = 0;
251
252 login_type_env = getenv("CKTEEC_LOGIN_TYPE");
253
254 if (login_type_env) {
255 if (strcmp(login_type_env, "public") == 0) {
256 login_method = TEEC_LOGIN_PUBLIC;
257 } else if (strcmp(login_type_env, "user") == 0) {
258 login_method = TEEC_LOGIN_USER;
259 } else if (strcmp(login_type_env, "group") == 0) {
260 login_gid_env = getenv("CKTEEC_LOGIN_GID");
261 if (!login_gid_env || !strlen(login_gid_env)) {
262 EMSG("missing CKTEEC_LOGIN_GID");
263 rv = CKR_ARGUMENTS_BAD;
264 goto out;
265 }
266
267 login_method = TEEC_LOGIN_GROUP;
268 tmpconv = strtoul(login_gid_env, &endp, 10);
269 if (errno == ERANGE || tmpconv > (gid_t)-1 ||
270 (login_gid_env + strlen(login_gid_env) != endp)) {
271 EMSG("failed to convert CKTEEC_LOGIN_GID");
272 rv = CKR_ARGUMENTS_BAD;
273 goto out;
274 }
275
276 login_gid = (gid_t)tmpconv;
277 login_data = &login_gid;
278 } else {
279 EMSG("invalid value for CKTEEC_LOGIN_TYPE");
280 rv = CKR_ARGUMENTS_BAD;
281 goto out;
282 }
283 }
284
285 e = pthread_mutex_lock(&ta_ctx.init_mutex);
286 if (e)
287 return CKR_CANT_LOCK;
288
289 if (ta_ctx.initiated) {
290 rv = CKR_CRYPTOKI_ALREADY_INITIALIZED;
291 goto out;
292 }
293
294 res = TEEC_InitializeContext(NULL, &ta_ctx.context);
295 if (res != TEEC_SUCCESS) {
296 EMSG("TEEC init context failed\n");
297 rv = CKR_DEVICE_ERROR;
298 goto out;
299 }
300
301 res = TEEC_OpenSession(&ta_ctx.context, &ta_ctx.session, &uuid,
302 login_method, login_data, NULL, &origin);
303 if (res != TEEC_SUCCESS) {
304 EMSG("TEEC open session failed %x from %d\n", res, origin);
305 TEEC_FinalizeContext(&ta_ctx.context);
306 rv = CKR_DEVICE_ERROR;
307 goto out;
308 }
309
310 rv = ping_ta();
311
312 if (rv == CKR_OK) {
313 ta_ctx.initiated = true;
314 } else {
315 TEEC_CloseSession(&ta_ctx.session);
316 TEEC_FinalizeContext(&ta_ctx.context);
317 }
318
319 out:
320 e = pthread_mutex_unlock(&ta_ctx.init_mutex);
321 if (e) {
322 EMSG("pthread_mutex_unlock: %s", strerror(e));
323 EMSG("terminating...");
324 exit(EXIT_FAILURE);
325 }
326
327 return rv;
328 }
329
ckteec_invoke_terminate(void)330 CK_RV ckteec_invoke_terminate(void)
331 {
332 CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
333 int e = 0;
334
335 e = pthread_mutex_lock(&ta_ctx.init_mutex);
336 if (e) {
337 EMSG("pthread_mutex_lock: %s", strerror(e));
338 EMSG("terminating...");
339 exit(EXIT_FAILURE);
340 }
341
342 if (!ta_ctx.initiated)
343 goto out;
344
345 ta_ctx.initiated = false;
346 TEEC_CloseSession(&ta_ctx.session);
347 TEEC_FinalizeContext(&ta_ctx.context);
348
349 rv = CKR_OK;
350
351 out:
352 e = pthread_mutex_unlock(&ta_ctx.init_mutex);
353 if (e) {
354 EMSG("pthread_mutex_unlock: %s", strerror(e));
355 EMSG("terminating...");
356 exit(EXIT_FAILURE);
357 }
358
359 return rv;
360 }
361