xref: /freebsd/sys/netinet/libalias/alias_pptp.c (revision f0a75d274af375d15b97b830966b99a02b7db911)
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 ip *pip, 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 ip *pip, 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 
299 static void
300 AliasHandlePptpOut(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 
371 static void
372 AliasHandlePptpIn(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 
437 static		PptpCallId
438 AliasVerifyPptp(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 
474 static int
475 AliasHandlePptpGreOut(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 
498 static int
499 AliasHandlePptpGreIn(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