Hi,
First of all, I want to thank you for your work.
This is a really amazing project!
In Aurora OS we use OP-TEE as a base for various security improvements. In particular, we need to log security events and — depending on system configuration — store them locally or send to a remote server.
For that purpose Aurora OS has a logging service provided via a certain set of API. To make logging available in TEE we have implemented a new RPC service and a deferred work framework (to flush early-boot events when tee_supplicant is ready).
We beleive that functionality might be useful, so we'd like to share it.
However, since our logger implementation depends on REE OS logging mechanisms, we think it might be helpful to render tee-supplicant a bit more flexible in terms of providing services. We propose to add a framework for loadable plugins in tee-supplicant.
We can do it with the help of dynamic shared objects, loading them in runtime with libdl.
To create a plugin one would need to:
- Implement the plugin's methods according to this header:
#ifndef PLUGIN_METHOD_H
#define PLUGIN_METHOD_H
struct plugin_method {
int (*init)(void);
int (*fini)(void);
int (*invoke)(int cmd, void *data);
};
/* the bind function must save plugin methods into the passed argument */
#define IMPLEMENT_PLUGIN_METHOD_BIND_FN(fn) \
int supplicant_plugin_bind_func(struct plugin_method *m); \
int supplicant_plugin_bind_func(struct plugin_method *m) { \
if(!fn(m)) return 0; \
return 1; }
#endif /* PLUGIN_METHOD_H */
- Compile the plugin as a .so file and place it in a system directory (e.g. /usr/lib/tee-supplicant/plugins/). Plugins can be easily installed/updated this way.
tee-supplicant is assumed to load all the plugins found in the above directory into a list, binding external functions during startup process. For tee-supplicant a plugin can be described as:
#ifndef PLUGIN_H
#define PLUGIN_H
#include <plugin_method.h>
struct plugin {
uuid_t *uuid;
struct plugin_method *method; /* implemented by plugins' authors */
int references;
struct plugin *next;
};
/* loads all shared objects from /usr/lib/tee-supplicant/plugins/ and binds all functions */
int plugin_load_all(void);
/* used in RPC handler to handle TEE requests and run some plugin commands */
int plugin_invoke(uuid_t *uuid, int cmd, void *data);
#endif /* PLUGIN_H */
- We assume that in TEE we add a common interface for the plugins. It is a new RPC-call for kernel code.
And to use the plugins from TAs code we propose to implement a new special PTA in optee core.
Thereby TAs might communicate with any plugins through the open/invoke/close interfaces.
After tee-supplicant has loaded every plugin, optee-os can use them with either a direct RPC-call from the kernel
or via a new PTA from TAs.
Example of plugin code:
#include <plugin_method.h>
/* It's assumed that the plugin's file name after compilation will be equal to its UUID */
#define PLUGIN_UUID \
{ 0x4f5a5123, 0x0acc, 0x4ad4, \
{ 0x83, 0x11, 0x22, 0xfd, 0x7b, 0x70, 0x89, 0x78} }
static int awesome_plugin_init(void) {return 0;}
static int awesome_plugin_finit(void) {return 0;}
static int awesome_plugin_invoke(int cmd, void *data)
{
switch(cmd) {
case AWESOME_CMD:
awesome_cmd_func(data);
break;
}
return 0;
}
static int awesome_plugin_bind_helper(plugin_method *m)
{
m->init = awesome_plugin_init;
m->fini = awesome_plugin_finit;
m->invoke = awesome_plugin_invoke;
return 0;
}
IMPLEMENT_PLUGIN_METHOD_BIND_FN(awesome_plugin_bind_helper);
Example of using the plugin from OP-TEE kernel
plugin_rpc(awesome_plugin_uuid, AWESOME_CMD, data);
Example of an RPC handler in tee-supplicant
plugin_invoke(awesome_plugin_uuid, AWESOME_CMD, data);
Summary:
- If someone needs a new feature in the supplicant that isn't needed in upstream, they can use the unified plugin interface
- Easy to sync upstream version with own fork.
- No need to add new RPC-calls for new feature.
We would like to know your opinion about the proposed design.
What do you think about this?
Thank you!