1/*	$NetBSD: isakmp_frag.c,v 1.4 2006/09/09 16:22:09 manu Exp $	*/
2
3/* Id: isakmp_frag.c,v 1.4 2004/11/13 17:31:36 manubsd Exp */
4
5/*
6 * Copyright (C) 2004 Emmanuel Dreyfus
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the project nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "config.h"
35
36#include <sys/types.h>
37#include <sys/param.h>
38#include <sys/socket.h>
39#include <sys/queue.h>
40
41#include <netinet/in.h>
42#include <arpa/inet.h>
43
44#ifdef HAVE_OPENSSL
45#include <openssl/md5.h>
46#endif
47
48#include <stdlib.h>
49#include <stdio.h>
50#include <fcntl.h>
51#include <string.h>
52#include <errno.h>
53#if TIME_WITH_SYS_TIME
54# include <sys/time.h>
55# include <time.h>
56#else
57# if HAVE_SYS_TIME_H
58#  include <sys/time.h>
59# else
60#  include <time.h>
61# endif
62#endif
63#include <netdb.h>
64#ifdef HAVE_UNISTD_H
65#include <unistd.h>
66#endif
67#include <ctype.h>
68
69#include "var.h"
70#include "misc.h"
71#include "vmbuf.h"
72#include "plog.h"
73#include "sockmisc.h"
74#include "schedule.h"
75#include "debug.h"
76
77#include "isakmp_var.h"
78#include "isakmp.h"
79#include "handler.h"
80#include "isakmp_frag.h"
81#include "strnames.h"
82#include "nattraversal.h"
83#include "grabmyaddr.h"
84#include "localconf.h"
85#include "crypto_openssl.h"
86
87int
88isakmp_sendfrags(iph1, buf)
89	phase1_handle_t *iph1;
90	vchar_t *buf;
91{
92	struct isakmp *hdr;
93	struct isakmp_frag *fraghdr;
94	caddr_t data;
95	caddr_t sdata;
96	size_t datalen;
97	size_t max_datalen;
98	size_t fraglen;
99	vchar_t *frag;
100	unsigned int fragnum = 0;
101	size_t len;
102	int etype;
103#ifdef ENABLE_NATT
104	size_t extralen = NON_ESP_MARKER_USE(iph1)? NON_ESP_MARKER_LEN : 0;
105#else
106	size_t extralen = 0;
107#endif
108	int s;
109	vchar_t *vbuf;
110
111
112	/* select the socket to be sent */
113	s = getsockmyaddr((struct sockaddr *)iph1->local);
114	if (s == -1){
115		return -1;
116	}
117
118	/*
119	 * Catch the exchange type for later: the fragments and the
120	 * fragmented packet must have the same exchange type.
121	 */
122	hdr = (struct isakmp *)buf->v;
123	etype = hdr->etype;
124
125	/*
126	 * We want to send a a packet smaller than ISAKMP_FRAG_MAXLEN
127	 * First compute the maximum data length that will fit in it
128	 */
129	max_datalen = ISAKMP_FRAG_MAXLEN -
130	    (sizeof(*hdr) + sizeof(*fraghdr));
131
132	sdata = buf->v;
133	len = buf->l;
134
135	while (len > 0) {
136		fragnum++;
137
138		if (len > max_datalen)
139			datalen = max_datalen;
140		else
141			datalen = len;
142
143		fraglen = sizeof(*hdr) + sizeof(*fraghdr) + datalen;
144
145		if ((frag = vmalloc(fraglen)) == NULL) {
146			plog(ASL_LEVEL_ERR,
147			    "Cannot allocate memory\n");
148			return -1;
149		}
150
151		set_isakmp_header1(frag, iph1, ISAKMP_NPTYPE_FRAG);
152		hdr = (struct isakmp *)frag->v;
153		hdr->etype = etype;
154
155		fraghdr = (struct isakmp_frag *)(hdr + 1);
156		fraghdr->unknown0 = 0;
157		fraghdr->len = htons(fraglen - sizeof(*hdr));
158		fraghdr->unknown1 = htons(1);
159		fraghdr->index = fragnum;
160		if (len == datalen)
161			fraghdr->flags = ISAKMP_FRAG_LAST;
162		else
163			fraghdr->flags = 0;
164
165		data = (caddr_t)(fraghdr + 1);
166		memcpy(data, sdata, datalen);
167
168#ifdef ENABLE_NATT
169		/* If NAT-T port floating is in use, 4 zero bytes (non-ESP marker)
170		 must added just before the packet itself. For this we must
171		 allocate a new buffer and release it at the end. */
172		if (extralen) {
173			if ((vbuf = vmalloc(frag->l + extralen)) == NULL) {
174				plog(ASL_LEVEL_ERR,
175					 "%s: vbuf allocation failed\n", __FUNCTION__);
176				vfree(frag);
177				return -1;
178			}
179			*ALIGNED_CAST(u_int32_t *)vbuf->v = 0; // non-esp marker
180			memcpy(vbuf->v + extralen, frag->v, frag->l);
181			vfree(frag);
182			frag = vbuf;
183		}
184#endif
185
186		if (sendfromto(s, frag->v, frag->l,
187					   iph1->local, iph1->remote, lcconf->count_persend) == -1) {
188			plog(ASL_LEVEL_ERR, "%s: sendfromto failed\n", __FUNCTION__);
189			vfree(frag);
190			return -1;
191		}
192
193		vfree(frag);
194
195		len -= datalen;
196		sdata += datalen;
197	}
198
199	plog(ASL_LEVEL_DEBUG,
200		 "%s: processed %d fragments\n", __FUNCTION__, fragnum);
201
202	return fragnum;
203}
204
205unsigned int
206vendorid_frag_cap(gen)
207	struct isakmp_gen *gen;
208{
209	int *hp;
210	int hashlen_bytes = eay_md5_hashlen() >> 3;
211
212	hp = ALIGNED_CAST(int *)(gen + 1);
213
214	return ntohl(hp[hashlen_bytes / sizeof(*hp)]);
215}
216
217int
218isakmp_frag_extract(iph1, msg)
219	phase1_handle_t *iph1;
220	vchar_t *msg;
221{
222	struct isakmp *isakmp;
223	struct isakmp_frag *frag;
224	struct isakmp_frag_item *item;
225	vchar_t *buf;
226	int last_frag = 0;
227	char *data;
228	int i;
229
230	if (msg->l < sizeof(*isakmp) + sizeof(*frag)) {
231		plog(ASL_LEVEL_ERR, "Message too short\n");
232		return -1;
233	}
234
235	isakmp = (struct isakmp *)msg->v;
236	frag = (struct isakmp_frag *)(isakmp + 1);
237
238	/*
239	 * frag->len is the frag payload data plus the frag payload header,
240	 * whose size is sizeof(*frag)
241	 */
242	if (msg->l < sizeof(*isakmp) + ntohs(frag->len) ||
243		ntohs(frag->len) < sizeof(*frag) + 1) {
244		plog(ASL_LEVEL_ERR, "Fragment too short\n");
245		return -1;
246	}
247
248	if (ntohs(frag->len) < sizeof(*frag)) {
249		plog(ASL_LEVEL_ERR,
250			 "invalid Frag, frag-len %d\n",
251			 ntohs(frag->len));
252		return -1;
253	}
254
255	if ((buf = vmalloc(ntohs(frag->len) - sizeof(*frag))) == NULL) {
256		plog(ASL_LEVEL_ERR, "Cannot allocate memory\n");
257		return -1;
258	}
259
260	if ((item = racoon_malloc(sizeof(*item))) == NULL) {
261		plog(ASL_LEVEL_ERR, "Cannot allocate memory\n");
262		vfree(buf);
263		return -1;
264	}
265    bzero(item, sizeof(*item));
266
267	data = (char *)(frag + 1);
268	memcpy(buf->v, data, buf->l);
269
270	item->frag_num = frag->index;
271	item->frag_last = (frag->flags & ISAKMP_FRAG_LAST);
272	item->frag_next = NULL;
273	item->frag_packet = buf;
274	item->frag_id = ntohs(frag->unknown1);
275
276    plog(ASL_LEVEL_DEBUG,
277		 "%s: received fragment #%d  frag ID=%d  last frag=%d\n",
278            __FUNCTION__, item->frag_num, item->frag_id, item->frag_last);
279
280    /* Insert if new and find the last frag num if present */
281    struct isakmp_frag_item *current;
282
283    last_frag = (item->frag_last ? item->frag_num : 0);
284    current = iph1->frag_chain;
285    while (current) {
286        if (current->frag_num == item->frag_num) {  // duplicate?
287            vfree(item->frag_packet);
288            racoon_free(item);
289            return 0;   // already have it
290        }
291        if (current->frag_last)
292            last_frag = current->frag_num;
293        current = current->frag_next;
294    }
295    /* no dup - insert it */
296    item->frag_next = iph1->frag_chain;
297    iph1->frag_chain = item;
298
299    /* Check if the chain is complete   */
300    if (last_frag == 0)
301        return 0;       /* if last_frag not found - chain is not complete */
302    for (i = 1; i <= last_frag; i++) {
303        current = iph1->frag_chain;
304        while (current) {
305            if (current->frag_num == i)
306                break;
307            current = current->frag_next;
308        };
309        if (!current)
310            return 0;   /* chain not complete */
311    }
312
313	plog(ASL_LEVEL_DEBUG,
314		 "%s: processed fragment %d\n", __FUNCTION__, frag->index);
315	return 1;   /* chain is complete */
316}
317
318vchar_t *
319isakmp_frag_reassembly(iph1)
320	phase1_handle_t *iph1;
321{
322	struct isakmp_frag_item *item;
323	size_t len = 0;
324	vchar_t *buf = NULL;
325	int frag_count = 0, frag_max = 0;
326	int i;
327	char *data;
328
329	if ((item = iph1->frag_chain) == NULL) {
330		plog(ASL_LEVEL_ERR, "No fragment to reassemble\n");
331		goto out;
332	}
333
334	do {
335		frag_count++;
336		if (item->frag_num > frag_max && item->frag_last) {
337			frag_max = item->frag_num;
338		}
339		len += item->frag_packet->l;
340		item = item->frag_next;
341	} while (item != NULL);
342
343	if ((buf = vmalloc(len)) == NULL) {
344		plog(ASL_LEVEL_ERR, "Cannot allocate memory\n");
345		goto out;
346	}
347	data = buf->v;
348
349	for (i = 1; i <= frag_max; i++) {
350		item = iph1->frag_chain;
351		do {
352			if (item->frag_num == i)
353				break;
354			item = item->frag_next;
355		} while (item != NULL);
356
357		if (item == NULL) {
358			plog(ASL_LEVEL_ERR,
359			    "Missing fragment #%d\n", i);
360			vfree(buf);
361			buf = NULL;
362			return buf;
363        }
364		memcpy(data, item->frag_packet->v, item->frag_packet->l);
365		data += item->frag_packet->l;
366	}
367
368	plog(ASL_LEVEL_DEBUG,
369		 "%s: processed %d fragments\n", __FUNCTION__, frag_count);
370
371out:
372	item = iph1->frag_chain;
373
374	while (item != NULL) {
375		struct isakmp_frag_item *next_item;
376
377		next_item = item->frag_next;
378
379		vfree(item->frag_packet);
380		racoon_free(item);
381
382		item = next_item;
383	}
384
385	iph1->frag_chain = NULL;
386
387    //plogdump(ASL_LEVEL_DEBUG, buf->v, buf->l, "re-assembled fragements:\n");
388	return buf;
389}
390
391vchar_t *
392isakmp_frag_addcap(buf, cap)
393	vchar_t *buf;
394	int cap;
395{
396	int val, *capp;
397	size_t len;
398	int hashlen_bytes = eay_md5_hashlen() >> 3;
399
400	/* If the capability has not been added, add room now */
401	len = buf->l;
402	if (len == hashlen_bytes) {
403		if ((buf = vrealloc(buf, len + sizeof(cap))) == NULL) {
404			plog(ASL_LEVEL_ERR,
405			    "Cannot allocate memory\n");
406			return NULL;
407		}
408        val = 0;
409        memcpy(buf->v + len, &val, sizeof(val));        // Wcast_lign fix - copy instead of assign for unaligned move
410    }
411    capp = (int *)(void*)(buf->v + hashlen_bytes);      // Wcast_lign fix - copy instead of assign for unaligned move
412    memcpy(&val, capp, sizeof(val));
413    val |= htonl(cap);
414    memcpy(capp, &val, sizeof(val));
415
416	return buf;
417}
418
419int
420sendfragsfromto(s, buf, local, remote, count_persend, frag_flags)
421	int              s;
422	vchar_t         *buf;
423	struct sockaddr_storage *local;
424	struct sockaddr_storage *remote;
425	int              count_persend;
426	u_int32_t        frag_flags;
427{
428	struct isakmp *main_hdr;
429	struct isakmp *hdr;
430	struct isakmp_frag *fraghdr;
431	caddr_t data;
432	caddr_t sdata;
433	size_t datalen;
434	size_t max_datalen;
435	size_t fraglen;
436	vchar_t *frag;
437	unsigned int fragnum = 0;
438	size_t len;
439#ifdef ENABLE_NATT
440	size_t extralen = (frag_flags & FRAG_PUT_NON_ESP_MARKER)? NON_ESP_MARKER_LEN : 0;
441#else
442	size_t extralen = 0;
443#endif
444
445	/*
446	 * fragmented packet must have the same exchange type (amongst other fields in the header).
447	 */
448	main_hdr = (struct isakmp *)buf->v;
449
450	/*
451	 * We want to send a a packet smaller than ISAKMP_FRAG_MAXLEN
452	 * First compute the maximum data length that will fit in it
453	 */
454	max_datalen = ISAKMP_FRAG_MAXLEN -
455	(sizeof(*main_hdr) + sizeof(*fraghdr));
456
457	sdata = buf->v;
458	len = buf->l;
459
460	while (len > 0) {
461		fragnum++;
462
463		if (len > max_datalen)
464			datalen = max_datalen;
465		else
466			datalen = len;
467
468		fraglen = sizeof(*hdr) + sizeof(*fraghdr) + datalen;
469
470		if ((frag = vmalloc(fraglen)) == NULL) {
471			plog(ASL_LEVEL_ERR,
472				 "Cannot allocate memory\n");
473			return -1;
474		}
475
476		hdr = (struct isakmp *)frag->v;
477		bcopy(main_hdr, hdr, sizeof(*hdr));
478		hdr->len = htonl(frag->l);
479		hdr->np = ISAKMP_NPTYPE_FRAG;
480
481		fraghdr = (struct isakmp_frag *)(hdr + 1);
482		fraghdr->unknown0 = 0;
483		fraghdr->len = htons(fraglen - sizeof(*hdr));
484		fraghdr->unknown1 = htons(1);
485		fraghdr->index = fragnum;
486		if (len == datalen)
487			fraghdr->flags = ISAKMP_FRAG_LAST;
488		else
489			fraghdr->flags = 0;
490
491		data = (caddr_t)(fraghdr + 1);
492		memcpy(data, sdata, datalen);
493
494#ifdef ENABLE_NATT
495		/* If NAT-T port floating is in use, 4 zero bytes (non-ESP marker)
496		 must added just before the packet itself. For this we must
497		 allocate a new buffer and release it at the end. */
498		if (extralen) {
499			vchar_t *vbuf;
500
501			if ((vbuf = vmalloc(frag->l + extralen)) == NULL) {
502				plog(ASL_LEVEL_ERR,
503					 "%s: vbuf allocation failed\n", __FUNCTION__);
504				vfree(frag);
505				return -1;
506			}
507			*ALIGNED_CAST(u_int32_t *)vbuf->v = 0; // non-esp marker
508			memcpy(vbuf->v + extralen, frag->v, frag->l);
509			vfree(frag);
510			frag = vbuf;
511		}
512#endif
513
514		if (sendfromto(s, frag->v, frag->l, local, remote, count_persend) == -1) {
515			plog(ASL_LEVEL_ERR, "sendfromto failed\n");
516			vfree(frag);
517			return -1;
518		}
519
520		vfree(frag);
521
522		len -= datalen;
523		sdata += datalen;
524	}
525
526	plog(ASL_LEVEL_DEBUG,
527		 "%s: processed %d fragments\n", __FUNCTION__, fragnum);
528
529	return fragnum;
530}
531