]> git.hungrycats.org Git - linux/commitdiff
[IPSEC]: Clean up key manager algorithm handling.
authorJames Morris <jmorris@intercode.com.au>
Wed, 8 Jan 2003 15:59:28 +0000 (07:59 -0800)
committerJames Morris <jmorris@intercode.com.au>
Wed, 8 Jan 2003 15:59:28 +0000 (07:59 -0800)
include/linux/pfkeyv2.h
include/net/xfrm.h
net/ipv4/Makefile
net/ipv4/ah.c
net/ipv4/xfrm_algo.c [new file with mode: 0644]
net/key/af_key.c
net/netsyms.c

index ff599ee1cf3d252149968ccb917ae9894b5bf7df..c6e4e6e29f10cc60c15abb47efd197168084f289 100644 (file)
@@ -242,17 +242,25 @@ struct sadb_x_ipsecrequest {
 #define SADB_SATYPE_MAX                9
 
 /* Authentication algorithms */
-#define SADB_AALG_NONE         0
-#define SADB_AALG_MD5HMAC      2
-#define SADB_AALG_SHA1HMAC     3
-#define SADB_AALG_MAX          3
+#define SADB_AALG_NONE                 0
+#define SADB_AALG_MD5HMAC              2
+#define SADB_AALG_SHA1HMAC             3
+#define SADB_X_AALG_SHA2_256HMAC       5
+#define SADB_X_AALG_SHA2_384HMAC       6
+#define SADB_X_AALG_SHA2_512HMAC       7
+#define SADB_X_AALG_RIPEMD160HMAC      8
+#define SADB_X_AALG_NULL               251     /* kame */
+#define SADB_AALG_MAX                  251
 
 /* Encryption algorithms */
-#define SADB_EALG_NONE         0
-#define SADB_EALG_DESCBC       1
-#define SADB_EALG_3DESCBC      2
-#define SADB_EALG_NULL         11
-#define SADB_EALG_MAX          11
+#define SADB_EALG_NONE                 0
+#define SADB_EALG_DESCBC               1
+#define SADB_EALG_3DESCBC              2
+#define SADB_X_EALG_CASTCBC            6
+#define SADB_X_EALG_BLOWFISHCBC                7
+#define SADB_EALG_NULL                 11
+#define SADB_X_EALG_AESCBC             12
+#define SADB_EALG_MAX                  12
 
 /* Extension Header values */
 #define SADB_EXT_RESERVED              0
index 37cb371e930a2122a5affcc9353a3b43da42841a..82d2187eeb2af1ba5b241d8d3e0614aff32160d5 100644 (file)
@@ -1,13 +1,19 @@
+#ifndef _NET_XFRM_H
+#define _NET_XFRM_H
+
 #include <linux/xfrm.h>
 #include <linux/spinlock.h>
 #include <linux/list.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <linux/crypto.h>
+#include <linux/pfkeyv2.h>
 
 #include <net/dst.h>
 #include <net/route.h>
 
+#define XFRM_ALIGN8(len)       (((len) + 7) & ~7)
+
 extern struct semaphore xfrm_cfg_sem;
 
 /* Organization of SPD aka "XFRM rules"
@@ -347,6 +353,29 @@ static inline void xfrm_sk_free_policy(struct sock *sk)
        }
 }
 
+/*
+ * xfrm algorithm information
+ */
+struct xfrm_algo_auth_info {
+       u16 icv_truncbits;
+       u16 icv_fullbits;
+};
+
+struct xfrm_algo_encr_info {
+       u16 blockbits;
+       u16 defkeybits;
+};
+
+struct xfrm_algo_desc {
+       char *name;
+       u8 available:1;
+       union {
+               struct xfrm_algo_auth_info auth;
+               struct xfrm_algo_encr_info encr;
+       } uinfo;
+       struct sadb_alg desc;
+};
+
 extern void xfrm_state_init(void);
 extern void xfrm_input_init(void);
 extern int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), void *);
@@ -385,3 +414,15 @@ extern wait_queue_head_t km_waitq;
 extern void km_warn_expired(struct xfrm_state *x);
 extern void km_expired(struct xfrm_state *x);
 extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *pol);
+
+extern void xfrm_probe_algs(void);
+extern int xfrm_count_auth_supported(void);
+extern int xfrm_count_enc_supported(void);
+extern struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx);
+extern struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx);
+extern struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id);
+extern struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id);
+extern struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name);
+extern struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name);
+
+#endif /* _NET_XFRM_H */
index 5e758522316d9f2f33be659425315e781c7fbf5a..7ce979fcaae4551ff12b53e57f1244cd72177038 100644 (file)
@@ -22,4 +22,4 @@ obj-$(CONFIG_IP_PNP) += ipconfig.o
 obj-$(CONFIG_NETFILTER)        += netfilter/
 obj-$(CONFIG_XFRM_USER) += xfrm_user.o
 
-obj-y += xfrm_policy.o xfrm_state.o xfrm_input.o
+obj-y += xfrm_policy.o xfrm_state.o xfrm_input.o xfrm_algo.o
index efccce969e555f2908c130d22991e730b7f7507f..ba5abf93f0aa94be9bcc214cb8d3d180722c2fdb 100644 (file)
@@ -7,26 +7,31 @@
 #include <net/icmp.h>
 #include <asm/scatterlist.h>
 
+#define AH_HLEN_NOICV  12
+
+typedef void (icv_update_fn_t)(struct crypto_tfm *,
+                               struct scatterlist *, unsigned int);
+
 struct ah_data
 {
        u8                      *key;
        int                     key_len;
-       u8                      *work_digest;
-       int                     digest_len;
+       u8                      *work_icv;
+       int                     icv_full_len;
+       int                     icv_trunc_len;
 
-       void                    (*digest)(struct ah_data*,
-                                         struct sk_buff *skb,
-                                         u8 *digest);
+       void                    (*icv)(struct ah_data*,
+                                      struct sk_buff *skb, u8 *icv);
 
        struct crypto_tfm       *tfm;
 };
 
 
 /* Clear mutable options and find final destination to substitute
- * into IP header for digest calculation. Options are already checked
+ * into IP header for icv calculation. Options are already checked
  * for validity, so paranoia is not required. */
 
-int ip_clear_mutable_options(struct iphdr *iph, u32 *daddr)
+static int ip_clear_mutable_options(struct iphdr *iph, u32 *daddr)
 {
        unsigned char * optptr = (unsigned char*)(iph+1);
        int  l = iph->ihl*4 - 20;
@@ -66,7 +71,8 @@ int ip_clear_mutable_options(struct iphdr *iph, u32 *daddr)
        return 0;
 }
 
-void skb_ah_walk(const struct sk_buff *skb, struct crypto_tfm *tfm)
+static void skb_ah_walk(const struct sk_buff *skb,
+                        struct crypto_tfm *tfm, icv_update_fn_t icv_update)
 {
        int offset = 0;
        int len = skb->len;
@@ -83,7 +89,7 @@ void skb_ah_walk(const struct sk_buff *skb, struct crypto_tfm *tfm)
                sg.offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
                sg.length = copy;
                
-               crypto_hmac_update(tfm, &sg, 1);
+               icv_update(tfm, &sg, 1);
                
                if ((len -= copy) == 0)
                        return;
@@ -106,7 +112,7 @@ void skb_ah_walk(const struct sk_buff *skb, struct crypto_tfm *tfm)
                        sg.offset = frag->page_offset + offset-start;
                        sg.length = copy;
                        
-                       crypto_hmac_update(tfm, &sg, 1);
+                       icv_update(tfm, &sg, 1);
                        
                        if (!(len -= copy))
                                return;
@@ -127,7 +133,7 @@ void skb_ah_walk(const struct sk_buff *skb, struct crypto_tfm *tfm)
                        if ((copy = end - offset) > 0) {
                                if (copy > len)
                                        copy = len;
-                               skb_ah_walk(list, tfm);
+                               skb_ah_walk(list, tfm, icv_update);
                                if ((len -= copy) == 0)
                                        return;
                                offset += copy;
@@ -144,14 +150,14 @@ ah_hmac_digest(struct ah_data *ahp, struct sk_buff *skb, u8 *auth_data)
 {
        struct crypto_tfm *tfm = ahp->tfm;
 
-       memset(auth_data, 0, ahp->digest_len);
+       memset(auth_data, 0, ahp->icv_trunc_len);
        crypto_hmac_init(tfm, ahp->key, &ahp->key_len);
-       skb_ah_walk(skb, tfm);
-       crypto_hmac_final(tfm, ahp->key, &ahp->key_len, ahp->work_digest);
-       memcpy(auth_data, ahp->work_digest, ahp->digest_len);
+       skb_ah_walk(skb, tfm, crypto_hmac_update);
+       crypto_hmac_final(tfm, ahp->key, &ahp->key_len, ahp->work_icv);
+       memcpy(auth_data, ahp->work_icv, ahp->icv_trunc_len);
 }
 
-int ah_output(struct sk_buff *skb)
+static int ah_output(struct sk_buff *skb)
 {
        int err;
        struct dst_entry *dst = skb->dst;
@@ -210,11 +216,13 @@ int ah_output(struct sk_buff *skb)
                ah->nexthdr = iph->protocol;
        }
        ahp = x->data;
-       ah->hdrlen  = (((ahp->digest_len + 12 + 7)&~7)>>2)-2;
+       ah->hdrlen  = (XFRM_ALIGN8(ahp->icv_trunc_len +
+                       AH_HLEN_NOICV) >> 2) - 2;
+
        ah->reserved = 0;
        ah->spi = x->id.spi;
        ah->seq_no = htonl(++x->replay.oseq);
-       ahp->digest(ahp, skb, ah->auth_data);
+       ahp->icv(ahp, skb, ah->auth_data);
        top_iph->tos = iph->tos;
        top_iph->ttl = iph->ttl;
        if (x->props.mode) {
@@ -246,6 +254,7 @@ error_nolock:
 
 int ah_input(struct xfrm_state *x, struct sk_buff *skb)
 {
+       int ah_hlen;
        struct iphdr *iph;
        struct ip_auth_hdr *ah;
        struct ah_data *ahp;
@@ -255,13 +264,14 @@ int ah_input(struct xfrm_state *x, struct sk_buff *skb)
                goto out;
 
        ah = (struct ip_auth_hdr*)skb->data;
-
        ahp = x->data;
-
-       if (((ah->hdrlen+2)<<2) != ((ahp->digest_len + 12 + 7)&~7))
+       ah_hlen = (ah->hdrlen + 2) << 2;
+       
+       if (ah_hlen != XFRM_ALIGN8(ahp->icv_full_len + AH_HLEN_NOICV) &&
+           ah_hlen != XFRM_ALIGN8(ahp->icv_trunc_len + AH_HLEN_NOICV)) 
                goto out;
 
-       if (!pskb_may_pull(skb, (ah->hdrlen+2)<<2))
+       if (!pskb_may_pull(skb, ah_hlen))
                goto out;
 
        /* We are going to _remove_ AH header to keep sockets happy,
@@ -285,17 +295,18 @@ int ah_input(struct xfrm_state *x, struct sk_buff *skb)
                        goto out;
        }
         {
-               u8 auth_data[ahp->digest_len];
-               memcpy(auth_data, ah->auth_data, ahp->digest_len);
+               u8 auth_data[ahp->icv_trunc_len];
+               
+               memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
                skb_push(skb, skb->data - skb->nh.raw);
-               ahp->digest(ahp, skb, ah->auth_data);
-               if (memcmp(ah->auth_data, auth_data, ahp->digest_len)) {
+               ahp->icv(ahp, skb, ah->auth_data);
+               if (memcmp(ah->auth_data, auth_data, ahp->icv_trunc_len)) {
                        x->stats.integrity_failed++;
                        goto out;
                }
        }
        ((struct iphdr*)work_buf)->protocol = ah->nexthdr;
-       skb->nh.raw = skb_pull(skb, (ah->hdrlen+2)<<2);
+       skb->nh.raw = skb_pull(skb, ah_hlen);
        memcpy(skb->nh.raw, work_buf, iph->ihl*4);
        skb->nh.iph->tot_len = htons(skb->len);
        skb_pull(skb, skb->nh.iph->ihl*4);
@@ -325,12 +336,13 @@ void ah4_err(struct sk_buff *skb, u32 info)
        xfrm_state_put(x);
 }
 
-int ah_init_state(struct xfrm_state *x, void *args)
+static int ah_init_state(struct xfrm_state *x, void *args)
 {
        struct ah_data *ahp = NULL;
+       struct xfrm_algo_desc *aalg_desc;
 
-       if (x->aalg == NULL || x->aalg->alg_key_len == 0 ||
-           x->aalg->alg_key_len > 512)
+       /* null auth can use a zero length key */
+       if (x->aalg->alg_key_len > 512)
                goto error;
 
        ahp = kmalloc(sizeof(*ahp), GFP_KERNEL);
@@ -344,13 +356,33 @@ int ah_init_state(struct xfrm_state *x, void *args)
        ahp->tfm = crypto_alloc_tfm(x->aalg->alg_name, 0);
        if (!ahp->tfm)
                goto error;
-       ahp->digest = ah_hmac_digest;
-       ahp->digest_len = 12;
-       ahp->work_digest = kmalloc(crypto_tfm_alg_digestsize(ahp->tfm),
-                                  GFP_KERNEL);
-       if (!ahp->work_digest)
+       ahp->icv = ah_hmac_digest;
+       
+       /*
+        * Lookup the algorithm description maintained by pfkey,
+        * verify crypto transform properties, and store information
+        * we need for AH processing.  This lookup cannot fail here
+        * after a successful crypto_alloc_tfm().
+        */
+       aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name);
+       BUG_ON(!aalg_desc);
+
+       if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
+           crypto_tfm_alg_digestsize(ahp->tfm)) {
+               printk(KERN_INFO "AH: %s digestsize %u != %hu\n",
+                      x->aalg->alg_name, crypto_tfm_alg_digestsize(ahp->tfm),
+                      aalg_desc->uinfo.auth.icv_fullbits/8);
+               goto error;
+       }
+       
+       ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
+       ahp->icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
+       
+       ahp->work_icv = kmalloc(ahp->icv_full_len, GFP_KERNEL);
+       if (!ahp->work_icv)
                goto error;
-       x->props.header_len = (12 + ahp->digest_len + 7)&~7;
+       
+       x->props.header_len = XFRM_ALIGN8(ahp->icv_trunc_len + AH_HLEN_NOICV);
        if (x->props.mode)
                x->props.header_len += 20;
        x->data = ahp;
@@ -359,8 +391,8 @@ int ah_init_state(struct xfrm_state *x, void *args)
 
 error:
        if (ahp) {
-               if (ahp->work_digest)
-                       kfree(ahp->work_digest);
+               if (ahp->work_icv)
+                       kfree(ahp->work_icv);
                if (ahp->tfm)
                        crypto_free_tfm(ahp->tfm);
                kfree(ahp);
@@ -368,13 +400,13 @@ error:
        return -EINVAL;
 }
 
-void ah_destroy(struct xfrm_state *x)
+static void ah_destroy(struct xfrm_state *x)
 {
        struct ah_data *ahp = x->data;
 
-       if (ahp->work_digest) {
-               kfree(ahp->work_digest);
-               ahp->work_digest = NULL;
+       if (ahp->work_icv) {
+               kfree(ahp->work_icv);
+               ahp->work_icv = NULL;
        }
        if (ahp->tfm) {
                crypto_free_tfm(ahp->tfm);
@@ -399,7 +431,7 @@ static struct inet_protocol ah4_protocol = {
        .no_policy      =       1,
 };
 
-int __init ah4_init(void)
+static int __init ah4_init(void)
 {
        SET_MODULE_OWNER(&ah_type);
        if (xfrm_register_type(&ah_type) < 0) {
diff --git a/net/ipv4/xfrm_algo.c b/net/ipv4/xfrm_algo.c
new file mode 100644 (file)
index 0000000..07744c5
--- /dev/null
@@ -0,0 +1,348 @@
+/* 
+ * xfrm algorithm interface
+ *
+ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/pfkeyv2.h>
+#include <net/xfrm.h>
+
+/*
+ * Algorithms supported by IPsec.  These entries contain properties which
+ * are used in key negotiation and xfrm processing, and are used to verify
+ * that instantiated crypto transforms have correct parameters for IPsec
+ * purposes.
+ */
+static struct xfrm_algo_desc aalg_list[] = {
+{
+       .name = "digest_null",
+       
+       .uinfo = {
+               .auth = {
+                       .icv_truncbits = 0,
+                       .icv_fullbits = 0,
+               }
+       },
+       
+       .desc = {
+               .sadb_alg_id = SADB_X_AALG_NULL,
+               .sadb_alg_ivlen = 0,
+               .sadb_alg_minbits = 0,
+               .sadb_alg_maxbits = 0
+       }
+},
+{
+       .name = "md5",
+
+       .uinfo = {
+               .auth = {
+                       .icv_truncbits = 96,
+                       .icv_fullbits = 128,
+               }
+       },
+       
+       .desc = {
+               .sadb_alg_id = SADB_AALG_MD5HMAC,
+               .sadb_alg_ivlen = 0,
+               .sadb_alg_minbits = 128,
+               .sadb_alg_maxbits = 128
+       }
+},
+{
+       .name = "sha1",
+
+       .uinfo = {
+               .auth = {
+                       .icv_truncbits = 96,
+                       .icv_fullbits = 160,
+               }
+       },
+
+       .desc = {
+               .sadb_alg_id = SADB_AALG_SHA1HMAC,
+               .sadb_alg_ivlen = 0,
+               .sadb_alg_minbits = 160,
+               .sadb_alg_maxbits = 160
+       }
+},
+{
+       .name = "sha256",
+
+       .uinfo = {
+               .auth = {
+                       .icv_truncbits = 128,
+                       .icv_fullbits = 256,
+               }
+       },
+
+       .desc = {
+               .sadb_alg_id = SADB_X_AALG_SHA2_256HMAC,
+               .sadb_alg_ivlen = 0,
+               .sadb_alg_minbits = 256,
+               .sadb_alg_maxbits = 256
+       }
+},
+{
+       .name = "ripemd160",
+
+       .uinfo = {
+               .auth = {
+                       .icv_truncbits = 96,
+                       .icv_fullbits = 160,
+               }
+       },
+
+       .desc = {
+               .sadb_alg_id = SADB_X_AALG_RIPEMD160HMAC,
+               .sadb_alg_ivlen = 0,
+               .sadb_alg_minbits = 160,
+               .sadb_alg_maxbits = 160
+       }
+},
+};
+
+static struct xfrm_algo_desc ealg_list[] = {
+{
+       .name = "cipher_null",
+       
+       .uinfo = {
+               .encr = {
+                       .blockbits = 8,
+                       .defkeybits = 0,
+               }
+       },
+       
+       .desc = {
+               .sadb_alg_id =  SADB_EALG_NULL,
+               .sadb_alg_ivlen = 0,
+               .sadb_alg_minbits = 0,
+               .sadb_alg_maxbits = 0
+       }
+},
+{
+       .name = "des",
+
+       .uinfo = {
+               .encr = {
+                       .blockbits = 64,
+                       .defkeybits = 64,
+               }
+       },
+
+       .desc = {
+               .sadb_alg_id = SADB_EALG_DESCBC,
+               .sadb_alg_ivlen = 8,
+               .sadb_alg_minbits = 64,
+               .sadb_alg_maxbits = 64
+       }
+},
+{
+       .name = "des3_ede",
+
+       .uinfo = {
+               .encr = {
+                       .blockbits = 64,
+                       .defkeybits = 192,
+               }
+       },
+
+       .desc = {
+               .sadb_alg_id = SADB_EALG_3DESCBC,
+               .sadb_alg_ivlen = 8,
+               .sadb_alg_minbits = 192,
+               .sadb_alg_maxbits = 192
+       }
+},
+{
+       .name = "cast128",
+
+       .uinfo = {
+               .encr = {
+                       .blockbits = 64,
+                       .defkeybits = 128,
+               }
+       },
+
+       .desc = {
+               .sadb_alg_id = SADB_X_EALG_CASTCBC,
+               .sadb_alg_ivlen = 8,
+               .sadb_alg_minbits = 40,
+               .sadb_alg_maxbits = 128
+       }
+},
+{
+       .name = "blowfish",
+
+       .uinfo = {
+               .encr = {
+                       .blockbits = 64,
+                       .defkeybits = 128,
+               }
+       },
+
+       .desc = {
+               .sadb_alg_id = SADB_X_EALG_BLOWFISHCBC,
+               .sadb_alg_ivlen = 8,
+               .sadb_alg_minbits = 40,
+               .sadb_alg_maxbits = 448
+       }
+},
+{
+       .name = "aes",
+
+       .uinfo = {
+               .encr = {
+                       .blockbits = 128,
+                       .defkeybits = 128,
+               }
+       },
+
+       .desc = {
+               .sadb_alg_id = SADB_X_EALG_AESCBC,
+               .sadb_alg_ivlen = 8,
+               .sadb_alg_minbits = 128,
+               .sadb_alg_maxbits = 256
+       }
+},
+};
+
+static inline int aalg_entries(void)
+{
+       return sizeof(aalg_list) / sizeof(aalg_list[0]);
+}
+
+static inline int ealg_entries(void)
+{
+       return sizeof(ealg_list) / sizeof(ealg_list[0]);
+}
+
+struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id)
+{
+       int i;
+
+       for (i = 0; i < aalg_entries(); i++) {
+               if (aalg_list[i].desc.sadb_alg_id == alg_id) {
+                       if (aalg_list[i].available)
+                               return &aalg_list[i];
+                       else
+                               break;
+               }
+       }
+       return NULL;
+}
+
+struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id)
+{
+       int i;
+
+       for (i = 0; i < ealg_entries(); i++) {
+               if (ealg_list[i].desc.sadb_alg_id == alg_id) {
+                       if (ealg_list[i].available)
+                               return &ealg_list[i];
+                       else
+                               break;
+               }
+       }
+       return NULL;
+}
+
+struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name)
+{
+       int i;
+
+       if (!name)
+               return NULL;
+
+       for (i=0; i < aalg_entries(); i++) {
+               if (strcmp(name, aalg_list[i].name) == 0) {
+                       if (aalg_list[i].available)
+                               return &aalg_list[i];
+                       else
+                               break;
+               }
+       }
+       return NULL;
+}
+
+struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name)
+{
+       int i;
+
+       if (!name)
+               return NULL;
+
+       for (i=0; i < ealg_entries(); i++) {
+               if (strcmp(name, ealg_list[i].name) == 0) {
+                       if (ealg_list[i].available)
+                               return &ealg_list[i];
+                       else
+                               break;
+               }
+       }
+       return NULL;
+}
+
+struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx)
+{
+       if (idx >= aalg_entries())
+               return NULL;
+
+       return &aalg_list[idx];
+}
+
+struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx)
+{
+       if (idx >= ealg_entries())
+               return NULL;
+
+       return &ealg_list[idx];
+}
+
+/*
+ * Probe for the availability of crypto algorithms, and set the available
+ * flag for any algorithms found on the system.  This is typically called by
+ * pfkey during userspace SA add, update or register.
+ */
+void xfrm_probe_algs(void)
+{
+       int i, status;
+       
+       BUG_ON(in_softirq());
+
+       for (i = 0; i < aalg_entries(); i++) {
+               status = crypto_alg_available(aalg_list[i].name, 0);
+               if (aalg_list[i].available != status)
+                       aalg_list[i].available = status;
+       }
+       
+       for (i = 0; i < ealg_entries(); i++) {
+               status = crypto_alg_available(ealg_list[i].name, 0);
+               if (ealg_list[i].available != status)
+                       ealg_list[i].available = status;
+       }
+}
+
+int xfrm_count_auth_supported(void)
+{
+       int i, n;
+
+       for (i = 0, n = 0; i < aalg_entries(); i++)
+               if (aalg_list[i].available)
+                       n++;
+       return n;
+}
+
+int xfrm_count_enc_supported(void)
+{
+       int i, n;
+
+       for (i = 0, n = 0; i < ealg_entries(); i++)
+               if (ealg_list[i].available)
+                       n++;
+       return n;
+}
index f26b06de650ea06ca276b81f5f9d7b661b98bcb6..f2b013194120e5e16938eb9f634b81d465038466 100644 (file)
@@ -553,118 +553,6 @@ static struct  xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **
        return x;
 }
 
-/* Table of algos supported by pfkeyv2 interface. */
-
-struct algo_desc {
-       char            *id;
-       struct sadb_alg desc;
-};
-
-struct algo_desc aalg_list[] = {
-{ .id  = NULL,
-         .desc = {
-                 .sadb_alg_id =        SADB_AALG_NONE,
-                 .sadb_alg_ivlen = 0,
-                 .sadb_alg_minbits = 0,
-                 .sadb_alg_maxbits = 0
-         }
-},
-{ .id  = "md5",
-         .desc = {
-                 .sadb_alg_id =        SADB_AALG_MD5HMAC,
-                 .sadb_alg_ivlen = 0,
-                 .sadb_alg_minbits = 128,
-                 .sadb_alg_maxbits = 128
-         }
-},
-{ .id  = "sha1",
-         .desc = {
-                 .sadb_alg_id =        SADB_AALG_SHA1HMAC,
-                 .sadb_alg_ivlen = 0,
-                 .sadb_alg_minbits = 160,
-                 .sadb_alg_maxbits = 160
-         }
-}
-};
-
-struct algo_desc ealg_list[] = {
-{ .id  = NULL,
-         .desc = {
-                 .sadb_alg_id =        SADB_EALG_NONE,
-                 .sadb_alg_ivlen = 0,
-                 .sadb_alg_minbits = 0,
-                 .sadb_alg_maxbits = 2048
-         }
-},
-{ .id  = "des",
-         .desc = {
-                 .sadb_alg_id =        SADB_EALG_DESCBC,
-                 .sadb_alg_ivlen = 8,
-                 .sadb_alg_minbits = 64,
-                 .sadb_alg_maxbits = 64
-         }
-},
-{ .id  = "des3_ede",
-         .desc = {
-                 .sadb_alg_id =        SADB_EALG_3DESCBC,
-                 .sadb_alg_ivlen = 8,
-                 .sadb_alg_minbits = 192,
-                 .sadb_alg_maxbits = 192
-         }
-}
-};
-
-static struct algo_desc *aalg_get_byid(int alg_id)     
-{
-       int i;
-
-       for (i=0; i<sizeof(aalg_list)/sizeof(aalg_list[0]); i++) {
-               if (aalg_list[i].desc.sadb_alg_id == alg_id)
-                       return &aalg_list[i];
-       }
-       return NULL;
-}
-
-static struct algo_desc *ealg_get_byid(int alg_id)     
-{
-       int i;
-
-       for (i=0; i<sizeof(ealg_list)/sizeof(ealg_list[0]); i++) {
-               if (ealg_list[i].desc.sadb_alg_id == alg_id)
-                       return &ealg_list[i];
-       }
-       return NULL;
-}
-
-static struct algo_desc *aalg_get_byname(char *name)   
-{
-       int i;
-
-       if (!name)
-               return NULL;
-
-       for (i=1; i<sizeof(aalg_list)/sizeof(aalg_list[0]); i++) {
-               if (strcmp(name, aalg_list[i].id) == 0)
-                       return &aalg_list[i];
-       }
-       return NULL;
-}
-
-static struct algo_desc *ealg_get_byname(char *name)   
-{
-       int i;
-
-       if (!name)
-               return NULL;
-
-       for (i=1; i<sizeof(ealg_list)/sizeof(ealg_list[0]); i++) {
-               if (strcmp(name, ealg_list[i].id) == 0)
-                       return &ealg_list[i];
-       }
-       return NULL;
-}
-
-
 #define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1)))
 static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, int hsc)
 {
@@ -730,12 +618,12 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
                sa->sadb_sa_state = SADB_SASTATE_DEAD;
        sa->sadb_sa_auth = 0;
        if (x->aalg) {
-               struct algo_desc *a = aalg_get_byname(x->aalg->alg_name);
+               struct xfrm_algo_desc *a = xfrm_aalg_get_byname(x->aalg->alg_name);
                sa->sadb_sa_auth = a ? a->desc.sadb_alg_id : 0;
        }
        sa->sadb_sa_encrypt = 0;
        if (x->ealg) {
-               struct algo_desc *a = ealg_get_byname(x->ealg->alg_name);
+               struct xfrm_algo_desc *a = xfrm_ealg_get_byname(x->ealg->alg_name);
                sa->sadb_sa_encrypt = a ? a->desc.sadb_alg_id : 0;
        }
        sa->sadb_sa_flags = 0;
@@ -938,7 +826,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
        key = (struct sadb_key*) ext_hdrs[SADB_EXT_KEY_AUTH-1];
        if (sa->sadb_sa_auth) {
                int keysize = 0;
-               struct algo_desc *a = aalg_get_byid(sa->sadb_sa_auth);
+               struct xfrm_algo_desc *a = xfrm_aalg_get_byid(sa->sadb_sa_auth);
                if (!a)
                        goto out;
                if (key)
@@ -946,7 +834,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
                x->aalg = kmalloc(sizeof(*x->aalg) + keysize, GFP_KERNEL);
                if (!x->aalg)
                        goto out;
-               strcpy(x->aalg->alg_name, a->id);
+               strcpy(x->aalg->alg_name, a->name);
                x->aalg->alg_key_len = 0;
                if (key) {
                        x->aalg->alg_key_len = key->sadb_key_bits;
@@ -958,7 +846,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
        key = (struct sadb_key*) ext_hdrs[SADB_EXT_KEY_ENCRYPT-1];
        if (sa->sadb_sa_encrypt) {
                int keysize = 0;
-               struct algo_desc *a = ealg_get_byid(sa->sadb_sa_encrypt);
+               struct xfrm_algo_desc *a = xfrm_ealg_get_byid(sa->sadb_sa_encrypt);
                if (!a)
                        goto out;
                if (key)
@@ -966,7 +854,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
                x->ealg = kmalloc(sizeof(*x->ealg) + keysize, GFP_KERNEL);
                if (!x->ealg)
                        goto out;
-               strcpy(x->ealg->alg_name, a->id);
+               strcpy(x->ealg->alg_name, a->name);
                x->ealg->alg_key_len = 0;
                if (key) {
                        x->ealg->alg_key_len = key->sadb_key_bits;
@@ -1131,6 +1019,8 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
        struct xfrm_state *x;
        struct xfrm_state *x1;
 
+       xfrm_probe_algs();
+       
        x = pfkey_msg2xfrm_state(hdr, ext_hdrs);
        if (IS_ERR(x))
                return PTR_ERR(x);
@@ -1238,17 +1128,21 @@ static struct sk_buff *compose_sadb_supported(struct sadb_msg *orig, int allocat
 {
        struct sk_buff *skb;
        struct sadb_msg *hdr;
-       int len, ah_len, esp_len, i;
-
-       ah_len = sizeof(aalg_list)/sizeof(aalg_list[0]) - 1;
-       ah_len *= sizeof(struct sadb_alg);
-       esp_len = sizeof(ealg_list)/sizeof(ealg_list[0]) - 1;
-       esp_len *= sizeof(struct sadb_alg);
-       if (ah_len)
-               ah_len += sizeof(struct sadb_supported);
-       if (esp_len)
-               esp_len += sizeof(struct sadb_supported);
-       len = esp_len + ah_len + sizeof(struct sadb_msg);
+       int len, auth_len, enc_len, i;
+
+       auth_len = xfrm_count_auth_supported();
+       if (auth_len) {
+               auth_len *= sizeof(struct sadb_alg);
+               auth_len += sizeof(struct sadb_supported);
+       }
+       
+       enc_len = xfrm_count_enc_supported();
+       if (enc_len) {
+               enc_len *= sizeof(struct sadb_alg);
+               enc_len += sizeof(struct sadb_supported);
+       }
+       
+       len = enc_len + auth_len + sizeof(struct sadb_msg);
 
        skb = alloc_skb(len + 16, allocation);
        if (!skb)
@@ -1259,32 +1153,42 @@ static struct sk_buff *compose_sadb_supported(struct sadb_msg *orig, int allocat
        hdr->sadb_msg_errno = 0;
        hdr->sadb_msg_len = len / sizeof(uint64_t);
 
-       if (ah_len) {
+       if (auth_len) {
                struct sadb_supported *sp;
                struct sadb_alg *ap;
 
-               sp = (struct sadb_supported *) skb_put(skb, ah_len);
+               sp = (struct sadb_supported *) skb_put(skb, auth_len);
                ap = (struct sadb_alg *) (sp + 1);
 
-               sp->sadb_supported_len = ah_len / sizeof(uint64_t);
+               sp->sadb_supported_len = auth_len / sizeof(uint64_t);
                sp->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH;
 
-               for (i=1; i<sizeof(aalg_list)/sizeof(aalg_list[0]); i++)
-                       *ap++ = aalg_list[i].desc;
+               for (i = 0; ; i++) {
+                       struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
+                       if (!aalg)
+                               break;
+                       if (aalg->available)
+                               *ap++ = aalg->desc;
+               }
        }
 
-       if (esp_len) {
+       if (enc_len) {
                struct sadb_supported *sp;
                struct sadb_alg *ap;
 
-               sp = (struct sadb_supported *) skb_put(skb, esp_len);
+               sp = (struct sadb_supported *) skb_put(skb, enc_len);
                ap = (struct sadb_alg *) (sp + 1);
 
-               sp->sadb_supported_len = esp_len / sizeof(uint64_t);
+               sp->sadb_supported_len = enc_len / sizeof(uint64_t);
                sp->sadb_supported_exttype = SADB_EXT_SUPPORTED_ENCRYPT;
 
-               for (i=1; i<sizeof(ealg_list)/sizeof(ealg_list[0]); i++)
-                       *ap++ = ealg_list[i].desc;
+               for (i = 0; ; i++) {
+                       struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
+                       if (!ealg)
+                               break;
+                       if (ealg->available)
+                               *ap++ = ealg->desc;
+               }
        }
 
 out_put_algs:
@@ -1305,6 +1209,8 @@ static int pfkey_register(struct sock *sk, struct sk_buff *skb, struct sadb_msg
                pfk->registered |= (1<<hdr->sadb_msg_satype);
        }
 
+       xfrm_probe_algs();
+       
        supp_skb = compose_sadb_supported(hdr, GFP_KERNEL);
        if (!supp_skb) {
                if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC)
@@ -1955,35 +1861,55 @@ static struct sadb_msg *pfkey_get_base_msg(struct sk_buff *skb, int *errp)
        return hdr;
 }
 
-int count_ah_combs(struct xfrm_tmpl *t)
+static inline int aalg_tmpl_set(struct xfrm_tmpl *t, struct xfrm_algo_desc *d)
 {
-       int sz = 0;
-       int i;
+       return t->aalgos & (1 << d->desc.sadb_alg_id);
+}
 
-       for (i=1; i<sizeof(aalg_list)/sizeof(aalg_list[0]); i++) {
-               if (t->aalgos&(1<<aalg_list[i].desc.sadb_alg_id))
+static inline int ealg_tmpl_set(struct xfrm_tmpl *t, struct xfrm_algo_desc *d)
+{
+       return t->ealgos & (1 << d->desc.sadb_alg_id);
+}
+
+static int count_ah_combs(struct xfrm_tmpl *t)
+{
+       int i, sz = 0;
+
+       for (i = 0; ; i++) {
+               struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
+               if (!aalg)
+                       break;
+               if (aalg_tmpl_set(t, aalg) && aalg->available)
                        sz += sizeof(struct sadb_comb);
        }
        return sz + sizeof(struct sadb_prop);
 }
 
-int count_esp_combs(struct xfrm_tmpl *t)
+static int count_esp_combs(struct xfrm_tmpl *t)
 {
-       int sz = 0;
-       int i, k;
+       int i, k, sz = 0;
 
-       for (i=1; i<sizeof(ealg_list)/sizeof(ealg_list[0]); i++) {
-               if (!(t->ealgos&(1<<ealg_list[i].desc.sadb_alg_id)))
+       for (i = 0; ; i++) {
+               struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
+               if (!ealg)
+                       break;
+                       
+               if (!(ealg_tmpl_set(t, ealg) && ealg->available))
                        continue;
-               for (k=1; k<sizeof(aalg_list)/sizeof(aalg_list[0]); k++) {
-                       if (t->aalgos&(1<<aalg_list[i].desc.sadb_alg_id))
+                       
+               for (k = 1; ; k++) {
+                       struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k);
+                       if (!aalg)
+                               break;
+                               
+                       if (aalg_tmpl_set(t, aalg) && aalg->available)
                                sz += sizeof(struct sadb_comb);
                }
        }
        return sz + sizeof(struct sadb_prop);
 }
 
-void dump_ah_combs(struct sk_buff *skb, struct xfrm_tmpl *t)
+static void dump_ah_combs(struct sk_buff *skb, struct xfrm_tmpl *t)
 {
        struct sadb_prop *p;
        int i;
@@ -1993,15 +1919,19 @@ void dump_ah_combs(struct sk_buff *skb, struct xfrm_tmpl *t)
        p->sadb_prop_exttype = SADB_EXT_PROPOSAL;
        p->sadb_prop_replay = 32;
 
-       for (i=1; i<sizeof(aalg_list)/sizeof(aalg_list[0]); i++) {
-               if (t->aalgos&(1<<aalg_list[i].desc.sadb_alg_id)) {
+       for (i = 0; ; i++) {
+               struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i);
+               if (!aalg)
+                       break;
+
+               if (aalg_tmpl_set(t, aalg) && aalg->available) {
                        struct sadb_comb *c;
                        c = (struct sadb_comb*)skb_put(skb, sizeof(struct sadb_comb));
                        memset(c, 0, sizeof(*c));
                        p->sadb_prop_len += sizeof(struct sadb_comb)/8;
-                       c->sadb_comb_auth = aalg_list[i].desc.sadb_alg_id;
-                       c->sadb_comb_auth_minbits = aalg_list[i].desc.sadb_alg_minbits;
-                       c->sadb_comb_auth_maxbits = aalg_list[i].desc.sadb_alg_maxbits;
+                       c->sadb_comb_auth = aalg->desc.sadb_alg_id;
+                       c->sadb_comb_auth_minbits = aalg->desc.sadb_alg_minbits;
+                       c->sadb_comb_auth_maxbits = aalg->desc.sadb_alg_maxbits;
                        c->sadb_comb_hard_addtime = 24*60*60;
                        c->sadb_comb_soft_addtime = 20*60*60;
                        c->sadb_comb_hard_usetime = 8*60*60;
@@ -2010,7 +1940,7 @@ void dump_ah_combs(struct sk_buff *skb, struct xfrm_tmpl *t)
        }
 }
 
-void dump_esp_combs(struct sk_buff *skb, struct xfrm_tmpl *t)
+static void dump_esp_combs(struct sk_buff *skb, struct xfrm_tmpl *t)
 {
        struct sadb_prop *p;
        int i, k;
@@ -2020,22 +1950,30 @@ void dump_esp_combs(struct sk_buff *skb, struct xfrm_tmpl *t)
        p->sadb_prop_exttype = SADB_EXT_PROPOSAL;
        p->sadb_prop_replay = 32;
 
-       for (i=1; i<sizeof(ealg_list)/sizeof(ealg_list[0]); i++) {
-               if (!(t->ealgos&(1<<ealg_list[i].desc.sadb_alg_id)))
+       for (i=0; ; i++) {
+               struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i);
+               if (!ealg)
+                       break;
+       
+               if (!(ealg_tmpl_set(t, ealg) && ealg->available))
                        continue;
-               for (k=1; k<sizeof(aalg_list)/sizeof(aalg_list[0]); k++) {
+                       
+               for (k = 1; ; k++) {
                        struct sadb_comb *c;
-                       if (!(t->aalgos&(1<<aalg_list[i].desc.sadb_alg_id)))
+                       struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k);
+                       if (!aalg)
+                               break;
+                       if (!(aalg_tmpl_set(t, aalg) && aalg->available))
                                continue;
                        c = (struct sadb_comb*)skb_put(skb, sizeof(struct sadb_comb));
                        memset(c, 0, sizeof(*c));
                        p->sadb_prop_len += sizeof(struct sadb_comb)/8;
-                       c->sadb_comb_auth = aalg_list[k].desc.sadb_alg_id;
-                       c->sadb_comb_auth_minbits = aalg_list[k].desc.sadb_alg_minbits;
-                       c->sadb_comb_auth_maxbits = aalg_list[k].desc.sadb_alg_maxbits;
-                       c->sadb_comb_encrypt = ealg_list[i].desc.sadb_alg_id;
-                       c->sadb_comb_encrypt_minbits = ealg_list[i].desc.sadb_alg_minbits;
-                       c->sadb_comb_encrypt_maxbits = ealg_list[i].desc.sadb_alg_maxbits;
+                       c->sadb_comb_auth = aalg->desc.sadb_alg_id;
+                       c->sadb_comb_auth_minbits = aalg->desc.sadb_alg_minbits;
+                       c->sadb_comb_auth_maxbits = aalg->desc.sadb_alg_maxbits;
+                       c->sadb_comb_encrypt = ealg->desc.sadb_alg_id;
+                       c->sadb_comb_encrypt_minbits = ealg->desc.sadb_alg_minbits;
+                       c->sadb_comb_encrypt_maxbits = ealg->desc.sadb_alg_maxbits;
                        c->sadb_comb_hard_addtime = 24*60*60;
                        c->sadb_comb_soft_addtime = 20*60*60;
                        c->sadb_comb_hard_usetime = 8*60*60;
index c387bd415bd3c8397d0305ac868767382127770f..0528cd35b1006b3a1be3ef3f352c44bcd29b5a60 100644 (file)
@@ -323,6 +323,15 @@ EXPORT_SYMBOL(xfrm_policy_flush);
 EXPORT_SYMBOL(xfrm_policy_byid);
 EXPORT_SYMBOL(xfrm_policy_list);
 
+EXPORT_SYMBOL_GPL(xfrm_probe_algs);
+EXPORT_SYMBOL_GPL(xfrm_count_auth_supported);
+EXPORT_SYMBOL_GPL(xfrm_count_enc_supported);
+EXPORT_SYMBOL_GPL(xfrm_aalg_get_byidx);
+EXPORT_SYMBOL_GPL(xfrm_ealg_get_byidx);
+EXPORT_SYMBOL_GPL(xfrm_aalg_get_byid);
+EXPORT_SYMBOL_GPL(xfrm_ealg_get_byid);
+EXPORT_SYMBOL_GPL(xfrm_aalg_get_byname);
+EXPORT_SYMBOL_GPL(xfrm_ealg_get_byname);
 
 #if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_IP_SCTP_MODULE)
 /* inet functions common to v4 and v6 */