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