xfrm: add extack support to verify_newsa_info
authorSabrina Dubroca <sd@queasysnail.net>
Wed, 14 Sep 2022 17:04:00 +0000 (19:04 +0200)
committerSteffen Klassert <steffen.klassert@secunet.com>
Thu, 22 Sep 2022 05:36:05 +0000 (07:36 +0200)
Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
net/xfrm/xfrm_user.c

index 772a051..4167c18 100644 (file)
@@ -149,7 +149,8 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
 }
 
 static int verify_newsa_info(struct xfrm_usersa_info *p,
-                            struct nlattr **attrs)
+                            struct nlattr **attrs,
+                            struct netlink_ext_ack *extack)
 {
        int err;
 
@@ -163,10 +164,12 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
                break;
 #else
                err = -EAFNOSUPPORT;
+               NL_SET_ERR_MSG(extack, "IPv6 support disabled");
                goto out;
 #endif
 
        default:
+               NL_SET_ERR_MSG(extack, "Invalid address family");
                goto out;
        }
 
@@ -175,65 +178,98 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
                break;
 
        case AF_INET:
-               if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32)
+               if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32) {
+                       NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 32 for IPv4)");
                        goto out;
+               }
 
                break;
 
        case AF_INET6:
 #if IS_ENABLED(CONFIG_IPV6)
-               if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128)
+               if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128) {
+                       NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 128 for IPv6)");
                        goto out;
+               }
 
                break;
 #else
+               NL_SET_ERR_MSG(extack, "IPv6 support disabled");
                err = -EAFNOSUPPORT;
                goto out;
 #endif
 
        default:
+               NL_SET_ERR_MSG(extack, "Invalid address family in selector");
                goto out;
        }
 
        err = -EINVAL;
        switch (p->id.proto) {
        case IPPROTO_AH:
-               if ((!attrs[XFRMA_ALG_AUTH]     &&
-                    !attrs[XFRMA_ALG_AUTH_TRUNC]) ||
-                   attrs[XFRMA_ALG_AEAD]       ||
+               if (!attrs[XFRMA_ALG_AUTH]      &&
+                   !attrs[XFRMA_ALG_AUTH_TRUNC]) {
+                       NL_SET_ERR_MSG(extack, "Missing required attribute for AH: AUTH_TRUNC or AUTH");
+                       goto out;
+               }
+
+               if (attrs[XFRMA_ALG_AEAD]       ||
                    attrs[XFRMA_ALG_CRYPT]      ||
                    attrs[XFRMA_ALG_COMP]       ||
-                   attrs[XFRMA_TFCPAD])
+                   attrs[XFRMA_TFCPAD]) {
+                       NL_SET_ERR_MSG(extack, "Invalid attributes for AH: AEAD, CRYPT, COMP, TFCPAD");
                        goto out;
+               }
                break;
 
        case IPPROTO_ESP:
-               if (attrs[XFRMA_ALG_COMP])
+               if (attrs[XFRMA_ALG_COMP]) {
+                       NL_SET_ERR_MSG(extack, "Invalid attribute for ESP: COMP");
                        goto out;
+               }
+
                if (!attrs[XFRMA_ALG_AUTH] &&
                    !attrs[XFRMA_ALG_AUTH_TRUNC] &&
                    !attrs[XFRMA_ALG_CRYPT] &&
-                   !attrs[XFRMA_ALG_AEAD])
+                   !attrs[XFRMA_ALG_AEAD]) {
+                       NL_SET_ERR_MSG(extack, "Missing required attribute for ESP: at least one of AUTH, AUTH_TRUNC, CRYPT, AEAD");
                        goto out;
+               }
+
                if ((attrs[XFRMA_ALG_AUTH] ||
                     attrs[XFRMA_ALG_AUTH_TRUNC] ||
                     attrs[XFRMA_ALG_CRYPT]) &&
-                   attrs[XFRMA_ALG_AEAD])
+                   attrs[XFRMA_ALG_AEAD]) {
+                       NL_SET_ERR_MSG(extack, "Invalid attribute combination for ESP: AEAD can't be used with AUTH, AUTH_TRUNC, CRYPT");
                        goto out;
+               }
+
                if (attrs[XFRMA_TFCPAD] &&
-                   p->mode != XFRM_MODE_TUNNEL)
+                   p->mode != XFRM_MODE_TUNNEL) {
+                       NL_SET_ERR_MSG(extack, "TFC padding can only be used in tunnel mode");
                        goto out;
+               }
                break;
 
        case IPPROTO_COMP:
-               if (!attrs[XFRMA_ALG_COMP]      ||
-                   attrs[XFRMA_ALG_AEAD]       ||
+               if (!attrs[XFRMA_ALG_COMP]) {
+                       NL_SET_ERR_MSG(extack, "Missing required attribute for COMP: COMP");
+                       goto out;
+               }
+
+               if (attrs[XFRMA_ALG_AEAD]       ||
                    attrs[XFRMA_ALG_AUTH]       ||
                    attrs[XFRMA_ALG_AUTH_TRUNC] ||
                    attrs[XFRMA_ALG_CRYPT]      ||
-                   attrs[XFRMA_TFCPAD]         ||
-                   (ntohl(p->id.spi) >= 0x10000))
+                   attrs[XFRMA_TFCPAD]) {
+                       NL_SET_ERR_MSG(extack, "Invalid attributes for COMP: AEAD, AUTH, AUTH_TRUNC, CRYPT, TFCPAD");
+                       goto out;
+               }
+
+               if (ntohl(p->id.spi) >= 0x10000) {
+                       NL_SET_ERR_MSG(extack, "SPI is too large for COMP (must be < 0x10000)");
                        goto out;
+               }
                break;
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -246,13 +282,20 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
                    attrs[XFRMA_ALG_CRYPT]      ||
                    attrs[XFRMA_ENCAP]          ||
                    attrs[XFRMA_SEC_CTX]        ||
-                   attrs[XFRMA_TFCPAD]         ||
-                   !attrs[XFRMA_COADDR])
+                   attrs[XFRMA_TFCPAD]) {
+                       NL_SET_ERR_MSG(extack, "Invalid attributes for DSTOPTS/ROUTING");
+                       goto out;
+               }
+
+               if (!attrs[XFRMA_COADDR]) {
+                       NL_SET_ERR_MSG(extack, "Missing required COADDR attribute for DSTOPTS/ROUTING");
                        goto out;
+               }
                break;
 #endif
 
        default:
+               NL_SET_ERR_MSG(extack, "Unsupported protocol");
                goto out;
        }
 
@@ -266,7 +309,7 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
                goto out;
        if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP)))
                goto out;
-       if ((err = verify_sec_ctx_len(attrs, NULL)))
+       if ((err = verify_sec_ctx_len(attrs, extack)))
                goto out;
        if ((err = verify_replay(p, attrs)))
                goto out;
@@ -280,14 +323,19 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
                break;
 
        default:
+               NL_SET_ERR_MSG(extack, "Unsupported mode");
                goto out;
        }
 
        err = 0;
 
-       if (attrs[XFRMA_MTIMER_THRESH])
-               if (!attrs[XFRMA_ENCAP])
+       if (attrs[XFRMA_MTIMER_THRESH]) {
+               if (!attrs[XFRMA_ENCAP]) {
+                       NL_SET_ERR_MSG(extack, "MTIMER_THRESH attribute can only be set on ENCAP states");
                        err = -EINVAL;
+                       goto out;
+               }
+       }
 
 out:
        return err;
@@ -688,7 +736,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        int err;
        struct km_event c;
 
-       err = verify_newsa_info(p, attrs);
+       err = verify_newsa_info(p, attrs, extack);
        if (err)
                return err;