config.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. /*
  2. * 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
  3. * All rights reserved.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. */
  15. #include <linux/kernel.h>
  16. #include <linux/connector.h>
  17. #include <linux/crypto.h>
  18. #include <linux/list.h>
  19. #include <linux/mutex.h>
  20. #include <linux/string.h>
  21. #include <linux/in.h>
  22. #include <linux/slab.h>
  23. #include "netfs.h"
  24. /*
  25. * Global configuration list.
  26. * Each client can be asked to get one of them.
  27. *
  28. * Allows to provide remote server address (ipv4/v6/whatever), port
  29. * and so on via kernel connector.
  30. */
  31. static struct cb_id pohmelfs_cn_id = {.idx = POHMELFS_CN_IDX, .val = POHMELFS_CN_VAL};
  32. static LIST_HEAD(pohmelfs_config_list);
  33. static DEFINE_MUTEX(pohmelfs_config_lock);
  34. static inline int pohmelfs_config_eql(struct pohmelfs_ctl *sc, struct pohmelfs_ctl *ctl)
  35. {
  36. if (sc->idx == ctl->idx && sc->type == ctl->type &&
  37. sc->proto == ctl->proto &&
  38. sc->addrlen == ctl->addrlen &&
  39. !memcmp(&sc->addr, &ctl->addr, ctl->addrlen))
  40. return 1;
  41. return 0;
  42. }
  43. static struct pohmelfs_config_group *pohmelfs_find_config_group(unsigned int idx)
  44. {
  45. struct pohmelfs_config_group *g, *group = NULL;
  46. list_for_each_entry(g, &pohmelfs_config_list, group_entry) {
  47. if (g->idx == idx) {
  48. group = g;
  49. break;
  50. }
  51. }
  52. return group;
  53. }
  54. static struct pohmelfs_config_group *pohmelfs_find_create_config_group(unsigned int idx)
  55. {
  56. struct pohmelfs_config_group *g;
  57. g = pohmelfs_find_config_group(idx);
  58. if (g)
  59. return g;
  60. g = kzalloc(sizeof(struct pohmelfs_config_group), GFP_KERNEL);
  61. if (!g)
  62. return NULL;
  63. INIT_LIST_HEAD(&g->config_list);
  64. g->idx = idx;
  65. g->num_entry = 0;
  66. list_add_tail(&g->group_entry, &pohmelfs_config_list);
  67. return g;
  68. }
  69. static inline void pohmelfs_insert_config_entry(struct pohmelfs_sb *psb, struct pohmelfs_config *dst)
  70. {
  71. struct pohmelfs_config *tmp;
  72. INIT_LIST_HEAD(&dst->config_entry);
  73. list_for_each_entry(tmp, &psb->state_list, config_entry) {
  74. if (dst->state.ctl.prio > tmp->state.ctl.prio)
  75. list_add_tail(&dst->config_entry, &tmp->config_entry);
  76. }
  77. if (list_empty(&dst->config_entry))
  78. list_add_tail(&dst->config_entry, &psb->state_list);
  79. }
  80. static int pohmelfs_move_config_entry(struct pohmelfs_sb *psb,
  81. struct pohmelfs_config *dst, struct pohmelfs_config *new)
  82. {
  83. if ((dst->state.ctl.prio == new->state.ctl.prio) &&
  84. (dst->state.ctl.perm == new->state.ctl.perm))
  85. return 0;
  86. dprintk("%s: dst: prio: %d, perm: %x, new: prio: %d, perm: %d.\n",
  87. __func__, dst->state.ctl.prio, dst->state.ctl.perm,
  88. new->state.ctl.prio, new->state.ctl.perm);
  89. dst->state.ctl.prio = new->state.ctl.prio;
  90. dst->state.ctl.perm = new->state.ctl.perm;
  91. list_del_init(&dst->config_entry);
  92. pohmelfs_insert_config_entry(psb, dst);
  93. return 0;
  94. }
  95. /*
  96. * pohmelfs_copy_config() is used to copy new state configs from the
  97. * config group (controlled by the netlink messages) into the superblock.
  98. * This happens either at startup time where no transactions can access
  99. * the list of the configs (and thus list of the network states), or at
  100. * run-time, where it is protected by the psb->state_lock.
  101. */
  102. int pohmelfs_copy_config(struct pohmelfs_sb *psb)
  103. {
  104. struct pohmelfs_config_group *g;
  105. struct pohmelfs_config *c, *dst;
  106. int err = -ENODEV;
  107. mutex_lock(&pohmelfs_config_lock);
  108. g = pohmelfs_find_config_group(psb->idx);
  109. if (!g)
  110. goto out_unlock;
  111. /*
  112. * Run over all entries in given config group and try to create and
  113. * initialize those, which do not exist in superblock list.
  114. * Skip all existing entries.
  115. */
  116. list_for_each_entry(c, &g->config_list, config_entry) {
  117. err = 0;
  118. list_for_each_entry(dst, &psb->state_list, config_entry) {
  119. if (pohmelfs_config_eql(&dst->state.ctl, &c->state.ctl)) {
  120. err = pohmelfs_move_config_entry(psb, dst, c);
  121. if (!err)
  122. err = -EEXIST;
  123. break;
  124. }
  125. }
  126. if (err)
  127. continue;
  128. dst = kzalloc(sizeof(struct pohmelfs_config), GFP_KERNEL);
  129. if (!dst) {
  130. err = -ENOMEM;
  131. break;
  132. }
  133. memcpy(&dst->state.ctl, &c->state.ctl, sizeof(struct pohmelfs_ctl));
  134. pohmelfs_insert_config_entry(psb, dst);
  135. err = pohmelfs_state_init_one(psb, dst);
  136. if (err) {
  137. list_del(&dst->config_entry);
  138. kfree(dst);
  139. }
  140. err = 0;
  141. }
  142. out_unlock:
  143. mutex_unlock(&pohmelfs_config_lock);
  144. return err;
  145. }
  146. int pohmelfs_copy_crypto(struct pohmelfs_sb *psb)
  147. {
  148. struct pohmelfs_config_group *g;
  149. int err = -ENOENT;
  150. mutex_lock(&pohmelfs_config_lock);
  151. g = pohmelfs_find_config_group(psb->idx);
  152. if (!g)
  153. goto err_out_exit;
  154. if (g->hash_string) {
  155. err = -ENOMEM;
  156. psb->hash_string = kstrdup(g->hash_string, GFP_KERNEL);
  157. if (!psb->hash_string)
  158. goto err_out_exit;
  159. psb->hash_strlen = g->hash_strlen;
  160. }
  161. if (g->cipher_string) {
  162. psb->cipher_string = kstrdup(g->cipher_string, GFP_KERNEL);
  163. if (!psb->cipher_string)
  164. goto err_out_free_hash_string;
  165. psb->cipher_strlen = g->cipher_strlen;
  166. }
  167. if (g->hash_keysize) {
  168. psb->hash_key = kmemdup(g->hash_key, g->hash_keysize,
  169. GFP_KERNEL);
  170. if (!psb->hash_key)
  171. goto err_out_free_cipher_string;
  172. psb->hash_keysize = g->hash_keysize;
  173. }
  174. if (g->cipher_keysize) {
  175. psb->cipher_key = kmemdup(g->cipher_key, g->cipher_keysize,
  176. GFP_KERNEL);
  177. if (!psb->cipher_key)
  178. goto err_out_free_hash;
  179. psb->cipher_keysize = g->cipher_keysize;
  180. }
  181. mutex_unlock(&pohmelfs_config_lock);
  182. return 0;
  183. err_out_free_hash:
  184. kfree(psb->hash_key);
  185. err_out_free_cipher_string:
  186. kfree(psb->cipher_string);
  187. err_out_free_hash_string:
  188. kfree(psb->hash_string);
  189. err_out_exit:
  190. mutex_unlock(&pohmelfs_config_lock);
  191. return err;
  192. }
  193. static int pohmelfs_send_reply(int err, int msg_num, int action, struct cn_msg *msg, struct pohmelfs_ctl *ctl)
  194. {
  195. struct pohmelfs_cn_ack *ack;
  196. ack = kzalloc(sizeof(struct pohmelfs_cn_ack), GFP_KERNEL);
  197. if (!ack)
  198. return -ENOMEM;
  199. memcpy(&ack->msg, msg, sizeof(struct cn_msg));
  200. if (action == POHMELFS_CTLINFO_ACK)
  201. memcpy(&ack->ctl, ctl, sizeof(struct pohmelfs_ctl));
  202. ack->msg.len = sizeof(struct pohmelfs_cn_ack) - sizeof(struct cn_msg);
  203. ack->msg.ack = msg->ack + 1;
  204. ack->error = err;
  205. ack->msg_num = msg_num;
  206. cn_netlink_send(&ack->msg, 0, GFP_KERNEL);
  207. kfree(ack);
  208. return 0;
  209. }
  210. static int pohmelfs_cn_disp(struct cn_msg *msg)
  211. {
  212. struct pohmelfs_config_group *g;
  213. struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data;
  214. struct pohmelfs_config *c, *tmp;
  215. int err = 0, i = 1;
  216. if (msg->len != sizeof(struct pohmelfs_ctl))
  217. return -EBADMSG;
  218. mutex_lock(&pohmelfs_config_lock);
  219. g = pohmelfs_find_config_group(ctl->idx);
  220. if (!g) {
  221. pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL);
  222. goto out_unlock;
  223. }
  224. list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) {
  225. struct pohmelfs_ctl *sc = &c->state.ctl;
  226. if (pohmelfs_send_reply(err, g->num_entry - i, POHMELFS_CTLINFO_ACK, msg, sc)) {
  227. err = -ENOMEM;
  228. goto out_unlock;
  229. }
  230. i += 1;
  231. }
  232. out_unlock:
  233. mutex_unlock(&pohmelfs_config_lock);
  234. return err;
  235. }
  236. static int pohmelfs_cn_dump(struct cn_msg *msg)
  237. {
  238. struct pohmelfs_config_group *g;
  239. struct pohmelfs_config *c, *tmp;
  240. int err = 0, i = 1;
  241. int total_msg = 0;
  242. if (msg->len != sizeof(struct pohmelfs_ctl))
  243. return -EBADMSG;
  244. mutex_lock(&pohmelfs_config_lock);
  245. list_for_each_entry(g, &pohmelfs_config_list, group_entry)
  246. total_msg += g->num_entry;
  247. if (total_msg == 0) {
  248. if (pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL))
  249. err = -ENOMEM;
  250. goto out_unlock;
  251. }
  252. list_for_each_entry(g, &pohmelfs_config_list, group_entry) {
  253. list_for_each_entry_safe(c, tmp, &g->config_list,
  254. config_entry) {
  255. struct pohmelfs_ctl *sc = &c->state.ctl;
  256. if (pohmelfs_send_reply(err, total_msg - i,
  257. POHMELFS_CTLINFO_ACK, msg,
  258. sc)) {
  259. err = -ENOMEM;
  260. goto out_unlock;
  261. }
  262. i += 1;
  263. }
  264. }
  265. out_unlock:
  266. mutex_unlock(&pohmelfs_config_lock);
  267. return err;
  268. }
  269. static int pohmelfs_cn_flush(struct cn_msg *msg)
  270. {
  271. struct pohmelfs_config_group *g;
  272. struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data;
  273. struct pohmelfs_config *c, *tmp;
  274. int err = 0;
  275. if (msg->len != sizeof(struct pohmelfs_ctl))
  276. return -EBADMSG;
  277. mutex_lock(&pohmelfs_config_lock);
  278. if (ctl->idx != POHMELFS_NULL_IDX) {
  279. g = pohmelfs_find_config_group(ctl->idx);
  280. if (!g)
  281. goto out_unlock;
  282. list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) {
  283. list_del(&c->config_entry);
  284. g->num_entry--;
  285. kfree(c);
  286. }
  287. } else {
  288. list_for_each_entry(g, &pohmelfs_config_list, group_entry) {
  289. list_for_each_entry_safe(c, tmp, &g->config_list,
  290. config_entry) {
  291. list_del(&c->config_entry);
  292. g->num_entry--;
  293. kfree(c);
  294. }
  295. }
  296. }
  297. out_unlock:
  298. mutex_unlock(&pohmelfs_config_lock);
  299. pohmelfs_cn_dump(msg);
  300. return err;
  301. }
  302. static int pohmelfs_modify_config(struct pohmelfs_ctl *old, struct pohmelfs_ctl *new)
  303. {
  304. old->perm = new->perm;
  305. old->prio = new->prio;
  306. return 0;
  307. }
  308. static int pohmelfs_cn_ctl(struct cn_msg *msg, int action)
  309. {
  310. struct pohmelfs_config_group *g;
  311. struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data;
  312. struct pohmelfs_config *c, *tmp;
  313. int err = 0;
  314. if (msg->len != sizeof(struct pohmelfs_ctl))
  315. return -EBADMSG;
  316. mutex_lock(&pohmelfs_config_lock);
  317. g = pohmelfs_find_create_config_group(ctl->idx);
  318. if (!g) {
  319. err = -ENOMEM;
  320. goto out_unlock;
  321. }
  322. list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) {
  323. struct pohmelfs_ctl *sc = &c->state.ctl;
  324. if (pohmelfs_config_eql(sc, ctl)) {
  325. if (action == POHMELFS_FLAGS_ADD) {
  326. err = -EEXIST;
  327. goto out_unlock;
  328. } else if (action == POHMELFS_FLAGS_DEL) {
  329. list_del(&c->config_entry);
  330. g->num_entry--;
  331. kfree(c);
  332. goto out_unlock;
  333. } else if (action == POHMELFS_FLAGS_MODIFY) {
  334. err = pohmelfs_modify_config(sc, ctl);
  335. goto out_unlock;
  336. } else {
  337. err = -EEXIST;
  338. goto out_unlock;
  339. }
  340. }
  341. }
  342. if (action == POHMELFS_FLAGS_DEL) {
  343. err = -EBADMSG;
  344. goto out_unlock;
  345. }
  346. c = kzalloc(sizeof(struct pohmelfs_config), GFP_KERNEL);
  347. if (!c) {
  348. err = -ENOMEM;
  349. goto out_unlock;
  350. }
  351. memcpy(&c->state.ctl, ctl, sizeof(struct pohmelfs_ctl));
  352. g->num_entry++;
  353. list_add_tail(&c->config_entry, &g->config_list);
  354. out_unlock:
  355. mutex_unlock(&pohmelfs_config_lock);
  356. if (pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL))
  357. err = -ENOMEM;
  358. return err;
  359. }
  360. static int pohmelfs_crypto_hash_init(struct pohmelfs_config_group *g, struct pohmelfs_crypto *c)
  361. {
  362. char *algo = (char *)c->data;
  363. u8 *key = (u8 *)(algo + c->strlen);
  364. if (g->hash_string)
  365. return -EEXIST;
  366. g->hash_string = kstrdup(algo, GFP_KERNEL);
  367. if (!g->hash_string)
  368. return -ENOMEM;
  369. g->hash_strlen = c->strlen;
  370. g->hash_keysize = c->keysize;
  371. g->hash_key = kmemdup(key, c->keysize, GFP_KERNEL);
  372. if (!g->hash_key) {
  373. kfree(g->hash_string);
  374. return -ENOMEM;
  375. }
  376. return 0;
  377. }
  378. static int pohmelfs_crypto_cipher_init(struct pohmelfs_config_group *g, struct pohmelfs_crypto *c)
  379. {
  380. char *algo = (char *)c->data;
  381. u8 *key = (u8 *)(algo + c->strlen);
  382. if (g->cipher_string)
  383. return -EEXIST;
  384. g->cipher_string = kstrdup(algo, GFP_KERNEL);
  385. if (!g->cipher_string)
  386. return -ENOMEM;
  387. g->cipher_strlen = c->strlen;
  388. g->cipher_keysize = c->keysize;
  389. g->cipher_key = kmemdup(key, c->keysize, GFP_KERNEL);
  390. if (!g->cipher_key) {
  391. kfree(g->cipher_string);
  392. return -ENOMEM;
  393. }
  394. return 0;
  395. }
  396. static int pohmelfs_cn_crypto(struct cn_msg *msg)
  397. {
  398. struct pohmelfs_crypto *crypto = (struct pohmelfs_crypto *)msg->data;
  399. struct pohmelfs_config_group *g;
  400. int err = 0;
  401. dprintk("%s: idx: %u, strlen: %u, type: %u, keysize: %u, algo: %s.\n",
  402. __func__, crypto->idx, crypto->strlen, crypto->type,
  403. crypto->keysize, (char *)crypto->data);
  404. mutex_lock(&pohmelfs_config_lock);
  405. g = pohmelfs_find_create_config_group(crypto->idx);
  406. if (!g) {
  407. err = -ENOMEM;
  408. goto out_unlock;
  409. }
  410. switch (crypto->type) {
  411. case POHMELFS_CRYPTO_HASH:
  412. err = pohmelfs_crypto_hash_init(g, crypto);
  413. break;
  414. case POHMELFS_CRYPTO_CIPHER:
  415. err = pohmelfs_crypto_cipher_init(g, crypto);
  416. break;
  417. default:
  418. err = -ENOTSUPP;
  419. break;
  420. }
  421. out_unlock:
  422. mutex_unlock(&pohmelfs_config_lock);
  423. if (pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL))
  424. err = -ENOMEM;
  425. return err;
  426. }
  427. static void pohmelfs_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
  428. {
  429. int err;
  430. if (!cap_raised(current_cap(), CAP_SYS_ADMIN))
  431. return;
  432. switch (msg->flags) {
  433. case POHMELFS_FLAGS_ADD:
  434. case POHMELFS_FLAGS_DEL:
  435. case POHMELFS_FLAGS_MODIFY:
  436. err = pohmelfs_cn_ctl(msg, msg->flags);
  437. break;
  438. case POHMELFS_FLAGS_FLUSH:
  439. err = pohmelfs_cn_flush(msg);
  440. break;
  441. case POHMELFS_FLAGS_SHOW:
  442. err = pohmelfs_cn_disp(msg);
  443. break;
  444. case POHMELFS_FLAGS_DUMP:
  445. err = pohmelfs_cn_dump(msg);
  446. break;
  447. case POHMELFS_FLAGS_CRYPTO:
  448. err = pohmelfs_cn_crypto(msg);
  449. break;
  450. default:
  451. err = -ENOSYS;
  452. break;
  453. }
  454. }
  455. int pohmelfs_config_check(struct pohmelfs_config *config, int idx)
  456. {
  457. struct pohmelfs_ctl *ctl = &config->state.ctl;
  458. struct pohmelfs_config *tmp;
  459. int err = -ENOENT;
  460. struct pohmelfs_ctl *sc;
  461. struct pohmelfs_config_group *g;
  462. mutex_lock(&pohmelfs_config_lock);
  463. g = pohmelfs_find_config_group(ctl->idx);
  464. if (g) {
  465. list_for_each_entry(tmp, &g->config_list, config_entry) {
  466. sc = &tmp->state.ctl;
  467. if (pohmelfs_config_eql(sc, ctl)) {
  468. err = 0;
  469. break;
  470. }
  471. }
  472. }
  473. mutex_unlock(&pohmelfs_config_lock);
  474. return err;
  475. }
  476. int __init pohmelfs_config_init(void)
  477. {
  478. /* XXX remove (void *) cast when vanilla connector got synced */
  479. return cn_add_callback(&pohmelfs_cn_id, "pohmelfs", (void *)pohmelfs_cn_callback);
  480. }
  481. void pohmelfs_config_exit(void)
  482. {
  483. struct pohmelfs_config *c, *tmp;
  484. struct pohmelfs_config_group *g, *gtmp;
  485. cn_del_callback(&pohmelfs_cn_id);
  486. mutex_lock(&pohmelfs_config_lock);
  487. list_for_each_entry_safe(g, gtmp, &pohmelfs_config_list, group_entry) {
  488. list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) {
  489. list_del(&c->config_entry);
  490. kfree(c);
  491. }
  492. list_del(&g->group_entry);
  493. kfree(g->hash_string);
  494. kfree(g->cipher_string);
  495. kfree(g);
  496. }
  497. mutex_unlock(&pohmelfs_config_lock);
  498. }