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