xref: /freebsd/sys/netinet/libalias/alias_pptp.c (revision a0409676120c1e558d0ade943019934e0f15118d)
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 
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 
93 static int
94 fingerprintgre(struct libalias *la, struct alias_data *ah)
95 {
96 
97 	return (0);
98 }
99 
100 static int
101 protohandlerin(struct libalias *la, struct ip *pip, struct alias_data *ah)
102 {
103 
104 	AliasHandlePptpIn(la, pip, ah->lnk);
105 	return (0);
106 }
107 
108 static int
109 protohandlerout(struct libalias *la, struct ip *pip, struct alias_data *ah)
110 {
111 
112 	AliasHandlePptpOut(la, pip, ah->lnk);
113 	return (0);
114 }
115 
116 static int
117 protohandlergrein(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 
126 static int
127 protohandlergreout(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. */
136 struct 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 };
172 static int
173 mod_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
193 static
194 #endif
195 moduledata_t alias_mod = {
196        "alias_pptp", mod_handler, NULL
197 };
198 
199 #ifdef	_KERNEL
200 DECLARE_MODULE(alias_pptp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
201 MODULE_VERSION(alias_pptp, 1);
202 MODULE_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 
235 struct 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 };
244 typedef 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 
256 enum {
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 */
275 struct 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 };
282 typedef struct pptpMsgHead *PptpMsgHead;
283 
284 struct pptpCodes {
285 	u_int8_t	resCode;/* Result Code */
286 	u_int8_t	errCode;/* Error Code */
287 };
288 typedef struct pptpCodes *PptpCode;
289 
290 struct pptpCallIds {
291 	u_int16_t	cid1;	/* Call ID field #1 */
292 	u_int16_t	cid2;	/* Call ID field #2 */
293 };
294 typedef struct pptpCallIds *PptpCallId;
295 
296 static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *);
297 
298 static void
299 AliasHandlePptpOut(struct libalias *la,
300     struct ip *pip,		/* IP packet to examine/patch */
301     struct alias_link *lnk)
302 {				/* The PPTP control link */
303 	struct alias_link *pptp_lnk;
304 	PptpCallId cptr;
305 	PptpCode codes;
306 	u_int16_t ctl_type;	/* control message type */
307 	struct tcphdr *tc;
308 
309 	/* Verify valid PPTP control message */
310 	if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
311 		return;
312 
313 	/* Modify certain PPTP messages */
314 	switch (ctl_type) {
315 	case PPTP_OutCallRequest:
316 	case PPTP_OutCallReply:
317 	case PPTP_InCallRequest:
318 	case PPTP_InCallReply:
319 		/*
320 		 * Establish PPTP link for address and Call ID found in
321 		 * control message.
322 		 */
323 		pptp_lnk = AddPptp(la, GetOriginalAddress(lnk), GetDestAddress(lnk),
324 		    GetAliasAddress(lnk), cptr->cid1);
325 		break;
326 	case PPTP_CallClearRequest:
327 	case PPTP_CallDiscNotify:
328 		/*
329 		 * Find PPTP link for address and Call ID found in control
330 		 * message.
331 		 */
332 		pptp_lnk = FindPptpOutByCallId(la, GetOriginalAddress(lnk),
333 		    GetDestAddress(lnk),
334 		    cptr->cid1);
335 		break;
336 	default:
337 		return;
338 	}
339 
340 	if (pptp_lnk != NULL) {
341 		int accumulate = cptr->cid1;
342 
343 		/* alias the Call Id */
344 		cptr->cid1 = GetAliasPort(pptp_lnk);
345 
346 		/* Compute TCP checksum for revised packet */
347 		tc = (struct tcphdr *)ip_next(pip);
348 		accumulate -= cptr->cid1;
349 		ADJUST_CHECKSUM(accumulate, tc->th_sum);
350 
351 		switch (ctl_type) {
352 		case PPTP_OutCallReply:
353 		case PPTP_InCallReply:
354 			codes = (PptpCode) (cptr + 1);
355 			if (codes->resCode == 1)	/* Connection
356 							 * established, */
357 				SetDestCallId(pptp_lnk,	/* note the Peer's Call
358 								 * ID. */
359 				    cptr->cid2);
360 			else
361 				SetExpire(pptp_lnk, 0);	/* Connection refused. */
362 			break;
363 		case PPTP_CallDiscNotify:	/* Connection closed. */
364 			SetExpire(pptp_lnk, 0);
365 			break;
366 		}
367 	}
368 }
369 
370 static void
371 AliasHandlePptpIn(struct libalias *la,
372     struct ip *pip,		/* IP packet to examine/patch */
373     struct alias_link *lnk)
374 {				/* The PPTP control link */
375 	struct alias_link *pptp_lnk;
376 	PptpCallId cptr;
377 	u_int16_t *pcall_id;
378 	u_int16_t ctl_type;	/* control message type */
379 	struct tcphdr *tc;
380 
381 	/* Verify valid PPTP control message */
382 	if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
383 		return;
384 
385 	/* Modify certain PPTP messages */
386 	switch (ctl_type) {
387 	case PPTP_InCallConn:
388 	case PPTP_WanErrorNotify:
389 	case PPTP_SetLinkInfo:
390 		pcall_id = &cptr->cid1;
391 		break;
392 	case PPTP_OutCallReply:
393 	case PPTP_InCallReply:
394 		pcall_id = &cptr->cid2;
395 		break;
396 	case PPTP_CallDiscNotify:	/* Connection closed. */
397 		pptp_lnk = FindPptpInByCallId(la, GetDestAddress(lnk),
398 		    GetAliasAddress(lnk),
399 		    cptr->cid1);
400 		if (pptp_lnk != NULL)
401 			SetExpire(pptp_lnk, 0);
402 		return;
403 	default:
404 		return;
405 	}
406 
407 	/* Find PPTP link for address and Call ID found in PPTP Control Msg */
408 	pptp_lnk = FindPptpInByPeerCallId(la, GetDestAddress(lnk),
409 	    GetAliasAddress(lnk),
410 	    *pcall_id);
411 
412 	if (pptp_lnk != NULL) {
413 		int accumulate = *pcall_id;
414 
415 		/* De-alias the Peer's Call Id. */
416 		*pcall_id = GetOriginalPort(pptp_lnk);
417 
418 		/* Compute TCP checksum for modified packet */
419 		tc = (struct tcphdr *)ip_next(pip);
420 		accumulate -= *pcall_id;
421 		ADJUST_CHECKSUM(accumulate, tc->th_sum);
422 
423 		if (ctl_type == PPTP_OutCallReply || ctl_type == PPTP_InCallReply) {
424 			PptpCode codes = (PptpCode) (cptr + 1);
425 
426 			if (codes->resCode == 1)	/* Connection
427 							 * established, */
428 				SetDestCallId(pptp_lnk,	/* note the Call ID. */
429 				    cptr->cid1);
430 			else
431 				SetExpire(pptp_lnk, 0);	/* Connection refused. */
432 		}
433 	}
434 }
435 
436 static		PptpCallId
437 AliasVerifyPptp(struct ip *pip, u_int16_t * ptype)
438 {				/* IP packet to examine/patch */
439 	int hlen, tlen, dlen;
440 	PptpMsgHead hptr;
441 	struct tcphdr *tc;
442 
443 	/* Calculate some lengths */
444 	tc = (struct tcphdr *)ip_next(pip);
445 	hlen = (pip->ip_hl + tc->th_off) << 2;
446 	tlen = ntohs(pip->ip_len);
447 	dlen = tlen - hlen;
448 
449 	/* Verify data length */
450 	if (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds)))
451 		return (NULL);
452 
453 	/* Move up to PPTP message header */
454 	hptr = (PptpMsgHead) tcp_next(tc);
455 
456 	/* Return the control message type */
457 	*ptype = ntohs(hptr->type);
458 
459 	/* Verify PPTP Control Message */
460 	if ((ntohs(hptr->msgType) != PPTP_CTRL_MSG_TYPE) ||
461 	    (ntohl(hptr->magic) != PPTP_MAGIC))
462 		return (NULL);
463 
464 	/* Verify data length. */
465 	if ((*ptype == PPTP_OutCallReply || *ptype == PPTP_InCallReply) &&
466 	    (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds) +
467 		sizeof(struct pptpCodes))))
468 		return (NULL);
469 	else
470 		return (PptpCallId) (hptr + 1);
471 }
472 
473 static int
474 AliasHandlePptpGreOut(struct libalias *la, struct ip *pip)
475 {
476 	GreHdr *gr;
477 	struct alias_link *lnk;
478 
479 	gr = (GreHdr *) ip_next(pip);
480 
481 	/* Check GRE header bits. */
482 	if ((ntohl(*((u_int32_t *) gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
483 		return (-1);
484 
485 	lnk = FindPptpOutByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
486 	if (lnk != NULL) {
487 		struct in_addr alias_addr = GetAliasAddress(lnk);
488 
489 		/* Change source IP address. */
490 		DifferentialChecksum(&pip->ip_sum,
491 		    &alias_addr, &pip->ip_src, 2);
492 		pip->ip_src = alias_addr;
493 	}
494 	return (0);
495 }
496 
497 static int
498 AliasHandlePptpGreIn(struct libalias *la, struct ip *pip)
499 {
500 	GreHdr *gr;
501 	struct alias_link *lnk;
502 
503 	gr = (GreHdr *) ip_next(pip);
504 
505 	/* Check GRE header bits. */
506 	if ((ntohl(*((u_int32_t *) gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
507 		return (-1);
508 
509 	lnk = FindPptpInByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
510 	if (lnk != NULL) {
511 		struct in_addr src_addr = GetOriginalAddress(lnk);
512 
513 		/* De-alias the Peer's Call Id. */
514 		gr->gh_call_id = GetOriginalPort(lnk);
515 
516 		/* Restore original IP address. */
517 		DifferentialChecksum(&pip->ip_sum,
518 		    &src_addr, &pip->ip_dst, 2);
519 		pip->ip_dst = src_addr;
520 	}
521 	return (0);
522 }
523