xref: /freebsd/sys/netinet/libalias/alias_pptp.c (revision da8fa4e37a0c048a67d7baa3b5a9bed637d02564)
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$");
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 
69 static void
70 AliasHandlePptpOut(struct libalias *, struct ip *, struct alias_link *);
71 
72 static void
73 AliasHandlePptpIn(struct libalias *, struct ip *, struct alias_link *);
74 
75 static int
76 AliasHandlePptpGreOut(struct libalias *, struct ip *);
77 
78 static int
79 AliasHandlePptpGreIn(struct libalias *, struct ip *);
80 
81 static int
82 fingerprint(struct libalias *la, struct alias_data *ah)
83 {
84 	if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL)
85 		return (-1);
86 	if (ntohs(*ah->dport) == PPTP_CONTROL_PORT_NUMBER
87 	    || ntohs(*ah->sport) == PPTP_CONTROL_PORT_NUMBER)
88 		return (0);
89 	return (-1);
90 }
91 
92 static int
93 fingerprintgre(struct libalias *la, struct alias_data *ah)
94 {
95 	return (0);
96 }
97 
98 static int
99 protohandlerin(struct libalias *la, struct ip *pip, struct alias_data *ah)
100 {
101 	AliasHandlePptpIn(la, pip, ah->lnk);
102 	return (0);
103 }
104 
105 static int
106 protohandlerout(struct libalias *la, struct ip *pip, struct alias_data *ah)
107 {
108 	AliasHandlePptpOut(la, pip, ah->lnk);
109 	return (0);
110 }
111 
112 static int
113 protohandlergrein(struct libalias *la, struct ip *pip, struct alias_data *ah)
114 {
115 	if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY ||
116 	    AliasHandlePptpGreIn(la, pip) == 0)
117 		return (0);
118 	return (-1);
119 }
120 
121 static int
122 protohandlergreout(struct libalias *la, struct ip *pip, struct alias_data *ah)
123 {
124 	if (AliasHandlePptpGreOut(la, pip) == 0)
125 		return (0);
126 	return (-1);
127 }
128 
129 /* Kernel module definition. */
130 struct proto_handler handlers[] = {
131 	{
132 	  .pri = 200,
133 	  .dir = IN,
134 	  .proto = TCP,
135 	  .fingerprint = &fingerprint,
136 	  .protohandler = &protohandlerin
137 	},
138 	{
139 	  .pri = 210,
140 	  .dir = OUT,
141 	  .proto = TCP,
142 	  .fingerprint = &fingerprint,
143 	  .protohandler = &protohandlerout
144 	},
145 /*
146  * WATCH OUT!!! these 2 handlers NEED a priority of INT_MAX (highest possible)
147  * cause they will ALWAYS process packets, so they must be the last one
148  * in chain: look fingerprintgre() above.
149  */
150 	{
151 	  .pri = INT_MAX,
152 	  .dir = IN,
153 	  .proto = IP,
154 	  .fingerprint = &fingerprintgre,
155 	  .protohandler = &protohandlergrein
156 	},
157 	{
158 	  .pri = INT_MAX,
159 	  .dir = OUT,
160 	  .proto = IP,
161 	  .fingerprint = &fingerprintgre,
162 	  .protohandler = &protohandlergreout
163 	},
164 	{ EOH }
165 };
166 static int
167 mod_handler(module_t mod, int type, void *data)
168 {
169 	int error;
170 
171 	switch (type) {
172 	case MOD_LOAD:
173 		error = 0;
174 		LibAliasAttachHandlers(handlers);
175 		break;
176 	case MOD_UNLOAD:
177 		error = 0;
178 		LibAliasDetachHandlers(handlers);
179 		break;
180 	default:
181 		error = EINVAL;
182 	}
183 	return (error);
184 }
185 
186 #ifdef _KERNEL
187 static
188 #endif
189 moduledata_t alias_mod = {
190        "alias_pptp", mod_handler, NULL
191 };
192 
193 #ifdef _KERNEL
194 DECLARE_MODULE(alias_pptp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
195 MODULE_VERSION(alias_pptp, 1);
196 MODULE_DEPEND(alias_pptp, libalias, 1, 1, 1);
197 #endif
198 
199 /*
200    Alias_pptp.c performs special processing for PPTP sessions under TCP.
201    Specifically, watch PPTP control messages and alias the Call ID or the
202    Peer's Call ID in the appropriate messages.  Note, PPTP requires
203    "de-aliasing" of incoming packets, this is different than any other
204    TCP applications that are currently (ie. FTP, IRC and RTSP) aliased.
205 
206    For Call IDs encountered for the first time, a PPTP alias link is created.
207    The PPTP alias link uses the Call ID in place of the original port number.
208    An alias Call ID is created.
209 
210    For this routine to work, the PPTP control messages must fit entirely
211    into a single TCP packet.  This is typically the case, but is not
212    required by the spec.
213 
214    Unlike some of the other TCP applications that are aliased (ie. FTP,
215    IRC and RTSP), the PPTP control messages that need to be aliased are
216    guaranteed to remain the same length.  The aliased Call ID is a fixed
217    length field.
218 
219    Reference: RFC 2637
220 
221    Initial version:  May, 2000 (eds)
222 */
223 
224 /*
225  * PPTP definitions
226  */
227 
228 struct grehdr {				/* Enhanced GRE header. */
229 	u_int16_t	gh_flags;	/* Flags. */
230 	u_int16_t	gh_protocol;	/* Protocol type. */
231 	u_int16_t	gh_length;	/* Payload length. */
232 	u_int16_t	gh_call_id;	/* Call ID. */
233 	u_int32_t	gh_seq_no;	/* Sequence number (optional). */
234 	u_int32_t	gh_ack_no;	/* Acknowledgment number
235 					 * (optional). */
236 };
237 typedef struct grehdr GreHdr;
238 
239 /* The PPTP protocol ID used in the GRE 'proto' field. */
240 #define PPTP_GRE_PROTO          0x880b
241 
242 /* Bits that must be set a certain way in all PPTP/GRE packets. */
243 #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
244 #define PPTP_INIT_MASK		0xef7fffff
245 
246 #define PPTP_MAGIC		0x1a2b3c4d
247 #define PPTP_CTRL_MSG_TYPE	1
248 
249 enum {
250 	PPTP_StartCtrlConnRequest = 1,
251 	PPTP_StartCtrlConnReply = 2,
252 	PPTP_StopCtrlConnRequest = 3,
253 	PPTP_StopCtrlConnReply = 4,
254 	PPTP_EchoRequest = 5,
255 	PPTP_EchoReply = 6,
256 	PPTP_OutCallRequest = 7,
257 	PPTP_OutCallReply = 8,
258 	PPTP_InCallRequest = 9,
259 	PPTP_InCallReply = 10,
260 	PPTP_InCallConn = 11,
261 	PPTP_CallClearRequest = 12,
262 	PPTP_CallDiscNotify = 13,
263 	PPTP_WanErrorNotify = 14,
264 	PPTP_SetLinkInfo = 15
265 };
266 
267 /* Message structures */
268 struct pptpMsgHead {
269 	u_int16_t	length;	/* total length */
270 	u_int16_t	msgType;/* PPTP message type */
271 	u_int32_t	magic;	/* magic cookie */
272 	u_int16_t	type;	/* control message type */
273 	u_int16_t	resv0;	/* reserved */
274 };
275 typedef struct pptpMsgHead *PptpMsgHead;
276 
277 struct pptpCodes {
278 	u_int8_t	resCode;/* Result Code */
279 	u_int8_t	errCode;/* Error Code */
280 };
281 typedef struct pptpCodes *PptpCode;
282 
283 struct pptpCallIds {
284 	u_int16_t	cid1;	/* Call ID field #1 */
285 	u_int16_t	cid2;	/* Call ID field #2 */
286 };
287 typedef struct pptpCallIds *PptpCallId;
288 
289 static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *);
290 
291 static void
292 AliasHandlePptpOut(struct libalias *la,
293     struct ip *pip,		/* IP packet to examine/patch */
294     struct alias_link *lnk)	/* The PPTP control link */
295 {
296 	struct alias_link *pptp_lnk;
297 	PptpCallId cptr;
298 	PptpCode codes;
299 	u_int16_t ctl_type;	/* control message type */
300 	struct tcphdr *tc;
301 
302 	/* Verify valid PPTP control message */
303 	if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
304 		return;
305 
306 	/* Modify certain PPTP messages */
307 	switch (ctl_type) {
308 	case PPTP_OutCallRequest:
309 	case PPTP_OutCallReply:
310 	case PPTP_InCallRequest:
311 	case PPTP_InCallReply:
312 		/*
313 		 * Establish PPTP link for address and Call ID found in
314 		 * control message.
315 		 */
316 		pptp_lnk = AddPptp(la, GetOriginalAddress(lnk), GetDestAddress(lnk),
317 		    GetAliasAddress(lnk), cptr->cid1);
318 		break;
319 	case PPTP_CallClearRequest:
320 	case PPTP_CallDiscNotify:
321 		/*
322 		 * Find PPTP link for address and Call ID found in control
323 		 * message.
324 		 */
325 		pptp_lnk = FindPptpOutByCallId(la, GetOriginalAddress(lnk),
326 		    GetDestAddress(lnk), cptr->cid1);
327 		break;
328 	default:
329 		return;
330 	}
331 
332 	if (pptp_lnk != NULL) {
333 		int accumulate = cptr->cid1;
334 
335 		/* alias the Call Id */
336 		cptr->cid1 = GetAliasPort(pptp_lnk);
337 
338 		/* Compute TCP checksum for revised packet */
339 		tc = (struct tcphdr *)ip_next(pip);
340 		accumulate -= cptr->cid1;
341 		ADJUST_CHECKSUM(accumulate, tc->th_sum);
342 
343 		switch (ctl_type) {
344 		case PPTP_OutCallReply:
345 		case PPTP_InCallReply:
346 			codes = (PptpCode)(cptr + 1);
347 			if (codes->resCode == 1)
348 				/* Connection established,
349 				 * note the Peer's Call ID. */
350 				SetDestCallId(pptp_lnk, cptr->cid2);
351 			else
352 				/* Connection refused. */
353 				SetExpire(pptp_lnk, 0);
354 			break;
355 		case PPTP_CallDiscNotify:
356 			/* Connection closed. */
357 			SetExpire(pptp_lnk, 0);
358 			break;
359 		}
360 	}
361 }
362 
363 static void
364 AliasHandlePptpIn(struct libalias *la,
365     struct ip *pip,		/* IP packet to examine/patch */
366     struct alias_link *lnk)	/* The PPTP control link */
367 {
368 	struct alias_link *pptp_lnk;
369 	PptpCallId cptr;
370 	u_int16_t *pcall_id;
371 	u_int16_t ctl_type;	/* control message type */
372 	struct tcphdr *tc;
373 
374 	/* Verify valid PPTP control message */
375 	if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
376 		return;
377 
378 	/* Modify certain PPTP messages */
379 	switch (ctl_type) {
380 	case PPTP_InCallConn:
381 	case PPTP_WanErrorNotify:
382 	case PPTP_SetLinkInfo:
383 		pcall_id = &cptr->cid1;
384 		break;
385 	case PPTP_OutCallReply:
386 	case PPTP_InCallReply:
387 		pcall_id = &cptr->cid2;
388 		break;
389 	case PPTP_CallDiscNotify:
390 		/* Connection closed. */
391 		pptp_lnk = FindPptpInByCallId(la, GetDestAddress(lnk),
392 		    GetAliasAddress(lnk), cptr->cid1);
393 		if (pptp_lnk != NULL)
394 			SetExpire(pptp_lnk, 0);
395 		return;
396 	default:
397 		return;
398 	}
399 
400 	/* Find PPTP link for address and Call ID found in PPTP Control Msg */
401 	pptp_lnk = FindPptpInByPeerCallId(la, GetDestAddress(lnk),
402 	    GetAliasAddress(lnk), *pcall_id);
403 
404 	if (pptp_lnk != NULL) {
405 		int accumulate = *pcall_id;
406 
407 		/* De-alias the Peer's Call Id. */
408 		*pcall_id = GetOriginalPort(pptp_lnk);
409 
410 		/* Compute TCP checksum for modified packet */
411 		tc = (struct tcphdr *)ip_next(pip);
412 		accumulate -= *pcall_id;
413 		ADJUST_CHECKSUM(accumulate, tc->th_sum);
414 
415 		if (ctl_type == PPTP_OutCallReply ||
416 		    ctl_type == PPTP_InCallReply) {
417 			PptpCode codes = (PptpCode)(cptr + 1);
418 
419 			if (codes->resCode == 1)
420 				/* Connection established,
421 				 * note the Call ID. */
422 				SetDestCallId(pptp_lnk, cptr->cid1);
423 			else
424 				/* Connection refused. */
425 				SetExpire(pptp_lnk, 0);
426 		}
427 	}
428 }
429 
430 static PptpCallId
431 AliasVerifyPptp(struct ip *pip, u_int16_t * ptype) /* IP packet to examine/patch */
432 {
433 	int hlen, tlen, dlen;
434 	PptpMsgHead hptr;
435 	struct tcphdr *tc;
436 
437 	/* Calculate some lengths */
438 	tc = (struct tcphdr *)ip_next(pip);
439 	hlen = (pip->ip_hl + tc->th_off) << 2;
440 	tlen = ntohs(pip->ip_len);
441 	dlen = tlen - hlen;
442 
443 	/* Verify data length */
444 	if (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds)))
445 		return (NULL);
446 
447 	/* Move up to PPTP message header */
448 	hptr = (PptpMsgHead)tcp_next(tc);
449 
450 	/* Return the control message type */
451 	*ptype = ntohs(hptr->type);
452 
453 	/* Verify PPTP Control Message */
454 	if ((ntohs(hptr->msgType) != PPTP_CTRL_MSG_TYPE) ||
455 	    (ntohl(hptr->magic) != PPTP_MAGIC))
456 		return (NULL);
457 
458 	/* Verify data length. */
459 	if ((*ptype == PPTP_OutCallReply || *ptype == PPTP_InCallReply) &&
460 	    (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds) +
461 		sizeof(struct pptpCodes))))
462 		return (NULL);
463 	else
464 		return ((PptpCallId)(hptr + 1));
465 }
466 
467 static int
468 AliasHandlePptpGreOut(struct libalias *la, struct ip *pip)
469 {
470 	GreHdr *gr;
471 	struct alias_link *lnk;
472 
473 	gr = (GreHdr *)ip_next(pip);
474 
475 	/* Check GRE header bits. */
476 	if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
477 		return (-1);
478 
479 	lnk = FindPptpOutByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
480 	if (lnk != NULL) {
481 		struct in_addr alias_addr = GetAliasAddress(lnk);
482 
483 		/* Change source IP address. */
484 		DifferentialChecksum(&pip->ip_sum,
485 		    &alias_addr, &pip->ip_src, 2);
486 		pip->ip_src = alias_addr;
487 	}
488 	return (0);
489 }
490 
491 static int
492 AliasHandlePptpGreIn(struct libalias *la, struct ip *pip)
493 {
494 	GreHdr *gr;
495 	struct alias_link *lnk;
496 
497 	gr = (GreHdr *)ip_next(pip);
498 
499 	/* Check GRE header bits. */
500 	if ((ntohl(*((u_int32_t *)gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
501 		return (-1);
502 
503 	lnk = FindPptpInByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
504 	if (lnk != NULL) {
505 		struct in_addr src_addr = GetOriginalAddress(lnk);
506 
507 		/* De-alias the Peer's Call Id. */
508 		gr->gh_call_id = GetOriginalPort(lnk);
509 
510 		/* Restore original IP address. */
511 		DifferentialChecksum(&pip->ip_sum,
512 		    &src_addr, &pip->ip_dst, 2);
513 		pip->ip_dst = src_addr;
514 	}
515 	return (0);
516 }
517