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