ubusd_acl.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. /*
  2. * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
  3. * Copyright (C) 2018 Hans Dedecker <dedeckeh@gmail.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU Lesser General Public License version 2.1
  7. * as published by the Free Software Foundation
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. */
  14. #define _GNU_SOURCE
  15. #include <sys/socket.h>
  16. #include <sys/types.h>
  17. #include <sys/stat.h>
  18. #include <syslog.h>
  19. #include <unistd.h>
  20. #include <glob.h>
  21. #include <grp.h>
  22. #include <pwd.h>
  23. #include <libubox/vlist.h>
  24. #include <libubox/blobmsg_json.h>
  25. #include <libubox/avl-cmp.h>
  26. #include <libubox/ulog.h>
  27. #include "ubusd.h"
  28. #ifndef SO_PEERCRED
  29. struct ucred {
  30. int pid;
  31. int uid;
  32. int gid;
  33. };
  34. #endif
  35. struct ubusd_acl_obj {
  36. struct avl_node avl;
  37. struct list_head list;
  38. bool partial;
  39. const char *user;
  40. const char *group;
  41. struct blob_attr *methods;
  42. struct blob_attr *tags;
  43. struct blob_attr *priv;
  44. bool subscribe;
  45. bool publish;
  46. bool listen;
  47. bool send;
  48. };
  49. struct ubusd_acl_file {
  50. struct vlist_node avl;
  51. const char *user;
  52. const char *group;
  53. struct blob_attr *blob;
  54. struct list_head acl;
  55. int ok;
  56. };
  57. const char *ubusd_acl_dir = "/usr/share/acl.d";
  58. static struct blob_buf bbuf;
  59. static struct avl_tree ubusd_acls;
  60. static int ubusd_acl_seq;
  61. static struct ubus_object *acl_obj;
  62. static int
  63. ubusd_acl_match_cred(struct ubus_client *cl, struct ubusd_acl_obj *obj)
  64. {
  65. if (obj->user && !strcmp(cl->user, obj->user))
  66. return 0;
  67. if (obj->group && !strcmp(cl->group, obj->group))
  68. return 0;
  69. return -1;
  70. }
  71. int
  72. ubusd_acl_check(struct ubus_client *cl, const char *obj,
  73. const char *method, enum ubusd_acl_type type)
  74. {
  75. struct ubusd_acl_obj *acl;
  76. int match_len = 0;
  77. if (!cl || !cl->uid || !obj)
  78. return 0;
  79. /*
  80. * Since this tree is sorted alphabetically, we can only expect
  81. * to find matching entries as long as the number of matching
  82. * characters between the access list string and the object path
  83. * is monotonically increasing.
  84. */
  85. avl_for_each_element(&ubusd_acls, acl, avl) {
  86. const char *key = acl->avl.key;
  87. int cur_match_len;
  88. bool full_match;
  89. full_match = ubus_strmatch_len(obj, key, &cur_match_len);
  90. if (cur_match_len < match_len)
  91. break;
  92. match_len = cur_match_len;
  93. if (!full_match) {
  94. if (!acl->partial)
  95. continue;
  96. if (match_len != (int) strlen(key))
  97. continue;
  98. }
  99. if (ubusd_acl_match_cred(cl, acl))
  100. continue;
  101. switch (type) {
  102. case UBUS_ACL_PUBLISH:
  103. if (acl->publish)
  104. return 0;
  105. break;
  106. case UBUS_ACL_SUBSCRIBE:
  107. if (acl->subscribe)
  108. return 0;
  109. break;
  110. case UBUS_ACL_LISTEN:
  111. if (acl->listen)
  112. return 0;
  113. break;
  114. case UBUS_ACL_SEND:
  115. if (acl->send)
  116. return 0;
  117. break;
  118. case UBUS_ACL_ACCESS:
  119. if (acl->methods) {
  120. struct blob_attr *cur;
  121. char *cur_method;
  122. size_t rem;
  123. blobmsg_for_each_attr(cur, acl->methods, rem)
  124. if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING) {
  125. cur_method = blobmsg_get_string(cur);
  126. if (!strcmp(method, cur_method) || !strcmp("*", cur_method))
  127. return 0;
  128. }
  129. }
  130. break;
  131. }
  132. }
  133. return -1;
  134. }
  135. int
  136. ubusd_acl_init_client(struct ubus_client *cl, int fd)
  137. {
  138. struct ucred cred;
  139. struct passwd *pwd;
  140. struct group *group;
  141. #ifdef SO_PEERCRED
  142. unsigned int len = sizeof(struct ucred);
  143. if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1) {
  144. ULOG_ERR("Failed getsockopt(): %m\n");
  145. return -1;
  146. }
  147. #else
  148. memset(&cred, 0, sizeof(cred));
  149. #endif
  150. pwd = getpwuid(cred.uid);
  151. if (!pwd) {
  152. ULOG_ERR("Failed getpwuid(): %m\n");
  153. return -1;
  154. }
  155. group = getgrgid(cred.gid);
  156. if (!group) {
  157. ULOG_ERR("Failed getgrgid(): %m\n");
  158. return -1;
  159. }
  160. cl->uid = cred.uid;
  161. cl->gid = cred.gid;
  162. cl->group = strdup(group->gr_name);
  163. cl->user = strdup(pwd->pw_name);
  164. return 0;
  165. }
  166. void
  167. ubusd_acl_free_client(struct ubus_client *cl)
  168. {
  169. free(cl->group);
  170. free(cl->user);
  171. }
  172. static void
  173. ubusd_acl_file_free(struct ubusd_acl_file *file)
  174. {
  175. struct ubusd_acl_obj *p, *q;
  176. list_for_each_entry_safe(p, q, &file->acl, list) {
  177. avl_delete(&ubusd_acls, &p->avl);
  178. list_del(&p->list);
  179. free(p);
  180. }
  181. free(file);
  182. }
  183. enum {
  184. ACL_ACCESS_METHODS,
  185. ACL_ACCESS_TAGS,
  186. ACL_ACCESS_PRIV,
  187. __ACL_ACCESS_MAX
  188. };
  189. static const struct blobmsg_policy acl_obj_policy[__ACL_ACCESS_MAX] = {
  190. [ACL_ACCESS_METHODS] = { .name = "methods", .type = BLOBMSG_TYPE_ARRAY },
  191. [ACL_ACCESS_TAGS] = { .name = "tags", .type = BLOBMSG_TYPE_ARRAY },
  192. [ACL_ACCESS_PRIV] = { .name = "acl", .type = BLOBMSG_TYPE_TABLE },
  193. };
  194. static struct ubusd_acl_obj*
  195. ubusd_acl_alloc_obj(struct ubusd_acl_file *file, const char *obj)
  196. {
  197. struct ubusd_acl_obj *o;
  198. int len = strlen(obj);
  199. char *k;
  200. bool partial = false;
  201. if (obj[len - 1] == '*') {
  202. partial = true;
  203. len--;
  204. }
  205. o = calloc_a(sizeof(*o), &k, len + 1);
  206. o->partial = partial;
  207. o->user = file->user;
  208. o->group = file->group;
  209. o->avl.key = memcpy(k, obj, len);
  210. list_add(&o->list, &file->acl);
  211. avl_insert(&ubusd_acls, &o->avl);
  212. return o;
  213. }
  214. static void
  215. ubusd_acl_add_access(struct ubusd_acl_file *file, struct blob_attr *obj)
  216. {
  217. struct blob_attr *tb[__ACL_ACCESS_MAX];
  218. struct ubusd_acl_obj *o;
  219. blobmsg_parse(acl_obj_policy, __ACL_ACCESS_MAX, tb, blobmsg_data(obj),
  220. blobmsg_data_len(obj));
  221. if (!tb[ACL_ACCESS_METHODS] && !tb[ACL_ACCESS_TAGS] && !tb[ACL_ACCESS_PRIV])
  222. return;
  223. o = ubusd_acl_alloc_obj(file, blobmsg_name(obj));
  224. o->methods = tb[ACL_ACCESS_METHODS];
  225. o->tags = tb[ACL_ACCESS_TAGS];
  226. o->priv = tb[ACL_ACCESS_PRIV];
  227. if (file->user || file->group)
  228. file->ok = 1;
  229. }
  230. static void
  231. ubusd_acl_add_subscribe(struct ubusd_acl_file *file, const char *obj)
  232. {
  233. struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
  234. o->subscribe = true;
  235. }
  236. static void
  237. ubusd_acl_add_publish(struct ubusd_acl_file *file, const char *obj)
  238. {
  239. struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
  240. o->publish = true;
  241. }
  242. static void ubusd_acl_add_listen(struct ubusd_acl_file *file, const char *obj)
  243. {
  244. struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
  245. o->listen = true;
  246. }
  247. static void ubusd_acl_add_send(struct ubusd_acl_file *file, const char *obj)
  248. {
  249. struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
  250. o->send = true;
  251. }
  252. enum {
  253. ACL_USER,
  254. ACL_GROUP,
  255. ACL_ACCESS,
  256. ACL_PUBLISH,
  257. ACL_SUBSCRIBE,
  258. ACL_INHERIT,
  259. ACL_LISTEN,
  260. ACL_SEND,
  261. __ACL_MAX
  262. };
  263. static const struct blobmsg_policy acl_policy[__ACL_MAX] = {
  264. [ACL_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING },
  265. [ACL_GROUP] = { .name = "group", .type = BLOBMSG_TYPE_STRING },
  266. [ACL_ACCESS] = { .name = "access", .type = BLOBMSG_TYPE_TABLE },
  267. [ACL_PUBLISH] = { .name = "publish", .type = BLOBMSG_TYPE_ARRAY },
  268. [ACL_SUBSCRIBE] = { .name = "subscribe", .type = BLOBMSG_TYPE_ARRAY },
  269. [ACL_INHERIT] = { .name = "inherit", .type = BLOBMSG_TYPE_ARRAY },
  270. [ACL_LISTEN] = { .name= "listen", .type = BLOBMSG_TYPE_ARRAY },
  271. [ACL_SEND] = { .name= "send", .type = BLOBMSG_TYPE_ARRAY },
  272. };
  273. static void
  274. ubusd_acl_file_add(struct ubusd_acl_file *file)
  275. {
  276. struct blob_attr *tb[__ACL_MAX], *cur;
  277. size_t rem;
  278. blobmsg_parse(acl_policy, __ACL_MAX, tb, blob_data(file->blob),
  279. blob_len(file->blob));
  280. if (tb[ACL_USER])
  281. file->user = blobmsg_get_string(tb[ACL_USER]);
  282. else if (tb[ACL_GROUP])
  283. file->group = blobmsg_get_string(tb[ACL_GROUP]);
  284. else
  285. return;
  286. if (tb[ACL_ACCESS])
  287. blobmsg_for_each_attr(cur, tb[ACL_ACCESS], rem)
  288. ubusd_acl_add_access(file, cur);
  289. if (tb[ACL_SUBSCRIBE])
  290. blobmsg_for_each_attr(cur, tb[ACL_SUBSCRIBE], rem)
  291. if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
  292. ubusd_acl_add_subscribe(file, blobmsg_get_string(cur));
  293. if (tb[ACL_PUBLISH])
  294. blobmsg_for_each_attr(cur, tb[ACL_PUBLISH], rem)
  295. if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
  296. ubusd_acl_add_publish(file, blobmsg_get_string(cur));
  297. if (tb[ACL_LISTEN])
  298. blobmsg_for_each_attr(cur, tb[ACL_LISTEN], rem)
  299. if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
  300. ubusd_acl_add_listen(file, blobmsg_get_string(cur));
  301. if (tb[ACL_SEND])
  302. blobmsg_for_each_attr(cur, tb[ACL_SEND], rem)
  303. if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
  304. ubusd_acl_add_send(file, blobmsg_get_string(cur));
  305. }
  306. static void
  307. ubusd_acl_update_cb(struct vlist_tree *tree, struct vlist_node *node_new,
  308. struct vlist_node *node_old)
  309. {
  310. struct ubusd_acl_file *file;
  311. if (node_old) {
  312. file = container_of(node_old, struct ubusd_acl_file, avl);
  313. ubusd_acl_file_free(file);
  314. }
  315. if (node_new) {
  316. file = container_of(node_new, struct ubusd_acl_file, avl);
  317. ubusd_acl_file_add(file);
  318. }
  319. }
  320. static struct ubus_msg_buf *
  321. ubusd_create_sequence_event_msg(void *priv, const char *id)
  322. {
  323. void *s;
  324. blob_buf_init(&b, 0);
  325. blob_put_int32(&b, UBUS_ATTR_OBJID, 0);
  326. blob_put_string(&b, UBUS_ATTR_METHOD, id);
  327. s = blob_nest_start(&b, UBUS_ATTR_DATA);
  328. blobmsg_add_u32(&b, "sequence", ubusd_acl_seq);
  329. blob_nest_end(&b, s);
  330. return ubus_msg_new(b.head, blob_raw_len(b.head), true);
  331. }
  332. static VLIST_TREE(ubusd_acl_files, avl_strcmp, ubusd_acl_update_cb, false, false);
  333. static int
  334. ubusd_acl_load_file(const char *filename)
  335. {
  336. struct ubusd_acl_file *file;
  337. void *blob;
  338. blob_buf_init(&bbuf, 0);
  339. if (!blobmsg_add_json_from_file(&bbuf, filename)) {
  340. syslog(LOG_ERR, "failed to parse %s\n", filename);
  341. return -1;
  342. }
  343. file = calloc_a(sizeof(*file), &blob, blob_raw_len(bbuf.head));
  344. if (!file)
  345. return -1;
  346. file->blob = blob;
  347. memcpy(blob, bbuf.head, blob_raw_len(bbuf.head));
  348. INIT_LIST_HEAD(&file->acl);
  349. vlist_add(&ubusd_acl_files, &file->avl, filename);
  350. syslog(LOG_INFO, "loading %s\n", filename);
  351. return 0;
  352. }
  353. void
  354. ubusd_acl_load(void)
  355. {
  356. struct stat st;
  357. glob_t gl;
  358. size_t j;
  359. const char *suffix = "/*.json";
  360. char *path = alloca(strlen(ubusd_acl_dir) + strlen(suffix) + 1);
  361. sprintf(path, "%s%s", ubusd_acl_dir, suffix);
  362. if (glob(path, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl))
  363. return;
  364. vlist_update(&ubusd_acl_files);
  365. for (j = 0; j < gl.gl_pathc; j++) {
  366. if (stat(gl.gl_pathv[j], &st) || !S_ISREG(st.st_mode))
  367. continue;
  368. if (st.st_uid || st.st_gid) {
  369. syslog(LOG_ERR, "%s has wrong owner\n", gl.gl_pathv[j]);
  370. continue;
  371. }
  372. if (st.st_mode & (S_IWOTH | S_IWGRP | S_IXOTH)) {
  373. syslog(LOG_ERR, "%s has wrong permissions\n", gl.gl_pathv[j]);
  374. continue;
  375. }
  376. ubusd_acl_load_file(gl.gl_pathv[j]);
  377. }
  378. globfree(&gl);
  379. vlist_flush(&ubusd_acl_files);
  380. ubusd_acl_seq++;
  381. ubusd_send_event(NULL, "ubus.acl.sequence", ubusd_create_sequence_event_msg, NULL);
  382. }
  383. static void
  384. ubusd_reply_add(struct ubus_object *obj)
  385. {
  386. struct ubusd_acl_obj *acl;
  387. int match_len = 0;
  388. if (!obj->path.key)
  389. return;
  390. /*
  391. * Since this tree is sorted alphabetically, we can only expect
  392. * to find matching entries as long as the number of matching
  393. * characters between the access list string and the object path
  394. * is monotonically increasing.
  395. */
  396. avl_for_each_element(&ubusd_acls, acl, avl) {
  397. const char *key = acl->avl.key;
  398. int cur_match_len;
  399. bool full_match;
  400. void *c;
  401. if (!acl->priv)
  402. continue;
  403. full_match = ubus_strmatch_len(obj->path.key, key, &cur_match_len);
  404. if (cur_match_len < match_len)
  405. break;
  406. match_len = cur_match_len;
  407. if (!full_match) {
  408. if (!acl->partial)
  409. continue;
  410. if (match_len != (int) strlen(key))
  411. continue;
  412. }
  413. c = blobmsg_open_table(&b, NULL);
  414. blobmsg_add_string(&b, "obj", obj->path.key);
  415. if (acl->user)
  416. blobmsg_add_string(&b, "user", acl->user);
  417. if (acl->group)
  418. blobmsg_add_string(&b, "group", acl->group);
  419. blobmsg_add_field(&b, blobmsg_type(acl->priv), "acl",
  420. blobmsg_data(acl->priv), blobmsg_data_len(acl->priv));
  421. blobmsg_close_table(&b, c);
  422. }
  423. }
  424. static int ubusd_reply_query(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr, struct blob_attr *msg)
  425. {
  426. struct ubus_object *obj;
  427. void *d, *a;
  428. if (!attr[UBUS_ATTR_OBJID])
  429. return UBUS_STATUS_INVALID_ARGUMENT;
  430. obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID]));
  431. if (!obj)
  432. return UBUS_STATUS_NOT_FOUND;
  433. blob_buf_init(&b, 0);
  434. blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id);
  435. d = blob_nest_start(&b, UBUS_ATTR_DATA);
  436. blobmsg_add_u32(&b, "seq", ubusd_acl_seq);
  437. a = blobmsg_open_array(&b, "acl");
  438. list_for_each_entry(obj, &cl->objects, list)
  439. ubusd_reply_add(obj);
  440. blobmsg_close_table(&b, a);
  441. blob_nest_end(&b, d);
  442. ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA);
  443. return 0;
  444. }
  445. static int ubusd_acl_recv(struct ubus_client *cl, struct ubus_msg_buf *ub, const char *method, struct blob_attr *msg)
  446. {
  447. if (!strcmp(method, "query"))
  448. return ubusd_reply_query(cl, ub, ubus_parse_msg(ub->data, blob_raw_len(ub->data)), msg);
  449. return UBUS_STATUS_INVALID_COMMAND;
  450. }
  451. void ubusd_acl_init(void)
  452. {
  453. ubus_init_string_tree(&ubusd_acls, true);
  454. acl_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_ACL);
  455. acl_obj->recv_msg = ubusd_acl_recv;
  456. }