1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Module for modifying the secmark field of the skb, for use by
4 * security subsystems.
5 *
6 * Based on the nfmark match by:
7 * (C) 1999-2001 Marc Boucher <marc@mbsi.ca>
8 *
9 * (C) 2006,2008 Red Hat, Inc., James Morris <jmorris@redhat.com>
10 */
11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12 #include <linux/module.h>
13 #include <linux/security.h>
14 #include <linux/skbuff.h>
15 #include <linux/netfilter/x_tables.h>
16 #include <linux/netfilter/xt_SECMARK.h>
17
18 MODULE_LICENSE("GPL");
19 MODULE_AUTHOR("James Morris <jmorris@redhat.com>");
20 MODULE_DESCRIPTION("Xtables: packet security mark modification");
21 MODULE_ALIAS("ipt_SECMARK");
22 MODULE_ALIAS("ip6t_SECMARK");
23
24 #define PFX "SECMARK: "
25
26 static u8 mode;
27
28 static unsigned int
secmark_tg(struct sk_buff * skb,const struct xt_secmark_target_info_v1 * info)29 secmark_tg(struct sk_buff *skb, const struct xt_secmark_target_info_v1 *info)
30 {
31 u32 secmark = 0;
32
33 switch (mode) {
34 case SECMARK_MODE_SEL:
35 secmark = info->secid;
36 break;
37 default:
38 BUG();
39 }
40
41 skb->secmark = secmark;
42 return XT_CONTINUE;
43 }
44
checkentry_lsm(struct xt_secmark_target_info_v1 * info)45 static int checkentry_lsm(struct xt_secmark_target_info_v1 *info)
46 {
47 int err;
48
49 info->secctx[SECMARK_SECCTX_MAX - 1] = '\0';
50 info->secid = 0;
51
52 err = security_secctx_to_secid(info->secctx, strlen(info->secctx),
53 &info->secid);
54 if (err) {
55 if (err == -EINVAL)
56 pr_info_ratelimited("invalid security context \'%s\'\n",
57 info->secctx);
58 return err;
59 }
60
61 if (!info->secid) {
62 pr_info_ratelimited("unable to map security context \'%s\'\n",
63 info->secctx);
64 return -ENOENT;
65 }
66
67 err = security_secmark_relabel_packet(info->secid);
68 if (err) {
69 pr_info_ratelimited("unable to obtain relabeling permission\n");
70 return err;
71 }
72
73 security_secmark_refcount_inc();
74 return 0;
75 }
76
77 static int
secmark_tg_check(const char * table,struct xt_secmark_target_info_v1 * info)78 secmark_tg_check(const char *table, struct xt_secmark_target_info_v1 *info)
79 {
80 int err;
81
82 if (strcmp(table, "mangle") != 0 &&
83 strcmp(table, "security") != 0) {
84 pr_info_ratelimited("only valid in \'mangle\' or \'security\' table, not \'%s\'\n",
85 table);
86 return -EINVAL;
87 }
88
89 if (mode && mode != info->mode) {
90 pr_info_ratelimited("mode already set to %hu cannot mix with rules for mode %hu\n",
91 mode, info->mode);
92 return -EINVAL;
93 }
94
95 switch (info->mode) {
96 case SECMARK_MODE_SEL:
97 break;
98 default:
99 pr_info_ratelimited("invalid mode: %hu\n", info->mode);
100 return -EINVAL;
101 }
102
103 err = checkentry_lsm(info);
104 if (err)
105 return err;
106
107 if (!mode)
108 mode = info->mode;
109 return 0;
110 }
111
secmark_tg_destroy(const struct xt_tgdtor_param * par)112 static void secmark_tg_destroy(const struct xt_tgdtor_param *par)
113 {
114 switch (mode) {
115 case SECMARK_MODE_SEL:
116 security_secmark_refcount_dec();
117 }
118 }
119
secmark_tg_check_v0(const struct xt_tgchk_param * par)120 static int secmark_tg_check_v0(const struct xt_tgchk_param *par)
121 {
122 struct xt_secmark_target_info *info = par->targinfo;
123 struct xt_secmark_target_info_v1 newinfo = {
124 .mode = info->mode,
125 };
126 int ret;
127
128 memcpy(newinfo.secctx, info->secctx, SECMARK_SECCTX_MAX);
129
130 ret = secmark_tg_check(par->table, &newinfo);
131 info->secid = newinfo.secid;
132
133 return ret;
134 }
135
136 static unsigned int
secmark_tg_v0(struct sk_buff * skb,const struct xt_action_param * par)137 secmark_tg_v0(struct sk_buff *skb, const struct xt_action_param *par)
138 {
139 const struct xt_secmark_target_info *info = par->targinfo;
140 struct xt_secmark_target_info_v1 newinfo = {
141 .secid = info->secid,
142 };
143
144 return secmark_tg(skb, &newinfo);
145 }
146
secmark_tg_check_v1(const struct xt_tgchk_param * par)147 static int secmark_tg_check_v1(const struct xt_tgchk_param *par)
148 {
149 return secmark_tg_check(par->table, par->targinfo);
150 }
151
152 static unsigned int
secmark_tg_v1(struct sk_buff * skb,const struct xt_action_param * par)153 secmark_tg_v1(struct sk_buff *skb, const struct xt_action_param *par)
154 {
155 return secmark_tg(skb, par->targinfo);
156 }
157
158 static struct xt_target secmark_tg_reg[] __read_mostly = {
159 {
160 .name = "SECMARK",
161 .revision = 0,
162 .family = NFPROTO_UNSPEC,
163 .checkentry = secmark_tg_check_v0,
164 .destroy = secmark_tg_destroy,
165 .target = secmark_tg_v0,
166 .targetsize = sizeof(struct xt_secmark_target_info),
167 .me = THIS_MODULE,
168 },
169 {
170 .name = "SECMARK",
171 .revision = 1,
172 .family = NFPROTO_UNSPEC,
173 .checkentry = secmark_tg_check_v1,
174 .destroy = secmark_tg_destroy,
175 .target = secmark_tg_v1,
176 .targetsize = sizeof(struct xt_secmark_target_info_v1),
177 .usersize = offsetof(struct xt_secmark_target_info_v1, secid),
178 .me = THIS_MODULE,
179 },
180 };
181
secmark_tg_init(void)182 static int __init secmark_tg_init(void)
183 {
184 return xt_register_targets(secmark_tg_reg, ARRAY_SIZE(secmark_tg_reg));
185 }
186
secmark_tg_exit(void)187 static void __exit secmark_tg_exit(void)
188 {
189 xt_unregister_targets(secmark_tg_reg, ARRAY_SIZE(secmark_tg_reg));
190 }
191
192 module_init(secmark_tg_init);
193 module_exit(secmark_tg_exit);
194