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 <[email protected]>
37  */
38 
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41 
42 /*
43    Alias_pptp.c performs special processing for PPTP sessions under TCP.
44    Specifically, watch PPTP control messages and alias the Call ID or the
45    Peer's Call ID in the appropriate messages.  Note, PPTP requires
46    "de-aliasing" of incoming packets, this is different than any other
47    TCP applications that are currently (ie. FTP, IRC and RTSP) aliased.
48 
49    For Call IDs encountered for the first time, a PPTP alias link is created.
50    The PPTP alias link uses the Call ID in place of the original port number.
51    An alias Call ID is created.
52 
53    For this routine to work, the PPTP control messages must fit entirely
54    into a single TCP packet.  This is typically the case, but is not
55    required by the spec.
56 
57    Unlike some of the other TCP applications that are aliased (ie. FTP,
58    IRC and RTSP), the PPTP control messages that need to be aliased are
59    guaranteed to remain the same length.  The aliased Call ID is a fixed
60    length field.
61 
62    Reference: RFC 2637
63 
64    Initial version:  May, 2000 (eds)
65 
66 */
67 
68 /* Includes */
69 #ifdef _KERNEL
70 #include <sys/param.h>
71 #else
72 #include <sys/types.h>
73 #include <stdio.h>
74 #endif
75 
76 #include <netinet/in_systm.h>
77 #include <netinet/in.h>
78 #include <netinet/ip.h>
79 #include <netinet/tcp.h>
80 
81 #ifdef _KERNEL
82 #include <netinet/libalias/alias_local.h>
83 #else
84 #include "alias_local.h"
85 #endif
86 
87 /*
88  * PPTP definitions
89  */
90 
91 struct grehdr {			/* Enhanced GRE header. */
92 	u_int16_t	gh_flags;	/* Flags. */
93 	u_int16_t	gh_protocol;	/* Protocol type. */
94 	u_int16_t	gh_length;	/* Payload length. */
95 	u_int16_t	gh_call_id;	/* Call ID. */
96 	u_int32_t	gh_seq_no;	/* Sequence number (optional). */
97 	u_int32_t	gh_ack_no;	/* Acknowledgment number
98 					 * (optional). */
99 };
100 typedef struct grehdr GreHdr;
101 
102 /* The PPTP protocol ID used in the GRE 'proto' field. */
103 #define PPTP_GRE_PROTO          0x880b
104 
105 /* Bits that must be set a certain way in all PPTP/GRE packets. */
106 #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
107 #define PPTP_INIT_MASK		0xef7fffff
108 
109 #define PPTP_MAGIC		0x1a2b3c4d
110 #define PPTP_CTRL_MSG_TYPE	1
111 
112 enum {
113 	PPTP_StartCtrlConnRequest = 1,
114 	PPTP_StartCtrlConnReply = 2,
115 	PPTP_StopCtrlConnRequest = 3,
116 	PPTP_StopCtrlConnReply = 4,
117 	PPTP_EchoRequest = 5,
118 	PPTP_EchoReply = 6,
119 	PPTP_OutCallRequest = 7,
120 	PPTP_OutCallReply = 8,
121 	PPTP_InCallRequest = 9,
122 	PPTP_InCallReply = 10,
123 	PPTP_InCallConn = 11,
124 	PPTP_CallClearRequest = 12,
125 	PPTP_CallDiscNotify = 13,
126 	PPTP_WanErrorNotify = 14,
127 	PPTP_SetLinkInfo = 15
128 };
129 
130  /* Message structures */
131 struct pptpMsgHead {
132 	u_int16_t	length;	/* total length */
133 	u_int16_t	msgType;/* PPTP message type */
134 	u_int32_t	magic;	/* magic cookie */
135 	u_int16_t	type;	/* control message type */
136 	u_int16_t	resv0;	/* reserved */
137 };
138 typedef struct pptpMsgHead *PptpMsgHead;
139 
140 struct pptpCodes {
141 	u_int8_t	resCode;/* Result Code */
142 	u_int8_t	errCode;/* Error Code */
143 };
144 typedef struct pptpCodes *PptpCode;
145 
146 struct pptpCallIds {
147 	u_int16_t	cid1;	/* Call ID field #1 */
148 	u_int16_t	cid2;	/* Call ID field #2 */
149 };
150 typedef struct pptpCallIds *PptpCallId;
151 
152 static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *);
153 
154 
155 void
156 AliasHandlePptpOut(struct libalias *la,
157     struct ip *pip,		/* IP packet to examine/patch */
158     struct alias_link *lnk)
159 {				/* The PPTP control link */
160 	struct alias_link *pptp_lnk;
161 	PptpCallId cptr;
162 	PptpCode codes;
163 	u_int16_t ctl_type;	/* control message type */
164 	struct tcphdr *tc;
165 
166 	/* Verify valid PPTP control message */
167 	if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
168 		return;
169 
170 	/* Modify certain PPTP messages */
171 	switch (ctl_type) {
172 	case PPTP_OutCallRequest:
173 	case PPTP_OutCallReply:
174 	case PPTP_InCallRequest:
175 	case PPTP_InCallReply:
176 		/*
177 		 * Establish PPTP link for address and Call ID found in
178 		 * control message.
179 		 */
180 		pptp_lnk = AddPptp(la, GetOriginalAddress(lnk), GetDestAddress(lnk),
181 		    GetAliasAddress(lnk), cptr->cid1);
182 		break;
183 	case PPTP_CallClearRequest:
184 	case PPTP_CallDiscNotify:
185 		/*
186 		 * Find PPTP link for address and Call ID found in control
187 		 * message.
188 		 */
189 		pptp_lnk = FindPptpOutByCallId(la, GetOriginalAddress(lnk),
190 		    GetDestAddress(lnk),
191 		    cptr->cid1);
192 		break;
193 	default:
194 		return;
195 	}
196 
197 	if (pptp_lnk != NULL) {
198 		int accumulate = cptr->cid1;
199 
200 		/* alias the Call Id */
201 		cptr->cid1 = GetAliasPort(pptp_lnk);
202 
203 		/* Compute TCP checksum for revised packet */
204 		tc = (struct tcphdr *)ip_next(pip);
205 		accumulate -= cptr->cid1;
206 		ADJUST_CHECKSUM(accumulate, tc->th_sum);
207 
208 		switch (ctl_type) {
209 		case PPTP_OutCallReply:
210 		case PPTP_InCallReply:
211 			codes = (PptpCode) (cptr + 1);
212 			if (codes->resCode == 1)	/* Connection
213 							 * established, */
214 				SetDestCallId(pptp_lnk,	/* note the Peer's Call
215 								 * ID. */
216 				    cptr->cid2);
217 			else
218 				SetExpire(pptp_lnk, 0);	/* Connection refused. */
219 			break;
220 		case PPTP_CallDiscNotify:	/* Connection closed. */
221 			SetExpire(pptp_lnk, 0);
222 			break;
223 		}
224 	}
225 }
226 
227 void
228 AliasHandlePptpIn(struct libalias *la,
229     struct ip *pip,		/* IP packet to examine/patch */
230     struct alias_link *lnk)
231 {				/* The PPTP control link */
232 	struct alias_link *pptp_lnk;
233 	PptpCallId cptr;
234 	u_int16_t *pcall_id;
235 	u_int16_t ctl_type;	/* control message type */
236 	struct tcphdr *tc;
237 
238 	/* Verify valid PPTP control message */
239 	if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
240 		return;
241 
242 	/* Modify certain PPTP messages */
243 	switch (ctl_type) {
244 	case PPTP_InCallConn:
245 	case PPTP_WanErrorNotify:
246 	case PPTP_SetLinkInfo:
247 		pcall_id = &cptr->cid1;
248 		break;
249 	case PPTP_OutCallReply:
250 	case PPTP_InCallReply:
251 		pcall_id = &cptr->cid2;
252 		break;
253 	case PPTP_CallDiscNotify:	/* Connection closed. */
254 		pptp_lnk = FindPptpInByCallId(la, GetDestAddress(lnk),
255 		    GetAliasAddress(lnk),
256 		    cptr->cid1);
257 		if (pptp_lnk != NULL)
258 			SetExpire(pptp_lnk, 0);
259 		return;
260 	default:
261 		return;
262 	}
263 
264 	/* Find PPTP link for address and Call ID found in PPTP Control Msg */
265 	pptp_lnk = FindPptpInByPeerCallId(la, GetDestAddress(lnk),
266 	    GetAliasAddress(lnk),
267 	    *pcall_id);
268 
269 	if (pptp_lnk != NULL) {
270 		int accumulate = *pcall_id;
271 
272 		/* De-alias the Peer's Call Id. */
273 		*pcall_id = GetOriginalPort(pptp_lnk);
274 
275 		/* Compute TCP checksum for modified packet */
276 		tc = (struct tcphdr *)ip_next(pip);
277 		accumulate -= *pcall_id;
278 		ADJUST_CHECKSUM(accumulate, tc->th_sum);
279 
280 		if (ctl_type == PPTP_OutCallReply || ctl_type == PPTP_InCallReply) {
281 			PptpCode codes = (PptpCode) (cptr + 1);
282 
283 			if (codes->resCode == 1)	/* Connection
284 							 * established, */
285 				SetDestCallId(pptp_lnk,	/* note the Call ID. */
286 				    cptr->cid1);
287 			else
288 				SetExpire(pptp_lnk, 0);	/* Connection refused. */
289 		}
290 	}
291 }
292 
293 static		PptpCallId
294 AliasVerifyPptp(struct ip *pip, u_int16_t * ptype)
295 {				/* IP packet to examine/patch */
296 	int hlen, tlen, dlen;
297 	PptpMsgHead hptr;
298 	struct tcphdr *tc;
299 
300 	/* Calculate some lengths */
301 	tc = (struct tcphdr *)ip_next(pip);
302 	hlen = (pip->ip_hl + tc->th_off) << 2;
303 	tlen = ntohs(pip->ip_len);
304 	dlen = tlen - hlen;
305 
306 	/* Verify data length */
307 	if (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds)))
308 		return (NULL);
309 
310 	/* Move up to PPTP message header */
311 	hptr = (PptpMsgHead) tcp_next(tc);
312 
313 	/* Return the control message type */
314 	*ptype = ntohs(hptr->type);
315 
316 	/* Verify PPTP Control Message */
317 	if ((ntohs(hptr->msgType) != PPTP_CTRL_MSG_TYPE) ||
318 	    (ntohl(hptr->magic) != PPTP_MAGIC))
319 		return (NULL);
320 
321 	/* Verify data length. */
322 	if ((*ptype == PPTP_OutCallReply || *ptype == PPTP_InCallReply) &&
323 	    (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds) +
324 		sizeof(struct pptpCodes))))
325 		return (NULL);
326 	else
327 		return (PptpCallId) (hptr + 1);
328 }
329 
330 
331 int
332 AliasHandlePptpGreOut(struct libalias *la, struct ip *pip)
333 {
334 	GreHdr *gr;
335 	struct alias_link *lnk;
336 
337 	gr = (GreHdr *) ip_next(pip);
338 
339 	/* Check GRE header bits. */
340 	if ((ntohl(*((u_int32_t *) gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
341 		return (-1);
342 
343 	lnk = FindPptpOutByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
344 	if (lnk != NULL) {
345 		struct in_addr alias_addr = GetAliasAddress(lnk);
346 
347 		/* Change source IP address. */
348 		DifferentialChecksum(&pip->ip_sum,
349 		    &alias_addr, &pip->ip_src, 2);
350 		pip->ip_src = alias_addr;
351 	}
352 	return (0);
353 }
354 
355 
356 int
357 AliasHandlePptpGreIn(struct libalias *la, struct ip *pip)
358 {
359 	GreHdr *gr;
360 	struct alias_link *lnk;
361 
362 	gr = (GreHdr *) ip_next(pip);
363 
364 	/* Check GRE header bits. */
365 	if ((ntohl(*((u_int32_t *) gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
366 		return (-1);
367 
368 	lnk = FindPptpInByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
369 	if (lnk != NULL) {
370 		struct in_addr src_addr = GetOriginalAddress(lnk);
371 
372 		/* De-alias the Peer's Call Id. */
373 		gr->gh_call_id = GetOriginalPort(lnk);
374 
375 		/* Restore original IP address. */
376 		DifferentialChecksum(&pip->ip_sum,
377 		    &src_addr, &pip->ip_dst, 2);
378 		pip->ip_dst = src_addr;
379 	}
380 	return (0);
381 }
382