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 /* 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
fingerprint(struct libalias * la,struct alias_data * ah)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
fingerprintgre(struct libalias * la,struct alias_data * ah)94 fingerprintgre(struct libalias *la, struct alias_data *ah)
95 {
96
97 return (0);
98 }
99
100 static int
protohandlerin(struct libalias * la,struct ip * pip,struct alias_data * ah)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
protohandlerout(struct libalias * la,struct ip * pip,struct alias_data * ah)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
protohandlergrein(struct libalias * la,struct ip * pip,struct alias_data * ah)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
protohandlergreout(struct libalias * la,struct ip * pip,struct alias_data * ah)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
mod_handler(module_t mod,int type,void * data)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
AliasHandlePptpOut(struct libalias * la,struct ip * pip,struct alias_link * lnk)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
AliasHandlePptpIn(struct libalias * la,struct ip * pip,struct alias_link * lnk)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
AliasVerifyPptp(struct ip * pip,u_int16_t * ptype)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
AliasHandlePptpGreOut(struct libalias * la,struct ip * pip)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
AliasHandlePptpGreIn(struct libalias * la,struct ip * pip)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