plugin: fix double free in finish callback
[oweals/rpcd.git] / plugin.c
1 /*
2  * rpcd - UBUS RPC server
3  *
4  *   Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <rpcd/plugin.h>
20
21 static struct blob_buf buf;
22
23 struct rpc_plugin_lookup_context {
24         uint32_t id;
25         char *name;
26         bool found;
27 };
28
29 static void
30 rpc_plugin_lookup_plugin_cb(struct ubus_context *ctx,
31                             struct ubus_object_data *obj, void *priv)
32 {
33         struct rpc_plugin_lookup_context *c = priv;
34
35         if (c->id == obj->id)
36         {
37                 c->found = true;
38                 sprintf(c->name, "%s", obj->path);
39         }
40 }
41
42 static bool
43 rpc_plugin_lookup_plugin(struct ubus_context *ctx, struct ubus_object *obj,
44                          char *strptr)
45 {
46         struct rpc_plugin_lookup_context c = { .id = obj->id, .name = strptr };
47
48         if (ubus_lookup(ctx, NULL, rpc_plugin_lookup_plugin_cb, &c))
49                 return false;
50
51         return c.found;
52 }
53
54 struct call_context {
55         char path[PATH_MAX];
56         const char *argv[4];
57         char *method;
58         char *input;
59         json_tokener *tok;
60         json_object *obj;
61         bool input_done;
62         bool output_done;
63 };
64
65 static int
66 rpc_plugin_call_stdin_cb(struct ustream *s, void *priv)
67 {
68         struct call_context *c = priv;
69
70         if (!c->input_done)
71         {
72                 ustream_write(s, c->input, strlen(c->input), false);
73                 c->input_done = true;
74         }
75
76         return 0;
77 }
78
79 static int
80 rpc_plugin_call_stdout_cb(struct blob_buf *blob, char *buf, int len, void *priv)
81 {
82         struct call_context *c = priv;
83
84         if (!c->output_done)
85         {
86                 c->obj = json_tokener_parse_ex(c->tok, buf, len);
87
88                 if (json_tokener_get_error(c->tok) != json_tokener_continue)
89                         c->output_done = true;
90         }
91
92         return len;
93 }
94
95 static int
96 rpc_plugin_call_stderr_cb(struct blob_buf *blob, char *buf, int len, void *priv)
97 {
98         return len;
99 }
100
101 static int
102 rpc_plugin_call_finish_cb(struct blob_buf *blob, int stat, void *priv)
103 {
104         struct call_context *c = priv;
105         int rv = UBUS_STATUS_INVALID_ARGUMENT;
106
107         if (json_tokener_get_error(c->tok) == json_tokener_success)
108         {
109                 if (c->obj)
110                 {
111                         if (json_object_get_type(c->obj) == json_type_object &&
112                             blobmsg_add_object(blob, c->obj))
113                                 rv = UBUS_STATUS_OK;
114
115                         json_object_put(c->obj);
116                 }
117                 else
118                 {
119                         rv = UBUS_STATUS_NO_DATA;
120                 }
121         }
122
123         json_tokener_free(c->tok);
124
125         free(c->input);
126
127         return rv;
128 }
129
130 static int
131 rpc_plugin_call(struct ubus_context *ctx, struct ubus_object *obj,
132                 struct ubus_request_data *req, const char *method,
133                 struct blob_attr *msg)
134 {
135         int rv = UBUS_STATUS_UNKNOWN_ERROR;
136         struct call_context *c;
137         char *plugin, *mptr;
138
139         c = calloc_a(sizeof(*c), &mptr, strlen(method) + 1);
140
141         if (!c)
142                 goto fail;
143
144         c->method = strcpy(mptr, method);
145         c->input = blobmsg_format_json(msg, true);
146         c->tok = json_tokener_new();
147
148         if (!c->input || !c->tok)
149                 goto fail;
150
151         plugin = c->path + sprintf(c->path, "%s/", RPC_PLUGIN_DIRECTORY);
152
153         if (!rpc_plugin_lookup_plugin(ctx, obj, plugin))
154         {
155                 rv = UBUS_STATUS_NOT_FOUND;
156                 goto fail;
157         }
158
159         c->argv[0] = c->path;
160         c->argv[1] = "call";
161         c->argv[2] = c->method;
162
163         rv = rpc_exec(c->argv, rpc_plugin_call_stdin_cb,
164                       rpc_plugin_call_stdout_cb, rpc_plugin_call_stderr_cb,
165                       rpc_plugin_call_finish_cb, c, ctx, req);
166
167         if (rv == UBUS_STATUS_OK)
168                 return rv;
169
170 fail:
171         if (c)
172         {
173                 if (c->input)
174                         free(c->input);
175
176                 if (c->tok)
177                         json_tokener_free(c->tok);
178
179                 free(c);
180         }
181
182         return rv;
183 }
184
185 static bool
186 rpc_plugin_parse_signature(struct blob_attr *sig, struct ubus_method *method)
187 {
188         int rem, n_attr;
189         enum blobmsg_type type;
190         struct blob_attr *attr;
191         struct blobmsg_policy *policy = NULL;
192
193         if (!sig || blobmsg_type(sig) != BLOBMSG_TYPE_TABLE)
194                 return false;
195
196         n_attr = 0;
197
198         blobmsg_for_each_attr(attr, sig, rem)
199                 n_attr++;
200
201         if (n_attr)
202         {
203                 policy = calloc(n_attr, sizeof(*policy));
204
205                 if (!policy)
206                         return false;
207
208                 n_attr = 0;
209
210                 blobmsg_for_each_attr(attr, sig, rem)
211                 {
212                         type = blobmsg_type(attr);
213
214                         if (type == BLOBMSG_TYPE_INT32)
215                         {
216                                 switch (blobmsg_get_u32(attr))
217                                 {
218                                 case 8:
219                                         type = BLOBMSG_TYPE_INT8;
220                                         break;
221
222                                 case 16:
223                                         type = BLOBMSG_TYPE_INT16;
224                                         break;
225
226                                 case 64:
227                                         type = BLOBMSG_TYPE_INT64;
228                                         break;
229
230                                 default:
231                                         type = BLOBMSG_TYPE_INT32;
232                                         break;
233                                 }
234                         }
235
236                         policy[n_attr].name = strdup(blobmsg_name(attr));
237                         policy[n_attr].type = type;
238
239                         n_attr++;
240                 }
241         }
242
243         method->name = strdup(blobmsg_name(sig));
244         method->handler = rpc_plugin_call;
245         method->policy = policy;
246         method->n_policy = n_attr;
247
248         return true;
249 }
250
251 static struct ubus_object *
252 rpc_plugin_parse_exec(const char *name, int fd)
253 {
254         int len, rem, n_method;
255         struct blob_attr *cur;
256         struct ubus_method *methods;
257         struct ubus_object_type *obj_type;
258         struct ubus_object *obj;
259         char outbuf[1024];
260
261         json_tokener *tok;
262         json_object *jsobj;
263
264         blob_buf_init(&buf, 0);
265
266         tok = json_tokener_new();
267
268         if (!tok)
269                 return NULL;
270
271         while ((len = read(fd, outbuf, sizeof(outbuf))) > 0)
272         {
273                 jsobj = json_tokener_parse_ex(tok, outbuf, len);
274
275                 if (json_tokener_get_error(tok) == json_tokener_continue)
276                         continue;
277
278                 if (json_tokener_get_error(tok) != json_tokener_success)
279                         break;
280
281                 if (jsobj)
282                 {
283                         if (json_object_get_type(jsobj) == json_type_object)
284                                 blobmsg_add_object(&buf, jsobj);
285
286                         json_object_put(jsobj);
287                         break;
288                 }
289         }
290
291         json_tokener_free(tok);
292
293         n_method = 0;
294
295         blob_for_each_attr(cur, buf.head, rem)
296                 n_method++;
297
298         if (!n_method)
299                 return NULL;
300
301         methods = calloc(n_method, sizeof(*methods));
302
303         if (!methods)
304                 return NULL;
305
306         n_method = 0;
307
308         blob_for_each_attr(cur, buf.head, rem)
309         {
310                 if (!rpc_plugin_parse_signature(cur, &methods[n_method]))
311                         continue;
312
313                 n_method++;
314         }
315
316         obj = calloc(1, sizeof(*obj));
317
318         if (!obj)
319                 return NULL;
320
321         obj_type = calloc(1, sizeof(*obj_type));
322
323         if (!obj_type) {
324                 free(obj);
325                 return NULL;
326         }
327
328         if (asprintf((char **)&obj_type->name, "luci-rpc-plugin-%s", name) < 0) {
329                 free(obj);
330                 free(obj_type);
331                 return NULL;
332         }
333
334         obj_type->methods = methods;
335         obj_type->n_methods = n_method;
336
337         obj->name = strdup(name);
338         obj->type = obj_type;
339         obj->methods = methods;
340         obj->n_methods = n_method;
341
342         return obj;
343 }
344
345 static int
346 rpc_plugin_register_exec(struct ubus_context *ctx, const char *path)
347 {
348         pid_t pid;
349         int rv = UBUS_STATUS_NO_DATA, fd, fds[2];
350         const char *name;
351         struct ubus_object *plugin;
352
353         name = strrchr(path, '/');
354
355         if (!name)
356                 return UBUS_STATUS_INVALID_ARGUMENT;
357
358         if (pipe(fds))
359                 return UBUS_STATUS_UNKNOWN_ERROR;
360
361         switch ((pid = fork()))
362         {
363         case -1:
364                 return UBUS_STATUS_UNKNOWN_ERROR;
365
366         case 0:
367                 fd = open("/dev/null", O_RDWR);
368
369                 if (fd > -1)
370                 {
371                         dup2(fd, 0);
372                         dup2(fd, 2);
373
374                         if (fd > 2)
375                                 close(fd);
376                 }
377
378                 dup2(fds[1], 1);
379
380                 close(fds[0]);
381                 close(fds[1]);
382
383                 if (execl(path, path, "list", NULL))
384                         return UBUS_STATUS_UNKNOWN_ERROR;
385
386         default:
387                 plugin = rpc_plugin_parse_exec(name + 1, fds[0]);
388
389                 if (!plugin)
390                         goto out;
391
392                 rv = ubus_add_object(ctx, plugin);
393
394 out:
395                 close(fds[0]);
396                 close(fds[1]);
397                 waitpid(pid, NULL, 0);
398
399                 return rv;
400         }
401 }
402
403
404 static LIST_HEAD(plugins);
405
406 static const struct rpc_daemon_ops ops = {
407         .session_access     = rpc_session_access,
408         .session_create_cb  = rpc_session_create_cb,
409         .session_destroy_cb = rpc_session_destroy_cb,
410         .exec               = rpc_exec,
411         .exec_timeout       = &rpc_exec_timeout,
412 };
413
414 static int
415 rpc_plugin_register_library(struct ubus_context *ctx, const char *path)
416 {
417         struct rpc_plugin *p;
418         void *dlh;
419
420         dlh = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
421
422         if (!dlh)
423                 return UBUS_STATUS_UNKNOWN_ERROR;
424
425         p = dlsym(dlh, "rpc_plugin");
426
427         if (!p)
428                 return UBUS_STATUS_NOT_FOUND;
429
430         list_add(&p->list, &plugins);
431
432         return p->init(&ops, ctx);
433 }
434
435 int rpc_plugin_api_init(struct ubus_context *ctx)
436 {
437         DIR *d;
438         int rv = 0;
439         struct stat s;
440         struct dirent *e;
441         char path[PATH_MAX];
442
443         if ((d = opendir(RPC_PLUGIN_DIRECTORY)) != NULL)
444         {
445                 while ((e = readdir(d)) != NULL)
446                 {
447                         snprintf(path, sizeof(path) - 1,
448                                  RPC_PLUGIN_DIRECTORY "/%s", e->d_name);
449
450                         if (stat(path, &s) || !S_ISREG(s.st_mode) || !(s.st_mode & S_IXUSR))
451                                 continue;
452
453                         rv |= rpc_plugin_register_exec(ctx, path);
454                 }
455
456                 closedir(d);
457         }
458
459         if ((d = opendir(RPC_LIBRARY_DIRECTORY)) != NULL)
460         {
461                 while ((e = readdir(d)) != NULL)
462                 {
463                         snprintf(path, sizeof(path) - 1,
464                                  RPC_LIBRARY_DIRECTORY "/%s", e->d_name);
465
466                         if (stat(path, &s) || !S_ISREG(s.st_mode))
467                                 continue;
468
469                         rv |= rpc_plugin_register_library(ctx, path);
470                 }
471
472                 closedir(d);
473         }
474
475         return rv;
476 }