Blame view

net/ipv4/ah4.c 12.2 KB
dff3bb062   Steffen Klassert   ah4: convert to a...
1
  #include <crypto/hash.h>
07d4ee583   Herbert Xu   [IPSEC]: Use HMAC...
2
  #include <linux/err.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
4
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
8
9
  #include <net/ip.h>
  #include <net/xfrm.h>
  #include <net/ah.h>
  #include <linux/crypto.h>
  #include <linux/pfkeyv2.h>
dff3bb062   Steffen Klassert   ah4: convert to a...
10
  #include <linux/scatterlist.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
  #include <net/icmp.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
12
  #include <net/protocol.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13

dff3bb062   Steffen Klassert   ah4: convert to a...
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  struct ah_skb_cb {
  	struct xfrm_skb_cb xfrm;
  	void *tmp;
  };
  
  #define AH_SKB_CB(__skb) ((struct ah_skb_cb *)&((__skb)->cb[0]))
  
  static void *ah_alloc_tmp(struct crypto_ahash *ahash, int nfrags,
  			  unsigned int size)
  {
  	unsigned int len;
  
  	len = size + crypto_ahash_digestsize(ahash) +
  	      (crypto_ahash_alignmask(ahash) &
  	       ~(crypto_tfm_ctx_alignment() - 1));
  
  	len = ALIGN(len, crypto_tfm_ctx_alignment());
  
  	len += sizeof(struct ahash_request) + crypto_ahash_reqsize(ahash);
  	len = ALIGN(len, __alignof__(struct scatterlist));
  
  	len += sizeof(struct scatterlist) * nfrags;
  
  	return kmalloc(len, GFP_ATOMIC);
  }
  
  static inline u8 *ah_tmp_auth(void *tmp, unsigned int offset)
  {
  	return tmp + offset;
  }
  
  static inline u8 *ah_tmp_icv(struct crypto_ahash *ahash, void *tmp,
  			     unsigned int offset)
  {
  	return PTR_ALIGN((u8 *)tmp + offset, crypto_ahash_alignmask(ahash) + 1);
  }
  
  static inline struct ahash_request *ah_tmp_req(struct crypto_ahash *ahash,
  					       u8 *icv)
  {
  	struct ahash_request *req;
  
  	req = (void *)PTR_ALIGN(icv + crypto_ahash_digestsize(ahash),
  				crypto_tfm_ctx_alignment());
  
  	ahash_request_set_tfm(req, ahash);
  
  	return req;
  }
  
  static inline struct scatterlist *ah_req_sg(struct crypto_ahash *ahash,
  					     struct ahash_request *req)
  {
  	return (void *)ALIGN((unsigned long)(req + 1) +
  			     crypto_ahash_reqsize(ahash),
  			     __alignof__(struct scatterlist));
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
74
  
  /* Clear mutable options and find final destination to substitute
   * into IP header for icv calculation. Options are already checked
   * for validity, so paranoia is not required. */
b71d1d426   Eric Dumazet   inet: constify ip...
75
  static int ip_clear_mutable_options(const struct iphdr *iph, __be32 *daddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  {
  	unsigned char * optptr = (unsigned char*)(iph+1);
  	int  l = iph->ihl*4 - sizeof(struct iphdr);
  	int  optlen;
  
  	while (l > 0) {
  		switch (*optptr) {
  		case IPOPT_END:
  			return 0;
  		case IPOPT_NOOP:
  			l--;
  			optptr++;
  			continue;
  		}
  		optlen = optptr[1];
  		if (optlen<2 || optlen>l)
  			return -EINVAL;
  		switch (*optptr) {
  		case IPOPT_SEC:
  		case 0x85:	/* Some "Extended Security" crap. */
11a03f78f   Paul Moore   [NetLabel]: core ...
96
  		case IPOPT_CIPSO:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
99
100
101
102
103
104
105
106
  		case IPOPT_RA:
  		case 0x80|21:	/* RFC1770 */
  			break;
  		case IPOPT_LSRR:
  		case IPOPT_SSRR:
  			if (optlen < 6)
  				return -EINVAL;
  			memcpy(daddr, optptr+optlen-4, 4);
  			/* Fall through */
  		default:
96fe1c023   Nick Bowler   [IPSEC] AH4: Upda...
107
  			memset(optptr, 0, optlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
111
112
113
  		}
  		l -= optlen;
  		optptr += optlen;
  	}
  	return 0;
  }
dff3bb062   Steffen Klassert   ah4: convert to a...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  static void ah_output_done(struct crypto_async_request *base, int err)
  {
  	u8 *icv;
  	struct iphdr *iph;
  	struct sk_buff *skb = base->data;
  	struct xfrm_state *x = skb_dst(skb)->xfrm;
  	struct ah_data *ahp = x->data;
  	struct iphdr *top_iph = ip_hdr(skb);
  	struct ip_auth_hdr *ah = ip_auth_hdr(skb);
  	int ihl = ip_hdrlen(skb);
  
  	iph = AH_SKB_CB(skb)->tmp;
  	icv = ah_tmp_icv(ahp->ahash, iph, ihl);
  	memcpy(ah->auth_data, icv, ahp->icv_trunc_len);
  
  	top_iph->tos = iph->tos;
  	top_iph->ttl = iph->ttl;
  	top_iph->frag_off = iph->frag_off;
  	if (top_iph->ihl != 5) {
  		top_iph->daddr = iph->daddr;
  		memcpy(top_iph+1, iph+1, top_iph->ihl*4 - sizeof(struct iphdr));
  	}
dff3bb062   Steffen Klassert   ah4: convert to a...
136
137
138
  	kfree(AH_SKB_CB(skb)->tmp);
  	xfrm_output_resume(skb, err);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
140
141
  static int ah_output(struct xfrm_state *x, struct sk_buff *skb)
  {
  	int err;
dff3bb062   Steffen Klassert   ah4: convert to a...
142
143
144
145
146
147
148
  	int nfrags;
  	int ihl;
  	u8 *icv;
  	struct sk_buff *trailer;
  	struct crypto_ahash *ahash;
  	struct ahash_request *req;
  	struct scatterlist *sg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
  	struct iphdr *iph, *top_iph;
  	struct ip_auth_hdr *ah;
  	struct ah_data *ahp;
dff3bb062   Steffen Klassert   ah4: convert to a...
152
153
154
155
156
157
158
  
  	ahp = x->data;
  	ahash = ahp->ahash;
  
  	if ((err = skb_cow_data(skb, 0, &trailer)) < 0)
  		goto out;
  	nfrags = err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159

7b277b1a5   Herbert Xu   [IPSEC]: Set skb-...
160
  	skb_push(skb, -skb_network_offset(skb));
dff3bb062   Steffen Klassert   ah4: convert to a...
161
162
163
164
165
166
167
168
169
170
171
172
173
  	ah = ip_auth_hdr(skb);
  	ihl = ip_hdrlen(skb);
  
  	err = -ENOMEM;
  	iph = ah_alloc_tmp(ahash, nfrags, ihl);
  	if (!iph)
  		goto out;
  
  	icv = ah_tmp_icv(ahash, iph, ihl);
  	req = ah_tmp_req(ahash, icv);
  	sg = ah_req_sg(ahash, req);
  
  	memset(ah->auth_data, 0, ahp->icv_trunc_len);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
174
  	top_iph = ip_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
179
180
181
182
183
184
  
  	iph->tos = top_iph->tos;
  	iph->ttl = top_iph->ttl;
  	iph->frag_off = top_iph->frag_off;
  
  	if (top_iph->ihl != 5) {
  		iph->daddr = top_iph->daddr;
  		memcpy(iph+1, top_iph+1, top_iph->ihl*4 - sizeof(struct iphdr));
  		err = ip_clear_mutable_options(top_iph, &top_iph->daddr);
  		if (err)
dff3bb062   Steffen Klassert   ah4: convert to a...
185
  			goto out_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
  	}
37fedd3aa   Herbert Xu   [IPSEC]: Use IPv6...
187
188
  	ah->nexthdr = *skb_mac_header(skb);
  	*skb_mac_header(skb) = IPPROTO_AH;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
192
193
  
  	top_iph->tos = 0;
  	top_iph->tot_len = htons(skb->len);
  	top_iph->frag_off = 0;
  	top_iph->ttl = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  	top_iph->check = 0;
fa9921e46   Nicolas Dichtel   ipsec: allow to a...
195
196
197
198
  	if (x->props.flags & XFRM_STATE_ALIGN4)
  		ah->hdrlen  = (XFRM_ALIGN4(sizeof(*ah) + ahp->icv_trunc_len) >> 2) - 2;
  	else
  		ah->hdrlen  = (XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len) >> 2) - 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
  
  	ah->reserved = 0;
  	ah->spi = x->id.spi;
1ce3644ad   Steffen Klassert   xfrm: Use separat...
202
  	ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
b7c6538cd   Herbert Xu   [IPSEC]: Move sta...
203

dff3bb062   Steffen Klassert   ah4: convert to a...
204
205
  	sg_init_table(sg, nfrags);
  	skb_to_sgvec(skb, sg, 0, skb->len);
b7c6538cd   Herbert Xu   [IPSEC]: Move sta...
206

dff3bb062   Steffen Klassert   ah4: convert to a...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
  	ahash_request_set_crypt(req, sg, icv, skb->len);
  	ahash_request_set_callback(req, 0, ah_output_done, skb);
  
  	AH_SKB_CB(skb)->tmp = iph;
  
  	err = crypto_ahash_digest(req);
  	if (err) {
  		if (err == -EINPROGRESS)
  			goto out;
  
  		if (err == -EBUSY)
  			err = NET_XMIT_DROP;
  		goto out_free;
  	}
  
  	memcpy(ah->auth_data, icv, ahp->icv_trunc_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
225
226
227
228
229
230
  
  	top_iph->tos = iph->tos;
  	top_iph->ttl = iph->ttl;
  	top_iph->frag_off = iph->frag_off;
  	if (top_iph->ihl != 5) {
  		top_iph->daddr = iph->daddr;
  		memcpy(top_iph+1, iph+1, top_iph->ihl*4 - sizeof(struct iphdr));
  	}
dff3bb062   Steffen Klassert   ah4: convert to a...
231
232
233
  out_free:
  	kfree(iph);
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
  	return err;
  }
dff3bb062   Steffen Klassert   ah4: convert to a...
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
  static void ah_input_done(struct crypto_async_request *base, int err)
  {
  	u8 *auth_data;
  	u8 *icv;
  	struct iphdr *work_iph;
  	struct sk_buff *skb = base->data;
  	struct xfrm_state *x = xfrm_input_state(skb);
  	struct ah_data *ahp = x->data;
  	struct ip_auth_hdr *ah = ip_auth_hdr(skb);
  	int ihl = ip_hdrlen(skb);
  	int ah_hlen = (ah->hdrlen + 2) << 2;
  
  	work_iph = AH_SKB_CB(skb)->tmp;
  	auth_data = ah_tmp_auth(work_iph, ihl);
  	icv = ah_tmp_icv(ahp->ahash, auth_data, ahp->icv_trunc_len);
  
  	err = memcmp(icv, auth_data, ahp->icv_trunc_len) ? -EBADMSG: 0;
  	if (err)
  		goto out;
b7ea81a58   Nick Bowler   ah: Read nexthdr ...
255
  	err = ah->nexthdr;
dff3bb062   Steffen Klassert   ah4: convert to a...
256
257
258
259
  	skb->network_header += ah_hlen;
  	memcpy(skb_network_header(skb), work_iph, ihl);
  	__skb_pull(skb, ah_hlen + ihl);
  	skb_set_transport_header(skb, -ihl);
dff3bb062   Steffen Klassert   ah4: convert to a...
260
261
262
263
  out:
  	kfree(AH_SKB_CB(skb)->tmp);
  	xfrm_input_resume(skb, err);
  }
e695633e2   Herbert Xu   [IPSEC]: Kill unu...
264
  static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
  {
  	int ah_hlen;
31a4ab930   Herbert Xu   [IPSEC] proto: Mo...
267
  	int ihl;
631a6698d   Herbert Xu   [IPSEC]: Move IP ...
268
  	int nexthdr;
dff3bb062   Steffen Klassert   ah4: convert to a...
269
270
271
272
273
274
275
276
  	int nfrags;
  	u8 *auth_data;
  	u8 *icv;
  	struct sk_buff *trailer;
  	struct crypto_ahash *ahash;
  	struct ahash_request *req;
  	struct scatterlist *sg;
  	struct iphdr *iph, *work_iph;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
  	struct ip_auth_hdr *ah;
  	struct ah_data *ahp;
dff3bb062   Steffen Klassert   ah4: convert to a...
279
  	int err = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280

87bdc48d3   Herbert Xu   [IPSEC]: Get rid ...
281
  	if (!pskb_may_pull(skb, sizeof(*ah)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
  		goto out;
87bdc48d3   Herbert Xu   [IPSEC]: Get rid ...
283
  	ah = (struct ip_auth_hdr *)skb->data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
  	ahp = x->data;
dff3bb062   Steffen Klassert   ah4: convert to a...
285
  	ahash = ahp->ahash;
631a6698d   Herbert Xu   [IPSEC]: Move IP ...
286
  	nexthdr = ah->nexthdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
  	ah_hlen = (ah->hdrlen + 2) << 2;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
288

fa9921e46   Nicolas Dichtel   ipsec: allow to a...
289
290
291
292
293
294
295
296
297
  	if (x->props.flags & XFRM_STATE_ALIGN4) {
  		if (ah_hlen != XFRM_ALIGN4(sizeof(*ah) + ahp->icv_full_len) &&
  		    ah_hlen != XFRM_ALIGN4(sizeof(*ah) + ahp->icv_trunc_len))
  			goto out;
  	} else {
  		if (ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_full_len) &&
  		    ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len))
  			goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
301
302
303
304
305
306
307
308
  
  	if (!pskb_may_pull(skb, ah_hlen))
  		goto out;
  
  	/* We are going to _remove_ AH header to keep sockets happy,
  	 * so... Later this can change. */
  	if (skb_cloned(skb) &&
  	    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
  		goto out;
  
  	skb->ip_summed = CHECKSUM_NONE;
dff3bb062   Steffen Klassert   ah4: convert to a...
309
310
311
312
  
  	if ((err = skb_cow_data(skb, 0, &trailer)) < 0)
  		goto out;
  	nfrags = err;
4b0ef1f22   Dang Hongwu   ah: reload pointe...
313
314
315
  	ah = (struct ip_auth_hdr *)skb->data;
  	iph = ip_hdr(skb);
  	ihl = ip_hdrlen(skb);
dff3bb062   Steffen Klassert   ah4: convert to a...
316
317
318
319
320
321
322
323
  	work_iph = ah_alloc_tmp(ahash, nfrags, ihl + ahp->icv_trunc_len);
  	if (!work_iph)
  		goto out;
  
  	auth_data = ah_tmp_auth(work_iph, ihl);
  	icv = ah_tmp_icv(ahash, auth_data, ahp->icv_trunc_len);
  	req = ah_tmp_req(ahash, icv);
  	sg = ah_req_sg(ahash, req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324

dff3bb062   Steffen Klassert   ah4: convert to a...
325
326
327
  	memcpy(work_iph, iph, ihl);
  	memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
  	memset(ah->auth_data, 0, ahp->icv_trunc_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
330
331
332
  
  	iph->ttl = 0;
  	iph->tos = 0;
  	iph->frag_off = 0;
  	iph->check = 0;
31a4ab930   Herbert Xu   [IPSEC] proto: Mo...
333
  	if (ihl > sizeof(*iph)) {
d5a0a1e31   Al Viro   [IPV4]: encapsula...
334
  		__be32 dummy;
dff3bb062   Steffen Klassert   ah4: convert to a...
335
336
337
  		err = ip_clear_mutable_options(iph, &dummy);
  		if (err)
  			goto out_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
  	}
0ebea8ef3   Herbert Xu   [IPSEC]: Move sta...
339

dff3bb062   Steffen Klassert   ah4: convert to a...
340
  	skb_push(skb, ihl);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
341

dff3bb062   Steffen Klassert   ah4: convert to a...
342
343
344
345
346
347
348
349
350
351
352
353
  	sg_init_table(sg, nfrags);
  	skb_to_sgvec(skb, sg, 0, skb->len);
  
  	ahash_request_set_crypt(req, sg, icv, skb->len);
  	ahash_request_set_callback(req, 0, ah_input_done, skb);
  
  	AH_SKB_CB(skb)->tmp = work_iph;
  
  	err = crypto_ahash_digest(req);
  	if (err) {
  		if (err == -EINPROGRESS)
  			goto out;
dff3bb062   Steffen Klassert   ah4: convert to a...
354
  		goto out_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
  	}
0ebea8ef3   Herbert Xu   [IPSEC]: Move sta...
356

dff3bb062   Steffen Klassert   ah4: convert to a...
357
  	err = memcmp(icv, auth_data, ahp->icv_trunc_len) ? -EBADMSG: 0;
0ebea8ef3   Herbert Xu   [IPSEC]: Move sta...
358
  	if (err)
dff3bb062   Steffen Klassert   ah4: convert to a...
359
  		goto out_free;
0ebea8ef3   Herbert Xu   [IPSEC]: Move sta...
360

b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
361
  	skb->network_header += ah_hlen;
dff3bb062   Steffen Klassert   ah4: convert to a...
362
  	memcpy(skb_network_header(skb), work_iph, ihl);
31a4ab930   Herbert Xu   [IPSEC] proto: Mo...
363
  	__skb_pull(skb, ah_hlen + ihl);
dff3bb062   Steffen Klassert   ah4: convert to a...
364
  	skb_set_transport_header(skb, -ihl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365

dff3bb062   Steffen Klassert   ah4: convert to a...
366
  	err = nexthdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367

dff3bb062   Steffen Klassert   ah4: convert to a...
368
369
  out_free:
  	kfree (work_iph);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
  out:
07d4ee583   Herbert Xu   [IPSEC]: Use HMAC...
371
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
374
375
  }
  
  static void ah4_err(struct sk_buff *skb, u32 info)
  {
4fb236bac   Alexey Dobriyan   netns xfrm: AH/ES...
376
  	struct net *net = dev_net(skb->dev);
b71d1d426   Eric Dumazet   inet: constify ip...
377
  	const struct iphdr *iph = (const struct iphdr *)skb->data;
d9319100c   Jianjun Kong   net: clean up net...
378
  	struct ip_auth_hdr *ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  	struct xfrm_state *x;
88c7664f1   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
380
381
  	if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH ||
  	    icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
  		return;
b71d1d426   Eric Dumazet   inet: constify ip...
383
384
  	x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
  			      ah->spi, IPPROTO_AH, AF_INET);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
387
388
389
390
391
  	if (!x)
  		return;
  	printk(KERN_DEBUG "pmtu discovery on SA AH/%08x/%08x
  ",
  	       ntohl(ah->spi), ntohl(iph->daddr));
  	xfrm_state_put(x);
  }
72cb6962a   Herbert Xu   [IPSEC]: Add xfrm...
392
  static int ah_init_state(struct xfrm_state *x)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
394
395
  {
  	struct ah_data *ahp = NULL;
  	struct xfrm_algo_desc *aalg_desc;
dff3bb062   Steffen Klassert   ah4: convert to a...
396
  	struct crypto_ahash *ahash;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
399
  
  	if (!x->aalg)
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
  	if (x->encap)
  		goto error;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
402
  	ahp = kzalloc(sizeof(*ahp), GFP_KERNEL);
dff3bb062   Steffen Klassert   ah4: convert to a...
403
  	if (!ahp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
  		return -ENOMEM;
dff3bb062   Steffen Klassert   ah4: convert to a...
405
406
  	ahash = crypto_alloc_ahash(x->aalg->alg_name, 0, 0);
  	if (IS_ERR(ahash))
07d4ee583   Herbert Xu   [IPSEC]: Use HMAC...
407
  		goto error;
dff3bb062   Steffen Klassert   ah4: convert to a...
408
409
410
  	ahp->ahash = ahash;
  	if (crypto_ahash_setkey(ahash, x->aalg->alg_key,
  				(x->aalg->alg_key_len + 7) / 8))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
  		goto error;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
412

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
416
  	/*
  	 * Lookup the algorithm description maintained by xfrm_algo,
  	 * verify crypto transform properties, and store information
  	 * we need for AH processing.  This lookup cannot fail here
dff3bb062   Steffen Klassert   ah4: convert to a...
417
  	 * after a successful crypto_alloc_ahash().
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
420
421
422
  	 */
  	aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
  	BUG_ON(!aalg_desc);
  
  	if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
dff3bb062   Steffen Klassert   ah4: convert to a...
423
  	    crypto_ahash_digestsize(ahash)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
425
  		printk(KERN_INFO "AH: %s digestsize %u != %hu
  ",
dff3bb062   Steffen Klassert   ah4: convert to a...
426
  		       x->aalg->alg_name, crypto_ahash_digestsize(ahash),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
429
  		       aalg_desc->uinfo.auth.icv_fullbits/8);
  		goto error;
  	}
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
430

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
  	ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
8f8a088c2   Martin Willi   xfrm: Use the use...
432
  	ahp->icv_trunc_len = x->aalg->alg_trunc_len/8;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
433

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
  	BUG_ON(ahp->icv_trunc_len > MAX_AH_AUTH_LEN);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
435

fa9921e46   Nicolas Dichtel   ipsec: allow to a...
436
437
438
439
440
441
  	if (x->props.flags & XFRM_STATE_ALIGN4)
  		x->props.header_len = XFRM_ALIGN4(sizeof(struct ip_auth_hdr) +
  						  ahp->icv_trunc_len);
  	else
  		x->props.header_len = XFRM_ALIGN8(sizeof(struct ip_auth_hdr) +
  						  ahp->icv_trunc_len);
7e49e6de3   Masahide NAKAMURA   [XFRM]: Add XFRM_...
442
  	if (x->props.mode == XFRM_MODE_TUNNEL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
444
445
446
447
448
449
  		x->props.header_len += sizeof(struct iphdr);
  	x->data = ahp;
  
  	return 0;
  
  error:
  	if (ahp) {
dff3bb062   Steffen Klassert   ah4: convert to a...
450
  		crypto_free_ahash(ahp->ahash);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
453
454
455
456
457
458
459
460
461
  		kfree(ahp);
  	}
  	return -EINVAL;
  }
  
  static void ah_destroy(struct xfrm_state *x)
  {
  	struct ah_data *ahp = x->data;
  
  	if (!ahp)
  		return;
dff3bb062   Steffen Klassert   ah4: convert to a...
462
  	crypto_free_ahash(ahp->ahash);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
  	kfree(ahp);
  }
533cb5b0a   Eric Dumazet   [XFRM]: constify ...
465
  static const struct xfrm_type ah_type =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
467
468
469
  {
  	.description	= "AH4",
  	.owner		= THIS_MODULE,
  	.proto	     	= IPPROTO_AH,
436a0a402   Herbert Xu   [IPSEC]: Move out...
470
  	.flags		= XFRM_TYPE_REPLAY_PROT,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471
472
473
474
475
  	.init_state	= ah_init_state,
  	.destructor	= ah_destroy,
  	.input		= ah_input,
  	.output		= ah_output
  };
32613090a   Alexey Dobriyan   net: constify str...
476
  static const struct net_protocol ah4_protocol = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
479
  	.handler	=	xfrm4_rcv,
  	.err_handler	=	ah4_err,
  	.no_policy	=	1,
4fb236bac   Alexey Dobriyan   netns xfrm: AH/ES...
480
  	.netns_ok	=	1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  };
  
  static int __init ah4_init(void)
  {
  	if (xfrm_register_type(&ah_type, AF_INET) < 0) {
  		printk(KERN_INFO "ip ah init: can't add xfrm type
  ");
  		return -EAGAIN;
  	}
  	if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) {
  		printk(KERN_INFO "ip ah init: can't add protocol
  ");
  		xfrm_unregister_type(&ah_type, AF_INET);
  		return -EAGAIN;
  	}
  	return 0;
  }
  
  static void __exit ah4_fini(void)
  {
  	if (inet_del_protocol(&ah4_protocol, IPPROTO_AH) < 0)
  		printk(KERN_INFO "ip ah close: can't remove protocol
  ");
  	if (xfrm_unregister_type(&ah_type, AF_INET) < 0)
  		printk(KERN_INFO "ip ah close: can't remove xfrm type
  ");
  }
  
  module_init(ah4_init);
  module_exit(ah4_fini);
  MODULE_LICENSE("GPL");
d3d6dd3ad   Masahide NAKAMURA   [XFRM]: Add modul...
512
  MODULE_ALIAS_XFRM_TYPE(AF_INET, XFRM_PROTO_AH);