xref: /optee_os/ta/pkcs11/src/object.c (revision a1d5c81f8834a9d2c6f4372cce2e59e70e709121)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2017-2020, Linaro Limited
4  */
5 
6 #include <assert.h>
7 #include <inttypes.h>
8 #include <string_ext.h>
9 #include <tee_internal_api.h>
10 #include <tee_internal_api_extensions.h>
11 
12 #include "attributes.h"
13 #include "handle.h"
14 #include "object.h"
15 #include "pkcs11_attributes.h"
16 #include "pkcs11_helpers.h"
17 #include "pkcs11_token.h"
18 #include "sanitize_object.h"
19 #include "serializer.h"
20 
21 struct pkcs11_object *pkcs11_handle2object(uint32_t handle,
22 					   struct pkcs11_session *session)
23 {
24 	return handle_lookup(&session->object_handle_db, handle);
25 }
26 
27 uint32_t pkcs11_object2handle(struct pkcs11_object *obj,
28 			      struct pkcs11_session *session)
29 {
30 	return handle_lookup_handle(&session->object_handle_db, obj);
31 }
32 
33 /* Currently handle pkcs11 sessions and tokens */
34 
35 static struct object_list *get_session_objects(void *session)
36 {
37 	/* Currently supporting only pkcs11 session */
38 	struct pkcs11_session *ck_session = session;
39 
40 	return pkcs11_get_session_objects(ck_session);
41 }
42 
43 static struct ck_token *get_session_token(void *session)
44 {
45 	struct pkcs11_session *ck_session = session;
46 
47 	return pkcs11_session2token(ck_session);
48 }
49 
50 /* Release resources of a non-persistent object */
51 static void cleanup_volatile_obj_ref(struct pkcs11_object *obj)
52 {
53 	if (!obj)
54 		return;
55 
56 	if (obj->key_handle != TEE_HANDLE_NULL)
57 		TEE_FreeTransientObject(obj->key_handle);
58 
59 	if (obj->attribs_hdl != TEE_HANDLE_NULL)
60 		TEE_CloseObject(obj->attribs_hdl);
61 
62 	TEE_Free(obj->attributes);
63 	TEE_Free(obj->uuid);
64 	TEE_Free(obj);
65 }
66 
67 /* Release resources of a persistent object including volatile resources */
68 static void cleanup_persistent_object(struct pkcs11_object *obj,
69 				      struct ck_token *token)
70 {
71 	TEE_Result res = TEE_SUCCESS;
72 
73 	if (!obj)
74 		return;
75 
76 	/* Open handle with write properties to destroy the object */
77 	if (obj->attribs_hdl != TEE_HANDLE_NULL)
78 		TEE_CloseObject(obj->attribs_hdl);
79 
80 	res = TEE_OpenPersistentObject(TEE_STORAGE_PRIVATE,
81 				       obj->uuid, sizeof(TEE_UUID),
82 				       TEE_DATA_FLAG_ACCESS_WRITE_META,
83 				       &obj->attribs_hdl);
84 	if (!res)
85 		TEE_CloseAndDeletePersistentObject1(obj->attribs_hdl);
86 
87 	obj->attribs_hdl = TEE_HANDLE_NULL;
88 	destroy_object_uuid(token, obj);
89 
90 	LIST_REMOVE(obj, link);
91 
92 	cleanup_volatile_obj_ref(obj);
93 }
94 
95 /*
96  * destroy_object - destroy an PKCS11 TA object
97  *
98  * @session - session requesting object destruction
99  * @obj - reference to the PKCS11 TA object
100  * @session_only - true if only session object shall be destroyed
101  */
102 void destroy_object(struct pkcs11_session *session, struct pkcs11_object *obj,
103 		    bool session_only)
104 {
105 #ifdef DEBUG
106 	trace_attributes("[destroy]", obj->attributes);
107 	if (obj->uuid)
108 		MSG_RAW("[destroy] obj uuid %pUl", (void *)obj->uuid);
109 #endif
110 
111 	/*
112 	 * Remove from session list only if it was published.
113 	 *
114 	 * This depends on obj->link.le_prev always pointing on the
115 	 * link.le_next element in the previous object in the list even if
116 	 * there's only a single object in the list. In the first object in
117 	 * the list obj->link.le_prev instead points to lh_first in the
118 	 * list head. If list implementation is changed we need to revisit
119 	 * this.
120 	 */
121 	if (obj->link.le_next || obj->link.le_prev)
122 		LIST_REMOVE(obj, link);
123 
124 	if (session_only) {
125 		/* Destroy object due to session closure */
126 		handle_put(&session->object_handle_db,
127 			   pkcs11_object2handle(obj, session));
128 		cleanup_volatile_obj_ref(obj);
129 
130 		return;
131 	}
132 
133 	/* Destroy target object (persistent or not) */
134 	if (get_bool(obj->attributes, PKCS11_CKA_TOKEN)) {
135 		assert(obj->uuid);
136 		/* Try twice otherwise panic! */
137 		if (unregister_persistent_object(session->token, obj->uuid) &&
138 		    unregister_persistent_object(session->token, obj->uuid))
139 			TEE_Panic(0);
140 
141 		handle_put(&session->object_handle_db,
142 			   pkcs11_object2handle(obj, session));
143 		cleanup_persistent_object(obj, session->token);
144 	} else {
145 		handle_put(&session->object_handle_db,
146 			   pkcs11_object2handle(obj, session));
147 		cleanup_volatile_obj_ref(obj);
148 	}
149 }
150 
151 static struct pkcs11_object *create_obj_instance(struct obj_attrs *head)
152 {
153 	struct pkcs11_object *obj = NULL;
154 
155 	obj = TEE_Malloc(sizeof(struct pkcs11_object), TEE_MALLOC_FILL_ZERO);
156 	if (!obj)
157 		return NULL;
158 
159 	obj->key_handle = TEE_HANDLE_NULL;
160 	obj->attribs_hdl = TEE_HANDLE_NULL;
161 	obj->attributes = head;
162 
163 	return obj;
164 }
165 
166 struct pkcs11_object *create_token_object(struct obj_attrs *head,
167 					  TEE_UUID *uuid)
168 {
169 	struct pkcs11_object *obj = create_obj_instance(head);
170 
171 	if (obj)
172 		obj->uuid = uuid;
173 
174 	return obj;
175 }
176 
177 /*
178  * create_object - create an PKCS11 TA object from its attributes and value
179  *
180  * @sess - session requesting object creation
181  * @head - reference to serialized attributes
182  * @out_handle - generated handle for the created object
183  */
184 enum pkcs11_rc create_object(void *sess, struct obj_attrs *head,
185 			     uint32_t *out_handle)
186 {
187 	enum pkcs11_rc rc = 0;
188 	struct pkcs11_object *obj = NULL;
189 	struct pkcs11_session *session = (struct pkcs11_session *)sess;
190 	uint32_t obj_handle = 0;
191 
192 #ifdef DEBUG
193 	trace_attributes("[create]", head);
194 #endif
195 
196 	/*
197 	 * We do not check the key attributes. At this point, key attributes
198 	 * are expected consistent and reliable.
199 	 */
200 
201 	obj = create_obj_instance(head);
202 	if (!obj)
203 		return PKCS11_CKR_DEVICE_MEMORY;
204 
205 	/* Create a handle for the object in the session database */
206 	obj_handle = handle_get(&session->object_handle_db, obj);
207 	if (!obj_handle) {
208 		rc = PKCS11_CKR_DEVICE_MEMORY;
209 		goto err;
210 	}
211 
212 	if (get_bool(obj->attributes, PKCS11_CKA_TOKEN)) {
213 		TEE_Result res = TEE_SUCCESS;
214 
215 		/*
216 		 * Get an ID for the persistent object
217 		 * Create the file
218 		 * Register the object in the persistent database
219 		 * (move the full sequence to persisent_db.c?)
220 		 */
221 		size_t size = sizeof(struct obj_attrs) +
222 			      obj->attributes->attrs_size;
223 		uint32_t tee_obj_flags = TEE_DATA_FLAG_ACCESS_READ |
224 					 TEE_DATA_FLAG_ACCESS_WRITE |
225 					 TEE_DATA_FLAG_ACCESS_WRITE_META;
226 
227 		rc = create_object_uuid(get_session_token(session), obj);
228 		if (rc)
229 			goto err;
230 
231 		res = TEE_CreatePersistentObject(TEE_STORAGE_PRIVATE,
232 						 obj->uuid, sizeof(TEE_UUID),
233 						 tee_obj_flags,
234 						 TEE_HANDLE_NULL,
235 						 obj->attributes, size,
236 						 &obj->attribs_hdl);
237 		if (res) {
238 			rc = tee2pkcs_error(res);
239 			goto err;
240 		}
241 
242 		rc = register_persistent_object(get_session_token(session),
243 						obj->uuid);
244 		if (rc)
245 			goto err;
246 
247 		LIST_INSERT_HEAD(&session->token->object_list, obj, link);
248 	} else {
249 		rc = PKCS11_CKR_OK;
250 		LIST_INSERT_HEAD(get_session_objects(session), obj, link);
251 	}
252 
253 	*out_handle = obj_handle;
254 
255 	return PKCS11_CKR_OK;
256 err:
257 	/* make sure that supplied "head" isn't freed */
258 	obj->attributes = NULL;
259 	handle_put(&session->object_handle_db, obj_handle);
260 	if (get_bool(head, PKCS11_CKA_TOKEN))
261 		cleanup_persistent_object(obj, session->token);
262 	else
263 		cleanup_volatile_obj_ref(obj);
264 
265 	return rc;
266 }
267 
268 enum pkcs11_rc entry_create_object(struct pkcs11_client *client,
269 				   uint32_t ptypes, TEE_Param *params)
270 {
271 	const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT,
272 						TEE_PARAM_TYPE_NONE,
273 						TEE_PARAM_TYPE_MEMREF_OUTPUT,
274 						TEE_PARAM_TYPE_NONE);
275 	enum pkcs11_rc rc = PKCS11_CKR_OK;
276 	TEE_Param *ctrl = params;
277 	TEE_Param *out = params + 2;
278 	struct serialargs ctrlargs = { };
279 	struct pkcs11_session *session = NULL;
280 	struct obj_attrs *head = NULL;
281 	struct pkcs11_object_head *template = NULL;
282 	size_t template_size = 0;
283 	uint32_t obj_handle = 0;
284 
285 	/*
286 	 * Collect the arguments of the request
287 	 */
288 
289 	if (!client || ptypes != exp_pt ||
290 	    out->memref.size != sizeof(obj_handle))
291 		return PKCS11_CKR_ARGUMENTS_BAD;
292 
293 	serialargs_init(&ctrlargs, ctrl->memref.buffer, ctrl->memref.size);
294 
295 	rc = serialargs_get_session_from_handle(&ctrlargs, client, &session);
296 	if (rc)
297 		return rc;
298 
299 	rc = serialargs_alloc_get_attributes(&ctrlargs, &template);
300 	if (rc)
301 		return rc;
302 
303 	if (serialargs_remaining_bytes(&ctrlargs)) {
304 		rc = PKCS11_CKR_ARGUMENTS_BAD;
305 		goto out;
306 	}
307 
308 	template_size = sizeof(*template) + template->attrs_size;
309 
310 	/*
311 	 * Prepare a clean initial state for the requested object attributes.
312 	 * Free temporary template once done.
313 	 */
314 	rc = create_attributes_from_template(&head, template, template_size,
315 					     NULL, PKCS11_FUNCTION_IMPORT,
316 					     PKCS11_PROCESSING_IMPORT);
317 	TEE_Free(template);
318 	template = NULL;
319 	if (rc)
320 		goto out;
321 
322 	/*
323 	 * Check target object attributes match target processing
324 	 * Check target object attributes match token state
325 	 */
326 	rc = check_created_attrs_against_processing(PKCS11_PROCESSING_IMPORT,
327 						    head);
328 	if (rc)
329 		goto out;
330 
331 	rc = check_created_attrs_against_token(session, head);
332 	if (rc)
333 		goto out;
334 
335 	/*
336 	 * At this stage the object is almost created: all its attributes are
337 	 * referenced in @head, including the key value and are assumed
338 	 * reliable. Now need to register it and get a handle for it.
339 	 */
340 	rc = create_object(session, head, &obj_handle);
341 	if (rc)
342 		goto out;
343 
344 	/*
345 	 * Now obj_handle (through the related struct pkcs11_object
346 	 * instance) owns the serialised buffer that holds the object
347 	 * attributes. We clear reference in head to NULL as the serializer
348 	 * object is now referred from obj_handle. This allows smooth pass
349 	 * through free at function exit.
350 	 */
351 	head = NULL;
352 
353 	TEE_MemMove(out->memref.buffer, &obj_handle, sizeof(obj_handle));
354 	out->memref.size = sizeof(obj_handle);
355 
356 	DMSG("PKCS11 session %"PRIu32": import object %#"PRIx32,
357 	     session->handle, obj_handle);
358 
359 out:
360 	TEE_Free(template);
361 	TEE_Free(head);
362 
363 	return rc;
364 }
365 
366 enum pkcs11_rc entry_destroy_object(struct pkcs11_client *client,
367 				    uint32_t ptypes, TEE_Param *params)
368 {
369 	const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT,
370 						TEE_PARAM_TYPE_NONE,
371 						TEE_PARAM_TYPE_NONE,
372 						TEE_PARAM_TYPE_NONE);
373 	enum pkcs11_rc rc = PKCS11_CKR_OK;
374 	TEE_Param *ctrl = params;
375 	struct serialargs ctrlargs = { };
376 	uint32_t object_handle = 0;
377 	struct pkcs11_session *session = NULL;
378 	struct pkcs11_object *object = NULL;
379 
380 	if (!client || ptypes != exp_pt)
381 		return PKCS11_CKR_ARGUMENTS_BAD;
382 
383 	serialargs_init(&ctrlargs, ctrl->memref.buffer, ctrl->memref.size);
384 
385 	rc = serialargs_get_session_from_handle(&ctrlargs, client, &session);
386 	if (rc)
387 		return rc;
388 
389 	rc = serialargs_get_u32(&ctrlargs, &object_handle);
390 	if (rc)
391 		return rc;
392 
393 	if (serialargs_remaining_bytes(&ctrlargs))
394 		return PKCS11_CKR_ARGUMENTS_BAD;
395 
396 	object = pkcs11_handle2object(object_handle, session);
397 	if (!object)
398 		return PKCS11_CKR_OBJECT_HANDLE_INVALID;
399 
400 	destroy_object(session, object, false);
401 
402 	DMSG("PKCS11 session %"PRIu32": destroy object %#"PRIx32,
403 	     session->handle, object_handle);
404 
405 	return rc;
406 }
407