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