alias_pptp.c revision 302408
1/*
2 * alias_pptp.c
3 *
4 * Copyright (c) 2000 Whistle Communications, Inc.
5 * All rights reserved.
6 *
7 * Subject to the following obligations and disclaimer of warranty, use and
8 * redistribution of this software, in source or object code forms, with or
9 * without modifications are expressly permitted by Whistle Communications;
10 * provided, however, that:
11 * 1. Any and all reproductions of the source or object code must include the
12 *    copyright notice above and the following disclaimer of warranties; and
13 * 2. No rights are granted, in any manner or form, to use Whistle
14 *    Communications, Inc. trademarks, including the mark "WHISTLE
15 *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
16 *    such appears in the above copyright notice or in the software.
17 *
18 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
19 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
20 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
21 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
23 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
24 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
25 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
26 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
27 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
28 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
29 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
34 * OF SUCH DAMAGE.
35 *
36 * Author: Erik Salander <erik@whistle.com>
37 */
38
39#include <sys/cdefs.h>
40__FBSDID("$FreeBSD: stable/11/sys/netinet/libalias/alias_pptp.c 259858 2013-12-25 02:06:57Z glebius $");
41
42/* Includes */
43#ifdef _KERNEL
44#include <sys/param.h>
45#include <sys/limits.h>
46#include <sys/kernel.h>
47#include <sys/module.h>
48#else
49#include <errno.h>
50#include <limits.h>
51#include <sys/types.h>
52#include <stdio.h>
53#endif
54
55#include <netinet/tcp.h>
56
57#ifdef _KERNEL
58#include <netinet/libalias/alias.h>
59#include <netinet/libalias/alias_local.h>
60#include <netinet/libalias/alias_mod.h>
61#else
62#include "alias.h"
63#include "alias_local.h"
64#include "alias_mod.h"
65#endif
66
67#define PPTP_CONTROL_PORT_NUMBER 1723
68
69static void
70AliasHandlePptpOut(struct libalias *, struct ip *, struct alias_link *);
71
72static void
73AliasHandlePptpIn(struct libalias *, struct ip *, struct alias_link *);
74
75static int
76AliasHandlePptpGreOut(struct libalias *, struct ip *);
77
78static int
79AliasHandlePptpGreIn(struct libalias *, struct ip *);
80
81static int
82fingerprint(struct libalias *la, struct alias_data *ah)
83{
84
85	if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL)
86		return (-1);
87	if (ntohs(*ah->dport) == PPTP_CONTROL_PORT_NUMBER
88	    || ntohs(*ah->sport) == PPTP_CONTROL_PORT_NUMBER)
89		return (0);
90	return (-1);
91}
92
93static int
94fingerprintgre(struct libalias *la, struct alias_data *ah)
95{
96
97	return (0);
98}
99
100static int
101protohandlerin(struct libalias *la, struct ip *pip, struct alias_data *ah)
102{
103
104	AliasHandlePptpIn(la, pip, ah->lnk);
105	return (0);
106}
107
108static int
109protohandlerout(struct libalias *la, struct ip *pip, struct alias_data *ah)
110{
111
112	AliasHandlePptpOut(la, pip, ah->lnk);
113	return (0);
114}
115
116static int
117protohandlergrein(struct libalias *la, struct ip *pip, struct alias_data *ah)
118{
119
120	if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY ||
121	    AliasHandlePptpGreIn(la, pip) == 0)
122		return (0);
123	return (-1);
124}
125
126static int
127protohandlergreout(struct libalias *la, struct ip *pip, struct alias_data *ah)
128{
129
130	if (AliasHandlePptpGreOut(la, pip) == 0)
131		return (0);
132	return (-1);
133}
134
135/* Kernel module definition. */
136struct proto_handler handlers[] = {
137	{
138	  .pri = 200,
139	  .dir = IN,
140	  .proto = TCP,
141	  .fingerprint = &fingerprint,
142	  .protohandler = &protohandlerin
143	},
144	{
145	  .pri = 210,
146	  .dir = OUT,
147	  .proto = TCP,
148	  .fingerprint = &fingerprint,
149	  .protohandler = &protohandlerout
150	},
151/*
152 * WATCH OUT!!! these 2 handlers NEED a priority of INT_MAX (highest possible)
153 * cause they will ALWAYS process packets, so they must be the last one
154 * in chain: look fingerprintgre() above.
155 */
156	{
157	  .pri = INT_MAX,
158	  .dir = IN,
159	  .proto = IP,
160	  .fingerprint = &fingerprintgre,
161	  .protohandler = &protohandlergrein
162	},
163	{
164	  .pri = INT_MAX,
165	  .dir = OUT,
166	  .proto = IP,
167	  .fingerprint = &fingerprintgre,
168	  .protohandler = &protohandlergreout
169	},
170	{ EOH }
171};
172static int
173mod_handler(module_t mod, int type, void *data)
174{
175	int error;
176
177	switch (type) {
178	case MOD_LOAD:
179		error = 0;
180		LibAliasAttachHandlers(handlers);
181		break;
182	case MOD_UNLOAD:
183		error = 0;
184		LibAliasDetachHandlers(handlers);
185		break;
186	default:
187		error = EINVAL;
188	}
189	return (error);
190}
191
192#ifdef _KERNEL
193static
194#endif
195moduledata_t alias_mod = {
196       "alias_pptp", mod_handler, NULL
197};
198
199#ifdef	_KERNEL
200DECLARE_MODULE(alias_pptp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
201MODULE_VERSION(alias_pptp, 1);
202MODULE_DEPEND(alias_pptp, libalias, 1, 1, 1);
203#endif
204
205/*
206   Alias_pptp.c performs special processing for PPTP sessions under TCP.
207   Specifically, watch PPTP control messages and alias the Call ID or the
208   Peer's Call ID in the appropriate messages.  Note, PPTP requires
209   "de-aliasing" of incoming packets, this is different than any other
210   TCP applications that are currently (ie. FTP, IRC and RTSP) aliased.
211
212   For Call IDs encountered for the first time, a PPTP alias link is created.
213   The PPTP alias link uses the Call ID in place of the original port number.
214   An alias Call ID is created.
215
216   For this routine to work, the PPTP control messages must fit entirely
217   into a single TCP packet.  This is typically the case, but is not
218   required by the spec.
219
220   Unlike some of the other TCP applications that are aliased (ie. FTP,
221   IRC and RTSP), the PPTP control messages that need to be aliased are
222   guaranteed to remain the same length.  The aliased Call ID is a fixed
223   length field.
224
225   Reference: RFC 2637
226
227   Initial version:  May, 2000 (eds)
228
229*/
230
231/*
232 * PPTP definitions
233 */
234
235struct grehdr {			/* Enhanced GRE header. */
236	u_int16_t	gh_flags;	/* Flags. */
237	u_int16_t	gh_protocol;	/* Protocol type. */
238	u_int16_t	gh_length;	/* Payload length. */
239	u_int16_t	gh_call_id;	/* Call ID. */
240	u_int32_t	gh_seq_no;	/* Sequence number (optional). */
241	u_int32_t	gh_ack_no;	/* Acknowledgment number
242					 * (optional). */
243};
244typedef struct grehdr GreHdr;
245
246/* The PPTP protocol ID used in the GRE 'proto' field. */
247#define PPTP_GRE_PROTO          0x880b
248
249/* Bits that must be set a certain way in all PPTP/GRE packets. */
250#define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
251#define PPTP_INIT_MASK		0xef7fffff
252
253#define PPTP_MAGIC		0x1a2b3c4d
254#define PPTP_CTRL_MSG_TYPE	1
255
256enum {
257	PPTP_StartCtrlConnRequest = 1,
258	PPTP_StartCtrlConnReply = 2,
259	PPTP_StopCtrlConnRequest = 3,
260	PPTP_StopCtrlConnReply = 4,
261	PPTP_EchoRequest = 5,
262	PPTP_EchoReply = 6,
263	PPTP_OutCallRequest = 7,
264	PPTP_OutCallReply = 8,
265	PPTP_InCallRequest = 9,
266	PPTP_InCallReply = 10,
267	PPTP_InCallConn = 11,
268	PPTP_CallClearRequest = 12,
269	PPTP_CallDiscNotify = 13,
270	PPTP_WanErrorNotify = 14,
271	PPTP_SetLinkInfo = 15
272};
273
274 /* Message structures */
275struct pptpMsgHead {
276	u_int16_t	length;	/* total length */
277	u_int16_t	msgType;/* PPTP message type */
278	u_int32_t	magic;	/* magic cookie */
279	u_int16_t	type;	/* control message type */
280	u_int16_t	resv0;	/* reserved */
281};
282typedef struct pptpMsgHead *PptpMsgHead;
283
284struct pptpCodes {
285	u_int8_t	resCode;/* Result Code */
286	u_int8_t	errCode;/* Error Code */
287};
288typedef struct pptpCodes *PptpCode;
289
290struct pptpCallIds {
291	u_int16_t	cid1;	/* Call ID field #1 */
292	u_int16_t	cid2;	/* Call ID field #2 */
293};
294typedef struct pptpCallIds *PptpCallId;
295
296static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *);
297
298
299static void
300AliasHandlePptpOut(struct libalias *la,
301    struct ip *pip,		/* IP packet to examine/patch */
302    struct alias_link *lnk)
303{				/* The PPTP control link */
304	struct alias_link *pptp_lnk;
305	PptpCallId cptr;
306	PptpCode codes;
307	u_int16_t ctl_type;	/* control message type */
308	struct tcphdr *tc;
309
310	/* Verify valid PPTP control message */
311	if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
312		return;
313
314	/* Modify certain PPTP messages */
315	switch (ctl_type) {
316	case PPTP_OutCallRequest:
317	case PPTP_OutCallReply:
318	case PPTP_InCallRequest:
319	case PPTP_InCallReply:
320		/*
321		 * Establish PPTP link for address and Call ID found in
322		 * control message.
323		 */
324		pptp_lnk = AddPptp(la, GetOriginalAddress(lnk), GetDestAddress(lnk),
325		    GetAliasAddress(lnk), cptr->cid1);
326		break;
327	case PPTP_CallClearRequest:
328	case PPTP_CallDiscNotify:
329		/*
330		 * Find PPTP link for address and Call ID found in control
331		 * message.
332		 */
333		pptp_lnk = FindPptpOutByCallId(la, GetOriginalAddress(lnk),
334		    GetDestAddress(lnk),
335		    cptr->cid1);
336		break;
337	default:
338		return;
339	}
340
341	if (pptp_lnk != NULL) {
342		int accumulate = cptr->cid1;
343
344		/* alias the Call Id */
345		cptr->cid1 = GetAliasPort(pptp_lnk);
346
347		/* Compute TCP checksum for revised packet */
348		tc = (struct tcphdr *)ip_next(pip);
349		accumulate -= cptr->cid1;
350		ADJUST_CHECKSUM(accumulate, tc->th_sum);
351
352		switch (ctl_type) {
353		case PPTP_OutCallReply:
354		case PPTP_InCallReply:
355			codes = (PptpCode) (cptr + 1);
356			if (codes->resCode == 1)	/* Connection
357							 * established, */
358				SetDestCallId(pptp_lnk,	/* note the Peer's Call
359								 * ID. */
360				    cptr->cid2);
361			else
362				SetExpire(pptp_lnk, 0);	/* Connection refused. */
363			break;
364		case PPTP_CallDiscNotify:	/* Connection closed. */
365			SetExpire(pptp_lnk, 0);
366			break;
367		}
368	}
369}
370
371static void
372AliasHandlePptpIn(struct libalias *la,
373    struct ip *pip,		/* IP packet to examine/patch */
374    struct alias_link *lnk)
375{				/* The PPTP control link */
376	struct alias_link *pptp_lnk;
377	PptpCallId cptr;
378	u_int16_t *pcall_id;
379	u_int16_t ctl_type;	/* control message type */
380	struct tcphdr *tc;
381
382	/* Verify valid PPTP control message */
383	if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
384		return;
385
386	/* Modify certain PPTP messages */
387	switch (ctl_type) {
388	case PPTP_InCallConn:
389	case PPTP_WanErrorNotify:
390	case PPTP_SetLinkInfo:
391		pcall_id = &cptr->cid1;
392		break;
393	case PPTP_OutCallReply:
394	case PPTP_InCallReply:
395		pcall_id = &cptr->cid2;
396		break;
397	case PPTP_CallDiscNotify:	/* Connection closed. */
398		pptp_lnk = FindPptpInByCallId(la, GetDestAddress(lnk),
399		    GetAliasAddress(lnk),
400		    cptr->cid1);
401		if (pptp_lnk != NULL)
402			SetExpire(pptp_lnk, 0);
403		return;
404	default:
405		return;
406	}
407
408	/* Find PPTP link for address and Call ID found in PPTP Control Msg */
409	pptp_lnk = FindPptpInByPeerCallId(la, GetDestAddress(lnk),
410	    GetAliasAddress(lnk),
411	    *pcall_id);
412
413	if (pptp_lnk != NULL) {
414		int accumulate = *pcall_id;
415
416		/* De-alias the Peer's Call Id. */
417		*pcall_id = GetOriginalPort(pptp_lnk);
418
419		/* Compute TCP checksum for modified packet */
420		tc = (struct tcphdr *)ip_next(pip);
421		accumulate -= *pcall_id;
422		ADJUST_CHECKSUM(accumulate, tc->th_sum);
423
424		if (ctl_type == PPTP_OutCallReply || ctl_type == PPTP_InCallReply) {
425			PptpCode codes = (PptpCode) (cptr + 1);
426
427			if (codes->resCode == 1)	/* Connection
428							 * established, */
429				SetDestCallId(pptp_lnk,	/* note the Call ID. */
430				    cptr->cid1);
431			else
432				SetExpire(pptp_lnk, 0);	/* Connection refused. */
433		}
434	}
435}
436
437static		PptpCallId
438AliasVerifyPptp(struct ip *pip, u_int16_t * ptype)
439{				/* IP packet to examine/patch */
440	int hlen, tlen, dlen;
441	PptpMsgHead hptr;
442	struct tcphdr *tc;
443
444	/* Calculate some lengths */
445	tc = (struct tcphdr *)ip_next(pip);
446	hlen = (pip->ip_hl + tc->th_off) << 2;
447	tlen = ntohs(pip->ip_len);
448	dlen = tlen - hlen;
449
450	/* Verify data length */
451	if (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds)))
452		return (NULL);
453
454	/* Move up to PPTP message header */
455	hptr = (PptpMsgHead) tcp_next(tc);
456
457	/* Return the control message type */
458	*ptype = ntohs(hptr->type);
459
460	/* Verify PPTP Control Message */
461	if ((ntohs(hptr->msgType) != PPTP_CTRL_MSG_TYPE) ||
462	    (ntohl(hptr->magic) != PPTP_MAGIC))
463		return (NULL);
464
465	/* Verify data length. */
466	if ((*ptype == PPTP_OutCallReply || *ptype == PPTP_InCallReply) &&
467	    (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds) +
468		sizeof(struct pptpCodes))))
469		return (NULL);
470	else
471		return (PptpCallId) (hptr + 1);
472}
473
474static int
475AliasHandlePptpGreOut(struct libalias *la, struct ip *pip)
476{
477	GreHdr *gr;
478	struct alias_link *lnk;
479
480	gr = (GreHdr *) ip_next(pip);
481
482	/* Check GRE header bits. */
483	if ((ntohl(*((u_int32_t *) gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
484		return (-1);
485
486	lnk = FindPptpOutByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
487	if (lnk != NULL) {
488		struct in_addr alias_addr = GetAliasAddress(lnk);
489
490		/* Change source IP address. */
491		DifferentialChecksum(&pip->ip_sum,
492		    &alias_addr, &pip->ip_src, 2);
493		pip->ip_src = alias_addr;
494	}
495	return (0);
496}
497
498static int
499AliasHandlePptpGreIn(struct libalias *la, struct ip *pip)
500{
501	GreHdr *gr;
502	struct alias_link *lnk;
503
504	gr = (GreHdr *) ip_next(pip);
505
506	/* Check GRE header bits. */
507	if ((ntohl(*((u_int32_t *) gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
508		return (-1);
509
510	lnk = FindPptpInByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
511	if (lnk != NULL) {
512		struct in_addr src_addr = GetOriginalAddress(lnk);
513
514		/* De-alias the Peer's Call Id. */
515		gr->gh_call_id = GetOriginalPort(lnk);
516
517		/* Restore original IP address. */
518		DifferentialChecksum(&pip->ip_sum,
519		    &src_addr, &pip->ip_dst, 2);
520		pip->ip_dst = src_addr;
521	}
522	return (0);
523}
524