macsec: fix SA leak if initialization fails
[sfrench/cifs-2.6.git] / drivers / net / macsec.c
index 84d3e5ca8817197a15165d2c61f8a0f58550c5b6..b37d348b8ea0b99c3227d558e9cf846c03e6e9c2 100644 (file)
@@ -880,12 +880,12 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb,
        macsec_skb_cb(skb)->valid = false;
        skb = skb_share_check(skb, GFP_ATOMIC);
        if (!skb)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        req = aead_request_alloc(rx_sa->key.tfm, GFP_ATOMIC);
        if (!req) {
                kfree_skb(skb);
-               return NULL;
+               return ERR_PTR(-ENOMEM);
        }
 
        hdr = (struct macsec_eth_header *)skb->data;
@@ -905,7 +905,7 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb,
                skb = skb_unshare(skb, GFP_ATOMIC);
                if (!skb) {
                        aead_request_free(req);
-                       return NULL;
+                       return ERR_PTR(-ENOMEM);
                }
        } else {
                /* integrity only: all headers + data authenticated */
@@ -921,14 +921,14 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb,
        dev_hold(dev);
        ret = crypto_aead_decrypt(req);
        if (ret == -EINPROGRESS) {
-               return NULL;
+               return ERR_PTR(ret);
        } else if (ret != 0) {
                /* decryption/authentication failed
                 * 10.6 if validateFrames is disabled, deliver anyway
                 */
                if (ret != -EBADMSG) {
                        kfree_skb(skb);
-                       skb = NULL;
+                       skb = ERR_PTR(ret);
                }
        } else {
                macsec_skb_cb(skb)->valid = true;
@@ -1146,8 +1146,10 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb)
            secy->validate_frames != MACSEC_VALIDATE_DISABLED)
                skb = macsec_decrypt(skb, dev, rx_sa, sci, secy);
 
-       if (!skb) {
-               macsec_rxsa_put(rx_sa);
+       if (IS_ERR(skb)) {
+               /* the decrypt callback needs the reference */
+               if (PTR_ERR(skb) != -EINPROGRESS)
+                       macsec_rxsa_put(rx_sa);
                rcu_read_unlock();
                *pskb = NULL;
                return RX_HANDLER_CONSUMED;
@@ -1161,7 +1163,8 @@ deliver:
                            macsec_extra_len(macsec_skb_cb(skb)->has_sci));
        macsec_reset_skb(skb, secy->netdev);
 
-       macsec_rxsa_put(rx_sa);
+       if (rx_sa)
+               macsec_rxsa_put(rx_sa);
        count_rx(dev, skb->len);
 
        rcu_read_unlock();
@@ -1622,8 +1625,9 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info)
        }
 
        rx_sa = kmalloc(sizeof(*rx_sa), GFP_KERNEL);
-       if (init_rx_sa(rx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), secy->key_len,
-                      secy->icv_len)) {
+       if (!rx_sa || init_rx_sa(rx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]),
+                                secy->key_len, secy->icv_len)) {
+               kfree(rx_sa);
                rtnl_unlock();
                return -ENOMEM;
        }
@@ -1768,6 +1772,7 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info)
        tx_sa = kmalloc(sizeof(*tx_sa), GFP_KERNEL);
        if (!tx_sa || init_tx_sa(tx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]),
                                 secy->key_len, secy->icv_len)) {
+               kfree(tx_sa);
                rtnl_unlock();
                return -ENOMEM;
        }
@@ -2268,7 +2273,7 @@ static int dump_secy(struct macsec_secy *secy, struct net_device *dev,
        if (!hdr)
                return -EMSGSIZE;
 
-       rtnl_lock();
+       genl_dump_check_consistent(cb, hdr, &macsec_fam);
 
        if (nla_put_u32(skb, MACSEC_ATTR_IFINDEX, dev->ifindex))
                goto nla_put_failure;
@@ -2429,18 +2434,17 @@ static int dump_secy(struct macsec_secy *secy, struct net_device *dev,
 
        nla_nest_end(skb, rxsc_list);
 
-       rtnl_unlock();
-
        genlmsg_end(skb, hdr);
 
        return 0;
 
 nla_put_failure:
-       rtnl_unlock();
        genlmsg_cancel(skb, hdr);
        return -EMSGSIZE;
 }
 
+static int macsec_generation = 1; /* protected by RTNL */
+
 static int macsec_dump_txsc(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct net *net = sock_net(skb->sk);
@@ -2450,6 +2454,10 @@ static int macsec_dump_txsc(struct sk_buff *skb, struct netlink_callback *cb)
        dev_idx = cb->args[0];
 
        d = 0;
+       rtnl_lock();
+
+       cb->seq = macsec_generation;
+
        for_each_netdev(net, dev) {
                struct macsec_secy *secy;
 
@@ -2467,6 +2475,7 @@ next:
        }
 
 done:
+       rtnl_unlock();
        cb->args[0] = d;
        return skb->len;
 }
@@ -2920,10 +2929,14 @@ static void macsec_dellink(struct net_device *dev, struct list_head *head)
        struct net_device *real_dev = macsec->real_dev;
        struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev);
 
+       macsec_generation++;
+
        unregister_netdevice_queue(dev, head);
        list_del_rcu(&macsec->secys);
-       if (list_empty(&rxd->secys))
+       if (list_empty(&rxd->secys)) {
                netdev_rx_handler_unregister(real_dev);
+               kfree(rxd);
+       }
 
        macsec_del_dev(macsec);
 }
@@ -2945,8 +2958,10 @@ static int register_macsec_dev(struct net_device *real_dev,
 
                err = netdev_rx_handler_register(real_dev, macsec_handle_frame,
                                                 rxd);
-               if (err < 0)
+               if (err < 0) {
+                       kfree(rxd);
                        return err;
+               }
        }
 
        list_add_tail_rcu(&macsec->secys, &rxd->secys);
@@ -3066,6 +3081,8 @@ static int macsec_newlink(struct net *net, struct net_device *dev,
        if (err < 0)
                goto del_dev;
 
+       macsec_generation++;
+
        dev_hold(real_dev);
 
        return 0;