summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/netlabel.h11
-rw-r--r--net/ipv6/calipso.c165
-rw-r--r--net/netlabel/netlabel_calipso.c82
-rw-r--r--net/netlabel/netlabel_calipso.h7
-rw-r--r--net/netlabel/netlabel_kapi.c32
-rw-r--r--security/selinux/hooks.c15
6 files changed, 308 insertions, 4 deletions
diff --git a/include/net/netlabel.h b/include/net/netlabel.h
index a2408c30a7f7..e0e4ce8f22af 100644
--- a/include/net/netlabel.h
+++ b/include/net/netlabel.h
@@ -231,6 +231,10 @@ struct netlbl_lsm_secattr {
231 * @sock_delattr: remove the socket's attr 231 * @sock_delattr: remove the socket's attr
232 * @req_setattr: set the req socket's attr 232 * @req_setattr: set the req socket's attr
233 * @req_delattr: remove the req socket's attr 233 * @req_delattr: remove the req socket's attr
234 * @opt_getattr: retrieve attr from memory block
235 * @skbuff_optptr: find option in packet
236 * @skbuff_setattr: set the skbuff's attr
237 * @skbuff_delattr: remove the skbuff's attr
234 * 238 *
235 * Description: 239 * Description:
236 * This structure is filled out by the CALIPSO engine and passed 240 * This structure is filled out by the CALIPSO engine and passed
@@ -258,6 +262,13 @@ struct netlbl_calipso_ops {
258 const struct calipso_doi *doi_def, 262 const struct calipso_doi *doi_def,
259 const struct netlbl_lsm_secattr *secattr); 263 const struct netlbl_lsm_secattr *secattr);
260 void (*req_delattr)(struct request_sock *req); 264 void (*req_delattr)(struct request_sock *req);
265 int (*opt_getattr)(const unsigned char *calipso,
266 struct netlbl_lsm_secattr *secattr);
267 unsigned char *(*skbuff_optptr)(const struct sk_buff *skb);
268 int (*skbuff_setattr)(struct sk_buff *skb,
269 const struct calipso_doi *doi_def,
270 const struct netlbl_lsm_secattr *secattr);
271 int (*skbuff_delattr)(struct sk_buff *skb);
261}; 272};
262 273
263/* 274/*
diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c
index 067a5640ef17..fa371a8827cf 100644
--- a/net/ipv6/calipso.c
+++ b/net/ipv6/calipso.c
@@ -62,6 +62,11 @@
62 */ 62 */
63#define CALIPSO_OPT_LEN_MAX_WITH_PAD (3 + CALIPSO_OPT_LEN_MAX + 7) 63#define CALIPSO_OPT_LEN_MAX_WITH_PAD (3 + CALIPSO_OPT_LEN_MAX + 7)
64 64
65 /* Maximium size of u32 aligned buffer required to hold calipso
66 * option. Max of 3 initial pad bytes starting from buffer + 3.
67 * i.e. the worst case is when the previous tlv finishes on 4n + 3.
68 */
69#define CALIPSO_MAX_BUFFER (6 + CALIPSO_OPT_LEN_MAX)
65 70
66/* List of available DOI definitions */ 71/* List of available DOI definitions */
67static DEFINE_SPINLOCK(calipso_doi_list_lock); 72static DEFINE_SPINLOCK(calipso_doi_list_lock);
@@ -973,6 +978,162 @@ static void calipso_req_delattr(struct request_sock *req)
973 kfree(new); 978 kfree(new);
974} 979}
975 980
981/* skbuff functions.
982 */
983
984/**
985 * calipso_skbuff_optptr - Find the CALIPSO option in the packet
986 * @skb: the packet
987 *
988 * Description:
989 * Parse the packet's IP header looking for a CALIPSO option. Returns a pointer
990 * to the start of the CALIPSO option on success, NULL if one if not found.
991 *
992 */
993static unsigned char *calipso_skbuff_optptr(const struct sk_buff *skb)
994{
995 const struct ipv6hdr *ip6_hdr = ipv6_hdr(skb);
996 int offset;
997
998 if (ip6_hdr->nexthdr != NEXTHDR_HOP)
999 return NULL;
1000
1001 offset = ipv6_find_tlv(skb, sizeof(*ip6_hdr), IPV6_TLV_CALIPSO);
1002 if (offset >= 0)
1003 return (unsigned char *)ip6_hdr + offset;
1004
1005 return NULL;
1006}
1007
1008/**
1009 * calipso_skbuff_setattr - Set the CALIPSO option on a packet
1010 * @skb: the packet
1011 * @doi_def: the CALIPSO DOI to use
1012 * @secattr: the security attributes
1013 *
1014 * Description:
1015 * Set the CALIPSO option on the given packet based on the security attributes.
1016 * Returns a pointer to the IP header on success and NULL on failure.
1017 *
1018 */
1019static int calipso_skbuff_setattr(struct sk_buff *skb,
1020 const struct calipso_doi *doi_def,
1021 const struct netlbl_lsm_secattr *secattr)
1022{
1023 int ret_val;
1024 struct ipv6hdr *ip6_hdr;
1025 struct ipv6_opt_hdr *hop;
1026 unsigned char buf[CALIPSO_MAX_BUFFER];
1027 int len_delta, new_end, pad;
1028 unsigned int start, end;
1029
1030 ip6_hdr = ipv6_hdr(skb);
1031 if (ip6_hdr->nexthdr == NEXTHDR_HOP) {
1032 hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
1033 ret_val = calipso_opt_find(hop, &start, &end);
1034 if (ret_val && ret_val != -ENOENT)
1035 return ret_val;
1036 } else {
1037 start = 0;
1038 end = 0;
1039 }
1040
1041 memset(buf, 0, sizeof(buf));
1042 ret_val = calipso_genopt(buf, start & 3, sizeof(buf), doi_def, secattr);
1043 if (ret_val < 0)
1044 return ret_val;
1045
1046 new_end = start + ret_val;
1047 /* At this point new_end aligns to 4n, so (new_end & 4) pads to 8n */
1048 pad = ((new_end & 4) + (end & 7)) & 7;
1049 len_delta = new_end - (int)end + pad;
1050 ret_val = skb_cow(skb, skb_headroom(skb) + len_delta);
1051 if (ret_val < 0)
1052 return ret_val;
1053
1054 if (len_delta) {
1055 if (len_delta > 0)
1056 skb_push(skb, len_delta);
1057 else
1058 skb_pull(skb, -len_delta);
1059 memmove((char *)ip6_hdr - len_delta, ip6_hdr,
1060 sizeof(*ip6_hdr) + start);
1061 skb_reset_network_header(skb);
1062 ip6_hdr = ipv6_hdr(skb);
1063 }
1064
1065 hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
1066 if (start == 0) {
1067 struct ipv6_opt_hdr *new_hop = (struct ipv6_opt_hdr *)buf;
1068
1069 new_hop->nexthdr = ip6_hdr->nexthdr;
1070 new_hop->hdrlen = len_delta / 8 - 1;
1071 ip6_hdr->nexthdr = NEXTHDR_HOP;
1072 } else {
1073 hop->hdrlen += len_delta / 8;
1074 }
1075 memcpy((char *)hop + start, buf + (start & 3), new_end - start);
1076 calipso_pad_write((unsigned char *)hop, new_end, pad);
1077
1078 return 0;
1079}
1080
1081/**
1082 * calipso_skbuff_delattr - Delete any CALIPSO options from a packet
1083 * @skb: the packet
1084 *
1085 * Description:
1086 * Removes any and all CALIPSO options from the given packet. Returns zero on
1087 * success, negative values on failure.
1088 *
1089 */
1090static int calipso_skbuff_delattr(struct sk_buff *skb)
1091{
1092 int ret_val;
1093 struct ipv6hdr *ip6_hdr;
1094 struct ipv6_opt_hdr *old_hop;
1095 u32 old_hop_len, start = 0, end = 0, delta, size, pad;
1096
1097 if (!calipso_skbuff_optptr(skb))
1098 return 0;
1099
1100 /* since we are changing the packet we should make a copy */
1101 ret_val = skb_cow(skb, skb_headroom(skb));
1102 if (ret_val < 0)
1103 return ret_val;
1104
1105 ip6_hdr = ipv6_hdr(skb);
1106 old_hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
1107 old_hop_len = ipv6_optlen(old_hop);
1108
1109 ret_val = calipso_opt_find(old_hop, &start, &end);
1110 if (ret_val)
1111 return ret_val;
1112
1113 if (start == sizeof(*old_hop) && end == old_hop_len) {
1114 /* There's no other option in the header so we delete
1115 * the whole thing. */
1116 delta = old_hop_len;
1117 size = sizeof(*ip6_hdr);
1118 ip6_hdr->nexthdr = old_hop->nexthdr;
1119 } else {
1120 delta = (end - start) & ~7;
1121 if (delta)
1122 old_hop->hdrlen -= delta / 8;
1123 pad = (end - start) & 7;
1124 size = sizeof(*ip6_hdr) + start + pad;
1125 calipso_pad_write((unsigned char *)old_hop, start, pad);
1126 }
1127
1128 if (delta) {
1129 skb_pull(skb, delta);
1130 memmove((char *)ip6_hdr + delta, ip6_hdr, size);
1131 skb_reset_network_header(skb);
1132 }
1133
1134 return 0;
1135}
1136
976static const struct netlbl_calipso_ops ops = { 1137static const struct netlbl_calipso_ops ops = {
977 .doi_add = calipso_doi_add, 1138 .doi_add = calipso_doi_add,
978 .doi_free = calipso_doi_free, 1139 .doi_free = calipso_doi_free,
@@ -985,6 +1146,10 @@ static const struct netlbl_calipso_ops ops = {
985 .sock_delattr = calipso_sock_delattr, 1146 .sock_delattr = calipso_sock_delattr,
986 .req_setattr = calipso_req_setattr, 1147 .req_setattr = calipso_req_setattr,
987 .req_delattr = calipso_req_delattr, 1148 .req_delattr = calipso_req_delattr,
1149 .opt_getattr = calipso_opt_getattr,
1150 .skbuff_optptr = calipso_skbuff_optptr,
1151 .skbuff_setattr = calipso_skbuff_setattr,
1152 .skbuff_delattr = calipso_skbuff_delattr,
988}; 1153};
989 1154
990/** 1155/**
diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c
index 79519e3a6a93..0d02c262dbf6 100644
--- a/net/netlabel/netlabel_calipso.c
+++ b/net/netlabel/netlabel_calipso.c
@@ -618,3 +618,85 @@ void calipso_req_delattr(struct request_sock *req)
618 if (ops) 618 if (ops)
619 ops->req_delattr(req); 619 ops->req_delattr(req);
620} 620}
621
622/**
623 * calipso_optptr - Find the CALIPSO option in the packet
624 * @skb: the packet
625 *
626 * Description:
627 * Parse the packet's IP header looking for a CALIPSO option. Returns a pointer
628 * to the start of the CALIPSO option on success, NULL if one if not found.
629 *
630 */
631unsigned char *calipso_optptr(const struct sk_buff *skb)
632{
633 unsigned char *ret_val = NULL;
634 const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
635
636 if (ops)
637 ret_val = ops->skbuff_optptr(skb);
638 return ret_val;
639}
640
641/**
642 * calipso_getattr - Get the security attributes from a memory block.
643 * @calipso: the CALIPSO option
644 * @secattr: the security attributes
645 *
646 * Description:
647 * Inspect @calipso and return the security attributes in @secattr.
648 * Returns zero on success and negative values on failure.
649 *
650 */
651int calipso_getattr(const unsigned char *calipso,
652 struct netlbl_lsm_secattr *secattr)
653{
654 int ret_val = -ENOMSG;
655 const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
656
657 if (ops)
658 ret_val = ops->opt_getattr(calipso, secattr);
659 return ret_val;
660}
661
662/**
663 * calipso_skbuff_setattr - Set the CALIPSO option on a packet
664 * @skb: the packet
665 * @doi_def: the CALIPSO DOI to use
666 * @secattr: the security attributes
667 *
668 * Description:
669 * Set the CALIPSO option on the given packet based on the security attributes.
670 * Returns a pointer to the IP header on success and NULL on failure.
671 *
672 */
673int calipso_skbuff_setattr(struct sk_buff *skb,
674 const struct calipso_doi *doi_def,
675 const struct netlbl_lsm_secattr *secattr)
676{
677 int ret_val = -ENOMSG;
678 const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
679
680 if (ops)
681 ret_val = ops->skbuff_setattr(skb, doi_def, secattr);
682 return ret_val;
683}
684
685/**
686 * calipso_skbuff_delattr - Delete any CALIPSO options from a packet
687 * @skb: the packet
688 *
689 * Description:
690 * Removes any and all CALIPSO options from the given packet. Returns zero on
691 * success, negative values on failure.
692 *
693 */
694int calipso_skbuff_delattr(struct sk_buff *skb)
695{
696 int ret_val = -ENOMSG;
697 const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get();
698
699 if (ops)
700 ret_val = ops->skbuff_delattr(skb);
701 return ret_val;
702}
diff --git a/net/netlabel/netlabel_calipso.h b/net/netlabel/netlabel_calipso.h
index 1372fdd86588..66ba92e9289f 100644
--- a/net/netlabel/netlabel_calipso.h
+++ b/net/netlabel/netlabel_calipso.h
@@ -137,5 +137,12 @@ int calipso_req_setattr(struct request_sock *req,
137 const struct calipso_doi *doi_def, 137 const struct calipso_doi *doi_def,
138 const struct netlbl_lsm_secattr *secattr); 138 const struct netlbl_lsm_secattr *secattr);
139void calipso_req_delattr(struct request_sock *req); 139void calipso_req_delattr(struct request_sock *req);
140unsigned char *calipso_optptr(const struct sk_buff *skb);
141int calipso_getattr(const unsigned char *calipso,
142 struct netlbl_lsm_secattr *secattr);
143int calipso_skbuff_setattr(struct sk_buff *skb,
144 const struct calipso_doi *doi_def,
145 const struct netlbl_lsm_secattr *secattr);
146int calipso_skbuff_delattr(struct sk_buff *skb);
140 147
141#endif 148#endif
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index 9b725f75c750..c50a1c5d1e1a 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -1147,13 +1147,17 @@ int netlbl_skbuff_setattr(struct sk_buff *skb,
1147{ 1147{
1148 int ret_val; 1148 int ret_val;
1149 struct iphdr *hdr4; 1149 struct iphdr *hdr4;
1150#if IS_ENABLED(CONFIG_IPV6)
1151 struct ipv6hdr *hdr6;
1152#endif
1150 struct netlbl_dommap_def *entry; 1153 struct netlbl_dommap_def *entry;
1151 1154
1152 rcu_read_lock(); 1155 rcu_read_lock();
1153 switch (family) { 1156 switch (family) {
1154 case AF_INET: 1157 case AF_INET:
1155 hdr4 = ip_hdr(skb); 1158 hdr4 = ip_hdr(skb);
1156 entry = netlbl_domhsh_getentry_af4(secattr->domain,hdr4->daddr); 1159 entry = netlbl_domhsh_getentry_af4(secattr->domain,
1160 hdr4->daddr);
1157 if (entry == NULL) { 1161 if (entry == NULL) {
1158 ret_val = -ENOENT; 1162 ret_val = -ENOENT;
1159 goto skbuff_setattr_return; 1163 goto skbuff_setattr_return;
@@ -1174,9 +1178,26 @@ int netlbl_skbuff_setattr(struct sk_buff *skb,
1174 break; 1178 break;
1175#if IS_ENABLED(CONFIG_IPV6) 1179#if IS_ENABLED(CONFIG_IPV6)
1176 case AF_INET6: 1180 case AF_INET6:
1177 /* since we don't support any IPv6 labeling protocols right 1181 hdr6 = ipv6_hdr(skb);
1178 * now we can optimize everything away until we do */ 1182 entry = netlbl_domhsh_getentry_af6(secattr->domain,
1179 ret_val = 0; 1183 &hdr6->daddr);
1184 if (entry == NULL) {
1185 ret_val = -ENOENT;
1186 goto skbuff_setattr_return;
1187 }
1188 switch (entry->type) {
1189 case NETLBL_NLTYPE_CALIPSO:
1190 ret_val = calipso_skbuff_setattr(skb, entry->calipso,
1191 secattr);
1192 break;
1193 case NETLBL_NLTYPE_UNLABELED:
1194 /* just delete the protocols we support for right now
1195 * but we could remove other protocols if needed */
1196 ret_val = calipso_skbuff_delattr(skb);
1197 break;
1198 default:
1199 ret_val = -ENOENT;
1200 }
1180 break; 1201 break;
1181#endif /* IPv6 */ 1202#endif /* IPv6 */
1182 default: 1203 default:
@@ -1215,6 +1236,9 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
1215 break; 1236 break;
1216#if IS_ENABLED(CONFIG_IPV6) 1237#if IS_ENABLED(CONFIG_IPV6)
1217 case AF_INET6: 1238 case AF_INET6:
1239 ptr = calipso_optptr(skb);
1240 if (ptr && calipso_getattr(ptr, secattr) == 0)
1241 return 0;
1218 break; 1242 break;
1219#endif /* IPv6 */ 1243#endif /* IPv6 */
1220 } 1244 }
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index a00ab81ab719..cb7c5c8028e7 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5063,6 +5063,15 @@ static unsigned int selinux_ipv4_output(void *priv,
5063 return selinux_ip_output(skb, PF_INET); 5063 return selinux_ip_output(skb, PF_INET);
5064} 5064}
5065 5065
5066#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
5067static unsigned int selinux_ipv6_output(void *priv,
5068 struct sk_buff *skb,
5069 const struct nf_hook_state *state)
5070{
5071 return selinux_ip_output(skb, PF_INET6);
5072}
5073#endif /* IPV6 */
5074
5066static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, 5075static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
5067 int ifindex, 5076 int ifindex,
5068 u16 family) 5077 u16 family)
@@ -6297,6 +6306,12 @@ static struct nf_hook_ops selinux_nf_ops[] = {
6297 .hooknum = NF_INET_FORWARD, 6306 .hooknum = NF_INET_FORWARD,
6298 .priority = NF_IP6_PRI_SELINUX_FIRST, 6307 .priority = NF_IP6_PRI_SELINUX_FIRST,
6299 }, 6308 },
6309 {
6310 .hook = selinux_ipv6_output,
6311 .pf = NFPROTO_IPV6,
6312 .hooknum = NF_INET_LOCAL_OUT,
6313 .priority = NF_IP6_PRI_SELINUX_FIRST,
6314 },
6300#endif /* IPV6 */ 6315#endif /* IPV6 */
6301}; 6316};
6302 6317