1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2020, Open Mobile Platform LLC
4  */
5 
6 #include <assert.h>
7 #include <ctype.h>
8 #include <dlfcn.h>
9 #include <dirent.h>
10 #include <plugin.h>
11 #include <stdio.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <tee_client_api.h>
17 #include <teec_trace.h>
18 #include <tee_supplicant.h>
19 
20 #include "optee_msg_supplicant.h"
21 
22 #ifndef __aligned
23 #define __aligned(x) __attribute__((__aligned__(x)))
24 #endif
25 #include <linux/tee.h>
26 
27 /* internal possible returned values */
28 enum plugin_err {
29 	PLUGIN_OK = 0,
30 	PLUGIN_DL_OPEN_ERR = -1,
31 	PLUGIN_DL_SYM_ERR = -2,
32 };
33 
34 static struct plugin *plugin_list_head;
35 
36 /* returns 0, if u1 and u2 are equal */
uuid_cmp(TEEC_UUID * u1,TEEC_UUID * u2)37 static int uuid_cmp(TEEC_UUID *u1, TEEC_UUID *u2)
38 {
39 	if (!memcmp(u1, u2, sizeof(TEEC_UUID)))
40 		return 0;
41 
42 	return 1;
43 }
44 
uuid_from_octets(TEEC_UUID * d,const uint8_t s[TEE_IOCTL_UUID_LEN])45 static void uuid_from_octets(TEEC_UUID *d, const uint8_t s[TEE_IOCTL_UUID_LEN])
46 {
47 	d->timeLow = ((uint32_t)s[0] << 24) | ((uint32_t)s[1] << 16) |
48 		((uint32_t)s[2] << 8) | s[3];
49 	d->timeMid = ((uint32_t)s[4] << 8) | s[5];
50 	d->timeHiAndVersion = ((uint32_t)s[6] << 8) | s[7];
51 	memcpy(d->clockSeqAndNode, s + 8, sizeof(d->clockSeqAndNode));
52 }
53 
push_plugin(struct plugin * p)54 static void push_plugin(struct plugin *p)
55 {
56 	p->next = plugin_list_head;
57 	plugin_list_head = p;
58 }
59 
find_plugin(TEEC_UUID * u)60 static struct plugin *find_plugin(TEEC_UUID *u)
61 {
62 	struct plugin *p = plugin_list_head;
63 
64 	while (p) {
65 		if (!uuid_cmp(&p->method->uuid, u))
66 			return p;
67 
68 		p = p->next;
69 	}
70 
71 	return NULL;
72 }
73 
load_plugin(const char * name,struct plugin * p)74 static enum plugin_err load_plugin(const char *name, struct plugin *p)
75 {
76 	void *handle = NULL;
77 	struct plugin_method *m = NULL;
78 
79 	handle = dlopen(name, RTLD_LAZY);
80 	if (!handle)
81 		return PLUGIN_DL_OPEN_ERR;
82 
83 	m = (struct plugin_method *)dlsym(handle, "plugin_method");
84 	if (!m || !m->name || !m->invoke) {
85 		dlclose(handle);
86 		return PLUGIN_DL_SYM_ERR;
87 	}
88 
89 	p->handle = handle;
90 	p->method = m;
91 
92 	return PLUGIN_OK;
93 }
94 
plugin_invoke(TEEC_UUID * u,unsigned int cmd,unsigned int sub_cmd,void * data,size_t in_len,size_t * out_len)95 static TEEC_Result plugin_invoke(TEEC_UUID *u, unsigned int cmd,
96 				 unsigned int sub_cmd, void *data,
97 				 size_t in_len, size_t *out_len)
98 {
99 	struct plugin *p = NULL;
100 
101 	p = find_plugin(u);
102 	if (!p)
103 		return TEEC_ERROR_ITEM_NOT_FOUND;
104 
105 	assert(p->method->invoke);
106 
107 	return p->method->invoke(cmd, sub_cmd, data, in_len, out_len);
108 }
109 
plugin_load_all(void)110 TEEC_Result plugin_load_all(void)
111 {
112 	DIR *dir = NULL;
113 	enum plugin_err res = PLUGIN_OK;
114 	TEEC_Result teec_res = TEEC_SUCCESS;
115 	struct dirent *entry = NULL;
116 	char path[PATH_MAX] = { 0 };
117 
118 	dir = opendir(supplicant_params.plugin_load_path);
119 
120 	if (!dir) {
121 		IMSG("could not open directory %s", supplicant_params.plugin_load_path);
122 
123 		/* don't generate error if there is no the dir */
124 		return TEEC_SUCCESS;
125 	}
126 
127 	while ((entry = readdir(dir))) {
128 		struct plugin *p;
129 		int r;
130 
131 		if (!strcmp(entry->d_name, "..") || !strcmp(entry->d_name, "."))
132 			continue;
133 
134 		p = calloc(1, sizeof(struct plugin));
135 		if (!p) {
136 			EMSG("allocate mem for plugin <%s> failed",
137 			     entry->d_name);
138 			closedir(dir);
139 			return TEEC_ERROR_OUT_OF_MEMORY;
140 		}
141 		r = snprintf(path, sizeof(path), "%s/%s",
142 		             supplicant_params.plugin_load_path, entry->d_name);
143 		if (r < 0 || r >= (int)sizeof(path)) {
144 			EMSG("assemble of full path for plugin <%s> failed",
145 			     entry->d_name);
146 			closedir(dir);
147 			return TEEC_ERROR_GENERIC ;
148 		}
149 		res = load_plugin(path, p);
150 		switch (res) {
151 		case PLUGIN_DL_OPEN_ERR:
152 			EMSG("open plugin <%s> failed: %s",
153 			     entry->d_name, dlerror());
154 			free(p);
155 			continue;
156 		case PLUGIN_DL_SYM_ERR:
157 			EMSG("find 'plugin_method' sym in <%s> failed: %s",
158 			     entry->d_name, dlerror());
159 			free(p);
160 			continue;
161 		default:
162 			DMSG("loading the <%s> plugin were successful",
163 			     p->method->name);
164 			break;
165 		}
166 
167 		/* Init the plugin */
168 		if (p->method->init) {
169 			teec_res = p->method->init();
170 			if (teec_res) {
171 				EMSG("init the <%s> plugin failed with 0x%x",
172 				     p->method->name, teec_res);
173 				free(p);
174 				continue;
175 			}
176 		}
177 
178 		push_plugin(p);
179 	}
180 
181 	closedir(dir);
182 	return TEEC_SUCCESS;
183 }
184 
plugin_process(size_t num_params,struct tee_ioctl_param * params)185 TEEC_Result plugin_process(size_t num_params, struct tee_ioctl_param *params)
186 {
187 	unsigned int cmd = 0;
188 	unsigned int sub_cmd = 0;
189 	void *data = NULL;
190 	uint32_t data_len = 0;
191 	TEEC_UUID uuid = { };
192 	uint32_t uuid_words[4] = { };
193 	size_t outlen = 0;
194 	TEEC_Result res = TEEC_ERROR_NOT_SUPPORTED;
195 
196 	if (num_params != 4 ||
197 	    (params[0].attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) !=
198 		    TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT ||
199 	    (params[1].attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) !=
200 		    TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT ||
201 	    (params[2].attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) !=
202 		    TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT ||
203 	    (params[3].attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) !=
204 		    TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT)
205 		return TEEC_ERROR_BAD_PARAMETERS;
206 
207 	uuid_words[0] = params[0].b;
208 	uuid_words[1] = params[0].c;
209 	uuid_words[2] = params[1].a;
210 	uuid_words[3] = params[1].b;
211 
212 	uuid_from_octets(&uuid, (const uint8_t *)uuid_words);
213 
214 	cmd = params[1].c;
215 	sub_cmd = params[2].a;
216 
217 	data = tee_supp_param_to_va(params + 3);
218 	data_len = MEMREF_SIZE(params + 3);
219 
220 	if (data_len && !data)
221 		return TEEC_ERROR_BAD_PARAMETERS;
222 
223 	switch (params[0].a) {
224 	case OPTEE_INVOKE_PLUGIN:
225 		res = plugin_invoke(&uuid, cmd, sub_cmd, data, data_len,
226 				    &outlen);
227 		params[2].b = outlen;
228 	default:
229 		break;
230 	}
231 
232 	return res;
233 }
234