Blame view

net/ipv4/ah4.c 13.6 KB
09c434b8a   Thomas Gleixner   treewide: Add SPD...
1
  // SPDX-License-Identifier: GPL-2.0-only
afd465030   Joe Perches   net: ipv4: Standa...
2
  #define pr_fmt(fmt) "IPsec: " fmt
67df58a3e   Sabrina Dubroca   ah: use crypto_me...
3
  #include <crypto/algapi.h>
dff3bb062   Steffen Klassert   ah4: convert to a...
4
  #include <crypto/hash.h>
07d4ee583   Herbert Xu   [IPSEC]: Use HMAC...
5
  #include <linux/err.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
7
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
  #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...
13
  #include <linux/scatterlist.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  #include <net/icmp.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
15
  #include <net/protocol.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16

dff3bb062   Steffen Klassert   ah4: convert to a...
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
71
72
73
  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
74
75
76
77
  
  /* 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...
78
  static int ip_clear_mutable_options(const struct iphdr *iph, __be32 *daddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
  {
5e73ea1a3   Daniel Baluta   ipv4: fix checkpa...
80
  	unsigned char *optptr = (unsigned char *)(iph+1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
  	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 ...
99
  		case IPOPT_CIPSO:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
104
105
106
107
108
109
  		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...
110
  			memset(optptr, 0, optlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
114
115
116
  		}
  		l -= optlen;
  		optptr += optlen;
  	}
  	return 0;
  }
dff3bb062   Steffen Klassert   ah4: convert to a...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  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...
139
140
141
  	kfree(AH_SKB_CB(skb)->tmp);
  	xfrm_output_resume(skb, err);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
  static int ah_output(struct xfrm_state *x, struct sk_buff *skb)
  {
  	int err;
dff3bb062   Steffen Klassert   ah4: convert to a...
145
146
147
148
149
150
151
  	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
152
153
154
  	struct iphdr *iph, *top_iph;
  	struct ip_auth_hdr *ah;
  	struct ah_data *ahp;
d4d573d03   Fan Du   {IPv4,xfrm} Add E...
155
156
157
158
  	int seqhi_len = 0;
  	__be32 *seqhi;
  	int sglists = 0;
  	struct scatterlist *seqhisg;
dff3bb062   Steffen Klassert   ah4: convert to a...
159
160
161
162
163
164
165
  
  	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
166

7b277b1a5   Herbert Xu   [IPSEC]: Set skb-...
167
  	skb_push(skb, -skb_network_offset(skb));
dff3bb062   Steffen Klassert   ah4: convert to a...
168
169
  	ah = ip_auth_hdr(skb);
  	ihl = ip_hdrlen(skb);
d4d573d03   Fan Du   {IPv4,xfrm} Add E...
170
171
172
173
  	if (x->props.flags & XFRM_STATE_ESN) {
  		sglists = 1;
  		seqhi_len = sizeof(*seqhi);
  	}
dff3bb062   Steffen Klassert   ah4: convert to a...
174
  	err = -ENOMEM;
d4d573d03   Fan Du   {IPv4,xfrm} Add E...
175
  	iph = ah_alloc_tmp(ahash, nfrags + sglists, ihl + seqhi_len);
dff3bb062   Steffen Klassert   ah4: convert to a...
176
177
  	if (!iph)
  		goto out;
d4d573d03   Fan Du   {IPv4,xfrm} Add E...
178
179
  	seqhi = (__be32 *)((char *)iph + ihl);
  	icv = ah_tmp_icv(ahash, seqhi, seqhi_len);
dff3bb062   Steffen Klassert   ah4: convert to a...
180
181
  	req = ah_tmp_req(ahash, icv);
  	sg = ah_req_sg(ahash, req);
d4d573d03   Fan Du   {IPv4,xfrm} Add E...
182
  	seqhisg = sg + nfrags;
dff3bb062   Steffen Klassert   ah4: convert to a...
183
184
  
  	memset(ah->auth_data, 0, ahp->icv_trunc_len);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
185
  	top_iph = ip_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
188
189
190
191
192
193
194
195
  
  	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...
196
  			goto out_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
  	}
37fedd3aa   Herbert Xu   [IPSEC]: Use IPv6...
198
199
  	ah->nexthdr = *skb_mac_header(skb);
  	*skb_mac_header(skb) = IPPROTO_AH;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
203
204
  
  	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
205
  	top_iph->check = 0;
fa9921e46   Nicolas Dichtel   ipsec: allow to a...
206
207
208
209
  	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
210
211
212
  
  	ah->reserved = 0;
  	ah->spi = x->id.spi;
1ce3644ad   Steffen Klassert   xfrm: Use separat...
213
  	ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
b7c6538cd   Herbert Xu   [IPSEC]: Move sta...
214

d4d573d03   Fan Du   {IPv4,xfrm} Add E...
215
  	sg_init_table(sg, nfrags + sglists);
3f2977072   Jason A. Donenfeld   ipsec: check retu...
216
217
218
  	err = skb_to_sgvec_nomark(skb, sg, 0, skb->len);
  	if (unlikely(err < 0))
  		goto out_free;
b7c6538cd   Herbert Xu   [IPSEC]: Move sta...
219

d4d573d03   Fan Du   {IPv4,xfrm} Add E...
220
221
222
223
224
225
  	if (x->props.flags & XFRM_STATE_ESN) {
  		/* Attach seqhi sg right after packet payload */
  		*seqhi = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
  		sg_set_buf(seqhisg, seqhi, seqhi_len);
  	}
  	ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len);
dff3bb062   Steffen Klassert   ah4: convert to a...
226
227
228
229
230
231
232
233
  	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;
068c2e703   Gilad Ben-Yossef   net: use -ENOSPC ...
234
  		if (err == -ENOSPC)
dff3bb062   Steffen Klassert   ah4: convert to a...
235
236
237
238
239
  			err = NET_XMIT_DROP;
  		goto out_free;
  	}
  
  	memcpy(ah->auth_data, icv, ahp->icv_trunc_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
243
244
245
246
247
  
  	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...
248
249
250
  out_free:
  	kfree(iph);
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
  	return err;
  }
dff3bb062   Steffen Klassert   ah4: convert to a...
253
254
255
256
257
258
259
260
261
262
263
  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;
ebd89a2d0   Gilad Ben-Yossef   IPsec: do not ign...
264
265
  	if (err)
  		goto out;
dff3bb062   Steffen Klassert   ah4: convert to a...
266
267
268
  	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);
67df58a3e   Sabrina Dubroca   ah: use crypto_me...
269
  	err = crypto_memneq(icv, auth_data, ahp->icv_trunc_len) ? -EBADMSG : 0;
dff3bb062   Steffen Klassert   ah4: convert to a...
270
271
  	if (err)
  		goto out;
b7ea81a58   Nick Bowler   ah: Read nexthdr ...
272
  	err = ah->nexthdr;
dff3bb062   Steffen Klassert   ah4: convert to a...
273
274
275
  	skb->network_header += ah_hlen;
  	memcpy(skb_network_header(skb), work_iph, ihl);
  	__skb_pull(skb, ah_hlen + ihl);
7143dfac6   Li RongQing   ah4/esp4: set tra...
276
277
278
279
280
  
  	if (x->props.mode == XFRM_MODE_TUNNEL)
  		skb_reset_transport_header(skb);
  	else
  		skb_set_transport_header(skb, -ihl);
dff3bb062   Steffen Klassert   ah4: convert to a...
281
282
283
284
  out:
  	kfree(AH_SKB_CB(skb)->tmp);
  	xfrm_input_resume(skb, err);
  }
e695633e2   Herbert Xu   [IPSEC]: Kill unu...
285
  static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
  {
  	int ah_hlen;
31a4ab930   Herbert Xu   [IPSEC] proto: Mo...
288
  	int ihl;
631a6698d   Herbert Xu   [IPSEC]: Move IP ...
289
  	int nexthdr;
dff3bb062   Steffen Klassert   ah4: convert to a...
290
291
292
293
294
295
296
297
  	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
298
299
  	struct ip_auth_hdr *ah;
  	struct ah_data *ahp;
dff3bb062   Steffen Klassert   ah4: convert to a...
300
  	int err = -ENOMEM;
d8b2a8600   Fan Du   {IPv4,xfrm} Add E...
301
302
303
304
  	int seqhi_len = 0;
  	__be32 *seqhi;
  	int sglists = 0;
  	struct scatterlist *seqhisg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305

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

fa9921e46   Nicolas Dichtel   ipsec: allow to a...
314
315
316
317
318
319
320
321
322
  	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
323
324
325
326
327
328
  
  	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. */
14bbd6a56   Pravin B Shelar   net: Add skb_uncl...
329
  	if (skb_unclone(skb, GFP_ATOMIC))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
332
  		goto out;
  
  	skb->ip_summed = CHECKSUM_NONE;
dff3bb062   Steffen Klassert   ah4: convert to a...
333
334
335
336
  
  	if ((err = skb_cow_data(skb, 0, &trailer)) < 0)
  		goto out;
  	nfrags = err;
4b0ef1f22   Dang Hongwu   ah: reload pointe...
337
338
339
  	ah = (struct ip_auth_hdr *)skb->data;
  	iph = ip_hdr(skb);
  	ihl = ip_hdrlen(skb);
d8b2a8600   Fan Du   {IPv4,xfrm} Add E...
340
341
342
343
344
345
346
  	if (x->props.flags & XFRM_STATE_ESN) {
  		sglists = 1;
  		seqhi_len = sizeof(*seqhi);
  	}
  
  	work_iph = ah_alloc_tmp(ahash, nfrags + sglists, ihl +
  				ahp->icv_trunc_len + seqhi_len);
94c10f0ea   David S. Miller   ah4: Fix error re...
347
348
  	if (!work_iph) {
  		err = -ENOMEM;
dff3bb062   Steffen Klassert   ah4: convert to a...
349
  		goto out;
94c10f0ea   David S. Miller   ah4: Fix error re...
350
  	}
dff3bb062   Steffen Klassert   ah4: convert to a...
351

d8b2a8600   Fan Du   {IPv4,xfrm} Add E...
352
353
  	seqhi = (__be32 *)((char *)work_iph + ihl);
  	auth_data = ah_tmp_auth(seqhi, seqhi_len);
dff3bb062   Steffen Klassert   ah4: convert to a...
354
355
356
  	icv = ah_tmp_icv(ahash, auth_data, ahp->icv_trunc_len);
  	req = ah_tmp_req(ahash, icv);
  	sg = ah_req_sg(ahash, req);
d8b2a8600   Fan Du   {IPv4,xfrm} Add E...
357
  	seqhisg = sg + nfrags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358

dff3bb062   Steffen Klassert   ah4: convert to a...
359
360
361
  	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
362
363
364
365
366
  
  	iph->ttl = 0;
  	iph->tos = 0;
  	iph->frag_off = 0;
  	iph->check = 0;
31a4ab930   Herbert Xu   [IPSEC] proto: Mo...
367
  	if (ihl > sizeof(*iph)) {
d5a0a1e31   Al Viro   [IPV4]: encapsula...
368
  		__be32 dummy;
dff3bb062   Steffen Klassert   ah4: convert to a...
369
370
371
  		err = ip_clear_mutable_options(iph, &dummy);
  		if (err)
  			goto out_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
  	}
0ebea8ef3   Herbert Xu   [IPSEC]: Move sta...
373

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

d8b2a8600   Fan Du   {IPv4,xfrm} Add E...
376
  	sg_init_table(sg, nfrags + sglists);
3f2977072   Jason A. Donenfeld   ipsec: check retu...
377
378
379
  	err = skb_to_sgvec_nomark(skb, sg, 0, skb->len);
  	if (unlikely(err < 0))
  		goto out_free;
dff3bb062   Steffen Klassert   ah4: convert to a...
380

d8b2a8600   Fan Du   {IPv4,xfrm} Add E...
381
382
383
384
385
386
  	if (x->props.flags & XFRM_STATE_ESN) {
  		/* Attach seqhi sg right after packet payload */
  		*seqhi = XFRM_SKB_CB(skb)->seq.input.hi;
  		sg_set_buf(seqhisg, seqhi, seqhi_len);
  	}
  	ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len);
dff3bb062   Steffen Klassert   ah4: convert to a...
387
388
389
390
391
392
393
394
  	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...
395
  		goto out_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
  	}
0ebea8ef3   Herbert Xu   [IPSEC]: Move sta...
397

67df58a3e   Sabrina Dubroca   ah: use crypto_me...
398
  	err = crypto_memneq(icv, auth_data, ahp->icv_trunc_len) ? -EBADMSG : 0;
0ebea8ef3   Herbert Xu   [IPSEC]: Move sta...
399
  	if (err)
dff3bb062   Steffen Klassert   ah4: convert to a...
400
  		goto out_free;
0ebea8ef3   Herbert Xu   [IPSEC]: Move sta...
401

b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
402
  	skb->network_header += ah_hlen;
dff3bb062   Steffen Klassert   ah4: convert to a...
403
  	memcpy(skb_network_header(skb), work_iph, ihl);
31a4ab930   Herbert Xu   [IPSEC] proto: Mo...
404
  	__skb_pull(skb, ah_hlen + ihl);
7143dfac6   Li RongQing   ah4/esp4: set tra...
405
406
407
408
  	if (x->props.mode == XFRM_MODE_TUNNEL)
  		skb_reset_transport_header(skb);
  	else
  		skb_set_transport_header(skb, -ihl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409

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

dff3bb062   Steffen Klassert   ah4: convert to a...
412
413
  out_free:
  	kfree (work_iph);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
  out:
07d4ee583   Herbert Xu   [IPSEC]: Use HMAC...
415
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
  }
e5b56454e   Steffen Klassert   ah4: Use the IPse...
417
  static int ah4_err(struct sk_buff *skb, u32 info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
  {
4fb236bac   Alexey Dobriyan   netns xfrm: AH/ES...
419
  	struct net *net = dev_net(skb->dev);
b71d1d426   Eric Dumazet   inet: constify ip...
420
  	const struct iphdr *iph = (const struct iphdr *)skb->data;
d9319100c   Jianjun Kong   net: clean up net...
421
  	struct ip_auth_hdr *ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
  	struct xfrm_state *x;
55be7a9c6   David S. Miller   ipv4: Add redirec...
423
424
425
  	switch (icmp_hdr(skb)->type) {
  	case ICMP_DEST_UNREACH:
  		if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
e5b56454e   Steffen Klassert   ah4: Use the IPse...
426
  			return 0;
55be7a9c6   David S. Miller   ipv4: Add redirec...
427
428
429
  	case ICMP_REDIRECT:
  		break;
  	default:
e5b56454e   Steffen Klassert   ah4: Use the IPse...
430
  		return 0;
55be7a9c6   David S. Miller   ipv4: Add redirec...
431
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432

b71d1d426   Eric Dumazet   inet: constify ip...
433
434
  	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
435
  	if (!x)
e5b56454e   Steffen Klassert   ah4: Use the IPse...
436
  		return 0;
55be7a9c6   David S. Miller   ipv4: Add redirec...
437

387aa65a8   Timo Teräs   ipv4: properly re...
438
  	if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
d888f3966   Maciej Żenczykowski   net-ipv4: remove ...
439
  		ipv4_update_pmtu(skb, net, info, 0, IPPROTO_AH);
387aa65a8   Timo Teräs   ipv4: properly re...
440
  	else
1042caa79   Maciej Żenczykowski   net-ipv4: remove ...
441
  		ipv4_redirect(skb, net, 0, IPPROTO_AH);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
  	xfrm_state_put(x);
e5b56454e   Steffen Klassert   ah4: Use the IPse...
443
444
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
  }
72cb6962a   Herbert Xu   [IPSEC]: Add xfrm...
446
  static int ah_init_state(struct xfrm_state *x)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
449
  {
  	struct ah_data *ahp = NULL;
  	struct xfrm_algo_desc *aalg_desc;
dff3bb062   Steffen Klassert   ah4: convert to a...
450
  	struct crypto_ahash *ahash;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
453
  
  	if (!x->aalg)
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
  	if (x->encap)
  		goto error;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
456
  	ahp = kzalloc(sizeof(*ahp), GFP_KERNEL);
dff3bb062   Steffen Klassert   ah4: convert to a...
457
  	if (!ahp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
  		return -ENOMEM;
dff3bb062   Steffen Klassert   ah4: convert to a...
459
460
  	ahash = crypto_alloc_ahash(x->aalg->alg_name, 0, 0);
  	if (IS_ERR(ahash))
07d4ee583   Herbert Xu   [IPSEC]: Use HMAC...
461
  		goto error;
dff3bb062   Steffen Klassert   ah4: convert to a...
462
463
464
  	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
465
  		goto error;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
466

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
470
  	/*
  	 * 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...
471
  	 * after a successful crypto_alloc_ahash().
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
475
476
  	 */
  	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...
477
  	    crypto_ahash_digestsize(ahash)) {
afd465030   Joe Perches   net: ipv4: Standa...
478
479
480
481
482
  		pr_info("%s: %s digestsize %u != %hu
  ",
  			__func__, x->aalg->alg_name,
  			crypto_ahash_digestsize(ahash),
  			aalg_desc->uinfo.auth.icv_fullbits / 8);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
484
  		goto error;
  	}
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
485

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

fa9921e46   Nicolas Dichtel   ipsec: allow to a...
489
490
491
492
493
494
  	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_...
495
  	if (x->props.mode == XFRM_MODE_TUNNEL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
497
498
499
500
501
502
  		x->props.header_len += sizeof(struct iphdr);
  	x->data = ahp;
  
  	return 0;
  
  error:
  	if (ahp) {
dff3bb062   Steffen Klassert   ah4: convert to a...
503
  		crypto_free_ahash(ahp->ahash);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
505
506
507
508
509
510
511
512
513
514
  		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...
515
  	crypto_free_ahash(ahp->ahash);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
517
  	kfree(ahp);
  }
e5b56454e   Steffen Klassert   ah4: Use the IPse...
518
519
520
521
  static int ah4_rcv_cb(struct sk_buff *skb, int err)
  {
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522

533cb5b0a   Eric Dumazet   [XFRM]: constify ...
523
  static const struct xfrm_type ah_type =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
526
527
  {
  	.description	= "AH4",
  	.owner		= THIS_MODULE,
  	.proto	     	= IPPROTO_AH,
436a0a402   Herbert Xu   [IPSEC]: Move out...
528
  	.flags		= XFRM_TYPE_REPLAY_PROT,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
530
531
532
533
  	.init_state	= ah_init_state,
  	.destructor	= ah_destroy,
  	.input		= ah_input,
  	.output		= ah_output
  };
e5b56454e   Steffen Klassert   ah4: Use the IPse...
534
  static struct xfrm4_protocol ah4_protocol = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
  	.handler	=	xfrm4_rcv,
e5b56454e   Steffen Klassert   ah4: Use the IPse...
536
537
  	.input_handler	=	xfrm_input,
  	.cb_handler	=	ah4_rcv_cb,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
  	.err_handler	=	ah4_err,
e5b56454e   Steffen Klassert   ah4: Use the IPse...
539
  	.priority	=	0,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
542
543
544
  };
  
  static int __init ah4_init(void)
  {
  	if (xfrm_register_type(&ah_type, AF_INET) < 0) {
058bd4d2a   Joe Perches   net: Convert prin...
545
546
  		pr_info("%s: can't add xfrm type
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
548
  		return -EAGAIN;
  	}
e5b56454e   Steffen Klassert   ah4: Use the IPse...
549
  	if (xfrm4_protocol_register(&ah4_protocol, IPPROTO_AH) < 0) {
058bd4d2a   Joe Perches   net: Convert prin...
550
551
  		pr_info("%s: can't add protocol
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
553
554
555
556
557
558
559
  		xfrm_unregister_type(&ah_type, AF_INET);
  		return -EAGAIN;
  	}
  	return 0;
  }
  
  static void __exit ah4_fini(void)
  {
e5b56454e   Steffen Klassert   ah4: Use the IPse...
560
  	if (xfrm4_protocol_deregister(&ah4_protocol, IPPROTO_AH) < 0)
058bd4d2a   Joe Perches   net: Convert prin...
561
562
  		pr_info("%s: can't remove protocol
  ", __func__);
4f518e802   Florian Westphal   xfrm: remove type...
563
  	xfrm_unregister_type(&ah_type, AF_INET);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
566
567
568
  }
  
  module_init(ah4_init);
  module_exit(ah4_fini);
  MODULE_LICENSE("GPL");
d3d6dd3ad   Masahide NAKAMURA   [XFRM]: Add modul...
569
  MODULE_ALIAS_XFRM_TYPE(AF_INET, XFRM_PROTO_AH);