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 p->handle = handle;
84
85 m = (struct plugin_method *)dlsym(handle, "plugin_method");
86 if (!m || !m->name || !m->invoke)
87 return PLUGIN_DL_SYM_ERR;
88
89 p->method = m;
90
91 return PLUGIN_OK;
92 }
93
plugin_invoke(TEEC_UUID * u,unsigned int cmd,unsigned int sub_cmd,void * data,size_t in_len,size_t * out_len)94 static TEEC_Result plugin_invoke(TEEC_UUID *u, unsigned int cmd,
95 unsigned int sub_cmd, void *data,
96 size_t in_len, size_t *out_len)
97 {
98 struct plugin *p = NULL;
99
100 p = find_plugin(u);
101 if (!p)
102 return TEEC_ERROR_ITEM_NOT_FOUND;
103
104 assert(p->method->invoke);
105
106 return p->method->invoke(cmd, sub_cmd, data, in_len, out_len);
107 }
108
plugin_load_all(void)109 TEEC_Result plugin_load_all(void)
110 {
111 DIR *dir = NULL;
112 enum plugin_err res = PLUGIN_OK;
113 TEEC_Result teec_res = TEEC_SUCCESS;
114 struct dirent *entry = NULL;
115 char path[PATH_MAX] = { 0 };
116
117 dir = opendir(supplicant_params.plugin_load_path);
118
119 if (!dir) {
120 IMSG("could not open directory %s", supplicant_params.plugin_load_path);
121
122 /* don't generate error if there is no the dir */
123 return TEEC_SUCCESS;
124 }
125
126 while ((entry = readdir(dir))) {
127 struct plugin *p;
128 int r;
129
130 if (!strcmp(entry->d_name, "..") || !strcmp(entry->d_name, "."))
131 continue;
132
133 p = calloc(1, sizeof(struct plugin));
134 if (!p) {
135 EMSG("allocate mem for plugin <%s> failed",
136 entry->d_name);
137 closedir(dir);
138 return TEEC_ERROR_OUT_OF_MEMORY;
139 }
140 r = snprintf(path, sizeof(path), "%s/%s",
141 supplicant_params.plugin_load_path, entry->d_name);
142 if (r < 0 || r >= (int)sizeof(path)) {
143 EMSG("assemble of full path for plugin <%s> failed",
144 entry->d_name);
145 closedir(dir);
146 return TEEC_ERROR_GENERIC ;
147 }
148 res = load_plugin(path, p);
149 switch (res) {
150 case PLUGIN_DL_OPEN_ERR:
151 EMSG("open plugin <%s> failed: %s",
152 entry->d_name, dlerror());
153 free(p);
154 continue;
155 case PLUGIN_DL_SYM_ERR:
156 EMSG("find 'plugin_method' sym in <%s> failed: %s",
157 entry->d_name, dlerror());
158 free(p);
159 continue;
160 default:
161 DMSG("loading the <%s> plugin were successful",
162 p->method->name);
163 break;
164 }
165
166 /* Init the plugin */
167 if (p->method->init) {
168 teec_res = p->method->init();
169 if (teec_res) {
170 EMSG("init the <%s> plugin failed with 0x%x",
171 p->method->name, teec_res);
172 free(p);
173 continue;
174 }
175 }
176
177 push_plugin(p);
178 }
179
180 closedir(dir);
181 return TEEC_SUCCESS;
182 }
183
plugin_process(size_t num_params,struct tee_ioctl_param * params)184 TEEC_Result plugin_process(size_t num_params, struct tee_ioctl_param *params)
185 {
186 unsigned int cmd = 0;
187 unsigned int sub_cmd = 0;
188 void *data = NULL;
189 uint32_t data_len = 0;
190 TEEC_UUID uuid = { };
191 uint32_t uuid_words[4] = { };
192 size_t outlen = 0;
193 TEEC_Result res = TEEC_ERROR_NOT_SUPPORTED;
194
195 if (num_params != 4 ||
196 (params[0].attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) !=
197 TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT ||
198 (params[1].attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) !=
199 TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT ||
200 (params[2].attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) !=
201 TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT ||
202 (params[3].attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) !=
203 TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT)
204 return TEEC_ERROR_BAD_PARAMETERS;
205
206 uuid_words[0] = params[0].b;
207 uuid_words[1] = params[0].c;
208 uuid_words[2] = params[1].a;
209 uuid_words[3] = params[1].b;
210
211 uuid_from_octets(&uuid, (const uint8_t *)uuid_words);
212
213 cmd = params[1].c;
214 sub_cmd = params[2].a;
215
216 data = tee_supp_param_to_va(params + 3);
217 data_len = MEMREF_SIZE(params + 3);
218
219 if (data_len && !data)
220 return TEEC_ERROR_BAD_PARAMETERS;
221
222 switch (params[0].a) {
223 case OPTEE_INVOKE_PLUGIN:
224 res = plugin_invoke(&uuid, cmd, sub_cmd, data, data_len,
225 &outlen);
226 params[2].b = outlen;
227 default:
228 break;
229 }
230
231 return res;
232 }
233